diff --git a/libs/harfbuzz/.ci/build-win32.sh b/libs/harfbuzz/.ci/build-win32.sh new file mode 100644 index 00000000..a1ca53d2 --- /dev/null +++ b/libs/harfbuzz/.ci/build-win32.sh @@ -0,0 +1,26 @@ +#!/bin/bash +set -e + +meson --cross-file=.ci/win32-cross-file.txt \ + --wrap-mode=forcefallback \ + -Dtests=disabled \ + -Dcairo=enabled \ + -Dcairo:fontconfig=disabled \ + -Dglib=enabled \ + -Dfreetype=enabled \ + -Dgdi=enabled \ + -Ddirectwrite=enabled \ + -Dcairo=enabled \ + win32build \ + $@ + +ninja -Cwin32build -j3 # building with all the cores won't work fine with CricleCI for some reason + +rm -rf win32build/harfbuzz-win32 +mkdir win32build/harfbuzz-win32 +cp win32build/util/hb-*.exe win32build/harfbuzz-win32 +find win32build -name '*.dll' -exec cp {} win32build/harfbuzz-win32 \; +i686-w64-mingw32-strip win32build/harfbuzz-win32/*.{dll,exe} +rm -f harfbuzz-win32.zip +(cd win32build && zip -r ../harfbuzz-win32.zip harfbuzz-win32) +echo "harfbuzz-win32.zip is ready." diff --git a/libs/harfbuzz/.ci/build-win64.sh b/libs/harfbuzz/.ci/build-win64.sh new file mode 100644 index 00000000..385d5597 --- /dev/null +++ b/libs/harfbuzz/.ci/build-win64.sh @@ -0,0 +1,26 @@ +#!/bin/bash +set -e + +meson --cross-file=.ci/win64-cross-file.txt \ + --wrap-mode=forcefallback \ + -Dtests=disabled \ + -Dcairo=enabled \ + -Dcairo:fontconfig=disabled \ + -Dglib=enabled \ + -Dfreetype=enabled \ + -Dgdi=enabled \ + -Ddirectwrite=enabled \ + -Dcairo=enabled \ + win64build \ + $@ + +ninja -Cwin64build -j3 # building with all the cores won't work fine with CricleCI for some reason + +rm -rf win64build/harfbuzz-win64 +mkdir win64build/harfbuzz-win64 +cp win64build/util/hb-*.exe win64build/harfbuzz-win64 +find win64build -name '*.dll' -exec cp {} win64build/harfbuzz-win64 \; +x86_64-w64-mingw32-strip win64build/harfbuzz-win64/*.{dll,exe} +rm -f harfbuzz-win64.zip +(cd win64build && zip -r ../harfbuzz-win64.zip harfbuzz-win64) +echo "harfbuzz-win64.zip is ready." diff --git a/libs/harfbuzz/.ci/deploy-docs.sh b/libs/harfbuzz/.ci/deploy-docs.sh new file mode 100644 index 00000000..2b3f9f23 --- /dev/null +++ b/libs/harfbuzz/.ci/deploy-docs.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +set -x +set -o errexit -o nounset + +DOCSDIR=build-docs +REVISION=$(git rev-parse --short HEAD) + +rm -rf $DOCSDIR || exit +mkdir $DOCSDIR +cd $DOCSDIR + +cp ../build/docs/html/* . +#cp ../build/docs/CNAME . + +git init +git branch -m main +git config user.name "CI" +git config user.email "harfbuzz-admin@googlegroups.com" +set +x +echo "git remote add upstream \"https://\$GH_TOKEN@github.com/harfbuzz/harfbuzz.github.io.git\"" +git remote add upstream "https://$GH_TOKEN@github.com/harfbuzz/harfbuzz.github.io.git" +set -x +git fetch upstream +git reset upstream/main + +touch . +git add -A . + +if [[ $(git status -s) ]]; then + git commit -m "Rebuild docs for https://github.com/harfbuzz/harfbuzz/commit/$REVISION" + git push -q upstream HEAD:main +fi diff --git a/libs/harfbuzz/.ci/publish_release_artifact.sh b/libs/harfbuzz/.ci/publish_release_artifact.sh new file mode 100644 index 00000000..272d90be --- /dev/null +++ b/libs/harfbuzz/.ci/publish_release_artifact.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash + +set -e +set -o pipefail + +if [[ -z $GITHUB_TOKEN ]]; then + echo "No GITHUB_TOKEN secret found, artifact publishing skipped" + exit +fi + +if ! hash ghr 2> /dev/null; then + _GHR_VER=v0.14.0 + _GHR=ghr_${_GHR_VER}_linux_amd64 + mkdir -p $HOME/.local/bin + curl -sfL https://github.com/tcnksm/ghr/releases/download/$_GHR_VER/$_GHR.tar.gz | + tar xz -C $HOME/.local/bin --strip-components=1 $_GHR/ghr +fi + +ghr -replace \ + -u $CIRCLE_PROJECT_USERNAME \ + -r $CIRCLE_PROJECT_REPONAME \ + $CIRCLE_TAG \ + $1 diff --git a/libs/harfbuzz/.ci/win32-cross-file.txt b/libs/harfbuzz/.ci/win32-cross-file.txt new file mode 100644 index 00000000..982a909b --- /dev/null +++ b/libs/harfbuzz/.ci/win32-cross-file.txt @@ -0,0 +1,20 @@ +[host_machine] +system = 'windows' +cpu_family = 'x86' +cpu = 'i686' +endian = 'little' + +[properties] +c_args = [] +c_link_args = ['-static-libgcc', '-Wl,-Bstatic', '-lpthread'] +cpp_args = [] +cpp_link_args = ['-static-libgcc', '-static-libstdc++', '-Wl,-Bstatic', '-lpthread'] + +[binaries] +c = 'i686-w64-mingw32-gcc' +cpp = 'i686-w64-mingw32-g++' +ar = 'i686-w64-mingw32-ar' +ld = 'i686-w64-mingw32-ld' +objcopy = 'i686-w64-mingw32-objcopy' +strip = 'i686-w64-mingw32-strip' +windres = 'i686-w64-mingw32-windres' diff --git a/libs/harfbuzz/.ci/win64-cross-file.txt b/libs/harfbuzz/.ci/win64-cross-file.txt new file mode 100644 index 00000000..e906e085 --- /dev/null +++ b/libs/harfbuzz/.ci/win64-cross-file.txt @@ -0,0 +1,20 @@ +[host_machine] +system = 'windows' +cpu_family = 'x86_64' +cpu = 'x86_64' +endian = 'little' + +[properties] +c_args = [] +c_link_args = ['-static-libgcc', '-Wl,-Bstatic', '-lpthread'] +cpp_args = [] +cpp_link_args = ['-static-libgcc', '-static-libstdc++', '-Wl,-Bstatic', '-lpthread'] + +[binaries] +c = 'x86_64-w64-mingw32-gcc' +cpp = 'x86_64-w64-mingw32-g++' +ar = 'x86_64-w64-mingw32-ar' +ld = 'x86_64-w64-mingw32-ld' +objcopy = 'x86_64-w64-mingw32-objcopy' +strip = 'x86_64-w64-mingw32-strip' +windres = 'x86_64-w64-mingw32-windres' diff --git a/libs/harfbuzz/.circleci/config.yml b/libs/harfbuzz/.circleci/config.yml new file mode 100644 index 00000000..9525c4ae --- /dev/null +++ b/libs/harfbuzz/.circleci/config.yml @@ -0,0 +1,229 @@ +version: 2.1 + +executors: + win32-executor: + docker: + - image: cimg/base:edge-20.04 + win64-executor: + docker: + - image: cimg/base:edge-20.04 + autotools-executor: + docker: + - image: cimg/base:edge-20.04 + +jobs: + + macos-aat-fonts: + macos: + xcode: "12.5.1" + steps: + - checkout + - run: HOMEBREW_NO_AUTO_UPDATE=1 brew install pkg-config ragel freetype glib cairo python3 icu4c graphite2 gobject-introspection gtk-doc ninja + - run: pip3 install meson --upgrade + - run: PKG_CONFIG_PATH="/usr/local/opt/icu4c/lib/pkgconfig:/usr/local/opt/libffi/lib/pkgconfig" meson build -Dcoretext=enabled -Dgraphite=enabled -Dauto_features=enabled -Dchafa=disabled + - run: meson compile -Cbuild + - run: meson test -Cbuild --print-errorlogs + - store_artifacts: + path: build/meson-logs/ + + # will be dropped with autotools removal + distcheck: + executor: autotools-executor + steps: + - checkout + - run: sudo apt update && DEBIAN_FRONTEND=noninteractive sudo apt install -y git ninja-build binutils libtool autoconf automake make gcc g++ pkg-config ragel gtk-doc-tools gobject-introspection libfreetype6-dev libglib2.0-dev libgirepository1.0-dev libcairo2-dev libicu-dev libgraphite2-dev python3 python3-pip + - run: pip3 install fonttools meson --upgrade + - run: ./autogen.sh + - run: make -j2 distcheck + - run: rm harfbuzz-* && make distdir + - run: cd harfbuzz-* && meson build && ninja -j2 -Cbuild test + - run: make dist + - persist_to_workspace: + root: . + paths: harfbuzz-*.tar.xz + + publish-dist: + executor: autotools-executor + steps: + - checkout + - attach_workspace: + at: . + - run: | + .ci/publish_release_artifact.sh harfbuzz-$CIRCLE_TAG.tar.xz + + fedora-valgrind: + docker: + - image: fedora:36 + steps: + - checkout + - run: dnf install -y pkg-config ragel valgrind gcc gcc-c++ meson git glib2-devel freetype-devel cairo-devel libicu-devel gobject-introspection-devel graphite2-devel redhat-rpm-config python python-pip || true + - run: meson build --buildtype=debugoptimized + - run: ninja -Cbuild -j9 + # TOOD: increase timeouts and remove --no-suite=slow + - run: RUN_VALGRIND=1 meson test -Cbuild --no-suite=slow --wrap='valgrind --leak-check=full --error-exitcode=1' --print-errorlogs --num-processes=$(($(nproc)/2 + 1)) + + alpine: + docker: + - image: alpine + steps: + - checkout + - run: apk update && apk add ragel gcc g++ glib-dev freetype-dev cairo-dev git py3-pip ninja + - run: pip3 install meson==0.56.0 + - run: meson build --buildtype=minsize + - run: ninja -Cbuild -j9 + - run: meson test -Cbuild --print-errorlogs + + archlinux: + docker: + - image: archlinux/base + steps: + - checkout + - run: pacman --noconfirm -Syu freetype2 meson git clang cairo icu gettext gobject-introspection gcc gcc-libs glib2 graphite pkg-config ragel python python-pip base-devel gtk-doc + - run: pip install flake8 fonttools + - run: flake8 . --count --select=E901,E999,F821,F822,F823 --show-source --statistics + - run: meson build -Dgraphite=enabled -Dauto_features=enabled -Dexperimental_api=true + - run: meson compile -Cbuild -j9 + - run: meson test -Cbuild --print-errorlogs + - run: meson dist -Cbuild + - run: clang -c src/harfbuzz.cc -DHB_NO_MT + - run: clang -c src/hb-*.cc -DHB_NO_MT -DHB_TINY -DHB_NO_OT_FONT + + asan-ubsan: + docker: + - image: ubuntu:20.04 + steps: + - checkout + - run: apt update || true + - run: DEBIAN_FRONTEND=noninteractive apt install -y python3 python3-pip ninja-build clang lld git binutils pkg-config ragel libfreetype6-dev libglib2.0-dev libcairo2-dev libicu-dev libgraphite2-dev + - run: pip3 install meson==0.56.0 + - run: CC=clang CXX=clang++ meson build --default-library=static -Db_sanitize=address,undefined --buildtype=debugoptimized --wrap-mode=nodownload -Dexperimental_api=true + - run: ninja -Cbuild -j8 && meson test -Cbuild --print-errorlogs | asan_symbolize | c++filt + + tsan: + docker: + - image: ubuntu:20.04 + steps: + - checkout + - run: apt update || true + - run: DEBIAN_FRONTEND=noninteractive apt install -y python3 python3-pip ninja-build clang lld git binutils pkg-config ragel libfreetype6-dev libglib2.0-dev libcairo2-dev libicu-dev libgraphite2-dev + - run: pip3 install meson==0.56.0 + - run: CC=clang CXX=clang++ meson build --default-library=static -Db_sanitize=thread --buildtype=debugoptimized --wrap-mode=nodownload -Dexperimental_api=true + - run: ninja -Cbuild -j8 && meson test -Cbuild --print-errorlogs | asan_symbolize | c++filt + + msan: + docker: + - image: ubuntu:20.04 + steps: + - checkout + - run: apt update || true + - run: DEBIAN_FRONTEND=noninteractive apt install -y python3 python3-pip ninja-build clang lld git binutils pkg-config ragel libfreetype6-dev libglib2.0-dev libcairo2-dev libicu-dev libgraphite2-dev + - run: pip3 install meson==0.56.0 + # msan, needs --force-fallback-for=glib,freetype2 also which doesn't work yet but runs fuzzer cases at least + - run: CC=clang CXX=clang++ meson build --default-library=static -Db_sanitize=memory --buildtype=debugoptimized --wrap-mode=nodownload -Dauto_features=disabled -Dtests=enabled -Dexperimental_api=true + - run: ninja -Cbuild -j8 && meson test -Cbuild --print-errorlogs | asan_symbolize | c++filt + + clang-cxx2a: + docker: + - image: ubuntu:20.04 + steps: + - checkout + - run: apt update || true + - run: DEBIAN_FRONTEND=noninteractive apt install -y clang lld git binutils + - run: clang -c src/harfbuzz-subset.cc -DHB_NO_MT -Werror -std=c++2a + + crossbuild-win32: + executor: win32-executor + steps: + - checkout + - run: sudo apt update && DEBIAN_FRONTEND=noninteractive sudo apt install -y ninja-build gtk-doc-tools python3 python3-pip git g++-mingw-w64-i686 zip + - run: pip3 install meson==0.56.0 --upgrade + - run: .ci/build-win32.sh + - store_artifacts: + path: harfbuzz-win32.zip + - persist_to_workspace: + root: . + paths: harfbuzz-win32.zip + + publish-win32: + executor: win32-executor + steps: + - checkout + - attach_workspace: + at: . + - run: | + mv harfbuzz-win32{,-$CIRCLE_TAG}.zip + .ci/publish_release_artifact.sh harfbuzz-win32-$CIRCLE_TAG.zip + + crossbuild-win64: + executor: win64-executor + steps: + - checkout + - run: sudo apt update && DEBIAN_FRONTEND=noninteractive sudo apt install -y ninja-build gtk-doc-tools python3 python3-pip git g++-mingw-w64-x86-64 zip + - run: pip3 install meson==0.56.0 --upgrade + - run: bash .ci/build-win64.sh + - store_artifacts: + path: harfbuzz-win64.zip + - persist_to_workspace: + root: . + paths: harfbuzz-win64.zip + + publish-win64: + executor: win64-executor + steps: + - checkout + - attach_workspace: + at: . + - run: | + mv harfbuzz-win64{,-$CIRCLE_TAG}.zip + .ci/publish_release_artifact.sh harfbuzz-win64-$CIRCLE_TAG.zip + + +workflows: + version: 2 + + build: + jobs: + - macos-aat-fonts + - distcheck: + filters: # must have filter or won't work as a dependency + tags: + only: /.*/ + - publish-dist: + requires: + - distcheck + filters: + tags: + only: /^\d+\.\d+\.\d+$/ + branches: + ignore: /.*/ + - fedora-valgrind + - alpine + #- archlinux + - asan-ubsan + - tsan + - msan + - clang-cxx2a + - crossbuild-win32: + filters: # must have filter or won't work as a dependency + tags: + only: /.*/ + - crossbuild-win64: + filters: # must have filter or won't work as a dependency + tags: + only: /.*/ + - publish-win32: + requires: + - crossbuild-win32 + filters: + tags: + only: /^\d+\.\d+\.\d+$/ + branches: + ignore: /.*/ + - publish-win64: + requires: + - crossbuild-win64 + filters: + tags: + only: /^\d+\.\d+\.\d+$/ + branches: + ignore: /.*/ diff --git a/libs/harfbuzz/.clang-format b/libs/harfbuzz/.clang-format new file mode 100644 index 00000000..259427cb --- /dev/null +++ b/libs/harfbuzz/.clang-format @@ -0,0 +1,38 @@ +# The following tries to match the current code style, is imperfect for now +# but good for new codes be added + +IndentWidth: 2 +TabWidth: 8 +UseTab: Always +SpaceBeforeParens: Always +AllowShortLoopsOnASingleLine: true +BreakBeforeBraces: Custom +BraceWrapping: + AfterEnum: true + AfterStruct: false + SplitEmptyFunction: false + AfterClass: true + AfterControlStatement: true + AfterEnum: false + AfterFunction: true + AfterNamespace: false + AfterStruct: true + AfterUnion: true + BeforeElse: true +AlwaysBreakTemplateDeclarations: true +AlignTrailingComments: true +AlignEscapedNewlines: Left +AllowShortBlocksOnASingleLine: true +SpaceAfterCStyleCast: true +AlwaysBreakAfterDefinitionReturnType: TopLevel +BinPackParameters: false +AllowShortFunctionsOnASingleLine: Inline +AccessModifierOffset: 0 +AlignTrailingComments: true +AllowShortIfStatementsOnASingleLine: true +AlignAfterOpenBracket: Align +AlignOperands: true +AllowShortCaseLabelsOnASingleLine: true + +# We like to have this only for function parameters and structs fields, not always +# AlignConsecutiveDeclarations: true diff --git a/libs/harfbuzz/.codecov.yml b/libs/harfbuzz/.codecov.yml new file mode 100644 index 00000000..abd04ca9 --- /dev/null +++ b/libs/harfbuzz/.codecov.yml @@ -0,0 +1,8 @@ +comment: off + +coverage: + status: + project: + default: + threshold: 1% + patch: off diff --git a/libs/harfbuzz/.editorconfig b/libs/harfbuzz/.editorconfig new file mode 100644 index 00000000..cac917b9 --- /dev/null +++ b/libs/harfbuzz/.editorconfig @@ -0,0 +1,23 @@ +root = true + +[*] +charset = utf-8 +trim_trailing_whitespace = true +end_of_line = lf +insert_final_newline = true + +[*.{c,cc,h,hh,rl}] +tab_width = 8 +indent_size = 2 +indent_style = tab # should be space + +[*.{py,sh}] +indent_style = tab + +[{Makefile.am,Makefile.sources,configure.ac}] +tab_width = 8 + +[{meson.build,meson_options.txt}] +tab_width = 8 +indent_style = space +indent_size = 2 diff --git a/libs/harfbuzz/.github/dependabot.yml b/libs/harfbuzz/.github/dependabot.yml new file mode 100644 index 00000000..5ace4600 --- /dev/null +++ b/libs/harfbuzz/.github/dependabot.yml @@ -0,0 +1,6 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" diff --git a/libs/harfbuzz/.github/workflows/cifuzz.yml b/libs/harfbuzz/.github/workflows/cifuzz.yml new file mode 100644 index 00000000..07e4cd57 --- /dev/null +++ b/libs/harfbuzz/.github/workflows/cifuzz.yml @@ -0,0 +1,24 @@ +name: CIFuzz +on: [pull_request] +jobs: + Fuzzing: + runs-on: ubuntu-latest + steps: + - name: Build Fuzzers + id: build + uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master + with: + oss-fuzz-project-name: 'harfbuzz' + dry-run: false + - name: Run Fuzzers + uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master + with: + oss-fuzz-project-name: 'harfbuzz' + fuzz-seconds: 600 + dry-run: false + - name: Upload Crash + uses: actions/upload-artifact@v3 + if: failure() && steps.build.outcome == 'success' + with: + name: artifacts + path: ./out/artifacts diff --git a/libs/harfbuzz/.github/workflows/configs-build.yml b/libs/harfbuzz/.github/workflows/configs-build.yml new file mode 100644 index 00000000..9f4fd78e --- /dev/null +++ b/libs/harfbuzz/.github/workflows/configs-build.yml @@ -0,0 +1,27 @@ +name: configs-ci + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +permissions: + contents: read + +jobs: + build: + runs-on: ubuntu-18.04 + + steps: + - uses: actions/checkout@v3 + - name: install dependencies + run: sudo apt-get install gcc + - name: HB_DISABLE_DEPRECATED + run: g++ -std=c++11 -c src/harfbuzz.cc -DHB_DISABLE_DEPRECATED + - name: HB_MINI + run: g++ -std=c++11 -c src/harfbuzz.cc -DHB_MINI + - name: HB_LEAN + run: g++ -std=c++11 -c src/harfbuzz.cc -DHB_LEAN + - name: HB_TINY + run: g++ -std=c++11 -c src/harfbuzz.cc -DHB_TINY diff --git a/libs/harfbuzz/.github/workflows/coverity-scan.yml b/libs/harfbuzz/.github/workflows/coverity-scan.yml new file mode 100644 index 00000000..e9fc5435 --- /dev/null +++ b/libs/harfbuzz/.github/workflows/coverity-scan.yml @@ -0,0 +1,42 @@ +name: coverity-scan + +on: + schedule: + - cron: '0 10 * * *' # Daily at 10:00 UTC + +permissions: + contents: read + +jobs: + latest: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - run: sudo apt-get install gcc clang wget git curl pkg-config libfreetype6-dev libglib2.0-dev libicu-dev libgraphite2-dev + + - name: Download Coverity + run: | + wget -q https://scan.coverity.com/download/cxx/linux64 --post-data "token=$TOKEN&project=HarfBuzz" -O cov-analysis-linux64.tar.gz + mkdir cov-analysis-linux64 + tar xzf cov-analysis-linux64.tar.gz --strip 1 -C cov-analysis-linux64 + env: + TOKEN: ${{ secrets.COVERITY_SCAN_TOKEN }} + + # ideally we should've used meson and ninja here but it complains about coverage or something + - run: cov-analysis-linux64/bin/cov-build --dir cov-int clang src/hb-*.cc -DHAVE_FREETYPE -DHAVE_GRAPHITE2 -DHAVE_GLIB -DHAVE_ICU `pkg-config --cflags freetype2 graphite2 glib-2.0 icu-uc` -DHAVE_ROUNDF -DHAVE_SYS_MMAN_H -DHAVE_UNISTD_H -DHAVE_GETPAGESIZE -DHB_EXPERIMENTAL_API -c + + - run: tar czvf harfbuzz.tgz cov-int + + - name: submit to coverity + run: | + curl \ + --form project=HarfBuzz \ + --form token=$TOKEN \ + --form email=harfbuzz-bots-chatter@googlegroups.com \ + --form file=@harfbuzz.tgz \ + --form version=trunk \ + --form description="`git rev-parse --short HEAD`" \ + https://scan.coverity.com/builds?project=HarfBuzz + env: + TOKEN: ${{ secrets.COVERITY_SCAN_TOKEN }} diff --git a/libs/harfbuzz/.github/workflows/linux-ci.yml b/libs/harfbuzz/.github/workflows/linux-ci.yml new file mode 100644 index 00000000..f67f5d3f --- /dev/null +++ b/libs/harfbuzz/.github/workflows/linux-ci.yml @@ -0,0 +1,57 @@ +name: linux-ci + +on: + push: + branches: [ main ] + tags: ["*.*.*"] + pull_request: + branches: [ main ] + +permissions: + contents: read + +jobs: + build: + runs-on: ubuntu-18.04 + + steps: + - uses: actions/checkout@v3 + - name: install dependencies + run: sudo apt-get update && sudo apt-get install pkg-config gcc gtk-doc-tools libfreetype6-dev libglib2.0-dev libcairo2-dev libicu-dev libgraphite2-dev python3 python3-setuptools ninja-build gobject-introspection libgirepository1.0-dev + - run: sudo pip3 install fonttools meson==0.56.0 gcovr==5.0 + - name: run + run: meson build -Db_coverage=true --auto-features=enabled -Dgraphite=enabled -Dchafa=disabled -Dragel_subproject=true -Doptimization=2 + - name: ci + run: meson test --print-errorlogs -Cbuild + + - name: generate documentations + run: ninja -Cbuild harfbuzz-doc + - name: deploy documentations + if: github.ref_type == 'tag' + run: .ci/deploy-docs.sh + env: + GH_TOKEN: ${{ secrets.GH_TOKEN }} + REVISION: ${{ github.sha }} + + # waiting for https://github.com/rhysd/github-action-benchmark/issues/36 to happen + # - name: benchmark + # run: build/perf/perf --benchmark_format=json > perf/result.json + # - name: store benchmark result + # uses: rhysd/github-action-benchmark@b2ee598 + # if: github.event_name != 'pull_request' + # with: + # name: C++ Benchmark + # tool: 'googlecpp' + # output-file-path: perf/result.json + # gh-pages-branch: gh-pages + # github-token: ${{ secrets.PERSONAL_GITHUB_TOKEN }} + # auto-push: true + # alert-threshold: '150%' + # comment-on-alert: true + # fail-on-alert: true + + - name: cov + run: ninja -Cbuild coverage-xml + - uses: codecov/codecov-action@v3 + with: + file: build/meson-logs/coverage.xml diff --git a/libs/harfbuzz/.github/workflows/macos-ci.yml b/libs/harfbuzz/.github/workflows/macos-ci.yml new file mode 100644 index 00000000..a6b6d3db --- /dev/null +++ b/libs/harfbuzz/.github/workflows/macos-ci.yml @@ -0,0 +1,30 @@ +name: macos-ci + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +permissions: + contents: read + +jobs: + build: + runs-on: macos-latest + + steps: + - uses: actions/checkout@v3 + - name: install dependencies + run: HOMEBREW_NO_AUTO_UPDATE=1 brew install pkg-config freetype glib glib-utils cairo icu4c graphite2 gobject-introspection gtk-doc ninja + - run: pip3 install fonttools meson==0.56.0 gcovr==5.0 + - name: run + run: PKG_CONFIG_PATH="/usr/local/opt/icu4c/lib/pkgconfig:/usr/local/opt/libffi/lib/pkgconfig" meson build -Db_coverage=true -Dcoretext=enabled -Dgraphite=enabled -Dauto_features=enabled -Dchafa=disabled -Doptimization=2 + - name: ci + run: meson test --print-errorlogs -Cbuild + + - name: cov + run: ninja -Cbuild coverage-xml + - uses: codecov/codecov-action@v3 + with: + file: build/meson-logs/coverage.xml diff --git a/libs/harfbuzz/.github/workflows/msvc-ci.yml b/libs/harfbuzz/.github/workflows/msvc-ci.yml new file mode 100644 index 00000000..57aef04c --- /dev/null +++ b/libs/harfbuzz/.github/workflows/msvc-ci.yml @@ -0,0 +1,58 @@ +name: msvc + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +permissions: + contents: read + +jobs: + msvc: + runs-on: ${{ matrix.os }} + + strategy: + matrix: + os: [windows-2019, windows-latest] + include: + - name: msvc-2019-x86 + os: windows-2019 + ARCH: x86 + - name: msvc-2019-amd64 + os: windows-latest + ARCH: amd64 + name: ${{ matrix.name }} + + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-python@v4 + with: + python-version: '3.x' + - uses: ilammy/msvc-dev-cmd@v1 + with: + arch : ${{ matrix.ARCH }} + - name: Upgrade pip + run: | + python -m pip install -U pip + - name: Install Dependencies + run: | + pip install --upgrade meson ninja fonttools + - name: Build + run: | + # This dir contains a pkg-config which meson will happily use and later fail, so remove it + $env:path = ($env:path.Split(';') | Where-Object { $_ -ne 'C:\Strawberry\perl\bin' }) -join ';' + + meson setup build ` + --wrap-mode=default ` + --buildtype=release ` + -Dglib=enabled ` + -Dfreetype=enabled ` + -Dgdi=enabled ` + -Ddirectwrite=enabled + + meson compile -C build + - name: Test + run: | + meson test --print-errorlogs --suite=harfbuzz -C build diff --git a/libs/harfbuzz/.github/workflows/msys2-ci.yml b/libs/harfbuzz/.github/workflows/msys2-ci.yml new file mode 100644 index 00000000..0704f4ca --- /dev/null +++ b/libs/harfbuzz/.github/workflows/msys2-ci.yml @@ -0,0 +1,68 @@ +name: msys2 + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +permissions: + contents: read + +jobs: + msys2: + runs-on: windows-latest + + strategy: + matrix: + include: + - MSYSTEM: MINGW32 + MSYS2_ARCH: i686 + - MSYSTEM: MINGW64 + MSYS2_ARCH: x86_64 + name: ${{ matrix.MSYSTEM }} + + defaults: + run: + shell: msys2 {0} + steps: + - uses: actions/checkout@v3 + - uses: msys2/setup-msys2@v2 + with: + msystem: ${{ matrix.MSYSTEM }} + update: true + install: >- + mingw-w64-${{ matrix.MSYS2_ARCH }}-cairo + mingw-w64-${{ matrix.MSYS2_ARCH }}-freetype + mingw-w64-${{ matrix.MSYS2_ARCH }}-gcc + mingw-w64-${{ matrix.MSYS2_ARCH }}-gcc-libs + mingw-w64-${{ matrix.MSYS2_ARCH }}-gettext + mingw-w64-${{ matrix.MSYS2_ARCH }}-glib2 + mingw-w64-${{ matrix.MSYS2_ARCH }}-gobject-introspection + mingw-w64-${{ matrix.MSYS2_ARCH }}-graphite2 + mingw-w64-${{ matrix.MSYS2_ARCH }}-icu + mingw-w64-${{ matrix.MSYS2_ARCH }}-meson + mingw-w64-${{ matrix.MSYS2_ARCH }}-ninja + mingw-w64-${{ matrix.MSYS2_ARCH }}-pkg-config + mingw-w64-${{ matrix.MSYS2_ARCH }}-python + mingw-w64-${{ matrix.MSYS2_ARCH }}-python-pip + mingw-w64-${{ matrix.MSYS2_ARCH }}-ragel + - name: Install Python Dependencies + run: | + pip install --upgrade fonttools + - name: Build + run: | + meson build \ + --wrap-mode=nodownload \ + --auto-features=enabled \ + -Ddirectwrite=enabled \ + -Dgdi=enabled \ + -Dgraphite=enabled \ + -Dchafa=disabled + ninja -C build + - name: Test + run: | + meson test \ + --print-errorlogs \ + --suite=harfbuzz \ + -C build diff --git a/libs/harfbuzz/AUTHORS b/libs/harfbuzz/AUTHORS new file mode 100644 index 00000000..83c0c66f --- /dev/null +++ b/libs/harfbuzz/AUTHORS @@ -0,0 +1,14 @@ +Behdad Esfahbod +David Corbett +David Turner +Ebrahim Byagowi +Garret Rieger +Jonathan Kew +Khaled Hosny +Lars Knoll +Martin Hosken +Owen Taylor +Roderick Sheeter +Roozbeh Pournader +Simon Hausmann +Werner Lemberg diff --git a/libs/harfbuzz/BUILD.md b/libs/harfbuzz/BUILD.md new file mode 100644 index 00000000..8e822738 --- /dev/null +++ b/libs/harfbuzz/BUILD.md @@ -0,0 +1,29 @@ +On Linux, install the development packages for FreeType, Cairo, and GLib. For +example, on Ubuntu / Debian, you would do: + +$ sudo apt-get install meson pkg-config ragel gtk-doc-tools gcc g++ libfreetype6-dev libglib2.0-dev libcairo2-dev + +whereas on Fedora, RHEL, CentOS, and other Red Hat based systems you would do: + +$ sudo dnf install meson pkgconfig gtk-doc gcc gcc-c++ freetype-devel glib2-devel cairo-devel + +and on ArchLinux and Manjaro: + +$ sudo pacman -Suy meson pkg-config ragel gcc freetype2 glib2 cairo + +then use meson to build the project like `meson build && meson test -Cbuild`. + +On macOS, `brew install pkg-config ragel gtk-doc freetype glib cairo meson` +then use meson like above. + +On Windows, meson can build the project like above if a working MSVC's cl.exe +(`vcvarsall.bat`) or gcc/clang is already on your path, and if you use +something like `meson build --wrap-mode=default` it fetches and compiles most +of the dependencies also. It is recommended to install CMake either manually +or via the Visual Studio installer when building with MSVC, using meson. + +Our CI configurations is also a good source of learning how to build HarfBuzz. + +There is also amalgam source provided with HarfBuzz which reduces whole process +of building HarfBuzz like `g++ src/harfbuzz.cc -fno-exceptions` but there is +not guarantee provided with buildability and reliability of features you get. diff --git a/libs/harfbuzz/CMakeLists.txt b/libs/harfbuzz/CMakeLists.txt new file mode 100644 index 00000000..ae3388d1 --- /dev/null +++ b/libs/harfbuzz/CMakeLists.txt @@ -0,0 +1,832 @@ +cmake_minimum_required(VERSION 3.12) +project(harfbuzz) + +message(WARN "HarfBuzz has a Meson port and tries to migrate all the other build systems to it, please consider using it as we might remove our cmake port soon.") + +## Limit framework build to Xcode generator +if (BUILD_FRAMEWORK) + # for a framework build on macOS, use: + # cmake -DBUILD_FRAMEWORK=ON -Bbuild -H. -GXcode && cmake --build build + if (NOT "${CMAKE_GENERATOR}" STREQUAL "Xcode") + message(FATAL_ERROR + "You should use Xcode generator with BUILD_FRAMEWORK enabled") + endif () + set (CMAKE_OSX_ARCHITECTURES "$(ARCHS_STANDARD_32_64_BIT)") + set (CMAKE_MACOSX_RPATH ON) + set (BUILD_SHARED_LIBS ON) +endif () + + +## Disallow in-source builds, as CMake generated make files can collide with autotools ones +if (NOT MSVC AND "${PROJECT_BINARY_DIR}" STREQUAL "${PROJECT_SOURCE_DIR}") + message(FATAL_ERROR + " +In-source builds are not permitted! Make a separate folder for" + " building, e.g.," + " + mkdir build; cd build; cmake .." + " +Before that, remove the files created by this failed run with" + " + rm -rf CMakeCache.txt CMakeFiles") +endif () + + +## HarfBuzz build configurations +option(HB_HAVE_FREETYPE "Enable freetype interop helpers" OFF) +option(HB_HAVE_GRAPHITE2 "Enable Graphite2 complementary shaper" OFF) +option(HB_HAVE_GLIB "Enable glib unicode functions" OFF) +option(HB_HAVE_ICU "Enable icu unicode functions" OFF) +if (TARGET freetype) + set (HB_HAVE_FREETYPE ON) + add_definitions(-DHAVE_FREETYPE=1) +endif () +if (APPLE) + option(HB_HAVE_CORETEXT "Enable CoreText shaper backend on macOS" ON) + set (CMAKE_MACOSX_RPATH ON) +endif () +if (WIN32) + option(HB_HAVE_UNISCRIBE "Enable Uniscribe shaper backend on Windows" OFF) + option(HB_HAVE_GDI "Enable GDI integration helpers on Windows" OFF) + option(HB_HAVE_DIRECTWRITE "Enable DirectWrite shaper backend on Windows" OFF) +endif () +option(HB_BUILD_UTILS "Build harfbuzz utils, needs cairo, freetype, and glib properly be installed" OFF) +if (HB_BUILD_UTILS) + set (HB_HAVE_GLIB ON) + set (HB_HAVE_FREETYPE ON) +endif () + +option(HB_BUILD_SUBSET "Build harfbuzz-subset" ON) + +option(HB_HAVE_GOBJECT "Enable GObject Bindings" OFF) +if (HB_HAVE_GOBJECT) + set (HB_HAVE_GLIB ON) +endif () + +option(HB_HAVE_INTROSPECTION "Enable building introspection (.gir/.typelib) files" OFF) +if (HB_HAVE_INTROSPECTION) + set (HB_HAVE_GOBJECT ON) + set (HB_HAVE_GLIB ON) +endif () + +include_directories(AFTER + ${PROJECT_SOURCE_DIR}/src + ${PROJECT_BINARY_DIR}/src +) + +# We need PYTHON_EXECUTABLE to be set for running the tests... +include (FindPythonInterp) + +## Functions and headers +include (CheckFunctionExists) +include (CheckIncludeFile) +include (CheckIncludeFiles) +macro (check_funcs) # Similar to AC_CHECK_FUNCS of autotools + foreach (func_name ${ARGN}) + string(TOUPPER ${func_name} definition_to_add) + check_function_exists(${func_name} HAVE_${definition_to_add}) + if (${HAVE_${definition_to_add}}) + add_definitions(-DHAVE_${definition_to_add}) + endif () + endforeach () +endmacro () +if (UNIX) + list(APPEND CMAKE_REQUIRED_LIBRARIES m) +endif () +check_funcs(atexit mprotect sysconf getpagesize mmap isatty) +check_include_file(unistd.h HAVE_UNISTD_H) +if (${HAVE_UNISTD_H}) + add_definitions(-DHAVE_UNISTD_H) +endif () +check_include_file(sys/mman.h HAVE_SYS_MMAN_H) +if (${HAVE_SYS_MMAN_H}) + add_definitions(-DHAVE_SYS_MMAN_H) +endif () +check_include_file(stdbool.h HAVE_STDBOOL_H) +if (${HAVE_STDBOOL_H}) + add_definitions(-DHAVE_STDBOOL_H) +endif () + +# These will be used while making pkg-config .pc files +set(PC_REQUIRES_PRIV "") +set(PC_LIBS_PRIV "") + +if (NOT MSVC) + set(THREADS_PREFER_PTHREAD_FLAG ON) + find_package(Threads) + if (CMAKE_USE_PTHREADS_INIT) + add_definitions("-DHAVE_PTHREAD") + list(APPEND THIRD_PARTY_LIBS Threads::Threads) + list(APPEND PC_LIBS_PRIV -pthread) + endif () +endif () + +if (MSVC) + add_definitions(-wd4244 -wd4267 -D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_WARNINGS) +endif () + + +## Detect if we are running inside a distribution or regular repository folder +# if (EXISTS "${PROJECT_SOURCE_DIR}/ChangeLog") +# # perhaps we are on dist directory +# set (IN_HB_DIST TRUE) +# #set (HB_VERSION_H "${PROJECT_SOURCE_DIR}/src/hb-version.h") +# endif () + + +## Extract variables from Makefile files +function (extract_make_variable variable makefile_source) + string(REGEX MATCH "${variable} = ([^$]+)\\$" temp "${makefile_source}") + string(REGEX MATCHALL "[^ \n\t\\]+" listVar "${CMAKE_MATCH_1}") + set (${variable} ${listVar} PARENT_SCOPE) +endfunction () + +# https://stackoverflow.com/a/27630120 +function (add_prefix_to_list var prefix) + set (listVar "") + foreach (f ${${var}}) + list(APPEND listVar "${prefix}${f}") + endforeach () + set (${var} "${listVar}" PARENT_SCOPE) +endfunction () + +file(READ ${PROJECT_SOURCE_DIR}/src/Makefile.sources SRCSOURCES) +file(READ ${PROJECT_SOURCE_DIR}/util/Makefile.sources UTILSOURCES) + +extract_make_variable(HB_BASE_headers ${SRCSOURCES}) +add_prefix_to_list(HB_BASE_headers "${PROJECT_SOURCE_DIR}/src/") + +extract_make_variable(HB_SUBSET_sources ${SRCSOURCES}) +add_prefix_to_list(HB_SUBSET_sources "${PROJECT_SOURCE_DIR}/src/") + +extract_make_variable(HB_SUBSET_headers ${SRCSOURCES}) +add_prefix_to_list(HB_SUBSET_headers "${PROJECT_SOURCE_DIR}/src/") + +extract_make_variable(HB_BASE_RAGEL_GENERATED_sources ${SRCSOURCES}) +#if (IN_HB_DIST) + add_prefix_to_list(HB_BASE_RAGEL_GENERATED_sources "${PROJECT_SOURCE_DIR}/src/") +#else () +# add_prefix_to_list(HB_BASE_RAGEL_GENERATED_sources "${PROJECT_BINARY_DIR}/src/") +#endif () + +extract_make_variable(HB_VIEW_sources ${UTILSOURCES}) +add_prefix_to_list(HB_VIEW_sources "${PROJECT_SOURCE_DIR}/util/") +extract_make_variable(HB_SHAPE_sources ${UTILSOURCES}) +add_prefix_to_list(HB_SHAPE_sources "${PROJECT_SOURCE_DIR}/util/") +extract_make_variable(HB_SUBSET_CLI_sources ${UTILSOURCES}) +add_prefix_to_list(HB_SUBSET_CLI_sources "${PROJECT_SOURCE_DIR}/util/") +extract_make_variable(HB_OT_SHAPE_CLOSURE_sources ${UTILSOURCES}) +add_prefix_to_list(HB_OT_SHAPE_CLOSURE_sources "${PROJECT_SOURCE_DIR}/util/") + + +file(READ configure.ac CONFIGUREAC) +string(REGEX MATCH "\\[(([0-9]+)\\.([0-9]+)\\.([0-9]+))\\]" HB_VERSION_MATCH ${CONFIGUREAC}) +set (HB_VERSION ${CMAKE_MATCH_1}) +set (HB_VERSION_MAJOR ${CMAKE_MATCH_2}) +set (HB_VERSION_MINOR ${CMAKE_MATCH_3}) +set (HB_VERSION_MICRO ${CMAKE_MATCH_4}) + +## Define sources and headers of the project +set (project_sources ${PROJECT_SOURCE_DIR}/src/harfbuzz.cc) # use amalgam source +set (subset_project_sources ${HB_SUBSET_sources}) +set (project_extra_sources) +set (project_headers ${HB_BASE_headers}) +set (subset_project_headers ${HB_SUBSET_headers}) + +## Find and include needed header folders and libraries +if (HB_HAVE_FREETYPE AND NOT TARGET freetype) + include (FindFreetype) + if (NOT FREETYPE_FOUND) + message(FATAL_ERROR "HB_HAVE_FREETYPE was set, but we failed to find it. Maybe add a CMAKE_PREFIX_PATH= to your Freetype2 install prefix") + endif () + + list(APPEND THIRD_PARTY_LIBS ${FREETYPE_LIBRARIES}) + include_directories(AFTER ${FREETYPE_INCLUDE_DIRS}) + add_definitions(-DHAVE_FREETYPE=1) + + list(APPEND project_headers ${PROJECT_SOURCE_DIR}/src/hb-ft.h) + + # So check_funcs can find its headers + set (CMAKE_REQUIRED_INCLUDES ${CMAKE_REQUIRED_INCLUDES} ${FREETYPE_INCLUDE_DIRS}) + set (CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} ${FREETYPE_LIBRARIES}) + + check_funcs(FT_Get_Var_Blend_Coordinates FT_Set_Var_Blend_Coordinates FT_Done_MM_Var) +endif () + +if (HB_HAVE_FREETYPE) + list(APPEND PC_REQUIRES_PRIV "freetype2 >= 12.0.6") +endif () + +if (HB_HAVE_GRAPHITE2) + add_definitions(-DHAVE_GRAPHITE2) + + find_path(GRAPHITE2_INCLUDE_DIR graphite2/Font.h) + find_library(GRAPHITE2_LIBRARY graphite2) + + include_directories(${GRAPHITE2_INCLUDE_DIR}) + + list(APPEND project_headers ${PROJECT_SOURCE_DIR}/src/hb-graphite2.h) + + list(APPEND THIRD_PARTY_LIBS ${GRAPHITE2_LIBRARY}) + + list(APPEND PC_REQUIRES_PRIV "graphite2 >= 1.2.0") + + mark_as_advanced(GRAPHITE2_INCLUDE_DIR GRAPHITE2_LIBRARY) +endif () + +if (HB_HAVE_GLIB) + add_definitions(-DHAVE_GLIB) + + # https://github.com/WebKit/webkit/blob/master/Source/cmake/FindGLIB.cmake + find_package(PkgConfig) + pkg_check_modules(PC_GLIB QUIET glib-2.0) + + find_library(GLIB_LIBRARIES NAMES glib-2.0 HINTS ${PC_GLIB_LIBDIR} ${PC_GLIB_LIBRARY_DIRS}) + find_path(GLIBCONFIG_INCLUDE_DIR NAMES glibconfig.h HINTS ${PC_LIBDIR} ${PC_LIBRARY_DIRS} ${PC_GLIB_INCLUDEDIR} ${PC_GLIB_INCLUDE_DIRS} PATH_SUFFIXES glib-2.0/include) + find_path(GLIB_INCLUDE_DIR NAMES glib.h HINTS ${PC_GLIB_INCLUDEDIR} ${PC_GLIB_INCLUDE_DIRS} PATH_SUFFIXES glib-2.0) + + include_directories(${GLIBCONFIG_INCLUDE_DIR} ${GLIB_INCLUDE_DIR}) + + list(APPEND project_headers ${PROJECT_SOURCE_DIR}/src/hb-glib.h) + + list(APPEND THIRD_PARTY_LIBS ${GLIB_LIBRARIES}) + + list(APPEND PC_REQUIRES_PRIV "glib-2.0 >= 2.19.1") + + mark_as_advanced(GLIB_LIBRARIES GLIBCONFIG_INCLUDE_DIR GLIB_INCLUDE_DIR) +endif () + +if (HB_HAVE_ICU) + add_definitions(-DHAVE_ICU) + + # https://github.com/WebKit/webkit/blob/fdd7733f2f30eab7fe096a9791f98c60f62f49c0/Source/cmake/FindICU.cmake + find_package(PkgConfig) + pkg_check_modules(PC_ICU QUIET icu-uc) + + find_path(ICU_INCLUDE_DIR NAMES unicode/utypes.h HINTS ${PC_ICU_INCLUDE_DIRS} ${PC_ICU_INCLUDEDIR}) + find_library(ICU_LIBRARY NAMES libicuuc cygicuuc cygicuuc32 icuuc HINTS ${PC_ICU_LIBRARY_DIRS} ${PC_ICU_LIBDIR}) + + include_directories(${ICU_INCLUDE_DIR}) + + list(APPEND project_headers ${PROJECT_SOURCE_DIR}/src/hb-icu.h) + + list(APPEND THIRD_PARTY_LIBS ${ICU_LIBRARY}) + + mark_as_advanced(ICU_INCLUDE_DIR ICU_LIBRARY) +endif () + +if (APPLE AND HB_HAVE_CORETEXT) + # Apple Advanced Typography + add_definitions(-DHAVE_CORETEXT) + + list(APPEND project_headers ${PROJECT_SOURCE_DIR}/src/hb-coretext.h) + + if (HB_IOS) + find_library(COREFOUNDATION CoreFoundation) + if (COREFOUNDATION) + list(APPEND THIRD_PARTY_LIBS ${COREFOUNDATION}) + list(APPEND PC_LIBS_PRIV "-framework CoreFoundation") + endif () + mark_as_advanced(COREFOUNDATION) + + find_library(CORETEXT CoreText) + if (CORETEXT) + list(APPEND THIRD_PARTY_LIBS ${CORETEXT}) + list(APPEND PC_LIBS_PRIV "-framework CoreText") + endif () + mark_as_advanced(CORETEXT) + + find_library(COREGRAPHICS CoreGraphics) + if (COREGRAPHICS) + list(APPEND THIRD_PARTY_LIBS ${COREGRAPHICS}) + list(APPEND PC_LIBS_PRIV "-framework CoreGraphics") + endif () + mark_as_advanced(COREGRAPHICS) + else () + find_library(APPLICATION_SERVICES_FRAMEWORK ApplicationServices) + if (APPLICATION_SERVICES_FRAMEWORK) + list(APPEND THIRD_PARTY_LIBS ${APPLICATION_SERVICES_FRAMEWORK}) + list(APPEND PC_LIBS_PRIV "-framework ApplicationServices") + endif () + + mark_as_advanced(APPLICATION_SERVICES_FRAMEWORK) + endif () +endif () + +if (WIN32 AND HB_HAVE_GDI) + add_definitions(-DHAVE_GDI) + list(APPEND project_headers ${PROJECT_SOURCE_DIR}/src/hb-gdi.h) + list(APPEND THIRD_PARTY_LIBS gdi32) + list(APPEND PC_LIBS_PRIV -lgdi32) +endif () + +if (WIN32 AND HB_HAVE_UNISCRIBE) + add_definitions(-DHAVE_UNISCRIBE) + list(APPEND project_headers ${PROJECT_SOURCE_DIR}/src/hb-uniscribe.h) + list(APPEND THIRD_PARTY_LIBS usp10 gdi32 rpcrt4) + list(APPEND PC_LIBS_PRIV -lusp10 -lgdi32 -lrpcrt4) +endif () + +if (WIN32 AND HB_HAVE_DIRECTWRITE) + if (CMAKE_VERSION VERSION_GREATER 3.12) + check_include_files("windows.h;dwrite_1.h" HAVE_DWRITE_1_H LANGUAGE CXX) + else () + check_include_files("windows.h;dwrite_1.h" HAVE_DWRITE_1_H) + endif () + if (NOT HAVE_DWRITE_1_H) + message(FATAL_ERROR "DirectWrite was enabled explicitly, but required header is missing") + endif () + add_definitions(-DHAVE_DIRECTWRITE) + list(APPEND project_headers ${PROJECT_SOURCE_DIR}/src/hb-directwrite.h) +endif () + +if (HB_HAVE_GOBJECT) + add_definitions(-DHAVE_GOBJECT) + include (FindPerl) + + # Use the hints from glib-2.0.pc to find glib-mkenums + find_package(PkgConfig) + pkg_check_modules(PC_GLIB QUIET glib-2.0) + find_program(GLIB_MKENUMS glib-mkenums + HINTS ${PC_glib_mkenums} + ) + set (GLIB_MKENUMS_CMD) + + if (WIN32 AND NOT MINGW) + # In Visual Studio builds, shebang lines are not supported + # in the standard cmd.exe shell that we use, so we need to + # first determine whether glib-mkenums is a Python or PERL + # script + execute_process(COMMAND "${PYTHON_EXECUTABLE}" "${GLIB_MKENUMS}" --version + RESULT_VARIABLE GLIB_MKENUMS_PYTHON + OUTPUT_QUIET ERROR_QUIET + ) + if (GLIB_MKENUMS_PYTHON EQUAL 0) + message("${GLIB_MKENUMS} is a Python script.") + set (GLIB_MKENUMS_CMD "${PYTHON_EXECUTABLE}" "${GLIB_MKENUMS}") + else () + execute_process(COMMAND "${PERL_EXECUTABLE}" "${GLIB_MKENUMS}" --version + RESULT_VARIABLE GLIB_MKENUMS_PERL + OUTPUT_QUIET ERROR_QUIET + ) + if (GLIB_MKENUMS_PERL EQUAL 0) + message("${GLIB_MKENUMS} is a PERL script.") + set (GLIB_MKENUMS_CMD "${PERL_EXECUTABLE}" "${GLIB_MKENUMS}") + endif () + if (NOT GLIB_MKENUMS_PERL EQUAL 0 AND NOT GLIB_MKENUMS_PYTHON EQUAL 0) + message(FATAL_ERROR "Unable to determine type of glib-mkenums script") + endif () + endif () + else () + set (GLIB_MKENUMS_CMD "${GLIB_MKENUMS}") + endif () + if (NOT GLIB_MKENUMS_CMD) + message(FATAL_ERROR "HB_HAVE_GOBJECT was set, but we failed to find glib-mkenums, which is required") + endif () + + pkg_check_modules(PC_GOBJECT QUIET gobject-2.0) + + find_library(GOBJECT_LIBRARIES NAMES gobject-2.0 HINTS ${PC_GLIB_LIBDIR} ${PC_GLIB_LIBRARY_DIRS}) + find_path(GOBJECT_INCLUDE_DIR NAMES glib-object.h HINTS ${PC_GLIB_INCLUDEDIR} ${PC_GLIB_INCLUDE_DIRS} PATH_SUFFIXES glib-2.0) + + include_directories(${GOBJECTCONFIG_INCLUDE_DIR} ${GOBJECT_INCLUDE_DIR}) + mark_as_advanced(GOBJECT_LIBRARIES GOBJECT_INCLUDE_DIR) + + list(APPEND hb_gobject_sources ${PROJECT_SOURCE_DIR}/src/hb-gobject-structs.cc) + list(APPEND hb_gobject_gen_sources + ${CMAKE_CURRENT_BINARY_DIR}/src/hb-gobject-enums.cc + ) + list(APPEND hb_gobject_structs_headers + ${PROJECT_SOURCE_DIR}/src/hb-gobject-structs.h + ) + list(APPEND hb_gobject_headers + ${PROJECT_SOURCE_DIR}/src/hb-gobject.h + ${hb_gobject_structs_headers} + ) + list(APPEND hb_gobject_gen_headers + ${CMAKE_CURRENT_BINARY_DIR}/src/hb-gobject-enums.h + ) + + add_custom_command( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/src/hb-gobject-enums.h + COMMAND ${GLIB_MKENUMS_CMD} + --template=${PROJECT_SOURCE_DIR}/src/hb-gobject-enums.h.tmpl + --identifier-prefix hb_ + --symbol-prefix hb_gobject + ${hb_gobject_structs_headers} + ${project_headers} + > ${CMAKE_CURRENT_BINARY_DIR}/src/hb-gobject-enums.h.tmp + COMMAND "${CMAKE_COMMAND}" + "-DENUM_INPUT_SRC=${CMAKE_CURRENT_BINARY_DIR}/src/hb-gobject-enums.h.tmp" + "-DENUM_OUTPUT_SRC=${CMAKE_CURRENT_BINARY_DIR}/src/hb-gobject-enums.h" + -P ${PROJECT_SOURCE_DIR}/replace-enum-strings.cmake + DEPENDS ${PROJECT_SOURCE_DIR}/src/hb-gobject-enums.h.tmpl + ${hb_gobject_header} + ${project_headers} + ) + + add_custom_command( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/src/hb-gobject-enums.cc + COMMAND ${GLIB_MKENUMS_CMD} + --template=${PROJECT_SOURCE_DIR}/src/hb-gobject-enums.cc.tmpl + --identifier-prefix hb_ + --symbol-prefix hb_gobject + ${hb_gobject_header} + ${project_headers} + > ${CMAKE_CURRENT_BINARY_DIR}/src/hb-gobject-enums.cc.tmp + COMMAND "${CMAKE_COMMAND}" + "-DENUM_INPUT_SRC=${CMAKE_CURRENT_BINARY_DIR}/src/hb-gobject-enums.cc.tmp" + "-DENUM_OUTPUT_SRC=${CMAKE_CURRENT_BINARY_DIR}/src/hb-gobject-enums.cc" + -P ${PROJECT_SOURCE_DIR}/replace-enum-strings.cmake + DEPENDS ${PROJECT_SOURCE_DIR}/src/hb-gobject-enums.cc.tmpl + ${CMAKE_CURRENT_BINARY_DIR}/src/hb-gobject-enums.h + ${hb_gobject_header} + ${project_headers} + ) +endif () + + +## Define harfbuzz library +add_library(harfbuzz ${project_sources} ${project_extra_sources} ${project_headers}) +target_link_libraries(harfbuzz ${THIRD_PARTY_LIBS}) +target_include_directories(harfbuzz PUBLIC + "$" + "$") +if (HB_HAVE_FREETYPE AND TARGET freetype) + target_link_libraries(harfbuzz freetype) +endif () + + +## Define harfbuzz-icu library +if (HB_HAVE_ICU) + add_library(harfbuzz-icu ${PROJECT_SOURCE_DIR}/src/hb-icu.cc ${PROJECT_SOURCE_DIR}/src/hb-icu.h) + add_dependencies(harfbuzz-icu harfbuzz) + target_link_libraries(harfbuzz-icu harfbuzz ${THIRD_PARTY_LIBS}) + + if (BUILD_SHARED_LIBS) + set_target_properties(harfbuzz harfbuzz-icu PROPERTIES VISIBILITY_INLINES_HIDDEN TRUE) + endif () +endif () + + +## Define harfbuzz-subset library +if (HB_BUILD_SUBSET) + add_library(harfbuzz-subset ${subset_project_sources} ${subset_project_headers}) + list(APPEND project_headers ${PROJECT_SOURCE_DIR}/src/hb-subset.h) + add_dependencies(harfbuzz-subset harfbuzz) + target_link_libraries(harfbuzz-subset harfbuzz ${THIRD_PARTY_LIBS}) + + if (BUILD_SHARED_LIBS) + set_target_properties(harfbuzz harfbuzz-subset PROPERTIES VISIBILITY_INLINES_HIDDEN TRUE) + endif () +endif () + +if (UNIX OR MINGW) + # Make symbols link locally + include (CheckCXXCompilerFlag) + CHECK_CXX_COMPILER_FLAG(-Bsymbolic-functions CXX_SUPPORTS_FLAG_BSYMB_FUNCS) + if (CXX_SUPPORTS_FLAG_BSYMB_FUNCS) + link_libraries(-Bsymbolic-functions) + endif () + + # As of CMake 3.0.0, the compiler id for Apple-provided Clang is now "AppleClang"; + # thus we use MATCHES instead of STREQUAL to include either regular Clang or AppleClang + if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + # Make sure we don't link to libstdc++ + set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti -fno-exceptions") + set (CMAKE_CXX_IMPLICIT_LINK_LIBRARIES "m") # libm + set (CMAKE_CXX_IMPLICIT_LINK_DIRECTORIES "") + set_target_properties(harfbuzz PROPERTIES LINKER_LANGUAGE C) + if (HB_BUILD_SUBSET) + set_target_properties(harfbuzz-subset PROPERTIES LINKER_LANGUAGE C) + endif () + + # No threadsafe statics as we do it ourselves + set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-threadsafe-statics") + endif () + + CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11) + if (COMPILER_SUPPORTS_CXX11) + set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") + else() + message(STATUS "The compiler ${CMAKE_CXX_COMPILER} has no C++11 support. Please use a different C++ compiler.") + endif() +endif () + + +## Define harfbuzz-gobject library +if (HB_HAVE_GOBJECT) + add_library(harfbuzz-gobject + ${hb_gobject_sources} + ${hb_gobject_gen_sources} + ${hb_gobject_headers} + ${hb_gobject_gen_headers} + ) + include_directories(BEFORE ${CMAKE_CURRENT_BINARY_DIR}/src) + add_dependencies(harfbuzz-gobject harfbuzz) + target_link_libraries(harfbuzz-gobject harfbuzz ${GOBJECT_LIBRARIES} ${THIRD_PARTY_LIBS}) + + if (BUILD_SHARED_LIBS) + set_target_properties(harfbuzz-gobject PROPERTIES VISIBILITY_INLINES_HIDDEN TRUE) + endif () +endif () + +if (BUILD_SHARED_LIBS AND WIN32 AND NOT MINGW) + add_definitions("-DHB_DLL_EXPORT") +endif () + +# On Windows, g-ir-scanner requires a DLL build in order for it to work +if (WIN32) + if (NOT BUILD_SHARED_LIBS) + message("Building introspection files on Windows requires BUILD_SHARED_LIBS to be enabled.") + set (HB_HAVE_INTROSPECTION OFF) + endif () +endif () + +if (HB_HAVE_INTROSPECTION) + find_package(PkgConfig) + pkg_check_modules(PC_GI QUIET gobject-introspection-1.0) + + find_program(G_IR_SCANNER g-ir-scanner + HINTS ${PC_g_ir_scanner} + ) + + find_program(G_IR_COMPILER g-ir-compiler + HINTS ${PC_g_ir_compiler} + ) + + if (WIN32 AND NOT MINGW) + # Note that since we already enable HB_HAVE_GOBJECT + # we would already have PYTHON_EXECUTABLE handy + set (G_IR_SCANNER_CMD "${PYTHON_EXECUTABLE}" "${G_IR_SCANNER}") + else () + set (G_IR_SCANNER_CMD "${G_IR_SCANNER}") + endif () + + # We need to account for the varying output directories + # when we build using Visual Studio projects + if ("${CMAKE_GENERATOR}" MATCHES "Visual Studio*") + set (hb_libpath "${CMAKE_CURRENT_BINARY_DIR}/$") + else () + set (hb_libpath "$") + endif () + + # Get the CFlags that we used to build HarfBuzz/HarfBuzz-GObject + set (hb_defines_cflags "") + foreach (hb_cflag ${hb_cflags}) + list(APPEND hb_defines_cflags "-D${hb_cflag}") + endforeach (hb_cflag) + + # Get the other dependent libraries we used to build HarfBuzz/HarfBuzz-GObject + set (extra_libs "") + foreach (extra_lib ${THIRD_PARTY_LIBS}) + # We don't want the .lib extension here... + string(REPLACE ".lib" "" extra_lib_stripped "${extra_lib}") + list(APPEND extra_libs "--extra-library=${extra_lib_stripped}") + endforeach () + + set (introspected_sources) + foreach (f + ${project_headers} + ${project_sources} + ${hb_gobject_gen_sources} + ${hb_gobject_gen_headers} + ${hb_gobject_sources} + ${hb_gobject_headers} + ) + if (WIN32) + # Nasty issue: We need to make drive letters lower case, + # otherwise g-ir-scanner won't like it and give us a bunch + # of invalid items and unresolved types... + STRING(SUBSTRING "${f}" 0 1 drive) + STRING(SUBSTRING "${f}" 1 -1 path) + if (drive MATCHES "[A-Z]") + STRING(TOLOWER ${drive} drive_lower) + list(APPEND introspected_sources "${drive_lower}${path}") + else () + list(APPEND introspected_sources "${f}") + endif () + else () + list(APPEND introspected_sources "${f}") + endif () + endforeach () + + file(REMOVE ${CMAKE_CURRENT_BINARY_DIR}/src/hb_gir_list) + foreach (s ${introspected_sources}) + file(APPEND ${CMAKE_CURRENT_BINARY_DIR}/src/hb_gir_list "${s}\n") + endforeach () + + # Finally, build the introspection files... + add_custom_command( + TARGET harfbuzz-gobject + POST_BUILD + COMMAND ${G_IR_SCANNER_CMD} + --warn-all --no-libtool --verbose + --namespace=HarfBuzz + --nsversion=0.0 + --symbol-prefix=hb + --symbol-prefix=hb_gobject + --identifier-prefix=hb_ + --include GObject-2.0 + --pkg-export=harfbuzz-gobject + --c-include=hb-gobject.h + --cflags-begin + -I${PROJECT_SOURCE_DIR}/src + -I${PROJECT_BINARY_DIR}/src + ${hb_includedir_cflags} + ${hb_defines_cflags} + -DHB_H + -DHB_H_IN + -DHB_OT_H + -DHB_OT_H_IN + -DHB_AAT_H + -DHB_AAT_H_IN + -DHB_GOBJECT_H + -DHB_GOBJECT_H_IN + -DHB_EXTERN= + --cflags-end + --library=harfbuzz-gobject + --library=harfbuzz + -L${hb_libpath} + ${extra_libs} + --filelist ${CMAKE_CURRENT_BINARY_DIR}/src/hb_gir_list + -o ${hb_libpath}/HarfBuzz-0.0.gir + DEPENDS harfbuzz-gobject harfbuzz ${CMAKE_CURRENT_BINARY_DIR}/src/hb_gir_list + ) + + add_custom_command( + TARGET harfbuzz-gobject + POST_BUILD + COMMAND "${G_IR_COMPILER}" + --verbose --debug + --includedir ${CMAKE_CURRENT_BINARY_DIR} + ${hb_libpath}/HarfBuzz-0.0.gir + -o ${hb_libpath}/HarfBuzz-0.0.typelib + DEPENDS ${hb_libpath}/HarfBuzz-0.0.gir harfbuzz-gobject + ) +endif () + + +## Additional framework build configs +if (BUILD_FRAMEWORK) + set (CMAKE_MACOSX_RPATH ON) + set_target_properties(harfbuzz PROPERTIES + FRAMEWORK TRUE + PUBLIC_HEADER "${project_headers}" + XCODE_ATTRIBUTE_INSTALL_PATH "@rpath" + ) + set (MACOSX_FRAMEWORK_IDENTIFIER "harfbuzz") + set (MACOSX_FRAMEWORK_SHORT_VERSION_STRING "${HB_VERSION}") + set (MACOSX_FRAMEWORK_BUNDLE_VERSION "${HB_VERSION}") +endif () + + +## Additional harfbuzz build artifacts +if (HB_BUILD_UTILS) + # https://github.com/WebKit/webkit/blob/master/Source/cmake/FindCairo.cmake + find_package(PkgConfig) + pkg_check_modules(PC_CAIRO QUIET cairo) + + find_path(CAIRO_INCLUDE_DIRS NAMES cairo.h HINTS ${PC_CAIRO_INCLUDEDIR} ${PC_CAIRO_INCLUDE_DIRS} PATH_SUFFIXES cairo) + find_library(CAIRO_LIBRARIESNAMES cairo HINTS ${PC_CAIRO_LIBDIR} ${PC_CAIRO_LIBRARY_DIRS}) + + add_definitions("-DPACKAGE_NAME=\"HarfBuzz\"") + add_definitions("-DPACKAGE_VERSION=\"${HB_VERSION}\"") + include_directories(${CAIRO_INCLUDE_DIRS}) + + add_executable(hb-view ${HB_VIEW_sources}) + target_link_libraries(hb-view harfbuzz ${CAIRO_LIBRARIESNAMES}) + + add_executable(hb-shape ${HB_SHAPE_sources}) + target_link_libraries(hb-shape harfbuzz) + + add_executable(hb-subset ${HB_SUBSET_CLI_sources}) + target_link_libraries(hb-subset harfbuzz harfbuzz-subset) + + add_executable(hb-ot-shape-closure ${HB_OT_SHAPE_CLOSURE_sources}) + target_link_libraries(hb-ot-shape-closure harfbuzz) + + mark_as_advanced(CAIRO_INCLUDE_DIRS CAIRO_LIBRARIESNAMES) +endif () + +## Install +include (GNUInstallDirs) + +if (NOT SKIP_INSTALL_HEADERS AND NOT SKIP_INSTALL_ALL) + install(FILES ${project_headers} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/harfbuzz) + if (HB_HAVE_GOBJECT) + install(FILES ${hb_gobject_headers} ${hb_gobject_gen_headers} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/harfbuzz) + endif () +endif () + +# get these variables in the required format +list(REMOVE_DUPLICATES PC_REQUIRES_PRIV) +string(REPLACE ";" ", " PC_REQUIRES_PRIV "${PC_REQUIRES_PRIV}") +list(REMOVE_DUPLICATES PC_LIBS_PRIV) +string(REPLACE ";" " " PC_LIBS_PRIV "${PC_LIBS_PRIV}") + +# Macro to write pkg-config .pc configuration files +macro ( make_pkgconfig_pc_file name ) + file(READ "${PROJECT_SOURCE_DIR}/src/${name}.pc.in" FSTR) + + string(REPLACE "%prefix%" "${CMAKE_INSTALL_PREFIX}" FSTR ${FSTR}) + string(REPLACE "%exec_prefix%" "\${prefix}" FSTR ${FSTR}) + + if (IS_ABSOLUTE "${CMAKE_INSTALL_INCLUDEDIR}") + string(REPLACE "%includedir%" "${CMAKE_INSTALL_INCLUDEDIR}" FSTR ${FSTR}) + else () + string(REPLACE "%includedir%" "\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}" FSTR ${FSTR}) + endif () + + if (IS_ABSOLUTE "${CMAKE_INSTALL_LIBDIR}") + string(REPLACE "%libdir%" "${CMAKE_INSTALL_LIBDIR}" FSTR ${FSTR}) + else () + string(REPLACE "%libdir%" "\${prefix}/${CMAKE_INSTALL_LIBDIR}" FSTR ${FSTR}) + endif () + + string(REPLACE "%VERSION%" "${HB_VERSION}" FSTR ${FSTR}) + string(REPLACE "%requires_private%" "${PC_REQUIRES_PRIV}" FSTR ${FSTR}) + string(REPLACE "%libs_private%" "${PC_LIBS_PRIV}" FSTR ${FSTR}) + + file(WRITE "${PROJECT_BINARY_DIR}/${name}.pc" ${FSTR}) + + install( + FILES "${PROJECT_BINARY_DIR}/${name}.pc" + DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig" + COMPONENT pkgconfig + ) +endmacro ( make_pkgconfig_pc_file ) + +if (NOT SKIP_INSTALL_LIBRARIES AND NOT SKIP_INSTALL_ALL) + install(TARGETS harfbuzz + EXPORT harfbuzzConfig + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + FRAMEWORK DESTINATION Library/Frameworks + ) + make_pkgconfig_pc_file("harfbuzz") + install(EXPORT harfbuzzConfig + NAMESPACE harfbuzz:: + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/harfbuzz + ) + if (HB_HAVE_ICU) + install(TARGETS harfbuzz-icu + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + FRAMEWORK DESTINATION Library/Frameworks + ) + make_pkgconfig_pc_file("harfbuzz-icu") + endif () + if (HB_BUILD_SUBSET) + install(TARGETS harfbuzz-subset + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + ) + make_pkgconfig_pc_file("harfbuzz-subset") + endif () + if (HB_BUILD_UTILS) + if (WIN32 AND BUILD_SHARED_LIBS) + install(TARGETS harfbuzz-subset + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + ) + endif () + install(TARGETS hb-view + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + ) + install(TARGETS hb-subset + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + ) + + install(TARGETS hb-shape + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + ) + + install(TARGETS hb-ot-shape-closure + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + ) + endif () + if (HB_HAVE_GOBJECT) + install(TARGETS harfbuzz-gobject + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + ) + make_pkgconfig_pc_file("harfbuzz-gobject") + if (HB_HAVE_INTROSPECTION) + if ("${CMAKE_GENERATOR}" MATCHES "Visual Studio*") + set (hb_libpath "${CMAKE_CURRENT_BINARY_DIR}/$") + else () + set (hb_libpath "$") + endif () + + install(FILES "${hb_libpath}/HarfBuzz-0.0.gir" + DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/gir-1.0 + ) + + install(FILES "${hb_libpath}/HarfBuzz-0.0.typelib" + DESTINATION ${CMAKE_INSTALL_LIBDIR}/girepository-1.0 + ) + endif () + endif () +endif () diff --git a/libs/harfbuzz/CONFIG.md b/libs/harfbuzz/CONFIG.md new file mode 100644 index 00000000..0faa359e --- /dev/null +++ b/libs/harfbuzz/CONFIG.md @@ -0,0 +1,158 @@ +# Configuring HarfBuzz + +Most of the time you will not need any custom configuration. The configuration +options provided by `meson` should be enough. In particular, if you just want +HarfBuzz library plus hb-shape / hb-view utilities, make sure FreeType and Cairo +are available and found during configuration. + +If you are building for distribution, you should more carefully consider whether +you need Glib, ICU, Graphite2, as well as CoreText / Uniscribe / DWrite. Make +sure the relevant ones are enabled. + +If you are building for custom environment (embedded, downloadable app, etc) +where you mostly just want to call `hb_shape()` and the binary size of the +resulting library is very important to you, the rest of this file guides you +through your options to disable features you may not need, in exchange for +binary size savings. + +## Compiler Options + +Make sure you build with your compiler's "optimize for size" option. On `gcc` +this is `-Os`, and can be enabled by passing `CXXFLAGS=-Os`. On clang there +is an even more extreme flag, `-Oz`. Meson also provides `--buildtype=minsize` +for more convenience. + +HarfBuzz heavily uses inline functions and the optimize-size flag can make the +library smaller by 20% or more. Moreover, sometimes, based on the target CPU, +the optimize-size builds perform *faster* as well, thanks to lower code +footprint and caching effects. So, definitely try that even if size is not +extremely tight but you have a huge application. For example, Chrome does +that. Note that this configuration also automatically enables certain internal +optimizations. Search for `HB_OPTIMIZE_SIZE` for details, if you are using +other compilers, or continue reading. + +Another compiler option to consider is "link-time optimization", also known as +'lto'. To enable that, feel free to use `-Db_lto=true` of meson. +This, also, can have a huge impact on the final size, 20% or more. + +Finally, if you are making a static library build or otherwise linking the +library into your app, make sure your linker removes unused functions. This +can be tricky and differ from environment to environment, but you definitely +want to make sure this happens. Otherwise, every unused public function will +be adding unneeded bytes to your binary. The following pointers might come +handy: + + * https://lwn.net/Articles/741494/ (all of the four-part series) + * https://elinux.org/images/2/2d/ELC2010-gc-sections_Denys_Vlasenko.pdf + +Combining the above three build options should already shrink your library a lot. +The rest of this file shows you ways to shrink the library even further at the +expense of removing functionality (that may not be needed). The remaining +options are all enabled by defining pre-processor macros, which can be done +via `CXXFLAGS` or `CPPFLAGS` similarly. + + +## Unicode-functions + +Access to Unicode data can be configured at compile time as well as run-time. +By default, HarfBuzz ships with its own compact subset of properties from +Unicode Character Database that it needs. This is a highly-optimized +implementation that depending on compile settings (optimize-size or not) +takes around ~40kb or ~60kb. Using this implementation (default) is highly +recommended, as HarfBuzz always ships with data from latest version of Unicode. +This implementation can be disabled by defining `HB_NO_UCD`. + +For example, if you are enabling ICU as a built-in option, or GLib, those +can provide Unicode data as well, so defining `HB_NO_UCD` might save you +space without reducing functionality (to the extent that the Unicode version +of those implementations is recent.) + +If, however, you provide your own Unicode data to HarfBuzz at run-time by +calling `hb_buffer_set_unicode_funcs` on every buffer you create, and you do +not rely on `hb_unicode_funcs_get_default()` results, you can disable the +internal implementation by defining both `HB_NO_UCD` and `HB_NO_UNICODE_FUNCS`. +The latter is needed to guard against accidentally building a library without +any default Unicode implementations. + + +## Font-functions + +Access to certain font functionalities can also be configured at run-time. By +default, HarfBuzz uses an efficient internal implementation of OpenType +functionality for this. This internal implementation is called `hb-ot-font`. +All newly-created `hb_font_t` objects by default use `hb-ot-font`. Using this +is highly recommended, and is what fonts use by default when they are created. + +Most embedded uses will probably use HarfBuzz with FreeType using `hb-ft.h`. +In that case, or if you otherwise provide those functions by calling +`hb_font_set_funcs()` on every font you create, you can disable `hb-ot-font` +without loss of functionality by defining `HB_NO_OT_FONT`. + + +## Shapers + +Most HarfBuzz clients use it for the main shaper, called "ot". However, it +is legitimate to want to compile HarfBuzz with only another backend, eg. +CoreText, for example for an iOS app. For that, you want `HB_NO_OT_SHAPE`. +If you are going down that route, check if you want `HB_NO_OT`. + +This is very rarely what you need. Make sure you understand exactly what you +are doing. + +Defining `HB_NO_FALLBACK_SHAPE` however is pretty harmless. That removes the +(unused) "fallback" shaper. This is defined by the `HB_TINY` profile already +(more below). + + +## Thread-safety + +By default HarfBuzz builds as a thread-safe library. The exception is that +the `HB_TINY` predefined configuration (more below) disables thread-safety. + +If you do *not* need thread-safety in the library (eg. you always call into +HarfBuzz from the same thread), you can disable thread-safety by defining +`HB_NO_MT`. As noted already, this is enabled by `HB_TINY`. + + +## Pre-defined configurations + +The [`hb-config.hh`](src/hb-config.hh) internal header supports three +pre-defined configurations as well grouping of various configuration options. +The pre-defined configurations are: + + * `HB_MINI`: Disables shaping of AAT as well as legacy fonts. Ie. it produces + a capable OpenType shaper only. + + * `HB_LEAN`: Disables various non-shaping functionality in the library, as well + as esoteric or rarely-used shaping features. See the definition for details. + + * `HB_TINY`: Enables both `HB_MINI` and `HB_LEAN` configurations, as well as + disabling thread-safety and debugging, and use even more size-optimized data + tables. + + +## Tailoring configuration + +Most of the time, one of the pre-defined configuration is exactly what one needs. +Sometimes, however, the pre-defined configuration cuts out features that might +be desired in the library. Unfortunately there is no quick way to undo those +configurations from the command-line. + +However, configuration can still be overridden from a file. To do that, add your +override instructions (mostly `undef` instructions) to a header file and define +the macro `HB_CONFIG_OVERRIDE_H` to the string containing to that header file's +name. HarfBuzz will then include that file at the appropriate place during +configuration. + +Up until HarfBuzz 3.1.2 the the configuration override header file's name was +fixed and called `config-override.h`, and was activated by defining the macro +`HAVE_CONFIG_OVERRIDE_H`. That still works. + + +## Notes + +Note that the config option `HB_NO_CFF`, which is enabled by `HB_LEAN` and +`HB_TINY` does *not* mean that the resulting library won't work with CFF fonts. +The library can shape valid CFF fonts just fine, with or without this option. +This option disables (among other things) the code to calculate glyph extents +for CFF fonts, which many clients might not need. diff --git a/libs/harfbuzz/COPYING b/libs/harfbuzz/COPYING new file mode 100644 index 00000000..48d1b30f --- /dev/null +++ b/libs/harfbuzz/COPYING @@ -0,0 +1,38 @@ +HarfBuzz is licensed under the so-called "Old MIT" license. Details follow. +For parts of HarfBuzz that are licensed under different licenses see individual +files names COPYING in subdirectories where applicable. + +Copyright © 2010,2011,2012,2013,2014,2015,2016,2017,2018,2019,2020 Google, Inc. +Copyright © 2018,2019,2020 Ebrahim Byagowi +Copyright © 2019,2020 Facebook, Inc. +Copyright © 2012 Mozilla Foundation +Copyright © 2011 Codethink Limited +Copyright © 2008,2010 Nokia Corporation and/or its subsidiary(-ies) +Copyright © 2009 Keith Stribley +Copyright © 2009 Martin Hosken and SIL International +Copyright © 2007 Chris Wilson +Copyright © 2005,2006,2020,2021 Behdad Esfahbod +Copyright © 2005 David Turner +Copyright © 2004,2007,2008,2009,2010 Red Hat, Inc. +Copyright © 1998-2004 David Turner and Werner Lemberg + +For full copyright notices consult the individual files in the package. + + +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. diff --git a/libs/harfbuzz/Makefile.am b/libs/harfbuzz/Makefile.am new file mode 100644 index 00000000..c14b4b7c --- /dev/null +++ b/libs/harfbuzz/Makefile.am @@ -0,0 +1,82 @@ +# Process this file with automake to produce Makefile.in + +NULL = + +ACLOCAL_AMFLAGS = -I m4 + +SUBDIRS = src util test perf docs + +EXTRA_DIST = \ + autogen.sh \ + harfbuzz.doap \ + README.md \ + README.python.md \ + BUILD.md \ + CONFIG.md \ + RELEASING.md \ + TESTING.md \ + CMakeLists.txt \ + replace-enum-strings.cmake \ + meson.build \ + meson_options.txt \ + subprojects/cairo.wrap \ + subprojects/freetype2.wrap \ + subprojects/glib.wrap \ + subprojects/google-benchmark.wrap \ + subprojects/ragel.wrap \ + subprojects/packagefiles/ragel/meson.build \ + mingw-configure.sh \ + $(NULL) + +MAINTAINERCLEANFILES = \ + $(GITIGNORE_MAINTAINERCLEANFILES_TOPLEVEL) \ + $(GITIGNORE_MAINTAINERCLEANFILES_M4_LIBTOOL) \ + $(GITIGNORE_MAINTAINERCLEANFILES_MAKEFILE_IN) \ + $(srcdir)/INSTALL \ + $(srcdir)/ChangeLog \ + $(srcdir)/gtk-doc.make \ + $(srcdir)/m4/gtk-doc.m4 \ + $(NULL) + + +# +# ChangeLog generation +# +CHANGELOG_RANGE = +ChangeLog: $(srcdir)/ChangeLog +$(srcdir)/ChangeLog: + $(AM_V_GEN) if test -d "$(top_srcdir)/.git"; then \ + (GIT_DIR=$(top_srcdir)/.git \ + $(GIT) log $(CHANGELOG_RANGE) --stat) > $@.tmp \ + && mv -f $@.tmp "$(srcdir)/ChangeLog" \ + || ($(RM) $@.tmp; \ + echo Failed to generate ChangeLog, your ChangeLog may be outdated >&2; \ + (test -f $@ || echo git-log is required to generate this file >> "$(srcdir)/$@")); \ + else \ + test -f $@ || \ + (echo A git checkout and git-log is required to generate ChangeLog >&2 && \ + echo A git checkout and git-log is required to generate this file >> "$(srcdir)/$@"); \ + fi +.PHONY: ChangeLog $(srcdir)/ChangeLog + + +# +# Release engineering +# + +DISTCHECK_CONFIGURE_FLAGS = \ + --enable-gtk-doc \ + --disable-doc-cross-references \ + --with-gobject \ + --enable-introspection \ + $(NULL) + +# TAR_OPTIONS is not set as env var for 'make dist'. How to fix that? +TAR_OPTIONS = --owner=0 --group=0 + +dist-hook: dist-clear-sticky-bits +# Clean up any sticky bits we may inherit from parent dir +dist-clear-sticky-bits: + chmod -R a-s $(distdir) + +-include $(top_srcdir)/git.mk diff --git a/libs/harfbuzz/NEWS b/libs/harfbuzz/NEWS new file mode 100644 index 00000000..8d4389fc --- /dev/null +++ b/libs/harfbuzz/NEWS @@ -0,0 +1,3019 @@ +Overview of changes leading to 5.3.1 +Wednesday, October 19, 2022 +==================================== +- Subsetter repacker fixes. (Garret Rieger) +- Adjust Grapheme clusters for Katakana voiced sound marks. (Behdad Esfahbod) +- New “hb-subset” option “--preprocess-face”. (Garret Rieger) + + +Overview of changes leading to 5.3.0 +Saturday, October 8, 2022 +"Women, Life, Freedom" #MahsaAmini +==================================== +- Don’t add glyphs from dropped MATH or COLR tables to the subset glyphs. + (Khaled Hosny) +- Map “rlig” to appropriate AAT feature selectors. (Jonathan Kew) +- Update USE data files to latest version. (David Corbett) +- Check “CBDT” extents first before outline tables, to help with fonts that + also include an empty “glyf” table. (Khaled Hosny) +- More work towards variable font instancing in the subsetter. (Qunxin Liu) +- Subsetter repacker improvements. (Garret Rieger) +- New API: ++hb_ot_layout_lookup_get_optical_bound() ++hb_face_builder_sort_tables() + + +Overview of changes leading to 5.2.0 +Saturday, September 17, 2022 +==================================== +- Fix regressions in hb-ft font functions for FT_Face’s with transformation + matrix. (Behdad Esfahbod) +- The experimental hb-repacker API now supports splitting several GPOS subtable + types when needed. (Garret Rieger) +- The HarfBuzz extensions to OpenType font format are now opt-in behind + build-time flags. (Behdad Esfahbod) +- The experimental hb-subset variable fonts instantiation API can now + instantiate more font tables and arbitrary axis locations. (Qunxin Liu) +- Unicode 15 support. (David Corbett) +- Various documentation improvements. (Behdad Esfahbod, Matthias Clasen) +- The hb-view command line tool now detects WezTerm inline images support. + (Wez Furlong) +- Fix FreeType and ICU dependency lookup with meson. (Xavier Claessens) + +- New API: ++HB_SCRIPT_KAWI ++HB_SCRIPT_NAG_MUNDARI + + +Overview of changes leading to 5.1.0 +Sunday, July 31, 2022 +==================================== +- More extensive buffer tracing messages. (Behdad Esfahbod) +- Fix hb-ft regression in bitmap fonts rendering. (Behdad Esfahbod) +- Support extension promotion of lookups in hb-subset-repacker. (Garret Rieger) +- A new HB_GLYPH_FLAG_SAFE_TO_INSERT_TATWEEL for scripts that use elongation + (e.g. Arabic) to signify where it is safe to insert tatweel glyph without + interrupting shaping. (Behdad Esfahbod) +- Add “--safe-to-insert-tatweel” to “hb-shape” tool. (Behdad Esfahbod) + +- New API ++HB_GLYPH_FLAG_SAFE_TO_INSERT_TATWEEL ++HB_BUFFER_FLAG_PRODUCE_SAFE_TO_INSERT_TATWEEL + + +Overview of changes leading to 5.0.1 +Saturday, July 23, 2022 +==================================== +- Fix version 2 “avar” table with hb-ft. (Behdad Esfahbod) + + +Overview of changes leading to 5.0.0 +Saturday, July 23, 2022 +==================================== +- Support fonts with more than 65535 glyphs in “GDEF”, “GSUB”, and “GPOS” + tables. This is part of https://github.com/be-fonts/boring-expansion-spec to + extend OpenType in a backward-compatible way. + (Behdad Esfahbod, Garret Rieger) +- Complete support for more than 65535 glyphs in “glyf” table that started in + 4.0.0 release. Part of boring-expansion-spec. (Behdad Esfahbod) +- Support version 2 of “avar” table. Part of boring-expansion-spec. + (Behdad Esfahbod) +- Fix mark attachment on multiple substitutions in some cases. + (Behdad Esfahbod) +- Fix application of “calt”, “rclt”, and “ccmp” features to better match + Uniscribe behaviour with some Arabic fonts. (Behdad Esfahbod) +- Improvement to interaction between multiple cursive attachments. + (Behdad Esfahbod) +- Improve multiple mark interactions in Hebrew. (Behdad Esfahbod) +- Implement language-specific forms in AAT shaping. (Behdad Esfahbod) +- Fix variation of “VORG” table. (Behdad Esfahbod) +- Support for specific script tags to be retained in the subsetter, and add + “--layout-scripts” option to “hb-subset” tool. (Garret Rieger) +- Accept space as delimiter for --features/--variations in command line tools. +- Improve subsetting of “COLR” table. (Qunxin Liu) +- Improved fuzzing coverage for ot-math API. (Frédéric Wang) +- Fix “kern” table version 2 (AAT) sanitization on 32-bit systems. + (Behdad Esfahbod) +- Allow negative glyph advances from “graphite2” shaper. (Stephan Bergmann) +- Implement loading (color) bitmap fonts with hb-ft. (Behdad Esfahbod) +- Fix regression in hb-ft when changing font size. (Behdad Esfahbod) +- Fix build on GCC < 7. (Kleis Auke Wolthuizen) +- Dynamically load dwrite.dll on windows if “directwrite” shaper is enabled. + (Luca Bacci) +- Provide a single-file harfbuzz-subset.cc file for easier alternate building + of hb-subset library, similar to harfbuzz.cc. (Khaled Hosny) + +- New API ++HB_SUBSET_SETS_LAYOUT_SCRIPT_TAG ++hb_language_matches() + + +Overview of changes leading to 4.4.1 +Wednesday, June 29, 2022 +==================================== +- Fix test failure with some compilers. +- Fix Telugu and Kannada kerning regression. + + +Overview of changes leading to 4.4.0 +Monday, June 27, 2022 +==================================== +- Caching of variable fonts shaping, in particular when using HarfBuzz’s own + font loading functions (ot). Bringing performance of variable shaping in par + with non-variable fonts shaping. (Behdad Esfahbod) +- Caching of format 2 “Contextual Substitution” and “Chained Contexts + Substitution” lookups. Resulting in up to 20% speedup of lookup-heavy fonts + like Gulzar or Noto Nastaliq Urdu. (Behdad Esfahbod) +- Improved ANSI output from hb-view. (Behdad Esfahbod) +- Support for shaping legacy, pre-OpenType Windows 3.1-era, Arabic fonts that + relied on a fixed PUA encoding. (Khaled Hosny, Behdad Esfahbod) +- Sinhala script is now shaped by the USE shaper instead of “indic” one. + (Behdad Esfahbod, David Corbett) +- Thai shaper improvements. (David Corbett) +- hb-ot-name API supports approximate BCP-47 language matching, for example + asking for “en_US” in a font that has only “en” names will return them. + (Behdad Esfahbod) +- Optimized TrueType glyph shape loading. (Behdad Esfahbod) +- Fix subsetting of HarfBuzz faces created via hb_face_create_for_tables(). + (Garret Rieger) +- Add 32 bit var store support to the subsetter. (Garret Rieger) + +- New API ++HB_BUFFER_FLAG_DEFINED ++HB_BUFFER_SERIALIZE_FLAG_DEFINED ++hb_font_changed() ++hb_font_get_serial() ++hb_ft_hb_font_changed() ++hb_set_hash() ++hb_map_copy() ++hb_map_hash() + + +Overview of changes leading to 4.3.0 +Friday, May 20, 2022 +==================================== +- Major speed up in loading and subsetting fonts, especially in + handling CFF table. Subsetting some fonts is now 3 times faster. + (Behdad Esfahbod, Garret Rieger) +- Speed up blending CFF2 table. (Behdad Esfahbod) +- Speed up hb_ot_tags_from_language(). (Behdad Esfahbod, David Corbett) +- Fix USE classification of U+10A38 to fix multiple marks on single Kharoshthi + base. (David Corbett) +- Fix parsing of empty CFF Index. (Behdad Esfahbod) +- Fix subsetting CPAL table with partial palette overlaps. (Garret Rieger) + +- New API ++hb_map_is_equal() (Behdad Esfahbod) + + +Overview of changes leading to 4.2.1 +Sunday, April 24, 2022 +==================================== +- Make sure hb_blob_create_from_file_or_fail() always returns nullptr in case + of failure and not empty blob sometimes. (Khaled Hosny) +- Add --passthrough-tables option to hb-subset. (Cosimo Lupo) +- Reinstate a pause after basic features in Khmer shaper, fixing a regression + introduced in previous release. (Behdad Esfahbod) +- Better handling of Regional_Indicator when shaped with RTL-native scripts, + reverting earlier fix that caused regressions in AAT shaping. (Behdad Esfahbod) + + +Overview of changes leading to 4.2.0 +Wednesday, March 30, 2022 +==================================== +- Source code reorganization, splitting large hb-ot-layout files into smaller, + per-subtable ones under OT/Layout/*. Code for more tables will follow suit in + later releases. (Garret Rieger, Behdad Esfahbod) +- Revert Indic shaper change in previous release that broke some fonts and + instead make per-syllable restriction of “GSUB” application limited to + script-specific Indic features, while applying them and discretionary + features in one go. (Behdad Esfahbod) +- Fix decoding of private in gvar table. (Behdad Esfahbod) +- Fix handling of contextual lookups that delete too many glyphs. (Behdad Esfahbod) +- Make “morx” deleted glyphs don’t block “GPOS” application. (Behdad Esfahbod) +- Various build fixes. (Chun-wei Fan, Khaled Hosny) + +- New API ++hb_set_next_many() (Andrew John) + + +Overview of changes leading to 4.1.0 +Wednesday, March 23, 2022 +==================================== +- Various OSS-Fuzz fixes. (Behdad Esfahbod) +- Make fallback vertical-origin match FreeType’s. (Behdad Esfahbod) +- Treat visible viramas like dependent vowels in USE shaper. (David Corbett) +- Apply presentation forms features and discretionary features in one go in + Indic shaper, which seems to match Uniscribe and CoreText behaviour. + (Behdad Esfahbod, David Corbett) +- Various bug fixes. + +- New API ++hb_set_add_sorted_array() (Andrew John) + + +Overview of changes leading to 4.0.1 +Friday, March 11, 2022 +==================================== +- Update OpenType to AAT mappings for “hist” and “vrtr” features. + (Florian Pircher) +- Update IANA Language Subtag Registry to 2022-03-02. (David Corbett) +- Update USE shaper to allow any non-numeric tail in a symbol cluster, and + remove obsolete data overrides. (David Corbett) +- Fix handling of baseline variations to return correctly scaled values. + (Matthias Clasen) +- A new experimental hb_subset_repack_or_fail() to repack an array of objects, + eliminating offset overflows. The API is not available unless HarfBuzz is + built with experimental APIs enabled. (Qunxin Liu) + +- New experimental API ++hb_link_t ++hb_object_t ++hb_subset_repack_or_fail() + + +Overview of changes leading to 4.0.0 +Tuesday, March 1, 2022 +==================================== +- New public API to create subset plan and gather information on things like + glyph mappings in the final subset. The plan can then be passed on to perform + the subsetting operation. (Garret Rieger) +- Draw API for extracting glyph shapes have been extended and finalized and is + no longer an experimental API. The draw API supports glyf, CFF and CFF2 + glyph outlines tables, and applies variation settings set on the font as well + as synthetic slant. The new public API is not backward compatible with the + previous, non-public, experimental API. (Behdad Esfahbod) +- The hb-view tool will use HarfBuzz draw API to render the glyphs instead of + cairo-ft when compiled with Cairo 1.17.5 or newer, setting HB_DRAW + environment variable to 1 or 0 will force using or not use the draw API, + respectively. (Behdad Esfahbod) +- The hb-shape and hb-view tools now default to using HarfBuzz’s own font + loading functions (ot) instead of FreeType ones (ft). They also have a new + option, --font-slant, to apply synthetic slant to the font. (Behdad Esfahbod) +- HarfBuzz now supports more than 65535 (the OpenType limit) glyph shapes and + metrics. See https://github.com/be-fonts/boring-expansion-spec/issues/6 and + https://github.com/be-fonts/boring-expansion-spec/issues/7 for details. + (Behdad Esfahbod) +- New API to get the dominant horizontal baseline tag for a given script. + (Behdad Esfahbod) +- New API to get the baseline positions from the font, and synthesize missing + ones. As well as new API to get font metrics and synthesize missing ones. + (Matthias Clasen) +- Improvements to finding dependencies on Windows when building with Visual + Studio. (Chun-wei Fan) +- New buffer flag, HB_BUFFER_FLAG_PRODUCE_UNSAFE_TO_CONCAT, that must be set + during shaping for HB_GLYPH_FLAG_UNSAFE_TO_CONCAT flag to be reliably + produced. This is to limit the performance hit of producing this flag to when + it is actually needed. (Behdad Esfahbod) +- Documentation improvements. (Matthias Clasen) + +- New API + - General: + +HB_BUFFER_FLAG_PRODUCE_UNSAFE_TO_CONCAT + +hb_var_num_t + + - Draw: + +hb_draw_funcs_t + +hb_draw_funcs_create() + +hb_draw_funcs_reference() + +hb_draw_funcs_destroy() + +hb_draw_funcs_is_immutable() + +hb_draw_funcs_make_immutable() + +hb_draw_move_to_func_t + +hb_draw_funcs_set_move_to_func() + +hb_draw_line_to_func_t + +hb_draw_funcs_set_line_to_func() + +hb_draw_quadratic_to_func_t + +hb_draw_funcs_set_quadratic_to_func() + +hb_draw_cubic_to_func_t + +hb_draw_funcs_set_cubic_to_func() + +hb_draw_close_path_func_t + +hb_draw_funcs_set_close_path_func() + +hb_draw_state_t + +HB_DRAW_STATE_DEFAULT + +hb_draw_move_to() + +hb_draw_line_to() + +hb_draw_quadratic_to() + +hb_draw_cubic_to() + +hb_draw_close_path() + +hb_font_get_glyph_shape_func_t + +hb_font_funcs_set_glyph_shape_func() + +hb_font_get_glyph_shape() + + - OpenType layout + +HB_OT_LAYOUT_BASELINE_TAG_IDEO_FACE_CENTRAL + +HB_OT_LAYOUT_BASELINE_TAG_IDEO_EMBOX_CENTRAL + +hb_ot_layout_get_horizontal_baseline_tag_for_script() + +hb_ot_layout_get_baseline_with_fallback() + + - Metrics: + +hb_ot_metrics_get_position_with_fallback() + + - Subset: + +hb_subset_plan_t + +hb_subset_plan_create_or_fail() + +hb_subset_plan_reference() + +hb_subset_plan_destroy() + +hb_subset_plan_set_user_data() + +hb_subset_plan_get_user_data() + +hb_subset_plan_execute_or_fail() + +hb_subset_plan_unicode_to_old_glyph_mapping() + +hb_subset_plan_new_to_old_glyph_mapping() + +hb_subset_plan_old_to_new_glyph_mapping() + + +Overview of changes leading to 3.4.0 +Sunday, February 13, 2022 +==================================== +- Perform sanity checks on shaping results is now part of “harfbuzz” library + and can be enabled by setting the buffer flag HB_BUFFER_FLAG_VERIFY. + (Behdad Esfahbod) +- Arabic Mark Transient Reordering Algorithm have been updated to revision 6. + (Khaled Hosny) +- ISO 15924 code for mathematical notation, ‘Zmth’, now maps to the OpenType + ‘math’ tag. (Alexis King) +- It is now possible to get at once all math kerning values for a given glyph + at a given corner. (Alexis King) +- Fix locale_t portability issues on systems the typedef’s it to a void + pointer. (Behdad Esfahbod) + +- New API: ++HB_BUFFER_FLAG_VERIFY ++HB_OT_TAG_MATH_SCRIPT ++HB_SCRIPT_MATH ++hb_ot_math_kern_entry_t ++hb_ot_math_get_glyph_kernings() + +- Deprecated API ++HB_OT_MATH_SCRIPT + + +Overview of changes leading to 3.3.2 +Sunday, February 6, 2022 +==================================== +- Revert splitting of pair positioning values introduced in 3.3.0 as it proved + problematic. (Behdad Esfahbod) + + +Overview of changes leading to 3.3.1 +Monday, January 31, 2022 +==================================== +- Fix heap-use-after-free in harfbuzz-subset introduced in previous release. + (Garret Rieger) + + +Overview of changes leading to 3.3.0 +Monday, January 31, 2022 +==================================== +- Improved documentation. (Matthias Clasen) +- Internal code cleanup, using C++ standard library more. (Behdad Esfahbod) +- The low 16-bits of face index will be used by hb_face_create() to select a + face inside a font collection file format, while the high 16-bits will be + used by hb_font_create() to load the named instance. (Behdad Esfahbod) +- Glyph positions and other font metrics now apply synthetic slant set by + hb_font_set_synthetic_slant(), for improved positioning for synthetically + slanted fonts. (Behdad Esfahbod) +- Fixed unintentional locale dependency in hb_variation_to_string() for decimal + point representation. (Matthias Clasen) +- When applying pair positioning (kerning) the positioning value is split + between the two sides of the pair for improved cursor positioning between + such pairs. (Behdad Esfahbod) +- Introduced new HB_GLYPH_FLAG_UNSAFE_TO_CONCAT, to be used in conjunction + with HB_GLYPH_FLAG_UNSAFE_TO_BREAK for optimizing re-shaping during line + breaking. Check the documentation for further details. (Behdad Esfahbod) +- Improved handling of macrolanguages when mapping BCP 47 codes to OpenType + tags. (David Corbett) + +- New API: ++HB_GLYPH_FLAG_UNSAFE_TO_CONCAT ++hb_segment_properties_overlay() ++hb_buffer_create_similar() ++hb_font_set_synthetic_slant() ++hb_font_get_synthetic_slant() ++hb_font_get_var_coords_design() + + +Overview of changes leading to 3.2.0 +Friday, November 26, 2021 +==================================== +“harfbuzz” library improvements: +- Fixed shaping of Apple Color Emoji flags in right-to-left context. (Behdad Esfahbod) +- Fixed positioning of CFF fonts in HB_TINY profile. (Behdad Esfahbod) +- OpenType 1.9 language tags update. (David Corbett) +- Add HB_NO_VERTICAL config option. +- Add HB_CONFIG_OVERRIDE_H for easier configuration. (Behdad Esfahbod) + +“harfbuzz-subset” library improvements: +- Improved packing of cmap, loca, and Ligature tables. (Garret Rieger) +- Significantly improved overflow-resolution strategy in the repacker. (Garret Rieger) + + +Overview of changes leading to 3.1.2 +Friday, November 26, 2021 +==================================== +- hb-shape / hb-view: revert treating text on the commandline as single + paragraph (was introduced in 3.0.0); add new --single-par to do that. + (Behdad Esfahbod) +- Subsetter bug fixes. (Garret Rieger, Qunxin Liu, Behdad Esfahbod) + + +Overview of changes leading to 3.1.1 +Wednesday, November 8, 2021 +==================================== +- Work around GCC cast-align error/warning on some platforms. (Behdad Esfahbod) +- Documentation improvements. (Matthias Clasen) + + +Overview of changes leading to 3.1.0 +Wednesday, November 3, 2021 +==================================== +- Better offset-overflow handling in the subsetter library. (Garret Rieger) +- Improved Unicode 14 properties in the USE shaper, and various other USE + shaper fixes. (David Corbett) +- MATH and COLR v1 tables subsetting support, and various other subsetter fixes. + (Qunxin Liu) +- Support for Pwo Karen / Ason Chin medial la. (Simon Cozens) +- Apply GPOS positioning when substituting with morx table, if kerx is missing. + (Behdad Esfahbod) +- Apply calt and clig features across syllable boundaries in Indic shaper. + (Behdad Esfahbod) +- meson option for enabling Graphite 2 has been renamed to graphite2. +- Build and documentation fixes. + +- New API: ++hb_buffer_set_not_found_glyph() ++hb_buffer_get_not_found_glyph() + + +Overview of changes leading to 3.0.0 +Friday, September 17, 2021 +==================================== +- Unicode 14.0 support (David Corbett). +- The hb-subset API and the harfbuzz-subset library's ABI are now declared + stable. The harfbuzz-subset library would not have been possible without the + work of Garret Rieger and Qunxin Liu from Google Fonts, and the earlier work + of Michiharu Ariza from Adobe. +- The hb-style API is now stable and no longer experimental. + +- New API: ++hb_style_tag_t ++hb_style_get_value() ++hb_subset_input_t ++hb_subset_flags_t ++hb_subset_sets_t ++hb_subset_input_create_or_fail() ++hb_subset_input_reference() ++hb_subset_input_destroy() ++hb_subset_input_set_user_data() ++hb_subset_input_get_user_data() ++hb_subset_input_unicode_set() ++hb_subset_input_glyph_set() ++hb_subset_input_set() ++hb_subset_input_get_flags() ++hb_subset_input_set_flags() ++hb_subset_or_fail() + +- Removed old unstable harfbuzz-subset API: +-hb_subset_input_nameid_set() +-hb_subset_input_namelangid_set() +-hb_subset_input_layout_features_set() +-hb_subset_input_no_subset_tables_set() +-hb_subset_input_drop_tables_set() +-hb_subset_input_set_drop_hints() +-hb_subset_input_get_drop_hints() +-hb_subset_input_set_desubroutinize() +-hb_subset_input_get_desubroutinize() +-hb_subset_input_set_retain_gids() +-hb_subset_input_get_retain_gids() +-hb_subset_input_set_name_legacy() +-hb_subset_input_get_name_legacy() +-hb_subset_input_set_overlaps_flag() +-hb_subset_input_get_overlaps_flag() +-hb_subset_input_set_notdef_outline() +-hb_subset_input_get_notdef_outline() +-hb_subset_input_set_no_prune_unicode_ranges() +-hb_subset_input_get_no_prune_unicode_ranges() +-hb_subset() + + +Overview of changes leading to 2.9.1 +Tuesday, September 7, 2021 +==================================== +- Final subset API is in place and if no issues are discovered, it will be the + stable subset API of HarfBuzz 3.0.0. Old API is kept to ease transition, but + will be removed in 3.0.0. +- Various fuzzer-found bug fixes. +- hb_buffer_append() now handles the pre- and post-context which previously + were left unchanged in the destination buffer. +- hb-view / hb-shape now accept following new arguments: + o --unicodes-before/after: takes a list of hex numbers that represent Unicode + codepoints. +- Undeprecated API: + hb_set_invert() + + +Overview of changes leading to 2.9.0 +Wednesday, August 18, 2021 +History Repeats Itself (Afghanistan) +==================================== +- Subsetter API is being stabilized, with the first stable API to happen in + 3.0.0 release (https://github.com/harfbuzz/harfbuzz/issues/3078). +- Support multiple variation axes with same tag, aka HOI. +- The “coretext” testing shaper now passes font variations to CoreText. +- hb-shape/hb-view does not break line at new lines unless text is read from + file. +- hb-view and hb-subset has a --batch now, similar to hb-shape. +- The --batch mode now uses ; as argument separator instead of : used previously. +- The --batch in hb-shape does not expect 0th argument anymore. That is, the + lines read are interpreted as argv[1:], instead of argv[0:]. +- The --batch option has been undocumented. We are ready to document it; send + feedback if you find it useful. +- hb-subset got arguments revamps. Added much-requested --gids-file, --glyphs, + --glyphs-file, --unicodes-file, supporting ranges in --unicodes. +- Various bug fixes. + + +Overview of changes leading to 2.8.2 +Tuesday, July 8, 2021 +==================================== +- Shaping LTR digits for RTL scripts now makes the native direction of the + digits LTR, applying shaping and positioning rules on the same glyph order as + Uniscribe. (Jonathan Kew, Khaled Hosny). +- Subsetting COLR v1 and CPAL tables is now supported. (Garret Rieger, Qunxin Liu) +- Various fixes and improvements to the subsetter. (Garret Rieger, Qunxin Liu, Behdad) +- When applying morx table, mark glyph widths should not be zeroed. (Jonathan Kew) +- GPOS is preferred over kerx, if GSUB was applied. (Behdad) +- Regional_Indicator pairs are grouped together when clustering. (Behdad) +- New API: ++hb_blob_create_or_fail() ++hb_blob_create_from_file_or_fail() ++hb_set_copy() + + +Overview of changes leading to 2.8.1 +Tuesday, May 4, 2021 +==================================== +- Subsetter now fully supports GSUB/GPOS/GDEF tables (including variations); as + such, layout tables are retained by subsetter by default. (Garret Rieger, Qunxin Liu) +- Build scripts no longer check for FontConfig as HarfBuzz does not use it. +- hb-view supports iTerm2 and kitty inline image protocols (Khaled Hosny), + it can also use Chafa for terminal graphics if available (Hans Petter Jansson). + +Overview of changes leading to 2.8.0 +Tuesday, March 16, 2021 +==================================== +- Shape joining scripts other than Arabic/Syriac using the Universal Shaping Engine. + Previously these were shaped using the generalized Arabic shaper. (David Corbett) +- Fix regression in shaping of U+0B55 ORIYA SIGN OVERLINE. (David Corbett) +- Update language tags. (David Corbett) +- Variations: reduce error: do not round each interpolated delta. (Just van Rossum) +- Documentation improvements. (Khaled Hosny, Nathan Willis) +- Subsetter improvements: subsets most, if not all, lookup types now. (Garret Rieger, Qunxin Liu) +- Fuzzer-found fixes and other improvements when memory failures happen. (Behdad) +- Removed most atomic implementations now that we have C++11 atomic impl. (Behdad) +- General codebase upkeep; using more C++11 features: constexpr constructors, etc. (Behdad) + + +Overview of changes leading to 2.7.4 +Sunday, December 27, 2020 +==================================== +- Fix missing --enable-introspection configure option from previous release + tarball. +- Documentation updates. + + +Overview of changes leading to 2.7.3 +Wednesday, December 23, 2020 +==================================== +- Update USE shaper to 2020-08-13 specification, and other improvements. +- Don’t disable liga feature in myanmar shaper, to match Uniscribe. +- Improvements to language and script tags handling. +- Update language system tag registry to OpenType 1.8.4 +- Support for serializing and deserializing Unicode buffers. Serialized buffers + are now delimited with `<>` or `[]` based on whether it is a Unicode or + glyphs buffer. +- Increase buffer work limits to handle fonts with many complex lookups. +- Handle more shaping operations in trace output. +- Memory access fixes. +- More OOM fixes. +- Improved documentation. +- Build system improvements. +- New API: ++hb_buffer_has_positions() ++hb_buffer_serialize() ++hb_buffer_serialize_unicode() ++hb_buffer_deserialize_unicode() + + +Overview of changes leading to 2.7.2 +Saturday, August 29, 2020 +==================================== +- Fix a regression in the previous release that caused a crash with Kaithi. +- More OOM fixes. + + +Overview of changes leading to 2.7.1 +Thursday, August 13, 2020 +==================================== +- ot-funcs now handles variable empty glyphs better when hvar/vvar isn't present. +- Reverted a GDEF processing regression. +- A couple of fixes to handle OOM better. + + +Overview of changes leading to 2.7.0 +Saturday, July 25, 2020 +==================================== +- Use an implementation for round that always rounds up, some minor fluctuations + are expected on var font specially when hb-ot callback is used. +- Fix an AAT's `kerx` issue on broken rendering of Devanagari Sangam MN. +- Remove AAT's `lcar` table support from _get_ligature_carets API, not even much + use on macOS installed fonts (only two files). GDEF support is the recommended + one and expected to work properly after issues fixed two releases ago. +- Minor memory fixes to handle OOM better specially in hb-ft. +- Minor .so files versioning scheme change and remove stable/unstable scheme + differences, was never used in practice (always default to stable scheme). +- We are now suggesting careful packaging of the library using meson, + https://github.com/harfbuzz/harfbuzz/wiki/Notes-on-migration-to-meson + for more information. +- Distribution package URL is changed, either use GitHub generated tarballs, + `https://github.com/harfbuzz/harfbuzz/archive/$pkgver.tar.gz` + or, even more preferably use commit hash of the release and git checkouts like, + `git+https://github.com/harfbuzz/harfbuzz#commit=$commit` + + +Overview of changes leading to 2.6.8 +Monday, June 22, 2020 +==================================== +- New API to fetch glyph alternates from GSUB table. +- hb-coretext build fix for macOS < 10.10. +- Meson build fixes, cmake port removal is postponed but please prepare for + it and give us feedback. + Autotools is still our main build system however please consider + experimenting with meson also for packaging the library. +- New API: ++hb_ot_layout_lookup_get_glyph_alternates() + + +Overview of changes leading to 2.6.7 +Wednesday, June 3, 2020 +==================================== +- Update to Unicode 13.0.0. +- Fix hb_ot_layout_get_ligature_carets for fonts without lcar table, it was + completely broken for all the other fonts since 2.1.2. +- As a part of our migration to meson, this release will be the last one + to provide cmake port files but autotools still is our main build system. + There is a possibility that the next version or the after be released + using meson. + + +Overview of changes leading to 2.6.6 +Tuesday, May 12, 2020 +==================================== +- A fix in AAT kerning for Geeza Pro. +- Better support for resource fork fonts on macOS. + + +Overview of changes leading to 2.6.5 +Friday, April 17, 2020 +==================================== +- Add experimental meson build system. Autotools is still the primary + and supported build system. +- AAT is now always preferred for horizontal scripts when both AAT and OT + layout tables exist at the same time. +- Subsetter improvements. +- New API: ++hb_ft_font_lock_face() ++hb_ft_font_unlock_face() + + +Overview of changes leading to 2.6.4 +Monday, October 29, 2019 +==================================== +- Small bug fix. +- Build fixes. + + +Overview of changes leading to 2.6.3 +Monday, October 28, 2019 +==================================== +- Misc small fixes, mostly to build-related issues. +- New API: ++hb_font_get_nominal_glyphs() + + +Overview of changes leading to 2.6.2 +Monday, September 30, 2019 +==================================== +- Misc small fixes, mostly to build-related issues. + + +Overview of changes leading to 2.6.1 +Thursday, August 22, 2019 +==================================== +- Fix regression with hb_font_create_sub_font scaling introduced in 2.6.0. +- Change interpretation of font PTEM size / CoreText font size handling. + See https://github.com/harfbuzz/harfbuzz/pull/1484 +- hb-ot-font: Prefer symbol cmap subtable if present. +- Apply 'dist'/'abvm'/'blwm' features to all scripts. +- Drop experimental DirectWrite API. + + +Overview of changes leading to 2.6.0 +Tuesday, August 13, 2019 +==================================== +- New OpenType metrics, baseline, and metadata table access APIs. +- New API to set font variations to a named-instance. +- New hb-gdi.h header and API for creating hb_face_t from HFONT. +- Amalgam: Provide a single-file harfbuzz.cc file for easier alternate building. +- More size-reduction configurable options, enabled by HB_TINY. +- New API: ++hb_font_set_var_named_instance() ++hb_gdi_face_create() ++hb_ot_layout_baseline_tag_t ++hb_ot_layout_get_baseline() ++hb_ot_meta_tag_t ++hb_ot_meta_get_entry_tags() ++hb_ot_meta_reference_entry() ++hb_ot_metrics_tag_t ++hb_ot_metrics_get_position() ++hb_ot_metrics_get_variation() ++hb_ot_metrics_get_x_variation() ++hb_ot_metrics_get_y_variation() + + +Overview of changes leading to 2.5.3 +Wednesday, June 26, 2019 +==================================== +- Fix UCD script data for Unicode 10+ scripts. This was broken since 2.5.0. +- More optimizations for HB_TINY. + + +Overview of changes leading to 2.5.2 +Thursday, June 20, 2019 +==================================== +- More hb-config.hh facilities to shrink library size, namely when built as + HB_TINY. +- New documentation of custom configurations in CONFIG.md. +- Fix build on gcc 4.8. That's supported again. +- Universal Shaping Engine improvements thanks to David Corbett. +- API Changes: Undeprecate some horizontal-kerning API and re-enable in hb-ft, + such that Type1 fonts will continue kerning. + + +Overview of changes leading to 2.5.1 +Friday, May 31, 2019 +==================================== +- Fix build with various versions of Visual Studio. +- Improved documentation, thanks to Nathan Willis. +- Bugfix in subsetting glyf table. +- Improved scripts for cross-compiling for Windows using mingw. +- Rename HB_MATH_GLYPH_PART_FLAG_EXTENDER to HB_OT_MATH_GLYPH_PART_FLAG_EXTENDER. + A deprecated macro is added for backwards-compatibility. + + +Overview of changes leading to 2.5.0 +Friday, May 24, 2019 +==================================== +- This release does not include much functional changes, but includes major internal + code-base changes. We now require C++11. Support for gcc 4.8 and earlier has been + dropped. +- New hb-config.hh facility for compiling smaller library for embedded and web usecases. +- New Unicode Character Database implementation that is half the size of previously-used + UCDN. +- Subsetter improvements. +- Improved documentation, thanks to Nathan Willis. +- Misc shaping fixes. + + +Overview of changes leading to 2.4.0 +Monday, March 25, 2019 +==================================== +- Unicode 12. +- Misc fixes. +- Subsetter improvements. +- New API: +HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE +hb_directwrite_face_create() + + +Overview of changes leading to 2.3.1 +Wednesday, January 30, 2019 +==================================== +- AAT bug fixes. +- Misc internal housekeeping cleanup. + + +Overview of changes leading to 2.3.0 +Thursday, December 20, 2018 +==================================== +- Fix regression on big-endian architectures. Ouch! +- Misc bug and build fixes. +- Fix subsetting of simple GSUB/GDEF. +- Merge CFF / CFF2 support contributed by Adobe. This mostly involves + the subsetter, but also get_glyph_extents on CFF fonts. + +New API in hb-aat.h: ++hb_aat_layout_has_substitution() ++hb_aat_layout_has_positioning() ++hb_aat_layout_has_tracking() + + +Overview of changes leading to 2.2.0 +Thursday, November 29, 2018 +==================================== +- Misc shaping bug fixes. +- Add font variations named-instance API. +- Deprecate font variations axis enumeration API and add replacement. +- AAT shaping improvements: + o Fixed 'kern' table Format 2 implementation. + o Implement 'feat' table API for feature detection. + o Blacklist 'GSUB' table of fonts from 'MUTF' foundry that also have 'morx'. + +New API: ++hb_aat_layout_feature_type_t ++hb_aat_layout_feature_selector_t ++hb_aat_layout_get_feature_types() ++hb_aat_layout_feature_type_get_name_id ++hb_aat_layout_feature_selector_info_t ++HB_AAT_LAYOUT_NO_SELECTOR_INDEX ++hb_aat_layout_feature_type_get_selector_infos() ++hb_ot_var_axis_flags_t ++hb_ot_var_axis_info_t ++hb_ot_var_get_axis_infos() ++hb_ot_var_find_axis_info() ++hb_ot_var_get_named_instance_count() ++hb_ot_var_named_instance_get_subfamily_name_id() ++hb_ot_var_named_instance_get_postscript_name_id() ++hb_ot_var_named_instance_get_design_coords() + +Deprecated API: ++HB_OT_VAR_NO_AXIS_INDEX ++hb_ot_var_axis_t ++hb_ot_var_get_axes() ++hb_ot_var_find_axis() + + +Overview of changes leading to 2.1.3 +Friday, November 16, 2018 +==================================== +- Fix AAT 'mort' shaping, which was broken in 2.1.2 + + +Overview of changes leading to 2.1.2 +Friday, November 16, 2018 +==================================== +- Various internal changes. +- AAT shaping improvements: + o Implement kern table Format 1 state-machine-based kerning. + o Implement cross-stream kerning (cursive positioning, etc). + o Ignore emptyish GSUB tables (zero scripts) if morx present. + o Don't apply GPOS if morx is being applied. Matches Apple. + + +-Overview of changes leading to 2.1.1 +Monday, November 5, 2018 +==================================== +- AAT improvements: + o Implement 'mort' table. + o Implement 'kern' subtables Format 1 and Format 3. + + +Overview of changes leading to 2.1.0 +Tuesday, October 30, 2018 +==================================== +- AAT shaping improvements: + o Allow user controlling AAT features, for whole buffer only currently. + o Several 'morx' fixes. + o Implement tuple-kerns in 'kerx'; Fixes kerning with Apple default + San Francisco fonts. +- Support for color fonts: + o COLR/CPAL API to fetch color layers. + o SVG table to fetch SVG documents. + o CBDT/sbix API to fetch PNG images. +- New 'name' table API. +- hb-ot-font now uses 'VORG' table to correctly position CFF glyphs + in vertical layout. +- Various fuzzer-found bug fixes. + +Changed API: + +A type and a macro added in 2.0.0 were renamed: + +hb_name_id_t -> hb_ot_name_id_t +HB_NAME_ID_INVALID -> HB_OT_NAME_ID_INVALID + +New API: + ++hb_color_t ++HB_COLOR ++hb_color_get_alpha() ++hb_color_get_red() ++hb_color_get_green() ++hb_color_get_blue() ++hb_ot_color_has_palettes() ++hb_ot_color_palette_get_count() ++hb_ot_color_palette_get_name_id() ++hb_ot_color_palette_color_get_name_id() ++hb_ot_color_palette_flags_t ++hb_ot_color_palette_get_flags() ++hb_ot_color_palette_get_colors() ++hb_ot_color_has_layers() ++hb_ot_color_layer_t ++hb_ot_color_glyph_get_layers() ++hb_ot_color_has_svg() ++hb_ot_color_glyph_reference_svg() ++hb_ot_color_has_png() ++hb_ot_color_glyph_reference_png() + ++hb_ot_name_id_t ++HB_OT_NAME_ID_INVALID ++HB_OT_NAME_ID_COPYRIGHT ++HB_OT_NAME_ID_FONT_FAMILY ++HB_OT_NAME_ID_FONT_SUBFAMILY ++HB_OT_NAME_ID_UNIQUE_ID ++HB_OT_NAME_ID_FULL_NAME ++HB_OT_NAME_ID_VERSION_STRING ++HB_OT_NAME_ID_POSTSCRIPT_NAME ++HB_OT_NAME_ID_TRADEMARK ++HB_OT_NAME_ID_MANUFACTURER ++HB_OT_NAME_ID_DESIGNER ++HB_OT_NAME_ID_DESCRIPTION ++HB_OT_NAME_ID_VENDOR_URL ++HB_OT_NAME_ID_DESIGNER_URL ++HB_OT_NAME_ID_LICENSE ++HB_OT_NAME_ID_LICENSE_URL ++HB_OT_NAME_ID_TYPOGRAPHIC_FAMILY ++HB_OT_NAME_ID_TYPOGRAPHIC_SUBFAMILY ++HB_OT_NAME_ID_MAC_FULL_NAME ++HB_OT_NAME_ID_SAMPLE_TEXT ++HB_OT_NAME_ID_CID_FINDFONT_NAME ++HB_OT_NAME_ID_WWS_FAMILY ++HB_OT_NAME_ID_WWS_SUBFAMILY ++HB_OT_NAME_ID_LIGHT_BACKGROUND ++HB_OT_NAME_ID_DARK_BACKGROUND ++HB_OT_NAME_ID_VARIATIONS_PS_PREFIX ++hb_ot_name_entry_t ++hb_ot_name_list_names() ++hb_ot_name_get_utf8() ++hb_ot_name_get_utf16() ++hb_ot_name_get_utf32() + + +Overview of changes leading to 2.0.2 +Saturday, October 20, 2018 +==================================== +- Fix two minor memory access issues in AAT tables. + + +Overview of changes leading to 2.0.1 +Friday, October 19, 2018 +==================================== +- Fix hb-version.h reported release version that went wrong (1.8.0) + with previous release. +- Fix extrapolation in 'trak' table. +- Fix hb-font infinite-recursion issue with some font funcs and + subclassed fonts. +- Implement variation-kerning format in kerx table, although without + variation. +- Fix return value of hb_map_is_empty(). + + +Overview of changes leading to 2.0.0 +Thursday, October 18, 2018 +==================================== +- Added AAT shaping support (morx/kerx/trak). + Automatically used if GSUB/GPOS are not available respectively. + Set HB_OPTIONS=aat env var to have morx/kerx preferred over + GSUB/GPOS. +- Apply TrueType kern table internally, instead of relying on + hb_font_t callbacks. +- Khmer shaper significantly rewritten to better match Uniscribe. +- Indic3 tags ('dev3', etc) are passed to USE shaper. +- .dfont Mac font containers implemented. +- Script- and language-mapping revamped to better use BCP 47. +- Misc USE and Indic fixes. +- Misc everything fixes. +- Too many things to list. Biggest release since 0.9.1, with + over 500 commits in just over 5 weeks! Didn't intend it to + be a big release. Just happened to become. +- hb-ft now locks underlying FT_Face during use. + +API changes: + +- Newly-created hb_font_t's now have our internal "hb-ot-font" + callbacks set on them, so they should work out of the box + without any callbacks set. If callbacks are set, everything + is back to what it was before, the fallback callbacks are + null. If you to get the internal implementation modified, + sub_font it. + +- New hb_font_funcs_set_nominal_glyphs_func() allows speeding + up character to glyph mapping. + +New API: ++HB_FEATURE_GLOBAL_START ++HB_FEATURE_GLOBAL_END ++hb_buffer_set_invisible_glyph() ++hb_buffer_get_invisible_glyph() ++hb_font_funcs_set_nominal_glyphs_func() ++hb_ot_layout_table_select_script() ++hb_ot_layout_script_select_language() ++hb_ot_layout_feature_get_name_ids() ++hb_ot_layout_feature_get_characters() ++hb_name_id_t ++HB_NAME_ID_INVALID ++HB_OT_MAX_TAGS_PER_SCRIPT ++hb_ot_tags_from_script_and_language() ++hb_ot_tags_to_script_and_language() + +Deprecated API: +-hb_font_funcs_set_glyph_func() +-hb_unicode_eastasian_width_func_t +-hb_unicode_funcs_set_eastasian_width_func() +-hb_unicode_eastasian_width() +-hb_unicode_decompose_compatibility_func_t +-HB_UNICODE_MAX_DECOMPOSITION_LEN +-hb_unicode_funcs_set_decompose_compatibility_func() +-hb_unicode_decompose_compatibility() +-hb_font_funcs_set_glyph_h_kerning_func() +-hb_font_funcs_set_glyph_v_kerning_func() +-hb_font_get_glyph_h_kerning() +-hb_font_get_glyph_v_kerning() +-hb_font_get_glyph_kerning_for_direction() +-hb_ot_layout_table_choose_script() +-hb_ot_layout_script_find_language() +-hb_ot_tags_from_script() +-hb_ot_tag_from_language() + + +Overview of changes leading to 1.9.0 +Monday, September 10, 2018 +==================================== +- Added 'cmap' API to hb_face_t. +- Face-builder API. +- hb-ot-font re-creation should be much leaner now, as the + font tables it uses are cached on hb_face_t now. +- Internal source header file name changes: + hb-*-private.hh is renamed to hb-*.hh. + +New API: ++HB_UNICODE_MAX ++hb_face_collect_unicodes() ++hb_face_collect_variation_selectors() ++hb_face_collect_variation_unicodes() ++hb_face_builder_create() ++hb_face_builder_add_table() + + +Overview of changes leading to 1.8.8 +Tuesday, August 14, 2018 +==================================== +- Fix hb-icu crash on architectures where compare_exchange_weak() can + fail falsely. This bug was introduced in 1.8.4. + https://bugs.chromium.org/p/chromium/issues/detail?id=873568 +- More internal refactoring of atomic operations and singletons. +- API changes: + The following functions do NOT reference their return value before + returning: + * hb_unicode_funcs_get_default() + * hb_glib_get_unicode_funcs() + * hb_icu_get_unicode_funcs() + This is consistent with their naming ("get", instead of "reference") + as well as how they are used in the wild (ie. no one calls destroy() + on their return value.) + + +Overview of changes leading to 1.8.7 +Wednesday, August 8, 2018 +==================================== +- Fix assertion failure with GDEF-blacklisted fonts. + + +Overview of changes leading to 1.8.6 +Tuesday, August 7, 2018 +==================================== +- Internal code shuffling. +- New API to speed up getting advance widths for implementations + that have heavy overhead in get_h_advance callback: ++hb_font_funcs_set_glyph_h_advances_func ++hb_font_funcs_set_glyph_v_advances_func ++hb_font_get_glyph_advances_for_direction ++hb_font_get_glyph_h_advances ++hb_font_get_glyph_h_advances_func_t ++hb_font_get_glyph_v_advances ++hb_font_get_glyph_v_advances_func_t + + +Overview of changes leading to 1.8.5 +Wednesday, August 1, 2018 +==================================== +- Major Khmer shaper improvements to better match Microsoft. +- Indic bug fixes. +- Internal improvements to atomic operations. + + +Overview of changes leading to 1.8.4 +Tuesday, July 17, 2018 +==================================== +- Fix build on non-C++11. +- Use C++-style GCC atomics and C++11 atomics. + + +Overview of changes leading to 1.8.3 +Wednesday, July 11, 2018 +==================================== +- A couple of Indic / USE bug fixes. +- Disable vectorization, as it was causing unaligned access bus error on + certain 32bit architectures. + + +Overview of changes leading to 1.8.2 +Tuesday, July 3, 2018 +==================================== +- Fix infinite loop in Khmer shaper. +- Improve hb_blob_create_from_file() for streams. + + +Overview of changes leading to 1.8.1 +Tuesday, June 12, 2018 +==================================== +- Fix hb-version.h file generation; last two releases went out with wrong ones. +- Add correctness bug in hb_set_t operations, introduced in 1.7.7. +- Remove HB_SUBSET_BUILTIN build option. Not necessary. + + +Overview of changes leading to 1.8.0 +Tuesday, June 5, 2018 +==================================== +- Update to Unicode 11.0.0. + + +Overview of changes leading to 1.7.7 +Tuesday, June 5, 2018 +==================================== +- Lots of internal changes, but not yet exposed externally. +- All HarfBuzz objects are significantly smaller in size now. +- Sinhala: Position repha on top of post-consonant, not base. + This better matches Windows 10 behavior, which was changed + from previous Windows versions. +- New build options: + o New cpp macro HB_NO_ATEXIT + o New cpp macro HB_SUBSET_BUILTIN +- Significant libharfbuzz-subset changes. API subject to change. +- New API in libharfbuzz: + ++hb_blob_create_from_file() ++hb_face_count() + +A hashmap implementation: ++hb-map.h ++HB_MAP_VALUE_INVALID ++hb_map_t ++hb_map_create() ++hb_map_get_empty() ++hb_map_reference() ++hb_map_destroy() ++hb_map_set_user_data() ++hb_map_get_user_data() ++hb_map_allocation_successful() ++hb_map_clear() ++hb_map_is_empty() ++hb_map_get_population() ++hb_map_set() ++hb_map_get() ++hb_map_del() ++hb_map_has() + + +Overview of changes leading to 1.7.6 +Wednesday, March 7, 2018 +==================================== + +- Fix to hb_set_t binary operations. Ouch. +- New experimental harfbuzz-subset library. All of hb-subset.h + is experimental right now and API WILL change. + +- New API: +hb_blob_copy_writable_or_fail() +HB_OT_TAG_BASE +hb_set_previous() +hb_set_previous_range() + + +Overview of changes leading to 1.7.5 +Tuesday, January 30, 2018 +==================================== + +- Separate Khmer shaper from Indic. +- First stab at AAT morx. Not hooked up. +- Misc bug fixes. + + +Overview of changes leading to 1.7.4 +Wednesday, December 20, 2017 +==================================== + +- Fix collect_glyphs() regression caused by hb_set_t changes. + + +Overview of changes leading to 1.7.3 +Monday, December 18, 2017 +==================================== + +- hb_set_t performance tuning and optimizations. +- Speed up collect_glyphs() and reject garbage data. +- In hb_coretext_font_create() set font point-size (ptem). +- Misc fixes. + + +Overview of changes leading to 1.7.2 +Monday, December 4, 2017 +==================================== + +- Optimize hb_set_add_range(). +- Misc fixes. +- New API: +hb_coretext_font_create() + + +Overview of changes leading to 1.7.1 +Tuesday, November 14, 2017 +==================================== + +- Fix atexit object destruction regression. +- Fix minor integer-overflow. + + +Overview of changes leading to 1.7.0 +Monday, November 13, 2017 +==================================== + +- Minor Indic fixes. +- Implement kerning and glyph names in hb-ot-font. +- Various DSO optimization re .data and .bss sizes. +- Make C++11 optional; build fixes. +- Mark all other backends "unsafe-to-break". +- Graphite fix. + + +Overview of changes leading to 1.6.3 +Thursday, October 26th, 2017 +==================================== + +- Fix hb_set_t some more. Should be solid now. +- Implement get_glyph_name() for hb-ot-font. +- Misc fixes. + + +Overview of changes leading to 1.6.2 +Monday, October 23nd, 2017 +==================================== + +- Yesterday's release had a bad crasher; don't use it. That's what + happens when one works on Sunday... + https://github.com/harfbuzz/harfbuzz/issues/578 +- Build fixes for FreeBSD and Chrome Android. + + +Overview of changes leading to 1.6.1 +Sunday, October 22nd, 2017 +==================================== + +- Don't skip over COMBINING GRAPHEME JOINER when ligating, etc. + To be refined: https://github.com/harfbuzz/harfbuzz/issues/554 +- Faster hb_set_t implementation. +- Don't use deprecated ICU API. +- Fix undefined-behavior in Myanmar shaper, introduced in 1.6.0 +- Deprecated API: + hb_set_invert() + + +Overview of changes leading to 1.6.0 +Friday, October the 13th, 2017 +==================================== + +- Update to Unicode 10. + +- Various Indic and Universal Shaping Engine fixes as a result of + HarfBuzz Hackfest with Jonathan Kew at Web Engines Hackfest at + the Igalia offices in A Coruña, Spain. Thanks Igalia for having + us! + +- Implement Unicode Arabic Mark Ordering Algorithm UTR#53. + +- Implement optical sizing / tracking in CoreText backend, using + new API hb_font_set_ptem(). + +- Allow notifying hb_font_t that underlying FT_Face changed sizing, + using new API hb_ft_font_changed(). + +- More Graphite backend RTL fixes. + +- Fix caching of variable font shaping plans. + +- hb-view / hb-shape now accept following new arguments: + + o --unicodes: takes a list of hex numbers that represent Unicode + codepoints. + +New API: ++hb_face_get_table_tags() ++hb_font_set_ptem() ++hb_font_get_ptem() ++hb_ft_font_changed() + + +Overview of changes leading to 1.5.1 +Tuesday, September 5, 2017 +==================================== + +- Fix "unsafe-to-break" in fallback shaping and other corner cases. + All our tests pass with --verify now, meaning unsafe-to-break API + works as expected. +- Add --unicodes to hb-view / hb-shape. +- [indic] Treat Consonant_With_Stacker as consonant. This will need + further tweaking. +- hb_buffer_diff() tweaks. + + +Overview of changes leading to 1.5.0 +Wednesday, August 23, 2017 +==================================== + +- Misc new API, for appending a buffer to another, and for comparing + contents of two buffers for types of differences. + +- New "unsafe-to-break" API. Can be used to speed up reshaping + in line-breaking situations. Essentially, after shaping, it returns + positions in the input string (some of the cluster boundaries) that + are "safe to break" in that if the text is segmented at that position + and two sides reshaped and concatenated, the shaping result is + exactly the same as shaping the text in one piece. + + hb-view and hb-shape and hb-shape now take --verify, which verifies + the above property. + + Some corner cases of the implementation are still not quite working. + Those will be fixed in subsequent releases. + +- New API: + +hb_buffer_append() + +hb_glyph_flags_t +HB_GLYPH_FLAG_UNSAFE_TO_BREAK +HB_GLYPH_FLAG_DEFINED +hb_glyph_info_get_glyph_flags() + +HB_BUFFER_SERIALIZE_FLAG_GLYPH_FLAGS + +hb_buffer_diff_flags_t +HB_BUFFER_DIFF_FLAG_EQUAL +HB_BUFFER_DIFF_FLAG_CONTENT_TYPE_MISMATCH +HB_BUFFER_DIFF_FLAG_LENGTH_MISMATCH +HB_BUFFER_DIFF_FLAG_NOTDEF_PRESENT +HB_BUFFER_DIFF_FLAG_DOTTED_CIRCLE_PRESENT +HB_BUFFER_DIFF_FLAG_CODEPOINT_MISMATCH +HB_BUFFER_DIFF_FLAG_CLUSTER_MISMATCH +HB_BUFFER_DIFF_FLAG_GLYPH_FLAGS_MISMATCH +HB_BUFFER_DIFF_FLAG_POSITION_MISMATCH +hb_buffer_diff + + +Overview of changes leading to 1.4.8 +Tuesday, August 8, 2017 +==================================== + +- Major fix to avar table handling. +- Rename hb-shape --show-message to --trace. +- Build fixes. + + +Overview of changes leading to 1.4.7 +Tuesday, July 18, 2017 +==================================== + +- Multiple Indic, Tibetan, and Cham fixes. +- CoreText: Allow disabling kerning. +- Adjust Arabic feature order again. +- Misc build fixes. + + +Overview of changes leading to 1.4.6 +Sunday, April 23, 2017 +==================================== + +- Graphite2: Fix RTL positioning issue. +- Backlist GDEF of more versions of Padauk and Tahoma. +- New, experimental, cmake alternative build system. + + +Overview of changes leading to 1.4.5 +Friday, March 10, 2017 +==================================== + +- Revert "Fix Context lookup application when moving back after a glyph..." + This introduced memory access problems. To be fixed properly soon. + + +Overview of changes leading to 1.4.4 +Sunday, March 5, 2017 +==================================== + +- Fix Context lookup application when moving back after a glyph deletion. +- Fix buffer-overrun in Bengali. + + +Overview of changes leading to 1.4.3 +Saturday, February 25, 2017 +==================================== + +- Route Adlam script to Arabic shaper. +- Misc fixes. +- New API: + hb_font_set_face() +- Deprecate API: + hb_graphite2_font_get_gr_font() + + +Overview of changes leading to 1.4.2 +Monday, January 23, 2017 +==================================== + +- Implement OpenType Font Variation tables avar/fvar/HVAR/VVAR. +- hb-shape and hb-view now accept --variations. +- New API: + +hb_variation_t +hb_variation_from_string() +hb_variation_to_string() + +hb_font_set_variations() +hb_font_set_var_coords_design() +hb_font_get_var_coords_normalized() + +hb-ot-var.h: +hb_ot_var_axis_t +hb_ot_var_has_data() +hb_ot_var_get_axis_count() +hb_ot_var_get_axes() +hb_ot_var_find_axis() +hb_ot_var_normalize_variations() +hb_ot_var_normalize_coords() + +- MVAR to be implemented later. Access to named instances to be + implemented later as well. + +- Misc fixes. + + +Overview of changes leading to 1.4.1 +Thursday, January 5, 2017 +==================================== + +- Always build and use UCDN for Unicode data by default. + Reduces dependence on version of Unicode data in glib, + specially in the Windows bundles we are shipping, which + have very old glib. + + +Overview of changes leading to 1.4.0 +Thursday, January 5, 2017 +==================================== + +- Merged "OpenType GX" branch which adds core of support for + OpenType 1.8 Font Variations. To that extent, the relevant + new API is: + +New API: +hb_font_set_var_coords_normalized() + + with supporting API: + +New API: +HB_OT_LAYOUT_NO_VARIATIONS_INDEX +hb_ot_layout_table_find_feature_variations() +hb_ot_layout_feature_with_variations_get_lookups() +hb_shape_plan_create2() +hb_shape_plan_create_cached2() + + Currently variations in GSUB/GPOS/GDEF are fully supported, + and no other tables are supported. In particular, fvar/avar + are NOT supported, hence the hb_font_set_var_coords_normalized() + taking normalized coordinates. API to take design coordinates + will be added in the future. + + HVAR/VVAR/MVAR support will also be added to hb-ot-font in the + future. + +- Fix regression in GDEF glyph class processing. +- Add decompositions for Chakma, Limbu, and Balinese in USE shaper. +- Misc fixes. + + +Overview of changes leading to 1.3.4 +Monday, December 5, 2016 +==================================== + +- Fix vertical glyph origin in hb-ot-font. +- Implement CBDT/CBLC color font glyph extents in hb-ot-font. + + +Overview of changes leading to 1.3.3 +Wednesday, September 28, 2016 +==================================== + +- Implement parsing of OpenType MATH table. +New API: +HB_OT_TAG_MATH +HB_OT_MATH_SCRIPT +hb_ot_math_constant_t +hb_ot_math_kern_t +hb_ot_math_glyph_variant_t +hb_ot_math_glyph_part_flags_t +hb_ot_math_glyph_part_t +hb_ot_math_has_data +hb_ot_math_get_constant +hb_ot_math_get_glyph_italics_correction +hb_ot_math_get_glyph_top_accent_attachment +hb_ot_math_get_glyph_kerning +hb_ot_math_is_glyph_extended_shape +hb_ot_math_get_glyph_variants +hb_ot_math_get_min_connector_overlap +hb_ot_math_get_glyph_assembly + + +Overview of changes leading to 1.3.2 +Wednesday, September 27, 2016 +==================================== + +- Fix build of hb-coretext on older OS X versions. + + +Overview of changes leading to 1.3.1 +Wednesday, September 7, 2016 +==================================== + +- Blacklist bad GDEF of more fonts (Padauk). +- More CoreText backend crash fixes with OS X 10.9.5. +- Misc fixes. + + +Overview of changes leading to 1.3.0 +Thursday, July 21, 2016 +==================================== + +- Update to Unicode 9.0.0 +- Move Javanese from Indic shaper to Universal Shaping Engine. +- Allow MultipleSubst to delete a glyph (matching Windows engine). +- Update Universal Shaping Engine to latest draft from Microsoft. +- DirectWrite backend improvements. Note: this backend is for testing ONLY. +- CoreText backend improvements with unreachable fonts. +- Implement symbol fonts (cmap 3.0.0) in hb-ft and hb-ot-font. +- Blacklist bad GDEF of more fonts (Tahoma & others). +- Misc fixes. + + +Overview of changes leading to 1.2.7 +Monday, May 2, 2016 +==================================== + +- Blacklist another version of Times New Roman (Bold) Italic from Windows 7. +- Fix Mongolian Free Variation Selectors shaping with certain fonts. +- Fix Tibetan shorthand contractions shaping. +- Improved list of language tag mappings. +- Unbreak build on Windows CE. +- Make 'glyf' table loading lazy in hb-ot-font. + + +Overview of changes leading to 1.2.6 +Friday, April 8, 2016 +==================================== + +- Blacklist GDEF table of another set of Times New Roman (Bold) Italic. +- DirectWrite backend improvements. Note: DirectWrite backend is + exclusively for our internal testing and should NOT be used in any + production system whatsoever. + + +Overview of changes leading to 1.2.5 +Monday, April 4, 2016 +==================================== + +- Fix GDEF mark-filtering-set, which was broken in 1.2.3. + + +Overview of changes leading to 1.2.4 +Thursday, March 17, 2016 +==================================== + +- Synthesize GDEF glyph class for any glyph that does not have one in GDEF. + I really hope we don't discover broken fonts that shape badly with this + change. +- Misc build and other minor fixes. +- API changes: + - Added HB_NDEBUG. It's fine for production systems to define this to + disable high-overhead debugging checks. However, I also reduced the + overhead of those checks, so it's a non-issue right now. You can + forget it. Just not defining anything at all is fine. + + +Overview of changes leading to 1.2.3 +Thursday, February 25, 2016 +==================================== + +- Blacklist GDEF table of certain versions of Times New Roman (Bold) Italic, + due to bug in glyph class of ASCII double-quote character. This should + address "regression" introduced in 1.2.0 when we switched mark zeroing + in most shapers from BY_UNICODE_LATE to BY_GDEF_LATE. + This fourth release in a week should finally stabilize things... + +- hb-ot-font's get_glyph() implementation saw some optimizations. Though, + might be really hard to measure in real-world situations. + +- Also, two rather small API changes: + +We now disable some time-consuming internal bookkeeping if built with NDEBUG +defined. This is a first time that we use NDEBUG to disable debug code. If +there exist production systems that do NOT want to enable NDEBUG, please let +me know and I'll add HB_NDEBUG. + +Added get_nominal_glyph() and get_variation_glyph() instead of get_glyph() + +New API: +- hb_font_get_nominal_glyph_func_t +- hb_font_get_variation_glyph_func_t +- hb_font_funcs_set_nominal_glyph_func() +- hb_font_funcs_set_variation_glyph_func() +- hb_font_get_nominal_glyph() +- hb_font_get_variation_glyph() + +Deprecated API: +- hb_font_get_glyph_func_t +- hb_font_funcs_set_glyph_func() + +Clients that implement their own font-funcs are encouraged to replace +their get_glyph() implementation with a get_nominal_glyph() and +get_variation_glyph() pair. The variation version can assume that +variation_selector argument is not zero. Old (deprecated) functions +will continue working indefinitely using internal gymnastics; it is +just more efficient to use the new functions. + + +Overview of changes leading to 1.2.2 +Wednesday, February 24, 2016 +==================================== + +- Fix regression with mark positioning with fonts that have + non-zero mark advances. This was introduced in 1.2.0 while + trying to make mark and cursive attachments to work together. + I have partially reverted that, so this version is much more + like what we had before. All clients who updated to 1.2.0 + should update to this version. + + +Overview of changes leading to 1.2.1 +Tuesday, February 23, 2016 +==================================== + +- CoreText: Fix bug with wrong scale if font scale was changed later. + https://github.com/libass/libass/issues/212 +- CoreText: Drastically speed up font initialization. +- CoreText: Fix tiny leak. +- Group ZWJ/ZWNJ with previous syllable under cluster-level=0. + https://github.com/harfbuzz/harfbuzz/issues/217 +- Add test/shaping/README.md about how to add tests to the suite. + + +Overview of changes leading to 1.2.0 +Friday, February 19, 2016 +==================================== + +- Fix various issues (hangs mostly) in case of memory allocation failure. +- Change mark zeroing types of most shapers from BY_UNICODE_LATE to + BY_GDEF_LATE. This seems to be what Uniscribe does. +- Change mark zeroing of USE shaper from NONE to BY_GDEF_EARLY. That's + what Windows does. +- Allow GPOS cursive connection on marks, and fix the interaction with + mark attachment. This work resulted in some changes to how mark + attachments work. See: + https://github.com/harfbuzz/harfbuzz/issues/211 + https://github.com/harfbuzz/harfbuzz/commit/86c68c7a2c971efe8e35b1f1bd99401dc8b688d2 +- Graphite2 shaper: improved negative advance handling (eg. Nastaliq). +- Add nmake-based build system for Windows. +- Minor speedup. +- Misc. improvements. + + +Overview of changes leading to 1.1.3 +Monday, January 11, 2016 +==================================== + +- Ported Indic shaper to Unicode 8.0 data. +- Universal Shaping Engine fixes. +- Speed up CoreText shaper when font fallback happens in CoreText. +- Documentation improvements, thanks to Khaled Hosny. +- Very rough directwrite shaper for testing, thanks to Ebrahim Byagowi. +- Misc bug fixes. +- New API: + + * Font extents: + hb_font_extents_t + hb_font_get_font_extents_func_t + hb_font_get_font_h_extents_func_t + hb_font_get_font_v_extents_func_t + hb_font_funcs_set_font_h_extents_func + hb_font_funcs_set_font_v_extents_func + hb_font_get_h_extents + hb_font_get_v_extents + hb_font_get_extents_for_direction + + * Buffer message (aka debug): + hb_buffer_message_func_t + hb_buffer_set_message_func() + Actual message protocol to be fleshed out later. + + +Overview of changes leading to 1.1.2 +Wednesday, November 26, 2015 +==================================== + +- Fix badly-broken fallback shaper that affected terminology. + https://github.com/harfbuzz/harfbuzz/issues/187 +- Fix y_scaling in Graphite shaper. +- API changes: + * An unset glyph_h_origin() function in font-funcs now (sensibly) + implies horizontal origin at 0,0. Ie, the nil callback returns + true instead of false. As such, implementations that have a + glyph_h_origin() that simply returns true, can remove that function + with HarfBuzz >= 1.1.2. This results in a tiny speedup. + + +Overview of changes leading to 1.1.1 +Wednesday, November 24, 2015 +==================================== + +- Build fixes, specially for hb-coretext. + + +Overview of changes leading to 1.1.0 +Wednesday, November 18, 2015 +==================================== + +- Implement 'stch' stretch feature for Syriac Abbreviation Mark. + https://github.com/harfbuzz/harfbuzz/issues/141 +- Disable use of decompose_compatibility() callback. +- Implement "shaping" of various Unicode space characters, even + if the font does not support them. + https://github.com/harfbuzz/harfbuzz/issues/153 +- If font does not support U+2011 NO-BREAK HYPHEN, fallback to + U+2010 HYPHEN. +- Changes resulting from libFuzzer continuous fuzzing: + * Reject font tables that need more than 8 edits, + * Bound buffer growth during shaping to 32x, + * Fix assertions and other issues at OOM / buffer max-growth. +- Misc fixes and optimizations. +- API changes: + * All fonts created with hb_font_create() now inherit from + (ie. have parent) hb_font_get_empty(). + + +Overview of changes leading to 1.0.6 +Thursday, October 15, 2015 +==================================== + +- Reduce max nesting level in OT lookups from 8 to 6. + Should not affect any real font as far as I know. +- Fix memory access issue in ot-font. +- Revert default load-flags of fonts created using hb_ft_font_create() + back to FT_LOAD_DEFAULT|FT_LOAD_NO_HINTING. This was changed in + last release (1.0.5), but caused major issues, so revert. + https://github.com/harfbuzz/harfbuzz/issues/143 + + +Overview of changes leading to 1.0.5 +Tuesday, October 13, 2015 +==================================== + +- Fix multiple memory access bugs discovered using libFuzzer. + https://github.com/harfbuzz/harfbuzz/issues/139 + Everyone should upgrade to this version as soon as possible. + We now have continuous fuzzing set up, to avoid issues like + these creeping in again. +- Misc fixes. + +- New API: + * hb_font_set_parent(). + * hb_ft_font_[sg]et_load_flags() + The default flags for fonts created using hb_ft_font_create() + has changed to default to FT_LOAD_DEFAULT now. Previously it + was defaulting to FT_LOAD_DFEAULT|FT_LOAD_NO_HINTING. + +- API changes: + * Fonts now default to units-per-EM as their scale, instead of 0. + * hb_font_create_sub_font() does NOT make parent font immutable + anymore. hb_font_make_immutable() does. + + +Overview of changes leading to 1.0.4 +Wednesday, September 30, 2015 +==================================== + +- Fix minor out-of-bounds read error. + + +Overview of changes leading to 1.0.3 +Tuesday, September 1, 2015 +==================================== + +- Start of user documentation, from Simon Cozens! +- Implement glyph_extents() for TrueType fonts in hb-ot-font. +- Improve GPOS cursive attachments with conflicting lookups. +- More fixes for cluster-level = 1. +- Uniscribe positioning fix. + + +Overview of changes leading to 1.0.2 +Wednesday, August 19, 2015 +==================================== + +- Fix shaping with cluster-level > 0. +- Fix Uniscribe backend font-size scaling. +- Declare dependencies in harfbuzz.pc. + FreeType is not declared though, to avoid bugs in pkg-config + 0.26 with recursive dependencies. +- Slightly improved debug infrastructure. More to come later. +- Misc build fixes. + + +Overview of changes leading to 1.0.1 +Monday, July 27, 2015 +==================================== + +- Fix out-of-bounds access in USE shaper. + + +Overview of changes leading to 1.0.0 +Sunday, July 26, 2015 +==================================== + +- Implement Universal Shaping Engine: + https://www.microsoft.com/typography/OpenTypeDev/USE/intro.htm + http://blogs.windows.com/bloggingwindows/2015/02/23/windows-shapes-the-worlds-languages/ +- Bump version to 1.0.0. The soname was NOT bumped. + + +Overview of changes leading to 0.9.42 +Thursday, July 26, 2015 +===================================== + +- New API to allow for retrieving finer-grained cluster + mappings if the client desires to handle them. Default + behavior is unchanged. +- Fix cluster merging when removing default-ignorables. +- Update to Unicode 8.0 +- hb-graphite2 fixes. +- Misc fixes. +- Removed HB_NO_MERGE_CLUSTERS hack. +- New API: + hb_buffer_cluster_level_t enum + hb_buffer_get_cluster_level() + hb_buffer_set_cluster_level() + hb-shape / hb-view --cluster-level + + +Overview of changes leading to 0.9.41 +Thursday, June 18, 2015 +===================================== + +- Fix hb-coretext with trailing whitespace in right-to-left. +- New API: hb_buffer_reverse_range(). +- Allow implementing atomic ops in config.h. +- Fix hb_language_t in language bindings. +- Misc fixes. + + +Overview of changes leading to 0.9.40 +Friday, March 20, 2015 +===================================== + +- Another hb-coretext crasher fix. Ouch! +- Happy Norouz! + + +Overview of changes leading to 0.9.39 +Wednesday, March 4, 2015 +===================================== + +- Critical hb-coretext fixes. +- Optimizations and refactoring; no functional change + expected. +- Misc build fixes. + + +Overview of changes leading to 0.9.38 +Friday, January 23, 2015 +===================================== + +- Fix minor out-of-bounds access in Indic shaper. +- Change New Tai Lue shaping engine from South-East Asian to default, + reflecting change in Unicode encoding model. +- Add hb-shape --font-size. Can take up to two numbers for separate + x / y size. +- Fix CoreText and FreeType scale issues with negative scales. +- Reject blobs larger than 2GB. This might break some icu-le-hb clients + that need security fixes. See: + http://www.icu-project.org/trac/ticket/11450 +- Avoid accessing font tables during face destruction, in casce rogue + clients released face data already. +- Fix up gobject-introspection a bit. Python bindings kinda working. + See README.python. +- Misc fixes. +- API additions: + hb_ft_face_create_referenced() + hb_ft_font_create_referenced() + + +Overview of changes leading to 0.9.37 +Wednesday, December 17, 2014 +===================================== + +- Fix out-of-bounds access in Context lookup format 3. +- Indic: Allow ZWJ/ZWNJ before syllable modifiers. + + +Overview of changes leading to 0.9.36 +Thursday, November 20, 2014 +===================================== + +- First time that three months went by without a release since + 0.9.2 was released on August 10, 2012! +- Fix performance bug in hb_ot_collect_glyphs(): + https://bugzilla.mozilla.org/show_bug.cgi?id=1090869 +- Add basic vertical-text support to hb-ot-font. +- Misc build fixes. + + +Overview of changes leading to 0.9.35 +Saturday, August 13, 2014 +===================================== + +- Fix major shape-plan caching bug when more than one shaper were + provided to hb_shape_full() (as exercised by XeTeX). + http://www.mail-archive.com/debian-bugs-dist@lists.debian.org/msg1246370.html +- Fix Arabic fallback shaping regression. This was broken in 0.9.32. +- Major hb-coretext fixes. That backend is complete now, including + respecing buffer direction and language, down to vertical writing. +- Build fixes for Windows CE. Should build fine now. +- Misc fixes: + Use atexit() only if it's safe to call from shared library + https://bugs.freedesktop.org/show_bug.cgi?id=82246 + Mandaic had errors in its Unicode Joining_Type + https://bugs.freedesktop.org/show_bug.cgi?id=82306 +- API changes: + + * hb_buffer_clear_contents() does not reset buffer flags now. + + After 763e5466c0a03a7c27020e1e2598e488612529a7, one doesn't + need to set flags for different pieces of text. The flags now + are something the client sets up once, depending on how it + actually uses the buffer. As such, don't clear it in + clear_contents(). + + I don't expect any changes to be needed to any existing client. + + +Overview of changes leading to 0.9.34 +Saturday, August 2, 2014 +===================================== + +- hb_feature_from_string() now accepts CSS font-feature-settings format. +- As a result, hb-shape / hb-view --features also accept CSS-style strings. + Eg, "'liga' off" is accepted now. +- Add old-spec Myanmar shaper: + https://bugs.freedesktop.org/show_bug.cgi?id=81775 +- Don't apply 'calt' in Hangul shaper. +- Fix mark advance zeroing for Hebrew shaper: + https://bugs.freedesktop.org/show_bug.cgi?id=76767 +- Implement Windows-1256 custom Arabic shaping. Only built on Windows, + and requires help from get_glyph(). Used by Firefox. + https://bugzilla.mozilla.org/show_bug.cgi?id=1045139 +- Disable 'liga' in vertical text. +- Build fixes. +- API changes: + + * Make HB_BUFFER_FLAG_BOT/EOT easier to use. + + Previously, we expected users to provide BOT/EOT flags when the + text *segment* was at paragraph boundaries. This meant that for + clients that provide full paragraph to HarfBuzz (eg. Pango), they + had code like this: + + hb_buffer_set_flags (hb_buffer, + (item_offset == 0 ? HB_BUFFER_FLAG_BOT : 0) | + (item_offset + item_length == paragraph_length ? + HB_BUFFER_FLAG_EOT : 0)); + + hb_buffer_add_utf8 (hb_buffer, + paragraph_text, paragraph_length, + item_offset, item_length); + + After this change such clients can simply say: + + hb_buffer_set_flags (hb_buffer, + HB_BUFFER_FLAG_BOT | HB_BUFFER_FLAG_EOT); + + hb_buffer_add_utf8 (hb_buffer, + paragraph_text, paragraph_length, + item_offset, item_length); + + Ie, HarfBuzz itself checks whether the segment is at the beginning/end + of the paragraph. Clients that only pass item-at-a-time to HarfBuzz + continue not setting any flags whatsoever. + + Another way to put it is: if there's pre-context text in the buffer, + HarfBuzz ignores the BOT flag. If there's post-context, it ignores + EOT flag. + + +Overview of changes leading to 0.9.33 +Tuesday, July 22, 2014 +===================================== + +- Turn off ARabic 'cswh' feature that was accidentally turned on. +- Add HB_TAG_MAX_SIGNED. +- Make hb_face_make_immutable() really make face immutable! +- Windows build fixes. + + +Overview of changes leading to 0.9.32 +Thursday, July 17, 2014 +===================================== + +- Apply Arabic shaping features in spec order exactly. +- Another fix for Mongolian free variation selectors. +- For non-Arabic scripts in Arabic shaper apply 'rlig' and 'calt' + together. +- Minor adjustment to U+FFFD logic. +- Fix hb-coretext build. + + +Overview of changes leading to 0.9.31 +Wednesday, July 16, 2014 +===================================== + +- Only accept valid UTF-8/16/32; we missed many cases before. +- Better shaping of invalid UTF-8/16/32. Falls back to + U+FFFD REPLACEMENT CHARACTER now. +- With all changes in this release, the buffer will contain fully + valid Unicode after hb_buffer_add_utf8/16/32 no matter how + broken the input is. This can be overridden though. See below. +- Fix Mongolian Variation Selectors for fonts without GDEF. +- Fix minor invalid buffer access. +- Accept zh-Hant and zh-Hans language tags. hb_ot_tag_to_language() + now uses these instead of private tags. +- Build fixes. +- New API: + * hb_buffer_add_codepoints(). This does what hb_buffer_add_utf32() + used to do, ie. no validity check on the input at all. add_utf32 + now replaces invalid Unicode codepoints with the replacement + character (see below). + * hb_buffer_set_replacement_codepoint() + * hb_buffer_get_replacement_codepoint() + Previously, in hb_buffer_add_utf8 and hb_buffer_add_utf16, when + we detected broken input, we replaced that with (hb_codepoint_t)-1. + This has changed to use U+FFFD now, but can be changed using these + new API. + + +Overview of changes leading to 0.9.30 +Wednesday, July 9, 2014 +===================================== + +- Update to Unicode 7.0.0: + * New scripts Manichaean and Psalter Pahlavi are shaped using + Arabic shaper. + * All the other new scripts to through the generic shaper for + now. +- Minor Indic improvements. +- Fix graphite2 backend cluster mapping [crasher!] +- API changes: + * New HB_SCRIPT_* values for Unicode 7.0 scripts. + * New function hb_ot_layout_language_get_required_feature(). +- Build fixes. + + +Overview of changes leading to 0.9.29 +Thursday, May 29, 2014 +===================================== + +- Implement cmap in hb-ot-font.h. No variation-selectors yet. +- Myanmar: Allow MedialYa+Asat. +- Various Indic fixes: + * Support most characters in Extended Devanagary and Vedic + Unicode blocks. + * Allow digits and a some punctuation as consonant placeholders. +- Build fixes. + + +Overview of changes leading to 0.9.28 +Monday, April 28, 2014 +===================================== + +- Unbreak old-spec Indic shaping. (bug 76705) +- Fix shaping of U+17DD and U+0FC6. +- Add HB_NO_MERGE_CLUSTERS build option. NOT to be enabled by default + for shipping libraries. It's an option for further experimentation + right now. When we are sure how to do it properly, we will add + public run-time API for the functionality. +- Build fixes. + + +Overview of changes leading to 0.9.27 +Tuesday, March 18, 2014 +===================================== + +- Don't use "register" storage class specifier +- Wrap definition of free_langs() with HAVE_ATEXIT +- Add coretext_aat shaper and hb_coretext_face_create() constructor +- If HAVE_ICU_BUILTIN is defined, use hb-icu Unicode callbacks +- Add Myanmar test case from OpenType Myanmar spec +- Only do fallback Hebrew composition if no GPOS 'mark' available +- Allow bootstrapping without gtk-doc +- Use AM_MISSING_PROG for ragel and git +- Typo in ucdn's Makefile.am +- Improve MemoryBarrier() implementation + + +Overview of changes leading to 0.9.26 +Thursday, January 30, 2014 +===================================== + +- Misc fixes. +- Fix application of 'rtlm' feature. +- Automatically apply frac/numr/dnom around U+2044 FRACTION SLASH. +- New header: hb-ot-shape.h +- Uniscribe: fix scratch-buffer accounting. +- Reorder Tai Tham SAKOT to after tone-marks. +- Add Hangul shaper. +- New files: + hb-ot-shape-complex-hangul.cc + hb-ot-shape-complex-hebrew.cc + hb-ot-shape-complex-tibetan.cc +- Disable 'cswh' feature in Arabic shaper. +- Coretext: better handle surrogate pairs. +- Add HB_TAG_MAX and _HB_SCRIPT_MAX_VALUE. + + +Overview of changes leading to 0.9.25 +Wednesday, December 4, 2013 +===================================== + +- Myanmar shaper improvements. +- Avoid font fallback in CoreText backend. +- Additional OpenType language tag mappiongs. +- More aggressive shape-plan caching. +- Build with / require automake 1.13. +- Build with libtool 2.4.2.418 alpha to support ppc64le. + + +Overview of changes leading to 0.9.24 +Tuesday, November 13, 2013 +===================================== + +- Misc compiler warning fixes with clang. +- No functional changes. + + +Overview of changes leading to 0.9.23 +Monday, October 28, 2013 +===================================== + +- "Udupi HarfBuzz Hackfest", Paris, October 14..18 2013. +- Fix (Chain)Context recursion with non-monotone lookup positions. +- Misc Indic bug fixes. +- New Javanese / Buginese shaping, similar to Windows 8.1. + + +Overview of changes leading to 0.9.22 +Thursday, October 3, 2013 +===================================== + +- Fix use-after-end-of-scope in hb_language_from_string(). +- Fix hiding of default_ignorables if font doesn't have space glyph. +- Protect against out-of-range lookup indices. + +- API Changes: + + * Added hb_ot_layout_table_get_lookup_count() + + +Overview of changes leading to 0.9.21 +Monday, September 16, 2013 +===================================== + +- Rename gobject-introspection library name from harfbuzz to HarfBuzz. +- Remove (long disabled) hb-old and hb-icu-le test shapers. +- Misc gtk-doc and gobject-introspection annotations. +- Misc fixes. +- API changes: + + * Add HB_SET_VALUE_INVALID + +Overview of changes leading to 0.9.20 +Thursday, August 29, 2013 +===================================== + +General: +- Misc substitute_closure() fixes. +- Build fixes. + +Documentation: +- gtk-doc boilerplate integrated. Docs are built now, but + contain no contents. By next release hopefully we have + some content in. Enable using --enable-gtk-doc. + +GObject and Introspection: +- Added harfbuzz-gobject library (hb-gobject.h) that has type + bindings for all HarfBuzz objects and enums. Enable using + --with-gobject. +- Added gobject-introspection boilerplate. Nothing useful + right now. Work in progress. Gets enabled automatically if + --with-gobject is used. Override with --disable-introspection. + +OpenType shaper: +- Apply 'mark' in Myanmar shaper. +- Don't apply 'dlig' by default. + +Uniscribe shaper: +- Support user features. +- Fix loading of fonts that are also installed on the system. +- Fix shaping of Arabic Presentation Forms. +- Fix build with wide chars. + +CoreText shaper: +- Support user features. + +Source changes: +- hb_face_t code moved to hb-face.h / hb-face.cc. +- Added hb-deprecated.h. + +API changes: +- Added HB_DISABLE_DEPRECATED. +- Deprecated HB_SCRIPT_CANADIAN_ABORIGINAL; replaced by + HB_SCRIPT_CANADIAN_SYLLABICS. +- Deprecated HB_BUFFER_FLAGS_DEFAULT; replaced by + HB_BUFFER_FLAG_DEFAULT. +- Deprecated HB_BUFFER_SERIALIZE_FLAGS_DEFAULT; replaced by + HB_BUFFER_SERIALIZE_FLAG_DEFAULT. + + +Overview of changes leading to 0.9.19 +Tuesday, July 16, 2013 +===================================== + +- Build fixes. +- Better handling of multiple variation selectors in a row. +- Pass on variation selector to GSUB if not consumed by cmap. +- Fix undefined memory access. +- Add Javanese config to Indic shaper. +- Misc bug fixes. + +Overview of changes leading to 0.9.18 +Tuesday, May 28, 2013 +===================================== + +New build system: + +- All unneeded code is all disabled by default, + +- Uniscribe and CoreText shapers can be enabled with their --with options, + +- icu_le and old shapers cannot be enabled for now, + +- glib, freetype, and cairo will be detected automatically. + They can be force on/off'ed with their --with options, + +- icu and graphite2 are default off, can be enabled with their --with + options, + +Moreover, ICU support is now build into a separate library: +libharfbuzz-icu.so, and a new harfbuzz-icu.pc is shipped for it. +Distros can enable ICU now without every application on earth +getting linked to via libharfbuzz.so. + +For distros I recommend that they make sure they are building --with-glib +--with-freetype --with-cairo, --with-icu, and optionally --with-graphite2; +And package harfbuzz and harfbuzz-icu separately. + + +Overview of changes leading to 0.9.17 +Monday, May 20, 2013 +===================================== + +- Build fixes. +- Fix bug in hb_set_get_min(). +- Fix regression with Arabic mark positioning / width-zeroing. + +Overview of changes leading to 0.9.16 +Friday, April 19, 2013 +===================================== + +- Major speedup in OpenType lookup processing. With the Amiri + Arabic font, this release is over 3x faster than previous + release. All scripts / languages should see this speedup. + +- New --num-iterations option for hb-shape / hb-view; useful for + profiling. + +Overview of changes leading to 0.9.15 +Friday, April 05, 2013 +===================================== + +- Build fixes. +- Fix crasher in graphite2 shaper. +- Fix Arabic mark width zeroing regression. +- Don't compose Hangul jamo into Unicode syllables. + + +Overview of changes leading to 0.9.14 +Thursday, March 21, 2013 +===================================== + +- Build fixes. +- Fix time-consuming sanitize with malicious fonts. +- Implement hb_buffer_deserialize_glyphs() for both json and text. +- Do not ignore Hangul filler characters. +- Indic fixes: + * Fix Malayalam pre-base reordering interaction with post-forms. + * Further adjust ZWJ handling. Should fix known regressions from + 0.9.13. + + +Overview of changes leading to 0.9.13 +Thursday, February 25, 2013 +===================================== + +- Build fixes. +- Ngapi HarfBuzz Hackfest in London (February 2013): + * Fixed all known Indic bugs, + * New Win8-style Myanmar shaper, + * New South-East Asian shaper for Tai Tham, Cham, and New Tai Lue, + * Smartly ignore Default_Ignorable characters (joiners, etc) wheb + matching GSUB/GPOS lookups, + * Fix 'Phags-Pa U+A872 shaping, + * Fix partial disabling of default-on features, + * Allow disabling of TrueType kerning. +- Fix possible crasher with broken fonts with overlapping tables. +- Removed generated files from git again. So, one needs ragel to + bootstrap from the git tree. + +API changes: +- hb_shape() and related APIs now abort if buffer direction is + HB_DIRECTION_INVALID. Previously, hb_shape() was calling + hb_buffer_guess_segment_properties() on the buffer before + shaping. The heuristics in that function are fragile. If the + user really wants the old behvaior, they can call that function + right before calling hb_shape() to get the old behavior. +- hb_blob_create_sub_blob() always creates sub-blob with + HB_MEMORY_MODE_READONLY. See comments for the reason. + + +Overview of changes leading to 0.9.12 +Thursday, January 18, 2013 +===================================== + +- Build fixes for Sun compiler. +- Minor bug fix. + +Overview of changes leading to 0.9.11 +Thursday, January 10, 2013 +===================================== + +- Build fixes. +- Fix GPOS mark attachment with null Anchor offsets. +- [Indic] Fix old-spec reordering of viramas if sequence ends in one. +- Fix multi-threaded shaper data creation crash. +- Add atomic ops for Solaris. + +API changes: +- Rename hb_buffer_clear() to hb_buffer_clear_contents(). + + +Overview of changes leading to 0.9.10 +Thursday, January 3, 2013 +===================================== + +- [Indic] Fixed rendering of Malayalam dot-reph +- Updated OT language tags. +- Updated graphite2 backend. +- Improved hb_ot_layout_get_size_params() logic. +- Improve hb-shape/hb-view help output. +- Fixed hb-set.h implementation to not crash. +- Fixed various issues with hb_ot_layout_collect_lookups(). +- Various build fixes. + +New API: + +hb_graphite2_face_get_gr_face() +hb_graphite2_font_get_gr_font() +hb_coretext_face_get_cg_font() + +Modified API: + +hb_ot_layout_get_size_params() + + +Overview of changes leading to 0.9.9 +Wednesday, December 5, 2012 +==================================== + +- Fix build on Windows. +- Minor improvements. + + +Overview of changes leading to 0.9.8 +Tuesday, December 4, 2012 +==================================== + + +- Actually implement hb_shape_plan_get_shaper (). +- Make UCDB data tables const. +- Lots of internal refactoring in OTLayout tables. +- Flesh out hb_ot_layout_lookup_collect_glyphs(). + +New API: + +hb_ot_layout_collect_lookups() +hb_ot_layout_get_size_params() + + +Overview of changes leading to 0.9.7 +Sunday, November 21, 2012 +==================================== + + +HarfBuzz "All-You-Can-Eat-Sushi" (aka Vancouver) Hackfest and follow-on fixes. + +- Fix Arabic contextual joining using pre-context text. +- Fix Sinhala "split matra" mess. +- Fix Khmer shaping with broken fonts. +- Implement Thai "PUA" shaping for old fonts. +- Do NOT route Kharoshthi script through the Indic shaper. +- Disable fallback positioning for Indic and Thai shapers. +- Misc fixes. + + +hb-shape / hb-view changes: + +- Add --text-before and --text-after +- Add --bot / --eot / --preserve-default-ignorables +- hb-shape --output-format=json + + +New API: + +hb_buffer_clear() + +hb_buffer_flags_t + +HB_BUFFER_FLAGS_DEFAULT +HB_BUFFER_FLAG_BOT +HB_BUFFER_FLAG_EOT +HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES + +hb_buffer_set_flags() +hb_buffer_get_flags() + +HB_BUFFER_SERIALIZE_FLAGS +hb_buffer_serialize_glyphs() +hb_buffer_deserialize_glyphs() +hb_buffer_serialize_list_formats() + +hb_set_add_range() +hb_set_del_range() +hb_set_get_population() +hb_set_next_range() + +hb_face_[sg]et_glyph_count() + +hb_segment_properties_t +HB_SEGMENT_PROPERTIES_DEFAULT +hb_segment_properties_equal() +hb_segment_properties_hash() + +hb_buffer_set_segment_properties() +hb_buffer_get_segment_properties() + +hb_ot_layout_glyph_class_t +hb_ot_layout_get_glyph_class() +hb_ot_layout_get_glyphs_in_class() + +hb_shape_plan_t +hb_shape_plan_create() +hb_shape_plan_create_cached() +hb_shape_plan_get_empty() +hb_shape_plan_reference() +hb_shape_plan_destroy() +hb_shape_plan_set_user_data() +hb_shape_plan_get_user_data() +hb_shape_plan_execute() +hb_shape_plan_get_shaper() + +hb_ot_shape_plan_collect_lookups() + + +API changes: + +- Remove "mask" parameter from hb_buffer_add(). +- Rename hb_ot_layout_would_substitute_lookup() and hb_ot_layout_substitute_closure_lookup(). +- hb-set.h API const correction. +- Renamed hb_set_min/max() to hb_set_get_min/max(). +- Rename hb_ot_layout_feature_get_lookup_indexes() to hb_ot_layout_feature_get_lookups(). +- Rename hb_buffer_guess_properties() to hb_buffer_guess_segment_properties(). + + + +Overview of changes leading to 0.9.6 +Sunday, November 13, 2012 +==================================== + +- Don't clear pre-context text if no new context is provided. +- Fix ReverseChainingSubstLookup, which was totally borked. +- Adjust output format of hb-shape a bit. +- Include config.h.in in-tree. Makes it easier for alternate build systems. +- Fix hb_buffer_set_length(buffer, 0) invalid memory allocation. +- Use ICU LayoutEngine's C API instead of C++. Avoids much headache. +- Drop glyphs for all of Unicode Default_Ignorable characters. +- Misc build fixes. + +Arabic shaper: +- Enable 'dlig' and 'mset' features in Arabic shaper. +- Implement 'Phags-pa shaping, improve Mongolian. + +Indic shaper: +- Decompose Sinhala split matras the way old HarfBuzz / Pango did. +- Initial support for Consonant Medials. +- Start adding new-style Myanmar shaping. +- Make reph and 'pref' logic introspect the font. +- Route Meetei-Mayek through the Indic shaper. +- Don't apply 'liga' in Indic shaper. +- Improve Malayalam pre-base reordering Ra interaction with Chillus. + + + +Overview of changes leading to 0.9.5 +Sunday, October 14, 2012 +==================================== + +- Synthetic-GSUB Arabic fallback shaping. + +- Misc Indic improvements. + +- Add build system support for pthread. + +- Imported UCDN for in-tree Unicode callbacks implementation. + +- Context-aware Arabic joining. + +- Misc other fixes. + +- New API: + + hb_feature_to/from-string() + hb_buffer_[sg]et_content_type() + + + +Overview of changes leading to 0.9.4 +Tuesday, Sep 03, 2012 +==================================== + +- Indic improvements with old-spec Malayalam. + +- Better fallback glyph positioning, specially with Thai / Lao marks. + +- Implement dotted-circle insertion. + +- Better Arabic fallback shaping / ligation. + +- Added ICU LayoutEngine backend for testing. Call it by the 'icu_le' name. + +- Misc fixes. + + + +Overview of changes leading to 0.9.3 +Friday, Aug 18, 2012 +==================================== + +- Fixed fallback mark positioning for left-to-right text. + +- Improve mark positioning for the remaining combining classes. + +- Unbreak Thai and fallback Arabic shaping. + +- Port Arabic shaper to shape-plan caching. + +- Use new ICU normalizer functions. + + + +Overview of changes leading to 0.9.2 +Friday, Aug 10, 2012 +==================================== + +- Over a thousand commits! This is the first major release of HarfBuzz. + +- HarfBuzz is feature-complete now! It should be in par, or better, than + both Pango's shapers and old HarfBuzz / Qt shapers. + +- New Indic shaper, supporting main Indic scripts, Sinhala, and Khmer. + +- Improved Arabic shaper, with fallback Arabic shaping, supporting Arabic, + Sinhala, N'ko, Mongolian, and Mandaic. + +- New Thai / Lao shaper. + +- Tibetan / Hangul support in the generic shaper. + +- Synthetic GDEF support for fonts without a GDEF table. + +- Fallback mark positioning for fonts without a GPOS table. + +- Unicode normalization shaping heuristic during glyph mapping. + +- New experimental Graphite2 backend. + +- New Uniscribe backend (primarily for testing). + +- New CoreText backend (primarily for testing). + +- Major optimization and speedup. + +- Test suites and testing infrastructure (work in progress). + +- Greatly improved hb-view cmdline tool. + +- hb-shape cmdline tool. + +- Unicode 6.1 support. + +Summary of API changes: + +o Changed API: + + - Users are expected to only include main header files now (ie. hb.h, + hb-glib.h, hb-ft.h, ...) + + - All struct tag names had their initial underscore removed. + Ie. "struct _hb_buffer_t" is "struct hb_buffer_t" now. + + - All set_user_data() functions now take a "replace" boolean parameter. + + - hb_buffer_create() takes zero arguments now. + Use hb_buffer_pre_allocate() to pre-allocate. + + - hb_buffer_add_utf*() now accept -1 for length parameters, + meaning "nul-terminated". + + - hb_direction_t enum values changed. + + - All *_from_string() APIs now take a length parameter to allow for + non-nul-terminated strings. A -1 length means "nul-terminated". + + - Typedef for hb_language_t changed. + + - hb_get_table_func_t renamed to hb_reference_table_func_t. + + - hb_ot_layout_table_choose_script() + + - Various renames in hb-unicode.h. + +o New API: + + - hb_buffer_guess_properties() + Automatically called by hb_shape(). + + - hb_buffer_normalize_glyphs() + + - hb_tag_from_string() + + - hb-coretext.h + + - hb-uniscribe.h + + - hb_face_reference_blob() + - hb_face_[sg]et_index() + - hb_face_set_upem() + + - hb_font_get_glyph_name_func_t + hb_font_get_glyph_from_name_func_t + hb_font_funcs_set_glyph_name_func() + hb_font_funcs_set_glyph_from_name_func() + hb_font_get_glyph_name() + hb_font_get_glyph_from_name() + hb_font_glyph_to_string() + hb_font_glyph_from_string() + + - hb_font_set_funcs_data() + + - hb_ft_font_set_funcs() + - hb_ft_font_get_face() + + - hb-gobject.h (work in progress) + + - hb_ot_shape_glyphs_closure() + hb_ot_layout_substitute_closure_lookup() + + - hb-set.h + + - hb_shape_full() + + - hb_unicode_combining_class_t + + - hb_unicode_compose_func_t + hb_unicode_decompose_func_t + hb_unicode_decompose_compatibility_func_t + hb_unicode_funcs_set_compose_func() + hb_unicode_funcs_set_decompose_func() + hb_unicode_funcs_set_decompose_compatibility_func() + hb_unicode_compose() + hb_unicode_decompose() + hb_unicode_decompose_compatibility() + +o Removed API: + + - hb_ft_get_font_funcs() + + - hb_ot_layout_substitute_start() + hb_ot_layout_substitute_lookup() + hb_ot_layout_substitute_finish() + hb_ot_layout_position_start() + hb_ot_layout_position_lookup() + hb_ot_layout_position_finish() + + + +Overview of changes leading to 0.6.0 +Friday, May 27, 2011 +==================================== + +- Vertical text support in GPOS +- Almost all API entries have unit tests now, under test/ +- All thread-safety issues are fixed + +Summary of API changes follows. + + +* Simple Types API: + + o New API: + HB_LANGUAGE_INVALID + hb_language_get_default() + hb_direction_to_string() + hb_direction_from_string() + hb_script_get_horizontal_direction() + HB_UNTAG() + + o Renamed API: + hb_category_t renamed to hb_unicode_general_category_t + + o Changed API: + hb_language_t is a typed pointers now + + o Removed API: + HB_TAG_STR() + + +* Use ISO 15924 tags for hb_script_t: + + o New API: + hb_script_from_iso15924_tag() + hb_script_to_iso15924_tag() + hb_script_from_string() + + o Changed API: + HB_SCRIPT_* enum members changed value. + + +* Buffer API streamlined: + + o New API: + hb_buffer_reset() + hb_buffer_set_length() + hb_buffer_allocation_successful() + + o Renamed API: + hb_buffer_ensure() renamed to hb_buffer_pre_allocate() + hb_buffer_add_glyph() renamed to hb_buffer_add() + + o Removed API: + hb_buffer_clear() + hb_buffer_clear_positions() + + o Changed API: + hb_buffer_get_glyph_infos() takes an out length parameter now + hb_buffer_get_glyph_positions() takes an out length parameter now + + +* Blob API streamlined: + + o New API: + hb_blob_get_data() + hb_blob_get_data_writable() + + o Renamed API: + hb_blob_create_empty() renamed to hb_blob_get_empty() + + o Removed API: + hb_blob_lock() + hb_blob_unlock() + hb_blob_is_writable() + hb_blob_try_writable() + + o Changed API: + hb_blob_create() takes user_data before destroy now + + +* Unicode functions API: + + o Unicode function vectors can subclass other unicode function vectors now. + Unimplemented callbacks in the subclass automatically chainup to the parent. + + o All hb_unicode_funcs_t callbacks take a user_data now. Their setters + take a user_data and its respective destroy callback. + + o New API: + hb_unicode_funcs_get_empty() + hb_unicode_funcs_get_default() + hb_unicode_funcs_get_parent() + + o Changed API: + hb_unicode_funcs_create() now takes a parent_funcs. + + o Removed func getter functions: + hb_unicode_funcs_get_mirroring_func() + hb_unicode_funcs_get_general_category_func() + hb_unicode_funcs_get_script_func() + hb_unicode_funcs_get_combining_class_func() + hb_unicode_funcs_get_eastasian_width_func() + + +* Face API: + + o Renamed API: + hb_face_get_table() renamed to hb_face_reference_table() + hb_face_create_for_data() renamed to hb_face_create() + + o Changed API: + hb_face_create_for_tables() takes user_data before destroy now + hb_face_reference_table() returns empty blob instead of NULL + hb_get_table_func_t accepts the face as first parameter now + +* Font API: + + o Fonts can subclass other fonts now. Unimplemented callbacks in the + subclass automatically chainup to the parent. When chaining up, + scale is adjusted if the parent font has a different scale. + + o All hb_font_funcs_t callbacks take a user_data now. Their setters + take a user_data and its respective destroy callback. + + o New API: + hb_font_get_parent() + hb_font_funcs_get_empty() + hb_font_create_sub_font() + + o Removed API: + hb_font_funcs_copy() + hb_font_unset_funcs() + + o Removed func getter functions: + hb_font_funcs_get_glyph_func() + hb_font_funcs_get_glyph_advance_func() + hb_font_funcs_get_glyph_extents_func() + hb_font_funcs_get_contour_point_func() + hb_font_funcs_get_kerning_func() + + o Changed API: + hb_font_create() takes a face and references it now + hb_font_set_funcs() takes user_data before destroy now + hb_font_set_scale() accepts signed integers now + hb_font_get_contour_point_func_t now takes glyph first, then point_index + hb_font_get_glyph_func_t returns a success boolean now + + +* Changed object model: + + o All object types have a _get_empty() now: + hb_blob_get_empty() + hb_buffer_get_empty() + hb_face_get_empty() + hb_font_get_empty() + hb_font_funcs_get_empty() + hb_unicode_funcs_get_empty() + + o Added _set_user_data() and _get_user_data() for all object types: + hb_blob_get_user_data() + hb_blob_set_user_data() + hb_buffer_get_user_data() + hb_buffer_set_user_data() + hb_face_get_user_data() + hb_face_set_user_data() + hb_font_funcs_get_user_data() + hb_font_funcs_set_user_data() + hb_font_get_user_data() + hb_font_set_user_data() + hb_unicode_funcs_get_user_data() + hb_unicode_funcs_set_user_data() + + o Removed the _get_reference_count() from all object types: + hb_blob_get_reference_count() + hb_buffer_get_reference_count() + hb_face_get_reference_count() + hb_font_funcs_get_reference_count() + hb_font_get_reference_count() + hb_unicode_funcs_get_reference_count() + + o Added _make_immutable() and _is_immutable() for all object types except for buffer: + hb_blob_make_immutable() + hb_blob_is_immutable() + hb_face_make_immutable() + hb_face_is_immutable() + + +* Changed API for vertical text support + + o The following callbacks where removed: + hb_font_get_glyph_advance_func_t + hb_font_get_kerning_func_t + + o The following new callbacks added instead: + hb_font_get_glyph_h_advance_func_t + hb_font_get_glyph_v_advance_func_t + hb_font_get_glyph_h_origin_func_t + hb_font_get_glyph_v_origin_func_t + hb_font_get_glyph_h_kerning_func_t + hb_font_get_glyph_v_kerning_func_t + + o The following API removed as such: + hb_font_funcs_set_glyph_advance_func() + hb_font_funcs_set_kerning_func() + hb_font_get_glyph_advance() + hb_font_get_kerning() + + o New API added instead: + hb_font_funcs_set_glyph_h_advance_func() + hb_font_funcs_set_glyph_v_advance_func() + hb_font_funcs_set_glyph_h_origin_func() + hb_font_funcs_set_glyph_v_origin_func() + hb_font_funcs_set_glyph_h_kerning_func() + hb_font_funcs_set_glyph_v_kerning_func() + hb_font_get_glyph_h_advance() + hb_font_get_glyph_v_advance() + hb_font_get_glyph_h_origin() + hb_font_get_glyph_v_origin() + hb_font_get_glyph_h_kerning() + hb_font_get_glyph_v_kerning() + + o The following higher-leve API added for convenience: + hb_font_get_glyph_advance_for_direction() + hb_font_get_glyph_origin_for_direction() + hb_font_add_glyph_origin_for_direction() + hb_font_subtract_glyph_origin_for_direction() + hb_font_get_glyph_kerning_for_direction() + hb_font_get_glyph_extents_for_origin() + hb_font_get_glyph_contour_point_for_origin() + + +* OpenType Layout API: + + o New API: + hb_ot_layout_position_start() + hb_ot_layout_substitute_start() + hb_ot_layout_substitute_finish() + + +* Glue code: + + o New API: + hb_glib_script_to_script() + hb_glib_script_from_script() + hb_icu_script_to_script() + hb_icu_script_from_script() + + +* Version API added: + + o New API: + HB_VERSION_MAJOR + HB_VERSION_MINOR + HB_VERSION_MICRO + HB_VERSION_STRING + HB_VERSION_CHECK() + hb_version() + hb_version_string() + hb_version_check() + + diff --git a/libs/harfbuzz/README b/libs/harfbuzz/README new file mode 100644 index 00000000..42061c01 --- /dev/null +++ b/libs/harfbuzz/README @@ -0,0 +1 @@ +README.md \ No newline at end of file diff --git a/libs/harfbuzz/README.md b/libs/harfbuzz/README.md new file mode 100644 index 00000000..4202961e --- /dev/null +++ b/libs/harfbuzz/README.md @@ -0,0 +1,99 @@ +[![Linux CI Status](https://github.com/harfbuzz/harfbuzz/workflows/linux-ci/badge.svg)](https://github.com/harfbuzz/harfbuzz/workflows/linux-ci/badge.svg) +[![CircleCI Build Status](https://circleci.com/gh/harfbuzz/harfbuzz/tree/main.svg?style=svg)](https://circleci.com/gh/harfbuzz/harfbuzz/tree/main) +[![OSS-Fuzz Status](https://oss-fuzz-build-logs.storage.googleapis.com/badges/harfbuzz.svg)](https://oss-fuzz-build-logs.storage.googleapis.com/index.html) +[![Coverity Scan Build Status](https://scan.coverity.com/projects/15166/badge.svg)](https://scan.coverity.com/projects/harfbuzz) +[![Codacy Badge](https://app.codacy.com/project/badge/Grade/89c872f5ce1c42af802602bfcd15d90a)](https://www.codacy.com/gh/harfbuzz/harfbuzz/dashboard?utm_source=github.com&utm_medium=referral&utm_content=harfbuzz/harfbuzz&utm_campaign=Badge_Grade) +[![Codecov Code Coverage](https://codecov.io/gh/harfbuzz/harfbuzz/branch/main/graph/badge.svg)](https://codecov.io/gh/harfbuzz/harfbuzz) +[![Packaging status](https://repology.org/badge/tiny-repos/harfbuzz.svg)](https://repology.org/project/harfbuzz/versions) + +# HarfBuzz + +HarfBuzz is a text shaping engine. It primarily supports [OpenType][1], but also +[Apple Advanced Typography][2]. HarfBuzz is used in Android, Chrome, +ChromeOS, Firefox, GNOME, GTK+, KDE, LibreOffice, OpenJDK, PlayStation, Qt, +XeTeX, and other places. + +For bug reports, mailing list, and other information please visit: + + http://harfbuzz.org/ + +For license information, see [COPYING](COPYING). + +## Documentation + +For user manual as well as API documentation, check: https://harfbuzz.github.io + +## Download + +For tarball releases of HarfBuzz, look [here][3]. At the same place you +will also find Win32/Win64 binary bundles that include libharfbuzz DLL, +hb-view.exe, hb-shape.exe, and all dependencies. + +The canonical source tree is available on [github][4]. + +The API that comes with `hb.h` will not change incompatibly. Other, peripheral, +headers are more likely to go through minor modifications, but again, we do our +best to never change API in an incompatible way. We will never break the ABI. + +If you are not sure whether Pango or HarfBuzz is right for you, read [Pango vs +HarfBuzz][5]. + +## Development + +For build information, see [BUILD.md](BUILD.md). + +For custom configurations, see [CONFIG.md](CONFIG.md). + +For testing and profiling, see [TESTING.md](TESTING.md). + +To get a better idea of where HarfBuzz stands in the text rendering stack you +may want to read [State of Text Rendering][6], though, that document is many +years old. Here are a few presentation slides about HarfBuzz at the +Internationalization and Unicode Conference over the years: + +* November 2014, [Unicode, OpenType, and HarfBuzz: Closing the Circle][7], +* October 2012, [HarfBuzz, The Free and Open Text Shaping Engine][8], +* October 2009, [HarfBuzz: the Free and Open Shaping Engine][9]. + +Both development and user support discussion around HarfBuzz happens on the +[github][4]. + +To report bugs or submit patches please use [github][4] issues and +pull-requests. + +For a comparison of old vs new HarfBuzz memory consumption see [this][10]. + + + +## Name + +HarfBuzz (حرف‌باز) is my Persian translation of “[OpenType][1]”, +transliterated using the Latin script. It sports a second meaning, but that +ain’t translatable. + +> Background: Originally there was this font format called TrueType. People and +> companies started calling their type engines all things ending in Type: +> FreeType, CoolType, ClearType, etc. And then came OpenType, which is the +> successor of TrueType. So, for my OpenType implementation, I decided to stick +> with the concept but use the Persian translation. Which is fitting given that +> Persian is written in the Arabic script, and OpenType is an extension of +> TrueType that adds support for complex script rendering, and HarfBuzz is an +> implementation of OpenType complex text shaping. + +
+ Packaging status of HarfBuzz + +[![Packaging status](https://repology.org/badge/vertical-allrepos/harfbuzz.svg?header=harfbuzz)](https://repology.org/project/harfbuzz/versions) + +
+ +[1]: https://docs.microsoft.com/en-us/typography/opentype/spec/ +[2]: https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6AATIntro.html +[3]: https://github.com/harfbuzz/harfbuzz/releases +[4]: https://github.com/harfbuzz/harfbuzz +[5]: http://mces.blogspot.com/2009/11/pango-vs-harfbuzz.html +[6]: http://behdad.org/text/ +[7]: https://goo.gl/FSIQuC +[8]: https://goo.gl/2wSRu +[9]: http://behdad.org/download/Presentations/slippy/harfbuzz_slides.pdf +[10]: https://goo.gl/woyty diff --git a/libs/harfbuzz/README.mingw.md b/libs/harfbuzz/README.mingw.md new file mode 100644 index 00000000..60629f6f --- /dev/null +++ b/libs/harfbuzz/README.mingw.md @@ -0,0 +1,58 @@ +For the development of HarfBuzz, the Microsoft shaping technology, Uniscribe, +as a widely used and tested shaper is used as more-or-less OpenType reference +implementation and that specially is important where OpenType specification +is or wasn't that clear. For having access to Uniscribe on Linux/macOS these +steps are recommended: + +You want to follow the 32bit instructions. The 64bit equivalents are included +for reference. + +1. Install Wine. + - Fedora: `dnf install wine`. + +2. Install `mingw-w64` compiler. + - Fedora, 32bit: `dnf install mingw32-gcc-c++` + - Fedora, 64bit: `dnf install mingw64-gcc-c++` + - Debian: `apt install g++-mingw-w64` + - Mac: `brew install mingw-w64` + +3. If you have drank the `meson` koolaid, look at `.ci/build-win32.sh` to see how to + invoke `meson` now, or just run that script. Otherwise, here's how to use the + old trusty autotools instead: + + a) Install dependencies. + - Fedora, 32bit: `dnf install mingw32-glib2 mingw32-cairo mingw32-freetype` + - Fedora, 64bit: `dnf install mingw64-glib2 mingw64-cairo mingw64-freetype` + + b) Configure: + - `NOCONFIGURE=1 ./autogen.sh && mkdir winbuild && cd winbuild` + - 32bit: `../mingw-configure.sh i686` + - 64bit: `../mingw-configure.sh x86_64` + + c) Build as usual: + - make + + d) Configure your wine to find system mingw libraries. See: + https://fedoraproject.org/wiki/MinGW/Configure_wine + +Now you can use `hb-shape` by `(cd win32build/util && wine hb-shape.exe)` +but if you like to shape with the Microsoft Uniscribe: + +4. Bring a 32bit version of `usp10.dll` for yourself from `C:\Windows\SysWOW64\usp10.dll` of your + Windows installation (assuming you have a 64-bit installation, otherwise + `C:\Windows\System32\usp10.dll`) that it is not a DirectWrite proxy + ([for more info](https://en.wikipedia.org/wiki/Uniscribe)). + Rule of thumb, your `usp10.dll` should have a size more than 500kb, otherwise + it is designed to work with DirectWrite which Wine can't work with its original one. + You want a Uniscribe from Windows 7 or older. + + Put the DLL in the folder you are going to run the next command, + +5. `WINEDLLOVERRIDES="usp10=n" wine hb-shape.exe fontname.ttf -u 0061,0062,0063 --shaper=uniscribe` + +(`0061,0062,0063` means `abc`, use test/shaping/hb-unicode-decode to generate ones you need) + +When you have built that, you can test HarfBuzz's native shaper against Uniscribe +following these instructions: + + https://github.com/harfbuzz/harfbuzz/issues/3671 diff --git a/libs/harfbuzz/README.python.md b/libs/harfbuzz/README.python.md new file mode 100644 index 00000000..7496f045 --- /dev/null +++ b/libs/harfbuzz/README.python.md @@ -0,0 +1,31 @@ +To enable HarfBuzz bindings for Python among other languages, make sure +you have latest version of gobject-introspection available. On Ubuntu, +you can install that this way: + +```bash +sudo apt-get install libgirepository1.0-dev +``` + +And then run `meson setup` and make sure that `Introspection` is reported +enabled in output. + +Compile and install. + +Make sure you have the installation lib dir in `LD_LIBRARY_PATH`, as needed +for the linker to find the library. + +Then make sure you also have `GI_TYPELIB_PATH` pointing to the resulting +`$prefix/lib/girepository-*` directory. + +Make sure you have pygobject installed. Then check that the following +import works in your Python interpreter: + +```python +from gi.repository import HarfBuzz +``` + +If it does, you are ready to call HarfBuzz from Python! Congratulations. +See [`src/sample.py`](src/sample.py). + +The Python API will change. Let us know on the mailing list if you are +using it, and send lots of feedback. diff --git a/libs/harfbuzz/RELEASING.md b/libs/harfbuzz/RELEASING.md new file mode 100644 index 00000000..8d5a4060 --- /dev/null +++ b/libs/harfbuzz/RELEASING.md @@ -0,0 +1,37 @@ +# HarfBuzz release walk-through checklist: + +- [ ] Open gitk and review changes since last release. + + - [ ] Print all public API changes: + `git diff $(git describe | sed 's/-.*//').. src/*.h` + + - [ ] Document them in NEWS. + All API and API semantic changes should be clearly marked as API additions, API changes, or API deletions. + + - [ ] Document deprecations. + Ensure all new API / deprecations are in listed correctly in docs/harfbuzz-sections.txt. + If release added new API, add entry for new API index at the end of docs/harfbuzz-docs.xml. + + If there's a backward-incompatible API change (including deletions for API used anywhere), that's a release blocker. + Do NOT release. + +- [ ] Based on severity of changes, decide whether it's a minor or micro release number bump. + +- [ ] Search for REPLACEME on the repository and replace it with the chosen version for the release. + +- [ ] Make sure you have correct date and new version at the top of NEWS file. + +- [ ] Bump version in line 3 of meson.build and configure.ac. + +- [ ] Do a `meson test -Cbuild` so it both checks the tests and updates hb-version.h (use `git diff` to see if is really updated). + +- [ ] Commit NEWS, meson.build, configure.ac, and src/hb-version.h, as well as any REPLACEME changes you made. + The commit message is simply the release number, e. g. "1.4.7" + +- [ ] Do a `meson dist -Cbuild` that runs the tests against the latest committed changes. + If doesn't pass, something fishy is going on, reset the repo and start over. + +- [ ] Tag the release and sign it: e.g. `git tag -s 1.4.7 -m 1.4.7`. + Enter your GPG password. + +- [ ] Push the commit and tag out: `git push --follow-tags`. diff --git a/libs/harfbuzz/TESTING.md b/libs/harfbuzz/TESTING.md new file mode 100644 index 00000000..18d70202 --- /dev/null +++ b/libs/harfbuzz/TESTING.md @@ -0,0 +1,47 @@ +## Build and Test + +```shell +meson build +ninja -Cbuild +meson test -Cbuild +``` + +### Debug with GDB + +```shell +meson test -Cbuild --gdb testname +``` + +## Build and Run + +Depending on what area you are working in change or add `HB_DEBUG_`. +Values defined in `hb-debug.hh`. + +```shell +CPPFLAGS='-DHB_DEBUG_SUBSET=100' meson setup build --reconfigure +meson test -C build +``` + +### Run tests with asan + +```shell +meson setup build -Db_sanitize=address --reconfigure +meson compile -C build +meson test -C build +``` + +### Enable Debug Logging + +```shell +CPPFLAGS=-DHB_DEBUG_SUBSET=100 meson build --reconfigure +ninja -C build +``` + +## Test with the Fuzzer + +FOr fuzzing, see `test/fuzzing/README.md`. + +## Profiling + +For profiling, see `perf/README.md`. + diff --git a/libs/harfbuzz/THANKS b/libs/harfbuzz/THANKS new file mode 100644 index 00000000..88cb7e9e --- /dev/null +++ b/libs/harfbuzz/THANKS @@ -0,0 +1,7 @@ +Bradley Grainger +Kenichi Ishibashi +Ivan Kuckir +Ryan Lortie +Jeff Muizelaar +suzuki toshiya +Philip Withnall diff --git a/libs/harfbuzz/autogen.sh b/libs/harfbuzz/autogen.sh new file mode 100644 index 00000000..085b4d86 --- /dev/null +++ b/libs/harfbuzz/autogen.sh @@ -0,0 +1,48 @@ +#!/bin/sh +# Run this to generate all the initial makefiles, etc. + +test -n "$srcdir" || srcdir=`dirname "$0"` +test -n "$srcdir" || srcdir=. + +olddir=`pwd` +cd $srcdir + +#printf "checking for ragel... " +#which ragel || { +# echo "You need to install ragel... See http://www.complang.org/ragel/" +# exit 1 +#} + +printf "checking for pkg-config... " +which pkg-config || { + echo "*** No pkg-config found, please install it ***" + exit 1 +} + +printf "checking for libtoolize... " +which glibtoolize || which libtoolize || { + echo "*** No libtoolize (libtool) found, please install it ***" + exit 1 +} +printf "checking for gtkdocize... " +if which gtkdocize ; then + gtkdocize --copy || exit 1 +else + echo "*** No gtkdocize (gtk-doc) found, skipping documentation ***" + echo "EXTRA_DIST = " > gtk-doc.make +fi + +printf "checking for autoreconf... " +which autoreconf || { + echo "*** No autoreconf (autoconf) found, please install it ***" + exit 1 +} + +echo "running autoreconf --force --install --verbose" +autoreconf --force --install --verbose || exit $? + +cd $olddir +test -n "$NOCONFIGURE" || { + echo "running configure $@" + "$srcdir/configure" "$@" +} diff --git a/libs/harfbuzz/configure.ac b/libs/harfbuzz/configure.ac new file mode 100644 index 00000000..f2480c34 --- /dev/null +++ b/libs/harfbuzz/configure.ac @@ -0,0 +1,486 @@ +AC_PREREQ([2.64]) +AC_INIT([HarfBuzz], + [5.3.1], + [https://github.com/harfbuzz/harfbuzz/issues/new], + [harfbuzz], + [http://harfbuzz.org/]) + +AC_CONFIG_MACRO_DIR([m4]) +AC_CONFIG_SRCDIR([src/harfbuzz.pc.in]) +AC_CONFIG_HEADERS([config.h]) + +AM_INIT_AUTOMAKE([1.13.0 gnits tar-ustar dist-xz no-dist-gzip -Wall no-define color-tests -Wno-portability]) +AM_SILENT_RULES([yes]) +AX_CODE_COVERAGE + +# Initialize libtool +m4_ifdef([AM_PROG_AR], [AM_PROG_AR]) +LT_PREREQ([2.2]) +LT_INIT([disable-static]) + +# Check for programs +AC_PROG_CC +AC_PROG_CC_C99 +AM_PROG_CC_C_O +AC_PROG_CXX +AX_CXX_COMPILE_STDCXX(11) +AC_SYS_LARGEFILE +PKG_PROG_PKG_CONFIG([0.28]) +AM_MISSING_PROG([RAGEL], [ragel]) +AM_MISSING_PROG([GIT], [git]) + +# Version +m4_define(hb_version_triplet,m4_split(AC_PACKAGE_VERSION,[[.]])) +m4_define(hb_version_major,m4_argn(1,hb_version_triplet)) +m4_define(hb_version_minor,m4_argn(2,hb_version_triplet)) +m4_define(hb_version_micro,m4_argn(3,hb_version_triplet)) +HB_VERSION_MAJOR=hb_version_major +HB_VERSION_MINOR=hb_version_minor +HB_VERSION_MICRO=hb_version_micro +HB_VERSION=AC_PACKAGE_VERSION +AC_SUBST(HB_VERSION_MAJOR) +AC_SUBST(HB_VERSION_MINOR) +AC_SUBST(HB_VERSION_MICRO) +AC_SUBST(HB_VERSION) + +# Libtool version +m4_define([hb_version_int], + m4_eval(hb_version_major*10000 + hb_version_minor*100 + hb_version_micro)) +HB_LIBTOOL_VERSION_INFO=hb_version_int:0:hb_version_int +AC_SUBST(HB_LIBTOOL_VERSION_INFO) + +AC_ARG_WITH([libstdc++], + [AS_HELP_STRING([--with-libstdc++=@<:@yes/no@:>@], + [Allow linking with libstdc++ @<:@default=no@:>@])], + [with_libstdcxx=$withval], + [with_libstdcxx=no]) +AM_CONDITIONAL(WITH_LIBSTDCXX, [test "x$with_libstdcxx" = "xyes"]) + +# Documentation +have_gtk_doc=false +m4_ifdef([GTK_DOC_CHECK], [ +GTK_DOC_CHECK([1.15],[--flavour no-tmpl]) + if test "x$enable_gtk_doc" = xyes; then + have_gtk_doc=true + fi +], [ + AM_CONDITIONAL([ENABLE_GTK_DOC], false) +]) + +# Functions and headers +AC_CHECK_FUNCS(atexit mprotect sysconf getpagesize mmap isatty newlocale uselocale) +AC_CHECK_HEADERS(unistd.h sys/mman.h stdbool.h xlocale.h) + +# Compiler flags +AC_CANONICAL_HOST +AC_CHECK_ALIGNOF([struct{char;}]) +if test "x$GCC" = "xyes"; then + + # Make symbols link locally + AX_CHECK_LINK_FLAG([[-Bsymbolic-functions]], [LDFLAGS="$LDFLAGS -Bsymbolic-functions"]) + + # Make it possible to not link to libstdc++ + # No threadsafe statics in C++ as we do it ourselves. + # We don't use these features, so it's safe to disable them + # even in the cases where we DO link to libstdc++. + # Put -fno-rtti before $CXXFLAGS such that users can re-enable it + # by overriding CXXFLAGS. + CXXFLAGS="-fno-rtti $CXXFLAGS -fno-exceptions -fno-threadsafe-statics" + + case "$host" in + *-*-mingw*) + ;; + *) + # Hide inline methods + CXXFLAGS="$CXXFLAGS -fvisibility-inlines-hidden" + ;; + esac + + case "$host" in + arm-*-*) + if test "x$ac_cv_alignof_struct_char__" != x1; then + # Request byte alignment + CXXFLAGS="$CXXFLAGS -mstructure-size-boundary=8" + fi + ;; + esac +fi + +AM_CONDITIONAL(HAVE_GCC, test "x$GCC" = "xyes") + +hb_os_win32=no +AC_MSG_CHECKING([for native Win32]) +case "$host" in + *-*-mingw*) + hb_os_win32=yes + ;; +esac +AC_MSG_RESULT([$hb_os_win32]) +AM_CONDITIONAL(OS_WIN32, test "$hb_os_win32" = "yes") + +have_pthread=false +AX_PTHREAD([have_pthread=true]) +if $have_pthread; then + AC_DEFINE(HAVE_PTHREAD, 1, [Have POSIX threads]) +fi +AM_CONDITIONAL(HAVE_PTHREAD, $have_pthread) + +dnl ========================================================================== + +AC_ARG_WITH(glib, + [AS_HELP_STRING([--with-glib=@<:@yes/no/auto@:>@], + [Use glib @<:@default=auto@:>@])],, + [with_glib=auto]) +have_glib=false +GLIB_DEPS="glib-2.0 >= 2.19.1" +AC_SUBST(GLIB_DEPS) +if test "x$with_glib" = "xyes" -o "x$with_glib" = "xauto"; then + PKG_CHECK_MODULES(GLIB, $GLIB_DEPS, have_glib=true, :) +fi +if test "x$with_glib" = "xyes" -a "x$have_glib" != "xtrue"; then + AC_MSG_ERROR([glib support requested but glib-2.0 not found]) +fi +if $have_glib; then + AC_DEFINE(HAVE_GLIB, 1, [Have glib2 library]) +fi +AM_CONDITIONAL(HAVE_GLIB, $have_glib) + +dnl =========================================================================== + +AC_ARG_WITH(gobject, + [AS_HELP_STRING([--with-gobject=@<:@yes/no/auto@:>@], + [Use gobject @<:@default=no@:>@])],, + [with_gobject=no]) +have_gobject=false +if test "x$with_gobject" = "xyes" -o "x$with_gobject" = "xauto"; then + PKG_CHECK_MODULES(GOBJECT, gobject-2.0 glib-2.0, have_gobject=true, :) +fi +if test "x$with_gobject" = "xyes" -a "x$have_gobject" != "xtrue"; then + AC_MSG_ERROR([gobject support requested but gobject-2.0 / glib-2.0 not found]) +fi +if $have_gobject; then + AC_DEFINE(HAVE_GOBJECT, 1, [Have gobject2 library]) + GLIB_MKENUMS=`$PKG_CONFIG --variable=glib_mkenums glib-2.0` + AC_SUBST(GLIB_MKENUMS) +fi +AM_CONDITIONAL(HAVE_GOBJECT, $have_gobject) +AC_SUBST(have_gobject) + +dnl =========================================================================== + + +dnl =========================================================================== +# Gobject-Introspection +have_introspection=false +m4_ifdef([GOBJECT_INTROSPECTION_CHECK], [ + if $have_gobject; then + GOBJECT_INTROSPECTION_CHECK([1.34.0]) + if test "x$found_introspection" = xyes; then + have_introspection=true + fi + else + AM_CONDITIONAL([HAVE_INTROSPECTION], false) + fi +], [ + AM_CONDITIONAL([HAVE_INTROSPECTION], false) +]) + +dnl ========================================================================== + +AC_ARG_WITH(cairo, + [AS_HELP_STRING([--with-cairo=@<:@yes/no/auto@:>@], + [Use cairo @<:@default=auto@:>@])],, + [with_cairo=auto]) +have_cairo=false +if test "x$with_cairo" = "xyes" -o "x$with_cairo" = "xauto"; then + PKG_CHECK_MODULES(CAIRO, cairo >= 1.8.0, have_cairo=true, :) + save_libs=$LIBS + LIBS="$LIBS $CAIRO_LIBS" + AC_CHECK_FUNCS(cairo_user_font_face_set_render_color_glyph_func) + LIBS=$save_libs +fi +if test "x$with_cairo" = "xyes" -a "x$have_cairo" != "xtrue"; then + AC_MSG_ERROR([cairo support requested but not found]) +fi +if $have_cairo; then + AC_DEFINE(HAVE_CAIRO, 1, [Have cairo graphics library]) +fi +AM_CONDITIONAL(HAVE_CAIRO, $have_cairo) + +have_cairo_ft=false +if $have_cairo; then + PKG_CHECK_MODULES(CAIRO_FT, cairo-ft, have_cairo_ft=true, :) +fi +if $have_cairo_ft; then + AC_DEFINE(HAVE_CAIRO_FT, 1, [Have cairo-ft support in cairo graphics library]) +fi +AM_CONDITIONAL(HAVE_CAIRO_FT, $have_cairo_ft) + +dnl ========================================================================== + +AC_ARG_WITH(chafa, + [AS_HELP_STRING([--with-chafa=@<:@yes/no/auto@:>@], + [Use chafa @<:@default=auto@:>@])],, + [with_chafa=auto]) +have_chafa=false +if test "x$with_chafa" = "xyes" -o "x$with_chafa" = "xauto"; then + PKG_CHECK_MODULES(CHAFA, chafa >= 1.6.0, have_chafa=true, :) +fi +if test "x$with_chafa" = "xyes" -a "x$have_chafa" != "xtrue"; then + AC_MSG_ERROR([chafa support requested but not found]) +fi +if $have_chafa; then + AC_DEFINE(HAVE_CHAFA, 1, [Have chafa terminal graphics library]) +fi +AM_CONDITIONAL(HAVE_CHAFA, $have_chafa) + +dnl ========================================================================== + +AC_ARG_WITH(icu, + [AS_HELP_STRING([--with-icu=@<:@yes/no/builtin/auto@:>@], + [Use ICU @<:@default=auto@:>@])],, + [with_icu=auto]) +have_icu=false +if test "x$with_icu" = "xyes" -o "x$with_icu" = "xbuiltin" -o "x$with_icu" = "xauto"; then + PKG_CHECK_MODULES(ICU, icu-uc, have_icu=true, :) +fi +if test \( "x$with_icu" = "xyes" -o "x$with_icu" = "xbuiltin" \) -a "x$have_icu" != "xtrue"; then + AC_MSG_ERROR([icu support requested but icu-uc not found]) +fi + +if $have_icu; then + CXXFLAGS="$CXXFLAGS `$PKG_CONFIG --variable=CXXFLAGS icu-uc`" + AC_DEFINE(HAVE_ICU, 1, [Have ICU library]) + if test "x$with_icu" = "xbuiltin"; then + AC_DEFINE(HAVE_ICU_BUILTIN, 1, [Use hb-icu Unicode callbacks]) + fi +fi +AM_CONDITIONAL(HAVE_ICU, $have_icu) +AM_CONDITIONAL(HAVE_ICU_BUILTIN, $have_icu && test "x$with_icu" = "xbuiltin") + +dnl =========================================================================== + +AC_ARG_WITH(graphite2, + [AS_HELP_STRING([--with-graphite2=@<:@yes/no/auto@:>@], + [Use the graphite2 library @<:@default=no@:>@])],, + [with_graphite2=no]) +have_graphite2=false +GRAPHITE2_DEPS="graphite2 >= 1.2.0" +AC_SUBST(GRAPHITE2_DEPS) +if test "x$with_graphite2" = "xyes" -o "x$with_graphite2" = "xauto"; then + PKG_CHECK_MODULES(GRAPHITE2, $GRAPHITE2_DEPS, have_graphite2=true, :) + if test "x$have_graphite2" != "xtrue"; then + # If pkg-config is not available, graphite2 can still be there + ac_save_CFLAGS="$CFLAGS" + ac_save_CPPFLAGS="$CPPFLAGS" + CFLAGS="$CFLAGS $GRAPHITE2_CFLAGS" + CPPFLAGS="$CPPFLAGS $GRAPHITE2_CFLAGS" + AC_CHECK_HEADER(graphite2/Segment.h, have_graphite2=true, :) + CPPFLAGS="$ac_save_CPPFLAGS" + CFLAGS="$ac_save_CFLAGS" + fi +fi +if test "x$with_graphite2" = "xyes" -a "x$have_graphite2" != "xtrue"; then + AC_MSG_ERROR([graphite2 support requested but libgraphite2 not found]) +fi +if $have_graphite2; then + AC_DEFINE(HAVE_GRAPHITE2, 1, [Have Graphite2 library]) +fi +AM_CONDITIONAL(HAVE_GRAPHITE2, $have_graphite2) + +dnl ========================================================================== + +AC_ARG_WITH(freetype, + [AS_HELP_STRING([--with-freetype=@<:@yes/no/auto@:>@], + [Use the FreeType library @<:@default=auto@:>@])],, + [with_freetype=auto]) +have_freetype=false +FREETYPE_DEPS="freetype2 >= 12.0.6" +AC_SUBST(FREETYPE_DEPS) +if test "x$with_freetype" = "xyes" -o "x$with_freetype" = "xauto"; then + # See freetype/docs/VERSION.DLL; 12.0.6 means freetype-2.4.2 + PKG_CHECK_MODULES(FREETYPE, $FREETYPE_DEPS, have_freetype=true, :) +fi +if test "x$with_freetype" = "xyes" -a "x$have_freetype" != "xtrue"; then + AC_MSG_ERROR([FreeType support requested but libfreetype2 not found]) +fi +if $have_freetype; then + AC_DEFINE(HAVE_FREETYPE, 1, [Have FreeType 2 library]) + save_libs=$LIBS + LIBS="$LIBS $FREETYPE_LIBS" + AC_CHECK_FUNCS(FT_Get_Var_Blend_Coordinates FT_Set_Var_Blend_Coordinates FT_Done_MM_Var FT_Get_Transform) + LIBS=$save_libs +fi +AM_CONDITIONAL(HAVE_FREETYPE, $have_freetype) + +dnl =========================================================================== + +AC_ARG_WITH(uniscribe, + [AS_HELP_STRING([--with-uniscribe=@<:@yes/no/auto@:>@], + [Use the Uniscribe library @<:@default=no@:>@])],, + [with_uniscribe=no]) +have_uniscribe=false +if test "x$with_uniscribe" = "xyes" -o "x$with_uniscribe" = "xauto"; then + AC_CHECK_HEADERS(usp10.h windows.h, have_uniscribe=true) +fi +if test "x$with_uniscribe" = "xyes" -a "x$have_uniscribe" != "xtrue"; then + AC_MSG_ERROR([uniscribe support requested but not found]) +fi +if $have_uniscribe; then + UNISCRIBE_CFLAGS= + UNISCRIBE_LIBS="-lusp10 -lgdi32 -lrpcrt4" + AC_SUBST(UNISCRIBE_CFLAGS) + AC_SUBST(UNISCRIBE_LIBS) + AC_DEFINE(HAVE_UNISCRIBE, 1, [Have Uniscribe library]) +fi +AM_CONDITIONAL(HAVE_UNISCRIBE, $have_uniscribe) + +dnl =========================================================================== + +AC_ARG_WITH(gdi, + [AS_HELP_STRING([--with-gdi=@<:@yes/no/auto@:>@], + [Provide GDI integration helpers @<:@default=no@:>@])],, + [with_gdi=no]) +have_gdi=false +if test "x$with_gdi" = "xyes" -o "x$with_gdi" = "xauto"; then + AC_CHECK_HEADERS(windows.h, have_gdi=true) +fi +if test "x$with_gdi" = "xyes" -a "x$have_gdi" != "xtrue"; then + AC_MSG_ERROR([gdi support requested but not found]) +fi +if $have_gdi; then + GDI_CFLAGS= + GDI_LIBS="-lgdi32" + AC_SUBST(GDI_CFLAGS) + AC_SUBST(GDI_LIBS) + AC_DEFINE(HAVE_GDI, 1, [Have GDI library]) +fi +AM_CONDITIONAL(HAVE_GDI, $have_gdi) + +dnl =========================================================================== + +AC_ARG_WITH(directwrite, + [AS_HELP_STRING([--with-directwrite=@<:@yes/no/auto@:>@], + [Use the DirectWrite library (experimental) @<:@default=no@:>@])],, + [with_directwrite=no]) +have_directwrite=false +AC_LANG_PUSH([C++]) +if test "x$with_directwrite" = "xyes" -o "x$with_directwrite" = "xauto"; then + AC_CHECK_HEADERS(dwrite_1.h, have_directwrite=true) +fi +AC_LANG_POP([C++]) +if test "x$with_directwrite" = "xyes" -a "x$have_directwrite" != "xtrue"; then + AC_MSG_ERROR([directwrite support requested but not found]) +fi +if $have_directwrite; then + AC_DEFINE(HAVE_DIRECTWRITE, 1, [Have DirectWrite library]) +fi +AM_CONDITIONAL(HAVE_DIRECTWRITE, $have_directwrite) + +dnl =========================================================================== + +AC_ARG_WITH(coretext, + [AS_HELP_STRING([--with-coretext=@<:@yes/no/auto@:>@], + [Use CoreText @<:@default=no@:>@])],, + [with_coretext=no]) +have_coretext=false +if test "x$with_coretext" = "xyes" -o "x$with_coretext" = "xauto"; then + AC_CHECK_TYPE(CTFontRef, have_coretext=true,, [#include ]) + + if $have_coretext; then + CORETEXT_CFLAGS= + CORETEXT_LIBS="-framework ApplicationServices" + AC_SUBST(CORETEXT_CFLAGS) + AC_SUBST(CORETEXT_LIBS) + else + # On iOS CoreText and CoreGraphics are stand-alone frameworks + if test "x$have_coretext" != "xtrue"; then + # Check for a different symbol to avoid getting cached result. + AC_CHECK_TYPE(CTRunRef, have_coretext=true,, [#include ]) + fi + + if $have_coretext; then + CORETEXT_CFLAGS= + CORETEXT_LIBS="-framework CoreText -framework CoreGraphics -framework CoreFoundation" + AC_SUBST(CORETEXT_CFLAGS) + AC_SUBST(CORETEXT_LIBS) + fi + fi +fi +if test "x$with_coretext" = "xyes" -a "x$have_coretext" != "xtrue"; then + AC_MSG_ERROR([CoreText support requested but libcoretext not found]) +fi +if $have_coretext; then + AC_DEFINE(HAVE_CORETEXT, 1, [Have Core Text backend]) +fi +AM_CONDITIONAL(HAVE_CORETEXT, $have_coretext) + +dnl =========================================================================== + +AC_CONFIG_FILES([ +Makefile +src/Makefile +src/harfbuzz-config.cmake +util/Makefile +test/Makefile +test/api/Makefile +test/fuzzing/Makefile +test/shape/Makefile +test/shape/data/Makefile +test/shape/data/aots/Makefile +test/shape/data/in-house/Makefile +test/shape/data/text-rendering-tests/Makefile +test/subset/Makefile +test/subset/data/Makefile +test/subset/data/repack_tests/Makefile +test/threads/Makefile +perf/Makefile +docs/Makefile +docs/version.xml +]) + +AC_OUTPUT + +echo +echo "C++ compiler version:" +$CXX --version +echo + +AC_MSG_NOTICE([ + +Autotools is no longer our supported build system for building the library +for *nix distributions, please migrate to meson. + +]) + + +AC_MSG_NOTICE([ + +Build configuration: + +Unicode callbacks (you want at least one): + Builtin true + Glib: ${have_glib} + ICU: ${have_icu} + +Font callbacks (the more the merrier): + FreeType: ${have_freetype} + +Tools used for command-line utilities: + Cairo: ${have_cairo} + Chafa: ${have_chafa} + +Additional shapers: + Graphite2: ${have_graphite2} + +Platform shapers (not normally needed): + CoreText: ${have_coretext} + DirectWrite: ${have_directwrite} + GDI: ${have_gdi} + Uniscribe: ${have_uniscribe} + +Other features: + Documentation: ${enable_gtk_doc} + GObject bindings: ${have_gobject} + Introspection: ${have_introspection} +]) diff --git a/libs/harfbuzz/docs/HarfBuzz.png b/libs/harfbuzz/docs/HarfBuzz.png new file mode 100644 index 00000000..c9d2e7b6 Binary files /dev/null and b/libs/harfbuzz/docs/HarfBuzz.png differ diff --git a/libs/harfbuzz/docs/HarfBuzz.svg b/libs/harfbuzz/docs/HarfBuzz.svg new file mode 100644 index 00000000..beb13add --- /dev/null +++ b/libs/harfbuzz/docs/HarfBuzz.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/libs/harfbuzz/docs/Makefile.am b/libs/harfbuzz/docs/Makefile.am new file mode 100644 index 00000000..36da8ae3 --- /dev/null +++ b/libs/harfbuzz/docs/Makefile.am @@ -0,0 +1,123 @@ +# Process this file with automake to produce Makefile.in + +# We require automake 1.6 at least. +AUTOMAKE_OPTIONS = 1.6 + +# This is a blank Makefile.am for using gtk-doc. +# Copy this to your project's API docs directory and modify the variables to +# suit your project. See the GTK+ Makefiles in gtk+/docs/reference for examples +# of using the various options. + +# The name of the module, e.g. 'glib'. +DOC_MODULE=harfbuzz + +# Uncomment for versioned docs and specify the version of the module, e.g. '2'. +#DOC_MODULE_VERSION=$(HB_VERSION_MAJOR) + +# The top-level SGML file. You can change this if you want to. +DOC_MAIN_SGML_FILE=$(DOC_MODULE)-docs.xml + +# Directories containing the source code. +# gtk-doc will search all .c and .h files beneath these paths +# for inline comments documenting functions and macros. +# e.g. DOC_SOURCE_DIR=$(top_srcdir)/gtk $(top_srcdir)/gdk +DOC_SOURCE_DIR=$(top_srcdir)/src $(top_builddir)/src + +# Extra options to pass to gtkdoc-scangobj. Not normally needed. +SCANGOBJ_OPTIONS= + +# Extra options to supply to gtkdoc-scan. +# e.g. SCAN_OPTIONS=--deprecated-guards="GTK_DISABLE_DEPRECATED" +SCAN_OPTIONS=--rebuild-types --deprecated-guards="HB_DISABLE_DEPRECATED" \ + --ignore-decorators='HB_EXTERN|HB_DEPRECATED' + +# Header files or dirs to ignore when scanning. Use base file/dir names +# e.g. IGNORE_HFILES=gtkdebug.h gtkintl.h private_code +IGNORE_HFILES=`cd $(top_srcdir)/src; find . -path './*/*.h' | sed 's@^.*/@@'` +IGNORE_HFILES+=hb-gobject.h hb-gobject-enums.h hb-gobject-structs.h + +# Extra options to supply to gtkdoc-mkdb. +# e.g. MKDB_OPTIONS=--xml-mode --output-format=xml +MKDB_OPTIONS=--source-suffixes=h,cc --xml-mode --output-format=xml --ignore-files="$(IGNORE_HFILES)" + +# Extra options to supply to gtkdoc-mktmpl +# e.g. MKTMPL_OPTIONS=--only-section-tmpl +MKTMPL_OPTIONS= + +# Extra options to supply to gtkdoc-mkhtml +MKHTML_OPTIONS= + +# Extra options to supply to gtkdoc-fixref. Not normally needed. +# e.g. FIXXREF_OPTIONS=--extra-dir=../gdk-pixbuf/html --extra-dir=../gdk/html +FIXXREF_OPTIONS= + +# Used for dependencies. The docs will be rebuilt if any of these change. +# e.g. HFILE_GLOB=$(top_srcdir)/gtk/*.h +# e.g. CFILE_GLOB=$(top_srcdir)/gtk/*.c +HFILE_GLOB=$(top_srcdir)/src/hb.h $(top_srcdir)/src/hb-*.h +CFILE_GLOB=$(top_srcdir)/src/hb-*.cc + +# Extra header to include when scanning, which are not under DOC_SOURCE_DIR +# e.g. EXTRA_HFILES=$(top_srcdir}/contrib/extra.h + +# Images to copy into HTML directory. +# e.g. HTML_IMAGES=$(top_srcdir)/gtk/stock-icons/stock_about_24.png +HTML_IMAGES= \ + HarfBuzz.png \ + HarfBuzz.svg + +# Extra SGML files that are included by $(DOC_MAIN_SGML_FILE). +# e.g. content_files=running.sgml building.sgml changes-2.0.sgml +content_files= \ + usermanual-what-is-harfbuzz.xml \ + usermanual-install-harfbuzz.xml \ + usermanual-getting-started.xml \ + usermanual-glyph-information.xml \ + usermanual-shaping-concepts.xml \ + usermanual-object-model.xml \ + usermanual-buffers-language-script-and-direction.xml \ + usermanual-fonts-and-faces.xml \ + usermanual-opentype-features.xml \ + usermanual-clusters.xml \ + usermanual-utilities.xml \ + usermanual-integration.xml \ + version.xml + +# SGML files where gtk-doc abbreviations (#GtkWidget) are expanded +# These files must be listed here *and* in content_files +# e.g. expand_content_files=running.sgml +expand_content_files= + +# CFLAGS and LDFLAGS for compiling gtkdoc-scangobj with your library. +# Only needed if you are using gtkdoc-scangobj to dynamically query widget +# signals and properties. +# e.g. GTKDOC_CFLAGS=-I$(top_srcdir) -I$(top_builddir) $(GTK_DEBUG_FLAGS) +# e.g. GTKDOC_LIBS=$(top_builddir)/gtk/$(gtktargetlib) +GTKDOC_CFLAGS= +GTKDOC_LIBS=$(top_builddir)/src/libharfbuzz.la +if HAVE_GOBJECT +GTKDOC_LIBS+=$(top_builddir)/src/libharfbuzz-gobject.la +endif + +# This includes the standard gtk-doc make rules, copied by gtkdocize. +include $(top_srcdir)/gtk-doc.make + +# Other files to distribute +# e.g. EXTRA_DIST += version.xml.in +EXTRA_DIST += version.xml.in meson.build + +# Files not to distribute +# for --rebuild-types in $(SCAN_OPTIONS), e.g. $(DOC_MODULE).types +# for --rebuild-sections in $(SCAN_OPTIONS) e.g. $(DOC_MODULE)-sections.txt +#DISTCLEANFILES += + +# Comment this out if you don't want 'make check' to test you doc status +# and run some sanity checks +if ENABLE_GTK_DOC +TESTS_ENVIRONMENT = cd $(srcdir) && \ + DOC_MODULE=$(DOC_MODULE) DOC_MAIN_SGML_FILE=$(DOC_MAIN_SGML_FILE) \ + SRCDIR=$(abs_srcdir) BUILDDIR=$(abs_builddir) +#TESTS = $(GTKDOC_CHECK) +endif + +-include $(top_srcdir)/git.mk diff --git a/libs/harfbuzz/docs/features.dot b/libs/harfbuzz/docs/features.dot new file mode 100644 index 00000000..88cb3fae --- /dev/null +++ b/libs/harfbuzz/docs/features.dot @@ -0,0 +1,259 @@ +digraph { + graph [outputorder=edgefirst]; + node [shape="record", fontname="Noto Sans Mono SemiBold", fontsize=15]; + edge [fontname="Verdana", fontsize=12,labeldistance=7.5 ]; + fontname="Verdana"; + ranksep=0.02; nodesep=0.5; + +subgraph { + ranksep="0.02 equally"; + preprocessing[style=filled,fillcolor="lightgreen",fontname="Verdana",label="Glyph pre-processing"]; + orthographic[style=filled,fillcolor="lightblue",fontname="Verdana",label="Orthographic Unit Shaping"]; + reordering[style=filled, fillcolor="lightcoral",fontname="Verdana",label="Reordering group (USE)"]; + topographic[style=filled,fillcolor="lightgoldenrod",fontname="Verdana",label="Topographical Features‡"]; + typographic[style=filled,fillcolor="lightpink",fontname="Verdana",label="Typographic Presentation"]; + positioning[style=filled,fillcolor="lightsalmon",fontname="Verdana",label="Positioning"]; + preprocessing->reordering->orthographic->topographic->typographic->positioning; +} + + decision1 [shape="diamond", label="Script\ndirection?",fontname="Verdana"]; + rvrn->decision1; + + ltrfeatures [label="{ltra|ltrm}", fillcolor="lightgreen",style="filled"]; + { + rtlfeatures [label="{rtla|rtlm¹}", fillcolor="lightgreen",style="filled"]; + } + { + rank=same; + fracfeatures [label="frac²|numr³|dnom⁴", fillcolor="lightpink",style="filled"]; + fracnotes [fontname="Verdana",shape=plaintext,label=< + + + + +
¹ rtlm is scoped to characters with a Unicode mirroring property
² frac is scoped to numr + the slash + dnom
³ numr is scoped to all decimal numbers before a U+2044 FRACTION SLASH.
⁴ dnom is scoped to all decimal numbers after a U+2044 FRACTION SLASH.
+ >]; + + } + rand [fillcolor="lightpink",style="filled"]; + + decision1 -> ltrfeatures [label="Left-to-right"]; + decision1 -> rtlfeatures [label="Right-to-left"]; + + decision1 -> fracfeatures [label="Other"]; + + ltrfeatures -> fracfeatures; + rtlfeatures -> fracfeatures; + fracfeatures->rand; + + decision2 [shape="diamond", label="Script?",fontname="Verdana"]; + +{rank=same; HARF [label="{Harf|HARF}"]; notes;} + rand -> trak -> HARF -> decision2; + + commonfeatures [shape=none,label=< + + + + + + + + + +
abvmblwmccmploclmarkmkmkrlig
> + ]; + + decision3 [shape="diamond", label="Script\ndirection?",fontname="Verdana"]; + + BUZZ [label="{Buzz|BUZZ}"]; + BUZZ -> commonfeatures -> decision3; + + horizontalfeatures [ + shape=none,label=< + + + + + + + +
calt (not Hangul)
clig (not Khmer)
curs
dist
kern
liga (not Khmer)
rclt
> + ]; + vert [label="vert",style=filled,fillcolor="lightpink"]; + + decision3 -> horizontalfeatures [label="Horizontal"]; + decision3 -> vert [label="Vertical"]; + + discretionary [label="User-selected\ndiscretionary\nfeatures",fontname="Verdana"]; + + horizontalfeatures -> discretionary; + vert -> discretionary; + + decision2->stch; + + BUZZ; + +subgraph shapers { + subgraph cluster_arabic { + bgcolor="lightyellow" + label="Arabic, Syriac"; + stch [ style="filled", fillcolor="lightgreen",label="stch"]; + ccmplocl [ style="filled", label="ccmp|locl", fillcolor="lightgreen"]; + arabicfeatures [label="isol|fina|fin2|fin3|medi|med2|init", style="filled", fillcolor="lightgoldenrod"]; + arabicfeatures2 [label="rclt|calt", style="filled",fillcolor="lightpink"]; +rlig[style="filled",fillcolor="lightpink"]; +mset [fillcolor="lightpink",style="filled"] + stch->ccmplocl->arabicfeatures->rlig->arabicfeatures2->mset; + } + mset->BUZZ:n; + + subgraph cluster_hangul { + bgcolor="lightyellow" + label="Hangul"; + hangulfeatures [label="ljmo|vjmo|tjmo", style="filled",fillcolor="lightgoldenrod"] + } + hangulfeatures->BUZZ:n; + + subgraph cluster_indic { + label="Indic"; + bgcolor="lightyellow" + // Preprocessing + loclccmpindic [label="locl†|ccmp†",style=filled,fillcolor="lightgreen"]; + node[style=filled,fillcolor="lightgreen"]; + nukt [label="nukt†"]; + akhn [label="akhn†"]; + loclccmpindic->indic_reorder_1->nukt->akhn; + indic_reorder_1[label="Initial reordering", fontname="Verdana",fillcolor="lightgrey",shape=ellipse,style=filled] + + // Orthographic + node[style=filled,fillcolor="lightblue"] + rphf [label="rphf⁵"]; + rkpf [label="rkpf†"]; + pref [label="pref⁶"]; + blwf [label="blwf⁷"]; + abvf [label="abvf⁸"]; + half [label="half⁹"]; + pstf [label="pstf⁸"]; + vatu [label="vatu†"]; + cjct [label="cjct†"]; + akhn ->rphf -> rkpf -> pref -> blwf -> abvf -> half -> pstf -> vatu -> cjct; + // Typographic presentation + indic_typographic[style=filled,fillcolor="lightpink",label="init|pres|abvs|blws|psts|haln"] + indic_reorder_2[label="Final reordering",fillcolor="lightgrey",fontname="Verdana", shape=ellipse,style=filled] + cjct->indic_reorder_2->indic_typographic; + + notes2 [fontname="Verdana",shape=plaintext,style="",label=< + + + + + +
⁵ rphf is scoped to pre-base ra+halant sequences
⁶ pref is scoped to the two glyphs after the base; outputs are reordered
⁷ blwf is usually scoped to the whole syllable, except in Telugu and Kannada where it is post-base
⁸ abvf and pstf are scoped to post-base
⁹ half is scoped to pre-base
+>]; + indic_typographic -> notes2 [style=invis]; + } + + + subgraph cluster_khmer { + label="Khmer"; + bgcolor="lightyellow" + + khmerbasic [style=filled,fillcolor="lightgreen",label="locl†|ccmp†|pref†|bwlf†|abvf†|pstf†|cfar†"] + khmerother [style=filled,fillcolor="lightpink",label="pres|abvs|blws|psts"] + khmerbasic -> khmerother -> khmerclig; + khmerclig [label="clig",style=filled,fillcolor="lightpink"]; + } + + subgraph cluster_myanmar { + label="Myanmar"; + bgcolor="lightyellow" + loclccmpmyanmar [label="locl†|ccmp†",style=filled,fillcolor="lightgreen"]; + rphfmymr [label="rphf†",style=filled,fillcolor="lightblue"] + prefmymr [label="pref†",style=filled,fillcolor="lightblue"] + blwfmymr [label="blwf†",style=filled,fillcolor="lightblue"] + pstfmymr [label="pstf†",style=filled,fillcolor="lightblue"] + myanmarother [label="pres|abvs|blws|psts",style=filled,fillcolor="lightpink"]; + reorder_myanmar[label="Reordering", shape=ellipse,style=filled,fontname="Verdana"] + loclccmpmyanmar -> reorder_myanmar-> rphfmymr -> prefmymr -> blwfmymr -> pstfmymr -> myanmarother; + } + + subgraph cluster_use { + label="Universal Shaping Engine" + bgcolor="lightyellow" + use_preprocessing [style=filled, label="locl†|ccmp†|nukt†|akhn†", fillcolor="lightgreen"]; + // Reoredering + rphfuse [label="rphf¹⁰", style=filled, fillcolor="lightcoral"]; + prefuse [label="pref¹¹", style=filled, fillcolor="lightcoral"]; + // Orthographic + orthographicuse [label="rkrf†|abvf†|blwf†|half†|pstf†|vatu†|cjct†", style="filled", fillcolor="lightblue"]; + topographicaluse [label="isol|init|medi|fina", style="filled", fillcolor="lightgoldenrod"]; + typographicaluse [label="abvs|blws|haln|pres|psts", style="filled", fillcolor="lightpink"]; + reorder_use[label="Reordering", shape=ellipse,style=filled,fontname="Verdana"] + use_preprocessing -> rphfuse -> prefuse->orthographicuse ->reorder_use -> topographicaluse -> typographicaluse; + notes3 [fontname="Verdana",shape=plaintext,label=< + + +
¹⁰ Outputs are reordered as category R
¹¹ Outputs are reordered to before base
+ >]; + typographicaluse -> notes3 [style=invis]; + } + +} + + indic_typographic->BUZZ:n; + typographicaluse->BUZZ:n; + khmerclig -> BUZZ:n; + myanmarother -> BUZZ:n; + + + decision2->hangulfeatures; + decision2->loclccmpindic; + decision2->khmerbasic; + decision2->loclccmpmyanmar; + decision2->use_preprocessing; + decision2->BUZZ [label=" Hebrew, Thai,\n Lao, other"]; + +notes [fontname="Verdana",shape=box,label=< + + + + + + + + + +
+Indic scripts are: Bengali, Devanagari, + Gujarati, Gurmukhi, Kannada, + Malayalam, Oriya, Tamil, + Telugu +
+USE scripts are: + Adlam, Ahom, Balinese, Batak, Bhaiksuki, Brahmi, Buginese, + Buhid, Chakma, Cham, Chorasmian, Dives Akuru, Dogra, Duployan, +
+Egyptian hieroglyphs, Elymaic, Grantha, Gunjala Ggondi, Hanifi Rohingya, + Hanunoo, Javanese, Kaithi, Kayah li, Kharoshthi, Khojki, +
+Khudawadi, Lepcha, Limbu, Mahajani, Makasar, Mandaic, Manichaean, + Marchen, Masaram Gondi, Medefaidrin, Meetei Mayek, Miao, Modi, +
+Mongolian, Multani, Nandinagari, Newa, Nko, Nyiakeng Puachue Hmong, + Old Sogdian, Pahawh Hmong, Phags Pa, Psalter Pahlavi, Rejang, +
+Saurashtra, Sharada, Siddham, Sinhala, Sogdian, Soyombo, Sundanese, + Syloti Nagri, Tagalog, Tagbanwa, Tai Le, Tai Tham, Tai Viet, +
+Takri, Tibetan, Tifinagh, Tirhuta, Wancho, Zanabazar square, +
>] + + + footnote[fontname="Verdana",label=< + + +
† Feature is scoped to each syllable
‡ All topographic features are scoped based on topographic position
>]; + notes3->footnote[style=invis]; + +} diff --git a/libs/harfbuzz/docs/harfbuzz-docs.xml b/libs/harfbuzz/docs/harfbuzz-docs.xml new file mode 100644 index 00000000..652a5e47 --- /dev/null +++ b/libs/harfbuzz/docs/harfbuzz-docs.xml @@ -0,0 +1,213 @@ + + + +]> + + + HarfBuzz Manual + + HarfBuzz + + + HarfBuzz is a text shaping library. Using the HarfBuzz library allows + programs to convert a sequence of Unicode input into + properly formatted and positioned glyph output—for any writing + system and language. + + + + The canonical source-code tree is available at + github.com/harfbuzz/harfbuzz. + See for + release tarballs. + + + + + + User's manual + + + + + + + + + + + + + + + + + This document is for HarfBuzz &version;. + + + + + Reference manual + + Core API + + + + + + + + + + + + + + + + + OpenType API + + + + + + + + + + + + + Apple Advanced Typography API + + + + + Integration API + + + + + + + + + + + + Style API + + + + + Subset API + + + + + + + API Index + Index of deprecated API + + Index of new symbols in 5.3.0 + Index of new symbols in 5.0.0 + Index of new symbols in 4.4.0 + Index of new symbols in 4.3.0 + Index of new symbols in 4.2.0 + Index of new symbols in 4.1.0 + Index of new symbols in 4.0.0 + Index of new symbols in 3.4.0 + Index of new symbols in 3.3.0 + Index of new symbols in 3.1.0 + Index of new symbols in 3.0.0 + Index of new symbols in 2.9.1 + Index of new symbols in 2.9.0 + Index of new symbols in 2.8.2 + Index of new symbols in 2.7.3 + Index of new symbols in 2.6.8 + Index of new symbols in 2.6.5 + Index of new symbols in 2.6.3 + Index of new symbols in 2.6.0 + Index of new symbols in 2.5.0 + Index of new symbols in 2.4.0 + Index of new symbols in 2.3.0 + Index of new symbols in 2.2.0 + Index of new symbols in 2.1.0 + Index of new symbols in 2.0.0 + Index of new symbols in 1.9.0 + Index of new symbols in 1.8.6 + Index of new symbols in 1.8.5 + Index of new symbols in 1.8.1 + Index of new symbols in 1.8.0 + Index of new symbols in 1.7.7 + Index of new symbols in 1.7.2 + Index of new symbols in 1.6.0 + Index of new symbols in 1.5.0 + Index of new symbols in 1.4.3 + Index of new symbols in 1.4.2 + Index of new symbols in 1.4.0 + Index of new symbols in 1.3.3 + Index of new symbols in 1.2.3 + Index of new symbols in 1.1.3 + Index of new symbols in 1.1.2 + Index of new symbols in 1.0.5 + Index of new symbols in 0.9.42 + Index of new symbols in 0.9.41 + Index of new symbols in 0.9.39 + Index of new symbols in 0.9.38 + Index of new symbols in 0.9.33 + Index of new symbols in 0.9.31 + Index of new symbols in 0.9.30 + Index of new symbols in 0.9.28 + Index of new symbols in 0.9.26 + Index of new symbols in 0.9.22 + Index of new symbols in 0.9.21 + Index of new symbols in 0.9.20 + Index of new symbols in 0.9.11 + Index of new symbols in 0.9.10 + Index of new symbols in 0.9.8 + Index of new symbols in 0.9.7 + Index of new symbols in 0.9.5 + Index of new symbols in 0.9.2 + Index of new symbols in 0.6.0 + + + + + + + The current HarfBuzz codebase is versioned 2.x.x and is stable + and under active maintenance. This is what is used in latest + versions of Firefox, GNOME, ChromeOS, Chrome, LibreOffice, + XeTeX, Android, and KDE, among other places. + + + Prior to 2012, the original HarfBuzz codebase (which, these days, is + referred to as harfbuzz-old) was derived from code + in FreeType, + Pango, and + Qt. + It is not actively developed or maintained, and is + extremely buggy. All users of harfbuzz-old are encouraged to switch over + to the new HarfBuzz as soon as possible. + + + To make this distinction clearer in discussions, the current HarfBuzz + codebase is sometimes referred to as harfbuzz-ng. + + + For reference purposes, the harfbuzz-old source tree is archived + here. + There are no release tarballs of harfbuzz-old whatsoever. + + + + diff --git a/libs/harfbuzz/docs/harfbuzz-overrides.txt b/libs/harfbuzz/docs/harfbuzz-overrides.txt new file mode 100644 index 00000000..e69de29b diff --git a/libs/harfbuzz/docs/harfbuzz-sections.txt b/libs/harfbuzz/docs/harfbuzz-sections.txt new file mode 100644 index 00000000..3a7aa358 --- /dev/null +++ b/libs/harfbuzz/docs/harfbuzz-sections.txt @@ -0,0 +1,794 @@ + +HB_H_IN +HB_OT_H_IN +HB_AAT_H_IN + + +
+hb-aat-layout +HB_AAT_LAYOUT_NO_SELECTOR_INDEX +hb_aat_layout_feature_type_t +hb_aat_layout_feature_selector_t +hb_aat_layout_feature_selector_info_t +hb_aat_layout_feature_type_get_name_id +hb_aat_layout_feature_type_get_selector_infos +hb_aat_layout_get_feature_types +hb_aat_layout_has_positioning +hb_aat_layout_has_substitution +hb_aat_layout_has_tracking +
+ +
+hb-blob +hb_blob_create +hb_blob_create_or_fail +hb_blob_create_from_file +hb_blob_create_from_file_or_fail +hb_blob_create_sub_blob +hb_blob_copy_writable_or_fail +hb_blob_destroy +hb_blob_get_data +hb_blob_get_data_writable +hb_blob_get_empty +hb_blob_get_length +hb_blob_get_user_data +hb_blob_is_immutable +hb_blob_make_immutable +hb_blob_reference +hb_blob_set_user_data +hb_blob_t +hb_memory_mode_t +
+ +
+hb-buffer +HB_SEGMENT_PROPERTIES_DEFAULT +HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT +hb_buffer_create +hb_buffer_create_similar +hb_buffer_reference +hb_buffer_get_empty +hb_buffer_destroy +hb_buffer_reset +hb_buffer_clear_contents +hb_buffer_pre_allocate +hb_buffer_allocation_successful +hb_buffer_add +hb_buffer_add_codepoints +hb_buffer_add_utf32 +hb_buffer_add_utf16 +hb_buffer_add_utf8 +hb_buffer_add_latin1 +hb_buffer_append +hb_buffer_set_content_type +hb_buffer_get_content_type +hb_buffer_set_direction +hb_buffer_get_direction +hb_buffer_set_script +hb_buffer_get_script +hb_buffer_set_language +hb_buffer_get_language +hb_buffer_set_flags +hb_buffer_get_flags +hb_buffer_set_cluster_level +hb_buffer_get_cluster_level +hb_buffer_set_length +hb_buffer_get_length +hb_buffer_set_segment_properties +hb_buffer_get_segment_properties +hb_buffer_guess_segment_properties +hb_buffer_set_unicode_funcs +hb_buffer_get_unicode_funcs +hb_buffer_set_user_data +hb_buffer_get_user_data +hb_buffer_get_glyph_infos +hb_buffer_get_glyph_positions +hb_buffer_has_positions +hb_buffer_get_invisible_glyph +hb_buffer_set_invisible_glyph +hb_buffer_get_not_found_glyph +hb_buffer_set_not_found_glyph +hb_buffer_set_replacement_codepoint +hb_buffer_get_replacement_codepoint +hb_buffer_normalize_glyphs +hb_buffer_reverse +hb_buffer_reverse_range +hb_buffer_reverse_clusters +hb_buffer_serialize +hb_buffer_serialize_glyphs +hb_buffer_deserialize_glyphs +hb_buffer_serialize_unicode +hb_buffer_deserialize_unicode +hb_buffer_serialize_format_from_string +hb_buffer_serialize_format_to_string +hb_buffer_serialize_list_formats +hb_segment_properties_equal +hb_segment_properties_hash +hb_segment_properties_overlay +hb_buffer_diff +hb_buffer_set_message_func +hb_buffer_t +hb_glyph_info_get_glyph_flags +hb_glyph_info_t +hb_glyph_flags_t +hb_glyph_position_t +hb_buffer_content_type_t +hb_buffer_flags_t +hb_buffer_cluster_level_t +hb_segment_properties_t +hb_buffer_serialize_format_t +hb_buffer_serialize_flags_t +hb_buffer_diff_flags_t +hb_buffer_message_func_t +
+ +
+hb-common +hb_tag_from_string +hb_tag_to_string +hb_direction_from_string +hb_direction_to_string +hb_script_from_iso15924_tag +hb_script_from_string +hb_script_to_iso15924_tag +hb_script_get_horizontal_direction +hb_language_from_string +hb_language_to_string +hb_language_get_default +hb_language_matches +hb_feature_from_string +hb_feature_to_string +hb_variation_from_string +hb_variation_to_string +hb_bool_t +hb_codepoint_t +hb_destroy_func_t +hb_direction_t +hb_language_t +hb_feature_t +hb_variation_t +hb_mask_t +hb_position_t +hb_tag_t +hb_script_t +hb_user_data_key_t +HB_TAG +HB_TAG_NONE +HB_TAG_MAX +HB_TAG_MAX_SIGNED +HB_UNTAG +HB_DIRECTION_REVERSE +HB_DIRECTION_IS_BACKWARD +HB_DIRECTION_IS_FORWARD +HB_DIRECTION_IS_HORIZONTAL +HB_DIRECTION_IS_VALID +HB_DIRECTION_IS_VERTICAL +HB_LANGUAGE_INVALID +HB_FEATURE_GLOBAL_END +HB_FEATURE_GLOBAL_START + +HB_BEGIN_DECLS +HB_END_DECLS +hb_var_int_t +hb_var_num_t +int16_t +int32_t +int64_t +int8_t +uint16_t +uint32_t +uint64_t +uint8_t + +HB_EXTERN +HB_DEPRECATED +HB_DEPRECATED_FOR +
+ +
+hb-draw +hb_draw_funcs_t +hb_draw_funcs_create +hb_draw_funcs_destroy +hb_draw_funcs_reference +hb_draw_funcs_is_immutable +hb_draw_funcs_make_immutable +hb_draw_move_to_func_t +hb_draw_funcs_set_move_to_func +hb_draw_line_to_func_t +hb_draw_funcs_set_line_to_func +hb_draw_quadratic_to_func_t +hb_draw_funcs_set_quadratic_to_func +hb_draw_cubic_to_func_t +hb_draw_funcs_set_cubic_to_func +hb_draw_close_path_func_t +hb_draw_funcs_set_close_path_func +hb_draw_state_t +HB_DRAW_STATE_DEFAULT +hb_draw_move_to +hb_draw_line_to +hb_draw_quadratic_to +hb_draw_cubic_to +hb_draw_close_path +
+ +
+hb-deprecated +HB_BUFFER_FLAGS_DEFAULT +HB_BUFFER_SERIALIZE_FLAGS_DEFAULT +HB_SCRIPT_CANADIAN_ABORIGINAL +hb_font_funcs_set_glyph_func +hb_font_get_glyph_func_t +HB_MATH_GLYPH_PART_FLAG_EXTENDER +HB_OT_MATH_SCRIPT +hb_ot_layout_table_choose_script +hb_ot_layout_table_find_script +hb_ot_tag_from_language +hb_ot_tags_from_script +HB_OT_VAR_NO_AXIS_INDEX +hb_ot_var_axis_t +hb_ot_var_find_axis +hb_ot_var_get_axes +hb_unicode_eastasian_width_func_t +hb_unicode_eastasian_width +hb_unicode_funcs_set_eastasian_width_func +HB_UNICODE_MAX_DECOMPOSITION_LEN +hb_unicode_decompose_compatibility_func_t +hb_unicode_decompose_compatibility +hb_unicode_funcs_set_decompose_compatibility_func +hb_font_funcs_set_glyph_v_kerning_func +hb_font_get_glyph_v_kerning +hb_font_get_glyph_v_kerning_func_t +
+ +
+hb-coretext +HB_CORETEXT_TAG_KERX +HB_CORETEXT_TAG_MORT +HB_CORETEXT_TAG_MORX +hb_coretext_face_create +hb_coretext_font_create +hb_coretext_face_get_cg_font +hb_coretext_font_get_ct_font +
+ +
+hb-directwrite +hb_directwrite_face_create +hb_directwrite_face_get_font_face +
+ +
+hb-face +hb_face_count +hb_face_t +hb_face_create +hb_face_create_for_tables +hb_face_destroy +hb_face_get_empty +hb_face_get_table_tags +hb_face_get_glyph_count +hb_face_get_index +hb_face_get_upem +hb_face_get_user_data +hb_face_is_immutable +hb_face_make_immutable +hb_face_reference +hb_face_reference_blob +hb_face_reference_table +hb_face_set_glyph_count +hb_face_set_index +hb_face_set_upem +hb_face_set_user_data +hb_face_collect_unicodes +hb_face_collect_variation_selectors +hb_face_collect_variation_unicodes +hb_face_builder_create +hb_face_builder_add_table +hb_face_builder_sort_tables +
+ +
+hb-font +hb_font_add_glyph_origin_for_direction +hb_font_create +hb_font_create_sub_font +hb_font_destroy +hb_font_funcs_create +hb_font_funcs_destroy +hb_font_funcs_get_empty +hb_font_funcs_get_user_data +hb_font_funcs_is_immutable +hb_font_funcs_make_immutable +hb_font_funcs_reference +hb_font_funcs_set_glyph_contour_point_func +hb_font_funcs_set_glyph_extents_func +hb_font_funcs_set_glyph_from_name_func +hb_font_funcs_set_glyph_h_advance_func +hb_font_funcs_set_glyph_h_advances_func +hb_font_funcs_set_glyph_h_kerning_func +hb_font_funcs_set_glyph_h_origin_func +hb_font_funcs_set_glyph_name_func +hb_font_funcs_set_glyph_shape_func +hb_font_funcs_set_glyph_v_advance_func +hb_font_funcs_set_glyph_v_advances_func +hb_font_funcs_set_glyph_v_origin_func +hb_font_funcs_set_nominal_glyph_func +hb_font_funcs_set_nominal_glyphs_func +hb_font_funcs_set_user_data +hb_font_funcs_set_variation_glyph_func +hb_font_funcs_t +hb_font_get_empty +hb_font_get_face +hb_font_get_glyph +hb_font_get_glyph_advance_for_direction +hb_font_get_glyph_advance_func_t +hb_font_get_glyph_advances_for_direction +hb_font_get_glyph_advances_func_t +hb_font_get_glyph_contour_point +hb_font_get_glyph_contour_point_for_origin +hb_font_get_glyph_contour_point_func_t +hb_font_get_glyph_extents +hb_font_get_glyph_extents_for_origin +hb_font_get_glyph_extents_func_t +hb_font_get_glyph_from_name +hb_font_get_glyph_from_name_func_t +hb_font_get_glyph_h_advance +hb_font_get_glyph_h_advance_func_t +hb_font_get_glyph_h_advances +hb_font_get_glyph_h_advances_func_t +hb_font_get_glyph_h_kerning +hb_font_get_glyph_h_kerning_func_t +hb_font_get_glyph_h_origin +hb_font_get_glyph_h_origin_func_t +hb_font_get_glyph_kerning_for_direction +hb_font_get_glyph_kerning_func_t +hb_font_get_glyph_name +hb_font_get_glyph_name_func_t +hb_font_get_glyph_origin_for_direction +hb_font_get_glyph_origin_func_t +hb_font_get_glyph_shape +hb_font_get_glyph_shape_func_t +hb_font_get_glyph_v_advance +hb_font_get_glyph_v_advance_func_t +hb_font_get_glyph_v_advances +hb_font_get_glyph_v_advances_func_t +hb_font_get_glyph_v_origin +hb_font_get_glyph_v_origin_func_t +hb_font_get_nominal_glyph +hb_font_get_nominal_glyph_func_t +hb_font_get_nominal_glyphs +hb_font_get_nominal_glyphs_func_t +hb_font_get_parent +hb_font_get_ppem +hb_font_get_ptem +hb_font_get_scale +hb_font_get_synthetic_slant +hb_font_get_user_data +hb_font_get_variation_glyph +hb_font_get_variation_glyph_func_t +hb_font_get_var_coords_design +hb_font_get_var_coords_normalized +hb_font_glyph_from_string +hb_font_glyph_to_string +hb_font_is_immutable +hb_font_make_immutable +hb_font_get_serial +hb_font_changed +hb_font_reference +hb_font_set_face +hb_font_set_funcs +hb_font_set_funcs_data +hb_font_set_parent +hb_font_set_ppem +hb_font_set_ptem +hb_font_set_scale +hb_font_set_synthetic_slant +hb_font_set_user_data +hb_font_set_variations +hb_font_set_var_coords_design +hb_font_set_var_coords_normalized +hb_font_set_var_named_instance +hb_font_subtract_glyph_origin_for_direction +hb_font_t +hb_reference_table_func_t +hb_font_funcs_set_font_h_extents_func +hb_font_funcs_set_font_v_extents_func +hb_font_get_extents_for_direction +hb_font_get_font_extents_func_t +hb_font_get_font_h_extents_func_t +hb_font_get_font_v_extents_func_t +hb_font_get_h_extents +hb_font_get_v_extents +hb_font_extents_t +hb_glyph_extents_t +
+ +
+hb-ft +hb_ft_face_create +hb_ft_face_create_cached +hb_ft_face_create_referenced +hb_ft_font_create +hb_ft_font_create_referenced +hb_ft_font_changed +hb_ft_font_get_face +hb_ft_font_lock_face +hb_ft_font_unlock_face +hb_ft_font_set_load_flags +hb_ft_font_get_load_flags +hb_ft_font_set_funcs +hb_ft_hb_font_changed +
+ +
+hb-gdi +hb_gdi_face_create +
+ +
+hb-glib +hb_glib_get_unicode_funcs +hb_glib_script_from_script +hb_glib_script_to_script +hb_glib_blob_create +
+ +
+hb-graphite2 +HB_GRAPHITE2_TAG_SILF +hb_graphite2_face_get_gr_face +hb_graphite2_font_get_gr_font +
+ +
+hb-icu +hb_icu_get_unicode_funcs +hb_icu_script_from_script +hb_icu_script_to_script +
+ +
+hb-map +HB_MAP_VALUE_INVALID +hb_map_allocation_successful +hb_map_clear +hb_map_copy +hb_map_create +hb_map_del +hb_map_destroy +hb_map_get +hb_map_get_empty +hb_map_get_population +hb_map_is_equal +hb_map_get_user_data +hb_map_has +hb_map_hash +hb_map_is_empty +hb_map_reference +hb_map_set +hb_map_set_user_data +hb_map_t +
+ +
+hb-ot-color +hb_color_t +HB_COLOR +hb_color_get_alpha +hb_color_get_blue +hb_color_get_green +hb_color_get_red +hb_ot_color_glyph_get_layers +hb_ot_color_glyph_reference_png +hb_ot_color_glyph_reference_svg +hb_ot_color_has_layers +hb_ot_color_has_palettes +hb_ot_color_has_png +hb_ot_color_has_svg +hb_ot_color_layer_t +hb_ot_color_palette_color_get_name_id +hb_ot_color_palette_flags_t +hb_ot_color_palette_get_colors +hb_ot_color_palette_get_count +hb_ot_color_palette_get_flags +hb_ot_color_palette_get_name_id +
+ +
+hb-ot-font +hb_ot_font_set_funcs +
+ +
+hb-ot-name +hb_ot_name_id_t +HB_OT_NAME_ID_INVALID +hb_ot_name_entry_t +hb_ot_name_list_names +hb_ot_name_get_utf16 +hb_ot_name_get_utf32 +hb_ot_name_get_utf8 +
+ +
+hb-ot-layout +HB_OT_MAX_TAGS_PER_LANGUAGE +HB_OT_MAX_TAGS_PER_SCRIPT +HB_OT_TAG_DEFAULT_LANGUAGE +HB_OT_TAG_DEFAULT_SCRIPT +hb_ot_tag_to_language +hb_ot_tag_to_script +hb_ot_tags_from_script_and_language +hb_ot_tags_to_script_and_language +HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX +HB_OT_LAYOUT_NO_FEATURE_INDEX +HB_OT_LAYOUT_NO_SCRIPT_INDEX +HB_OT_LAYOUT_NO_VARIATIONS_INDEX +HB_OT_TAG_BASE +HB_OT_TAG_GDEF +HB_OT_TAG_GPOS +HB_OT_TAG_GSUB +HB_OT_TAG_JSTF +hb_ot_layout_baseline_tag_t +hb_ot_layout_collect_lookups +hb_ot_layout_collect_features +hb_ot_layout_feature_get_characters +hb_ot_layout_feature_get_lookups +hb_ot_layout_feature_get_name_ids +hb_ot_layout_feature_with_variations_get_lookups +hb_ot_layout_get_attach_points +hb_ot_layout_get_horizontal_baseline_tag_for_script +hb_ot_layout_get_baseline +hb_ot_layout_get_baseline_with_fallback +hb_ot_layout_get_glyph_class +hb_ot_layout_get_glyphs_in_class +hb_ot_layout_get_ligature_carets +hb_ot_layout_get_size_params +hb_ot_layout_glyph_class_t +hb_ot_layout_has_glyph_classes +hb_ot_layout_has_positioning +hb_ot_layout_has_substitution +hb_ot_layout_language_find_feature +hb_ot_layout_language_get_feature_indexes +hb_ot_layout_language_get_feature_tags +hb_ot_layout_language_get_required_feature +hb_ot_layout_lookup_collect_glyphs +hb_ot_layout_lookup_get_glyph_alternates +hb_ot_layout_lookup_get_optical_bound +hb_ot_layout_lookup_substitute_closure +hb_ot_layout_lookups_substitute_closure +hb_ot_layout_lookup_would_substitute +hb_ot_layout_script_find_language +hb_ot_layout_script_get_language_tags +hb_ot_layout_script_select_language +hb_ot_layout_table_find_feature_variations +hb_ot_layout_table_get_feature_tags +hb_ot_layout_table_get_script_tags +hb_ot_layout_table_get_lookup_count +hb_ot_layout_table_select_script +hb_ot_shape_plan_collect_lookups +hb_ot_layout_language_get_required_feature_index +
+ +
+hb-ot-math +HB_OT_TAG_MATH +HB_OT_TAG_MATH_SCRIPT +hb_ot_math_constant_t +hb_ot_math_kern_t +hb_ot_math_kern_entry_t +hb_ot_math_glyph_variant_t +hb_ot_math_glyph_part_flags_t +hb_ot_math_glyph_part_t +hb_ot_math_has_data +hb_ot_math_get_constant +hb_ot_math_get_glyph_italics_correction +hb_ot_math_get_glyph_top_accent_attachment +hb_ot_math_get_glyph_kerning +hb_ot_math_get_glyph_kernings +hb_ot_math_is_glyph_extended_shape +hb_ot_math_get_glyph_variants +hb_ot_math_get_min_connector_overlap +hb_ot_math_get_glyph_assembly +
+ +
+hb-ot-meta +hb_ot_meta_tag_t +hb_ot_meta_get_entry_tags +hb_ot_meta_reference_entry +
+ +
+hb-ot-metrics +hb_ot_metrics_tag_t +hb_ot_metrics_get_position +hb_ot_metrics_get_position_with_fallback +hb_ot_metrics_get_variation +hb_ot_metrics_get_x_variation +hb_ot_metrics_get_y_variation +
+ +
+hb-ot-shape +hb_ot_shape_glyphs_closure +
+ +
+hb-ot-var +HB_OT_TAG_VAR_AXIS_ITALIC +HB_OT_TAG_VAR_AXIS_OPTICAL_SIZE +HB_OT_TAG_VAR_AXIS_SLANT +HB_OT_TAG_VAR_AXIS_WEIGHT +HB_OT_TAG_VAR_AXIS_WIDTH +hb_ot_var_has_data +hb_ot_var_axis_flags_t +hb_ot_var_axis_info_t +hb_ot_var_find_axis_info +hb_ot_var_get_axis_count +hb_ot_var_get_axis_infos +hb_ot_var_get_named_instance_count +hb_ot_var_named_instance_get_subfamily_name_id +hb_ot_var_named_instance_get_postscript_name_id +hb_ot_var_named_instance_get_design_coords +hb_ot_var_normalize_variations +hb_ot_var_normalize_coords +
+ +
+hb-set +HB_SET_VALUE_INVALID +hb_set_add +hb_set_add_range +hb_set_add_sorted_array +hb_set_allocation_successful +hb_set_copy +hb_set_clear +hb_set_create +hb_set_del +hb_set_del_range +hb_set_destroy +hb_set_get_empty +hb_set_get_max +hb_set_get_min +hb_set_get_population +hb_set_get_user_data +hb_set_has +hb_set_hash +hb_set_intersect +hb_set_invert +hb_set_is_empty +hb_set_is_equal +hb_set_is_subset +hb_set_next +hb_set_next_range +hb_set_next_many +hb_set_previous +hb_set_previous_range +hb_set_reference +hb_set_set +hb_set_set_user_data +hb_set_subtract +hb_set_symmetric_difference +hb_set_t +hb_set_union +
+ +
+hb-shape +hb_shape +hb_shape_full +hb_shape_list_shapers +
+ +
+hb-shape-plan +hb_shape_plan_create +hb_shape_plan_create_cached +hb_shape_plan_create2 +hb_shape_plan_create_cached2 +hb_shape_plan_destroy +hb_shape_plan_execute +hb_shape_plan_get_empty +hb_shape_plan_get_shaper +hb_shape_plan_get_user_data +hb_shape_plan_reference +hb_shape_plan_set_user_data +hb_shape_plan_t +
+ +
+hb-unicode +HB_UNICODE_MAX +hb_unicode_combining_class +hb_unicode_combining_class_func_t +hb_unicode_combining_class_t +hb_unicode_compose +hb_unicode_compose_func_t +hb_unicode_decompose +hb_unicode_decompose_func_t +hb_unicode_funcs_create +hb_unicode_funcs_destroy +hb_unicode_funcs_get_default +hb_unicode_funcs_get_empty +hb_unicode_funcs_get_parent +hb_unicode_funcs_get_user_data +hb_unicode_funcs_is_immutable +hb_unicode_funcs_make_immutable +hb_unicode_funcs_reference +hb_unicode_funcs_set_combining_class_func +hb_unicode_funcs_set_compose_func +hb_unicode_funcs_set_decompose_func +hb_unicode_funcs_set_general_category_func +hb_unicode_funcs_set_mirroring_func +hb_unicode_funcs_set_script_func +hb_unicode_funcs_set_user_data +hb_unicode_funcs_t +hb_unicode_general_category +hb_unicode_general_category_func_t +hb_unicode_general_category_t +hb_unicode_mirroring +hb_unicode_mirroring_func_t +hb_unicode_script +hb_unicode_script_func_t +
+ +
+hb-uniscribe +hb_uniscribe_font_get_hfont +hb_uniscribe_font_get_logfontw +
+ +
+hb-version +HB_VERSION_ATLEAST +HB_VERSION_MAJOR +HB_VERSION_MICRO +HB_VERSION_MINOR +HB_VERSION_STRING +hb_version +hb_version_atleast +hb_version_string +
+ +
+hb-style +hb_style_tag_t +hb_style_get_value +
+ +
+hb-subset +hb_subset_flags_t +hb_subset_input_t +hb_subset_sets_t +hb_subset_plan_t +hb_subset_input_create_or_fail +hb_subset_input_reference +hb_subset_input_destroy +hb_subset_input_set_user_data +hb_subset_input_get_user_data +hb_subset_input_get_flags +hb_subset_input_set_flags +hb_subset_input_unicode_set +hb_subset_input_glyph_set +hb_subset_input_set +hb_subset_or_fail +hb_subset_plan_create_or_fail +hb_subset_plan_reference +hb_subset_plan_destroy +hb_subset_plan_set_user_data +hb_subset_plan_get_user_data +hb_subset_plan_execute_or_fail +hb_subset_plan_unicode_to_old_glyph_mapping +hb_subset_plan_new_to_old_glyph_mapping +hb_subset_plan_old_to_new_glyph_mapping + +hb_link_t +hb_object_t +hb_subset_repack_or_fail +hb_subset_preprocess +hb_subset_input_pin_axis_location +hb_subset_input_pin_axis_to_default +
diff --git a/libs/harfbuzz/docs/meson.build b/libs/harfbuzz/docs/meson.build new file mode 100644 index 00000000..9da1fd56 --- /dev/null +++ b/libs/harfbuzz/docs/meson.build @@ -0,0 +1,66 @@ +if build_machine.system() == 'windows' + message('Skipping gtk-doc while building on Windows') + subdir_done() +endif + +if not find_program('gtkdoc-scan', required: get_option('docs')).found() + message('Not building documentation as gtk-doc was not found') + subdir_done() +endif + +conf.set('HAVE_GTK_DOC', 1) + +gnome = import('gnome') + +docconf = configuration_data() +docconf.set('HB_VERSION', meson.project_version()) + +version_xml = configure_file(input: 'version.xml.in', + output: 'version.xml', + configuration: docconf) + +content_files = [ + 'usermanual-what-is-harfbuzz.xml', + 'usermanual-install-harfbuzz.xml', + 'usermanual-getting-started.xml', + 'usermanual-glyph-information.xml', + 'usermanual-shaping-concepts.xml', + 'usermanual-object-model.xml', + 'usermanual-buffers-language-script-and-direction.xml', + 'usermanual-fonts-and-faces.xml', + 'usermanual-opentype-features.xml', + 'usermanual-clusters.xml', + 'usermanual-utilities.xml', + 'usermanual-integration.xml', + version_xml, +] + +html_images = [ + 'HarfBuzz.png', + 'HarfBuzz.svg', +] + +ignore_headers = [ + 'hb-gobject.h', + 'hb-gobject-enums.h', + 'hb-gobject-enums-tmp.h', + 'hb-gobject-structs.h', +] + +gnome.gtkdoc('harfbuzz', + main_sgml: 'harfbuzz-docs.xml', + src_dir: [meson.current_source_dir() / '..' / 'src', + meson.current_build_dir() / '..' / 'src', + ], + scan_args: ['--deprecated-guards=HB_DISABLE_DEPRECATED', + '--ignore-decorators=HB_EXTERN|HB_DEPRECATED', + ], + mkdb_args: ['--source-suffixes=h,cc', + '--xml-mode', + '--output-format=xml', + ], + content_files: content_files, + html_assets: html_images, + ignore_headers: ignore_headers, + dependencies: [libharfbuzz_dep], + install: true) diff --git a/libs/harfbuzz/docs/repacker.md b/libs/harfbuzz/docs/repacker.md new file mode 100644 index 00000000..397ce4fa --- /dev/null +++ b/libs/harfbuzz/docs/repacker.md @@ -0,0 +1,294 @@ +# Introduction + +Several tables in the opentype format are formed internally by a graph of subtables. Parent node's +reference their children through the use of positive offsets, which are typically 16 bits wide. +Since offsets are always positive this forms a directed acyclic graph. For storage in the font file +the graph must be given a topological ordering and then the subtables packed in serial according to +that ordering. Since 16 bit offsets have a maximum value of 65,535 if the distance between a parent +subtable and a child is more then 65,535 bytes then it's not possible for the offset to encode that +edge. + +For many fonts with complex layout rules (such as Arabic) it's not unusual for the tables containing +layout rules ([GSUB/GPOS](https://docs.microsoft.com/en-us/typography/opentype/spec/gsub)) to be +larger than 65kb. As a result these types of fonts are susceptible to offset overflows when +serializing to the binary font format. + +Offset overflows can happen for a variety of reasons and require different strategies to resolve: +* Simple overflows can often be resolved with a different topological ordering. +* If a subtable has many parents this can result in the link from furthest parent(s) + being at risk for overflows. In these cases it's possible to duplicate the shared subtable which + allows it to be placed closer to it's parent. +* If subtables exist which are themselves larger than 65kb it's not possible for any offsets to point + past them. In these cases the subtable can usually be split into two smaller subtables to allow + for more flexibility in the ordering. +* In GSUB/GPOS overflows from Lookup subtables can be resolved by changing the Lookup to an extension + lookup which uses a 32 bit offset instead of 16 bit offset. + +In general there isn't a simple solution to produce an optimal topological ordering for a given graph. +Finding an ordering which doesn't overflow is a NP hard problem. Existing solutions use heuristics +which attempt a combination of the above strategies to attempt to find a non-overflowing configuration. + +The harfbuzz subsetting library +[includes a repacking algorithm](https://github.com/harfbuzz/harfbuzz/blob/main/src/hb-repacker.hh) +which is used to resolve offset overflows that are present in the subsetted tables it produces. This +document provides a deep dive into how the harfbuzz repacking algorithm works. + +Other implementations exist, such as in +[fontTools](https://github.com/fonttools/fonttools/blob/7af43123d49c188fcef4e540fa94796b3b44e858/Lib/fontTools/ttLib/tables/otBase.py#L72), however these are not covered in this document. + +# Foundations + +There's four key pieces to the harfbuzz approach: + +* Subtable Graph: a table's internal structure is abstracted out into a lightweight graph + representation where each subtable is a node and each offset forms an edge. The nodes only need + to know how many bytes the corresponding subtable occupies. This lightweight representation can + be easily modified to test new ordering's and strategies as the repacking algorithm iterates. + +* [Topological sorting algorithm](https://en.wikipedia.org/wiki/Topological_sorting): an algorithm + which given a graph gives a linear sorting of the nodes such that all offsets will be positive. + +* Overflow check: given a graph and a topological sorting it checks if there will be any overflows + in any of the offsets. If there are overflows it returns a list of (parent, child) tuples that + will overflow. Since the graph has information on the size of each subtable it's straightforward + to calculate the final position of each subtable and then check if any offsets to it will + overflow. + +* Content Aware Preprocessing: if the overflow resolver is aware of the format of the underlying + tables (eg. GSUB, GPOS) then in some cases preprocessing can be done to increase the chance of + successfully packing the graph. For example for GSUB and GPOS we can preprocess the graph and + promote lookups to extension lookups (upgrades a 16 bit offset to 32 bits) or split large lookup + subtables into two or more pieces. + +* Offset resolution strategies: given a particular occurrence of an overflow these strategies + modify the graph to attempt to resolve the overflow. + +# High Level Algorithm + +``` +def repack(graph): + graph.topological_sort() + + if (graph.will_overflow()) + preprocess(graph) + assign_spaces(graph) + graph.topological_sort() + + while (overflows = graph.will_overflow()): + for overflow in overflows: + apply_offset_resolution_strategy (overflow, graph) + graph.topological_sort() +``` + +The actual code for this processing loop can be found in the function hb_resolve_overflows () of +[hb-repacker.hh](https://github.com/harfbuzz/harfbuzz/blob/main/src/hb-repacker.hh). + +# Topological Sorting Algorithms + +The harfbuzz repacker uses two different algorithms for topological sorting: +* [Kahn's Algorithm](https://en.wikipedia.org/wiki/Topological_sorting#Kahn's_algorithm) +* Sorting by shortest distance + +Kahn's algorithm is approximately twice as fast as the shortest distance sort so that is attempted +first (only on the first topological sort). If it fails to eliminate overflows then shortest distance +sort will be used for all subsequent topological sorting operations. + +## Shortest Distance Sort + +This algorithm orders the nodes based on total distance to each node. Nodes with a shorter distance +are ordered first. + +The "weight" of an edge is the sum of the size of the sub-table being pointed to plus 2^16 for a 16 bit +offset and 2^32 for a 32 bit offset. + +The distance of a node is the sum of all weights along the shortest path from the root to that node +plus a priority modifier (used to change where nodes are placed by moving increasing or +decreasing the effective distance). Ties between nodes with the same distance are broken based +on the order of the offset in the sub table bytes. + +The shortest distance to each node is determined using +[Djikstra's algorithm](https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm). Then the topological +ordering is produce by applying a modified version of Kahn's algorithm that uses a priority queue +based on the shortest distance to each node. + +## Optimizing the Sorting + +The topological sorting operation is the core of the repacker and is run on each iteration so it needs +to be as fast as possible. There's a few things that are done to speed up subsequent sorting +operations: + +* The number of incoming edges to each node is cached. This is required by the Kahn's algorithm + portion of both sorts. Where possible when the graph is modified we manually update the cached + edge counts of affected nodes. + +* The distance to each node is cached. Where possible when the graph is modified we manually update + the cached distances of any affected nodes. + +Caching these values allows the repacker to avoid recalculating them for the full graph on each +iteration. + +The other important factor to speed is a fast priority queue which is a core datastructure to +the topological sorting algorithm. Currently a basic heap based queue is used. Heap based queue's +don't support fast priority decreases, but that can be worked around by just adding redundant entries +to the priority queue and filtering the older ones out when poppping off entries. This is based +on the recommendations in +[a study of the practical performance of priority queues in Dijkstra's algorithm](https://www3.cs.stonybrook.edu/~rezaul/papers/TR-07-54.pdf) + +## Special Handling of 32 bit Offsets + +If a graph contains multiple 32 bit offsets then the shortest distance sorting will be likely be +suboptimal. For example consider the case where a graph contains two 32 bit offsets that each point +to a subgraph which are not connected to each other. The shortest distance sort will interleave the +subtables of the two subgraphs, potentially resulting in overflows. Since each of these subgraphs are +independent of each other, and 32 bit offsets can point extremely long distances a better strategy is +to pack the first subgraph in it's entirety and then have the second subgraph packed after with the 32 +bit offset pointing over the first subgraph. For example given the graph: + + +``` +a--- b -- d -- f + \ + \_ c -- e -- g +``` + +Where the links from a to b and a to c are 32 bit offsets, the shortest distance sort would be: + +``` +a, b, c, d, e, f, g + +``` + +If nodes d and e have a combined size greater than 65kb then the offset from d to f will overflow. +A better ordering is: + +``` +a, b, d, f, c, e, g +``` + +The ability for 32 bit offsets to point long distances is utilized to jump over the subgraph of +b which gives the remaining 16 bit offsets a better chance of not overflowing. + +The above is an ideal situation where the subgraphs are disconnected from each other, in practice +this is often not this case. So this idea can be generalized as follows: + +If there is a subgraph that is only reachable from one or more 32 bit offsets, then: +* That subgraph can be treated as an independent unit and all nodes of the subgraph packed in isolation + from the rest of the graph. +* In a table that occupies less than 4gb of space (in practice all fonts), that packed independent + subgraph can be placed anywhere after the parent nodes without overflowing the 32 bit offsets from + the parent nodes. + +The sorting algorithm incorporates this via a "space" modifier that can be applied to nodes in the +graph. By default all nodes are treated as being in space zero. If a node is given a non-zero space, n, +then the computed distance to the node will be modified by adding `n * 2^32`. This will cause that +node and it's descendants to be packed between all nodes in space n-1 and space n+1. Resulting in a +topological sort like: + +``` +| space 0 subtables | space 1 subtables | .... | space n subtables | +``` + +The assign_spaces() step in the high level algorithm is responsible for identifying independent +subgraphs and assigning unique spaces to each one. More information on the space assignment can be +found in the next section. + +# Graph Preprocessing + +For certain table types we can preprocess and modify the graph structure to reduce the occurences +of overflows. Currently the repacker implements preprocessing only for GPOS and GSUB tables. + +## GSUB/GPOS Table Splitting + +The GSUB/GPOS preprocessor scans each lookup subtable and determines if the subtable's children are +so large that no overflow resolution is possible (for example a single subtable that exceeds 65kb +cannot be pointed over). When such cases are detected table splitting is invoked: + +* The subtable is first analyzed to determine the smallest number of split points that will allow + for successful offset overflow resolution. + +* Then the subtable in the graph representation is modified to actually perform the split at the + previously computed split points. At a high level splits are done by inserting new subtables + which contain a subset of the data of the original subtable and then shrinking the original subtable. + +Table splitting must be aware of the underlying format of each subtable type and thus needs custom +code for each subtable type. Currently subtable splitting is only supported for GPOS subtable types. + +## GSUB/GPOS Extension Lookup Promotion + +In GSUB/GPOS tables lookups can be regular lookups which use 16 bit offsets to the children subtables +or extension lookups which use 32 bit offsets to the children subtables. If the sub graph of all +regular lookups is too large then it can be difficult to find an overflow free configuration. This +can be remedied by promoting one or more regular lookups to extension lookups. + +During preprocessing the graph is scanned to determine the size of the subgraph of regular lookups. +If the graph is found to be too big then the analysis finds a set of lookups to promote to reduce +the subgraph size. Lastly the graph is modified to convert those lookups to extension lookups. + +# Offset Resolution Strategies + +## Space Assignment + +The goal of space assignment is to find connected subgraphs that are only reachable via 32 bit offsets +and then assign each such subgraph to a unique non-zero space. The algorithm is roughly: + +1. Collect the set, `S`, of nodes that are children of 32 bit offsets. + +2. Do a directed traversal from each node in `S` and collect all encountered nodes into set `T`. + Mark all nodes in the graph that are not in `T` as being in space 0. + +3. Set `next_space = 1`. + +4. While set `S` is not empty: + + a. Pick a node `n` in set `S` then perform an undirected graph traversal and find the set `Q` of + nodes that are reachable from `n`. + + b. During traversal if a node, `m`, has a edge to a node in space 0 then `m` must be duplicated + to disconnect it from space 0. + + d. Remove all nodes in `Q` from `S` and assign all nodes in `Q` to `next_space`. + + + c. Increment `next_space` by one. + + +## Manual Iterative Resolutions + +For each overflow in each iteration the algorithm will attempt to apply offset overflow resolution +strategies to eliminate the overflow. The type of strategy applied is dependent on the characteristics +of the overflowing link: + +* If the overflowing offset is inside a space other than space 0 and the subgraph space has more + than one 32 bit offset pointing into the subgraph then subdivide the space by moving subgraph + from one of the 32 bit offsets into a new space via the duplication of shared nodes. + +* If the overflowing offset is pointing to a subtable with more than one incoming edge: duplicate + the node so that the overflowing offset is pointing at it's own copy of that node. + +* Otherwise, attempt to move the child subtable closer to it's parent. This is accomplished by + raising the priority of all children of the parent. Next time the topological sort is run the + children will be ordered closer to the parent. + +# Test Cases + +The harfbuzz repacker has tests defined using generic graphs: https://github.com/harfbuzz/harfbuzz/blob/main/src/test-repacker.cc + +# Future Improvements + +Currently for GPOS tables the repacker implementation is sufficient to handle both subsetting and the +general case of font compilation repacking. However for GSUB the repacker is only sufficient for +subsetting related overflows. To enable general case repacking of GSUB, support for splitting of +GSUB subtables will need to be added. Other table types such as COLRv1 shouldn't require table +splitting due to the wide use of 24 bit offsets throughout the table. + +Beyond subtable splitting there are a couple of "nice to have" improvements, but these are not required +to support the general case: + +* Extension demotion: currently extension promotion is supported but in some cases if the non-extension + subgraph is underfilled then packed size can be reduced by demoting extension lookups back to regular + lookups. + +* Currently only children nodes are moved to resolve offsets. However, in many cases moving a parent + node closer to it's children will have less impact on the size of other offsets. Thus the algorithm + should use a heuristic (based on parent and child subtable sizes) to decide if the children's + priority should be increased or the parent's priority decreased. diff --git a/libs/harfbuzz/docs/serializer.md b/libs/harfbuzz/docs/serializer.md new file mode 100644 index 00000000..0efda805 --- /dev/null +++ b/libs/harfbuzz/docs/serializer.md @@ -0,0 +1,178 @@ +# Introduction + +In hb-subset serialization is the process of writing the subsetted font +tables out to actual bytes in the final format. All serialization works +through an object called the serialize context +([hb_serialize_context_t](https://github.com/harfbuzz/harfbuzz/blob/main/src/hb-serialize.hh)). + +Internally the serialize context holds a fixed size memory buffer. For simple +tables the final bytes are written into the buffer sequentially to produce +the final serialized bytes. + +## Simple Tables + +Simple tables are tables that do not use offset graphs. + +To write a struct into the serialization context, first you call an +allocation method on the context which requests a writable array of bytes of +a fixed size. If the requested array will not exceed the bounds of the fixed +buffer the serializer will return a pointer to the next unwritten portion +of the buffer. Then the struct is cast onto the returned pointer and values +are written to the structs fields. + +Internally the serialization context ends up looking like: + +``` ++-------+-------+-----+-------+--------------+ +| Obj 1 | Obj 2 | ... | Obj N | Unused Space | ++-------+-------+-----+-------+--------------+ +``` + +Here Obj N, is the object currently being written. + +## Complex Tables + +Complex tables are made up of graphs of objects, where offset's are used +to form the edges of the graphs. Each object is a continuous slice of bytes +that contains zero or more offsets pointing to more objects. + +In this case the serialization buffer has a different layout: + +``` +|- in progress objects -| |--- packed objects --| ++-----------+-----------+--------------+-------+-----+-------+ +| Obj n+2 | Obj n+1 | Unused Space | Obj n | ... | Obj 0 | ++-----------+-----------+--------------+-------+-----+-------+ +|-----------------------> <---------------------| +``` + +The buffer holds two stacks: + +1. In progress objects are held in a stack starting from the start of buffer + that grows towards the end of the buffer. + +2. Packed objects are held in a stack that starts at the end of the buffer + and grows towards the start of the buffer. + +Once the object on the top of the in progress stack is finished being written +its bytes are popped from the in progress stack and copied to the top of +the packed objects stack. In the example above, finalizing Obj n+1 +would result in the following state: + +``` ++---------+--------------+---------+-------+-----+-------+ +| Obj n+2 | Unused Space | Obj n+1 | Obj n | ... | Obj 0 | ++---------+--------------+---------+-------+-----+-------+ +``` + +Each packed object is associated with an ID, it's zero based position in the packed +objects stack. In this example Obj 0, would have an ID of 0. + +During serialization offsets that link from one object to another are stored +using object ids. The serialize context maintains a list of links between +objects. Each link records the parent object id, the child object id, the position +of the offset field within the parent object, and the width of the offset. + +Links are always added to the current in progress object and you can only link too +objects that have been packed and thus have an ID. + +### Object De-duplication + +An important optimization in packing offset graphs is de-duplicating equivalent objects. If you +have two or more parent objects that point to child objects that are equivalent then you only need +to encode the child once and can have the parents point to the same child. This can significantly +reduce the final size of a serialized graph. + +During packing of an inprogress object the serialization context checks if any existing packed +objects are equivalent to the object being packed. Here equivalence means the object has the +exact same bytes and all of it's links are equivalent. If an equivalent object is found the +in progress object is discarded and not copied to the packed object stack. The object id of +the equivalent object is instead returned. Thus parent objects will then link to the existing +equivalent object. + +To find equivalent objects the serialization context maintains a hashmap from object to the canonical +object id. + +### Link Resolution + +Once all objects have been packed the next step is to assign actual values to all of the offset +fields. Prior to this point all links in the graph have been recorded using object id's. For each +link the resolver computes the offset between the parent and child and writes the offset into +the serialization buffer at the appropriate location. + +### Offset Overflow Resolution + +If during link resolution the resolver finds that an offsets value would exceed what can be encoded +in that offset field link resolution is aborted and the offset overflow resolver is invoked. +That process is documented [here](reapcker.md). + + +### Example of Complex Serialization + + +If we wanted to serialize the following graph: + +``` +a--b--d + \ / + c +``` + +Serializer would be called like this: + +```c++ +hb_serialize_context_t ctx; + +struct root { + char name; + Offset16To child_1; + Offset16To child_2; +} + +struct child { + char name; + Offset16To leaf; +} + +// Object A. +ctx->push(); +root* a = ctx->start_embed (); +ctx->extend_min (a); +a->name = 'a'; + +// Object B. +ctx->push(); +child* b = ctx->start_embed (); +ctx->extend_min (b); +b->name = 'b'; + +// Object D. +ctx->push(); +*ctx->allocate_size (1) = 'd'; +unsigned d_id = ctx->pop_pack (); + +ctx->add_link (b->leaf, d_id); +unsigned b_id = ctx->pop_pack (); + +// Object C +ctx->push(); +child* c = ctx->start_embed (); +ctx->extend_min (c); +c->name = 'c'; + +// Object D. +ctx->push(); +*ctx->allocate_size (1) = 'd'; +d_id = ctx->pop_pack (); // Serializer will automatically de-dup this with the previous 'd' + +ctx->add_link (c->leaf, d_id); +unsigned c_id = ctx->pop_pack (); + +// Object A's links: +ctx->add_link (a->child_1, b_id); +ctx->add_link (a->child_2, c_id); +ctx->pop_pack (); + +ctx->end_serialize (); + +``` diff --git a/libs/harfbuzz/docs/usermanual-buffers-language-script-and-direction.xml b/libs/harfbuzz/docs/usermanual-buffers-language-script-and-direction.xml new file mode 100644 index 00000000..0235d2d3 --- /dev/null +++ b/libs/harfbuzz/docs/usermanual-buffers-language-script-and-direction.xml @@ -0,0 +1,412 @@ + + + +]> + + Buffers, language, script and direction + + The input to the HarfBuzz shaper is a series of Unicode characters, stored in a + buffer. In this chapter, we'll look at how to set up a buffer with + the text that we want and how to customize the properties of the + buffer. We'll also look at a piece of lower-level machinery that + you will need to understand before proceeding: the functions that + HarfBuzz uses to retrieve Unicode information. + + + After shaping is complete, HarfBuzz puts its output back + into the buffer. But getting that output requires setting up a + face and a font first, so we will look at that in the next chapter + instead of here. + +
+ Creating and destroying buffers + + As we saw in our Getting Started example, a + buffer is created and + initialized with hb_buffer_create(). This + produces a new, empty buffer object, instantiated with some + default values and ready to accept your Unicode strings. + + + HarfBuzz manages the memory of objects (such as buffers) that it + creates, so you don't have to. When you have finished working on + a buffer, you can call hb_buffer_destroy(): + + + hb_buffer_t *buf = hb_buffer_create(); + ... + hb_buffer_destroy(buf); + + + This will destroy the object and free its associated memory - + unless some other part of the program holds a reference to this + buffer. If you acquire a HarfBuzz buffer from another subsystem + and want to ensure that it is not garbage collected by someone + else destroying it, you should increase its reference count: + + + void somefunc(hb_buffer_t *buf) { + buf = hb_buffer_reference(buf); + ... + + + And then decrease it once you're done with it: + + + hb_buffer_destroy(buf); + } + + + While we are on the subject of reference-counting buffers, it is + worth noting that an individual buffer can only meaningfully be + used by one thread at a time. + + + To throw away all the data in your buffer and start from scratch, + call hb_buffer_reset(buf). If you want to + throw away the string in the buffer but keep the options, you can + instead call hb_buffer_clear_contents(buf). + +
+ +
+ Adding text to the buffer + + Now we have a brand new HarfBuzz buffer. Let's start filling it + with text! From HarfBuzz's perspective, a buffer is just a stream + of Unicode code points, but your input string is probably in one of + the standard Unicode character encodings (UTF-8, UTF-16, or + UTF-32). HarfBuzz provides convenience functions that accept + each of these encodings: + hb_buffer_add_utf8(), + hb_buffer_add_utf16(), and + hb_buffer_add_utf32(). Other than the + character encoding they accept, they function identically. + + + You can add UTF-8 text to a buffer by passing in the text array, + the array's length, an offset into the array for the first + character to add, and the length of the segment to add: + + + hb_buffer_add_utf8 (hb_buffer_t *buf, + const char *text, + int text_length, + unsigned int item_offset, + int item_length) + + + So, in practice, you can say: + + + hb_buffer_add_utf8(buf, text, strlen(text), 0, strlen(text)); + + + This will append your new characters to + buf, not replace its existing + contents. Also, note that you can use -1 in + place of the first instance of strlen(text) + if your text array is NULL-terminated. Similarly, you can also use + -1 as the final argument want to add its full + contents. + + + Whatever start item_offset and + item_length you provide, HarfBuzz will also + attempt to grab the five characters before + the offset point and the five characters + after the designated end. These are the + before and after "context" segments, which are used internally + for HarfBuzz to make shaping decisions. They will not be part of + the final output, but they ensure that HarfBuzz's + script-specific shaping operations are correct. If there are + fewer than five characters available for the before or after + contexts, HarfBuzz will just grab what is there. + + + For longer text runs, such as full paragraphs, it might be + tempting to only add smaller sub-segments to a buffer and + shape them in piecemeal fashion. Generally, this is not a good + idea, however, because a lot of shaping decisions are + dependent on this context information. For example, in Arabic + and other connected scripts, HarfBuzz needs to know the code + points before and after each character in order to correctly + determine which glyph to return. + + + The safest approach is to add all of the text available (even + if your text contains a mix of scripts, directions, languages + and fonts), then use item_offset and + item_length to indicate which characters you + want shaped (which must all have the same script, direction, + language and font), so that HarfBuzz has access to any context. + + + You can also add Unicode code points directly with + hb_buffer_add_codepoints(). The arguments + to this function are the same as those for the UTF + encodings. But it is particularly important to note that + HarfBuzz does not do validity checking on the text that is added + to a buffer. Invalid code points will be replaced, but it is up + to you to do any deep-sanity checking necessary. + + +
+ +
+ Setting buffer properties + + Buffers containing input characters still need several + properties set before HarfBuzz can shape their text correctly. + + + Initially, all buffers are set to the + HB_BUFFER_CONTENT_TYPE_INVALID content + type. After adding text, the buffer should be set to + HB_BUFFER_CONTENT_TYPE_UNICODE instead, which + indicates that it contains un-shaped input + characters. After shaping, the buffer will have the + HB_BUFFER_CONTENT_TYPE_GLYPHS content type. + + + hb_buffer_add_utf8() and the + other UTF functions set the content type of their buffer + automatically. But if you are reusing a buffer you may want to + check its state with + hb_buffer_get_content_type(buffer). If + necessary you can set the content type with + + + hb_buffer_set_content_type(buf, HB_BUFFER_CONTENT_TYPE_UNICODE); + + + to prepare for shaping. + + + Buffers also need to carry information about the script, + language, and text direction of their contents. You can set + these properties individually: + + + hb_buffer_set_direction(buf, HB_DIRECTION_LTR); + hb_buffer_set_script(buf, HB_SCRIPT_LATIN); + hb_buffer_set_language(buf, hb_language_from_string("en", -1)); + + + However, since these properties are often repeated for + multiple text runs, you can also save them in a + hb_segment_properties_t for reuse: + + + hb_segment_properties_t *savedprops; + hb_buffer_get_segment_properties (buf, savedprops); + ... + hb_buffer_set_segment_properties (buf2, savedprops); + + + HarfBuzz also provides getter functions to retrieve a buffer's + direction, script, and language properties individually. + + + HarfBuzz recognizes four text directions in + hb_direction_t: left-to-right + (HB_DIRECTION_LTR), right-to-left (HB_DIRECTION_RTL), + top-to-bottom (HB_DIRECTION_TTB), and + bottom-to-top (HB_DIRECTION_BTT). For the + script property, HarfBuzz uses identifiers based on the + ISO 15924 + standard. For languages, HarfBuzz uses tags based on the + IETF BCP 47 standard. + + + Helper functions are provided to convert character strings into + the necessary script and language tag types. + + + Two additional buffer properties to be aware of are the + "invisible glyph" and the replacement code point. The + replacement code point is inserted into buffer output in place of + any invalid code points encountered in the input. By default, it + is the Unicode REPLACEMENT CHARACTER code + point, U+FFFD "�". You can change this with + + + hb_buffer_set_replacement_codepoint(buf, replacement); + + + passing in the replacement Unicode code point as the + replacement parameter. + + + The invisible glyph is used to replace all output glyphs that + are invisible. By default, the standard space character + U+0020 is used; you can replace this (for + example, when using a font that provides script-specific + spaces) with + + + hb_buffer_set_invisible_glyph(buf, replacement_glyph); + + + Do note that in the replacement_glyph + parameter, you must provide the glyph ID of the replacement you + wish to use, not the Unicode code point. + + + HarfBuzz supports a few additional flags you might want to set + on your buffer under certain circumstances. The + HB_BUFFER_FLAG_BOT and + HB_BUFFER_FLAG_EOT flags tell HarfBuzz + that the buffer represents the beginning or end (respectively) + of a text element (such as a paragraph or other block). Knowing + this allows HarfBuzz to apply certain contextual font features + when shaping, such as initial or final variants in connected + scripts. + + + HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES + tells HarfBuzz not to hide glyphs with the + Default_Ignorable property in Unicode. This + property designates control characters and other non-printing + code points, such as joiners and variation selectors. Normally + HarfBuzz replaces them in the output buffer with zero-width + space glyphs (using the "invisible glyph" property discussed + above); setting this flag causes them to be printed, which can + be helpful for troubleshooting. + + + Conversely, setting the + HB_BUFFER_FLAG_REMOVE_DEFAULT_IGNORABLES flag + tells HarfBuzz to remove Default_Ignorable + glyphs from the output buffer entirely. Finally, setting the + HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE + flag tells HarfBuzz not to insert the dotted-circle glyph + (U+25CC, "◌"), which is normally + inserted into buffer output when broken character sequences are + encountered (such as combining marks that are not attached to a + base character). + +
+ +
+ Customizing Unicode functions + + HarfBuzz requires some simple functions for accessing + information from the Unicode Character Database (such as the + General_Category (gc) and + Script (sc) properties) that is useful + for shaping, as well as some useful operations like composing and + decomposing code points. + + + HarfBuzz includes its own internal, lightweight set of Unicode + functions. At build time, it is also possible to compile support + for some other options, such as the Unicode functions provided + by GLib or the International Components for Unicode (ICU) + library. Generally, this option is only of interest for client + programs that have specific integration requirements or that do + a significant amount of customization. + + + If your program has access to other Unicode functions, however, + such as through a system library or application framework, you + might prefer to use those instead of the built-in + options. HarfBuzz supports this by implementing its Unicode + functions as a set of virtual methods that you can replace — + without otherwise affecting HarfBuzz's functionality. + + + The Unicode functions are specified in a structure called + unicode_funcs which is attached to each + buffer. But even though unicode_funcs is + associated with a hb_buffer_t, the functions + themselves are called by other HarfBuzz APIs that access + buffers, so it would be unwise for you to hook different + functions into different buffers. + + + In addition, you can mark your unicode_funcs + as immutable by calling + hb_unicode_funcs_make_immutable (ufuncs). + This is especially useful if your code is a + library or framework that will have its own client programs. By + marking your Unicode function choices as immutable, you prevent + your own client programs from changing the + unicode_funcs configuration and introducing + inconsistencies and errors downstream. + + + You can retrieve the Unicode-functions configuration for + your buffer by calling hb_buffer_get_unicode_funcs(): + + + hb_unicode_funcs_t *ufunctions; + ufunctions = hb_buffer_get_unicode_funcs(buf); + + + The current version of unicode_funcs uses six functions: + + + + + hb_unicode_combining_class_func_t: + returns the Canonical Combining Class of a code point. + + + + + hb_unicode_general_category_func_t: + returns the General Category (gc) of a code point. + + + + + hb_unicode_mirroring_func_t: returns + the Mirroring Glyph code point (for bi-directional + replacement) of a code point. + + + + + hb_unicode_script_func_t: returns the + Script (sc) property of a code point. + + + + + hb_unicode_compose_func_t: returns the + canonical composition of a sequence of two code points. + + + + + hb_unicode_decompose_func_t: returns + the canonical decomposition of a code point. + + + + + Note, however, that future HarfBuzz releases may alter this set. + + + Each Unicode function has a corresponding setter, with which you + can assign a callback to your replacement function. For example, + to replace + hb_unicode_general_category_func_t, you can call + + + hb_unicode_funcs_set_general_category_func (*ufuncs, func, *user_data, destroy) + + + Virtualizing this set of Unicode functions is primarily intended + to improve portability. There is no need for every client + program to make the effort to replace the default options, so if + you are unsure, do not feel any pressure to customize + unicode_funcs. + +
+ +
diff --git a/libs/harfbuzz/docs/usermanual-clusters.xml b/libs/harfbuzz/docs/usermanual-clusters.xml new file mode 100644 index 00000000..4f2825c0 --- /dev/null +++ b/libs/harfbuzz/docs/usermanual-clusters.xml @@ -0,0 +1,697 @@ + + + +]> + + Clusters +
+ Clusters and shaping + + In text shaping, a cluster is a sequence of + characters that needs to be treated as a single, indivisible + unit. A single letter or symbol can be a cluster of its + own. Other clusters correspond to longer subsequences of the + input code points — such as a ligature or conjunct form + — and require the shaper to ensure that the cluster is not + broken during the shaping process. + + + A cluster is distinct from a grapheme, + which is the smallest unit of meaning in a writing system or + script. + + + The definitions of the two terms are similar. However, clusters + are only relevant for script shaping and glyph layout. In + contrast, graphemes are a property of the underlying script, and + are of interest when client programs implement orthographic + or linguistic functionality. + + + For example, two individual letters are often two separate + graphemes. When two letters form a ligature, however, they + combine into a single glyph. They are then part of the same + cluster and are treated as a unit by the shaping engine — + even though the two original, underlying letters remain separate + graphemes. + + + HarfBuzz is concerned with clusters, not + with graphemes — although client programs using HarfBuzz + may still care about graphemes for other reasons from time to time. + + + During the shaping process, there are several shaping operations + that may merge adjacent characters (for example, when two code + points form a ligature or a conjunct form and are replaced by a + single glyph) or split one character into several (for example, + when decomposing a code point through the + ccmp feature). Operations like these alter + clusters; HarfBuzz tracks the changes to ensure that no clusters + get lost or broken during shaping. + + + HarfBuzz records cluster information independently from how + shaping operations affect the individual glyphs returned in an + output buffer. Consequently, a client program using HarfBuzz can + utilize the cluster information to implement features such as: + + + + + Correctly positioning the cursor within a shaped text run, + even when characters have formed ligatures, composed or + decomposed, reordered, or undergone other shaping operations. + + + + + Correctly highlighting a text selection that includes some, + but not all, of the characters in a word. + + + + + Applying text attributes (such as color or underlining) to + part, but not all, of a word. + + + + + Generating output document formats (such as PDF) with + embedded text that can be fully extracted. + + + + + Determining the mapping between input characters and output + glyphs, such as which glyphs are ligatures. + + + + + Performing line-breaking, justification, and other + line-level or paragraph-level operations that must be done + after shaping is complete, but which require examining + character-level properties. + + + +
+
+ Working with HarfBuzz clusters + + When you add text to a HarfBuzz buffer, each code point must be + assigned a cluster value. + + + This cluster value is an arbitrary number; HarfBuzz uses it only + to distinguish between clusters. Many client programs will use + the index of each code point in the input text stream as the + cluster value. This is for the sake of convenience; the actual + value does not matter. + + + Some of the shaping operations performed by HarfBuzz — + such as reordering, composition, decomposition, and substitution + — may alter the cluster values of some characters. The + final cluster values in the buffer at the end of the shaping + process will indicate to client programs which subsequences of + glyphs represent a cluster and, therefore, must not be + separated. + + + In addition, client programs can query the final cluster values + to discern other potentially important information about the + glyphs in the output buffer (such as whether or not a ligature + was formed). + + + For example, if the initial sequence of cluster values was: + + + 0,1,2,3,4 + + + and the final sequence of cluster values is: + + + 0,0,3,3 + + + then there are two clusters in the output buffer: the first + cluster includes the first two glyphs, and the second cluster + includes the third and fourth glyphs. It is also evident that a + ligature or conjunct has been formed, because there are fewer + glyphs in the output buffer (four) than there were code points + in the input buffer (five). + + + Although client programs using HarfBuzz are free to assign + initial cluster values in any manner they choose to, HarfBuzz + does offer some useful guarantees if the cluster values are + assigned in a monotonic (either non-decreasing or non-increasing) + order. + + + For buffers in the left-to-right (LTR) + or top-to-bottom (TTB) text flow direction, + HarfBuzz will preserve the monotonic property: client programs + are guaranteed that monotonically increasing initial cluster + values will be returned as monotonically increasing final + cluster values. + + + For buffers in the right-to-left (RTL) + or bottom-to-top (BTT) text flow direction, + the directionality of the buffer itself is reversed for final + output as a matter of design. Therefore, HarfBuzz inverts the + monotonic property: client programs are guaranteed that + monotonically increasing initial cluster values will be + returned as monotonically decreasing final + cluster values. + + + Client programs can adjust how HarfBuzz handles clusters during + shaping by setting the + cluster_level of the + buffer. HarfBuzz offers three levels of + clustering support for this property: + + + + Level 0 is the default and + reproduces the behavior of the old HarfBuzz library. + + + The distinguishing feature of level 0 behavior is that, at + the beginning of processing the buffer, all code points that + are categorized as marks, + modifier symbols, or + Emoji extended pictographic modifiers, + as well as the Zero Width Joiner and + Zero Width Non-Joiner code points, are + assigned the cluster value of the closest preceding code + point from different category. + + + In essence, whenever a base character is followed by a mark + character or a sequence of mark characters, those marks are + reassigned to the same initial cluster value as the base + character. This reassignment is referred to as + "merging" the affected clusters. This behavior is based on + the Grapheme Cluster Boundary specification in Unicode + Technical Report 29. + + + Client programs can specify level 0 behavior for a buffer by + setting its cluster_level to + HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES. + + + + + Level 1 tweaks the old behavior + slightly to produce better results. Therefore, level 1 + clustering is recommended for code that is not required to + implement backward compatibility with the old HarfBuzz. + + + Level 1 differs from level 0 by not merging the + clusters of marks and other modifier code points with the + preceding "base" code point's cluster. By preserving the + separate cluster values of these marks and modifier code + points, script shapers can perform additional operations + that might lead to improved results (for example, reordering + a sequence of marks). + + + Client programs can specify level 1 behavior for a buffer by + setting its cluster_level to + HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS. + + + + + Level 2 differs significantly in how it + treats cluster values. In level 2, HarfBuzz never merges + clusters. + + + This difference can be seen most clearly when HarfBuzz processes + ligature substitutions and glyph decompositions. In level 0 + and level 1, ligatures and glyph decomposition both involve + merging clusters; in level 2, neither of these operations + triggers a merge. + + + Client programs can specify level 2 behavior for a buffer by + setting its cluster_level to + HB_BUFFER_CLUSTER_LEVEL_CHARACTERS. + + + + + As mentioned earlier, client programs using HarfBuzz often + assign initial cluster values in a buffer by reusing the indices + of the code points in the input text. This gives a sequence of + cluster values that is monotonically increasing (for example, + 0,1,2,3,4). + + + It is not required that the cluster values + in a buffer be monotonically increasing. However, if the initial + cluster values in a buffer are monotonic and the buffer is + configured to use cluster level 0 or 1, then HarfBuzz + guarantees that the final cluster values in the shaped buffer + will also be monotonic. No such guarantee is made for cluster + level 2. + + + In levels 0 and 1, HarfBuzz implements the following conceptual + model for cluster values: + + + + + If the sequence of input cluster values is monotonic, the + sequence of cluster values will remain monotonic. + + + + + Each cluster value represents a single cluster. + + + + + Each cluster contains one or more glyphs and one or more + characters. + + + + + In practice, this model offers several benefits. Assuming that + the initial cluster values were monotonically increasing + and distinct before shaping began, then, in the final output: + + + + + All adjacent glyphs having the same final cluster + value belong to the same cluster. + + + + + Each character belongs to the cluster that has the highest + cluster value not larger than its + initial cluster value. + + + +
+ +
+ A clustering example for levels 0 and 1 + + The basic shaping operations affect clusters in a predictable + manner when using level 0 or level 1: + + + + + When two or more clusters merge, the + resulting merged cluster takes as its cluster value the + minimum of the incoming cluster values. + + + + + When a cluster decomposes, all of the + resulting child clusters inherit as their cluster value the + cluster value of the parent cluster. + + + + + When a character is reordered, the + reordered character and all clusters that the character + moves past as part of the reordering are merged into one cluster. + + + + + The functionality, guarantees, and benefits of level 0 and level + 1 behavior can be seen with some examples. First, let us examine + what happens with cluster values when shaping involves cluster + merging with ligatures and decomposition. + + + + Let's say we start with the following character sequence (top row) and + initial cluster values (bottom row): + + + A,B,C,D,E + 0,1,2,3,4 + + + During shaping, HarfBuzz maps these characters to glyphs from + the font. For simplicity, let us assume that each character maps + to the corresponding, identical-looking glyph: + + + A,B,C,D,E + 0,1,2,3,4 + + + Now if, for example, B and C + form a ligature, then the clusters to which they belong + "merge". This merged cluster takes for its cluster + value the minimum of all the cluster values of the clusters that + went in to the ligature. In this case, we get: + + + A,BC,D,E + 0,1 ,3,4 + + + because 1 is the minimum of the set {1,2}, which were the + cluster values of B and + C. + + + Next, let us say that the BC ligature glyph + decomposes into three components, and D also + decomposes into two components. Whenever a cluster decomposes, + its components each inherit the cluster value of their parent: + + + A,BC0,BC1,BC2,D0,D1,E + 0,1 ,1 ,1 ,3 ,3 ,4 + + + Next, if BC2 and D0 form a + ligature, then their clusters (cluster values 1 and 3) merge into + min(1,3) = 1: + + + A,BC0,BC1,BC2D0,D1,E + 0,1 ,1 ,1 ,1 ,4 + + + Note that the entirety of cluster 3 merges into cluster 1, not + just the D0 glyph. This reflects the fact + that the cluster must be treated as an + indivisible unit. + + + At this point, cluster 1 means: the character sequence + BCD is represented by glyphs + BC0,BC1,BC2D0,D1 and cannot be broken down any + further. + +
+
+ Reordering in levels 0 and 1 + + Another common operation in some shapers is glyph + reordering. In order to maintain a monotonic cluster sequence + when glyph reordering takes place, HarfBuzz merges the clusters + of everything in the reordering sequence. + + + For example, let us again start with the character sequence (top + row) and initial cluster values (bottom row): + + + A,B,C,D,E + 0,1,2,3,4 + + + If D is reordered to the position immediately + before B, then HarfBuzz merges the + B, C, and + D clusters — all the clusters between + the final position of the reordered glyph and its original + position. This means that we get: + + + A,D,B,C,E + 0,1,1,1,4 + + + as the final cluster sequence. + + + Merging this many clusters is not ideal, but it is the only + sensible way for HarfBuzz to maintain the guarantee that the + sequence of cluster values remains monotonic and to retain the + true relationship between glyphs and characters. + +
+
+ The distinction between levels 0 and 1 + + The preceding examples demonstrate the main effects of using + cluster levels 0 and 1. The only difference between the two + levels is this: in level 0, at the very beginning of the shaping + process, HarfBuzz merges the cluster of each base character + with the clusters of all Unicode marks (combining or not) and + modifiers that follow it. + + + For example, let us start with the following character sequence + (top row) and accompanying initial cluster values (bottom row): + + + A,acute,B + 0,1 ,2 + + + The acute is a Unicode mark. If HarfBuzz is + using cluster level 0 on this sequence, then the + A and acute clusters will + merge, and the result will become: + + + A,acute,B + 0,0 ,2 + + + This merger is performed before any other script-shaping + steps. + + + This initial cluster merging is the default behavior of the + Windows shaping engine, and the old HarfBuzz codebase copied + that behavior to maintain compatibility. Consequently, it has + remained the default behavior in the new HarfBuzz codebase. + + + But this initial cluster-merging behavior makes it impossible + for client programs to implement some features (such as to + color diacritic marks differently from their base + characters). That is why, in level 1, HarfBuzz does not perform + the initial merging step. + + + For client programs that rely on HarfBuzz cluster values to + perform cursor positioning, level 0 is more convenient. But + relying on cluster boundaries for cursor positioning is wrong: cursor + positions should be determined based on Unicode grapheme + boundaries, not on shaping-cluster boundaries. As such, using + level 1 clustering behavior is recommended. + + + One final facet of levels 0 and 1 is worth noting. HarfBuzz + currently does not allow any + multiple-substitution GSUB lookups to + replace a glyph with zero glyphs (in other words, to delete a + glyph). + + + But, in some other situations, glyphs can be deleted. In + those cases, if the glyph being deleted is the last glyph of its + cluster, HarfBuzz makes sure to merge the deleted glyph's + cluster with a neighboring cluster. + + + This is done primarily to make sure that the starting cluster of the + text always has the cluster index pointing to the start of the text + for the run; more than one client program currently relies on this + guarantee. + + + Incidentally, Apple's CoreText does something different to + maintain the same promise: it inserts a glyph with id 65535 at + the beginning of the glyph string if the glyph corresponding to + the first character in the run was deleted. HarfBuzz might do + something similar in the future. + +
+
+ Level 2 + + HarfBuzz's level 2 cluster behavior uses a significantly + different model than that of level 0 and level 1. + + + The level 2 behavior is easy to describe, but it may be + difficult to understand in practical terms. In brief, level 2 + performs no merging of clusters whatsoever. + + + This means that there is no initial base-and-mark merging step + (as is done in level 0), and it means that reordering moves and + ligature substitutions do not trigger a cluster merge. + + + Only one shaping operation directly affects clusters when using + level 2: + + + + + When a cluster decomposes, all of the + resulting child clusters inherit as their cluster value the + cluster value of the parent cluster. + + + + + When glyphs do form a ligature (or when some other feature + substitutes multiple glyphs with one glyph) the cluster value + of the first glyph is retained as the cluster value for the + resulting ligature. + + + This occurrence sounds similar to a cluster merge, but it is + different. In particular, no subsequent characters — + including marks and modifiers — are affected. They retain + their previous cluster values. + + + Level 2 cluster behavior is ultimately less complex than level 0 + or level 1, but there are several cases for which processing + cluster values produced at level 2 may be tricky. + +
+ Ligatures with combining marks in level 2 + + The first example of how HarfBuzz's level 2 cluster behavior + can be tricky is when the text to be shaped includes combining + marks attached to ligatures. + + + Let us start with an input sequence with the following + characters (top row) and initial cluster values (bottom row): + + + A,acute,B,breve,C,circumflex + 0,1 ,2,3 ,4,5 + + + If the sequence A,B,C forms a ligature, + then these are the cluster values HarfBuzz will return under + the various cluster levels: + + + Level 0: + + + ABC,acute,breve,circumflex + 0 ,0 ,0 ,0 + + + Level 1: + + + ABC,acute,breve,circumflex + 0 ,0 ,0 ,5 + + + Level 2: + + + ABC,acute,breve,circumflex + 0 ,1 ,3 ,5 + + + Making sense of the level 2 result is the hardest for a client + program, because there is nothing in the cluster values that + indicates that B and C + formed a ligature with A. + + + In contrast, the "merged" cluster values of the mark glyphs + that are seen in the level 0 and level 1 output are evidence + that a ligature substitution took place. + +
+
+ Reordering in level 2 + + Another example of how HarfBuzz's level 2 cluster behavior + can be tricky is when glyphs reorder. Consider an input sequence + with the following characters (top row) and initial cluster + values (bottom row): + + + A,B,C,D,E + 0,1,2,3,4 + + + Now imagine D moves before + B in a reordering operation. The cluster + values will then be: + + + A,D,B,C,E + 0,3,1,2,4 + + + Next, if D forms a ligature with + B, the output is: + + + A,DB,C,E + 0,3 ,2,4 + + + However, in a different scenario, in which the shaping rules + of the script instead caused A and + B to form a ligature + before the D reordered, the + result would be: + + + AB,D,C,E + 0 ,3,2,4 + + + There is no way for a client program to differentiate between + these two scenarios based on the cluster values + alone. Consequently, client programs that use level 2 might + need to undertake additional work in order to manage cursor + positioning, text attributes, or other desired features. + +
+
+ Other considerations in level 2 + + There may be other problems encountered with ligatures under + level 2, such as if the direction of the text is forced to + the opposite of its natural direction (for example, Arabic text + that is forced into left-to-right directionality). But, + generally speaking, these other scenarios are minor corner + cases that are too obscure for most client programs to need to + worry about. + +
+
+
diff --git a/libs/harfbuzz/docs/usermanual-fonts-and-faces.xml b/libs/harfbuzz/docs/usermanual-fonts-and-faces.xml new file mode 100644 index 00000000..abf5dc2b --- /dev/null +++ b/libs/harfbuzz/docs/usermanual-fonts-and-faces.xml @@ -0,0 +1,467 @@ + + + +]> + + Fonts, faces, and output + + In the previous chapter, we saw how to set up a buffer and fill + it with text as Unicode code points. In order to shape this + buffer text with HarfBuzz, you will need also need a font + object. + + + HarfBuzz provides abstractions to help you cache and reuse the + heavier parts of working with binary fonts, so we will look at + how to do that. We will also look at how to work with the + FreeType font-rendering library and at how you can customize + HarfBuzz to work with other libraries. + + + Finally, we will look at how to work with OpenType variable + fonts, the latest update to the OpenType font format, and at + some other recent additions to OpenType. + + +
+ Font and face objects + + The outcome of shaping a run of text depends on the contents of + a specific font file (such as the substitutions and positioning + moves in the 'GSUB' and 'GPOS' tables), so HarfBuzz makes + accessing those internals fast. + + + An hb_face_t represents a face + in HarfBuzz. This data type is a wrapper around an + hb_blob_t blob that holds the contents of a binary + font file. Since HarfBuzz supports TrueType Collections and + OpenType Collections (each of which can include multiple + typefaces), a HarfBuzz face also requires an index number + specifying which typeface in the file you want to use. Most of + the font files you will encounter in the wild include just a + single face, however, so most of the time you would pass in + 0 as the index when you create a face: + + + hb_blob_t* blob = hb_blob_create_from_file(file); + ... + hb_face_t* face = hb_face_create(blob, 0); + + + On its own, a face object is not quite ready to use for + shaping. The typeface must be set to a specific point size in + order for some details (such as hinting) to work. In addition, + if the font file in question is an OpenType Variable Font, then + you may need to specify one or variation-axis settings (or a + named instance) in order to get the output you need. + + + In HarfBuzz, you do this by creating a font + object from your face. + + + Font objects also have the advantage of being considerably + lighter-weight than face objects (remember that a face contains + the contents of a binary font file mapped into memory). As a + result, you can cache and reuse a font object, but you could + also create a new one for each additional size you needed. + Creating new fonts incurs some additional overhead, of course, + but whether or not it is excessive is your call in the end. In + contrast, face objects are substantially larger, and you really + should cache them and reuse them whenever possible. + + + You can create a font object from a face object: + + + hb_font_t* hb_font = hb_font_create(hb_face); + + + After creating a font, there are a few properties you should + set. Many fonts enable and disable hints based on the size it + is used at, so setting this is important for font + objects. hb_font_set_ppem(font, x_ppem, + y_ppem) sets the pixels-per-EM value of the font. You + can also set the point size of the font with + hb_font_set_ptem(font, ptem). HarfBuzz uses the + industry standard 72 points per inch. + + + HarfBuzz lets you specify the degree subpixel precision you want + through a scaling factor. You can set horizontal and + vertical scaling factors on the + font by calling hb_font_set_scale(font, x_scale, + y_scale). + + + There may be times when you are handed a font object and need to + access the face object that it comes from. For that, you can call + + + hb_face = hb_font_get_face(hb_font); + + + You can also create a font object from an existing font object + using the hb_font_create_sub_font() + function. This creates a child font object that is initiated + with the same attributes as its parent; it can be used to + quickly set up a new font for the purpose of overriding a specific + font-functions method. + + + All face objects and font objects are lifecycle-managed by + HarfBuzz. After creating a face, you increase its reference + count with hb_face_reference(face) and + decrease it with + hb_face_destroy(face). Likewise, you + increase the reference count on a font with + hb_font_reference(font) and decrease it + with hb_font_destroy(font). + + + You can also attach user data to face objects and font objects. + +
+ +
+ Customizing font functions + + During shaping, HarfBuzz frequently needs to query font objects + to get at the contents and parameters of the glyphs in a font + file. It includes a built-in set of functions that is tailored + to working with OpenType fonts. However, as was the case with + Unicode functions in the buffers chapter, HarfBuzz also wants to + make it easy for you to assign a substitute set of font + functions if you are developing a program to work with a library + or platform that provides its own font functions. + + + Therefore, the HarfBuzz API defines a set of virtual + methods for accessing font-object properties, and you can + replace the defaults with your own selections without + interfering with the shaping process. Each font object in + HarfBuzz includes a structure called + font_funcs that serves as a vtable for the + font object. The virtual methods in + font_funcs are: + + + + + hb_font_get_font_h_extents_func_t: returns + the extents of the font for horizontal text. + + + + + hb_font_get_font_v_extents_func_t: returns + the extents of the font for vertical text. + + + + + hb_font_get_nominal_glyph_func_t: returns + the font's nominal glyph for a given code point. + + + + + hb_font_get_variation_glyph_func_t: returns + the font's glyph for a given code point when it is followed by a + given Variation Selector. + + + + + hb_font_get_nominal_glyphs_func_t: returns + the font's nominal glyphs for a series of code points. + + + + + hb_font_get_glyph_advance_func_t: returns + the advance for a glyph. + + + + + hb_font_get_glyph_h_advance_func_t: returns + the advance for a glyph for horizontal text. + + + + + hb_font_get_glyph_v_advance_func_t:returns + the advance for a glyph for vertical text. + + + + + hb_font_get_glyph_advances_func_t: returns + the advances for a series of glyphs. + + + + + hb_font_get_glyph_h_advances_func_t: returns + the advances for a series of glyphs for horizontal text . + + + + + hb_font_get_glyph_v_advances_func_t: returns + the advances for a series of glyphs for vertical text. + + + + + hb_font_get_glyph_origin_func_t: returns + the origin coordinates of a glyph. + + + + + hb_font_get_glyph_h_origin_func_t: returns + the origin coordinates of a glyph for horizontal text. + + + + + hb_font_get_glyph_v_origin_func_t: returns + the origin coordinates of a glyph for vertical text. + + + + + hb_font_get_glyph_extents_func_t: returns + the extents for a glyph. + + + + + hb_font_get_glyph_contour_point_func_t: + returns the coordinates of a specific contour point from a glyph. + + + + + hb_font_get_glyph_name_func_t: returns the + name of a glyph (from its glyph index). + + + + + hb_font_get_glyph_from_name_func_t: returns + the glyph index that corresponds to a given glyph name. + + + + + You can create new font-functions by calling + hb_font_funcs_create(): + + + hb_font_funcs_t *ffunctions = hb_font_funcs_create (); + hb_font_set_funcs (font, ffunctions, font_data, destroy); + + + The individual methods can each be set with their own setter + function, such as + hb_font_funcs_set_nominal_glyph_func(ffunctions, + func, user_data, destroy). + + + Font-functions structures can be reused for multiple font + objects, and can be reference counted with + hb_font_funcs_reference() and + hb_font_funcs_destroy(). Just like other + objects in HarfBuzz, you can set user-data for each + font-functions structure and assign a destroy callback for + it. + + + You can also mark a font-functions structure as immutable, + with hb_font_funcs_make_immutable(). This + is especially useful if your code is a library or framework that + will have its own client programs. By marking your + font-functions structures as immutable, you prevent your client + programs from changing the configuration and introducing + inconsistencies and errors downstream. + + + To override only some functions while using the default implementation + for the others, you will need to create a sub-font. By default, the + sub-font uses the font functions of its parent except for the functions + that were explicitly set. The following code will override only the + hb_font_get_nominal_glyph_func_t for the sub-font: + + + hb_font_t *subfont = hb_font_create_sub_font (font) + hb_font_funcs_t *ffunctions = hb_font_funcs_create (); + hb_font_funcs_set_nominal_glyph_func (ffunctions, func, user_data, destroy); + hb_font_set_funcs (subfont, ffunctions, font_data, destroy); + hb_font_funcs_destroy (ffunctions); + +
+ +
+ Font objects and HarfBuzz's native OpenType implementation + + By default, whenever HarfBuzz creates a font object, it will + configure the font to use a built-in set of font functions that + supports contemporary OpenType font internals. If you want to + work with OpenType or TrueType fonts, you should be able to use + these functions without difficulty. + + + Many of the methods in the font-functions structure deal with + the fundamental properties of glyphs that are required for + shaping text: extents (the maximums and minimums on each axis), + origins (the (0,0) coordinate point which + glyphs are drawn in reference to), and advances (the amount that + the cursor needs to be moved after drawing each glyph, including + any empty space for the glyph's side bearings). + + + As you can see in the list of functions, there are separate "horizontal" + and "vertical" variants depending on whether the text is set in + the horizontal or vertical direction. For some scripts, fonts + that are designed to support text set horizontally or vertically (for + example, in Japanese) may include metrics for both text + directions. When fonts don't include this information, HarfBuzz + does its best to transform what the font provides. + + + In addition to the direction-specific functions, HarfBuzz + provides some higher-level functions for fetching information + like extents and advances for a glyph. If you call + + + hb_font_get_glyph_advance_for_direction(font, direction, extents); + + + then you can provide any hb_direction_t as the + direction parameter, and HarfBuzz will + use the correct function variant for the text direction. There + are similar higher-level versions of the functions for fetching + extents, origin coordinates, and contour-point + coordinates. There are also addition and subtraction functions + for moving points with respect to the origin. + + + There are also methods for fetching the glyph ID that + corresponds to a Unicode code point (possibly when followed by a + variation-selector code point), fetching the glyph name from the + font, and fetching the glyph ID that corresponds to a glyph name + you already have. + + + HarfBuzz also provides functions for converting between glyph + names and string + variables. hb_font_glyph_to_string(font, glyph, s, + size) retrieves the name for the glyph ID + glyph from the font object. It generates a + generic name of the form gidDDD (where DDD is + the glyph index) if there is no name for the glyph in the + font. The hb_font_glyph_from_string(font, s, len, + glyph) takes an input string s + and looks for a glyph with that name in the font, returning its + glyph ID in the glyph + output parameter. It automatically parses + gidDDD and uniUUUU strings. + +
+ + + + + + +
+ Working with OpenType Variable Fonts + + If you are working with OpenType Variable Fonts, there are a few + additional functions you should use to specify the + variation-axis settings of your font object. Without doing so, + your variable font's font object can still be used, but only at + the default setting for every axis (which, of course, is + sometimes what you want, but does not cover general usage). + + + HarfBuzz manages variation settings in the + hb_variation_t data type, which holds a tag for the + variation-axis identifier tag and a value for its + setting. You can retrieve the list of variation axes in a font + binary from the face object (not from a font object, notably) by + calling hb_ot_var_get_axis_count(face) to + find the number of axes, then using + hb_ot_var_get_axis_infos() to collect the + axis structures: + + + axes = hb_ot_var_get_axis_count(face); + ... + hb_ot_var_get_axis_infos(face, 0, axes, axes_array); + + + For each axis returned in the array, you can can access the + identifier in its tag. HarfBuzz also has + tag definitions predefined for the five standard axes specified + in OpenType (ital for italic, + opsz for optical size, + slnt for slant, wdth for + width, and wght for weight). Each axis also + has a min_value, a + default_value, and a max_value. + + + To set your font object's variation settings, you call the + hb_font_set_variations() function with an + array of hb_variation_t variation settings. Let's + say our font has weight and width axes. We need to specify each + of the axes by tag and assign a value on the axis: + + + unsigned int variation_count = 2; + hb_variation_t variation_data[variation_count]; + variation_data[0].tag = HB_OT_TAG_VAR_AXIS_WIDTH; + variation_data[1].tag = HB_OT_TAG_VAR_AXIS_WEIGHT; + variation_data[0].value = 80; + variation_data[1].value = 750; + ... + hb_font_set_variations(font, variation_data, variation_count); + + + That should give us a slightly condensed font ("normal" on the + wdth axis is 100) at a noticeably bolder + weight ("regular" is 400 on the wght axis). + + + In practice, though, you should always check that the value you + want to set on the axis is within the + [min_value,max_value] + range actually implemented in the font's variation axis. After + all, a font might only provide lighter-than-regular weights, and + setting a heavier value on the wght axis will + not change that. + + + Once your variation settings are specified on your font object, + however, shaping with a variable font is just like shaping a + static font. + +
+ +
diff --git a/libs/harfbuzz/docs/usermanual-getting-started.xml b/libs/harfbuzz/docs/usermanual-getting-started.xml new file mode 100644 index 00000000..eb8c1d72 --- /dev/null +++ b/libs/harfbuzz/docs/usermanual-getting-started.xml @@ -0,0 +1,312 @@ + + + +]> + + Getting started with HarfBuzz +
+ An overview of the HarfBuzz shaping API + + The core of the HarfBuzz shaping API is the function + hb_shape(). This function takes a font, a + buffer containing a string of Unicode codepoints and + (optionally) a list of font features as its input. It replaces + the codepoints in the buffer with the corresponding glyphs from + the font, correctly ordered and positioned, and with any of the + optional font features applied. + + + In addition to holding the pre-shaping input (the Unicode + codepoints that comprise the input string) and the post-shaping + output (the glyphs and positions), a HarfBuzz buffer has several + properties that affect shaping. The most important are the + text-flow direction (e.g., left-to-right, right-to-left, + top-to-bottom, or bottom-to-top), the script tag, and the + language tag. + + + + For input string buffers, flags are available to denote when the + buffer represents the beginning or end of a paragraph, to + indicate whether or not to visibly render Unicode Default + Ignorable codepoints, and to modify the cluster-merging + behavior for the buffer. For shaped output buffers, the + individual X and Y offsets and advances + (the logical dimensions) of each glyph are + accessible. HarfBuzz also flags glyphs as + UNSAFE_TO_BREAK if breaking the string at + that glyph (e.g., in a line-breaking or hyphenation process) + would require re-shaping the text. + + + + HarfBuzz also provides methods to compare the contents of + buffers, join buffers, normalize buffer contents, and handle + invalid codepoints, as well as to determine the state of a + buffer (e.g., input codepoints or output glyphs). Buffer + lifecycles are managed and all buffers are reference-counted. + + + + Although the default hb_shape() function is + sufficient for most use cases, a variant is also provided that + lets you specify which of HarfBuzz's shapers to use on a buffer. + + + + HarfBuzz can read TrueType fonts, TrueType collections, OpenType + fonts, and OpenType collections. Functions are provided to query + font objects about metrics, Unicode coverage, available tables and + features, and variation selectors. Individual glyphs can also be + queried for metrics, variations, and glyph names. OpenType + variable fonts are supported, and HarfBuzz allows you to set + variation-axis coordinates on font objects. + + + + HarfBuzz provides glue code to integrate with various other + libraries, including FreeType, GObject, and CoreText. Support + for integrating with Uniscribe and DirectWrite is experimental + at present. + +
+ +
+ Terminology + + + + + + + script + + + In text shaping, a script is a + writing system: a set of symbols, rules, and conventions + that is used to represent a language or multiple + languages. + + + In general computing lingo, the word "script" can also + be used to mean an executable program (usually one + written in a human-readable programming language). For + the sake of clarity, HarfBuzz documents will always use + more specific terminology when referring to this + meaning, such as "Python script" or "shell script." In + all other instances, "script" refers to a writing system. + + + For developers using HarfBuzz, it is important to note + the distinction between a script and a language. Most + scripts are used to write a variety of different + languages, and many languages may be written in more + than one script. + + + + + + shaper + + + In HarfBuzz, a shaper is a + handler for a specific script-shaping model. HarfBuzz + implements separate shapers for Indic, Arabic, Thai and + Lao, Khmer, Myanmar, Tibetan, Hangul, Hebrew, the + Universal Shaping Engine (USE), and a default shaper for + scripts with no script-specific shaping model. + + + + + + cluster + + + In text shaping, a cluster is a + sequence of codepoints that must be treated as an + indivisible unit. Clusters can include code-point + sequences that form a ligature or base-and-mark + sequences. Tracking and preserving clusters is important + when shaping operations might separate or reorder + code points. + + + HarfBuzz provides three cluster + levels that implement different + approaches to the problem of preserving clusters during + shaping operations. + + + + + + grapheme + + + In linguistics, a grapheme is one + of the indivisible units that make up a writing system or + script. Often, graphemes are individual symbols (letters, + numbers, punctuation marks, logograms, etc.) but, + depending on the writing system, a particular grapheme + might correspond to a sequence of several Unicode code + points. + + + In practice, HarfBuzz and other text-shaping engines + are not generally concerned with graphemes. However, it + is important for developers using HarfBuzz to recognize + that there is a difference between graphemes and shaping + clusters (see above). The two concepts may overlap + frequently, but there is no guarantee that they will be + identical. + + + + + + syllable + + + In linguistics, a syllable is an + a sequence of sounds that makes up a building block of a + particular language. Every language has its own set of + rules describing what constitutes a valid syllable. + + + For text-shaping purposes, the various definitions of + "syllable" are important because script-specific shaping + operations may be applied at the syllable level. For + example, a reordering rule might specify that a vowel + mark be reordered to the beginning of the syllable. + + + Syllables will consist of one or more Unicode code + points. The definition of a syllable for a particular + writing system might correspond to how HarfBuzz + identifies clusters (see above) for the same writing + system. However, it is important for developers using + HarfBuzz to recognize that there is a difference between + syllables and shaping clusters. The two concepts may + overlap frequently, but there is no guarantee that they + will be identical. + + + + + +
+ + +
+ A simple shaping example + + + Below is the simplest HarfBuzz shaping example possible. + + + + + Create a buffer and put your text in it. + + + + + #include <hb.h> + + hb_buffer_t *buf; + buf = hb_buffer_create(); + hb_buffer_add_utf8(buf, text, -1, 0, -1); + + + + + Set the script, language and direction of the buffer. + + + + + hb_buffer_set_direction(buf, HB_DIRECTION_LTR); + hb_buffer_set_script(buf, HB_SCRIPT_LATIN); + hb_buffer_set_language(buf, hb_language_from_string("en", -1)); + + + + + Create a face and a font from a font file. + + + + + hb_blob_t *blob = hb_blob_create_from_file(filename); /* or hb_blob_create_from_file_or_fail() */ + hb_face_t *face = hb_face_create(blob, 0); + hb_font_t *font = hb_font_create(face); + + + + + Shape! + + + + + hb_shape(font, buf, NULL, 0); + + + + + Get the glyph and position information. + + + + + unsigned int glyph_count; + hb_glyph_info_t *glyph_info = hb_buffer_get_glyph_infos(buf, &glyph_count); + hb_glyph_position_t *glyph_pos = hb_buffer_get_glyph_positions(buf, &glyph_count); + + + + + Iterate over each glyph. + + + + + hb_position_t cursor_x = 0; + hb_position_t cursor_y = 0; + for (unsigned int i = 0; i < glyph_count; i++) { + hb_codepoint_t glyphid = glyph_info[i].codepoint; + hb_position_t x_offset = glyph_pos[i].x_offset; + hb_position_t y_offset = glyph_pos[i].y_offset; + hb_position_t x_advance = glyph_pos[i].x_advance; + hb_position_t y_advance = glyph_pos[i].y_advance; + /* draw_glyph(glyphid, cursor_x + x_offset, cursor_y + y_offset); */ + cursor_x += x_advance; + cursor_y += y_advance; + } + + + + + Tidy up. + + + + + hb_buffer_destroy(buf); + hb_font_destroy(font); + hb_face_destroy(face); + hb_blob_destroy(blob); + + + + This example shows enough to get us started using HarfBuzz. In + the sections that follow, we will use the remainder of + HarfBuzz's API to refine and extend the example and improve its + text-shaping capabilities. + +
+
diff --git a/libs/harfbuzz/docs/usermanual-glyph-information.xml b/libs/harfbuzz/docs/usermanual-glyph-information.xml new file mode 100644 index 00000000..78f06c73 --- /dev/null +++ b/libs/harfbuzz/docs/usermanual-glyph-information.xml @@ -0,0 +1,14 @@ + + + +]> + + Glyph information + + Names and numbers + + + + diff --git a/libs/harfbuzz/docs/usermanual-install-harfbuzz.xml b/libs/harfbuzz/docs/usermanual-install-harfbuzz.xml new file mode 100644 index 00000000..760b9c5a --- /dev/null +++ b/libs/harfbuzz/docs/usermanual-install-harfbuzz.xml @@ -0,0 +1,349 @@ + + + +]> + + Installing HarfBuzz + +
+ Downloading HarfBuzz + + The HarfBuzz source code is hosted at github.com/harfbuzz/harfbuzz. + + + Tarball releases and Win32 binary bundles (which include the + libharfbuzz DLL, hb-view.exe, hb-shape.exe, and all + dependencies) of HarfBuzz can be downloaded from github.com/harfbuzz/harfbuzz/releases. + + + Release notes are posted with each new release to provide an + overview of the changes. The project tracks bug + reports and other issues on GitHub. Discussion and + questions are welcome on GitHub as well. + + + The API included in the hb.h file will not change in a + compatibility-breaking way in any release. However, other, + peripheral headers are more likely to go through minor + modifications. We will do our best to never change APIs in an + incompatible way. We will never break the ABI. + +
+ +
+ Building HarfBuzz + +
+ Building on Linux + + (1) To build HarfBuzz on Linux, you must first install the + development packages for FreeType, Cairo, and GLib. The exact + commands required for this step will vary depending on + the Linux distribution you use. + + + For example, on an Ubuntu or Debian system, you would run: + sudo apt install gcc g++ libfreetype6-dev libglib2.0-dev libcairo2-dev + On Fedora, RHEL, CentOS, or other Red-Hat–based systems, you would run: + sudo yum install gcc gcc-c++ freetype-devel glib2-devel cairo-devel + + + + + (2) The next step depends on whether you + are building from the source in a downloaded release tarball or + from the source directly from the git repository. + + + (2)(a) If you downloaded the HarfBuzz + source code in a tarball, you can now extract the source. + + + From a shell in the top-level directory of the extracted source + code, you can run meson build followed by + meson compile -C build as with any other standard package. + + + This should leave you with a shared + library in the src/ directory, and a few + utility programs including hb-view and + hb-shape under the util/ + directory. + + + (2)(b) If you are building from the source in the HarfBuzz git + repository, rather than installing from a downloaded tarball + release, then you must install two more auxiliary tools before you + can build for the first time: pkg-config. + + + On Ubuntu or Debian, run: + sudo apt-get install meson pkg-config gtk-doc-tools + On Fedora, RHEL, CentOS, run: + sudo yum install meson pkgconfig gtk-doc + + + + With pkg-config installed, you can now run + meson build then + meson compile -C build to build HarfBuzz. + +
+ + +
+ Building on Windows + + + Install meson + and run (from the console) meson build (by default + bundled dependencies are not built, --wrap-mode=default + overrides this), then meson compile -C build to + build HarfBuzz. + +
+ + +
+ Building on macOS + + + There are two ways to build HarfBuzz on Mac systems: MacPorts + and Homebrew. The process is similar to the process used on a + Linux system. + + + (1) You must first install the + development packages for FreeType, Cairo, and GLib. If you are + using MacPorts, you should run: + sudo port install freetype glib2 cairo + + + If you are using Homebrew, you should run: + brew install freetype glib cairo + + + (2) The next step depends on whether you are building from the + source in a downloaded release tarball or from the source directly + from the git repository. + + + (2)(a) If you are installing HarfBuzz + from a downloaded tarball release, extract the tarball and + open a Terminal in the extracted source-code directory. Run: + meson build + followed by: + meson compile -C build + to build HarfBuzz. + + + (2)(b) Alternatively, if you are building + HarfBuzz from the source in the HarfBuzz git repository, then + you must install several built-time dependencies before + proceeding. + + If you are + using MacPorts, you should run: + sudo port install meson pkgconfig gtk-doc + to install the build dependencies. + + If you are using Homebrew, you should run: + brew install meson pkgconfig gtk-doc + Finally, you can run: + meson build + + + (3) You can now build HarfBuzz (on either + a MacPorts or a Homebrew system) by running: + meson build + followed by: + meson compile -C build + + + This should leave you with a shared + library in the src/ directory, and a few + utility programs including hb-view and + hb-shape under the util/ + directory. + + +
+ +
+ Configuration options + + + The instructions in the "Building HarfBuzz" section will build + the source code under its default configuration. If needed, + the following additional configuration options are available. + + + + + + -Dglib=enabled + + + Use GLib. (Default = auto) + + + This option enables or disables usage of the GLib + library. The default setting is to check for the + presence of GLib and, if it is found, build with + GLib support. GLib is native to GNU/Linux systems but is + available on other operating system as well. + + + + + + -Dgobject=enabled + + + Use GObject. (Default = no) + + + This option enables or disables usage of the GObject + library. The default setting is to check for the + presence of GObject and, if it is found, build with + GObject support. GObject is native to GNU/Linux systems but is + available on other operating system as well. + + + + + + -Dcairo=enabled + + + Use Cairo. (Default = auto) + + + This option enables or disables usage of the Cairo + graphics-rendering library. The default setting is to + check for the presence of Cairo and, if it is found, + build with Cairo support. + + + Note: Cairo is used only by the HarfBuzz + command-line utilities, and not by the HarfBuzz library. + + + + + + -Dicu=enabled + + + Use the ICU library. (Default = auto) + + + This option enables or disables usage of the + International Components for + Unicode (ICU) library, which provides access + to Unicode Character Database (UCD) properties as well + as normalization and conversion functions. The default + setting is to check for the presence of ICU and, if it + is found, build with ICU support. + + + + + + -Dgraphite=enabled + + + Use the Graphite2 library. (Default = no) + + + This option enables or disables usage of the Graphite2 + library, which provides support for the Graphite shaping + model. + + + + + + -Dfreetype=enabled + + + Use the FreeType library. (Default = auto) + + + This option enables or disables usage of the FreeType + font-rendering library. The default setting is to check for the + presence of FreeType and, if it is found, build with + FreeType support. + + + + + + -Dgdi=enabled + + + Use the Uniscribe + library (experimental). (Default = no) + + + This option enables or disables usage of the Uniscribe + font-rendering library. Uniscribe is available on + Windows systems. Uniscribe support is used only for + testing purposes and does not need to be enabled for + HarfBuzz to run on Windows systems. + + + + + + -Ddirectwrite=enabled + + + Use the DirectWrite library (experimental). (Default = no) + + + This option enables or disables usage of the DirectWrite + font-rendering library. DirectWrite is available on + Windows systems. DirectWrite support is used only for + testing purposes and does not need to be enabled for + HarfBuzz to run on Windows systems. + + + + + + -Dcoretext=enabled + + + Use the CoreText library. (Default = no) + + + This option enables or disables usage of the CoreText + library. CoreText is available on macOS and iOS systems. + + + + + + -Ddocs=enabled + + + Use GTK-Doc. (Default = no) + + + This option enables the building of the documentation. + + + + +
+ +
+
diff --git a/libs/harfbuzz/docs/usermanual-integration.xml b/libs/harfbuzz/docs/usermanual-integration.xml new file mode 100644 index 00000000..5d31ec26 --- /dev/null +++ b/libs/harfbuzz/docs/usermanual-integration.xml @@ -0,0 +1,603 @@ + + + +]> + + Platform Integration Guide + + HarfBuzz was first developed for use with the GNOME and GTK + software stack commonly found in desktop Linux + distributions. Nevertheless, it can be used on other operating + systems and platforms, from iOS and macOS to Windows. It can also + be used with other application frameworks and components, such as + Android, Qt, or application-specific widget libraries. + + + This chapter will look at how HarfBuzz fits into a typical + text-rendering pipeline, and will discuss the APIs available to + integrate HarfBuzz with contemporary Linux, Mac, and Windows + software. It will also show how HarfBuzz integrates with popular + external libraries like FreeType and International Components for + Unicode (ICU) and describe the HarfBuzz language bindings for + Python. + + + On a GNOME system, HarfBuzz is designed to tie in with several + other common system libraries. The most common architecture uses + Pango at the layer directly "above" HarfBuzz; Pango is responsible + for text segmentation and for ensuring that each input + hb_buffer_t passed to HarfBuzz for shaping contains + Unicode code points that share the same segment properties + (namely, direction, language, and script, but also higher-level + properties like the active font, font style, and so on). + + + The layer directly "below" HarfBuzz is typically FreeType, which + is used to rasterize glyph outlines at the necessary optical size, + hinting settings, and pixel resolution. FreeType provides APIs for + accessing font and face information, so HarfBuzz includes + functions to create hb_face_t and + hb_font_t objects directly from FreeType + objects. HarfBuzz can use FreeType's built-in functions for + font_funcs vtable in an hb_font_t. + + + FreeType's output is bitmaps of the rasterized glyphs; on a + typical Linux system these will then be drawn by a graphics + library like Cairo, but those details are beyond HarfBuzz's + control. On the other hand, at the top end of the stack, Pango is + part of the larger GNOME framework, and HarfBuzz does include APIs + for working with key components of GNOME's higher-level libraries + — most notably GLib. + + + For other operating systems or application frameworks, the + critical integration points are where HarfBuzz gets font and face + information about the font used for shaping and where HarfBuzz + gets Unicode data about the input-buffer code points. + + + The font and face information is necessary for text shaping + because HarfBuzz needs to retrieve the glyph indices for + particular code points, and to know the extents and advances of + glyphs. Note that, in an OpenType variable font, both of those + types of information can change with different variation-axis + settings. + + + The Unicode information is necessary for shaping because the + properties of a code point (such as its General Category (gc), + Canonical Combining Class (ccc), and decomposition) can directly + impact the shaping moves that HarfBuzz performs. + + +
+ GNOME integration, GLib, and GObject + + As mentioned in the preceding section, HarfBuzz offers + integration APIs to help client programs using the + GNOME and GTK framework commonly found in desktop Linux + distributions. + + + GLib is the main utility library for GNOME applications. It + provides basic data types and conversions, file abstractions, + string manipulation, and macros, as well as facilities like + memory allocation and the main event loop. + + + Where text shaping is concerned, GLib provides several utilities + that HarfBuzz can take advantage of, including a set of + Unicode-data functions and a data type for script + information. Both are useful when working with HarfBuzz + buffers. To make use of them, you will need to include the + hb-glib.h header file. + + + GLib's Unicode + manipulation API includes all the functionality + necessary to retrieve Unicode data for the + unicode_funcs structure of a HarfBuzz + hb_buffer_t. + + + The function hb_glib_get_unicode_funcs() + sets up a hb_unicode_funcs_t structure configured + with the GLib Unicode functions and returns a pointer to it. + + + You can attach this Unicode-functions structure to your buffer, + and it will be ready for use with GLib: + + + #include <hb-glib.h> + ... + hb_unicode_funcs_t *glibufunctions; + glibufunctions = hb_glib_get_unicode_funcs(); + hb_buffer_set_unicode_funcs(buf, glibufunctions); + + + For script information, GLib uses the + GUnicodeScript type. Like HarfBuzz's own + hb_script_t, this data type is an enumeration + of Unicode scripts, but text segments passed in from GLib code + will be tagged with a GUnicodeScript. Therefore, + when setting the script property on a hb_buffer_t, + you will need to convert between the GUnicodeScript + of the input provided by GLib and HarfBuzz's + hb_script_t type. + + + The hb_glib_script_to_script() function + takes an GUnicodeScript script identifier as its + sole argument and returns the corresponding hb_script_t. + The hb_glib_script_from_script() does the + reverse, taking an hb_script_t and returning the + GUnicodeScript identifier for GLib. + + + Finally, GLib also provides a reference-counted object type called GBytes + that is used for accessing raw memory segments with the benefits + of GLib's lifecycle management. HarfBuzz provides a + hb_glib_blob_create() function that lets + you create an hb_blob_t directly from a + GBytes object. This function takes only the + GBytes object as its input; HarfBuzz registers the + GLib destroy callback automatically. + + + The GNOME platform also features an object system called + GObject. For HarfBuzz, the main advantage of GObject is a + feature called GObject + Introspection. This is a middleware facility that can be + used to generate language bindings for C libraries. HarfBuzz uses it + to build its Python bindings, which we will look at in a separate section. + +
+ +
+ FreeType integration + + FreeType is the free-software font-rendering engine included in + desktop Linux distributions, Android, ChromeOS, iOS, and multiple Unix + operating systems, and used by cross-platform programs like + Chrome, Java, and GhostScript. Used together, HarfBuzz can + perform shaping on Unicode text segments, outputting the glyph + IDs that FreeType should rasterize from the active font as well + as the positions at which those glyphs should be drawn. + + + HarfBuzz provides integration points with FreeType at the + face-object and font-object level and for the font-functions + virtual-method structure of a font object. To use the + FreeType-integration API, include the + hb-ft.h header. + + + In a typical client program, you will create your + hb_face_t face object and hb_font_t + font object from a FreeType FT_Face. HarfBuzz + provides a suite of functions for doing this. + + + In the most common case, you will want to use + hb_ft_font_create_referenced(), which + creates both an hb_face_t face object and + hb_font_t font object (linked to that face object), + and provides lifecycle management. + + + It is important to note, + though, that while HarfBuzz makes a distinction between its face and + font objects, FreeType's FT_Face does not. After + you create your FT_Face, you must set its size + parameter using FT_Set_Char_Size(), because + an hb_font_t is defined as an instance of an + hb_face_t with size specified. + + + #include <hb-ft.h> + ... + FT_New_Face(ft_library, font_path, index, &face); + FT_Set_Char_Size(face, 0, 1000, 0, 0); + hb_font_t *font = hb_ft_font_create(face); + + + hb_ft_font_create_referenced() is + the recommended function for creating an hb_face_t face + object. This function calls FT_Reference_Face() + before using the FT_Face and calls + FT_Done_Face() when it is finished using the + FT_Face. Consequently, your client program does not need + to worry about destroying the FT_Face while HarfBuzz + is still using it. + + + Although hb_ft_font_create_referenced() is + the recommended function, there is another variant for client code + where special circumstances make it necessary. The simpler + version of the function is hb_ft_font_create(), + which takes an FT_Face and an optional destroy callback + as its arguments. Because hb_ft_font_create() + does not offer lifecycle management, however, your client code will + be responsible for tracking references to the FT_Face + objects and destroying them when they are no longer needed. If you + do not have a valid reason for doing this, use + hb_ft_font_create_referenced(). + + + After you have created your font object from your + FT_Face, you can set or retrieve the + load_flags of the + FT_Face through the hb_font_t + object. HarfBuzz provides + hb_ft_font_set_load_flags() and + hb_ft_font_get_load_flags() for this + purpose. The ability to set the + load_flags through the font object + could be useful for enabling or disabling hinting, for example, + or to activate vertical layout. + + + HarfBuzz also provides a utility function called + hb_ft_font_has_changed() that you should + call whenever you have altered the properties of your underlying + FT_Face, as well as a + hb_ft_get_face() that you can call on an + hb_font_t font object to fetch its underlying FT_Face. + + + With an hb_face_t and hb_font_t both linked + to your FT_Face, you will typically also want to + use FreeType for the font_funcs + vtable of your hb_font_t. As a reminder, this + font-functions structure is the set of methods that HarfBuzz + will use to fetch important information from the font, such as + the advances and extents of individual glyphs. + + + All you need to do is call + + + hb_ft_font_set_funcs(font); + + + and HarfBuzz will use FreeType for the font-functions in + font. + + + As we noted above, an hb_font_t is derived from an + hb_face_t with size (and, perhaps, other + parameters, such as variation-axis coordinates) + specified. Consequently, you can reuse an hb_face_t + with several hb_font_t objects, and HarfBuzz + provides functions to simplify this. + + + The hb_ft_face_create_referenced() + function creates just an hb_face_t from a FreeType + FT_Face and, as with + hb_ft_font_create_referenced() above, + provides lifecycle management for the FT_Face. + + + Similarly, there is an hb_ft_face_create() + function variant that does not provide the lifecycle-management + feature. As with the font-object case, if you use this version + of the function, it will be your client code's respsonsibility + to track usage of the FT_Face objects. + + + A third variant of this function is + hb_ft_face_create_cached(), which is the + same as hb_ft_face_create() except that it + also uses the generic field of the + FT_Face structure to save a pointer to the newly + created hb_face_t. Subsequently, function calls + that pass the same FT_Face will get the same + hb_face_t returned — and the + hb_face_t will be correctly reference + counted. Still, as with + hb_ft_face_create(), your client code must + track references to the FT_Face itself, and destroy + it when it is unneeded. + +
+ +
+ Uniscribe integration + + If your client program is running on Windows, HarfBuzz offers + an additional API that can help integrate with Microsoft's + Uniscribe engine and the Windows GDI. + + + Overall, the Uniscribe API covers a broader set of typographic + layout functions than HarfBuzz implements, but HarfBuzz's + shaping API can serve as a drop-in replacement for Uniscribe's shaping + functionality. In fact, one of HarfBuzz's design goals is to + accurately reproduce the same output for shaping a given text + segment that Uniscribe produces — even to the point of + duplicating known shaping bugs or deviations from the + specification — so you can be confident that your users' + documents with their existing fonts will not be affected adversely by + switching to HarfBuzz. + + + At a basic level, HarfBuzz's hb_shape() + function replaces both the ScriptShape() + and ScriptPlace() + functions from Uniscribe. + + + However, whereas ScriptShape() returns the + glyphs and clusters for a shaped sequence and + ScriptPlace() returns the advances and + offsets for those glyphs, hb_shape() + handles both. After hb_shape() shapes a + buffer, the output glyph IDs and cluster IDs are returned as + an array of hb_glyph_info_t structures, and the + glyph advances and offsets are returned as an array of + hb_glyph_position_t structures. + + + Your client program only needs to ensure that it converts + correctly between HarfBuzz's low-level data types (such as + hb_position_t) and Windows's corresponding types + (such as GOFFSET and ABC). Be sure you + read the + chapter for a full explanation of how HarfBuzz input buffers are + used, and see for the + details of what hb_shape() returns in the + output buffer when shaping is complete. + + + Although hb_shape() itself is functionally + equivalent to Uniscribe's shaping routines, there are two + additional HarfBuzz functions you may want to use to integrate + the libraries in your code. Both are used to link HarfBuzz font + objects to the equivalent Windows structures. + + + The hb_uniscribe_font_get_logfontw() + function takes a hb_font_t font object and returns + a pointer to the LOGFONTW + "logical font" that corresponds to it. A LOGFONTW + structure holds font-wide attributes, including metrics, size, + and style information. + + + + The hb_uniscribe_font_get_hfont() function + also takes a hb_font_t font object, but it returns + an HFONT — a handle to the underlying logical + font — instead. + + + LOGFONTWs and HFONTs are both needed + by other Uniscribe functions. + + + As a final note, you may notice a reference to an optional + uniscribe shaper back-end in the section of the HarfBuzz manual. This + option is not a Uniscribe-integration facility. + + + Instead, it is a internal code path used in the + hb-shape command-line utility, which hands + shaping functionality over to Uniscribe entirely, when run on a + Windows system. That allows testing HarfBuzz's native output + against the Uniscribe engine, for tracking compatibility and + debugging. + + + Because this back-end is only used when testing HarfBuzz + functionality, it is disabled by default when building the + HarfBuzz binaries. + +
+ +
+ Core Text integration + + If your client program is running on macOS or iOS, HarfBuzz offers + an additional API that can help integrate with Apple's + Core Text engine and the underlying Core Graphics + framework. HarfBuzz does not attempt to offer the same + drop-in-replacement functionality for Core Text that it strives + for with Uniscribe on Windows, but you can still use HarfBuzz + to perform text shaping in native macOS and iOS applications. + + + Note, though, that if your interest is just in using fonts that + contain Apple Advanced Typography (AAT) features, then you do + not need to add Core Text integration. HarfBuzz natively + supports AAT features and will shape AAT fonts (on any platform) + automatically, without requiring additional work on your + part. This includes support for AAT-specific TrueType tables + such as mort, morx, and + kerx, which AAT fonts use instead of + GSUB and GPOS. + + + On a macOS or iOS system, the primary integration points offered + by HarfBuzz are for face objects and font objects. + + + The Apple APIs offer a pair of data structures that map well to + HarfBuzz's face and font objects. The Core Graphics API, which + is slightly lower-level than Core Text, provides + CGFontRef, which enables access to typeface + properties, but does not include size information. Core Text's + CTFontRef is analogous to a HarfBuzz font object, + with all of the properties required to render text at a specific + size and configuration. + Consequently, a HarfBuzz hb_font_t font object can + be hooked up to a Core Text CTFontRef, and a HarfBuzz + hb_face_t face object can be hooked up to a + CGFontRef. + + + You can create a hb_face_t from a + CGFontRef by using the + hb_coretext_face_create(). Subsequently, + you can retrieve the CGFontRef from a + hb_face_t with hb_coretext_face_get_cg_font(). + + + Likewise, you create a hb_font_t from a + CTFontRef by calling + hb_coretext_font_create(), and you can + fetch the associated CTFontRef from a + hb_font_t font object with + hb_coretext_face_get_ct_font(). + + + HarfBuzz also offers a hb_font_set_ptem() + that you an use to set the nominal point size on any + hb_font_t font object. Core Text uses this value to + implement optical scaling. + + + When integrating your client code with Core Text, it is + important to recognize that Core Text points + are not typographic points (standardized at 72 per inch) as the + term is used elsewhere in OpenType. Instead, Core Text points + are CSS points, which are standardized at 96 per inch. + + + HarfBuzz's font functions take this distinction into account, + but it can be an easy detail to miss in cross-platform + code. + + + As a final note, you may notice a reference to an optional + coretext shaper back-end in the section of the HarfBuzz manual. This + option is not a Core Text-integration facility. + + + Instead, it is a internal code path used in the + hb-shape command-line utility, which hands + shaping functionality over to Core Text entirely, when run on a + macOS system. That allows testing HarfBuzz's native output + against the Core Text engine, for tracking compatibility and debugging. + + + Because this back-end is only used when testing HarfBuzz + functionality, it is disabled by default when building the + HarfBuzz binaries. + +
+ +
+ ICU integration + + Although HarfBuzz includes its own Unicode-data functions, it + also provides integration APIs for using the International + Components for Unicode (ICU) library as a source of Unicode data + on any supported platform. + + + The principal integration point with ICU is the + hb_unicode_funcs_t Unicode-functions structure + attached to a buffer. This structure holds the virtual methods + used for retrieving Unicode character properties, such as + General Category, Script, Combining Class, decomposition + mappings, and mirroring information. + + + To use ICU in your client program, you need to call + hb_icu_get_unicode_funcs(), which creates a + Unicode-functions structure populated with the ICU function for + each included method. Subsequently, you can attach the + Unicode-functions structure to your buffer: + + + hb_unicode_funcs_t *icufunctions; + icufunctions = hb_icu_get_unicode_funcs(); + hb_buffer_set_unicode_funcs(buf, icufunctions); + + + and ICU will be used for Unicode-data access. + + + HarfBuzz also supplies a pair of functions + (hb_icu_script_from_script() and + hb_icu_script_to_script()) for converting + between ICU's and HarfBuzz's internal enumerations of Unicode + scripts. The hb_icu_script_from_script() + function converts from a HarfBuzz hb_script_t to an + ICU UScriptCode. The + hb_icu_script_to_script() function does the + reverse: converting from a UScriptCode identifier + to a hb_script_t. + + + By default, HarfBuzz's ICU support is built as a separate shared + library (libharfbuzz-icu.so) + when compiling HarfBuzz from source. This allows client programs + that do not need ICU to link against HarfBuzz without unnecessarily + adding ICU as a dependency. You can also build HarfBuzz with ICU + support built directly into the main HarfBuzz shared library + (libharfbuzz.so), + by specifying the --with-icu=builtin + compile-time option. + + +
+ +
+ Python bindings + + As noted in the section, + HarfBuzz uses a feature called GObject + Introspection (GI) to provide bindings for Python. + + + At compile time, the GI scanner analyzes the HarfBuzz C source + and builds metadata objects connecting the language bindings to + the C library. Your Python code can then use the HarfBuzz binary + through its Python interface. + + + HarfBuzz's Python bindings support Python 2 and Python 3. To use + them, you will need to have the pygobject + package installed. Then you should import + HarfBuzz from + gi.repository: + + + from gi.repository import HarfBuzz + + + and you can call HarfBuzz functions from Python. Sample code can + be found in the sample.py script in the + HarfBuzz src directory. + + + Do note, however, that the Python API is subject to change + without advance notice. GI allows the bindings to be + automatically updated, which is one of its advantages, but you + may need to update your Python code. + +
+ +
diff --git a/libs/harfbuzz/docs/usermanual-object-model.xml b/libs/harfbuzz/docs/usermanual-object-model.xml new file mode 100644 index 00000000..3c71a369 --- /dev/null +++ b/libs/harfbuzz/docs/usermanual-object-model.xml @@ -0,0 +1,266 @@ + + + +]> + + The HarfBuzz object model +
+ An overview of data types in HarfBuzz + + HarfBuzz features two kinds of data types: non-opaque, + pass-by-value types and opaque, heap-allocated types. This kind + of separation is common in C libraries that have to provide + API/ABI compatibility (almost) indefinitely. + + + Value types: The non-opaque, pass-by-value + types include integer types, enums, and small structs. Exposing + a struct in the public API makes it impossible to expand the + struct in the future. As such, exposing structs is reserved for + cases where it’s extremely inefficient to do otherwise. + + + In HarfBuzz, several structs, like hb_glyph_info_t and + hb_glyph_position_t, fall into that efficiency-sensitive + category and are non-opaque. + + + For all non-opaque structs where future extensibility may be + necessary, reserved members are included to hold space for + possible future members. As such, it’s important to provide + equal(), and hash() + methods for such structs, allowing users of the API do + effectively deal with the type without having to + adapt their code to future changes. + + + Important value types provided by HarfBuzz include the structs + for working with Unicode code points, glyphs, and tags for font + tables and features, as well as the enums for many Unicode and + OpenType properties. + +
+ +
+ Objects in HarfBuzz + + Object types: Opaque struct types are used + for what HarfBuzz loosely calls "objects." This doesn’t have + much to do with the terminology from object-oriented programming + (OOP), although some of the concepts are similar. + + + In HarfBuzz, all object types provide certain + lifecycle-management APIs. Objects are reference-counted, and + constructed with various create() methods, referenced via + reference() and dereferenced using + destroy(). + + + For example, + the hb_buffer_t object has + hb_buffer_create() as its constructor, + hb_buffer_reference() to reference, and + hb_buffer_destroy() to dereference. + + + After construction, each object's properties are accessible only + through the setter and getter functions described in the API + Reference manual. + + + Note that many object types can be marked as read-only or immutable, + facilitating their use in multi-threaded environments. + + + Key object types provided by HarfBuzz include: + + + + + blobs, which act as low-level wrappers around binary + data. Blobs are typically used to hold the contents of a + binary font file. + + + + + faces, which represent typefaces from a + font file, but without specific parameters (such as size) set. + + + + + fonts, which represent instances of a + face with all of their parameters specified. + + + + + buffers, which hold Unicode code points + for characters (before shaping) and the shaped glyph output + (after shaping). + + + + + shape plans, which store the settings + that HarfBuzz will use when shaping a particular text + segment. Shape plans are not generally used by client + programs directly, but as we will see in a later chapter, + they are still valuable to understand. + + + + +
+ + + +
+ Object lifecycle management + + Each object type in HarfBuzz provides a + create() method. Some object types provide + additional variants of create() to handle + special cases or to speed up common tasks; those variants are + documented in the API reference. For example, + hb_blob_create_from_file() constructs a new + blob directly from the contents of a file. + + + All objects are created with an initial reference count of + 1. Client programs can increase the reference + count on an object by calling its + reference() method. Whenever a client + program is finished with an object, it should call its + corresponding destroy() method. The destroy + method will decrease the reference count on the object and, + whenever the reference count reaches zero, it will also destroy + the object and free all of the associated memory. + + + All of HarfBuzz's object-lifecycle-management APIs are + thread-safe (unless you compiled HarfBuzz from source with the + HB_NO_MT configuration flag), even when the + object as a whole is not thread-safe. + It is also permissible to reference() or to + destroy() the NULL + value. + + + Some objects are thread-safe after they have been constructed + and set up. The general pattern is to + create() the object, make a few + set_*() calls to set up the + object, and then use it without further modification. + + + To ensure that such an object is not modified, client programs + can explicitly mark an object as immutable. HarfBuzz provides + make_immutable() methods to mark an object + as immutable and is_immutable() methods to + test whether or not an object is immutable. Attempts to use + setter functions on immutable objects will fail silently; see the API + Reference manual for specifics. + + + Note also that there are no "make mutable" methods. If client + programs need to alter an object previously marked as immutable, + they will need to make a duplicate of the original. + + + Finally, object constructors (and, indeed, as much of the + shaping API as possible) will never return + NULL. Instead, if there is an allocation + error, each constructor will return an “empty” object + singleton. + + + These empty-object singletons are inert and safe (although + typically useless) to pass around. This design choice avoids + having to check for NULL pointers all + throughout the code. + + + In addition, this “empty” object singleton can also be accessed + using the get_empty() method of the object + type in question. + +
+ + +
+ User data + + To better integrate with client programs, HarfBuzz's objects + offer a "user data" mechanism that can be used to attach + arbitrary data to the object. User-data attachment can be + useful for tying the lifecycles of various pieces of data + together, or for creating language bindings. + + + Each object type has a set_user_data() + method and a get_user_data() method. The + set_user_data() methods take a client-provided + key and a pointer, + user_data, pointing to the data itself. Once + the key-data pair has been attached to the object, the + get_user_data() method can be called with + the key, returning the user_data pointer. + + + The set_user_data() methods also support an + optional destroy callback. Client programs + can set the destroy callback and receive + notification from HarfBuzz whenever the object is destructed. + + + Finally, each set_user_data() method allows + the client program to set a replace Boolean + indicating whether or not the function call should replace any + existing user_data + associated with the specified key. + +
+ + + +
+ Blobs + + While most of HarfBuzz's object types are specific to the + shaping process, blobs are somewhat + different. + + + Blobs are an abstraction designed to negotiate lifecycle and + permissions for raw pieces of data. For example, when you load + the raw font data into memory and want to pass it to HarfBuzz, + you do so in a hb_blob_t wrapper. + + + This allows you to take advantage of HarfBuzz's + reference-counting and destroy + callbacks. If you allocated the memory for the data using + malloc(), you would create the blob using + + + hb_blob_create (data, length, HB_MEMORY_MODE_WRITABLE, data, free) + + + That way, HarfBuzz will call free() on the + allocated memory whenever the blob drops its last reference and + is deconstructed. Consequently, the user code can stop worrying + about freeing memory and let the reference-counting machinery + take care of that. + + + Most of the time, blobs are read-only, facilitating their use in + immutable objects. + +
+ +
diff --git a/libs/harfbuzz/docs/usermanual-opentype-features.xml b/libs/harfbuzz/docs/usermanual-opentype-features.xml new file mode 100644 index 00000000..56eba617 --- /dev/null +++ b/libs/harfbuzz/docs/usermanual-opentype-features.xml @@ -0,0 +1,336 @@ + + + +]> + + Shaping and shape plans + + Once you have your face and font objects configured as desired and + your input buffer is filled with the characters you need to shape, + all you need to do is call hb_shape(). + + + HarfBuzz will return the shaped version of the text in the same + buffer that you provided, but it will be in output mode. At that + point, you can iterate through the glyphs in the buffer, drawing + each one at the specified position or handing them off to the + appropriate graphics library. + + + For the most part, HarfBuzz's shaping step is straightforward from + the outside. But that doesn't mean there will never be cases where + you want to look under the hood and see what is happening on the + inside. HarfBuzz provides facilities for doing that, too. + + +
+ Shaping and buffer output + + The hb_shape() function call takes four arguments: the font + object to use, the buffer of characters to shape, an array of + user-specified features to apply, and the length of that feature + array. The feature array can be NULL, so for the sake of + simplicity we will start with that case. + + + Internally, HarfBuzz looks at the tables of the font file to + determine where glyph classes, substitutions, and positioning + are defined, using that information to decide which + shaper to use (ot for + OpenType fonts, aat for Apple Advanced + Typography fonts, and so on). It also looks at the direction, + script, and language properties of the segment to figure out + which script-specific shaping model is needed (at least, in + shapers that support multiple options). + + + If a font has a GDEF table, then that is used for + glyph classes; if not, HarfBuzz will fall back to Unicode + categorization by code point. If a font has an AAT morx table, + then it is used for substitutions; if not, but there is a GSUB + table, then the GSUB table is used. If the font has an AAT + kerx table, then it is used for positioning; if not, but + there is a GPOS table, then the GPOS table is used. If neither + table is found, but there is a kern table, then HarfBuzz will + use the kern table. If there is no kerx, no GPOS, and no + kern, HarfBuzz will fall back to positioning marks itself. + + + With a well-behaved OpenType font, you expect GDEF, GSUB, and + GPOS tables to all be applied. HarfBuzz implements the + script-specific shaping models in internal functions, rather + than in the public API. + + + The algorithms + used for shaping can be quite involved; HarfBuzz tries + to be compatible with the OpenType Layout specification + and, wherever there is any ambiguity, HarfBuzz attempts to replicate the + output of Microsoft's Uniscribe engine, to the extent that is feasible and desirable. See the Microsoft + Typography pages for more detail. + + + In general, though, all that you need to know is that + hb_shape() returns the results of shaping + in the same buffer that you provided. The buffer's content type + will now be set to + HB_BUFFER_CONTENT_TYPE_GLYPHS, indicating + that it contains shaped output, rather than input text. You can + now extract the glyph information and positioning arrays: + + + hb_glyph_info_t *glyph_info = hb_buffer_get_glyph_infos(buf, &glyph_count); + hb_glyph_position_t *glyph_pos = hb_buffer_get_glyph_positions(buf, &glyph_count); + + + The glyph information array holds a hb_glyph_info_t + for each output glyph, which has two fields: + codepoint and + cluster. Whereas, in the input buffer, + the codepoint field contained the Unicode + code point, it now contains the glyph ID of the corresponding + glyph in the font. The cluster field is + an integer that you can use to help identify when shaping has + reordered, split, or combined code points; we will say more + about that in the next chapter. + + + The glyph positions array holds a corresponding + hb_glyph_position_t for each output glyph, + containing four fields: x_advance, + y_advance, + x_offset, and + y_offset. The advances tell you how far + you need to move the drawing point after drawing this glyph, + depending on whether you are setting horizontal text (in which + case you will have x advances) or vertical text (for which you + will have y advances). The x and y offsets tell you where to + move to start drawing the glyph; usually you will have both and + x and a y offset, regardless of the text direction. + + + Most of the time, you will rely on a font-rendering library or + other graphics library to do the actual drawing of glyphs, so + you will need to iterate through the glyphs in the buffer and + pass the corresponding values off. + +
+ +
+ OpenType features + + OpenType features enable fonts to include smart behavior, + implemented as "lookup" rules stored in the GSUB and GPOS + tables. The OpenType specification defines a long list of + standard features that fonts can use for these behaviors; each + feature has a four-character reserved name and a well-defined + semantic meaning. + + + Some OpenType features are defined for the purpose of supporting + script-specific shaping, and are automatically activated, but + only when a buffer's script property is set to a script that the + feature supports. + + + Other features are more generic and can apply to several (or + any) script, and shaping engines are expected to implement + them. By default, HarfBuzz activates several of these features + on every text run. They include abvm, + blwm, ccmp, + locl, mark, + mkmk, and rlig. + + + In addition, if the text direction is horizontal, HarfBuzz + also applies the calt, + clig, curs, + dist, kern, + liga and rclt, features. + + + Additionally, when HarfBuzz encounters a fraction slash + (U+2044), it looks backward and forward for decimal + digits (Unicode General Category = Nd), and enables features + numr on the sequence before the fraction slash, + dnom on the sequence after the fraction slash, + and frac on the whole sequence including the fraction + slash. + + + Some script-specific shaping models + (see ) disable some of the + features listed above: + + + + + Hangul: calt + + + + + Indic: liga + + + + + Khmer: liga + + + + + If the text direction is vertical, HarfBuzz applies + the vert feature by default. + + + Still other features are designed to be purely optional and left + up to the application or the end user to enable or disable as desired. + + + You can adjust the set of features that HarfBuzz applies to a + buffer by supplying an array of hb_feature_t + features as the third argument to + hb_shape(). For a simple case, let's just + enable the dlig feature, which turns on any + "discretionary" ligatures in the font: + + + hb_feature_t userfeatures[1]; + userfeatures[0].tag = HB_TAG('d','l','i','g'); + userfeatures[0].value = 1; + userfeatures[0].start = HB_FEATURE_GLOBAL_START; + userfeatures[0].end = HB_FEATURE_GLOBAL_END; + + + HB_FEATURE_GLOBAL_END and + HB_FEATURE_GLOBAL_END are macros we can use + to indicate that the features will be applied to the entire + buffer. We could also have used a literal 0 + for the start and a -1 to indicate the end of + the buffer (or have selected other start and end positions, if needed). + + + When we pass the userfeatures array to + hb_shape(), any discretionary ligature + substitutions from our font that match the text in our buffer + will get performed: + + + hb_shape(font, buf, userfeatures, num_features); + + + Just like we enabled the dlig feature by + setting its value to + 1, you would disable a feature by setting its + value to 0. Some + features can take other value settings; + be sure you read the full specification of each feature tag to + understand what it does and how to control it. + +
+ +
+ Shaper selection + + The basic version of hb_shape() determines + its shaping strategy based on examining the capabilities of the + font file. OpenType font tables cause HarfBuzz to try the + ot shaper, while AAT font tables cause HarfBuzz to try the + aat shaper. + + + In the real world, however, a font might include some unusual + mix of tables, or one of the tables might simply be broken for + the script you need to shape. So, sometimes, you might not + want to rely on HarfBuzz's process for deciding what to do, and + just tell hb_shape() what you want it to try. + + + hb_shape_full() is an alternate shaping + function that lets you supply a list of shapers for HarfBuzz to + try, in order, when shaping your buffer. For example, if you + have determined that HarfBuzz's attempts to work around broken + tables gives you better results than the AAT shaper itself does, + you might move the AAT shaper to the end of your list of + preferences and call hb_shape_full() + + + char *shaperprefs[3] = {"ot", "default", "aat"}; + ... + hb_shape_full(font, buf, userfeatures, num_features, shaperprefs); + + + to get results you are happier with. + + + You may also want to call + hb_shape_list_shapers() to get a list of + the shapers that were built at compile time in your copy of HarfBuzz. + +
+ +
+ Plans and caching + + Internally, HarfBuzz uses a structure called a shape plan to + track its decisions about how to shape the contents of a + buffer. The hb_shape() function builds up the shape plan by + examining segment properties and by inspecting the contents of + the font. + + + This process can involve some decision-making and + trade-offs — for example, HarfBuzz inspects the GSUB and GPOS + lookups for the script and language tags set on the segment + properties, but it falls back on the lookups under the + DFLT tag (and sometimes other common tags) + if there are actually no lookups for the tag requested. + + + HarfBuzz also includes some work-arounds for + handling well-known older font conventions that do not follow + OpenType or Unicode specifications, for buggy system fonts, and for + peculiarities of Microsoft Uniscribe. All of that means that a + shape plan, while not something that you should edit directly in + client code, still might be an object that you want to + inspect. Furthermore, if resources are tight, you might want to + cache the shape plan that HarfBuzz builds for your buffer and + font, so that you do not have to rebuild it for every shaping call. + + + You can create a cacheable shape plan with + hb_shape_plan_create_cached(face, props, + user_features, num_user_features, shaper_list), where + face is a face object (not a font object, + notably), props is an + hb_segment_properties_t, + user_features is an array of + hb_feature_ts (with length + num_user_features), and + shaper_list is a list of shapers to try. + + + Shape plans are objects in HarfBuzz, so there are + reference-counting functions and user-data attachment functions + you can + use. hb_shape_plan_reference(shape_plan) + increases the reference count on a shape plan, while + hb_shape_plan_destroy(shape_plan) decreases + the reference count, destroying the shape plan when the last + reference is dropped. + + + You can attach user data to a shaper (with a key) using the + hb_shape_plan_set_user_data(shape_plan,key,data,destroy,replace) + function, optionally supplying a destroy + callback to use. You can then fetch the user data attached to a + shape plan with + hb_shape_plan_get_user_data(shape_plan, key). + +
+ +
diff --git a/libs/harfbuzz/docs/usermanual-shaping-concepts.xml b/libs/harfbuzz/docs/usermanual-shaping-concepts.xml new file mode 100644 index 00000000..a95b0cbf --- /dev/null +++ b/libs/harfbuzz/docs/usermanual-shaping-concepts.xml @@ -0,0 +1,368 @@ + + + +]> + + Shaping concepts +
+ Text shaping + + Text shaping is the process of transforming a sequence of Unicode + codepoints that represent individual characters (letters, + diacritics, tone marks, numbers, symbols, etc.) into the + orthographically and linguistically correct two-dimensional layout + of glyph shapes taken from a specified font. + + + For some writing systems (or scripts) and + languages, the process is simple, requiring the shaper to do + little more than advance the horizontal position forward by the + correct amount for each successive glyph. + + + But, for other scripts (often unceremoniously called complex scripts), any combination of + several shaping operations may be required, and the rules for how + and when they are applied vary from script to script. HarfBuzz and + other shaping engines implement these rules. + + + The exact rules and necessary operations for a particular script + constitute a shaping model. OpenType + specifies a set of shaping models that covers all of + Unicode. Other shaping models are available, however, including + Graphite and Apple Advanced Typography (AAT). + +
+ +
+ Script-specific shaping + + In many scripts, transforming the input + sequence into the final layout often requires some combination of + operations—such as context-dependent substitutions, + context-dependent mark positioning, glyph-to-glyph joining, + glyph reordering, or glyph stacking. + + + In some scripts, the shaping rules require that a text + run be divided into syllables before the operations can be + applied. Other scripts may apply shaping operations over + entire words or over the entire text run, with no subdivision + required. + + + Other scripts, do not require these + operations. However, correctly shaping a text run in + any script may still involve Unicode normalization, + ligature substitutions, mark positioning, kerning, and applying + other font features. + +
+ +
+ Shaping operations + + Shaping a text run involves transforming the + input sequence of Unicode codepoints with some combination of + operations that is specified in the shaping model for the + script. + + + The specific conditions that trigger a given operation for a + text run varies from script to script, as do the order that the + operations are performed in and which codepoints are + affected. However, the same general set of shaping operations is + common to all of the script shaping models. + + + + + + A reordering operation moves a glyph + from its original ("logical") position in the sequence to + some other ("visual") position. + + + The shaping model for a given script might involve + more than one reordering step. + + + + + + A joining operation replaces a glyph + with an alternate form that is designed to connect with one + or more of the adjacent glyphs in the sequence. + + + + + + A contextual substitution operation + replaces either a single glyph or a subsequence of several + glyphs with an alternate glyph. This substitution is + performed when the original glyph or subsequence of glyphs + occurs in a specified position with respect to the + surrounding sequence. For example, one substitution might be + performed only when the target glyph is the first glyph in + the sequence, while another substitution is performed only + when a different target glyph occurs immediately after a + particular string pattern. + + + The shaping model for a given script might involve + multiple contextual-substitution operations, each applying + to different target glyphs and patterns, and which are + performed in separate steps. + + + + + + A contextual positioning operation + moves the horizontal and/or vertical position of a + glyph. This positioning move is performed when the glyph + occurs in a specified position with respect to the + surrounding sequence. + + + Many contextual positioning operations are used to place + mark glyphs (such as diacritics, vowel + signs, and tone markers) with respect to + base glyphs. However, some + scripts may use contextual positioning operations to + correctly place base glyphs as well, such as + when the script uses stacking characters. + + + + +
+ +
+ Unicode character categories + + Shaping models are typically specified with respect to how + scripts are defined in the Unicode standard. + + + Every codepoint in the Unicode Character Database (UCD) is + assigned a Unicode General Category (UGC), + which provides the most fundamental information about the + codepoint: whether the codepoint represents a + Letter, a Mark, a + Number, Punctuation, a + Symbol, a Separator, + or something else (Other). + + + These UGC properties are "Major" categories. Each codepoint is + further assigned to a "minor" category within its Major + category, such as "Letter, uppercase" (Lu) or + "Letter, modifier" (Lm). + + + Shaping models are concerned primarily with Letter and Mark + codepoints. The minor categories of Mark codepoints are + particularly important for shaping. Marks can be nonspacing + (Mn), spacing combining + (Mc), or enclosing (Me). + + + In addition to the UGC property, codepoints in the Indic and + Southeast Asian scripts are also assigned + Unicode Indic Syllabic Category (UISC) and + Unicode Indic Positional Category (UIPC) + properties that provide more detailed information needed for + shaping. + + + The UISC property sub-categorizes Letters and Marks according to + common script-shaping behaviors. For example, UISC distinguishes + between consonant letters, vowel letters, and vowel marks. The + UIPC property sub-categorizes Mark codepoints by the relative visual + position that they occupy (above, below, right, left, or in + multiple positions). + + + Some scripts require that the text run be split into + syllables. What constitutes a valid syllable in these + scripts is specified in regular expressions, formed from the + Letter and Mark codepoints, that take the UISC and UIPC + properties into account. + + +
+ +
+ Text runs + + Real-world text usually contains codepoints from a mixture of + different Unicode scripts (including punctuation, numbers, symbols, + white-space characters, and other codepoints that do not belong + to any script). Real-world text may also be marked up with + formatting that changes font properties (including the font, + font style, and font size). + + + For shaping purposes, all real-world text streams must be first + segmented into runs that have a uniform set of properties. + + + In particular, shaping models always assume that every codepoint + in a text run has the same direction, + script tag, and + language tag. + +
+ +
+ OpenType shaping models + + OpenType provides shaping models for the following scripts: + + + + + + The default shaping model handles all + scripts with no script-specific shaping model, and may also be used as a fallback for + handling unrecognized scripts. + + + + + + The Indic shaping model handles the Indic + scripts Bengali, Devanagari, Gujarati, Gurmukhi, Kannada, + Malayalam, Oriya, Tamil, and Telugu. + + + The Indic shaping model was revised significantly in + 2005. To denote the change, a new set of script + tags was assigned for Bengali, Devanagari, + Gujarati, Gurmukhi, Kannada, Malayalam, Oriya, Tamil, and + Telugu. For the sake of clarity, the term "Indic2" is + sometimes used to refer to the current, revised shaping + model. + + + + + + The Arabic shaping model supports + Arabic, Mongolian, N'Ko, Syriac, and several other connected + or cursive scripts. + + + + + + The Thai/Lao shaping model supports + the Thai and Lao scripts. + + + + + + The Khmer shaping model supports the + Khmer script. + + + + + + The Myanmar shaping model supports the + Myanmar (or Burmese) script. + + + + + + The Tibetan shaping model supports the + Tibetan script. + + + + + + The Hangul shaping model supports the + Hangul script. + + + + + + The Hebrew shaping model supports the + Hebrew script. + + + + + + The Universal Shaping Engine (USE) + shaping model supports scripts not covered by one of + the above, script-specific shaping models, including + Javanese, Balinese, Buginese, Batak, Chakma, Lepcha, Modi, + Phags-pa, Tagalog, Siddham, Sundanese, Tai Le, Tai Tham, Tai + Viet, and many others. + + + + + + Text runs that do not fall under one of the above shaping + models may still require processing by a shaping engine. Of + particular note is Emoji shaping, which + may involve variation-selector sequences and glyph + substitution. Emoji shaping is handled by the default + shaping model. + + + + + +
+ +
+ Graphite shaping + + In contrast to OpenType shaping, Graphite shaping does not + specify a predefined set of shaping models or a set of supported + scripts. + + + Instead, each Graphite font contains a complete set of rules that + implement the required shaping model for the intended + script. These rules include finite-state machines to match + sequences of codepoints to the shaping operations to perform. + + + Graphite shaping can perform the same shaping operations used in + OpenType shaping, as well as other functions that have not been + defined for OpenType shaping. + +
+ +
+ AAT shaping + + In contrast to OpenType shaping, AAT shaping does not specify a + predefined set of shaping models or a set of supported scripts. + + + Instead, each AAT font includes a complete set of rules that + implement the desired shaping model for the intended + script. These rules include finite-state machines to match glyph + sequences and the shaping operations to perform. + + + Notably, AAT shaping rules are expressed for glyphs in the font, + not for Unicode codepoints. AAT shaping can perform the same + shaping operations used in OpenType shaping, as well as other + functions that have not been defined for OpenType shaping. + +
+
diff --git a/libs/harfbuzz/docs/usermanual-utilities.xml b/libs/harfbuzz/docs/usermanual-utilities.xml new file mode 100644 index 00000000..0208dbf7 --- /dev/null +++ b/libs/harfbuzz/docs/usermanual-utilities.xml @@ -0,0 +1,218 @@ + + + +]> + + Utilities + + HarfBuzz includes several auxiliary components in addition to the + main APIs. These include a set of command-line tools, a set of + lower-level APIs for common data types that may be of interest to + client programs. + + +
+ Command-line tools + + HarfBuzz include three command-line tools: + hb-shape, hb-view, and + hb-subset. They can be used to examine + HarfBuzz's functionality, debug font binaries, or explore the + various shaping models and features from a terminal. + + +
+ hb-shape + + hb-shape allows you to run HarfBuzz's + hb_shape() function on an input string and + to examine the outcome, in human-readable form, as terminal + output. hb-shape does + not render the results of the shaping call + into rendered text (you can use hb-view, below, for + that). Instead, it prints out the final glyph indices and + positions, taking all shaping operations into account, as if the + input string were a HarfBuzz input buffer. + + + You can specify the font to be used for shaping and, with + command-line options, you can add various aspects of the + internal state to the output that is sent to the terminal. The + general format is + + + hb-shape [OPTIONS] + path/to/font/file.ttf + yourinputtext + + + The default output format is plain text (although JSON output + can be selected instead by specifying the option + --output-format=json). The default output + syntax reports each glyph name (or glyph index if there is no + name) followed by its cluster value, its horizontal and vertical + position displacement, and its horizontal and vertical advances. + + + Output options exist to skip any of these elements in the + output, and to include additional data, such as Unicode + code-point values, glyph extents, glyph flags, or interim + shaping results. + + + Output can also be redirected to a file, or input read from a + file. Additional options enable you to enable or disable + specific font features, to set variation-font axis values, to + alter the language, script, direction, and clustering settings + used, to enable sanity checks, or to change which shaping engine is used. + + + For a complete explanation of the options available, run + + + hb-shape --help + +
+ +
+ hb-view + + hb-view allows you to + see the shaped output of an input string in rendered + form. Like hb-shape, + hb-view takes a font file and a text string + as its arguments: + + + hb-view [OPTIONS] + path/to/font/file.ttf + yourinputtext + + + By default, hb-view renders the shaped + text in ASCII block-character images as terminal output. By + appending the + --output-file=filename + switch, you can write the output to a PNG, SVG, or PDF file + (among other formats). + + + As with hb-shape, a lengthy set of options + is available, with which you can enable or disable + specific font features, set variation-font axis values, + alter the language, script, direction, and clustering settings + used, enable sanity checks, or change which shaping engine is + used. + + + You can also set the foreground and background colors used for + the output, independently control the width of all four + margins, alter the line spacing, and annotate the output image + with + + + In general, hb-view is a quick way to + verify that the output of HarfBuzz's shaping operation looks + correct for a given text-and-font combination, but you may + want to use hb-shape to figure out exactly + why something does not appear as expected. + +
+ +
+ hb-subset + + hb-subset allows you + to generate a subset of a given font, with a limited set of + supported characters, features, and variation settings. + + + By default, you provide an input font and an input text string + as the arguments to hb-subset, and it will + generate a font that covers the input text exactly like the + input font does, but includes no other characters or features. + + + hb-subset [OPTIONS] + path/to/font/file.ttf + yourinputtext + + + For example, to create a subset of Noto Serif that just includes the + numerals and the lowercase Latin alphabet, you could run + + + hb-subset [OPTIONS] + NotoSerif-Regular.ttf + 0123456789abcdefghijklmnopqrstuvwxyz + + + There are options available to remove hinting from the + subsetted font and to specify a list of variation-axis settings. + +
+ +
+ +
+ Common data types and APIs + + HarfBuzz includes several APIs for working with general-purpose + data that you may find convenient to leverage in your own + software. They include set operations and integer-to-integer + mapping operations. + + + HarfBuzz uses set operations for internal bookkeeping, such as + when it collects all of the glyph IDs covered by a particular + font feature. You can also use the set API to build sets, add + and remove elements, test whether or not sets contain particular + elements, or compute the unions, intersections, or differences + between sets. + + + All set elements are integers (specifically, + hb_codepoint_t 32-bit unsigned ints), and there are + functions for fetching the minimum and maximum element from a + set. The set API also includes some functions that might not + be part of a generic set facility, such as the ability to add a + contiguous range of integer elements to a set in bulk, and the + ability to fetch the next-smallest or next-largest element. + + + The HarfBuzz set API includes some conveniences as well. All + sets are lifecycle-managed, just like other HarfBuzz + objects. You increase the reference count on a set with + hb_set_reference() and decrease it with + hb_set_destroy(). You can also attach + user data to a set, just like you can to blobs, buffers, faces, + fonts, and other objects, and set destroy callbacks. + + + HarfBuzz also provides an API for keeping track of + integer-to-integer mappings. As with the set API, each integer is + stored as an unsigned 32-bit hb_codepoint_t + element. Maps, like other objects, are reference counted with + reference and destroy functions, and you can attach user data to + them. The mapping operations include adding and deleting + integer-to-integer key:value pairs to the map, testing for the + presence of a key, fetching the population of the map, and so on. + + + There are several other internal HarfBuzz facilities that are + exposed publicly and which you may want to take advantage of + while processing text. HarfBuzz uses a common + hb_tag_t for a variety of OpenType tag identifiers (for + scripts, languages, font features, table names, variation-axis + names, and more), and provides functions for converting strings + to tags and vice-versa. + + + Finally, HarfBuzz also includes data type for Booleans, bit + masks, and other simple types. + +
+ +
diff --git a/libs/harfbuzz/docs/usermanual-what-is-harfbuzz.xml b/libs/harfbuzz/docs/usermanual-what-is-harfbuzz.xml new file mode 100644 index 00000000..fdf04b24 --- /dev/null +++ b/libs/harfbuzz/docs/usermanual-what-is-harfbuzz.xml @@ -0,0 +1,441 @@ + + + +]> + + What is HarfBuzz? + + HarfBuzz is a text-shaping engine. If you + give HarfBuzz a font and a string containing a sequence of Unicode + codepoints, HarfBuzz selects and positions the corresponding + glyphs from the font, applying all of the necessary layout rules + and font features. HarfBuzz then returns the string to you in the + form that is correctly arranged for the language and writing + system. + + + HarfBuzz can properly shape all of the world's major writing + systems. It runs on all major operating systems and software + platforms and it supports the major font formats in use + today. + +
+ What is text shaping? + + Text shaping is the process of translating a string of character + codes (such as Unicode codepoints) into a properly arranged + sequence of glyphs that can be rendered onto a screen or into + final output form for inclusion in a document. + + + The shaping process is dependent on the input string, the active + font, the script (or writing system) that the string is in, and + the language that the string is in. + + + Modern software systems generally only deal with strings in the + Unicode encoding scheme (although legacy systems and documents may + involve other encodings). + + + There are several font formats that a program might + encounter, each of which has a set of standard text-shaping + rules. + + The dominant format is OpenType. The + OpenType specification defines a series of shaping models for + various scripts from around the world. These shaping models depend on + the font incorporating certain features as + lookups in its GSUB + and GPOS tables. + + + Alternatively, OpenType fonts can include shaping features for + the Graphite shaping model. + + + TrueType fonts can also include OpenType shaping + features. Alternatively, TrueType fonts can also include Apple + Advanced Typography (AAT) tables to implement shaping + support. AAT fonts are generally only found on macOS and iOS systems. + + + Text strings will usually be tagged with a script and language + tag that provide the context needed to perform text shaping + correctly. The necessary script + and language + tags are defined by OpenType. + +
+ +
+ Why do I need a shaping engine? + + Text shaping is an integral part of preparing text for + display. Before a Unicode sequence can be rendered, the + codepoints in the sequence must be mapped to the corresponding + glyphs provided in the font, and those glyphs must be positioned + correctly relative to each other. For many of the scripts + supported in Unicode, these steps involve script-specific layout + rules, including complex joining, reordering, and positioning + behavior. Implementing these rules is the job of the shaping engine. + + + Text shaping is a fairly low-level operation. HarfBuzz is + used directly by text-handling libraries like Pango, as well as by the layout + engines in Firefox, LibreOffice, and Chromium. Unless you are + writing one of these layout engines + yourself, you will probably not need to use HarfBuzz: normally, + a layout engine, toolkit, or other library will turn text into + glyphs for you. + + + However, if you are writing a layout engine + or graphics library yourself, then you will need to perform text + shaping, and this is where HarfBuzz can help you. + + + Here are some specific scenarios where a text-shaping engine + like HarfBuzz helps you: + + + + + OpenType fonts contain a set of glyphs (that is, shapes + to represent the letters, numbers, punctuation marks, and + all other symbols), which are indexed by a glyph ID. + + + A particular glyph ID within the font does not necessarily + correlate to a predictable Unicode codepoint. For instance, + some fonts have the letter "a" as glyph ID 1, but + many others do not. In order to retrieve the right glyph + from the font to display "a", you need to consult + the table inside the font (the cmap + table) that maps Unicode codepoints to glyph IDs. In other + words, text shaping turns codepoints into glyph + IDs. + + + + + Many OpenType fonts contain ligatures: combinations of + characters that are rendered as a single unit. For instance, + it is common for the "f, i" letter + sequence to appear in print as the single ligature glyph + "fi". + + + Whether you should render an "f, i" sequence + as fi or as "fi" does not + depend on the input text. Instead, it depends on the whether + or not the font includes an "fi" glyph and on the + level of ligature application you wish to perform. The font + and the amount of ligature application used are under your + control. In other words, text shaping involves + querying the font's ligature tables and determining what + substitutions should be made. + + + + + While ligatures like "fi" are optional typographic + refinements, some languages require certain + substitutions to be made in order to display text correctly. + + + For example, in Tamil, when the letter "TTA" (ட) + letter is followed by the vowel sign "U" (ு), the pair + must be replaced by the single glyph "டு". The + sequence of Unicode characters "ட,ு" needs to be + substituted with a single "டு" glyph from the + font. + + + But "டு" does not have a Unicode codepoint. To + find this glyph, you need to consult the table inside + the font (the GSUB table) that contains + substitution information. In other words, text shaping + chooses the correct glyph for a sequence of characters + provided. + + + + + Similarly, each Arabic character has four different variants + corresponding to the different positions it might appear in + within a sequence. Inside a font, there will be separate + glyphs for the initial, medial, final, and isolated forms of + each letter, each at a different glyph ID. + + + Unicode only assigns one codepoint per character, so a + Unicode string will not tell you which glyph variant to use + for each character. To decide, you need to analyze the whole + string and determine the appropriate glyph for each character + based on its position. In other words, text + shaping chooses the correct form of the letter by its + position and returns the correct glyph from the font. + + + + + Other languages involve marks and accents that need to be + rendered in specific positions relative a base character. For + instance, the Moldovan language includes the Cyrillic letter + "zhe" (ж) with a breve accent, like so: "ӂ". + + + Some fonts will provide this character as a single + zhe-with-breve glyph, but other fonts will not and, instead, + will expect the rendering engine to form the character by + superimposing the separate "ж" and "˘" + glyphs. + + + But exactly where you should draw the breve depends on the + height and width of the preceding zhe glyph. To find the + right position, you need to consult the table inside + the font (the GPOS table) that contains + positioning information. + In other words, text shaping tells you whether you + have a precomposed glyph within your font or if you need to + compose a glyph yourself out of combining marks—and, + if so, where to position those marks. + + + + + If tasks like these are something that you need to do, then you + need a text shaping engine. You could use Uniscribe if you are + writing Windows software; you could use CoreText on macOS; or + you could use HarfBuzz. + + + + In the rest of this manual, the text will assume that the reader + is that implementor of a text-layout engine. + + +
+ + +
+ What does HarfBuzz do? + + HarfBuzz provides text shaping through a cross-platform + C API that accepts sequences of Unicode codepoints as input. Currently, + the following OpenType shaping models are supported: + + + + + Indic (covering Devanagari, Bengali, Gujarati, + Gurmukhi, Kannada, Malayalam, Oriya, Tamil, and Telugu) + + + + + Arabic (covering Arabic, N'Ko, Syriac, and Mongolian) + + + + + Thai and Lao + + + + + Khmer + + + + + Myanmar + + + + + + Tibetan + + + + + + Hangul + + + + + + Hebrew + + + + + The Universal Shaping Engine or USE + (covering complex scripts not covered by the above shaping + models) + + + + + A default shaping model for non-complex scripts + (covering Latin, Cyrillic, Greek, Armenian, Georgian, Tifinagh, + and many others) + + + + + Emoji (including emoji modifier sequences, flag sequences, + and ZWJ sequences) + + + + + + In addition to OpenType shaping, HarfBuzz supports the latest + version of Graphite shaping (the "Graphite 2" model) and AAT + shaping. + + + + HarfBuzz can read and understand TrueType fonts (.ttf), TrueType + collections (.ttc), and OpenType fonts (.otf, including those + fonts that contain TrueType-style outlines and those that + contain PostScript CFF or CFF2 outlines). + + + + HarfBuzz is designed and tested to run on top of the FreeType + font renderer. It can run on Linux, Android, Windows, macOS, and + iOS systems. + + + + In addition to its core shaping functionality, HarfBuzz provides + functions for accessing other font features, including optional + GSUB and GPOS OpenType features, as well as + all color-font formats (CBDT, + sbix, COLR/CPAL, and + SVG-OT) and OpenType variable fonts. HarfBuzz + also includes a font-subsetting feature. HarfBuzz can perform + some low-level math-shaping operations, although it does not + currently perform full shaping for mathematical typesetting. + + + + A suite of command-line utilities is also provided in the + source-code tree, designed to help users test and debug + HarfBuzz's features on real-world fonts and input. + +
+ +
+ What HarfBuzz doesn't do + + HarfBuzz will take a Unicode string, shape it, and give you the + information required to lay it out correctly on a single + horizontal (or vertical) line using the font provided. That is the + extent of HarfBuzz's responsibility. + + + It is important to note that if you are implementing a complete + text-layout engine you may have other responsibilities that + HarfBuzz will not help you with. For example: + + + + + HarfBuzz won't help you with bidirectionality. If you want to + lay out text that includes a mix of Hebrew and English, you + will need to ensure that each buffer provided to HarfBuzz + has all of its characters in the same order and that the + directionality of the buffer is set correctly. This may mean + segmenting the text before it is placed into HarfBuzz buffers. In + other words, the user will hit the keys in the following + sequence: + + + A B C [space] ג ב א [space] D E F + + + but will expect to see in the output: + + + ABC אבג DEF + + + This reordering is called bidi processing + ("bidi" is short for bidirectional), and there's an + algorithm as an annex to the Unicode Standard which tells you how + to process a string of mixed directionality. + Before sending your string to HarfBuzz, you may need to apply the + bidi algorithm to it. Libraries such as ICU and fribidi can do this for you. + + + + + HarfBuzz won't help you with text that contains different font + properties. For instance, if you have the string "a + huge breakfast", and you expect + "huge" to be italic, then you will need to send three + strings to HarfBuzz: a, in your Roman font; + huge using your italic font; and + breakfast using your Roman font again. + + + Similarly, if you change the font, font size, script, + language, or direction within your string, then you will + need to shape each run independently and output them + independently. HarfBuzz expects to shape a run of characters + that all share the same properties. + + + + + HarfBuzz won't help you with line breaking, hyphenation, or + justification. As mentioned above, HarfBuzz lays out the string + along a single line of, notionally, + infinite length. If you want to find out where the potential + word, sentence and line break points are in your text, you + could use the ICU library's break iterator functions. + + + HarfBuzz can tell you how wide a shaped piece of text is, which is + useful input to a justification algorithm, but it knows nothing + about paragraphs, lines or line lengths. Nor will it adjust the + space between words to fit them proportionally into a line. + + + + + As a layout-engine implementor, HarfBuzz will help you with the + interface between your text and your font, and that's something + that you'll need—what you then do with the glyphs that your font + returns is up to you. + +
+ +
+ Why is it called HarfBuzz? + + HarfBuzz began its life as text-shaping code within the FreeType + project (and you will see references to the FreeType authors + within the source code copyright declarations), but was then + extracted out to its own project. This project is maintained by + Behdad Esfahbod, who named it HarfBuzz. Originally, it was a + shaping engine for OpenType fonts—"HarfBuzz" is + the Persian for "open type". + +
+
diff --git a/libs/harfbuzz/docs/version.xml.in b/libs/harfbuzz/docs/version.xml.in new file mode 100644 index 00000000..de213c2d --- /dev/null +++ b/libs/harfbuzz/docs/version.xml.in @@ -0,0 +1 @@ +@HB_VERSION@ diff --git a/libs/harfbuzz/git.mk b/libs/harfbuzz/git.mk new file mode 100644 index 00000000..cd52db1e --- /dev/null +++ b/libs/harfbuzz/git.mk @@ -0,0 +1,401 @@ +# git.mk, a small Makefile to autogenerate .gitignore files +# for autotools-based projects. +# +# Copyright 2009, Red Hat, Inc. +# Copyright 2010,2011,2012,2013 Behdad Esfahbod +# Written by Behdad Esfahbod +# +# Copying and distribution of this file, with or without modification, +# is permitted in any medium without royalty provided the copyright +# notice and this notice are preserved. +# +# The latest version of this file can be downloaded from: +GIT_MK_URL = https://raw.githubusercontent.com/behdad/git.mk/master/git.mk +# +# Bugs, etc, should be reported upstream at: +# https://github.com/behdad/git.mk +# +# To use in your project, import this file in your git repo's toplevel, +# then do "make -f git.mk". This modifies all Makefile.am files in +# your project to -include git.mk. Remember to add that line to new +# Makefile.am files you create in your project, or just rerun the +# "make -f git.mk". +# +# This enables automatic .gitignore generation. If you need to ignore +# more files, add them to the GITIGNOREFILES variable in your Makefile.am. +# But think twice before doing that. If a file has to be in .gitignore, +# chances are very high that it's a generated file and should be in one +# of MOSTLYCLEANFILES, CLEANFILES, DISTCLEANFILES, or MAINTAINERCLEANFILES. +# +# The only case that you need to manually add a file to GITIGNOREFILES is +# when remove files in one of mostlyclean-local, clean-local, distclean-local, +# or maintainer-clean-local make targets. +# +# Note that for files like editor backup, etc, there are better places to +# ignore them. See "man gitignore". +# +# If "make maintainer-clean" removes the files but they are not recognized +# by this script (that is, if "git status" shows untracked files still), send +# me the output of "git status" as well as your Makefile.am and Makefile for +# the directories involved and I'll diagnose. +# +# For a list of toplevel files that should be in MAINTAINERCLEANFILES, see +# Makefile.am.sample in the git.mk git repo. +# +# Don't EXTRA_DIST this file. It is supposed to only live in git clones, +# not tarballs. It serves no useful purpose in tarballs and clutters the +# build dir. +# +# This file knows how to handle autoconf, automake, libtool, gtk-doc, +# gnome-doc-utils, yelp.m4, mallard, intltool, gsettings, dejagnu, appdata, +# appstream, hotdoc. +# +# This makefile provides the following targets: +# +# - all: "make all" will build all gitignore files. +# - gitignore: makes all gitignore files in the current dir and subdirs. +# - .gitignore: make gitignore file for the current dir. +# - gitignore-recurse: makes all gitignore files in the subdirs. +# +# KNOWN ISSUES: +# +# - Recursive configure doesn't work as $(top_srcdir)/git.mk inside the +# submodule doesn't find us. If you have configure.{in,ac} files in +# subdirs, add a proxy git.mk file in those dirs that simply does: +# "include $(top_srcdir)/../git.mk". Add more ..'s to your taste. +# And add those files to git. See vte/gnome-pty-helper/git.mk for +# example. +# + + + +############################################################################### +# Variables user modules may want to add to toplevel MAINTAINERCLEANFILES: +############################################################################### + +# +# Most autotools-using modules should be fine including this variable in their +# toplevel MAINTAINERCLEANFILES: +GITIGNORE_MAINTAINERCLEANFILES_TOPLEVEL = \ + $(srcdir)/aclocal.m4 \ + $(srcdir)/autoscan.log \ + $(srcdir)/configure.scan \ + `AUX_DIR=$(srcdir)/$$(cd $(top_srcdir); $(AUTOCONF) --trace 'AC_CONFIG_AUX_DIR:$$1' ./configure.ac); \ + test "x$$AUX_DIR" = "x$(srcdir)/" && AUX_DIR=$(srcdir); \ + for x in \ + ar-lib \ + compile \ + config.guess \ + config.rpath \ + config.sub \ + depcomp \ + install-sh \ + ltmain.sh \ + missing \ + mkinstalldirs \ + test-driver \ + ylwrap \ + ; do echo "$$AUX_DIR/$$x"; done` \ + `cd $(top_srcdir); $(AUTOCONF) --trace 'AC_CONFIG_HEADERS:$$1' ./configure.ac | \ + head -n 1 | while read f; do echo "$(srcdir)/$$f.in"; done` +# +# All modules should also be fine including the following variable, which +# removes automake-generated Makefile.in files: +GITIGNORE_MAINTAINERCLEANFILES_MAKEFILE_IN = \ + `cd $(top_srcdir); $(AUTOCONF) --trace 'AC_CONFIG_FILES:$$1' ./configure.ac | \ + while read f; do \ + case $$f in Makefile|*/Makefile) \ + test -f "$(srcdir)/$$f.am" && echo "$(srcdir)/$$f.in";; esac; \ + done` +# +# Modules that use libtool and use AC_CONFIG_MACRO_DIR() may also include this, +# though it's harmless to include regardless. +GITIGNORE_MAINTAINERCLEANFILES_M4_LIBTOOL = \ + `MACRO_DIR=$(srcdir)/$$(cd $(top_srcdir); $(AUTOCONF) --trace 'AC_CONFIG_MACRO_DIR:$$1' ./configure.ac); \ + if test "x$$MACRO_DIR" != "x$(srcdir)/"; then \ + for x in \ + libtool.m4 \ + ltoptions.m4 \ + ltsugar.m4 \ + ltversion.m4 \ + lt~obsolete.m4 \ + ; do echo "$$MACRO_DIR/$$x"; done; \ + fi` +# +# Modules that use gettext and use AC_CONFIG_MACRO_DIR() may also include this, +# though it's harmless to include regardless. +GITIGNORE_MAINTAINERCLEANFILES_M4_GETTEXT = \ + `MACRO_DIR=$(srcdir)/$$(cd $(top_srcdir); $(AUTOCONF) --trace 'AC_CONFIG_MACRO_DIR:$$1' ./configure.ac); \ + if test "x$$MACRO_DIR" != "x$(srcdir)/"; then \ + for x in \ + codeset.m4 \ + extern-inline.m4 \ + fcntl-o.m4 \ + gettext.m4 \ + glibc2.m4 \ + glibc21.m4 \ + iconv.m4 \ + intdiv0.m4 \ + intl.m4 \ + intldir.m4 \ + intlmacosx.m4 \ + intmax.m4 \ + inttypes-pri.m4 \ + inttypes_h.m4 \ + lcmessage.m4 \ + lib-ld.m4 \ + lib-link.m4 \ + lib-prefix.m4 \ + lock.m4 \ + longlong.m4 \ + nls.m4 \ + po.m4 \ + printf-posix.m4 \ + progtest.m4 \ + size_max.m4 \ + stdint_h.m4 \ + threadlib.m4 \ + uintmax_t.m4 \ + visibility.m4 \ + wchar_t.m4 \ + wint_t.m4 \ + xsize.m4 \ + ; do echo "$$MACRO_DIR/$$x"; done; \ + fi` + + + +############################################################################### +# Default rule is to install ourselves in all Makefile.am files: +############################################################################### + +git-all: git-mk-install + +git-mk-install: + @echo "Installing git makefile" + @any_failed=; \ + find "`test -z "$(top_srcdir)" && echo . || echo "$(top_srcdir)"`" -name Makefile.am | while read x; do \ + if grep 'include .*/git.mk' $$x >/dev/null; then \ + echo "$$x already includes git.mk"; \ + else \ + failed=; \ + echo "Updating $$x"; \ + { cat $$x; \ + echo ''; \ + echo '-include $$(top_srcdir)/git.mk'; \ + } > $$x.tmp || failed=1; \ + if test x$$failed = x; then \ + mv $$x.tmp $$x || failed=1; \ + fi; \ + if test x$$failed = x; then : else \ + echo "Failed updating $$x"; >&2 \ + any_failed=1; \ + fi; \ + fi; done; test -z "$$any_failed" + +git-mk-update: + wget $(GIT_MK_URL) -O $(top_srcdir)/git.mk + +.PHONY: git-all git-mk-install git-mk-update + + + +############################################################################### +# Actual .gitignore generation: +############################################################################### + +$(srcdir)/.gitignore: Makefile.am $(top_srcdir)/git.mk $(top_srcdir)/configure.ac + @echo "git.mk: Generating $@" + @{ \ + if test "x$(DOC_MODULE)" = x -o "x$(DOC_MAIN_SGML_FILE)" = x; then :; else \ + for x in \ + $(DOC_MODULE)-decl-list.txt \ + $(DOC_MODULE)-decl.txt \ + tmpl/$(DOC_MODULE)-unused.sgml \ + "tmpl/*.bak" \ + $(REPORT_FILES) \ + $(DOC_MODULE).pdf \ + xml html \ + ; do echo "/$$x"; done; \ + FLAVOR=$$(cd $(top_srcdir); $(AUTOCONF) --trace 'GTK_DOC_CHECK:$$2' ./configure.ac); \ + case $$FLAVOR in *no-tmpl*) echo /tmpl;; esac; \ + if echo "$(SCAN_OPTIONS)" | grep -q "\-\-rebuild-types"; then \ + echo "/$(DOC_MODULE).types"; \ + fi; \ + if echo "$(SCAN_OPTIONS)" | grep -q "\-\-rebuild-sections"; then \ + echo "/$(DOC_MODULE)-sections.txt"; \ + fi; \ + if test "$(abs_srcdir)" != "$(abs_builddir)" ; then \ + for x in \ + $(SETUP_FILES) \ + $(DOC_MODULE).types \ + ; do echo "/$$x"; done; \ + fi; \ + fi; \ + if test "x$(DOC_MODULE)$(DOC_ID)" = x -o "x$(DOC_LINGUAS)" = x; then :; else \ + for lc in $(DOC_LINGUAS); do \ + for x in \ + $(if $(DOC_MODULE),$(DOC_MODULE).xml) \ + $(DOC_PAGES) \ + $(DOC_INCLUDES) \ + ; do echo "/$$lc/$$x"; done; \ + done; \ + for x in \ + $(_DOC_OMF_ALL) \ + $(_DOC_DSK_ALL) \ + $(_DOC_HTML_ALL) \ + $(_DOC_MOFILES) \ + $(DOC_H_FILE) \ + "*/.xml2po.mo" \ + "*/*.omf.out" \ + ; do echo /$$x; done; \ + fi; \ + if test "x$(HOTDOC)" = x; then :; else \ + $(foreach project, $(HOTDOC_PROJECTS),echo "/$(call HOTDOC_TARGET,$(project))"; \ + echo "/$(shell $(call HOTDOC_PROJECT_COMMAND,$(project)) --get-conf-path output)" ; \ + echo "/$(shell $(call HOTDOC_PROJECT_COMMAND,$(project)) --get-private-folder)" ; \ + ) \ + for x in \ + .hotdoc.d \ + ; do echo "/$$x"; done; \ + fi; \ + if test "x$(HELP_ID)" = x -o "x$(HELP_LINGUAS)" = x; then :; else \ + for lc in $(HELP_LINGUAS); do \ + for x in \ + $(HELP_FILES) \ + "$$lc.stamp" \ + "$$lc.mo" \ + ; do echo "/$$lc/$$x"; done; \ + done; \ + fi; \ + if test "x$(gsettings_SCHEMAS)" = x; then :; else \ + for x in \ + $(gsettings_SCHEMAS:.xml=.valid) \ + $(gsettings__enum_file) \ + ; do echo "/$$x"; done; \ + fi; \ + if test "x$(appdata_XML)" = x; then :; else \ + for x in \ + $(appdata_XML:.xml=.valid) \ + ; do echo "/$$x"; done; \ + fi; \ + if test "x$(appstream_XML)" = x; then :; else \ + for x in \ + $(appstream_XML:.xml=.valid) \ + ; do echo "/$$x"; done; \ + fi; \ + if test -f $(srcdir)/po/Makefile.in.in; then \ + for x in \ + ABOUT-NLS \ + po/Makefile.in.in \ + po/Makefile.in.in~ \ + po/Makefile.in \ + po/Makefile \ + po/Makevars.template \ + po/POTFILES \ + po/Rules-quot \ + po/stamp-it \ + po/stamp-po \ + po/.intltool-merge-cache \ + "po/*.gmo" \ + "po/*.header" \ + "po/*.mo" \ + "po/*.sed" \ + "po/*.sin" \ + po/$(GETTEXT_PACKAGE).pot \ + intltool-extract.in \ + intltool-merge.in \ + intltool-update.in \ + ; do echo "/$$x"; done; \ + fi; \ + if test -f $(srcdir)/configure; then \ + for x in \ + autom4te.cache \ + configure \ + config.h \ + stamp-h1 \ + libtool \ + config.lt \ + ; do echo "/$$x"; done; \ + fi; \ + if test "x$(DEJATOOL)" = x; then :; else \ + for x in \ + $(DEJATOOL) \ + ; do echo "/$$x.sum"; echo "/$$x.log"; done; \ + echo /site.exp; \ + fi; \ + if test "x$(am__dirstamp)" = x; then :; else \ + echo "$(am__dirstamp)"; \ + fi; \ + if test "x$(findstring libtool,$(LTCOMPILE))" = x -a "x$(findstring libtool,$(LTCXXCOMPILE))" = x -a "x$(GTKDOC_RUN)" = x; then :; else \ + for x in \ + "*.lo" \ + ".libs" "_libs" \ + ; do echo "$$x"; done; \ + fi; \ + for x in \ + .gitignore \ + $(GITIGNOREFILES) \ + $(CLEANFILES) \ + $(PROGRAMS) $(check_PROGRAMS) $(EXTRA_PROGRAMS) \ + $(LIBRARIES) $(check_LIBRARIES) $(EXTRA_LIBRARIES) \ + $(LTLIBRARIES) $(check_LTLIBRARIES) $(EXTRA_LTLIBRARIES) \ + so_locations \ + $(MOSTLYCLEANFILES) \ + $(TEST_LOGS) \ + $(TEST_LOGS:.log=.trs) \ + $(TEST_SUITE_LOG) \ + $(TESTS:=.test) \ + "*.gcda" \ + "*.gcno" \ + $(DISTCLEANFILES) \ + $(am__CONFIG_DISTCLEAN_FILES) \ + $(CONFIG_CLEAN_FILES) \ + TAGS ID GTAGS GRTAGS GSYMS GPATH tags \ + "*.tab.c" \ + $(MAINTAINERCLEANFILES) \ + $(BUILT_SOURCES) \ + $(patsubst %.vala,%.c,$(filter %.vala,$(SOURCES))) \ + $(filter %_vala.stamp,$(DIST_COMMON)) \ + $(filter %.vapi,$(DIST_COMMON)) \ + $(filter $(addprefix %,$(notdir $(patsubst %.vapi,%.h,$(filter %.vapi,$(DIST_COMMON))))),$(DIST_COMMON)) \ + Makefile \ + Makefile.in \ + "*.orig" \ + "*.rej" \ + "*.bak" \ + "*~" \ + ".*.sw[nop]" \ + ".dirstamp" \ + ; do echo "/$$x"; done; \ + for x in \ + "*.$(OBJEXT)" \ + $(DEPDIR) \ + ; do echo "$$x"; done; \ + } | \ + sed "s@^/`echo "$(srcdir)" | sed 's/\(.\)/[\1]/g'`/@/@" | \ + sed 's@/[.]/@/@g' | \ + LC_ALL=C sort | uniq > .gitignore.tmp && \ + (mv .gitignore.tmp $@ || (echo "WARNING: Cannot create $@ file; skipping"; \ + $(RM) .gitignore.tmp)); + +all: $(srcdir)/.gitignore gitignore-recurse-maybe +gitignore: $(srcdir)/.gitignore gitignore-recurse + +gitignore-recurse-maybe: + @for subdir in $(DIST_SUBDIRS); do \ + case " $(SUBDIRS) " in \ + *" $$subdir "*) :;; \ + *) test "$$subdir" = . -o -e "$$subdir/.git" || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) gitignore || echo "Skipping $$subdir");; \ + esac; \ + done +gitignore-recurse: + @for subdir in $(DIST_SUBDIRS); do \ + test "$$subdir" = . -o -e "$$subdir/.git" || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) gitignore || echo "Skipping $$subdir"); \ + done + +maintainer-clean: gitignore-clean +gitignore-clean: + -rm -f $(srcdir)/.gitignore + +.PHONY: gitignore-clean gitignore gitignore-recurse gitignore-recurse-maybe diff --git a/libs/harfbuzz/harfbuzz.doap b/libs/harfbuzz/harfbuzz.doap new file mode 100644 index 00000000..2a5c0e62 --- /dev/null +++ b/libs/harfbuzz/harfbuzz.doap @@ -0,0 +1,24 @@ + + + harfbuzz + Text shaping library + + + + + + + + + Behdad Esfahbod + + + + diff --git a/libs/harfbuzz/m4/ax_check_link_flag.m4 b/libs/harfbuzz/m4/ax_check_link_flag.m4 new file mode 100644 index 00000000..819409a2 --- /dev/null +++ b/libs/harfbuzz/m4/ax_check_link_flag.m4 @@ -0,0 +1,74 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_check_link_flag.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_CHECK_LINK_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS], [INPUT]) +# +# DESCRIPTION +# +# Check whether the given FLAG works with the linker or gives an error. +# (Warnings, however, are ignored) +# +# ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on +# success/failure. +# +# If EXTRA-FLAGS is defined, it is added to the linker's default flags +# when the check is done. The check is thus made with the flags: "LDFLAGS +# EXTRA-FLAGS FLAG". This can for example be used to force the linker to +# issue an error when a bad flag is given. +# +# INPUT gives an alternative input source to AC_LINK_IFELSE. +# +# NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this +# macro in sync with AX_CHECK_{PREPROC,COMPILE}_FLAG. +# +# LICENSE +# +# Copyright (c) 2008 Guido U. Draheim +# Copyright (c) 2011 Maarten Bosmans +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation, either version 3 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +# Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program. If not, see . +# +# As a special exception, the respective Autoconf Macro's copyright owner +# gives unlimited permission to copy, distribute and modify the configure +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Archive. When you make and distribute a +# modified version of the Autoconf Macro, you may extend this special +# exception to the GPL to apply to your modified version as well. + +#serial 5 + +AC_DEFUN([AX_CHECK_LINK_FLAG], +[AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_IF +AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_ldflags_$4_$1])dnl +AC_CACHE_CHECK([whether the linker accepts $1], CACHEVAR, [ + ax_check_save_flags=$LDFLAGS + LDFLAGS="$LDFLAGS $4 $1" + AC_LINK_IFELSE([m4_default([$5],[AC_LANG_PROGRAM()])], + [AS_VAR_SET(CACHEVAR,[yes])], + [AS_VAR_SET(CACHEVAR,[no])]) + LDFLAGS=$ax_check_save_flags]) +AS_VAR_IF(CACHEVAR,yes, + [m4_default([$2], :)], + [m4_default([$3], :)]) +AS_VAR_POPDEF([CACHEVAR])dnl +])dnl AX_CHECK_LINK_FLAGS diff --git a/libs/harfbuzz/m4/ax_code_coverage.m4 b/libs/harfbuzz/m4/ax_code_coverage.m4 new file mode 100644 index 00000000..6484f033 --- /dev/null +++ b/libs/harfbuzz/m4/ax_code_coverage.m4 @@ -0,0 +1,264 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_code_coverage.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_CODE_COVERAGE() +# +# DESCRIPTION +# +# Defines CODE_COVERAGE_CPPFLAGS, CODE_COVERAGE_CFLAGS, +# CODE_COVERAGE_CXXFLAGS and CODE_COVERAGE_LIBS which should be included +# in the CPPFLAGS, CFLAGS CXXFLAGS and LIBS/LIBADD variables of every +# build target (program or library) which should be built with code +# coverage support. Also defines CODE_COVERAGE_RULES which should be +# substituted in your Makefile; and $enable_code_coverage which can be +# used in subsequent configure output. CODE_COVERAGE_ENABLED is defined +# and substituted, and corresponds to the value of the +# --enable-code-coverage option, which defaults to being disabled. +# +# Test also for gcov program and create GCOV variable that could be +# substituted. +# +# Note that all optimization flags in CFLAGS must be disabled when code +# coverage is enabled. +# +# Usage example: +# +# configure.ac: +# +# AX_CODE_COVERAGE +# +# Makefile.am: +# +# @CODE_COVERAGE_RULES@ +# my_program_LIBS = ... $(CODE_COVERAGE_LIBS) ... +# my_program_CPPFLAGS = ... $(CODE_COVERAGE_CPPFLAGS) ... +# my_program_CFLAGS = ... $(CODE_COVERAGE_CFLAGS) ... +# my_program_CXXFLAGS = ... $(CODE_COVERAGE_CXXFLAGS) ... +# +# This results in a "check-code-coverage" rule being added to any +# Makefile.am which includes "@CODE_COVERAGE_RULES@" (assuming the module +# has been configured with --enable-code-coverage). Running `make +# check-code-coverage` in that directory will run the module's test suite +# (`make check`) and build a code coverage report detailing the code which +# was touched, then print the URI for the report. +# +# In earlier versions of this macro, CODE_COVERAGE_LDFLAGS was defined +# instead of CODE_COVERAGE_LIBS. They are both still defined, but use of +# CODE_COVERAGE_LIBS is preferred for clarity; CODE_COVERAGE_LDFLAGS is +# deprecated. They have the same value. +# +# This code was derived from Makefile.decl in GLib, originally licenced +# under LGPLv2.1+. +# +# LICENSE +# +# Copyright (c) 2012, 2016 Philip Withnall +# Copyright (c) 2012 Xan Lopez +# Copyright (c) 2012 Christian Persch +# Copyright (c) 2012 Paolo Borelli +# Copyright (c) 2012 Dan Winship +# Copyright (c) 2015 Bastien ROUCARIES +# +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or (at +# your option) any later version. +# +# This library is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser +# General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . + +#serial 25 + +AC_DEFUN([AX_CODE_COVERAGE],[ + dnl Check for --enable-code-coverage + AC_REQUIRE([AC_PROG_SED]) + + # allow to override gcov location + AC_ARG_WITH([gcov], + [AS_HELP_STRING([--with-gcov[=GCOV]], [use given GCOV for coverage (GCOV=gcov).])], + [_AX_CODE_COVERAGE_GCOV_PROG_WITH=$with_gcov], + [_AX_CODE_COVERAGE_GCOV_PROG_WITH=gcov]) + + AC_MSG_CHECKING([whether to build with code coverage support]) + AC_ARG_ENABLE([code-coverage], + AS_HELP_STRING([--enable-code-coverage], + [Whether to enable code coverage support]),, + enable_code_coverage=no) + + AM_CONDITIONAL([CODE_COVERAGE_ENABLED], [test x$enable_code_coverage = xyes]) + AC_SUBST([CODE_COVERAGE_ENABLED], [$enable_code_coverage]) + AC_MSG_RESULT($enable_code_coverage) + + AS_IF([ test "$enable_code_coverage" = "yes" ], [ + # check for gcov + AC_CHECK_TOOL([GCOV], + [$_AX_CODE_COVERAGE_GCOV_PROG_WITH], + [:]) + AS_IF([test "X$GCOV" = "X:"], + [AC_MSG_ERROR([gcov is needed to do coverage])]) + AC_SUBST([GCOV]) + + dnl Check if gcc is being used + AS_IF([ test "$GCC" = "no" ], [ + AC_MSG_ERROR([not compiling with gcc, which is required for gcov code coverage]) + ]) + + AC_CHECK_PROG([LCOV], [lcov], [lcov]) + AC_CHECK_PROG([GENHTML], [genhtml], [genhtml]) + + AS_IF([ test -z "$LCOV" ], [ + AC_MSG_ERROR([To enable code coverage reporting you must have lcov installed]) + ]) + + AS_IF([ test -z "$GENHTML" ], [ + AC_MSG_ERROR([Could not find genhtml from the lcov package]) + ]) + + dnl Build the code coverage flags + dnl Define CODE_COVERAGE_LDFLAGS for backwards compatibility + CODE_COVERAGE_CPPFLAGS="-DNDEBUG" + CODE_COVERAGE_CFLAGS="-O0 -g -fprofile-arcs -ftest-coverage" + CODE_COVERAGE_CXXFLAGS="-O0 -g -fprofile-arcs -ftest-coverage" + CODE_COVERAGE_LIBS="-lgcov" + CODE_COVERAGE_LDFLAGS="$CODE_COVERAGE_LIBS" + + AC_SUBST([CODE_COVERAGE_CPPFLAGS]) + AC_SUBST([CODE_COVERAGE_CFLAGS]) + AC_SUBST([CODE_COVERAGE_CXXFLAGS]) + AC_SUBST([CODE_COVERAGE_LIBS]) + AC_SUBST([CODE_COVERAGE_LDFLAGS]) + + [CODE_COVERAGE_RULES_CHECK=' + -$(A''M_V_at)$(MAKE) $(AM_MAKEFLAGS) -k check + $(A''M_V_at)$(MAKE) $(AM_MAKEFLAGS) code-coverage-capture +'] + [CODE_COVERAGE_RULES_CAPTURE=' + $(code_coverage_v_lcov_cap)$(LCOV) $(code_coverage_quiet) $(addprefix --directory ,$(CODE_COVERAGE_DIRECTORY)) --capture --output-file "$(CODE_COVERAGE_OUTPUT_FILE).tmp" --test-name "$(call code_coverage_sanitize,$(PACKAGE_NAME)-$(PACKAGE_VERSION))" --no-checksum --compat-libtool $(CODE_COVERAGE_LCOV_SHOPTS) $(CODE_COVERAGE_LCOV_OPTIONS) + $(code_coverage_v_lcov_ign)$(LCOV) $(code_coverage_quiet) $(addprefix --directory ,$(CODE_COVERAGE_DIRECTORY)) --remove "$(CODE_COVERAGE_OUTPUT_FILE).tmp" "/tmp/*" $(CODE_COVERAGE_IGNORE_PATTERN) --output-file "$(CODE_COVERAGE_OUTPUT_FILE)" $(CODE_COVERAGE_LCOV_SHOPTS) $(CODE_COVERAGE_LCOV_RMOPTS) + -@rm -f $(CODE_COVERAGE_OUTPUT_FILE).tmp + $(code_coverage_v_genhtml)LANG=C $(GENHTML) $(code_coverage_quiet) $(addprefix --prefix ,$(CODE_COVERAGE_DIRECTORY)) --output-directory "$(CODE_COVERAGE_OUTPUT_DIRECTORY)" --title "$(PACKAGE_NAME)-$(PACKAGE_VERSION) Code Coverage" --legend --show-details "$(CODE_COVERAGE_OUTPUT_FILE)" $(CODE_COVERAGE_GENHTML_OPTIONS) + @echo "file://$(abs_builddir)/$(CODE_COVERAGE_OUTPUT_DIRECTORY)/index.html" +'] + [CODE_COVERAGE_RULES_CLEAN=' +clean: code-coverage-clean +distclean: code-coverage-clean +code-coverage-clean: + -$(LCOV) --directory $(top_builddir) -z + -rm -rf $(CODE_COVERAGE_OUTPUT_FILE) $(CODE_COVERAGE_OUTPUT_FILE).tmp $(CODE_COVERAGE_OUTPUT_DIRECTORY) + -find . \( -name "*.gcda" -o -name "*.gcno" -o -name "*.gcov" \) -delete +'] + ], [ + [CODE_COVERAGE_RULES_CHECK=' + @echo "Need to reconfigure with --enable-code-coverage" +'] + CODE_COVERAGE_RULES_CAPTURE="$CODE_COVERAGE_RULES_CHECK" + CODE_COVERAGE_RULES_CLEAN='' + ]) + +[CODE_COVERAGE_RULES=' +# Code coverage +# +# Optional: +# - CODE_COVERAGE_DIRECTORY: Top-level directory for code coverage reporting. +# Multiple directories may be specified, separated by whitespace. +# (Default: $(top_builddir)) +# - CODE_COVERAGE_OUTPUT_FILE: Filename and path for the .info file generated +# by lcov for code coverage. (Default: +# $(PACKAGE_NAME)-$(PACKAGE_VERSION)-coverage.info) +# - CODE_COVERAGE_OUTPUT_DIRECTORY: Directory for generated code coverage +# reports to be created. (Default: +# $(PACKAGE_NAME)-$(PACKAGE_VERSION)-coverage) +# - CODE_COVERAGE_BRANCH_COVERAGE: Set to 1 to enforce branch coverage, +# set to 0 to disable it and leave empty to stay with the default. +# (Default: empty) +# - CODE_COVERAGE_LCOV_SHOPTS_DEFAULT: Extra options shared between both lcov +# instances. (Default: based on $CODE_COVERAGE_BRANCH_COVERAGE) +# - CODE_COVERAGE_LCOV_SHOPTS: Extra options to shared between both lcov +# instances. (Default: $CODE_COVERAGE_LCOV_SHOPTS_DEFAULT) +# - CODE_COVERAGE_LCOV_OPTIONS_GCOVPATH: --gcov-tool pathtogcov +# - CODE_COVERAGE_LCOV_OPTIONS_DEFAULT: Extra options to pass to the +# collecting lcov instance. (Default: $CODE_COVERAGE_LCOV_OPTIONS_GCOVPATH) +# - CODE_COVERAGE_LCOV_OPTIONS: Extra options to pass to the collecting lcov +# instance. (Default: $CODE_COVERAGE_LCOV_OPTIONS_DEFAULT) +# - CODE_COVERAGE_LCOV_RMOPTS_DEFAULT: Extra options to pass to the filtering +# lcov instance. (Default: empty) +# - CODE_COVERAGE_LCOV_RMOPTS: Extra options to pass to the filtering lcov +# instance. (Default: $CODE_COVERAGE_LCOV_RMOPTS_DEFAULT) +# - CODE_COVERAGE_GENHTML_OPTIONS_DEFAULT: Extra options to pass to the +# genhtml instance. (Default: based on $CODE_COVERAGE_BRANCH_COVERAGE) +# - CODE_COVERAGE_GENHTML_OPTIONS: Extra options to pass to the genhtml +# instance. (Default: $CODE_COVERAGE_GENHTML_OPTIONS_DEFAULT) +# - CODE_COVERAGE_IGNORE_PATTERN: Extra glob pattern of files to ignore +# +# The generated report will be titled using the $(PACKAGE_NAME) and +# $(PACKAGE_VERSION). In order to add the current git hash to the title, +# use the git-version-gen script, available online. + +# Optional variables +CODE_COVERAGE_DIRECTORY ?= $(top_builddir) +CODE_COVERAGE_OUTPUT_FILE ?= $(PACKAGE_NAME)-$(PACKAGE_VERSION)-coverage.info +CODE_COVERAGE_OUTPUT_DIRECTORY ?= $(PACKAGE_NAME)-$(PACKAGE_VERSION)-coverage +CODE_COVERAGE_BRANCH_COVERAGE ?= +CODE_COVERAGE_LCOV_SHOPTS_DEFAULT ?= $(if $(CODE_COVERAGE_BRANCH_COVERAGE),\ +--rc lcov_branch_coverage=$(CODE_COVERAGE_BRANCH_COVERAGE)) +CODE_COVERAGE_LCOV_SHOPTS ?= $(CODE_COVERAGE_LCOV_SHOPTS_DEFAULT) +CODE_COVERAGE_LCOV_OPTIONS_GCOVPATH ?= --gcov-tool "$(GCOV)" +CODE_COVERAGE_LCOV_OPTIONS_DEFAULT ?= $(CODE_COVERAGE_LCOV_OPTIONS_GCOVPATH) +CODE_COVERAGE_LCOV_OPTIONS ?= $(CODE_COVERAGE_LCOV_OPTIONS_DEFAULT) +CODE_COVERAGE_LCOV_RMOPTS_DEFAULT ?= +CODE_COVERAGE_LCOV_RMOPTS ?= $(CODE_COVERAGE_LCOV_RMOPTS_DEFAULT) +CODE_COVERAGE_GENHTML_OPTIONS_DEFAULT ?=\ +$(if $(CODE_COVERAGE_BRANCH_COVERAGE),\ +--rc genhtml_branch_coverage=$(CODE_COVERAGE_BRANCH_COVERAGE)) +CODE_COVERAGE_GENHTML_OPTIONS ?= $(CODE_COVERAGE_GENHTML_OPTIONS_DEFAULT) +CODE_COVERAGE_IGNORE_PATTERN ?= + +GITIGNOREFILES ?= +GITIGNOREFILES += $(CODE_COVERAGE_OUTPUT_FILE) $(CODE_COVERAGE_OUTPUT_DIRECTORY) + +code_coverage_v_lcov_cap = $(code_coverage_v_lcov_cap_$(V)) +code_coverage_v_lcov_cap_ = $(code_coverage_v_lcov_cap_$(AM_DEFAULT_VERBOSITY)) +code_coverage_v_lcov_cap_0 = @echo " LCOV --capture"\ + $(CODE_COVERAGE_OUTPUT_FILE); +code_coverage_v_lcov_ign = $(code_coverage_v_lcov_ign_$(V)) +code_coverage_v_lcov_ign_ = $(code_coverage_v_lcov_ign_$(AM_DEFAULT_VERBOSITY)) +code_coverage_v_lcov_ign_0 = @echo " LCOV --remove /tmp/*"\ + $(CODE_COVERAGE_IGNORE_PATTERN); +code_coverage_v_genhtml = $(code_coverage_v_genhtml_$(V)) +code_coverage_v_genhtml_ = $(code_coverage_v_genhtml_$(AM_DEFAULT_VERBOSITY)) +code_coverage_v_genhtml_0 = @echo " GEN " $(CODE_COVERAGE_OUTPUT_DIRECTORY); +code_coverage_quiet = $(code_coverage_quiet_$(V)) +code_coverage_quiet_ = $(code_coverage_quiet_$(AM_DEFAULT_VERBOSITY)) +code_coverage_quiet_0 = --quiet + +# sanitizes the test-name: replaces with underscores: dashes and dots +code_coverage_sanitize = $(subst -,_,$(subst .,_,$(1))) + +# Use recursive makes in order to ignore errors during check +check-code-coverage:'"$CODE_COVERAGE_RULES_CHECK"' + +# Capture code coverage data +code-coverage-capture: code-coverage-capture-hook'"$CODE_COVERAGE_RULES_CAPTURE"' + +# Hook rule executed before code-coverage-capture, overridable by the user +code-coverage-capture-hook: + +'"$CODE_COVERAGE_RULES_CLEAN"' + +A''M_DISTCHECK_CONFIGURE_FLAGS ?= +A''M_DISTCHECK_CONFIGURE_FLAGS += --disable-code-coverage + +.PHONY: check-code-coverage code-coverage-capture code-coverage-capture-hook code-coverage-clean +'] + + AC_SUBST([CODE_COVERAGE_RULES]) + m4_ifdef([_AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE([CODE_COVERAGE_RULES])]) +]) diff --git a/libs/harfbuzz/m4/ax_cxx_compile_stdcxx.m4 b/libs/harfbuzz/m4/ax_cxx_compile_stdcxx.m4 new file mode 100644 index 00000000..8b6df5a5 --- /dev/null +++ b/libs/harfbuzz/m4/ax_cxx_compile_stdcxx.m4 @@ -0,0 +1,982 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_CXX_COMPILE_STDCXX(VERSION, [ext|noext], [mandatory|optional]) +# +# DESCRIPTION +# +# Check for baseline language coverage in the compiler for the specified +# version of the C++ standard. If necessary, add switches to CXX and +# CXXCPP to enable support. VERSION may be '11' (for the C++11 standard) +# or '14' (for the C++14 standard). +# +# The second argument, if specified, indicates whether you insist on an +# extended mode (e.g. -std=gnu++11) or a strict conformance mode (e.g. +# -std=c++11). If neither is specified, you get whatever works, with +# preference for an extended mode. +# +# The third argument, if specified 'mandatory' or if left unspecified, +# indicates that baseline support for the specified C++ standard is +# required and that the macro should error out if no mode with that +# support is found. If specified 'optional', then configuration proceeds +# regardless, after defining HAVE_CXX${VERSION} if and only if a +# supporting mode is found. +# +# LICENSE +# +# Copyright (c) 2008 Benjamin Kosnik +# Copyright (c) 2012 Zack Weinberg +# Copyright (c) 2013 Roy Stogner +# Copyright (c) 2014, 2015 Google Inc.; contributed by Alexey Sokolov +# Copyright (c) 2015 Paul Norman +# Copyright (c) 2015 Moritz Klammler +# Copyright (c) 2016 Krzesimir Nowak +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 7 + +dnl This macro is based on the code from the AX_CXX_COMPILE_STDCXX_11 macro +dnl (serial version number 13). + +AX_REQUIRE_DEFINED([AC_MSG_WARN]) +AC_DEFUN([AX_CXX_COMPILE_STDCXX], [dnl + m4_if([$1], [11], [ax_cxx_compile_alternatives="11 0x"], + [$1], [14], [ax_cxx_compile_alternatives="14 1y"], + [$1], [17], [ax_cxx_compile_alternatives="17 1z"], + [m4_fatal([invalid first argument `$1' to AX_CXX_COMPILE_STDCXX])])dnl + m4_if([$2], [], [], + [$2], [ext], [], + [$2], [noext], [], + [m4_fatal([invalid second argument `$2' to AX_CXX_COMPILE_STDCXX])])dnl + m4_if([$3], [], [ax_cxx_compile_cxx$1_required=true], + [$3], [mandatory], [ax_cxx_compile_cxx$1_required=true], + [$3], [optional], [ax_cxx_compile_cxx$1_required=false], + [m4_fatal([invalid third argument `$3' to AX_CXX_COMPILE_STDCXX])]) + AC_LANG_PUSH([C++])dnl + ac_success=no + AC_CACHE_CHECK(whether $CXX supports C++$1 features by default, + ax_cv_cxx_compile_cxx$1, + [AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])], + [ax_cv_cxx_compile_cxx$1=yes], + [ax_cv_cxx_compile_cxx$1=no])]) + if test x$ax_cv_cxx_compile_cxx$1 = xyes; then + ac_success=yes + fi + + m4_if([$2], [noext], [], [dnl + if test x$ac_success = xno; then + for alternative in ${ax_cxx_compile_alternatives}; do + switch="-std=gnu++${alternative}" + cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch]) + AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch, + $cachevar, + [ac_save_CXX="$CXX" + CXX="$CXX $switch" + AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])], + [eval $cachevar=yes], + [eval $cachevar=no]) + CXX="$ac_save_CXX"]) + if eval test x\$$cachevar = xyes; then + CXX="$CXX $switch" + if test -n "$CXXCPP" ; then + CXXCPP="$CXXCPP $switch" + fi + ac_success=yes + break + fi + done + fi]) + + m4_if([$2], [ext], [], [dnl + if test x$ac_success = xno; then + dnl HP's aCC needs +std=c++11 according to: + dnl http://h21007.www2.hp.com/portal/download/files/unprot/aCxx/PDF_Release_Notes/769149-001.pdf + dnl Cray's crayCC needs "-h std=c++11" + for alternative in ${ax_cxx_compile_alternatives}; do + for switch in -std=c++${alternative} +std=c++${alternative} "-h std=c++${alternative}"; do + cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch]) + AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch, + $cachevar, + [ac_save_CXX="$CXX" + CXX="$CXX $switch" + AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])], + [eval $cachevar=yes], + [eval $cachevar=no]) + CXX="$ac_save_CXX"]) + if eval test x\$$cachevar = xyes; then + CXX="$CXX $switch" + if test -n "$CXXCPP" ; then + CXXCPP="$CXXCPP $switch" + fi + ac_success=yes + break + fi + done + if test x$ac_success = xyes; then + break + fi + done + fi]) + AC_LANG_POP([C++]) + if test x$ax_cxx_compile_cxx$1_required = xtrue; then + if test x$ac_success = xno; then + AC_MSG_ERROR([*** A compiler with support for C++$1 language features is required.]) + fi + fi + if test x$ac_success = xno; then + HAVE_CXX$1=0 + AC_MSG_NOTICE([No compiler with C++$1 support was found]) + else + HAVE_CXX$1=1 + AC_DEFINE(HAVE_CXX$1,1, + [define if the compiler supports basic C++$1 syntax]) + fi + AC_SUBST(HAVE_CXX$1) + m4_if([$1], [17], [AC_MSG_WARN([C++17 is not yet standardized, so the checks may change in incompatible ways anytime])]) +]) + + +dnl Test body for checking C++11 support + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_11], + _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 +) + + +dnl Test body for checking C++14 support + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_14], + _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 + _AX_CXX_COMPILE_STDCXX_testbody_new_in_14 +) + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_17], + _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 + _AX_CXX_COMPILE_STDCXX_testbody_new_in_14 + _AX_CXX_COMPILE_STDCXX_testbody_new_in_17 +) + +dnl Tests for new features in C++11 + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_11], [[ + +// If the compiler admits that it is not ready for C++11, why torture it? +// Hopefully, this will speed up the test. + +#ifndef __cplusplus + +#error "This is not a C++ compiler" + +#elif __cplusplus < 201103L + +#error "This is not a C++11 compiler" + +#else + +namespace cxx11 +{ + + namespace test_static_assert + { + + template + struct check + { + static_assert(sizeof(int) <= sizeof(T), "not big enough"); + }; + + } + + namespace test_final_override + { + + struct Base + { + virtual void f() {} + }; + + struct Derived : public Base + { + virtual void f() override {} + }; + + } + + namespace test_double_right_angle_brackets + { + + template < typename T > + struct check {}; + + typedef check single_type; + typedef check> double_type; + typedef check>> triple_type; + typedef check>>> quadruple_type; + + } + + namespace test_decltype + { + + int + f() + { + int a = 1; + decltype(a) b = 2; + return a + b; + } + + } + + namespace test_type_deduction + { + + template < typename T1, typename T2 > + struct is_same + { + static const bool value = false; + }; + + template < typename T > + struct is_same + { + static const bool value = true; + }; + + template < typename T1, typename T2 > + auto + add(T1 a1, T2 a2) -> decltype(a1 + a2) + { + return a1 + a2; + } + + int + test(const int c, volatile int v) + { + static_assert(is_same::value == true, ""); + static_assert(is_same::value == false, ""); + static_assert(is_same::value == false, ""); + auto ac = c; + auto av = v; + auto sumi = ac + av + 'x'; + auto sumf = ac + av + 1.0; + static_assert(is_same::value == true, ""); + static_assert(is_same::value == true, ""); + static_assert(is_same::value == true, ""); + static_assert(is_same::value == false, ""); + static_assert(is_same::value == true, ""); + return (sumf > 0.0) ? sumi : add(c, v); + } + + } + + namespace test_noexcept + { + + int f() { return 0; } + int g() noexcept { return 0; } + + static_assert(noexcept(f()) == false, ""); + static_assert(noexcept(g()) == true, ""); + + } + + namespace test_constexpr + { + + template < typename CharT > + unsigned long constexpr + strlen_c_r(const CharT *const s, const unsigned long acc) noexcept + { + return *s ? strlen_c_r(s + 1, acc + 1) : acc; + } + + template < typename CharT > + unsigned long constexpr + strlen_c(const CharT *const s) noexcept + { + return strlen_c_r(s, 0UL); + } + + static_assert(strlen_c("") == 0UL, ""); + static_assert(strlen_c("1") == 1UL, ""); + static_assert(strlen_c("example") == 7UL, ""); + static_assert(strlen_c("another\0example") == 7UL, ""); + + } + + namespace test_rvalue_references + { + + template < int N > + struct answer + { + static constexpr int value = N; + }; + + answer<1> f(int&) { return answer<1>(); } + answer<2> f(const int&) { return answer<2>(); } + answer<3> f(int&&) { return answer<3>(); } + + void + test() + { + int i = 0; + const int c = 0; + static_assert(decltype(f(i))::value == 1, ""); + static_assert(decltype(f(c))::value == 2, ""); + static_assert(decltype(f(0))::value == 3, ""); + } + + } + + namespace test_uniform_initialization + { + + struct test + { + static const int zero {}; + static const int one {1}; + }; + + static_assert(test::zero == 0, ""); + static_assert(test::one == 1, ""); + + } + + namespace test_lambdas + { + + void + test1() + { + auto lambda1 = [](){}; + auto lambda2 = lambda1; + lambda1(); + lambda2(); + } + + int + test2() + { + auto a = [](int i, int j){ return i + j; }(1, 2); + auto b = []() -> int { return '0'; }(); + auto c = [=](){ return a + b; }(); + auto d = [&](){ return c; }(); + auto e = [a, &b](int x) mutable { + const auto identity = [](int y){ return y; }; + for (auto i = 0; i < a; ++i) + a += b--; + return x + identity(a + b); + }(0); + return a + b + c + d + e; + } + + int + test3() + { + const auto nullary = [](){ return 0; }; + const auto unary = [](int x){ return x; }; + using nullary_t = decltype(nullary); + using unary_t = decltype(unary); + const auto higher1st = [](nullary_t f){ return f(); }; + const auto higher2nd = [unary](nullary_t f1){ + return [unary, f1](unary_t f2){ return f2(unary(f1())); }; + }; + return higher1st(nullary) + higher2nd(nullary)(unary); + } + + } + + namespace test_variadic_templates + { + + template + struct sum; + + template + struct sum + { + static constexpr auto value = N0 + sum::value; + }; + + template <> + struct sum<> + { + static constexpr auto value = 0; + }; + + static_assert(sum<>::value == 0, ""); + static_assert(sum<1>::value == 1, ""); + static_assert(sum<23>::value == 23, ""); + static_assert(sum<1, 2>::value == 3, ""); + static_assert(sum<5, 5, 11>::value == 21, ""); + static_assert(sum<2, 3, 5, 7, 11, 13>::value == 41, ""); + + } + + // https://stackoverflow.com/questions/13728184/template-aliases-and-sfinae + // Clang 3.1 fails with headers of libstd++ 4.8.3 when using std::function + // because of this. + namespace test_template_alias_sfinae + { + + struct foo {}; + + template + using member = typename T::member_type; + + template + void func(...) {} + + template + void func(member*) {} + + void test(); + + void test() { func(0); } + + } + +} // namespace cxx11 + +#endif // __cplusplus >= 201103L + +]]) + + +dnl Tests for new features in C++14 + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_14], [[ + +// If the compiler admits that it is not ready for C++14, why torture it? +// Hopefully, this will speed up the test. + +#ifndef __cplusplus + +#error "This is not a C++ compiler" + +#elif __cplusplus < 201402L + +#error "This is not a C++14 compiler" + +#else + +namespace cxx14 +{ + + namespace test_polymorphic_lambdas + { + + int + test() + { + const auto lambda = [](auto&&... args){ + const auto istiny = [](auto x){ + return (sizeof(x) == 1UL) ? 1 : 0; + }; + const int aretiny[] = { istiny(args)... }; + return aretiny[0]; + }; + return lambda(1, 1L, 1.0f, '1'); + } + + } + + namespace test_binary_literals + { + + constexpr auto ivii = 0b0000000000101010; + static_assert(ivii == 42, "wrong value"); + + } + + namespace test_generalized_constexpr + { + + template < typename CharT > + constexpr unsigned long + strlen_c(const CharT *const s) noexcept + { + auto length = 0UL; + for (auto p = s; *p; ++p) + ++length; + return length; + } + + static_assert(strlen_c("") == 0UL, ""); + static_assert(strlen_c("x") == 1UL, ""); + static_assert(strlen_c("test") == 4UL, ""); + static_assert(strlen_c("another\0test") == 7UL, ""); + + } + + namespace test_lambda_init_capture + { + + int + test() + { + auto x = 0; + const auto lambda1 = [a = x](int b){ return a + b; }; + const auto lambda2 = [a = lambda1(x)](){ return a; }; + return lambda2(); + } + + } + + namespace test_digit_separators + { + + constexpr auto ten_million = 100'000'000; + static_assert(ten_million == 100000000, ""); + + } + + namespace test_return_type_deduction + { + + auto f(int& x) { return x; } + decltype(auto) g(int& x) { return x; } + + template < typename T1, typename T2 > + struct is_same + { + static constexpr auto value = false; + }; + + template < typename T > + struct is_same + { + static constexpr auto value = true; + }; + + int + test() + { + auto x = 0; + static_assert(is_same::value, ""); + static_assert(is_same::value, ""); + return x; + } + + } + +} // namespace cxx14 + +#endif // __cplusplus >= 201402L + +]]) + + +dnl Tests for new features in C++17 + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_17], [[ + +// If the compiler admits that it is not ready for C++17, why torture it? +// Hopefully, this will speed up the test. + +#ifndef __cplusplus + +#error "This is not a C++ compiler" + +#elif __cplusplus <= 201402L + +#error "This is not a C++17 compiler" + +#else + +#if defined(__clang__) + #define REALLY_CLANG +#else + #if defined(__GNUC__) + #define REALLY_GCC + #endif +#endif + +#include +#include +#include + +namespace cxx17 +{ + +#if !defined(REALLY_CLANG) + namespace test_constexpr_lambdas + { + + // TODO: test it with clang++ from git + + constexpr int foo = [](){return 42;}(); + + } +#endif // !defined(REALLY_CLANG) + + namespace test::nested_namespace::definitions + { + + } + + namespace test_fold_expression + { + + template + int multiply(Args... args) + { + return (args * ... * 1); + } + + template + bool all(Args... args) + { + return (args && ...); + } + + } + + namespace test_extended_static_assert + { + + static_assert (true); + + } + + namespace test_auto_brace_init_list + { + + auto foo = {5}; + auto bar {5}; + + static_assert(std::is_same, decltype(foo)>::value); + static_assert(std::is_same::value); + } + + namespace test_typename_in_template_template_parameter + { + + template typename X> struct D; + + } + + namespace test_fallthrough_nodiscard_maybe_unused_attributes + { + + int f1() + { + return 42; + } + + [[nodiscard]] int f2() + { + [[maybe_unused]] auto unused = f1(); + + switch (f1()) + { + case 17: + f1(); + [[fallthrough]]; + case 42: + f1(); + } + return f1(); + } + + } + + namespace test_extended_aggregate_initialization + { + + struct base1 + { + int b1, b2 = 42; + }; + + struct base2 + { + base2() { + b3 = 42; + } + int b3; + }; + + struct derived : base1, base2 + { + int d; + }; + + derived d1 {{1, 2}, {}, 4}; // full initialization + derived d2 {{}, {}, 4}; // value-initialized bases + + } + + namespace test_general_range_based_for_loop + { + + struct iter + { + int i; + + int& operator* () + { + return i; + } + + const int& operator* () const + { + return i; + } + + iter& operator++() + { + ++i; + return *this; + } + }; + + struct sentinel + { + int i; + }; + + bool operator== (const iter& i, const sentinel& s) + { + return i.i == s.i; + } + + bool operator!= (const iter& i, const sentinel& s) + { + return !(i == s); + } + + struct range + { + iter begin() const + { + return {0}; + } + + sentinel end() const + { + return {5}; + } + }; + + void f() + { + range r {}; + + for (auto i : r) + { + [[maybe_unused]] auto v = i; + } + } + + } + + namespace test_lambda_capture_asterisk_this_by_value + { + + struct t + { + int i; + int foo() + { + return [*this]() + { + return i; + }(); + } + }; + + } + + namespace test_enum_class_construction + { + + enum class byte : unsigned char + {}; + + byte foo {42}; + + } + + namespace test_constexpr_if + { + + template + int f () + { + if constexpr(cond) + { + return 13; + } + else + { + return 42; + } + } + + } + + namespace test_selection_statement_with_initializer + { + + int f() + { + return 13; + } + + int f2() + { + if (auto i = f(); i > 0) + { + return 3; + } + + switch (auto i = f(); i + 4) + { + case 17: + return 2; + + default: + return 1; + } + } + + } + +#if !defined(REALLY_CLANG) + namespace test_template_argument_deduction_for_class_templates + { + + // TODO: test it with clang++ from git + + template + struct pair + { + pair (T1 p1, T2 p2) + : m1 {p1}, + m2 {p2} + {} + + T1 m1; + T2 m2; + }; + + void f() + { + [[maybe_unused]] auto p = pair{13, 42u}; + } + + } +#endif // !defined(REALLY_CLANG) + + namespace test_non_type_auto_template_parameters + { + + template + struct B + {}; + + B<5> b1; + B<'a'> b2; + + } + +#if !defined(REALLY_CLANG) + namespace test_structured_bindings + { + + // TODO: test it with clang++ from git + + int arr[2] = { 1, 2 }; + std::pair pr = { 1, 2 }; + + auto f1() -> int(&)[2] + { + return arr; + } + + auto f2() -> std::pair& + { + return pr; + } + + struct S + { + int x1 : 2; + volatile double y1; + }; + + S f3() + { + return {}; + } + + auto [ x1, y1 ] = f1(); + auto& [ xr1, yr1 ] = f1(); + auto [ x2, y2 ] = f2(); + auto& [ xr2, yr2 ] = f2(); + const auto [ x3, y3 ] = f3(); + + } +#endif // !defined(REALLY_CLANG) + +#if !defined(REALLY_CLANG) + namespace test_exception_spec_type_system + { + + // TODO: test it with clang++ from git + + struct Good {}; + struct Bad {}; + + void g1() noexcept; + void g2(); + + template + Bad + f(T*, T*); + + template + Good + f(T1*, T2*); + + static_assert (std::is_same_v); + + } +#endif // !defined(REALLY_CLANG) + + namespace test_inline_variables + { + + template void f(T) + {} + + template inline T g(T) + { + return T{}; + } + + template<> inline void f<>(int) + {} + + template<> int g<>(int) + { + return 5; + } + + } + +} // namespace cxx17 + +#endif // __cplusplus <= 201402L + +]]) diff --git a/libs/harfbuzz/m4/ax_pthread.m4 b/libs/harfbuzz/m4/ax_pthread.m4 new file mode 100644 index 00000000..5fbf9fe0 --- /dev/null +++ b/libs/harfbuzz/m4/ax_pthread.m4 @@ -0,0 +1,485 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_pthread.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]) +# +# DESCRIPTION +# +# This macro figures out how to build C programs using POSIX threads. It +# sets the PTHREAD_LIBS output variable to the threads library and linker +# flags, and the PTHREAD_CFLAGS output variable to any special C compiler +# flags that are needed. (The user can also force certain compiler +# flags/libs to be tested by setting these environment variables.) +# +# Also sets PTHREAD_CC to any special C compiler that is needed for +# multi-threaded programs (defaults to the value of CC otherwise). (This +# is necessary on AIX to use the special cc_r compiler alias.) +# +# NOTE: You are assumed to not only compile your program with these flags, +# but also to link with them as well. For example, you might link with +# $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS +# +# If you are only building threaded programs, you may wish to use these +# variables in your default LIBS, CFLAGS, and CC: +# +# LIBS="$PTHREAD_LIBS $LIBS" +# CFLAGS="$CFLAGS $PTHREAD_CFLAGS" +# CC="$PTHREAD_CC" +# +# In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant +# has a nonstandard name, this macro defines PTHREAD_CREATE_JOINABLE to +# that name (e.g. PTHREAD_CREATE_UNDETACHED on AIX). +# +# Also HAVE_PTHREAD_PRIO_INHERIT is defined if pthread is found and the +# PTHREAD_PRIO_INHERIT symbol is defined when compiling with +# PTHREAD_CFLAGS. +# +# ACTION-IF-FOUND is a list of shell commands to run if a threads library +# is found, and ACTION-IF-NOT-FOUND is a list of commands to run it if it +# is not found. If ACTION-IF-FOUND is not specified, the default action +# will define HAVE_PTHREAD. +# +# Please let the authors know if this macro fails on any platform, or if +# you have any other suggestions or comments. This macro was based on work +# by SGJ on autoconf scripts for FFTW (http://www.fftw.org/) (with help +# from M. Frigo), as well as ac_pthread and hb_pthread macros posted by +# Alejandro Forero Cuervo to the autoconf macro repository. We are also +# grateful for the helpful feedback of numerous users. +# +# Updated for Autoconf 2.68 by Daniel Richard G. +# +# LICENSE +# +# Copyright (c) 2008 Steven G. Johnson +# Copyright (c) 2011 Daniel Richard G. +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation, either version 3 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +# Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program. If not, see . +# +# As a special exception, the respective Autoconf Macro's copyright owner +# gives unlimited permission to copy, distribute and modify the configure +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Archive. When you make and distribute a +# modified version of the Autoconf Macro, you may extend this special +# exception to the GPL to apply to your modified version as well. + +#serial 24 + +AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD]) +AC_DEFUN([AX_PTHREAD], [ +AC_REQUIRE([AC_CANONICAL_HOST]) +AC_REQUIRE([AC_PROG_CC]) +AC_REQUIRE([AC_PROG_SED]) +AC_LANG_PUSH([C]) +ax_pthread_ok=no + +# We used to check for pthread.h first, but this fails if pthread.h +# requires special compiler flags (e.g. on Tru64 or Sequent). +# It gets checked for in the link test anyway. + +# First of all, check if the user has set any of the PTHREAD_LIBS, +# etcetera environment variables, and if threads linking works using +# them: +if test "x$PTHREAD_CFLAGS$PTHREAD_LIBS" != "x"; then + ax_pthread_save_CC="$CC" + ax_pthread_save_CFLAGS="$CFLAGS" + ax_pthread_save_LIBS="$LIBS" + AS_IF([test "x$PTHREAD_CC" != "x"], [CC="$PTHREAD_CC"]) + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + LIBS="$PTHREAD_LIBS $LIBS" + AC_MSG_CHECKING([for pthread_join using $CC $PTHREAD_CFLAGS $PTHREAD_LIBS]) + AC_LINK_IFELSE([AC_LANG_CALL([], [pthread_join])], [ax_pthread_ok=yes]) + AC_MSG_RESULT([$ax_pthread_ok]) + if test "x$ax_pthread_ok" = "xno"; then + PTHREAD_LIBS="" + PTHREAD_CFLAGS="" + fi + CC="$ax_pthread_save_CC" + CFLAGS="$ax_pthread_save_CFLAGS" + LIBS="$ax_pthread_save_LIBS" +fi + +# We must check for the threads library under a number of different +# names; the ordering is very important because some systems +# (e.g. DEC) have both -lpthread and -lpthreads, where one of the +# libraries is broken (non-POSIX). + +# Create a list of thread flags to try. Items starting with a "-" are +# C compiler flags, and other items are library names, except for "none" +# which indicates that we try without any flags at all, and "pthread-config" +# which is a program returning the flags for the Pth emulation library. + +ax_pthread_flags="pthreads none -Kthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config" + +# The ordering *is* (sometimes) important. Some notes on the +# individual items follow: + +# pthreads: AIX (must check this before -lpthread) +# none: in case threads are in libc; should be tried before -Kthread and +# other compiler flags to prevent continual compiler warnings +# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h) +# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads), Tru64 +# (Note: HP C rejects this with "bad form for `-t' option") +# -pthreads: Solaris/gcc (Note: HP C also rejects) +# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it +# doesn't hurt to check since this sometimes defines pthreads and +# -D_REENTRANT too), HP C (must be checked before -lpthread, which +# is present but should not be used directly; and before -mthreads, +# because the compiler interprets this as "-mt" + "-hreads") +# -mthreads: Mingw32/gcc, Lynx/gcc +# pthread: Linux, etcetera +# --thread-safe: KAI C++ +# pthread-config: use pthread-config program (for GNU Pth library) + +case $host_os in + + freebsd*) + + # -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able) + # lthread: LinuxThreads port on FreeBSD (also preferred to -pthread) + + ax_pthread_flags="-kthread lthread $ax_pthread_flags" + ;; + + hpux*) + + # From the cc(1) man page: "[-mt] Sets various -D flags to enable + # multi-threading and also sets -lpthread." + + ax_pthread_flags="-mt -pthread pthread $ax_pthread_flags" + ;; + + openedition*) + + # IBM z/OS requires a feature-test macro to be defined in order to + # enable POSIX threads at all, so give the user a hint if this is + # not set. (We don't define these ourselves, as they can affect + # other portions of the system API in unpredictable ways.) + + AC_EGREP_CPP([AX_PTHREAD_ZOS_MISSING], + [ +# if !defined(_OPEN_THREADS) && !defined(_UNIX03_THREADS) + AX_PTHREAD_ZOS_MISSING +# endif + ], + [AC_MSG_WARN([IBM z/OS requires -D_OPEN_THREADS or -D_UNIX03_THREADS to enable pthreads support.])]) + ;; + + solaris*) + + # On Solaris (at least, for some versions), libc contains stubbed + # (non-functional) versions of the pthreads routines, so link-based + # tests will erroneously succeed. (N.B.: The stubs are missing + # pthread_cleanup_push, or rather a function called by this macro, + # so we could check for that, but who knows whether they'll stub + # that too in a future libc.) So we'll check first for the + # standard Solaris way of linking pthreads (-mt -lpthread). + + ax_pthread_flags="-mt,pthread pthread $ax_pthread_flags" + ;; +esac + +# GCC generally uses -pthread, or -pthreads on some platforms (e.g. SPARC) + +AS_IF([test "x$GCC" = "xyes"], + [ax_pthread_flags="-pthread -pthreads $ax_pthread_flags"]) + +# The presence of a feature test macro requesting re-entrant function +# definitions is, on some systems, a strong hint that pthreads support is +# correctly enabled + +case $host_os in + darwin* | hpux* | linux* | osf* | solaris*) + ax_pthread_check_macro="_REENTRANT" + ;; + + aix*) + ax_pthread_check_macro="_THREAD_SAFE" + ;; + + *) + ax_pthread_check_macro="--" + ;; +esac +AS_IF([test "x$ax_pthread_check_macro" = "x--"], + [ax_pthread_check_cond=0], + [ax_pthread_check_cond="!defined($ax_pthread_check_macro)"]) + +# Are we compiling with Clang? + +AC_CACHE_CHECK([whether $CC is Clang], + [ax_cv_PTHREAD_CLANG], + [ax_cv_PTHREAD_CLANG=no + # Note that Autoconf sets GCC=yes for Clang as well as GCC + if test "x$GCC" = "xyes"; then + AC_EGREP_CPP([AX_PTHREAD_CC_IS_CLANG], + [/* Note: Clang 2.7 lacks __clang_[a-z]+__ */ +# if defined(__clang__) && defined(__llvm__) + AX_PTHREAD_CC_IS_CLANG +# endif + ], + [ax_cv_PTHREAD_CLANG=yes]) + fi + ]) +ax_pthread_clang="$ax_cv_PTHREAD_CLANG" + +ax_pthread_clang_warning=no + +# Clang needs special handling, because older versions handle the -pthread +# option in a rather... idiosyncratic way + +if test "x$ax_pthread_clang" = "xyes"; then + + # Clang takes -pthread; it has never supported any other flag + + # (Note 1: This will need to be revisited if a system that Clang + # supports has POSIX threads in a separate library. This tends not + # to be the way of modern systems, but it's conceivable.) + + # (Note 2: On some systems, notably Darwin, -pthread is not needed + # to get POSIX threads support; the API is always present and + # active. We could reasonably leave PTHREAD_CFLAGS empty. But + # -pthread does define _REENTRANT, and while the Darwin headers + # ignore this macro, third-party headers might not.) + + PTHREAD_CFLAGS="-pthread" + PTHREAD_LIBS= + + ax_pthread_ok=yes + + # However, older versions of Clang make a point of warning the user + # that, in an invocation where only linking and no compilation is + # taking place, the -pthread option has no effect ("argument unused + # during compilation"). They expect -pthread to be passed in only + # when source code is being compiled. + # + # Problem is, this is at odds with the way Automake and most other + # C build frameworks function, which is that the same flags used in + # compilation (CFLAGS) are also used in linking. Many systems + # supported by AX_PTHREAD require exactly this for POSIX threads + # support, and in fact it is often not straightforward to specify a + # flag that is used only in the compilation phase and not in + # linking. Such a scenario is extremely rare in practice. + # + # Even though use of the -pthread flag in linking would only print + # a warning, this can be a nuisance for well-run software projects + # that build with -Werror. So if the active version of Clang has + # this misfeature, we search for an option to squash it. + + AC_CACHE_CHECK([whether Clang needs flag to prevent "argument unused" warning when linking with -pthread], + [ax_cv_PTHREAD_CLANG_NO_WARN_FLAG], + [ax_cv_PTHREAD_CLANG_NO_WARN_FLAG=unknown + # Create an alternate version of $ac_link that compiles and + # links in two steps (.c -> .o, .o -> exe) instead of one + # (.c -> exe), because the warning occurs only in the second + # step + ax_pthread_save_ac_link="$ac_link" + ax_pthread_sed='s/conftest\.\$ac_ext/conftest.$ac_objext/g' + ax_pthread_link_step=`$as_echo "$ac_link" | sed "$ax_pthread_sed"` + ax_pthread_2step_ac_link="($ac_compile) && (echo ==== >&5) && ($ax_pthread_link_step)" + ax_pthread_save_CFLAGS="$CFLAGS" + for ax_pthread_try in '' -Qunused-arguments -Wno-unused-command-line-argument unknown; do + AS_IF([test "x$ax_pthread_try" = "xunknown"], [break]) + CFLAGS="-Werror -Wunknown-warning-option $ax_pthread_try -pthread $ax_pthread_save_CFLAGS" + ac_link="$ax_pthread_save_ac_link" + AC_LINK_IFELSE([AC_LANG_SOURCE([[int main(void){return 0;}]])], + [ac_link="$ax_pthread_2step_ac_link" + AC_LINK_IFELSE([AC_LANG_SOURCE([[int main(void){return 0;}]])], + [break]) + ]) + done + ac_link="$ax_pthread_save_ac_link" + CFLAGS="$ax_pthread_save_CFLAGS" + AS_IF([test "x$ax_pthread_try" = "x"], [ax_pthread_try=no]) + ax_cv_PTHREAD_CLANG_NO_WARN_FLAG="$ax_pthread_try" + ]) + + case "$ax_cv_PTHREAD_CLANG_NO_WARN_FLAG" in + no | unknown) ;; + *) PTHREAD_CFLAGS="$ax_cv_PTHREAD_CLANG_NO_WARN_FLAG $PTHREAD_CFLAGS" ;; + esac + +fi # $ax_pthread_clang = yes + +if test "x$ax_pthread_ok" = "xno"; then +for ax_pthread_try_flag in $ax_pthread_flags; do + + case $ax_pthread_try_flag in + none) + AC_MSG_CHECKING([whether pthreads work without any flags]) + ;; + + -mt,pthread) + AC_MSG_CHECKING([whether pthreads work with -mt -lpthread]) + PTHREAD_CFLAGS="-mt" + PTHREAD_LIBS="-lpthread" + ;; + + -*) + AC_MSG_CHECKING([whether pthreads work with $ax_pthread_try_flag]) + PTHREAD_CFLAGS="$ax_pthread_try_flag" + ;; + + pthread-config) + AC_CHECK_PROG([ax_pthread_config], [pthread-config], [yes], [no]) + AS_IF([test "x$ax_pthread_config" = "xno"], [continue]) + PTHREAD_CFLAGS="`pthread-config --cflags`" + PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`" + ;; + + *) + AC_MSG_CHECKING([for the pthreads library -l$ax_pthread_try_flag]) + PTHREAD_LIBS="-l$ax_pthread_try_flag" + ;; + esac + + ax_pthread_save_CFLAGS="$CFLAGS" + ax_pthread_save_LIBS="$LIBS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + LIBS="$PTHREAD_LIBS $LIBS" + + # Check for various functions. We must include pthread.h, + # since some functions may be macros. (On the Sequent, we + # need a special flag -Kthread to make this header compile.) + # We check for pthread_join because it is in -lpthread on IRIX + # while pthread_create is in libc. We check for pthread_attr_init + # due to DEC craziness with -lpthreads. We check for + # pthread_cleanup_push because it is one of the few pthread + # functions on Solaris that doesn't have a non-functional libc stub. + # We try pthread_create on general principles. + + AC_LINK_IFELSE([AC_LANG_PROGRAM([#include +# if $ax_pthread_check_cond +# error "$ax_pthread_check_macro must be defined" +# endif + static void routine(void *a) { a = 0; } + static void *start_routine(void *a) { return a; }], + [pthread_t th; pthread_attr_t attr; + pthread_create(&th, 0, start_routine, 0); + pthread_join(th, 0); + pthread_attr_init(&attr); + pthread_cleanup_push(routine, 0); + pthread_cleanup_pop(0) /* ; */])], + [ax_pthread_ok=yes], + []) + + CFLAGS="$ax_pthread_save_CFLAGS" + LIBS="$ax_pthread_save_LIBS" + + AC_MSG_RESULT([$ax_pthread_ok]) + AS_IF([test "x$ax_pthread_ok" = "xyes"], [break]) + + PTHREAD_LIBS="" + PTHREAD_CFLAGS="" +done +fi + +# Various other checks: +if test "x$ax_pthread_ok" = "xyes"; then + ax_pthread_save_CFLAGS="$CFLAGS" + ax_pthread_save_LIBS="$LIBS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + LIBS="$PTHREAD_LIBS $LIBS" + + # Detect AIX lossage: JOINABLE attribute is called UNDETACHED. + AC_CACHE_CHECK([for joinable pthread attribute], + [ax_cv_PTHREAD_JOINABLE_ATTR], + [ax_cv_PTHREAD_JOINABLE_ATTR=unknown + for ax_pthread_attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do + AC_LINK_IFELSE([AC_LANG_PROGRAM([#include ], + [int attr = $ax_pthread_attr; return attr /* ; */])], + [ax_cv_PTHREAD_JOINABLE_ATTR=$ax_pthread_attr; break], + []) + done + ]) + AS_IF([test "x$ax_cv_PTHREAD_JOINABLE_ATTR" != "xunknown" && \ + test "x$ax_cv_PTHREAD_JOINABLE_ATTR" != "xPTHREAD_CREATE_JOINABLE" && \ + test "x$ax_pthread_joinable_attr_defined" != "xyes"], + [AC_DEFINE_UNQUOTED([PTHREAD_CREATE_JOINABLE], + [$ax_cv_PTHREAD_JOINABLE_ATTR], + [Define to necessary symbol if this constant + uses a non-standard name on your system.]) + ax_pthread_joinable_attr_defined=yes + ]) + + AC_CACHE_CHECK([whether more special flags are required for pthreads], + [ax_cv_PTHREAD_SPECIAL_FLAGS], + [ax_cv_PTHREAD_SPECIAL_FLAGS=no + case $host_os in + solaris*) + ax_cv_PTHREAD_SPECIAL_FLAGS="-D_POSIX_PTHREAD_SEMANTICS" + ;; + esac + ]) + AS_IF([test "x$ax_cv_PTHREAD_SPECIAL_FLAGS" != "xno" && \ + test "x$ax_pthread_special_flags_added" != "xyes"], + [PTHREAD_CFLAGS="$ax_cv_PTHREAD_SPECIAL_FLAGS $PTHREAD_CFLAGS" + ax_pthread_special_flags_added=yes]) + + AC_CACHE_CHECK([for PTHREAD_PRIO_INHERIT], + [ax_cv_PTHREAD_PRIO_INHERIT], + [AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include ]], + [[int i = PTHREAD_PRIO_INHERIT;]])], + [ax_cv_PTHREAD_PRIO_INHERIT=yes], + [ax_cv_PTHREAD_PRIO_INHERIT=no]) + ]) + AS_IF([test "x$ax_cv_PTHREAD_PRIO_INHERIT" = "xyes" && \ + test "x$ax_pthread_prio_inherit_defined" != "xyes"], + [AC_DEFINE([HAVE_PTHREAD_PRIO_INHERIT], [1], [Have PTHREAD_PRIO_INHERIT.]) + ax_pthread_prio_inherit_defined=yes + ]) + + CFLAGS="$ax_pthread_save_CFLAGS" + LIBS="$ax_pthread_save_LIBS" + + # More AIX lossage: compile with *_r variant + if test "x$GCC" != "xyes"; then + case $host_os in + aix*) + AS_CASE(["x/$CC"], + [x*/c89|x*/c89_128|x*/c99|x*/c99_128|x*/cc|x*/cc128|x*/xlc|x*/xlc_v6|x*/xlc128|x*/xlc128_v6], + [#handle absolute path differently from PATH based program lookup + AS_CASE(["x$CC"], + [x/*], + [AS_IF([AS_EXECUTABLE_P([${CC}_r])],[PTHREAD_CC="${CC}_r"])], + [AC_CHECK_PROGS([PTHREAD_CC],[${CC}_r],[$CC])])]) + ;; + esac + fi +fi + +test -n "$PTHREAD_CC" || PTHREAD_CC="$CC" + +AC_SUBST([PTHREAD_LIBS]) +AC_SUBST([PTHREAD_CFLAGS]) +AC_SUBST([PTHREAD_CC]) + +# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: +if test "x$ax_pthread_ok" = "xyes"; then + ifelse([$1],,[AC_DEFINE([HAVE_PTHREAD],[1],[Define if you have POSIX threads libraries and header files.])],[$1]) + : +else + ax_pthread_ok=no + $2 +fi +AC_LANG_POP +])dnl AX_PTHREAD diff --git a/libs/harfbuzz/meson.build b/libs/harfbuzz/meson.build new file mode 100644 index 00000000..b80679d3 --- /dev/null +++ b/libs/harfbuzz/meson.build @@ -0,0 +1,394 @@ +project('harfbuzz', 'c', 'cpp', + meson_version: '>= 0.55.0', + version: '5.3.1', + default_options: [ + 'cpp_rtti=false', # Just to support msvc, we are passing -fno-exceptions also anyway + 'cpp_std=c++11', + 'wrap_mode=nofallback', # Use --wrap-mode=default to revert, https://github.com/harfbuzz/harfbuzz/pull/2548 + ], +) + +hb_version_arr = meson.project_version().split('.') +hb_version_major = hb_version_arr[0].to_int() +hb_version_minor = hb_version_arr[1].to_int() +hb_version_micro = hb_version_arr[2].to_int() + +# libtool versioning +hb_version_int = hb_version_major*10000 + hb_version_minor*100 + hb_version_micro +hb_libtool_version_info = '@0@:0:@0@'.format(hb_version_int) + +pkgmod = import('pkgconfig') +cpp = meson.get_compiler('cpp') +null_dep = dependency('', required: false) + +if cpp.get_argument_syntax() == 'msvc' + # Ignore several spurious warnings for things HarfBuzz does very commonly. + # If a warning is completely useless and spammy, use '/wdXXXX' to suppress it + # If a warning is harmless but hard to fix, use '/woXXXX' so it's shown once + # NOTE: Only add warnings here if you are sure they're spurious + msvc_args = [ + '/wd4018', # implicit signed/unsigned conversion + '/wd4146', # unary minus on unsigned (beware INT_MIN) + '/wd4244', # lossy type conversion (e.g. double -> int) + '/wd4305', # truncating type conversion (e.g. double -> float) + cpp.get_supported_arguments(['/utf-8']), # set the input encoding to utf-8 + ] + add_project_arguments(msvc_args, language: ['c', 'cpp']) + # Disable SAFESEH with MSVC for libs that use external deps that are built with MinGW + # noseh_link_args = ['/SAFESEH:NO'] + # disable exception handling + add_project_arguments(['/EHs-', '/EHc-'], language: 'cpp') +endif + +add_project_link_arguments(cpp.get_supported_link_arguments([ + '-Bsymbolic-functions' +]), language: 'c') + +add_project_arguments(cpp.get_supported_arguments([ + '-fno-exceptions', + '-fno-rtti', + '-fno-threadsafe-statics', + '-fvisibility-inlines-hidden', +]), language: 'cpp') + +if host_machine.cpu_family() == 'arm' and cpp.alignment('struct { char c; }') != 1 + if cpp.has_argument('-mstructure-size-boundary=8') + add_project_arguments('-mstructure-size-boundary=8', language: 'cpp') + endif +endif + +if host_machine.system() == 'windows' + add_project_arguments(cpp.get_supported_arguments([ + '-Wa,-mbig-obj' + ]), language : 'cpp') +endif + +check_headers = [ + ['unistd.h'], + ['sys/mman.h'], + ['stdbool.h'], + ['xlocale.h'], +] + +check_funcs = [ + ['atexit'], + ['mprotect'], + ['sysconf'], + ['getpagesize'], + ['mmap'], + ['isatty'], + ['uselocale'], + ['newlocale'], +] + +m_dep = cpp.find_library('m', required: false) + + +# Try pkgconfig name +freetype_dep = dependency('freetype2', required: false) +if not freetype_dep.found() + # Try cmake name + freetype_dep = dependency('freetype', required: false) +endif +if not freetype_dep.found() + # Subproject fallback, `allow_fallback: true` means the fallback will be + # tried even if the freetype option is set to `auto`. + freetype_dep = dependency('freetype2', + required: get_option('freetype'), + default_options: ['harfbuzz=disabled'], + allow_fallback: true) +endif + +glib_dep = dependency('glib-2.0', required: get_option('glib')) +gobject_dep = dependency('gobject-2.0', required: get_option('gobject')) +graphite2_dep = dependency('graphite2', required: get_option('graphite2')) +graphite_dep = dependency('graphite2', required: get_option('graphite')) + +# Try pkgconfig name +icu_dep = dependency('icu-uc', required: false) +if not icu_dep.found() + # Try cmake name + icu_dep = dependency('ICU', + required: false, + components: 'uc', + method: 'cmake') +endif +if not icu_dep.found() + # Subproject fallback if icu option is enabled + icu_dep = dependency('icu-uc', required: get_option('icu')) +endif + +if icu_dep.found() and icu_dep.type_name() == 'pkgconfig' + icu_defs = icu_dep.get_variable(pkgconfig: 'DEFS', default_value: '').split() + if icu_defs.length() > 0 + add_project_arguments(icu_defs, language: ['c', 'cpp']) + endif +endif + +cairo_dep = null_dep +cairo_ft_dep = null_dep +if not get_option('cairo').disabled() + cairo_dep = dependency('cairo', required: false) + cairo_ft_dep = dependency('cairo-ft', required: false) + + if (not cairo_dep.found() and + cpp.get_argument_syntax() == 'msvc' and + cpp.has_header('cairo.h')) + cairo_dep = cpp.find_library('cairo', required: false) + if cairo_dep.found() and cpp.has_function('cairo_ft_font_face_create_for_ft_face', + prefix: '#include ', + dependencies: cairo_dep) + cairo_ft_dep = cairo_dep + endif + endif + + if not cairo_dep.found() + # Note that we don't have harfbuzz -> cairo -> freetype2 -> harfbuzz fallback + # dependency cycle here because we have configured freetype2 above with + # harfbuzz support disabled, so when cairo will lookup freetype2 dependency + # it will be forced to use that one. + cairo_dep = dependency('cairo', required: get_option('cairo')) + cairo_ft_dep = dependency('cairo-ft', required: get_option('cairo')) + endif +endif + +chafa_dep = dependency('chafa', version: '>= 1.6.0', required: get_option('chafa')) + +conf = configuration_data() +incconfig = include_directories('.') + +add_project_arguments('-DHAVE_CONFIG_H', language: ['c', 'cpp']) + +warn_cflags = [ + '-Wno-non-virtual-dtor', +] + +cpp_args = cpp.get_supported_arguments(warn_cflags) + +if glib_dep.found() + conf.set('HAVE_GLIB', 1) +endif + +if gobject_dep.found() + conf.set('HAVE_GOBJECT', 1) +endif + +if cairo_dep.found() + conf.set('HAVE_CAIRO', 1) + if cairo_dep.type_name() == 'internal' + conf.set('HAVE_CAIRO_USER_FONT_FACE_SET_RENDER_COLOR_GLYPH_FUNC', 1) + else + check_funcs += [['cairo_user_font_face_set_render_color_glyph_func', {'deps': cairo_dep}]] + endif +endif + +if cairo_ft_dep.found() + conf.set('HAVE_CAIRO_FT', 1) +endif + +if chafa_dep.found() + conf.set('HAVE_CHAFA', 1) +endif + +if graphite2_dep.found() or graphite_dep.found() + conf.set('HAVE_GRAPHITE2', 1) +endif + +if icu_dep.found() + conf.set('HAVE_ICU', 1) +endif + +if get_option('icu_builtin') + conf.set('HAVE_ICU_BUILTIN', 1) +endif + +if get_option('experimental_api') + conf.set('HB_EXPERIMENTAL_API', 1) +endif + +if freetype_dep.found() + conf.set('HAVE_FREETYPE', 1) + check_freetype_funcs = [ + ['FT_Get_Var_Blend_Coordinates', {'deps': freetype_dep}], + ['FT_Set_Var_Blend_Coordinates', {'deps': freetype_dep}], + ['FT_Done_MM_Var', {'deps': freetype_dep}], + ['FT_Get_Transform', {'deps': freetype_dep}], + ] + + if freetype_dep.type_name() == 'internal' + foreach func: check_freetype_funcs + name = func[0] + conf.set('HAVE_@0@'.format(name.to_upper()), 1) + endforeach + else + check_funcs += check_freetype_funcs + endif +endif + +gdi_uniscribe_deps = [] +# GDI (Uniscribe) (Windows) +if host_machine.system() == 'windows' and not get_option('gdi').disabled() + if (get_option('directwrite').enabled() and + not (cpp.has_header('usp10.h') and cpp.has_header('windows.h'))) + error('GDI/Uniscribe was enabled explicitly, but required headers are missing.') + endif + + gdi_deps_found = true + foreach usplib : ['usp10', 'gdi32', 'rpcrt4'] + dep = cpp.find_library(usplib, required: get_option('gdi')) + gdi_deps_found = gdi_deps_found and dep.found() + gdi_uniscribe_deps += dep + endforeach + + if gdi_deps_found + conf.set('HAVE_UNISCRIBE', 1) + conf.set('HAVE_GDI', 1) + endif +endif + +# DirectWrite (Windows) +if host_machine.system() == 'windows' and not get_option('directwrite').disabled() + if get_option('directwrite').enabled() and not cpp.has_header('dwrite_1.h') + error('DirectWrite was enabled explicitly, but required header is missing.') + endif + + conf.set('HAVE_DIRECTWRITE', 1) +endif + +# CoreText (macOS) +coretext_deps = [] +if host_machine.system() == 'darwin' and not get_option('coretext').disabled() + app_services_dep = dependency('appleframeworks', modules: ['ApplicationServices'], required: false) + if cpp.has_type('CTFontRef', prefix: '#include ', dependencies: app_services_dep) + coretext_deps += [app_services_dep] + conf.set('HAVE_CORETEXT', 1) + # On iOS CoreText and CoreGraphics are stand-alone frameworks + # Check for a different symbol to avoid getting cached result + else + coretext_dep = dependency('appleframeworks', modules: ['CoreText'], required: false) + coregraphics_dep = dependency('appleframeworks', modules: ['CoreGraphics'], required: false) + corefoundation_dep = dependency('appleframeworks', modules: ['CoreFoundation'], required: false) + if cpp.has_type('CTRunRef', prefix: '#include ', dependencies: [coretext_dep, coregraphics_dep, corefoundation_dep]) + coretext_deps += [coretext_dep, coregraphics_dep, corefoundation_dep] + conf.set('HAVE_CORETEXT', 1) + elif get_option('coretext').enabled() + error('CoreText was enabled explicitly, but required headers or frameworks are missing.') + endif + endif +endif + +# threads +thread_dep = null_dep +if host_machine.system() != 'windows' + thread_dep = dependency('threads', required: false) + + if thread_dep.found() + conf.set('HAVE_PTHREAD', 1) + endif +endif + +conf.set_quoted('PACKAGE_NAME', 'HarfBuzz') +conf.set_quoted('PACKAGE_VERSION', meson.project_version()) + +foreach check : check_headers + name = check[0] + + if cpp.has_header(name) + conf.set('HAVE_@0@'.format(name.to_upper().underscorify()), 1) + endif +endforeach + +harfbuzz_extra_deps = [] +foreach check : check_funcs + name = check[0] + opts = check.get(1, {}) + link_withs = opts.get('link_with', []) + check_deps = opts.get('deps', []) + extra_deps = [] + found = true + + # First try without linking + found = cpp.has_function(name, dependencies: check_deps) + + if not found and link_withs.length() > 0 + found = true + + foreach link_with : link_withs + dep = cpp.find_library(link_with, required: false) + if dep.found() + extra_deps += dep + else + found = false + endif + endforeach + + if found + found = cpp.has_function(name, dependencies: check_deps + extra_deps) + endif + endif + + if found + harfbuzz_extra_deps += extra_deps + conf.set('HAVE_@0@'.format(name.to_upper()), 1) + endif +endforeach + +subdir('src') +subdir('util') + +if not get_option('tests').disabled() + subdir('test') +endif + +if not get_option('benchmark').disabled() + subdir('perf') +endif + +if not get_option('docs').disabled() + subdir('docs') +endif + +configure_file(output: 'config.h', configuration: conf) + +build_summary = { + 'Directories': + {'prefix': get_option('prefix'), + 'bindir': get_option('bindir'), + 'libdir': get_option('libdir'), + 'includedir': get_option('includedir'), + 'datadir': get_option('datadir'), + }, + 'Unicode callbacks (you want at least one)': + {'Builtin': true, + 'Glib': conf.get('HAVE_GLIB', 0) == 1, + 'ICU': conf.get('HAVE_ICU', 0) == 1, + }, + 'Font callbacks (the more the merrier)': + {'FreeType': conf.get('HAVE_FREETYPE', 0) == 1, + }, + 'Dependencies used for command-line utilities': + {'Cairo': conf.get('HAVE_CAIRO', 0) == 1, + 'Chafa': conf.get('HAVE_CHAFA', 0) == 1, + }, + 'Additional shapers': + {'Graphite2': conf.get('HAVE_GRAPHITE2', 0) == 1, + }, + 'Platform shapers (not normally needed)': + {'CoreText': conf.get('HAVE_CORETEXT', 0) == 1, + 'DirectWrite': conf.get('HAVE_DIRECTWRITE', 0) == 1, + 'GDI/Uniscribe': (conf.get('HAVE_GDI', 0) == 1) and (conf.get('HAVE_UNISCRIBE', 0) == 1), + }, + 'Other features': + {'Documentation': conf.get('HAVE_GTK_DOC', 0) == 1, + 'GObject bindings': conf.get('HAVE_GOBJECT', 0) == 1, + 'Introspection': conf.get('HAVE_INTROSPECTION', 0) == 1, + 'Experimental APIs': conf.get('HB_EXPERIMENTAL_API', 0) == 1, + }, + 'Testing': + {'Tests': get_option('tests').enabled(), + 'Benchmark': get_option('benchmark').enabled(), + }, +} +foreach section_title, section : build_summary + summary(section, bool_yn: true, section: section_title) +endforeach diff --git a/libs/harfbuzz/meson_options.txt b/libs/harfbuzz/meson_options.txt new file mode 100644 index 00000000..9ebba72c --- /dev/null +++ b/libs/harfbuzz/meson_options.txt @@ -0,0 +1,42 @@ +# HarfBuzz feature options +option('glib', type: 'feature', value: 'auto', + description: 'Enable GLib unicode functions') +option('gobject', type: 'feature', value: 'auto', + description: 'Enable GObject bindings') +option('cairo', type: 'feature', value: 'auto', + description: 'Use Cairo graphics library') +option('chafa', type: 'feature', value: 'auto', + description: 'Use Chafa terminal graphics library') +option('icu', type: 'feature', value: 'auto', + description: 'Enable ICU library unicode functions') +option('graphite', type: 'feature', value: 'disabled', + description: 'Deprecated use graphite2 option instead') +option('graphite2', type: 'feature', value: 'disabled', + description: 'Enable Graphite2 complementary shaper') +option('freetype', type: 'feature', value: 'auto', + description: 'Enable freetype interop helpers') +option('gdi', type: 'feature', value: 'disabled', + description: 'Enable GDI helpers and Uniscribe shaper backend (Windows only)') +option('directwrite', type: 'feature', value: 'disabled', + description: 'Enable DirectWrite shaper backend on Windows (experimental)') +option('coretext', type: 'feature', value: 'disabled', + description: 'Enable CoreText shaper backend on macOS') + +# Common feature options +option('tests', type: 'feature', value: 'enabled', yield: true, + description: 'Enable or disable unit tests') +option('introspection', type: 'feature', value: 'auto', yield: true, + description: 'Generate gobject-introspection bindings (.gir/.typelib files)') +option('docs', type: 'feature', value: 'auto', yield: true, + description: 'Generate documentation with gtk-doc') + +option('benchmark', type: 'feature', value: 'disabled', + description: 'Enable benchmark tests') +option('icu_builtin', type: 'boolean', value: false, + description: 'Don\'t separate ICU support as harfbuzz-icu module') +option('experimental_api', type: 'boolean', value: false, + description: 'Enable experimental APIs') +option('ragel_subproject', type: 'boolean', value: false, + description: 'Build Ragel subproject if no suitable version is found') +option('fuzzer_ldflags', type: 'string', + description: 'Extra LDFLAGS used during linking of fuzzing binaries') diff --git a/libs/harfbuzz/mingw-configure.sh b/libs/harfbuzz/mingw-configure.sh new file mode 100644 index 00000000..496cae29 --- /dev/null +++ b/libs/harfbuzz/mingw-configure.sh @@ -0,0 +1,30 @@ +#!/bin/sh + +case $1 in + i686 | x86_64) ;; + *) echo "Usage: $0 i686|x86_64" >&2; exit 1 ;; +esac + +target=$1-w64-mingw32 +shift + +exec "$(dirname "$0")"/configure \ + --build=`../config.guess` \ + --host=$target \ + --prefix=$HOME/.local/$target \ + CC= \ + CXX= \ + CPP= \ + LD= \ + CFLAGS="-static-libgcc" \ + CXXFLAGS="-O2 -static-libgcc -static-libstdc++" \ + CPPFLAGS="-I$HOME/.local/$target/include" \ + LDFLAGS=-L$HOME/.local/$target/lib \ + PKG_CONFIG_LIBDIR=$HOME/.local/$target/lib/pkgconfig:/usr/$target/sys-root/mingw/lib/pkgconfig/ \ + PKG_CONFIG_PATH=$HOME/.local/$target/share/pkgconfig:/usr/$target/sys-root/mingw/share/pkgconfig/ \ + PATH=$HOME/.local/$target/bin:/usr/$target/sys-root/mingw/bin:/usr/$target/bin:$PATH \ + --without-icu \ + --with-gdi \ + --with-uniscribe \ + --with-directwrite=auto \ + "$@" diff --git a/libs/harfbuzz/perf/Makefile.am b/libs/harfbuzz/perf/Makefile.am new file mode 100644 index 00000000..1e67b923 --- /dev/null +++ b/libs/harfbuzz/perf/Makefile.am @@ -0,0 +1,23 @@ +# Process this file with automake to produce Makefile.in + +NULL = +EXTRA_DIST = +SUBDIRS = + +EXTRA_DIST += \ + meson.build \ + benchmark-font.cc \ + benchmark-map.cc \ + benchmark-ot.cc \ + benchmark-set.cc \ + benchmark-shape.cc \ + benchmark-subset.cc \ + fonts \ + texts \ + $(NULL) + +# Convenience targets: +lib: + @$(MAKE) $(AM_MAKEFLAGS) -C $(top_builddir)/src lib + +-include $(top_srcdir)/git.mk diff --git a/libs/harfbuzz/perf/README.md b/libs/harfbuzz/perf/README.md new file mode 100644 index 00000000..91f49351 --- /dev/null +++ b/libs/harfbuzz/perf/README.md @@ -0,0 +1,54 @@ +# Building and Running + +Benchmarks are implemented using [Google Benchmark](https://github.com/google/benchmark). + +To build the benchmarks in this directory you need to set the benchmark +option while configuring the build with meson: + +``` +meson build -Dbenchmark=enabled --buildtype=release +``` +or: +``` +meson build -Dbenchmark=enabled --buildtype=debugoptimized +``` + + +Then build a specific benchmark binaries with ninja: +``` +ninja -Cbuild perf/benchmark-set +``` +or just build the whole project: +``` +ninja -Cbuild +``` + +Finally, to run one of the benchmarks: + +``` +./build/perf/benchmark-set +``` + +It's possible to filter the benchmarks being run and customize the output +via flags to the benchmark binary. See the +[Google Benchmark User Guide](https://github.com/google/benchmark/blob/main/docs/user_guide.md#user-guide) for more details. + +# Profiling + +Configure the build to include debug information for profiling: + +``` +CXXFLAGS="-fno-omit-frame-pointer" meson --reconfigure build -Dbenchmark=enabled --buildtype=debug +ninja -Cbuild +``` + +Then run the benchmark with perf: + +``` +perf record -g build/perf/benchmark-subset --benchmark_filter="BM_subset_codepoints/subset_notocjk/100000" --benchmark_repetitions=5 +``` +You probably want to filter to a specific benchmark of interest and set the number of repititions high enough to get a good sampling of profile data. + +Finally view the profile with: + +perf report diff --git a/libs/harfbuzz/perf/benchmark-font.cc b/libs/harfbuzz/perf/benchmark-font.cc new file mode 100644 index 00000000..98b6310d --- /dev/null +++ b/libs/harfbuzz/perf/benchmark-font.cc @@ -0,0 +1,245 @@ +#include "benchmark/benchmark.h" +#include +#include + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "hb.h" +#include "hb-ot.h" +#ifdef HAVE_FREETYPE +#include "hb-ft.h" +#endif + + +#define SUBSET_FONT_BASE_PATH "test/subset/data/fonts/" + +struct test_input_t +{ + bool is_variable; + const char *font_path; +} default_tests[] = +{ + {true , SUBSET_FONT_BASE_PATH "Roboto-Regular.ttf"}, + {false, SUBSET_FONT_BASE_PATH "SourceSansPro-Regular.otf"}, + {true , SUBSET_FONT_BASE_PATH "AdobeVFPrototype.otf"}, + {true , SUBSET_FONT_BASE_PATH "SourceSerifVariable-Roman.ttf"}, + {false, SUBSET_FONT_BASE_PATH "Comfortaa-Regular-new.ttf"}, + {false, SUBSET_FONT_BASE_PATH "NotoNastaliqUrdu-Regular.ttf"}, + {false, SUBSET_FONT_BASE_PATH "NotoSerifMyanmar-Regular.otf"}, +}; + +static test_input_t *tests = default_tests; +static unsigned num_tests = sizeof (default_tests) / sizeof (default_tests[0]); + +enum backend_t { HARFBUZZ, FREETYPE }; + +enum operation_t +{ + nominal_glyphs, + glyph_h_advances, + glyph_extents, + glyph_shape, +}; + +static void +_hb_move_to (hb_draw_funcs_t *, void *, hb_draw_state_t *, float, float, void *) {} + +static void +_hb_line_to (hb_draw_funcs_t *, void *, hb_draw_state_t *, float, float, void *) {} + +//static void +//_hb_quadratic_to (hb_draw_funcs_t *, void *, hb_draw_state_t *, float, float, float, float, void *) {} + +static void +_hb_cubic_to (hb_draw_funcs_t *, void *, hb_draw_state_t *, float, float, float, float, float, float, void *) {} + +static void +_hb_close_path (hb_draw_funcs_t *, void *, hb_draw_state_t *, void *) {} + +static hb_draw_funcs_t * +_draw_funcs_create (void) +{ + hb_draw_funcs_t *draw_funcs = hb_draw_funcs_create (); + hb_draw_funcs_set_move_to_func (draw_funcs, _hb_move_to, nullptr, nullptr); + hb_draw_funcs_set_line_to_func (draw_funcs, _hb_line_to, nullptr, nullptr); + //hb_draw_funcs_set_quadratic_to_func (draw_funcs, _hb_quadratic_to, nullptr, nullptr); + hb_draw_funcs_set_cubic_to_func (draw_funcs, _hb_cubic_to, nullptr, nullptr); + hb_draw_funcs_set_close_path_func (draw_funcs, _hb_close_path, nullptr, nullptr); + return draw_funcs; +} + +static void BM_Font (benchmark::State &state, + bool is_var, backend_t backend, operation_t operation, + const test_input_t &test_input) +{ + hb_font_t *font; + unsigned num_glyphs; + { + hb_blob_t *blob = hb_blob_create_from_file_or_fail (test_input.font_path); + assert (blob); + hb_face_t *face = hb_face_create (blob, 0); + hb_blob_destroy (blob); + num_glyphs = hb_face_get_glyph_count (face); + font = hb_font_create (face); + hb_face_destroy (face); + } + + if (is_var) + { + hb_variation_t wght = {HB_TAG ('w','g','h','t'), 500}; + hb_font_set_variations (font, &wght, 1); + } + + switch (backend) + { + case HARFBUZZ: + hb_ot_font_set_funcs (font); + break; + + case FREETYPE: +#ifdef HAVE_FREETYPE + hb_ft_font_set_funcs (font); +#endif + break; + } + + switch (operation) + { + case nominal_glyphs: + { + hb_set_t *set = hb_set_create (); + hb_face_collect_unicodes (hb_font_get_face (font), set); + unsigned pop = hb_set_get_population (set); + hb_codepoint_t *unicodes = (hb_codepoint_t *) calloc (pop, sizeof (hb_codepoint_t)); + hb_codepoint_t *glyphs = (hb_codepoint_t *) calloc (pop, sizeof (hb_codepoint_t)); + + hb_codepoint_t *p = unicodes; + for (hb_codepoint_t u = HB_SET_VALUE_INVALID; + hb_set_next (set, &u);) + *p++ = u; + assert (p == unicodes + pop); + + for (auto _ : state) + hb_font_get_nominal_glyphs (font, + pop, + unicodes, sizeof (*unicodes), + glyphs, sizeof (*glyphs)); + + free (glyphs); + free (unicodes); + hb_set_destroy (set); + break; + } + case glyph_h_advances: + { + hb_codepoint_t *glyphs = (hb_codepoint_t *) calloc (num_glyphs, sizeof (hb_codepoint_t)); + hb_position_t *advances = (hb_position_t *) calloc (num_glyphs, sizeof (hb_codepoint_t)); + + for (unsigned g = 0; g < num_glyphs; g++) + glyphs[g] = g; + + for (auto _ : state) + hb_font_get_glyph_h_advances (font, + num_glyphs, + glyphs, sizeof (*glyphs), + advances, sizeof (*advances)); + + free (advances); + free (glyphs); + break; + } + case glyph_extents: + { + hb_glyph_extents_t extents; + for (auto _ : state) + for (unsigned gid = 0; gid < num_glyphs; ++gid) + hb_font_get_glyph_extents (font, gid, &extents); + break; + } + case glyph_shape: + { + hb_draw_funcs_t *draw_funcs = _draw_funcs_create (); + for (auto _ : state) + for (unsigned gid = 0; gid < num_glyphs; ++gid) + hb_font_get_glyph_shape (font, gid, draw_funcs, nullptr); + break; + hb_draw_funcs_destroy (draw_funcs); + } + } + + + hb_font_destroy (font); +} + +static void test_backend (backend_t backend, + const char *backend_name, + bool variable, + operation_t op, + const char *op_name, + benchmark::TimeUnit time_unit, + const test_input_t &test_input) +{ + char name[1024] = "BM_Font/"; + strcat (name, op_name); + strcat (name, "/"); + const char *p = strrchr (test_input.font_path, '/'); + strcat (name, p ? p + 1 : test_input.font_path); + strcat (name, variable ? "/var" : ""); + strcat (name, "/"); + strcat (name, backend_name); + + benchmark::RegisterBenchmark (name, BM_Font, variable, backend, op, test_input) + ->Unit(time_unit); +} + +static void test_operation (operation_t op, + const char *op_name, + benchmark::TimeUnit time_unit) +{ + for (unsigned i = 0; i < num_tests; i++) + { + auto& test_input = tests[i]; + for (int variable = 0; variable < int (test_input.is_variable) + 1; variable++) + { + bool is_var = (bool) variable; + + test_backend (HARFBUZZ, "hb", is_var, op, op_name, time_unit, test_input); +#ifdef HAVE_FREETYPE + test_backend (FREETYPE, "ft", is_var, op, op_name, time_unit, test_input); +#endif + } + } +} + +int main(int argc, char** argv) +{ + benchmark::Initialize(&argc, argv); + + if (argc > 1) + { + num_tests = argc - 1; + tests = (test_input_t *) calloc (num_tests, sizeof (test_input_t)); + for (unsigned i = 0; i < num_tests; i++) + { + tests[i].is_variable = true; + tests[i].font_path = argv[i + 1]; + } + } + +#define TEST_OPERATION(op, time_unit) test_operation (op, #op, time_unit) + + TEST_OPERATION (nominal_glyphs, benchmark::kMicrosecond); + TEST_OPERATION (glyph_h_advances, benchmark::kMicrosecond); + TEST_OPERATION (glyph_extents, benchmark::kMicrosecond); + TEST_OPERATION (glyph_shape, benchmark::kMicrosecond); + +#undef TEST_OPERATION + + benchmark::RunSpecifiedBenchmarks(); + benchmark::Shutdown(); + + if (tests != default_tests) + free (tests); +} diff --git a/libs/harfbuzz/perf/benchmark-map.cc b/libs/harfbuzz/perf/benchmark-map.cc new file mode 100644 index 00000000..f278a0c9 --- /dev/null +++ b/libs/harfbuzz/perf/benchmark-map.cc @@ -0,0 +1,68 @@ +/* + * Benchmarks for hb_map_t operations. + */ +#include "benchmark/benchmark.h" + +#include +#include +#include "hb.h" + +void RandomMap(unsigned size, hb_map_t* out) { + hb_map_clear(out); + + srand(size); + for (unsigned i = 0; i < size; i++) { + while (true) { + hb_codepoint_t next = rand(); + if (hb_map_has (out, next)) continue; + + hb_map_set (out, next, rand ()); + break; + } + } +} + +/* Insert a single value into map of varying sizes. */ +static void BM_MapInsert(benchmark::State& state) { + unsigned map_size = state.range(0); + + hb_map_t* original = hb_map_create (); + RandomMap(map_size, original); + assert(hb_map_get_population(original) == map_size); + + auto needle = map_size / 2; + auto v = 0; + for (auto _ : state) { + // TODO(garretrieger): create a copy of the original map. + // Needs a hb_map_copy(..) in public api. + + hb_map_set (original, needle++, v++); + } + + hb_map_destroy(original); +} +BENCHMARK(BM_MapInsert) + ->Range(1 << 4, 1 << 20); + +/* Single value lookup on map of various sizes. */ +static void BM_MapLookup(benchmark::State& state) { + unsigned map_size = state.range(0); + + hb_map_t* original = hb_map_create (); + RandomMap(map_size, original); + assert(hb_map_get_population(original) == map_size); + + auto needle = map_size / 2; + + for (auto _ : state) { + benchmark::DoNotOptimize( + hb_map_get (original, needle++)); + } + + hb_map_destroy(original); +} +BENCHMARK(BM_MapLookup) + ->Range(1 << 4, 1 << 20); // Map size + + +BENCHMARK_MAIN(); diff --git a/libs/harfbuzz/perf/benchmark-ot.cc b/libs/harfbuzz/perf/benchmark-ot.cc new file mode 100644 index 00000000..c79c384a --- /dev/null +++ b/libs/harfbuzz/perf/benchmark-ot.cc @@ -0,0 +1,44 @@ +/* + * Benchmarks for hb_set_t operations. + */ +#include "benchmark/benchmark.h" + +#include "hb-ot.h" + +static void BM_hb_ot_tags_from_script_and_language (benchmark::State& state, + hb_script_t script, + const char *language_str) { + + hb_language_t language = hb_language_from_string (language_str, -1); + + for (auto _ : state) + { + hb_tag_t script_tags[HB_OT_MAX_TAGS_PER_SCRIPT]; + unsigned script_count = HB_OT_MAX_TAGS_PER_SCRIPT; + + hb_tag_t language_tags[HB_OT_MAX_TAGS_PER_LANGUAGE]; + unsigned language_count = HB_OT_MAX_TAGS_PER_LANGUAGE; + + hb_ot_tags_from_script_and_language (script, + language, + &script_count /* IN/OUT */, + script_tags /* OUT */, + &language_count /* IN/OUT */, + language_tags /* OUT */); + } +} +BENCHMARK_CAPTURE (BM_hb_ot_tags_from_script_and_language, COMMON zh_abcd, HB_SCRIPT_COMMON, "zh_abcd"); +BENCHMARK_CAPTURE (BM_hb_ot_tags_from_script_and_language, COMMON zh_hans, HB_SCRIPT_COMMON, "zh_hans"); +BENCHMARK_CAPTURE (BM_hb_ot_tags_from_script_and_language, COMMON ab_abcd, HB_SCRIPT_COMMON, "ab_abcd"); +BENCHMARK_CAPTURE (BM_hb_ot_tags_from_script_and_language, COMMON ab_abc, HB_SCRIPT_COMMON, "ab_abc"); +BENCHMARK_CAPTURE (BM_hb_ot_tags_from_script_and_language, COMMON abcdef_XY, HB_SCRIPT_COMMON, "abcdef_XY"); +BENCHMARK_CAPTURE (BM_hb_ot_tags_from_script_and_language, COMMON abcd_XY, HB_SCRIPT_COMMON, "abcd_XY"); +BENCHMARK_CAPTURE (BM_hb_ot_tags_from_script_and_language, COMMON cxy_CN, HB_SCRIPT_COMMON, "cxy_CN"); +BENCHMARK_CAPTURE (BM_hb_ot_tags_from_script_and_language, COMMON exy_CN, HB_SCRIPT_COMMON, "exy_CN"); +BENCHMARK_CAPTURE (BM_hb_ot_tags_from_script_and_language, COMMON zh_CN, HB_SCRIPT_COMMON, "zh_CN"); +BENCHMARK_CAPTURE (BM_hb_ot_tags_from_script_and_language, COMMON en_US, HB_SCRIPT_COMMON, "en_US"); +BENCHMARK_CAPTURE (BM_hb_ot_tags_from_script_and_language, LATIN en_US, HB_SCRIPT_LATIN, "en_US"); +BENCHMARK_CAPTURE (BM_hb_ot_tags_from_script_and_language, COMMON none, HB_SCRIPT_LATIN, nullptr); +BENCHMARK_CAPTURE (BM_hb_ot_tags_from_script_and_language, LATIN none, HB_SCRIPT_LATIN, nullptr); + +BENCHMARK_MAIN(); diff --git a/libs/harfbuzz/perf/benchmark-set.cc b/libs/harfbuzz/perf/benchmark-set.cc new file mode 100644 index 00000000..c170acfc --- /dev/null +++ b/libs/harfbuzz/perf/benchmark-set.cc @@ -0,0 +1,151 @@ +/* + * Benchmarks for hb_set_t operations. + */ +#include "benchmark/benchmark.h" + +#include +#include +#include "hb.h" + +void RandomSet(unsigned size, unsigned max_value, hb_set_t* out) { + hb_set_clear(out); + + srand(size * max_value); + for (unsigned i = 0; i < size; i++) { + while (true) { + unsigned next = rand() % max_value; + if (hb_set_has (out, next)) continue; + + hb_set_add(out, next); + break; + } + } +} + +// TODO(garretrieger): benchmark union/subtract/intersection etc. + +/* Insert a 1000 values into set of varying sizes. */ +static void BM_SetInsert_1000(benchmark::State& state) { + unsigned set_size = state.range(0); + unsigned max_value = state.range(0) * state.range(1); + + hb_set_t* original = hb_set_create (); + RandomSet(set_size, max_value, original); + assert(hb_set_get_population(original) == set_size); + + for (auto _ : state) { + state.PauseTiming (); + hb_set_t* data = hb_set_copy(original); + state.ResumeTiming (); + for (int i = 0; i < 1000; i++) { + hb_set_add(data, i * 2654435761u % max_value); + } + hb_set_destroy(data); + } + + hb_set_destroy(original); +} +BENCHMARK(BM_SetInsert_1000) + ->Unit(benchmark::kMicrosecond) + ->Ranges( + {{1 << 10, 1 << 16}, // Set Size + {2, 512}}); // Density + +/* Insert a 1000 values into set of varying sizes. */ +static void BM_SetOrderedInsert_1000(benchmark::State& state) { + unsigned set_size = state.range(0); + unsigned max_value = state.range(0) * state.range(1); + + hb_set_t* original = hb_set_create (); + RandomSet(set_size, max_value, original); + assert(hb_set_get_population(original) == set_size); + + for (auto _ : state) { + state.PauseTiming (); + hb_set_t* data = hb_set_copy(original); + state.ResumeTiming (); + for (int i = 0; i < 1000; i++) { + hb_set_add(data, i); + } + hb_set_destroy(data); + } + + hb_set_destroy(original); +} +BENCHMARK(BM_SetOrderedInsert_1000) + ->Unit(benchmark::kMicrosecond) + ->Ranges( + {{1 << 10, 1 << 16}, // Set Size + {2, 512}}); // Density + +/* Single value lookup on sets of various sizes. */ +static void BM_SetLookup(benchmark::State& state, unsigned interval) { + unsigned set_size = state.range(0); + unsigned max_value = state.range(0) * state.range(1); + + hb_set_t* original = hb_set_create (); + RandomSet(set_size, max_value, original); + assert(hb_set_get_population(original) == set_size); + + auto needle = max_value / 2; + for (auto _ : state) { + benchmark::DoNotOptimize( + hb_set_has (original, (needle += interval) % max_value)); + } + + hb_set_destroy(original); +} +BENCHMARK_CAPTURE(BM_SetLookup, ordered, 3) + ->Ranges( + {{1 << 10, 1 << 16}, // Set Size + {2, 512}}); // Density +BENCHMARK_CAPTURE(BM_SetLookup, random, 12345) + ->Ranges( + {{1 << 10, 1 << 16}, // Set Size + {2, 512}}); // Density + +/* Full iteration of sets of varying sizes. */ +static void BM_SetIteration(benchmark::State& state) { + unsigned set_size = state.range(0); + unsigned max_value = state.range(0) * state.range(1); + + hb_set_t* original = hb_set_create (); + RandomSet(set_size, max_value, original); + assert(hb_set_get_population(original) == set_size); + + hb_codepoint_t cp = HB_SET_VALUE_INVALID; + for (auto _ : state) { + hb_set_next (original, &cp); + } + + hb_set_destroy(original); +} +BENCHMARK(BM_SetIteration) + ->Ranges( + {{1 << 10, 1 << 16}, // Set Size + {2, 512}}); // Density + +/* Set copy. */ +static void BM_SetCopy(benchmark::State& state) { + unsigned set_size = state.range(0); + unsigned max_value = state.range(0) * state.range(1); + + hb_set_t* original = hb_set_create (); + RandomSet(set_size, max_value, original); + assert(hb_set_get_population(original) == set_size); + + for (auto _ : state) { + hb_set_t *s = hb_set_create (); + hb_set_set (s, original); + hb_set_destroy (s); + } + + hb_set_destroy(original); +} +BENCHMARK(BM_SetCopy) + ->Unit(benchmark::kMicrosecond) + ->Ranges( + {{1 << 10, 1 << 16}, // Set Size + {2, 512}}); // Density + +BENCHMARK_MAIN(); diff --git a/libs/harfbuzz/perf/benchmark-shape.cc b/libs/harfbuzz/perf/benchmark-shape.cc new file mode 100644 index 00000000..626a5977 --- /dev/null +++ b/libs/harfbuzz/perf/benchmark-shape.cc @@ -0,0 +1,172 @@ +#include "benchmark/benchmark.h" +#include + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "hb.h" +#include "hb-ot.h" +#ifdef HAVE_FREETYPE +#include "hb-ft.h" +#endif + +#define SUBSET_FONT_BASE_PATH "test/subset/data/fonts/" + +struct test_input_t +{ + const char *font_path; + const char *text_path; + bool is_variable; +} default_tests[] = +{ + + {"perf/fonts/NotoNastaliqUrdu-Regular.ttf", + "perf/texts/fa-thelittleprince.txt", + false}, + + {"perf/fonts/Amiri-Regular.ttf", + "perf/texts/fa-thelittleprince.txt", + false}, + + {"perf/fonts/Roboto-Regular.ttf", + "perf/texts/en-thelittleprince.txt", + false}, + + {"perf/fonts/Roboto-Regular.ttf", + "perf/texts/en-words.txt", + false}, + + {SUBSET_FONT_BASE_PATH "SourceSerifVariable-Roman.ttf", + "perf/texts/en-thelittleprince.txt", + true}, +}; + +static test_input_t *tests = default_tests; +static unsigned num_tests = sizeof (default_tests) / sizeof (default_tests[0]); + +enum backend_t { HARFBUZZ, FREETYPE }; + +static void BM_Shape (benchmark::State &state, + bool is_var, + backend_t backend, + const test_input_t &input) +{ + hb_font_t *font; + { + hb_blob_t *blob = hb_blob_create_from_file_or_fail (input.font_path); + assert (blob); + hb_face_t *face = hb_face_create (blob, 0); + hb_blob_destroy (blob); + font = hb_font_create (face); + hb_face_destroy (face); + } + + if (is_var) + { + hb_variation_t wght = {HB_TAG ('w','g','h','t'), 500}; + hb_font_set_variations (font, &wght, 1); + } + + switch (backend) + { + case HARFBUZZ: + hb_ot_font_set_funcs (font); + break; + + case FREETYPE: +#ifdef HAVE_FREETYPE + hb_ft_font_set_funcs (font); +#endif + break; + } + + hb_blob_t *text_blob = hb_blob_create_from_file_or_fail (input.text_path); + assert (text_blob); + unsigned orig_text_length; + const char *orig_text = hb_blob_get_data (text_blob, &orig_text_length); + + hb_buffer_t *buf = hb_buffer_create (); + for (auto _ : state) + { + unsigned text_length = orig_text_length; + const char *text = orig_text; + + const char *end; + while ((end = (const char *) memchr (text, '\n', text_length))) + { + hb_buffer_clear_contents (buf); + hb_buffer_add_utf8 (buf, text, text_length, 0, end - text); + hb_buffer_guess_segment_properties (buf); + hb_shape (font, buf, nullptr, 0); + + unsigned skip = end - text + 1; + text_length -= skip; + text += skip; + } + } + hb_buffer_destroy (buf); + + hb_blob_destroy (text_blob); + hb_font_destroy (font); +} + +static void test_backend (backend_t backend, + const char *backend_name, + bool variable, + const test_input_t &test_input) +{ + char name[1024] = "BM_Shape"; + const char *p; + strcat (name, "/"); + p = strrchr (test_input.font_path, '/'); + strcat (name, p ? p + 1 : test_input.font_path); + strcat (name, "/"); + p = strrchr (test_input.text_path, '/'); + strcat (name, p ? p + 1 : test_input.text_path); + strcat (name, variable ? "/var" : ""); + strcat (name, "/"); + strcat (name, backend_name); + + benchmark::RegisterBenchmark (name, BM_Shape, variable, backend, test_input) + ->Unit(benchmark::kMillisecond); +} + +int main(int argc, char** argv) +{ + benchmark::Initialize(&argc, argv); + + if (argc > 2) + { + num_tests = (argc - 1) / 2; + tests = (test_input_t *) calloc (num_tests, sizeof (test_input_t)); + for (unsigned i = 0; i < num_tests; i++) + { + tests[i].is_variable = true; + tests[i].font_path = argv[1 + i * 2]; + tests[i].text_path = argv[2 + i * 2]; + } + } + + for (unsigned i = 0; i < num_tests; i++) + { + auto& test_input = tests[i]; + for (int variable = 0; variable < int (test_input.is_variable) + 1; variable++) + { + bool is_var = (bool) variable; + + test_backend (HARFBUZZ, "hb", is_var, test_input); +#ifdef HAVE_FREETYPE + test_backend (FREETYPE, "ft", is_var, test_input); +#endif + } + } + + benchmark::RunSpecifiedBenchmarks(); + benchmark::Shutdown(); + + if (tests != default_tests) + free (tests); +} diff --git a/libs/harfbuzz/perf/benchmark-subset.cc b/libs/harfbuzz/perf/benchmark-subset.cc new file mode 100644 index 00000000..0451c11d --- /dev/null +++ b/libs/harfbuzz/perf/benchmark-subset.cc @@ -0,0 +1,221 @@ +#include "benchmark/benchmark.h" +#include +#include + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "hb-subset.h" + + +enum operation_t +{ + subset_codepoints, + subset_glyphs, + instance, +}; + +struct axis_location_t +{ + hb_tag_t axis_tag; + float axis_value; +}; + +static const axis_location_t +_roboto_flex_instance_opts[] = +{ + {HB_TAG ('w', 'g', 'h', 't'), 600.f}, + {HB_TAG ('w', 'd', 't', 'h'), 75.f}, + {HB_TAG ('o', 'p', 's', 'z'), 90.f}, + {HB_TAG ('G', 'R', 'A', 'D'), -100.f}, + {HB_TAG ('s', 'l', 'n', 't'), -3.f}, + {HB_TAG ('X', 'T', 'R', 'A'), 500.f}, + {HB_TAG ('X', 'O', 'P', 'Q'), 150.f}, + {HB_TAG ('Y', 'O', 'P', 'Q'), 100.f}, + {HB_TAG ('Y', 'T', 'L', 'C'), 480.f}, + {HB_TAG ('Y', 'T', 'U', 'C'), 600.f}, + {HB_TAG ('Y', 'T', 'A', 'S'), 800.f}, + {HB_TAG ('Y', 'T', 'D', 'E'), -50.f}, + {HB_TAG ('Y', 'T', 'F', 'I'), 600.f}, +}; + +static const axis_location_t +_mplus_instance_opts[] = +{ + {HB_TAG ('w', 'g', 'h', 't'), 800.f}, +}; + +template +static inline unsigned int ARRAY_LEN (const Type (&)[n]) { return n; } + +#define SUBSET_FONT_BASE_PATH "test/subset/data/fonts/" + +struct test_input_t +{ + const char *font_path; + const unsigned max_subset_size; + const axis_location_t *instance_opts; + const unsigned num_instance_opts; +} tests[] = +{ + {SUBSET_FONT_BASE_PATH "Roboto-Regular.ttf", 4000, nullptr, 0}, + {SUBSET_FONT_BASE_PATH "Amiri-Regular.ttf", 4000, nullptr, 0}, + {SUBSET_FONT_BASE_PATH "NotoNastaliqUrdu-Regular.ttf", 1000, nullptr, 0}, + {SUBSET_FONT_BASE_PATH "NotoSansDevanagari-Regular.ttf", 1000, nullptr, 0}, + {SUBSET_FONT_BASE_PATH "Mplus1p-Regular.ttf", 10000, nullptr, 0}, + {SUBSET_FONT_BASE_PATH "SourceHanSans-Regular_subset.otf", 10000, nullptr, 0}, + {SUBSET_FONT_BASE_PATH "SourceSansPro-Regular.otf", 2000, nullptr, 0}, + {SUBSET_FONT_BASE_PATH "MPLUS1-Variable.ttf", 6000, _mplus_instance_opts, ARRAY_LEN (_mplus_instance_opts)}, + {SUBSET_FONT_BASE_PATH "RobotoFlex-Variable.ttf", 900, _roboto_flex_instance_opts, ARRAY_LEN (_roboto_flex_instance_opts)}, +#if 0 + {"perf/fonts/NotoSansCJKsc-VF.ttf", 100000}, +#endif +}; + +void AddCodepoints(const hb_set_t* codepoints_in_font, + unsigned subset_size, + hb_subset_input_t* input) +{ + auto *unicodes = hb_subset_input_unicode_set (input); + hb_codepoint_t cp = HB_SET_VALUE_INVALID; + for (unsigned i = 0; i < subset_size; i++) { + // TODO(garretrieger): pick randomly. + if (!hb_set_next (codepoints_in_font, &cp)) return; + hb_set_add (unicodes, cp); + } +} + +void AddGlyphs(unsigned num_glyphs_in_font, + unsigned subset_size, + hb_subset_input_t* input) +{ + auto *glyphs = hb_subset_input_glyph_set (input); + for (unsigned i = 0; i < subset_size && i < num_glyphs_in_font; i++) { + // TODO(garretrieger): pick randomly. + hb_set_add (glyphs, i); + } +} + +// Preprocess face and populate the subset accelerator on it to speed up +// the subsetting operations. +static hb_face_t* preprocess_face(hb_face_t* face) +{ + #ifdef HB_EXPERIMENTAL_API + hb_face_t* new_face = hb_subset_preprocess(face); + hb_face_destroy(face); + return new_face; + #else + return face; + #endif +} + +/* benchmark for subsetting a font */ +static void BM_subset (benchmark::State &state, + operation_t operation, + const test_input_t &test_input) +{ + unsigned subset_size = state.range(0); + + hb_face_t *face; + { + hb_blob_t *blob = hb_blob_create_from_file_or_fail (test_input.font_path); + assert (blob); + face = hb_face_create (blob, 0); + hb_blob_destroy (blob); + + face = preprocess_face (face); + } + + hb_subset_input_t* input = hb_subset_input_create_or_fail (); + assert (input); + + switch (operation) + { + case subset_codepoints: + { + hb_set_t* all_codepoints = hb_set_create (); + hb_face_collect_unicodes (face, all_codepoints); + AddCodepoints(all_codepoints, subset_size, input); + hb_set_destroy (all_codepoints); + } + break; + + case subset_glyphs: + { + unsigned num_glyphs = hb_face_get_glyph_count (face); + AddGlyphs(num_glyphs, subset_size, input); + } + break; + + case instance: +#ifdef HB_EXPERIMENTAL_API + { + hb_set_t* all_codepoints = hb_set_create (); + hb_face_collect_unicodes (face, all_codepoints); + AddCodepoints(all_codepoints, subset_size, input); + hb_set_destroy (all_codepoints); + + for (unsigned i = 0; i < test_input.num_instance_opts; i++) + hb_subset_input_pin_axis_location (input, face, + test_input.instance_opts[i].axis_tag, + test_input.instance_opts[i].axis_value); + } +#endif + break; + } + + for (auto _ : state) + { + hb_face_t* subset = hb_subset_or_fail (face, input); + assert (subset); + hb_face_destroy (subset); + } + + hb_subset_input_destroy (input); + hb_face_destroy (face); +} + +static void test_subset (operation_t op, + const char *op_name, + benchmark::TimeUnit time_unit, + const test_input_t &test_input) +{ + if (op == instance && test_input.instance_opts == nullptr) + return; + + char name[1024] = "BM_subset/"; + strcat (name, op_name); + strcat (name, strrchr (test_input.font_path, '/')); + + benchmark::RegisterBenchmark (name, BM_subset, op, test_input) + ->Range(10, test_input.max_subset_size) + ->Unit(time_unit); +} + +static void test_operation (operation_t op, + const char *op_name, + benchmark::TimeUnit time_unit) +{ + for (auto& test_input : tests) + { + test_subset (op, op_name, time_unit, test_input); + } +} + +int main(int argc, char** argv) +{ +#define TEST_OPERATION(op, time_unit) test_operation (op, #op, time_unit) + + TEST_OPERATION (subset_glyphs, benchmark::kMillisecond); + TEST_OPERATION (subset_codepoints, benchmark::kMillisecond); +#ifdef HB_EXPERIMENTAL_API + TEST_OPERATION (instance, benchmark::kMillisecond); +#endif + +#undef TEST_OPERATION + + benchmark::Initialize(&argc, argv); + benchmark::RunSpecifiedBenchmarks(); + benchmark::Shutdown(); +} diff --git a/libs/harfbuzz/perf/fonts/Amiri-Regular.ttf b/libs/harfbuzz/perf/fonts/Amiri-Regular.ttf new file mode 100644 index 00000000..e328e063 Binary files /dev/null and b/libs/harfbuzz/perf/fonts/Amiri-Regular.ttf differ diff --git a/libs/harfbuzz/perf/fonts/NotoNastaliqUrdu-Regular.ttf b/libs/harfbuzz/perf/fonts/NotoNastaliqUrdu-Regular.ttf new file mode 100644 index 00000000..891f633d Binary files /dev/null and b/libs/harfbuzz/perf/fonts/NotoNastaliqUrdu-Regular.ttf differ diff --git a/libs/harfbuzz/perf/fonts/Roboto-Regular.ttf b/libs/harfbuzz/perf/fonts/Roboto-Regular.ttf new file mode 100644 index 00000000..500b1045 Binary files /dev/null and b/libs/harfbuzz/perf/fonts/Roboto-Regular.ttf differ diff --git a/libs/harfbuzz/perf/meson.build b/libs/harfbuzz/perf/meson.build new file mode 100644 index 00000000..ae122f43 --- /dev/null +++ b/libs/harfbuzz/perf/meson.build @@ -0,0 +1,62 @@ +google_benchmark = subproject('google-benchmark') +google_benchmark_dep = google_benchmark.get_variable('google_benchmark_dep') + +benchmark('benchmark-font', executable('benchmark-font', 'benchmark-font.cc', + dependencies: [ + google_benchmark_dep, freetype_dep, + ], + cpp_args: [], + include_directories: [incconfig, incsrc], + link_with: [libharfbuzz], + install: false, +), workdir: meson.current_source_dir() / '..', timeout: 100) + +benchmark('benchmark-map', executable('benchmark-map', 'benchmark-map.cc', + dependencies: [ + google_benchmark_dep, + ], + cpp_args: [], + include_directories: [incconfig, incsrc], + link_with: [libharfbuzz], + install: false, +), workdir: meson.current_source_dir() / '..', timeout: 100) + +benchmark('benchmark-ot', executable('benchmark-ot', 'benchmark-ot.cc', + dependencies: [ + google_benchmark_dep, + ], + cpp_args: [], + include_directories: [incconfig, incsrc], + link_with: [libharfbuzz], + install: false, +), workdir: meson.current_source_dir() / '..', timeout: 100) + +benchmark('benchmark-set', executable('benchmark-set', 'benchmark-set.cc', + dependencies: [ + google_benchmark_dep, + ], + cpp_args: [], + include_directories: [incconfig, incsrc], + link_with: [libharfbuzz], + install: false, +), workdir: meson.current_source_dir() / '..', timeout: 100) + +benchmark('benchmark-shape', executable('benchmark-shape', 'benchmark-shape.cc', + dependencies: [ + google_benchmark_dep, freetype_dep, + ], + cpp_args: [], + include_directories: [incconfig, incsrc], + link_with: [libharfbuzz], + install: false, +), workdir: meson.current_source_dir() / '..', timeout: 100) + +benchmark('benchmark-subset', executable('benchmark-subset', 'benchmark-subset.cc', + dependencies: [ + google_benchmark_dep, + ], + cpp_args: [], + include_directories: [incconfig, incsrc], + link_with: [libharfbuzz, libharfbuzz_subset], + install: false, +), workdir: meson.current_source_dir() / '..', timeout: 100) diff --git a/libs/harfbuzz/perf/run.sh b/libs/harfbuzz/perf/run.sh new file mode 100644 index 00000000..c7dc6e0c --- /dev/null +++ b/libs/harfbuzz/perf/run.sh @@ -0,0 +1,25 @@ +#!/bin/bash +CXX=clang++ +FONT=fonts/NotoNastaliqUrdu-Regular.ttf +TEXT=texts/fa-monologue.txt + +$CXX ../util/hb-shape.cc ../util/options.cc ../src/harfbuzz.cc \ + -lm -fno-rtti -fno-exceptions -fno-omit-frame-pointer -DHB_NO_MT \ + -I../src $FLAGS $SOURCES \ + -DPACKAGE_NAME='""' -DPACKAGE_VERSION='""' \ + -DHAVE_GLIB $(pkg-config --cflags --libs glib-2.0) \ + -o hb-shape -g -O2 # -O3 \ + #-march=native -mtune=native \ + #-Rpass=loop-vectorize -Rpass-missed=loop-vectorize \ + #-Rpass-analysis=loop-vectorize -fsave-optimization-record + +# -march=native: enable all vector instructions current CPU can offer +# -Rpass*: https://llvm.org/docs/Vectorizers.html#diagnostics + +#sudo rm capture.syscap > /dev/null +#sysprof-cli -c "./a.out $@" +#sysprof capture.syscap + +perf stat ./hb-shape -o /dev/null $FONT --text-file $TEXT --num-iterations=100 --font-funcs=ot +#perf record -g ./hb-shape -O '' -o /dev/null $FONT --text-file $TEXT --num-iterations=100 --font-funcs=ot +#perf report -g diff --git a/libs/harfbuzz/perf/texts/en-thelittleprince.txt b/libs/harfbuzz/perf/texts/en-thelittleprince.txt new file mode 100644 index 00000000..7bf4abcc --- /dev/null +++ b/libs/harfbuzz/perf/texts/en-thelittleprince.txt @@ -0,0 +1,1893 @@ +Downloaded from https://archive.org/details/TheLittlePrince-English + +THE LITTLE PRINCE + + + +Antoine De Saint-Exupery + + + + +Antoine de Saint-Exupery, who was a French author, journalist and pilot wrote +The Little Prince in 1943, one year before his death. + +The Little Prince appears to be a simple children’s tale, +some would say that it is actually a profound and deeply moving tale, +written in riddles and laced with philosophy and poetic metaphor. + + + + +Once when I was six years old I saw a magnificent picture in a book, called True Stories from +Nature, about the primeval forest. It was a picture of a boa constrictor in the act of swallowing an +animal. Here is a copy of the drawing. + +In the book it said: “Boa constrictors swallow their prey whole, without chewing it. After that they +are not able to move, and they sleep through the six months that they need for digestion.” I +pondered deeply, then, over the adventures of the jungle. And after some work with a coloured +pencil I succeeded in making my first drawing. My Drawing Number One. It looked like this: + + + + +I showed my masterpiece to the grown-ups, and asked them whether the drawing frightened them. +But they answered: “Frighten? Why should any one be frightened by a hat?” My drawing was not +a picture of a hat. It was a picture of a boa constrictor digesting an elephant. But since the grown- +ups were not able to understand it, I made another drawing: I drew the inside of the boa +constrictor, so that the grown-ups could see it clearly. They always need to have things explained. + + + +My Drawing Number Two looked like this: + + + + +The grown-ups’ response, this time, was to advise me to lay aside my drawings of boa +constrictors, whether from the inside or the outside, and devote myself instead to geography, +history, arithmetic and grammar. That is why, at the age of six, I gave up what might have been a +magnificent career as a painter. I had been disheartened by the failure of my Drawing Number +One and my Drawing Number Two. Grown-ups never understand anything by themselves, and it is +tiresome for children to be always and forever explaining things to them. + +So then I chose another profession, and learned to pilot air-planes. I have flown a little over all +parts of the world; and it is true that geography has been very useful to me. At a glance I can +distinguish China from Arizona. If one gets lost in the night, such knowledge is valuable. In the +course of this life I have had a great many encounters with a great many people who have been +concerned with matters of consequence. I have lived a great deal among grown-ups. I have seen +them intimately, close at hand. And that hasn’t much improved my opinion of them. + +Whenever I met one of them who seemed to me at all clear-sighted, I tried the experiment of +showing him my Drawing Number One, which I have always kept. I would try to find out, so, if this +was a person of true understanding. But, whoever it was, he, or she, would always say: “That is a +hat.” Then I would never talk to that person about boa constrictors, or primeval forests, or stars. I +would bring myself down to his level. I would talk to him about bridge, and golf, and politics, and +neckties. And the grown-up would be greatly pleased to have met such a sensible man. + +So I lived my life alone, without anyone that I could really talk to, until I had an accident with my +plane in the Desert of Sahara, six years ago. Something was broken in my engine. And as I had +with me neither a mechanic nor any passengers, I set myself to attempt the difficult repairs all +alone. It was a question of life or death for me: I had scarcely enough drinking water to last a +week. + +The first night, then, I went to sleep on the sand, a thousand miles from any human habitation. I +was more isolated than a shipwrecked sailor on a raft in the middle of the ocean. Thus you can +imagine my amazement, at sunrise, when I was awakened by an odd little voice. + + + + +It said: “If you please, draw me a sheep!” + +“What!” + +“Draw me a sheep!” + +I jumped to my feet, completely thunderstruck. I blinked my eyes hard. I looked carefully all +around me. And I saw a most extraordinary small person, who stood there examining me with +great seriousness. Here you may see the best portrait that, later, I was able to make of him. But +my drawing is certainly very much less charming than its model. + +That, however, is not my fault. The grown-ups discouraged me in my painter’s career when I was +six years old, and I never learned to draw anything, except boas from the outside and boas from +the inside. + +Now I stared at this sudden apparition with my eyes fairly starting out of my head in +astonishment. Remember, I had crashed in the desert a thousand miles from any inhabited region. +And yet my little man seemed neither to be straying uncertainly among the sands, nor to be +fainting from fatigue or hunger or thirst or fear. Nothing about him gave any suggestion of a child +lost in the middle of the desert, a thousand miles from any human habitation. + +When at last I was able to speak, I said to him: “But, what are you doing here?” And in answer he +repeated, very slowly, as if he were speaking of a matter of great consequence: + +“If you please, draw me a sheep...” + +When a mystery is too overpowering, one dare not disobey. Absurd as it might seem to me, a +thousand miles from any human habitation and in danger of death, I took out of my pocket a sheet +of paper and my fountain pen. But then I remembered how my studies had been concentrated on +geography, history, arithmetic, and grammar, and I told the little chap (a little crossly, too) that I +did not know how to draw. He answered me: “That doesn’t matter. Draw me a sheep...” + + + + +But I had never drawn a sheep. So I drew for him one of the two pictures I had drawn so often. It +was that of the boa constrictor from the outside. And I was astounded to hear the little fellow +greet it with, “No, no, no! I do not want an elephant inside a boa constrictor. A boa constrictor is a +very dangerous creature, and an elephant is very cumbersome. Where I live, everything is very +small. What I need is a sheep. Draw me a sheep. + + + + +So then I made a drawing. He looked at it carefully, then he said: “No. This sheep is already very +sickly. Make me another.” So I made another drawing. My friend smiled gently and indulgently. +“You see yourself,” he said, “that this is not a sheep. This is a ram. It has horns. + + + + +So then I did my drawing over once more. But it was rejected too, just like the others. “This one is +too old. I want a sheep that will live a long time. + +By this time my patience was exhausted, because I was in a hurry to start taking my engine apart. +So I tossed off this drawing. And I threw out an explanation with it. + +“This is only his box. The sheep you asked for is inside.” + + + + +I was very surprised to see a light break over the face of my young judge: + +“That is exactly the way I wanted it! Do you think that this sheep will have to have a great deal of +grass?” + +“Why?” + +“Because where I live everything is very small...” + +“There will surely be enough grass for him,” I said. + +“It is a very small sheep that I have given you.” + +He bent his head over the drawing: “Not so small that, Look! He has gone to sleep...” + +And that is how I made the acquaintance of the little prince. + +It took me a long time to learn where he came from. The little prince, who asked me so many +questions, never seemed to hear the ones I asked him. It was from words dropped by chance that, +little by little, everything was revealed to me. + +The first time he saw my air-plane, for instance (I shall not draw my air-plane; that would be much +too complicated for me), he asked me: “What is that object?” + +“That is not an object. It flies. It is an air-plane. It is my air-plane.” And I was proud to have him +learn that I could fly. He cried out, then: “What! You dropped down from the sky?” + + + +“Yes,” I answered, modestly. + + + +“Oh! That is funny!” And the little prince broke into a lovely peal of laughter, which irritated me +very much. I like my misfortunes to be taken seriously. + +Then he added: “So you, too, come from the sky! Which is your planet?” At that moment I caught +a gleam of light in the impenetrable mystery of his presence; and I demanded, abruptly: “Do you +come from another planet?” But he did not reply. He tossed his head gently, without taking his +eyes from my plane: “It is true that on that you can’t have come from very far away...” And he +sank into a reverie, which lasted a long time. Then, taking my sheep out of his pocket, he buried +himself in the contemplation of his treasure. + +You can imagine how my curiosity was aroused by this half-confidence about the “other planets.” I +made a great effort, therefore, to find out more on this subject. + +“My little man, where do you come from? What is this ‘where I live,’ of which you speak? Where +do you want to take your sheep?” + +After a reflective silence he answered: “The thing that is so good about the box you have given +me is that at night he can use it as his house.” + +“That is so. And if you are good I will give you a string, too, so that you can tie him during the day, +and a post to tie him to.” + +But the little prince seemed shocked by this offer: “Tie him! What a queer idea!” + +“But if you don’t tie him,” I said, “he will wander off somewhere, and get lost.” + +My friend broke into another peal of laughter: “But where do you think he would go?” +“Anywhere. Straight ahead of him.” + +Then the little prince said, earnestly: “That doesn’t matter. Where I live, everything is so small!” +And, with perhaps a hint of sadness, he added: “Straight ahead of him, nobody can go very far...” + + + + +I had thus learned a second fact of great importance: this was that the planet the little prince +came from was scarcely any larger than a house! But that did not really surprise me much. I knew +very well that in addition to the great planets, such as the Earth, Jupiter, Mars, Venus, to which +we have given names, there are also hundreds of others, some of which are so small that one has +a hard time seeing them through the telescope. + + + +When an astronomer discovers one of these he does not give it a name, but only a number. He +might call it, for example, “Asteroid 325.” + + + + +I have serious reason to believe that the planet from which the little prince came is the asteroid +known as B-612. This asteroid has only once been seen through the telescope. That was by a +Turkish astronomer, in 1909. + +On making his discovery, the astronomer had presented it to the International Astronomical +Congress, in a great demonstration. But he was in Turkish costume, and so nobody would believe +what he said. Grown-ups are like that... + +Fortunately, however, for the reputation of Asteroid B-612, a Turkish dictator made a law that his +subjects, under pain of death, should change to European costume. So in 1920 the astronomer +gave his demonstration all over again, dressed with impressive style and elegance. And this time +everybody accepted his report. + +If I have told you these details about the asteroid, and made a note of its number for you, it is on +account of the grown-ups and their ways. When you tell them that you have made a new friend, +they never ask you any questions about essential matters. They never say to you, “What does his +voice sound like? What games does he love best? Does he collect butterflies?” Instead, they +demand: “How old is he? How many brothers has he? How much does he weigh? How much +money does his father make?” + +Only from these figures do they think they have learned anything about him. + +If you were to say to the grown-ups: “I saw a beautiful house made of rosy brick, with geraniums +in the windows and doves on the roof,” they would not be able to get any idea of that house at all. + +You would have to say to them: “I saw a house that cost $ 20,000.” Then they would exclaim: “Oh, +what a pretty house that is!” Just so, you might say to them: “The proof that the little prince +existed is that he was charming, that he laughed, and that he was looking for a sheep. If anybody +wants a sheep, that is a proof that he exists.” And what good would it do to tell them that? They +would shrug their shoulders, and treat you like a child. But if you said to them: “The planet he +came from is Asteroid B-612,” then they would be convinced, and leave you in peace from their +questions. They are like that. One must not hold it against them. Children should always show +great forbearance toward grown-up people. But certainly, for us who understand life, figures are a +matter of indifference. + +I should have liked to begin this story in the fashion of the fairy-tales. I should have like to say: +“Once upon a time there was a little prince who lived on a planet that was scarcely any bigger + + + +than himself, and who had need of a sheep...” + + + +To those who understand life, that would have given a much greater air of truth to my story. Fori +do not want any one to read my book carelessly. I have suffered too much grief in setting down +these memories. Six years have already passed since my friend went away from me, with his +sheep. If I try to describe him here, it is to make sure that I shall not forget him. To forget a friend +is sad. Not every one has had a friend. And if I forget him, I may become like the grown-ups who +are no longer interested in anything but figures... It is for that purpose, again, that I have bought a +box of paints and some pencils. + +It is hard to take up drawing again at my age, when I have never made any pictures except those +of the boa constrictor from the outside and the boa constrictor from the inside, since I was six. I +shall certainly try to make my portraits as true to life as possible. But I am not at all sure of +success. One drawing goes along all right, and another has no resemblance to its subject. I make +some errors, too, in the little prince’s height: in one place he is too tall and in another too short. +And I feel some doubts about the colour of his costume. So I fumble along as best I can, now good, +now bad, and I hope generally fair-to- middling. In certain more important details I shall make +mistakes, also. But that is something that will not be my fault. My friend never explained anything +to me. He thought, perhaps, that I was like himself. But I, alas, do not know how to see sheep +through the walls of boxes. Perhaps I am a little like the grown-ups. I have had to grow old. + +As each day passed I would learn, in our talk, something about the little prince’s planet, his +departure from it, his journey. The information would come very slowly, as it might chance to fall +from his thoughts. It was in this way that I heard, on the third day, about the catastrophe of the +baobabs. + +This time, once more, I had the sheep to thank for it. For the little prince asked me abruptly, as if +seized by a grave doubt, + +“It is true, isn’t it, that sheep eat little bushes?” + +“Yes, that is true.” + +“Ah! I am glad!” + +I did not understand why it was so important that sheep should eat little bushes. But the little +prince added: “Then it follows that they also eat baobabs?” I pointed out to the little prince that +baobabs were not little bushes, but, on the contrary, trees as big as castles; and that even if he +took a whole herd of elephants away with him, the herd would not eat up one single baobab. + +The idea of the herd of elephants made the little prince laugh. “We would have to put them one on +top of the other,” he said. But he made a wise comment: + +“Before they grow so big, the baobabs start out by being little.” + +“That is strictly correct,” I said. “But why do you want the sheep to eat the little baobabs?” + +He answered me at once, “Oh, come, come!”, as if he were speaking of something that was self- +evident. And I was obliged to make a great mental effort to solve this problem, without any +assistance. + +Indeed, as I learned, there were on the planet where the little prince lived, as on all planets, good + + + + + +plants and bad plants. In consequence, there were good seeds from good plants, and bad seeds +from bad plants. But seeds are invisible. They sleep deep in the heart of the earth’s darkness, +until some one among them is seized with the desire to awaken. Then this little seed will stretch +itself and begin, timidly at first, to push a charming little sprig inoffensively upward toward the +sun. If it is only a sprout of radish or the sprig of a rose-bush, one would let it grow wherever it +might wish. But when it is a bad plant, one must destroy it as soon as possible, the very first +instant that one recognises it. + +Now there were some terrible seeds on the planet that was the home of the little prince; and these +were the seeds of the baobab. The soil of that planet was infested with them. A baobab is +something you will never, never be able to get rid of if you attend to it too late. It spreads over the +entire planet. It bores clear through it with its roots. And if the planet is too small, and the +baobabs are too many, they split it in pieces... + +“It is a question of discipline,” the little prince said to me later on. + +“When you’ve finished your own toilet in the morning, then it is time to attend to the toilet of your +planet, just so, with the greatest care. You must see to it that you pull up regularly all the baobabs, +at the very first moment when they can be distinguished from the rosebushes, which they +resemble so closely in their earliest youth. It is very tedious work,” the little prince added, “but +very easy.” And one day he said to me: “You ought to make a beautiful drawing, so that the +children where you live can see exactly how all this is. That would be very useful to them if they +were to travel some day. + + + + +Sometimes,” he added, “there is no harm in putting off a piece of work until another day. But +when it is a matter of baobabs, that always means a catastrophe. + +I knew a planet that was inhabited by a lazy man. He neglected three little bushes... So, as the +little prince described it to me, I have made a drawing of that planet. I do not much like to take +the tone of a moralist. But the danger of the baobabs is so little understood, and such considerable +risks would be run by anyone who might get lost on an asteroid, that for once I am breaking +through my reserve. “Children,” I say plainly, “watch out for the baobabs!” My friends, like +myself, have been skirting this danger for a long time, without ever knowing it; and so it is for +them that I have worked so hard over this drawing. + +The lesson which I pass on by this means is worth all the trouble it has cost me. Perhaps you will +ask me, “Why are there no other drawing in this book as magnificent and impressive as this +drawing of the baobabs?” The reply is simple. I have tried. But with the others I have not been +successful. When I made the drawing of the baobabs I was carried beyond myself by the inspiring +force of urgent necessity. + +Oh, little prince! Bit by bit I came to understand the secrets of your sad little life... For a long time +you had found your only entertainment in the quiet pleasure of looking at the sunset. + +I learned that new detail on the morning of the fourth day, when you said to me: + +“I am very fond of sunsets. Come, let us go look at a sunset now.” + +“But we must wait,” I said. + +“Wait? For what?” + +“For the sunset. We must wait until it is time.” + +At first you seemed to be very much surprised. And then you laughed to yourself. You said to me: +“I am always thinking that I am at home!” + +Just so. Everybody knows that when it is noon in the United States the sun is setting over France. +If you could fly to France in one minute, you could go straight into the sunset, right from noon. +Unfortunately, France is too far away for that. But on your tiny planet, my little prince, all you +need do is move your chair a few steps. You can see the day end and the twilight falling whenever +you like... + +“One day,” you said to me, “I saw the sunset forty-four times!” + +And a little later you added: “You know, one loves the sunset, when one is so sad...” “Were you so +sad, then?” I asked, “on the day of the forty-four sunsets?” + +But the little prince made no reply. + +On the fifth day, again, as always, it was thanks to the sheep, the secret of the little prince’s life +was revealed to me. + +Abruptly, without anything to lead up to it, and as if the question had been born of long and silent +meditation on his problem, he demanded: “A sheep; if it eats little bushes, does it eat flowers, +too?” + + + + +“A sheep,” I answered, “eats anything it finds in its reach.” + + + +“Even flowers that have thorns?” + +“Yes, even flowers that have thorns.” + +“Then the thorns, what use are they?” I did not know. + +At that moment I was very busy trying to unscrew a bolt that had got stuck in my engine. I was +very much worried, for it was becoming clear to me that the breakdown of my plane was +extremely serious. And I had so little drinking water left that I had to fear for the worst. + +“The thorns, what use are they?” + +The little prince never let go of a question, once he had asked it. As for me, I was upset over that +bolt. And I answered with the first thing that came into my head: “The thorns are of no use at all. +Flowers have thorns just for spite!” + +“Oh!” There was a moment of complete silence. + +Then the little prince flashed back at me, with a kind of resentfulness: “I don’t believe you! +Flowers are weak creatures. They are naive. They reassure themselves as best they can. They +believe that their thorns are terrible weapons...” + +I did not answer. At that instant I was saying to myself: “If this bolt still won’t turn, I am going to +knock it out with the hammer.” + +Again the little prince disturbed my thoughts. “And you actually believe that the flowers...” + +“Oh, no!” I cried. “No, no no! I don’t believe anything. I answered you with the first thing that +came into my head. Don’t you see, I am very busy with matters of consequence!” + +He stared at me, thunderstruck. “Matters of consequence!” + +He looked at me there, with my hammer in my hand, my fingers black with engine grease, bending +down over an object which seemed to him extremely ugly... + +“You talk just like the grown-ups!” That made me a little ashamed. But he went on, relentlessly: +“You mix everything up together... You confuse everything...” + +He was really very angry. He tossed his golden curls in the breeze. + +“I know a planet where there is a certain red-faced gentleman. He has never smelled a flower. He +has never looked at a star. He has never loved any one. He has never done anything in his life but +add up figures. And all day he says over and over, just like you: ‘I am busy with matters of +consequence!’ And that makes him swell up with pride. + +“But he is not a man, he is a mushroom!” + + + +“A what?” + + + +“A mushroom!” The little prince was now white with rage. “The flowers have been growing thorns +for millions of years. For millions of years the sheep have been eating them just the same. And is +it not a matter of consequence to try to understand why the flowers go to so much trouble to grow + + + + +' ' + +I \ + + + + +thorns, which are never of any use to them? Is the warfare between the sheep and the flowers not +important? Is this not of more consequence than a fat red-faced gentleman’s sums? And if I know, +I, myself, one flower which is unique in the world, which grows nowhere but on my planet, but +which one little sheep can destroy in a single bite some morning, without even noticing what he is +doing, Oh! You think that is not important! His face turned from white to red as he continued: “If +some one loves a flower, of which just one single blossom grows in all the millions and millions of +stars, it is enough to make him happy just to look at the stars. + +He can say to himself, ‘Somewhere, my flower is there...’ But if the sheep eats the flower, in one +moment all his stars will be darkened... And you think that is not important!” + +He could not say anything more. His words were choked by sobbing. The night had fallen. I had +let my tools drop from my hands. Of what moment now was my hammer, my bolt, or thirst, or +death? On one star, one planet, my planet, the Earth, there was a little prince to be comforted. I +took him in my arms, and rocked him. I said to him: “The flower that you love is not in danger. I +will draw you a muzzle for your sheep. I will draw you a railing to put around your flower. I will...” + +I did not know what to say to him. I felt awkward and blundering. I did not know how I could reach +him, where I could overtake him and go on hand in hand with him once more. + +It is such a secret place, the land of tears. + +I soon learned to know this flower better. On the little prince’s planet the flowers had always been +very simple. They had only one ring of petals; they took up no room at all; they were a trouble to +nobody. One morning they would appear in the grass, and by night they would have faded +peacefully away. But one day, from a seed blown from no one knew where, a new flower had come +up; and the little prince had watched very closely over this small sprout which was not like any +other small sprouts on his planet. + +It might, you see, have been a new kind of baobab. The shrub soon stopped growing, and began to +get ready to produce a flower. The little prince, who was present at the first appearance of a huge +bud, felt at once that some sort of miraculous apparition must emerge from it. But the flower was +not satisfied to complete the preparations for her beauty in the shelter of her green chamber. She + + + +chose her colours with the greatest care. She adjusted her petals one by one. She did not wish to +go out into the world all rumpled, like the field poppies. It was only in the full radiance of her +beauty that she wished to appear. Oh, yes! She was a coquettish creature! And her mysterious +adornment lasted for days and days. Then one morning, exactly at sunrise, she suddenly showed +herself. And, after working with all this painstaking precision, she yawned and said: “Ah! I am +scarcely awake. I beg that you will excuse me. My petals are still all disarranged...” But the little +prince could not restrain his admiration: + +“Oh! How beautiful you are!” + +“Am I not?” the flower responded, sweetly. “And I was born at the same moment as the sun...” + +The little prince could guess easily enough that she was not any too modest, but how moving, and +exciting she was! + +“I think it is time for breakfast,” she added an instant later. “If you would have the kindness to +think of my needs” And the little prince, completely abashed, went to look for a sprinkling can of +fresh water. + +So, he tended the flower. So, too, she began very quickly to torment him with her vanity, which +was, if the truth be known, a little difficult to deal with. + +One day, for instance, when she was speaking of her four thorns, she said to the little prince: “Let +the tigers come with their claws!” + +“There are no tigers on my planet,” the little prince objected. “And, anyway, tigers do not eat +weeds.” + +“I am not a weed,” the flower replied, sweetly. “Please excuse me...” “I am not at all afraid of +tigers,” she went on, “but I have a horror of drafts. I suppose you wouldn’t screen for me?" + +“A horror of drafts, that is bad luck, for a plant,” remarked the little prince, and added to himself, +“This flower is a very complex creature...” + +“At night I want you to put me under a glass globe. It is very cold where you live. In the place I +came from...” But she interrupted herself at that point. She had come in the form of a seed. She +could not have known anything of any other worlds. + +Embarrassed over having let herself be caught on the verge of such an untruth, she coughed two +or three times, in order to put the little prince in the wrong. + +“The screen?” + +“I was just going to look for it when you spoke to me...” + +Then she forced her cough a little more so that he should suffer from remorse just the same. So +the little prince, in spite of all the good will that was inseparable from his love, had soon come to +doubt her. He had taken seriously words which were without importance, and it made him very +unhappy. + +“I ought not to have listened to her,” he confided to me one day. + +“One never ought to listen to the flowers. One should simply look at them and breathe their + + + + +fragrance. Mine perfumed all my planet. But I did not know how to take pleasure in all her grace. +This tale of claws, which disturbed me so much, should only have filled my heart with tenderness +and pity.” + +And he continued his confidences: “The fact is that I did not know how to understand anything! I +ought to have judged by deeds and not by words. She cast her fragrance and her radiance over +me. I ought never to have run away from her... I ought to have guessed all the affection that lay +behind her poor little stratagems. Flowers are so inconsistent! But I was too young to know how to +love her...” + +I believe that for his escape he took advantage of the migration of a flock of wild birds. On the +morning of his departure he put his planet in perfect order. He carefully cleaned out his active +volcanoes. He possessed two active volcanoes; and they were very convenient for heating his +breakfast in the morning. He also had one volcano that was extinct. But, as he said, “One never +knows!” So he cleaned out the extinct volcano, too. If they are well cleaned out, volcanoes burn +slowly and steadily, without any eruptions. Volcanic eruptions are like fires in a chimney. + +On our earth we are obviously much too small to clean out our volcanoes. That is why they bring +no end of trouble upon us. The little prince also pulled up, with a certain sense of dejection, the +last little shoots of the baobabs. He believed that he would never want to return. But on this last +morning all these familiar tasks seemed very precious to him. And when he watered the flower for +the last time, and prepared to place her under the shelter of her glass globe, he realised that he +was very close to tears. “Goodbye,” he said to the flower. But she made no answer. “Goodbye,” +he said again. The flower coughed. But it was not because she had a cold. + +“I have been silly,” she said to him, at last. “I ask your forgiveness. Try to be happy...” He was +surprised by this absence of reproaches. He stood there all bewildered, the glass globe held +arrested in mid-air. He did not understand this quiet sweetness. + +“Of course I love you,” the flower said to him. “It is my fault that you have not known it all the +while. That is of no importance. But you, you have been just as foolish as I. Try to be happy... let +the glass globe be. I don’t want it any more.” + +“But the wind...” “My cold is not so bad as all that... the cool night air will do me good. I am a +flower.” + +“But the animals...” “Well, I must endure the presence of two or three caterpillars if I wish to +become acquainted with the butterflies. It seems that they are very beautiful. And if not the +butterflies and the caterpillars who will call upon me? You will be far away... as for the large +animals, I am not at all afraid of any of them. I have my claws.” + +And, naively, she showed her four thorns. + +Then she added: “Don’t linger like this. You have decided to go away. Now go!” + +For she did not want him to see her crying. She was such a proud flower... + + + +He found himself in the neighbourhood of the asteroids 325, 326, 327, 328, 329, and 330. He +began, therefore, by visiting them, in order to add to his knowledge. The first of them was +inhabited by a king. Clad in royal purple and ermine, he was seated upon a throne, which was at + + + + +the same time both simple and majestic. + + + +“Ah! Here is a subject,” exclaimed the king, when he saw the little prince coming. And the little +prince asked himself: “How could he recognise me when he had never seen me before?” + +He did not know how the world is simplified for kings. To them, all men are subjects. “Approach, +so that I may see you better,” said the king, who felt consumingly proud of being at last a king +over somebody. + +The little prince looked everywhere to find a place to sit down; but the entire planet was crammed +and obstructed by the king’s magnificent ermine robe. So he remained standing upright, and, since +he was tired, he yawned. + +“It is contrary to etiquette to yawn in the presence of a king,” the monarch said to him. “I forbid +you to do so.” + +“I can’t help it. I can’t stop myself,” replied the little prince, thoroughly embarrassed. + +“I have come on a long journey, and I have had no sleep...” + +“Ah, then,” the king said. “I order you to yawn. It is years since I have seen anyone yawning. +Yawns, to me, are objects of curiosity. Come, now! Yawn again! It is an order.” + +“That frightens me... I cannot, any more...” murmured the little prince, now completely abashed. + +“Hum! Hum!” replied the king. “Then I... I order you sometimes to yawn and sometimes to” He +sputtered a little, and seemed vexed. For what the king fundamentally insisted upon was that his +authority should be respected. He tolerated no disobedience. He was an absolute monarch. But, +because he was a very good man, he made his orders reasonable. + +“If I ordered a general,” he would say, by way of example, “if I ordered a general to change +himself into a sea bird, and if the general did not obey me, that would not be the fault of the +general. It would be my fault.” + +“May I sit down?” came now a timid inquiry from the little prince. “I order you to do so,” the king +answered him, and majestically gathered in a fold of his ermine mantle. But the little prince was +wondering... The planet was tiny. Over what could this king really rule? + +“Sire,” he said to him, “I beg that you will excuse my asking you a question” + +“I order you to ask me a question,” the king hastened to assure him. “Sire, over what do you +rule?” “Over everything,” said the king, with magnificent simplicity. + +“Over everything?” The king made a gesture, which took in his planet, the other planets, and all +the stars. “Over all that?” asked the little prince. “Over all that,” the king answered. For his rule +was not only absolute: it was also universal. “And the stars obey you?” “Certainly they do,” the +king said. “They obey instantly. I do not permit insubordination.” + +Such power was a thing for the little prince to marvel at. If he had been master of such complete +authority, he would have been able to watch the sunset, not forty-four times in one day, but +seventy-two, or even a hundred, or even two hundred times, with out ever having to move his +chair. And because he felt a bit sad as he remembered his little planet, which he had forsaken, he +plucked up his courage to ask the king a favour: + + + + + +“I should like to see a sunset... do me that kindness... Order the sun to set...” + +“If I ordered a general to fly from one flower to another like a butterfly, or to write a tragic +drama, or to change himself into a sea bird, and if the general did not carry out the order that he +had received, which one of us would be in the wrong?” the king demanded. “The general, or +myself?” + +“You,” said the little prince firmly. + +“Exactly. One much require from each one the duty which each one can perform,” the king went +on. “Accepted authority rests first of all on reason. If you ordered your people to go and throw +themselves into the sea, they would rise up in revolution. I have the right to require obedience +because my orders are reasonable.” + +“Then my sunset?” the little prince reminded him: for he never forgot a question once he had +asked it. + +“You shall have your sunset. I shall command it. But, according to my science of government, I +shall wait until conditions are favourable.” + +“When will that be?” inquired the little prince. “Hum! Hum!” replied the king; and before saying +anything else he consulted a bulky almanac. “Hum! Hum! That will be about... about... that will be +this evening about twenty minutes to eight. And you will see how well I am obeyed.” + +The little prince yawned. He was regretting his lost sunset. And then, too, he was already +beginning to be a little bored. “I have nothing more to do here,” he said to the king. “So I shall set +out on my way again.” “Do not go,” said the king, who was very proud of having a subject. “Do +not go. I will make you a Minister!” “Minister of what?” “Minster of...of Justice!” “But there is +nobody here to judge!” “We do not know that,” the king said to him. “I have not yet made a +complete tour of my kingdom. I am very old. There is no room here for a carriage. And it tires me +to walk.” “Oh, but I have looked already!” said the little prince, turning around to give one more +glance to the other side of the planet. + +On that side, as on this, there was nobody at all... “Then you shall judge yourself,” the king +answered, “that is the most difficult thing of all. It is much more difficult to j udge oneself than to +j udge others. If you succeed in j udging yourself rightly, then you are indeed a man of true +wisdom.” + + + +“Yes,” said the little prince, “but I can judge myself anywhere. I do not need to live on this planet. +“Hum! Hum!” said the king. “I have good reason to believe that somewhere on my planet there is +an old rat. I hear him at night. You can judge this old rat. From time to time you will condemn him +to death. Thus his life will depend on your j ustice. But you will pardon him on each occasion; for +he must be treated thriftily. He is the only one we have.” + +“I,” replied the little prince, “do not like to condemn anyone to death. And now I think I will go on +my way.” “No,” said the king. But the little prince, having now completed his preparations for +departure, had no wish to grieve the old monarch. “If Your Majesty wishes to be promptly +obeyed,” he said, “he should be able to give me a reasonable order. He should be able, for +example, to order me to be gone by the end of one minute. It seems to me that conditions are +favourable...” As the king made no answer, the little prince hesitated a moment. + +Then, with a sigh, he took his leave. “I made you my Ambassador,” the king called out, hastily. + +He had a magnificent air of authority. + +“The grown-ups are very strange,” the little prince said to himself, as he continued on his journey. +The second planet was inhabited by a conceited man. + + + + + + + + + + +“Ah! Ah! I am about to receive a visit from an admirer!” he exclaimed from afar, when he first +saw the little prince coming. For, to conceited men, all other men are admirers. + +“Good morning,” said the little prince. “That is a queer hat you are wearing.” + +“It is a hat for salutes,” the conceited man replied. “It is to raise in salute when people acclaim +me. Unfortunately, nobody at all ever passes this way.” + +“Yes?” said the little prince, who did not understand what the conceited man was talking about. + +“Clap your hands, one against the other,” the conceited man now directed him. The little prince +clapped his hands. The conceited man raised his hat in a modest salute. “This is more entertaining +than the visit to the king,” the little prince said to himself. And he began again to clap his hands, +one against the other. The conceited man against raised his hat in salute. After five minutes of + + + +this exercise the little prince grew tired of the game’s monotony. “And what should one do to +make the hat come down?” he asked. But the conceited man did not hear him. Conceited people +never hear anything but praise. + +“Do you really admire me very much?” he demanded of the little prince. “What does that mean, +‘admire’?” + +“To admire means that you regard me as the handsomest, the best-dressed, the richest, and the +most intelligent man on this planet.” “But you are the only man on your planet!” “Do me this +kindness. Admire me just the same.” + +“I admire you,” said the little prince, shrugging his shoulders slightly, “but what is there in that to +interest you so much?” + +And the little prince went away. “The grown-ups are certainly very odd,” he said to himself, as he +continued on his journey. + +The next planet was inhabited by a tippler. + + + + +This was a very short visit, but it plunged the little prince into deep dejection. “What are you +doing there?” he said to the tippler, whom he found settled down in silence before a collection of +empty bottles and also a collection of full bottles. + +“I am drinking,” replied the tippler, with a lugubrious air. + +“Why are you drinking?” demanded the little prince. + +“So that I may forget,” replied the tippler. “Forget what?” inquired the little prince, who already +was sorry for him. + +“Forget that I am ashamed,” the tippler confessed, hanging his head. + +“Ashamed of what?” insisted the little prince, who wanted to help him. + +“Ashamed of drinking!” The tippler brought his speech to an end, and shut himself up in an +impregnable silence. + +And the little prince went away, puzzled. “The grown-ups are certainly very, very odd,” he said to +himself, as he continued on his journey. + +The fourth planet belonged to a businessman. + +This man was so much occupied that he did not even raise his head at the little prince’s arrival. + + + +“Good morning,” the little prince said to him. “Your cigarette has gone out.” + + + +“Three and two make five. Five and seven make twelve. Twelve and three make fifteen. Good +morning. Fifteen and seven make twenty-two. Twenty-two and six make twenty-eight. I haven’t +time to light it again. Twenty-six and five make thirty-one. Phew ! Then that makes five-hundred- +and-one-million, six-hundred-twenty-two-thousand, seven-hundred-thirty-one.” + +“Five hundred million what?” asked the little prince. “Eh? Are you still there? Five-hundred-and- +one million, I can’t stop... I have so much to do! I am concerned with matters of consequence. I +don’t amuse myself with balderdash. Two and five make seven...” + +“Five-hundred-and-one million what?” repeated the little prince, who never in his life had let go of +a question once he had asked it. + +The businessman raised his head. “During the fifty-four years that I have inhabited this planet, I +have been disturbed only three times. The first time was twenty-two years ago, when some giddy +goose fell from goodness knows where. He made the most frightful noise that resounded all over +the place, and I made four mistakes in my addition. The second time, eleven years ago, I was +disturbed by an attack of rheumatism. I don’t get enough exercise. I have no time for loafing. The +third time, well, this is it! I was saying, then, five -hundred-and-one millions” + +“Millions of what?” The businessman suddenly realised that there was no hope of being left in +peace until he answered this question. + +“Millions of those little objects,” he said, “which one sometimes sees in the sky.” “Flies?” “Oh, +no. Little glittering objects.” “Bees?” “Oh, no. Little golden objects that set lazy men to idle +dreaming. As for me, I am concerned with matters of consequence. There is no time for idle +dreaming in my life.” “Ah! You mean the stars?” “Yes, that’s it. The stars.” “And what do you do +with five-hundred millions of stars?” “Five-hundred-and-one million, six-hundred-twenty-two +thousand, seven-hundred-thirty-one. I am concerned with matters of consequence: I am +accurate.” + +“And what do you do with these stars?” “What do I do with them?” “Yes.” “Nothing. I own them.” +“You own the stars?” “Yes.” “But I have already seen a king who...” “Kings do not own, they +reign over. It is a very different matter.” + +“And what good does it do you to own the stars?” “It does me the good of making me rich.” + +“And what good does it do you to be rich?” + +“It makes it possible for me to buy more stars, if any are ever discovered.” + +“This man,” the little prince said to himself, “reasons a little like my poor tippler...” Nevertheless, +he still had some more questions. “How is it possible for one to own the stars?” “To whom do they +belong?” the businessman retorted, peevishly. “I don’t know. To nobody.” “Then they belong to +me, because I was the first person to think of it.” “Is that all that is necessary?” “Certainly. + +When you find a diamond that belongs to nobody, it is yours. When you discover an island that +belongs to nobody, it is yours. When you get an idea before any one else, you take out a patent on +it: it is yours. So with me: I own the stars, because nobody else before me ever thought of owning +them.” + +“Yes, that is true,” said the little prince. “And what do you do with them?” + + + + +“I administer them,” replied the businessman. “I count them and recount them. It is difficult. But I +am a man who is naturally interested in matters of consequence.” + +The little prince was still not satisfied. “If I owned a silk scarf,” he said, “I could put it around my +neck and take it away with me. If I owned a flower, I could pluck that flower and take it away with +me. But you cannot pluck the stars from heaven...” + +“No. But I can put them in the bank.” “Whatever does that mean?” “That means that I write the +number of my stars on a little paper. And then I put this paper in a drawer and lock it with a key.” + +“And that is all?” + +“That is enough,” said the businessman. + +“It is entertaining,” thought the little prince. “It is rather poetic. But it is of no great +consequence.” On matters of consequence, the little prince had ideas, which were very different +from those of the grown-ups. + +“I myself own a flower,” he continued his conversation with the businessman, “which I water +every day. I own three volcanoes, which I clean out every week (for I also clean out the one that is +extinct; one never knows). It is of some use to my volcanoes, and it is of some use to my flower, +that I own them. But you are of no use to the stars...” + +The businessman opened his mouth, but he found nothing to say in answer. And the little prince +went away. “The grown-ups are certainly altogether extraordinary,” he said simply, talking to +himself as he continued on his journey. + +The fifth planet was very strange. It was the smallest of all. There was just enough room on it for +a street lamp and a lamplighter. + + + + +The little prince was not able to reach any explanation of the use of a street lamp and a +lamplighter, somewhere in the heavens, on a planet, which had no people, and not one house. + +But he said to himself, nevertheless: “It may well be that this man is absurd. But he is not so +absurd as the king, the conceited man, the businessman, and the tippler. For at least his work has + + + +some meaning. When he lights his street lamp, it is as if he brought one more star to life, or one +flower. When he puts out his lamp, he sends the flower, or the star, to sleep. That is a beautiful +occupation. And since it is beautiful, it is truly useful.” + +When he arrived on the planet he respectfully saluted the lamplighter. + +“Good morning. Why have you just put out your lamp?” + +“Those are the orders,” replied the lamplighter. “Good morning.” + +“What are the orders?” + +“The orders are that I put out my lamp. Good evening.” And he lighted his lamp again. “But why +have you just lighted it again?” + +“Those are the orders,” replied the lamplighter. + +“I do not understand,” said the little prince. + +“There is nothing to understand,” said the lamplighter. “Orders are orders. Good morning.” And +he put out his lamp. + +Then he mopped his forehead with a handkerchief decorated with red squares. + +“I follow a terrible profession. In the old days it was reasonable. I put the lamp out in the morning, +and in the evening I lighted it again. I had the rest of the day for relaxation and the rest of the +night for sleep.” + +“And the orders have been changed since that time?” + +“The orders have not been changed,” said the lamplighter. “That is the tragedy! From year to +year the planet has turned more rapidly and the orders have not been changed!” + +“Then what?” asked the little prince. + +“Then the planet now makes a complete turn every minute, and I no longer have a single second +for repose. Once every minute I have to light my lamp and put it out!” + +“That is very funny! A day lasts only one minute, here where you live!” + +“It is not funny at all!” said the lamplighter. “While we have been talking together a month has +gone by.” + +“A month?” + + + +“Yes, a month. Thirty minutes. Thirty days. Good evening.” And he lighted his lamp again. As the +little prince watched him, he felt that he loved this lamplighter who was so faithful to his orders. +He remembered the sunsets, which he himself had gone to seek, in other days, merely by pulling +up his chair; and he wanted to help his friend. + +“You know,” he said, “I can tell you a way you can rest whenever you want to...” + +“I always want to rest,” said the lamplighter. For it is possible for a man to be faithful and lazy at +the same time. + + + + +The little prince went on with his explanation: “Your planet is so small that three strides will take +you all the way around it. To be always in the sunshine, you need only walk along rather slowly. +When you want to rest, you will walk and the day will last as long as you like.” + +“That doesn’t do me much good,” said the lamplighter. “The one thing I love in life is to sleep.” + +“Then you’re unlucky,” said the little prince. + +“I am unlucky,” said the lamplighter. “Good morning.” And he put out his lamp. + +“That man,” said the little prince to himself, as he continued farther on his journey, “that man +would be scorned by all the others: by the king, by the conceited man, by the tippler, by the +businessman. Nevertheless he is the only one of them all who does not seem to me ridiculous. +Perhaps that is because he is thinking of something else besides himself.” + +He breathed a sigh of regret, and said to himself, again: “That man is the only one of them all +whom I could have made my friend. But his planet is indeed too small. There is no room on it for +two people...” What the little prince did not dare confess was that he was sorry most of all to leave +this planet, because it was blest every day with 1440 sunsets! + +The sixth planet was ten times larger than the last one. It was inhabited by an old gentleman who +wrote voluminous books. + + + + +“Oh, look! Here is an explorer!” he exclaimed to himself when he saw the little prince coming. + +The little prince sat down on the table and panted a little. He had already travelled so much and +so far! + +“Where do you come from?” the old gentleman said to him. + +“What is that big book?” said the little prince. “What are you doing?” + +“I am a geographer,” the old gentleman said to him. + +“What is a geographer?” asked the little prince. “A geographer is a scholar who knows the +location of all the seas, rivers, towns, mountains, and deserts.” + +“That is very interesting,” said the little prince. “Here at last is a man who has a real profession!” +And he cast a look around him at the planet of the geographer. + + + +It was the most magnificent and stately planet that he had ever seen. + +“Your planet is very beautiful,” he said. “Has it any oceans?” + +“I couldn’t tell you,” said the geographer. + +“Ah!” The little prince was disappointed. “Has it any mountains?” + +“I couldn’t tell you,” said the geographer. + +“And towns, and rivers, and deserts?” + +“I couldn’t tell you that, either.” + +“But you are a geographer!” + +“Exactly,” the geographer said. “But I am not an explorer. I haven’t a single explorer on my +planet. It is not the geographer who goes out to count the towns, the rivers, the mountains, the +seas, the oceans, and the deserts. The geographer is much too important to go loafing about. He +does not leave his desk. But he receives the explorers in his study. He asks them questions, and +he notes down what they recall of their travels. And if the recollections of any one among them +seem interesting to him, the geographer orders an inquiry into that explorer’s moral character.” + +“Why is that?” + +“Because an explorer who told lies would bring disaster on the books of the geographer. So would +an explorer who drank too much.” + +“Why is that?” asked the little prince. + +“Because intoxicated men see double. Then the geographer would note down two mountains in a +place where there was only one.” + +“I know some one,” said the little prince, “who would make a bad explorer.” + +“That is possible. Then, when the moral character of the explorer is shown to be good, an inquiry +is ordered into his discovery.” + +“One goes to see it?” + +“No. That would be too complicated. But one requires the explorer to furnish proofs. For example, +if the discovery in question is that of a large mountain, one requires that large stones be brought +back from it.” The geographer was suddenly stirred to excitement. “But you come from far away! +You are an explorer! You shall describe your planet to me!” And, having opened his big register, +the geographer sharpened his pencil. The recitals of explorers are put down first in pencil. One +waits until the explorer has furnished proofs, before putting them down in ink. “Well?” said the +geographer expectantly. + +“Oh, where I live,” said the little prince, “it is not very interesting. It is all so small. I have three +volcanoes. Two volcanoes are active and the other is extinct. But one never knows.” + +“One never knows,” said the geographer. + + + +“I have also a flower.” + + + + +“We do not record flowers,” said the geographer. + +“Why is that? The flower is the most beautiful thing on my planet!” + +“We do not record them,” said the geographer, “because they are ephemeral.” + +“What does that mean ‘ephemeral’?” + +“Geographies,” said the geographer, “are the books which, of all books, are most concerned with +matters of consequence. They never become old-fashioned. It is very rarely that a mountain +changes its position. It is very rarely that an ocean empties itself of its waters. We write of eternal +things.” + +“But extinct volcanoes may come to life again,” the little prince interrupted. + +“What does that mean ‘ephemeral’?” + +“Whether volcanoes are extinct or alive, it comes to the same thing for us,” said the geographer. +“The thing that matters to us is the mountain. It does not change.” + +“But what does that mean ‘ephemeral’?” repeated the little prince, who never in his life had let go +of a question, once he had asked it. + +“It means, ‘which is in danger of speedy disappearance.’ “ + +“Is my flower in danger of speedy disappearance?” + +“Certainly it is.” + +“My flower is ephemeral,” the little prince said to himself, “and she has only four thorns to +defend herself against the world. And I have left her on my planet, all alone!” + +That was his first moment of regret. But he took courage once more. “What place would you +advise me to visit now?” he asked. “The planet Earth,” replied the geographer. “It has a good +reputation.” And the little prince went away, thinking of his flower. + + + + +So then the seventh planet was the Earth. + +The Earth is not just an ordinary planet! + +One can count, there 111 kings (not forgetting, to be sure, the Negro kings among them), 7000 +geographers, 900,000 businessmen, 7,500,000 tipplers, 311,000,000 conceited men, that is to say, +about 2,000,000,000 grown-ups. + +To give you an idea of the size of the Earth, I will tell you that before the invention of electricity it +was necessary to maintain, over the whole of the six continents, a veritable army of 462,511 +lamplighters for the street lamps. Seen from a slight distance, that would make a splendid +spectacle. + +The movements of this army would be regulated like those of the ballet in the opera. First would +come the turn of the lamplighters of New Zealand and Australia. Having set their lamps alight, +these would go off to sleep. Next, the lamplighters of China and Siberia would enter for their steps +in the dance, and then they too would be waved back into the wings. After that would come the +turn of the lamplighters of Russia and the Indies; then those of Africa and Europe, then those of +South America; then those of North America. And never would they make a mistake in the order +of their entry upon the stage. It would be magnificent. + +Only the man who was in charge of the single lamp at the North Pole, and his colleague who was +responsible for the single lamp at the South Pole, only these two would live free from toil and +care: they would be busy twice a year. + +When one wishes to play the wit, he sometimes wanders a little from the truth. + +I have not been altogether honest in what I have told you about the lamplighters. And I realise +that I run the risk of giving a false idea of our planet to those who do not know it. + +Men occupy a very small place upon the Earth. If the two billion inhabitants who people its +surface were all to stand upright and somewhat crowded together, as they do for some big public +assembly, they could easily be put into one public square twenty miles long and twenty miles wide. +All humanity could be piled up on a small Pacific islet. + +The grown-ups, to be sure, will not believe you when you tell them that. They imagine that they fill +a great deal of space. They fancy themselves as important as the baobabs. You should advise +them, then, to make their own calculations. They adore figures, and that will please them. But do +not waste your time on this extra task. It is unnecessary. You have, I know, confidence in me. + +When the little prince arrived on the Earth, he was very much surprised not to see any people. He +was beginning to be afraid he had come to the wrong planet, when a coil of gold, the colour of the +moonlight, flashed across the sand. + +“Good evening,” said the little prince courteously. + +“Good evening,” said the snake. + +“What planet is this on which I have come down?” asked the little prince. + +“This is the Earth; this is Africa,” the snake answered. + +“Ah! Then there are no people on the Earth?” + + + + +“This is the desert. There are no people in the desert. The Earth is large,” said the snake. + +The little prince sat down on a stone, and raised his eyes toward the sky. + +“I wonder,” he said, “whether the stars are set alight in heaven so that one day each one of us +may find his own again... Look at my planet. It is right there above us. But how far away it is!” + +“It is beautiful,” the snake said. “What has brought you here?” + +“I have been having some trouble with a flower,” said the little prince. “Ah!” said the snake. And +they were both silent. + +“Where are the men?” the little prince at last took up the conversation again. “It is a little lonely +in the desert...” + +“It is also lonely among men,” the snake said. The little prince gazed at him for a long time. + +“You are a funny animal,” he said at last. “You are no thicker than a finger...” + +“But I am more powerful than the finger of a king,” said the snake. + +The little prince smiled. “You are not very powerful. You haven’t even any feet. You cannot even +travel...” + +“I can carry you farther than any ship could take you,” said the snake. He twined himself around +the little prince’s ankle, like a golden bracelet. + +“Whomever I touch, I send back to the earth from whence he came,” the snake spoke again. “But +you are innocent and true, and you come from a star...” + +The little prince made no reply. “You move me to pity, you are so weak on this Earth made of +granite,” the snake said. “I can help you, some day, if you grow too homesick for your own planet. +I can...” + +“Oh! I understand you very well,” said the little prince. “But why do you always speak in +riddles?” + +“I solve them all,” said the snake. And they were both silent. + +The little prince crossed the desert and met with only one flower. + +It was a flower with three petals, a flower of no account at all. + +“Good morning,” said the little prince. + +“Good morning,” said the flower. + +“Where are the men?” the little prince asked, politely. The flower had once seen a caravan +passing. + +“Men?” she echoed. “I think there are six or seven of them in existence. I saw them, several +years ago. But one never knows where to find them. The wind blows them away. They have no +roots, and that makes their life very difficult.” + + + +“Goodbye,” said the little prince. + + + + +“Goodbye,” said the flower. + + + +After that, the little prince climbed a high mountain. The only mountains he had ever known were +the three volcanoes, which came up to his knees. And he used the extinct volcano as a footstool. + +“From a mountain as high as this one,” he said to himself, “I shall be able to see the whole planet +at one glance, and all the people...” But he saw nothing, save peaks of rock that were sharpened +like needles. + + + + +“Good morning,” he said courteously. + +“Good morning...Good morning...Good morning,” answered the echo. + +“Who are you?” said the little prince. + +“Who are you...Who are you...Who are you?” answered the echo. + +“Be my friends. I am all alone,” he said. + +“I am all alone...all alone. ..all alone,” answered the echo. + +“What a queer planet!” he thought. “It is altogether dry, and altogether pointed, and altogether +harsh and forbidding. And the people have no imagination. They repeat whatever one says to +them... On my planet I had a flower; she always was the first to speak...” + +But it happened that after walking for a long time through sand, and rocks, and snow, the little +prince at last came upon a road. And all roads lead to the abodes of men. + +“Good morning,” he said. He was standing before a garden, all a-bloom with roses. + +“Good morning,” said the roses. + +The little prince gazed at them. They all looked like his flower. + +“Who are you?” he demanded, thunderstruck. + +“We are roses,” the roses said. And he was overcome with sadness. His flower had told him that +she was the only one of her kind in all the universe. And here were five thousand of them, all +alike, in one single garden! + +“She would be very much annoyed,” he said to himself, “if she should see that... she would cough + + + +most dreadfully, and she would pretend that she was dying, to avoid being laughed at. And I +should be obliged to pretend that I was nursing her back to life, for if I did not do that, to humble +myself also, she would really allow herself to die...” + +Then he went on with his reflections: “I thought that I was rich, with a flower that was unique in all +the world; and all I had was a common rose. A common rose, and three volcanoes that come up to +my knees — and one of them perhaps extinct forever... that doesn’t make me a very great +prince...” And he lay down in the grass and cried. + +It was then that the fox appeared. + +“Good morning,” said the fox. + +“Good morning,” the little prince responded politely, although when he turned around he saw +nothing. + +“I am right here,” the voice said, “under the apple tree.” “ + +Who are you?” asked the little prince, and added, “You are very pretty to look at.” + +“I am a fox,” said the fox. + +“Come and play with me,” proposed the little prince. + +“I am so unhappy.” “I cannot play with you,” the fox said. “I am not tamed.” + +“Ah! Please excuse me,” said the little prince. But, after some thought, he added: “What does +that mean, ‘tame’?” + + + + +“You do not live here,” said the fox. “What is it that you are looking for?” + +“I am looking for men,” said the little prince. “What does that mean, ‘tame’?” + +“Men,” said the fox. “They have guns, and they hunt. It is very disturbing. They also raise +chickens. These are their only interests. Are you looking for chickens?” + +“No,” said the little prince. “I am looking for friends. What does that mean, ‘tame’?” + +“It is an act too often neglected,” said the fox. It means to establish ties.” + + + +“‘To establish ties’?” + + + +“Just that,” said the fox. “To me, you are still nothing more than a little boy who is just like a +hundred thousand other little boys. And I have no need of you. And you, on your part, have no +need of me. To you, I am nothing more than a fox like a hundred thousand other foxes. But if you +tame me, then we shall need each other. To me, you will be unique in all the world. To you, I shall +be unique in all the world...” + +“I am beginning to understand,” said the little prince. “There is a flower... I think that she has +tamed me...” + +“It is possible,” said the fox. “On the Earth one sees all sorts of things.” + +“Oh, but this is not on the Earth!” said the little prince. The fox seemed perplexed, and very +curious. + +“On another planet?” + +“Yes.” + +“Are there hunters on this planet?” + +“No.” + +“Ah, that is interesting! Are there chickens?” + +“No.” + +“Nothing is perfect,” sighed the fox. But he came back to his idea. “My life is very monotonous,” +the fox said. “I hunt chickens; men hunt me. All the chickens are just alike, and all the men are +just alike. And, in consequence, I am a little bored. But if you tame me, it will be as if the sun +came to shine on my life. I shall know the sound of a step that will be different from all the others. +Other steps send me hurrying back underneath the ground. Yours will call me, like music, out of +my burrow. And then look: you see the grain-fields down yonder? I do not eat bread. Wheat is of +no use to me. The wheat fields have nothing to say to me. And that is sad. But you have hair that +is the colour of gold. Think how wonderful that will be when you have tamed me! The grain, which +is also golden, will bring me back the thought of you. And I shall love to listen to the wind in the +wheat...” The fox gazed at the little prince, for a long time. “Please, tame me!” he said. + +“I want to, very much,” the little prince replied. “But I have not much time. I have friends to +discover, and a great many things to understand.” + +“One only understands the things that one tames,” said the fox. “Men have no more time to +understand anything. They buy things all ready-made at the shops. But there is no shop anywhere +where one can buy friendship, and so men have no friends any more. If you want a friend, tame +me...” + +“What must I do, to tame you?” asked the little prince. + +“You must be very patient,” replied the fox. “First you will sit down at a little distance from me, +like that, in the grass. I shall look at you out of the corner of my eye, and you will say nothing. +Words are the source of misunderstandings. But you will sit a little closer to me, every day...” + + + + +The next day the little prince came back. + + + +“It would have been better to come back at the same hour,” said the fox. “If, for example, you +come at four o’clock in the afternoon, then at three o’clock I shall begin to be happy. I shall feel +happier and happier as the hour advances. At four o’clock, I shall already be worrying and +jumping about. I shall show you how happy I am! But if you come at just any time, I shall never +know at what hour my heart is to be ready to greet you... One must observe the proper rites...” + +“What is a rite?” asked the little prince. + +“Those also are actions too often neglected,” said the fox. “They are what make one day +different from other days, one hour from other hours. There is a rite, for example, among my +hunters. Every Thursday they dance with the village girls. So Thursday is a wonderful day for me! +I can take a walk as far as the vineyards. But if the hunters danced at just any time, every day +would be like every other day, and I should never have any vacation at all.” + +So the little prince tamed the fox. And when the hour of his departure drew near... + +“Ah,” said the fox, “I shall cry.” + +“It is your own fault,” said the little prince. “I never wished you any sort of harm; but you wanted +me to tame you...” + +“Yes, that is so,” said the fox. + +“But now you are going to cry!” said the little prince. + +“Yes, that is so,” said the fox. + +“Then it has done you no good at all!” + +“It has done me good,” said the fox, “because of the colour of the wheat fields.” And then he +added: “Go and look again at the roses. You will understand now that yours is unique in all the +world. Then come back to say goodbye to me, and I will make you a present of a secret.” + +The little prince went away, to look again at the roses. “You are not at all like my rose,” he said. +“As yet you are nothing. No one has tamed you, and you have tamed no one. You are like my fox +when I first knew him. He was only a fox like a hundred thousand other foxes. But I have made +him my friend, and now he is unique in all the world.” And the roses were very much embarrassed. +“You are beautiful, but you are empty,” he went on. “One could not die for you. To be sure, an +ordinary passer-by would think that my rose looked just like you, the rose that belongs to me. But +in herself alone she is more important than all the hundreds of you other roses: because it is she +that I have watered; because it is she that I have put under the glass globe; because it is she that +I have sheltered behind the screen; because it is for her that I have killed the caterpillars (except +the two or three that we saved to become butterflies); because it is she that I have listened to, +when she grumbled, or boasted, or even sometimes when she said nothing. Because she is my +rose. + +And he went back to meet the fox. “Goodbye,” he said. + +“Goodbye,” said the fox. “And now here is my secret, a very simple secret: It is only with the +heart that one can see rightly; what is essential is invisible to the eye.” + + + + +“What is essential is invisible to the eye,” the little prince repeated, so that he would be sure to +remember. + +“It is the time you have wasted for your rose that makes your rose so important.” + +“It is the time I have wasted for my rose...” said the little prince, so that he would be sure to +remember. + +“Men have forgotten this truth,” said the fox. “But you must not forget it. You become +responsible, forever, for what you have tamed. You are responsible for your rose...” + +“I am responsible for my rose,” the little prince repeated, so that he would be sure to remember. + +“Good morning,” said the little prince. + +“Good morning,” said the railway switchman. + +“What do you do here?” the little prince asked. + +“I sort out travellers, in bundles of a thousand,” said the switchman. “I send off the trains that +carry them; now to the right, now to the left.” And a brilliantly lighted express train shook the +switchman’s cabin as it rushed by with a roar like thunder. + +“They are in a great hurry,” said the little prince. “What are they looking for?” + +“Not even the locomotive engineer knows that,” said the switchman. And a second brilliantly +lighted express thundered by, in the opposite direction. + +“Are they coming back already?” demanded the little prince. “These are not the same ones,” said +the switchman. “It is an exchange.” + +“Were they not satisfied where they were?” asked the little prince. + +“No one is ever satisfied where he is,” said the switchman. And they heard the roaring thunder of +a third brilliantly lighted express. + +“Are they pursuing the first travellers?” demanded the little prince. + +“They are pursuing nothing at all,” said the switchman. “They are asleep in there, or if they are +not asleep they are yawning. Only the children are flattening their noses against the +windowpanes.” + +“Only the children know what they are looking for,” said the little prince. + +“They waste their time over a rag doll and it becomes very important to them; and if anybody +takes it away from them, they cry...” “They are lucky,” the switchman said. + +“Good morning,” said the little prince. + +“Good morning,” said the merchant. + +This was a merchant who sold pills that had been invented to quench thirst. You need only swallow +one pill a week, and you would feel no need of anything to drink. + + + + +“Why are you selling those?” asked the little prince. + + + +“Because they save a tremendous amount of time,” said the merchant. “Computations have been +made by experts. With these pills, you save fifty-three minutes in every week.” + +“And what do I do with those fifty-three minutes?” + +“Anything you like...” + +“As for me,” said the little prince to himself, “if I had fifty-three minutes to spend as I liked, I +should walk at my leisure toward a spring of fresh water.” + +It was now the eighth day since I had had my accident in the desert, and I had listened to the story +of the merchant as I was drinking the last drop of my water supply. + +“Ah,” I said to the little prince, “these memories of yours are very charming; but I have not yet +succeeded in repairing my plane; I have nothing more to drink; and I, too, should be very happy if +I could walk at my leisure toward a spring of fresh water!” + +“My friend the fox...” the little prince said to me. + +“My dear little man, this is no longer a matter that has anything to do with the fox!” + +“Why not?” + +“Because I am about to die of thirst...” + +He did not follow my reasoning, and he answered me: “It is a good thing to have had a friend, +even if one is about to die. I, for instance, am very glad to have had a fox as a friend...” + +“He has no way of guessing the danger,” I said to myself. “He has never been either hungry or +thirsty. A little sunshine is all he needs...” + +But he looked at me steadily, and replied to my thought: “I am thirsty, too. Let us look for a +well...” I made a gesture of weariness. It is absurd to look for a well, at random, in the immensity +of the desert. But nevertheless we started walking. + +When we had trudged along for several hours, in silence, the darkness fell, and the stars began to +come out. Thirst had made me a little feverish, and I looked at them as if I were in a dream. The +little prince’s last words came reeling back into my memory: “Then you are thirsty, too?” I +demanded. But he did not reply to my question. He merely said to me: “Water may also be good +for the heart...” + +I did not understand this answer, but I said nothing. I knew very well that it was impossible to +cross-examine him. He was tired. He sat down. I sat down beside him. And, after a little silence, +he spoke again: “The stars are beautiful, because of a flower that cannot be seen.” + +I replied, “Yes, that is so.” And, without saying anything more, I looked across the ridges of sand +that were stretched out before us in the moonlight. + +“The desert is beautiful,” the little prince added. + +And that was true. I have always loved the desert. One sits down on a desert sand dune, sees + + + + + +nothing, hears nothing. Yet through the silence something throbs, and gleams... + +“What makes the desert beautiful,” said the little prince, “is that somewhere it hides a well...” + +I was astonished by a sudden understanding of that mysterious radiation of the sands. When I was +a little boy I lived in an old house, and legend told us that a treasure was buried there. To be sure, +no one had ever known how to find it; perhaps no one had ever even looked for it. But it cast an +enchantment over that house. My home was hiding a secret in the depths of its heart... “Yes,” I +said to the little prince. “The house, the stars, the desert — what gives them their beauty is +something that is invisible!” + +“I am glad,” he said, “that you agree with my fox.” As the little prince dropped off to sleep, I took +him in my arms and set out walking once more. I felt deeply moved, and stirred. It seemed to me +that I was carrying a very fragile treasure. It seemed to me, even, that there was nothing more +fragile on all Earth. In the moonlight I looked at his pale forehead, his closed eyes, his locks of +hair that trembled in the wind, and I said to myself: + +“What I see here is nothing but a shell. What is most important is invisible...” + +As his lips opened slightly with the suspicious of a half-smile, I said to myself, again: “What +moves me so deeply, about this little prince who is sleeping here, is his loyalty to a flower — the +image of a rose that shines through his whole being like the flame of a lamp, even when he is +asleep...” + +And I felt him to be more fragile still. I felt the need of protecting him, as if he himself were a +flame that might be extinguished by a little puff of wind... And, as I walked on so, I found the well, +at daybreak. + +“Men,” said the little prince, “set out on their way in express trains, but they do not know what +they are looking for. Then they rush about, and get excited, and turn round and round...” And he +added: “It is not worth the trouble...” + + + +The well that we had come to was not like the wells of the Sahara. The wells of the Sahara are +mere holes dug in the sand. This one was like a well in a village. But there was no village here, +and I thought I must be dreaming... + +“It is strange,” I said to the little prince. “Everything is ready for use: the pulley, the bucket, the +rope...” He laughed, touched the rope, and set the pulley to working. And the pulley moaned, like +an old weathervane, which the wind has long since forgotten. + +“Do you hear?” said the little prince. “We have wakened the well, and it is singing...” + +I did not want him to tire himself with the rope. + +“Leave it to me,” I said. “It is too heavy for you.” I hoisted the bucket slowly to the edge of the +well and set it there, happy, tired as I was, over my achievement. The song of the pulley was still +in my ears, and I could see the sunlight shimmer in the still trembling water. + +“I am thirsty for this water,” said the little prince. “Give me some of it to drink...” + +And I understood what he had been looking for. I raised the bucket to his lips. He drank, his eyes +closed. It was as sweet as some special festival treat. This water was indeed a different thing from +ordinary nourishment. Its sweetness was born of the walk under the stars, the song of the pulley, +the effort of my arms. It was good for the heart, like a present. When I was a little boy, the lights +of the Christmas tree, the music of the Midnight Mass, the tenderness of smiling faces, used to +make up, so, the radiance of the gifts I received. + +“The men where you live,” said the little prince, “raise five thousand roses in the same garden +and they do not find in it what they are looking for.” + +“They do not find it,” I replied. + +“And yet what they are looking for could be found in one single rose, or in a little water.” + +“Yes, that is true,” I said. + +And the little prince added: “But the eyes are blind. One must look with the heart...” + +I had drunk the water. I breathed easily. At sunrise the sand is the colour of honey. And that +honey colour was making me happy, too. What brought me, then, this sense of grief? + +“You must keep your promise,” said the little prince, softly, as he sat down beside me once more. +“What promise?” “You know, a muzzle for my sheep... I am responsible for this flower...” + +I took my rough drafts of drawings out of my pocket. The little prince looked them over, and +laughed as he said: + +“Your baobabs, they look a little like cabbages.” + +“Oh!” I had been so proud of my baobabs! “Your fox, his ears look a little like horns; and they are +too long.” And he laughed again. + +“You are not fair, little prince,” I said. “I don’t know how to draw anything except boa constrictors +from the outside and boa constrictors from the inside.” + + + + + +“Oh, that will be all right,” he said, “children understand.” + +So then I made a pencil sketch of a muzzle. And as I gave it to him my heart was torn. + +“You have plans that I do not know about,” I said. But he did not answer me. He said to me, +instead: “You know, my descent to the earth... Tomorrow will be its anniversary.” Then, after a +silence, he went on: “I came down very near here.” And he flushed. + +And once again, without understanding why, I had a queer sense of sorrow. One question, +however, occurred to me: “Then it was not by chance that on the morning when I first met you — a +week ago — you were strolling along like that, all alone, a thousand miles from any inhabited +region? You were on the your way back to the place where you landed?” + +The little prince flushed again. And I added, with some hesitancy: “Perhaps it was because of the +anniversary?” The little prince flushed once more. He never answered questions, but when one +flushes does that not mean “Yes”? + +“Ah,” I said to him, “I am a little frightened...” + +But he interrupted me. “Now you must work. You must return to your engine. I will be waiting for +you here. Come back tomorrow evening...” + +But I was not reassured. I remembered the fox. One runs the risk of weeping a little, if one lets +himself be tamed... + +Beside the well there was the ruin of an old stone wall. When I came back from my work, the next +evening, I saw from some distance away my little prince sitting on top of a wall, with his feet +dangling. And I heard him say: “Then you don’t remember. This is not the exact spot.” Another +voice must have answered him, for he replied to it: “Yes, yes! It is the right day, but this is not the +place.” + +I continued my walk toward the wall. At no time did I see or hear anyone. The little prince, +however, replied once again: “...Exactly. You will see where my track begins, in the sand. You have +nothing to do but wait for me there. I shall be there tonight.” + + + +I was only twenty metres from the wall, and I still saw nothing. After a silence the little prince +spoke again: “You have good poison? You are sure that it will not make me suffer too long?” I +stopped in my tracks, my heart torn asunder; but still I did not understand. “Now go away,” said +the little prince. “I want to get down from the wall.” + +I dropped my eyes, then, to the foot of the wall... and I leaped into the air. There before me, facing +the little prince, was one of those yellow snakes that take just thirty seconds to bring your life to +an end. Even as I was digging into my pocked to get out my revolver I made a running step back. +But, at the noise I made, the snake let himself flow easily across the sand like the dying spray of +a fountain, and, in no apparent hurry, disappeared, with a light metallic sound, among the stones. I +reached the wall just in time to catch my little man in my arms; his face was white as snow. + +“What does this mean?” I demanded. “Why are you talking with snakes?” + +I had loosened the golden muffler that he always wore. I had moistened his temples, and had +given him some water to drink. And now I did not dare ask him any more questions. He looked at +me very gravely, and put his arms around my neck. I felt his heart beating like the heart of a +dying bird, shot with someone’s rifle... + +“I am glad that you have found what was the matter with your engine,” he said. “Now you can go +back home” + +“How do you know about that?” I was just coming to tell him that my work had been successful, +beyond anything that I had dared to hope. + +He made no answer to my question, but he added: “I, too, am going back home today...” Then, +sadly, “It is much farther... it is much more difficult...” I realised clearly that something +extraordinary was happening. I was holding him close in my arms as if he were a little child; and +yet it seemed to me that he was rushing headlong toward an abyss from which I could do nothing +to restrain him... His look was very serious, like some one lost far away. + +“I have your sheep. And I have the sheep’s box. And I have the muzzle...” + +And he gave me a sad smile. I waited a long time. I could see that he was reviving little by little. + +“Dear little man,” I said to him, “you are afraid...” He was afraid, there was no doubt about that. +But he laughed lightly. + +“I shall be much more afraid this evening...” + +Once again I felt myself frozen by the sense of something irreparable. And I knew that I could not +bear the thought of never hearing that laughter any more. For me, it was like a spring of fresh +water in the desert. + +“Little man,” I said, “I want to hear you laugh again.” But he said to me: “Tonight, it will be a +year... my star, then, can be found right above the place where I came to the Earth, a year ago...” + +“Little man,” I said, “tell me that it is only a bad dream, this affair of the snake, and the meeting- +place, and the star...” But he did not answer my plea. + +He said to me, instead: “The thing that is important is the thing that is not seen...” “Yes, I +know...” + + + + +“It is just as it is with the flower. If you love a flower that lives on a star, it is sweet to look at the +sky at night. All the stars are a-bloom with flowers...” + +“Yes, I know...” + +“It is just as it is with the water. Because of the pulley, and the rope, what you gave me to drink +was like music. You remember, how good it was.” + +“Yes, I know...” + +“And at night you will look up at the stars. Where I live everything is so small that I cannot show +you where my star is to be found. It is better, like that. My star will just be one of the stars, for +you. And so you will love to watch all the stars in the heavens... they will all be your friends. And, +besides, I am going to make you a present...” He laughed again. + +“Ah, little prince, dear little prince! I love to hear that laughter!” + +“That is my present. Just that. It will be as it was when we drank the water...” + +“What are you trying to say?” + +“All men have the stars,” he answered, “but they are not the same things for different people. For +some, who are travellers, the stars are guides. For others they are no more than little lights in the +sky. For others, who are scholars, they are problems. For my businessman they were wealth. But +all these stars are silent. You, you alone, will have the stars as no one else has them” + +“What are you trying to say?” + +“In one of the stars I shall be living. In one of them I shall be laughing. And so it will be as if all +the stars were laughing, when you look at the sky at night... you, only you, will have stars that can +laugh!” + +And he laughed again. “And when your sorrow is comforted (time soothes all sorrows) you will be +content that you have known me. You will always be my friend. You will want to laugh with me. And +you will sometimes open your window, so, for that pleasure... and your friends will be properly +astonished to see you laughing as you look up at the sky! Then you will say to them, ‘Yes, the +stars always make me laugh!’ And they will think you are crazy. It will be a very shabby trick that +I shall have played on you...” + +And he laughed again. “It will be as if, in place of the stars, I had given you a great number of +little bells that knew how to laugh...” + +And he laughed again. Then he quickly became serious: “Tonight, you know... do not come,” said +the little prince. + +“I shall not leave you,” I said. + +“I shall look as if I were suffering. I shall look a little as if I were dying. It is like that. Do not +come to see that. It is not worth the trouble...” + +“I shall not leave you.” + + + +But he was worried. “I tell you, it is also because of the snake. He must not bite you. Snakes, they + + + + +are malicious creatures. This one might bite you just for fun...” + + + +“I shall not leave you.” + +But a thought came to reassure him: “It is true that they have no more poison for a second bite.” + +That night I did not see him set out on his way. He got away from me without making a sound. +When I succeeded in catching up with him he was walking along with a quick and resolute step. He +said to me merely: “Ah! You are there...” And he took me by the hand. But he was still worrying. +“It was wrong of you to come. You will suffer. I shall look as if I were dead; and that will not be +true...” + +I said nothing. + +“You understand... it is too far. I cannot carry this body with me. It is too heavy.” + +I said nothing. + +“But it will be like an old abandoned shell. There is nothing sad about old shells...” + +I said nothing. He was a little discouraged. But he made one more effort: “You know, it will be +very nice. I, too, shall look at the stars. All the stars will be wells with a rusty pulley. All the stars +will pour out fresh water for me to drink...” + +I said nothing. + +“That will be so amusing! You will have five hundred million little bells, and I shall have five +hundred million springs of fresh water...” And he too said nothing more, because he was crying... + +“Here it is. Let me go on by myself.” And he sat down, because he was afraid. Then he said, +again: “You know, my flower... I am responsible for her. And she is so weak! She has four thorns, +of no use at all, to protect herself against all the world...” + +I too sat down, because I was not able to stand up any longer. “There now, that is all...” + +He still hesitated a little; then he got up. He took one step. I could not move. There was nothing +but a flash of yellow close to his ankle. He remained motionless for an instant. He did not cry out. +He fell as gently as a tree falls. There was not even any sound, because of the sand. + +And now six years have already gone by... I have never yet told this story. + +The companions who met me on my return were well content to see me alive. I was sad, but I told +them: “I am tired.” Now my sorrow is comforted a little. That is to say, not entirely. But I know +that he did go back to his planet, because I did not find his body at daybreak. It was not such a +heavy body... and at night I love to listen to the stars. It is like five hundred million little bells... +But there is one extraordinary thing... when I drew the muzzle for the little prince, I forgot to add +the leather strap to it. He will never have been able to fasten it on his sheep. + +So now I keep wondering: what is happening on his planet? Perhaps the sheep has eaten the +flower... At one time I say to myself: “Surely not! The little prince shuts his flower under her glass +globe every night, and he watches over his sheep very carefully...” Then I am happy. And there is +sweetness in the laughter of all the stars. + + + + +But at another time I say to myself: “At some moment or other one is absent-minded, and that is +enough! On some one evening he forgot the glass globe, or the sheep got out, without making any +noise, in the night...” And then the little bells are changed to tears... Here, then, is a great +mystery. + +For you who also love the little prince, and for me, nothing in the universe can be the same if +somewhere, we do not know where, a sheep that we never saw has eaten a rose... Look up at the +sky. Ask yourselves: is it yes or no? + +Has the sheep eaten the flower? And you will see how everything changes... And no grown-up will +ever understand that this is a matter of so much importance! This is, to me, the loveliest and +saddest landscape in the world. It is the same as that on the preceding page, but I have drawn it +again to impress it on your memory. It is here that the little prince appeared on Earth, and +disappeared. Look at it carefully so that you will be sure to recognise it in case you travel some +day to the African desert. And, if you should come upon this spot, please do not hurry on. Wait for +a time, exactly under the star. Then, if a little man appears who laughs, who has golden hair and +who refuses to answer questions, you will know who he is. If this should happen, please comfort +me. Send me word that he has come back. + + + +END diff --git a/libs/harfbuzz/perf/texts/en-words.txt b/libs/harfbuzz/perf/texts/en-words.txt new file mode 100644 index 00000000..14200faa --- /dev/null +++ b/libs/harfbuzz/perf/texts/en-words.txt @@ -0,0 +1,12391 @@ +a +A +aa +AA +aaa +AAA +aaae +AAAu +AAB +AAC +aacute +Aacute +Aacutesmall +AAD +aae +AAE +aaf +AAF +aalt +aao +aarch +Aari +aat +AAT +aatFeatureType +AAu +ab +AB +Abaza +abb +ABBREV +abc +ABC +abcde +abcdefghijklmnopqrstuvwxyz +ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz +ABD +ABE +ABEu +ABF +ABFu +abh +ABI +ability +Abkhazian +able +Aboriginal +ABORIGINAL +abort +about +above +Above +ABOVE +abq +abs +absolute +absolutely +absorb +abstraction +abstractions +ABu +abuse +abv +ABVF +abvm +abvs +ABVS +ABx +ABxCD +ABxD +ac +AC +acb +ACBu +acc +ACC +accel +Accelerate +accelerator +ACCELERATOR +accels +accent +ACCENT +accents +accept +acceptable +accepted +accepting +accepts +access +Access +accessed +accessible +accessing +accessors +accommodate +Accommodate +accompanying +accomplish +according +According +account +accounting +ACCu +accumulate +Accumulate +accuracy +accurate +AccuT +ACDu +acf +ach +Achi +achieve +Acholi +achung +achVendID +acircumflex +Acircumflex +Acircumflexsmall +ack +acm +Acoli +acom +acq +ACQ +acquire +ACQUIRE +acr +across +act +action +Action +ACTION +actionable +actionClass +actionData +actionLength +actions +Actions +ActionSubrecord +ActionSubrecordHeader +actionType +ActionType +activated +activates +active +acts +actual +Actual +actualGlyphsCount +actually +ACu +acute +Acutesmall +acw +acx +acy +ad +AD +ada +Adamawa +Adangme +Adap +adapt +Adapted +Adaptors +aData +adb +ADBu +add +Add +addcnt +added +AddFontMemResourceEx +addGlyph +adding +Adding +addition +Addition +additional +Additional +additionalCount +additions +addr +AddRef +address +addressof +adds +ADDu +Adeni +adequate +adf +adieresis +Adieresis +Adieresissmall +Adilabad +adjacent +Adjacent +adjust +Adjust +adjusted +adjustment +adjustments +Adjusts +ADL +Adlam +ADLAM +adobe +Adobe +adopt +Adopted +adorned +adp +ADu +adv +advance +Advance +Advanced +advanceMax +advanceMeasurement +advanceOffset +advances +ADVANCES +Advancing +advantage +ADVISED +advMap +ady +Adyghe +ae +AE +aea +aeaf +aeb +aec +AEDu +AEEu +AEFu +Aegean +AEsmall +AEu +af +AF +afa +afadd +Afar +AFAu +afb +AFBAu +AFBu +AFDKO +AFEu +AFF +affect +affected +affecting +affects +affinity +Afghanistan +Africa +Afrikaans +after +After +AFTER +afterGrowLimit +afterShrinkLimit +AFu +AG +again +against +Agaw +Agfa +agnostic +agrave +Agrave +Agravesmall +agree +agreement +ahead +Aheri +ahg +Ahom +AHOM +aht +Ahtena +ai +AI +aii +AIN +aio +Aiton +aiw +AIX +ajp +ak +aka +AKA +Akan +AKAT +AKHN +alaph +ALAPH +alas +Alaska +Alaskan +Albania +Albanian +ALBANIAN +Albay +alef +Alef +ALEF +Algeria +Algerian +algorithm +Algorithm +algorithmic +algorithms +Algorithms +algs +ALGS +alias +aliased +aliases +aliasing +align +aligned +alignment +alignof +aligns +alive +all +All +ALL +ALLAH +AllDirections +Allison +alloc +Alloc +allocate +Allocate +ALLOCATE +allocated +allocates +allocating +allocation +Allocation +allocations +Allocations +allocator +allow +Allow +allowed +allowing +allows +almost +aln +alone +along +alpha +Alpha +alphabet +Alphabet +alphabetic +Alphabetic +alphabetical +alphabetically +Alphanumeric +Alphanumerics +already +als +Alsatian +also +Also +alt +ALT +Altai +alter +alternate +Alternate +ALTERNATE +alternates +ALTERNATES +alternateSet +AlternateSet +AlternateSubst +AlternateSubstFormat +alternative +Alternatively +ALTERNATIVES +although +Although +alts +always +Always +am +AM +ambiguity +ambiguous +Ambo +Amend +American +Americanist +amf +Amharic +among +amongst +amount +Amoy +amp +ampersand +ampersandsmall +amw +an +An +AN +Ana +analysis +Analysis +ANALYSIS +analyze +Analyze +analyzer +Analyzer +analyzers +Analyzes +AnalyzeScript +ANATOLIAN +Ancash +anchor +Anchor +anchorData +anchored +AnchorFormat +AnchorMatrix +anchorPoint +anchors +Ancient +and +And +AND +android +Android +ANDROID +ang +angle +Angle +ANGLE +Anglo +ankr +ANKR +ankrActionIndex +ankrData +annex +annotate +Annotated +annotation +ANNOTATION +annotations +another +Another +Ansi +Antankarana +Antillean +any +Any +ANY +anymore +anything +Anything +anyway +ap +Apache +apc +apd +Api +API +apis +APIs +apj +apk +apl +apm +APP +Apparently +appear +appearance +appearing +append +Append +APPEND +appended +appending +Appendix +AppendixF +Appends +Appl +APPL +apple +Apple +APPLE +AppleColorEmoji +applicable +Applicable +APPLICABLE +application +applications +Applications +ApplicationServices +applied +applies +apply +Apply +APPLY +applying +Applying +approach +approaches +appropriate +approxequal +approximate +appveyor +April +apt +Apurímac +apw +AQ +ar +Arabia +ARABIAN +arabic +Arabic +ARABIC +ArabicShaping +Aragonese +Arakanese +Arakwal +Aramaic +ARAMAIC +arb +Arbëreshë +arbitrarily +arbitrary +ARC +Archaic +architecturally +archive +archives +are +ARE +area +Area +Arequipa +arg +ARG +Argentina +Argh +args +ARGS +argStack +argument +arguments +aring +Aring +Aringsmall +ARISING +arith +arithmetic +Ariza +armcc +Armenia +Armenian +ARMENIAN +arn +Aromanian +around +arounds +Arpitan +arq +arr +arranged +array +Array +ARRAY +ArrayOf +ArrayOfM +arrays +arrayZ +arrive +Arrows +ars +Arsi +articles +Arvanitika +ary +arz +as +As +AS +Asat +ascender +ASCENDER +ascenderOffset +ascending +ascent +ASCENT +ascii +ASCII +asciicircum +asciitilde +Asho +Asian +aSize +asked +Asking +Asmall +Asomtavruli +aspect +aspects +aspx +Assamese +assembly +assert +ASSERT +assertion +ASSERTION +assign +Assign +ASSIGN +assignable +assigned +assigns +assistance +assisted +associated +associates +associating +Association +assume +Assume +assumed +Assumed +assuming +Assuming +assumption +Assumption +Assyrian +ast +asterisk +ASTERISK +Asturian +asuperior +at +At +atexit +ATEXIT +aTextPosition +ath +Athapascan +Athapaskan +Atikamekw +atilde +Atilde +Atildesmall +atj +Atlas +atleast +ATLEAST +atomic +Atomic +ATOMIC +Atomically +atomics +atsFont +ATSFontGetFileReference +ATSFontRef +attach +Attach +ATTACH +attached +ATTACHED +attaches +attaching +attachList +AttachList +attachment +Attachment +ATTACHMENT +attachments +attachPoint +AttachPoint +attempt +attempted +attempts +Attempts +attr +ATTR +attractive +attrib +attribute +ATTRIBUTE +attributed +attributes +Attributes +attrs +atv +au +Au +AU +audiences +augmented +Australia +Austria +Author +AUTHOR +authors +auto +AUTO +autoconf +autogen +automake +automatic +Automatic +automatically +auxiliary +Auxiliary +auz +av +AV +Avagraha +AVAGRAHA +avail +Availability +AvailabilityMacros +available +avar +Avar +AVAR +Avaric +AVESTAN +avl +avoid +Avoid +avoided +avoiding +avoids +aw +awa +Awa +Awadhi +aware +away +awful +ax +Ax +AxCD +AxD +axes +axesZ +axis +Axis +AXIS +axisCount +axisIndex +axisNameID +AxisRecord +axisSize +axisTag +AxisValue +axisValueCount +AxisValueFormat +AxisValueMap +AxisValueRecord +axisValues +ay +Ayacucho +ayc +ayh +AYIN +ayl +Aymara +ayn +ayp +ayr +az +azb +Azerbaijan +Azerbaijani +Azeri +azj +b +B +ba +BA +Babalia +Babine +BABu +Bacanese +back +backend +backends +background +Background +BACKGROUND +backing +backslash +backtrack +backtrackClassDef +backtrackCount +backtracking +backward +BACKWARD +backwards +Backwards +bad +Badaga +badly +BADLY +bae +BAEu +BAFu +Bagheli +Baghelkhandi +Bagirmi +Bagri +Baharna +Bahrain +bai +bail +Bakhtiari +bal +Balanta +Balante +Balinese +BALINESE +Balkan +Balkar +Balochi +Balti +Baltic +Baluchi +Bamanankan +Bambara +Bamileke +BAMUM +ban +Banda +Bandjalang +Bangka +Bangladesh +Bangun +Banjar +Banna +Baoulé +bar +Bara +BARREE +barrier +Barrier +bars +Bas +base +Base +BASE +baseArray +BaseArray +BaseCoord +BaseCoordFormat +baseCoords +baseCoverage +BaseCoverage +based +Based +BaseFontBlend +baseFontName +BaseFontName +BaseGlyph +BaseGlyphRecord +baseGlyphsZ +BaseLangSysRecord +baseLangSysRecords +BaseLangSysRecords +baseLangSysTag +baseline +Baseline +BASELINE +baselines +BaselineTableFormat +baselinetags +baselineTags +bases +baseScript +BaseScript +baseScriptList +BaseScriptList +BaseScriptRecord +baseScriptRecords +BaseScriptRecords +baseScriptTag +baseTagCount +baseTagList +BaseTagList +baseValues +BaseValues +Bashkir +basic +Basic +BASIC +basically +BASIS +Basque +BASSA +Batak +BATAK +batch +BAu +Baulé +Bavarian +Bawm +BB +BBAu +BBBu +bbc +bbee +BBEu +BBF +BBu +bbz +bc +BC +bca +BCA +BCAu +BCBu +bcc +BCCu +bcd +BCD +BCDu +BCEu +bci +bcl +bcp +BCP +bcq +bcr +BCu +bcursor +bd +BD +bdadd +bdfaab +bdu +BDu +bdy +be +Be +BE +bea +BEAM +bearing +bearings +bearingX +bearingY +Beaver +beb +Bebele +Bebil +bec +because +Because +become +BECOME +becomes +bed +Bedawi +BEEH +been +BEEN +BEEu +before +Before +BEFORE +beforeGrowLimit +beforehand +beforeShrinkLimit +BeforeSub +began +begin +BEGIN +beginning +Beginning +BEH +behave +behaved +behaving +behavior +behaviors +behaviour +Behdad +BEHEH +behind +Bei +being +Being +BEInt +Belarus +Belarusian +Belarussian +Belgium +Belize +belong +belonging +belongs +below +Below +BELOW +bem +Bemba +Bench +bend +benefits +BENG +Bengali +BENGALI +ber +Berau +Berber +best +Bet +BET +Beti +bets +Betsimisaraka +better +between +BEu +beyond +bf +BF +bfaeafe +BFAu +bfb +BFF +bffc +BFFu +bfind +BFIND +bfq +bft +bfu +BFu +bfy +bg +bgc +bgn +bgp +bgq +bgr +BGRAColor +Bhaiksuki +BHAIKSUKI +Bhasha +bhb +bhi +Bhilali +Bhili +bhk +bho +Bhojpuri +bhr +bi +Bi +bias +biased +biasedSubrs +Bible +Bicolano +bidi +bidirectional +BiDirectional +bidirectionality +big +Big +BIG +BigGlyphMetrics +bik +Bikol +BILD +Bilen +Bilin +billion +bimap +BIMAP +bin +binaries +binary +Binary +BINARY +bindings +Bindu +BINDU +Bindus +BinSearchArrayOf +BinSearchHeader +Bishnupriya +Bislama +bit +Bit +bitcount +bitDepth +bitfield +bithacks +Bithacks +bitmap +Bitmap +BITMAP +bitmaps +BitmapSizeTable +bits +Bits +BITS +BitScanForward +BitScanReverse +bitset +bitsize +bitwise +bjj +bjn +BJN +bjq +bjt +bl +BL +bla +black +Black +BLACK +Blackfoot +blackhole +blacklist +BLACKLIST +blacklisted +blacklisting +Blas +ble +blend +Blend +BLEND +blendcs +blenddict +blended +blending +BlendInterpEnv +blends +Blink +blk +bln +blob +BLOB +blobs +Blobs +block +Block +BLOCK +blocked +Blocked +blocks +Blocks +Bloom +blow +blown +blue +Blue +BlueFuzz +BlueScale +BlueShift +BlueValues +blwf +BLWF +blwm +blws +BLWS +bm +bmg +bmm +bmp +BMP +bn +bo +Bodo +body +bok +Bokmal +Bokmål +bold +Bold +BOLD +bolder +Bolivia +Bolivian +book +Book +bookbold +bookkeeping +bool +BOOL +Boolean +Booleans +BOOM +Bopomofo +BOPOMOFO +Borana +BORDERS +Borgu +Borrowed +Bosnia +Bosnian +BOT +both +bother +bottom +Bottom +BOTTOM +bottomSide +bound +Bound +boundaries +boundary +Boundary +bounding +bounds +Bounds +BOUNDS +Bouyei +box +Box +BOX +boxed +BOXED +boxes +bpy +bqi +br +BR +bra +braceleft +braceright +braces +Bracket +BRACKET +bracketleft +bracketright +Brahmi +BRAHMI +Brahui +Braille +BRAILLE +Braj +branches +brand +Brawer +Brazil +break +BREAK +breakfast +breaking +breakpoint +BREAKPOINT +breaks +bretagne +Breton +breve +Brevesmall +brew +brh +brief +broken +Broken +brokenbar +Brunei +Bruno +bruteforce +brx +bs +bsb +bsearch +bsk +bsln +BSLN +Bsmall +bsuperior +bswap +btb +btj +bto +bts +btt +BTT +bu +Bu +Bualkhaw +Bubble +bucket +buf +buff +buffArray +buffer +Buffer +BUFFER +buffers +Buffers +BUFSIZ +bug +BUG +buggy +Buginese +BUGINESE +Bugis +bugs +bugzilla +Buhi +Buhid +BUHID +build +builder +Builder +building +Building +builds +built +builtin +BUILTIN +builtins +Bukit +Bukusu +Bulgaria +Bulgarian +bulk +bullet +Bulu +bum +Bumthangkha +bundles +burden +Buriat +Burmese +Burushaski +business +but +But +BUT +BV +bve +bvu +bxk +bxp +bxr +by +By +BY +Byagowi +Byelorussian +byes +byn +byte +Byte +BYTE +byteArray +byteOffsetToIndex +bytes +Bytes +BYTES +bytesArray +bytesX +bytesZ +byv +Byzantine +bzc +c +C +ca +Ca +CA +CABu +caca +cache +Cache +CACHE +cacheable +cached +caches +caching +Caching +cacute +Cacute +caf +cairo +Cairo +cairographics +Cajamarca +Cajatambo +Cajun +cak +calcOffSize +CalcTableChecksum +calculate +Calculate +calculated +calculates +calculation +Calderón +Calibry +call +Call +callback +Callback +callbacks +CALLBACKS +called +Called +caller +callers +callgsubr +calling +calloc +calls +Calls +callStack +callsubr +calt +Cambodia +Cameroon +Campidanese +can +Can +Canada +Canadian +CANADIAN +Cañar +CANCEL +candidate +candidates +cannot +Cannot +canon +canonical +Canonical +CANONICAL +canonically +cantarell +Cantarell +Cantillation +CANTILLATION +cap +Cap +CAP +capabilities +capable +capital +CAPS +care +careful +carefully +caret +Caret +CARET +caretOffset +carets +caretSlopeDenominator +caretSlopeNumerator +caretSlopeRise +caretSlopeRun +CaretValue +caretValueFormat +CaretValueFormat +caretValuePoint +Carian +CARIAN +Caribbean +caron +Caronsmall +Carpathian +Carrier +carry +cas +cascade +cascading +case +CASE +cases +cast +Cast +casting +casts +Casts +cat +Cat +CAT +Catalan +Catanduanes +catch +categories +Categories +CATEGORIES +categorization +categorize +categorized +categorizes +category +Category +CATEGORY +Cateories +CAu +CAUCASIAN +cause +caused +causes +causing +cautious +cb +CB +cbb +CBCu +cbdt +CBDT +CBEu +CBFu +cbk +cbl +cblc +CBLC +CBu +cc +Cc +CC +ccaron +Ccaron +CCAu +CCBu +ccc +CCC +ccccae +cccf +CCCu +CCD +CCDu +ccedilla +Ccedilla +Ccedillasmall +CCEu +cChars +ccmp +cco +ccq +CCu +cd +CD +CDATA +cdd +cdo +CDu +CDx +CDxA +CDxAB +CDxBA +ce +CE +ceb +Cebuano +CECu +cedilla +Cedillasmall +CEDu +CEEu +ceil +CEIL +cent +center +Center +centered +CENTERED +centinferior +centoldstyle +CentOS +central +Central +centsuperior +ceparams +certain +Certain +CEu +cf +Cf +CF +cfar +CFAR +CFArrayAppendValue +CFArrayCreate +CFArrayCreateMutable +CFArrayGetCount +CFArrayGetValueAtIndex +CFArrayRef +CFAttributedStringCreateMutable +CFAttributedStringRemoveAttribute +CFAttributedStringReplaceString +CFAttributedStringSetAttribute +CFComparisonResult +cfd +CFData +CFDataGetBytePtr +CFDataGetLength +CFDataRef +CFDictionaryCreate +CFDictionaryGetValue +CFDictionaryRef +CFDu +CFEqual +cff +CFF +cfff +CFFIndex +CFFIndexOf +CFFTag +CFFu +CFIndex +cfm +CFMutableArrayRef +CFMutableAttributedStringRef +CFNumberCreate +CFNumberRef +CFRange +CFRangeMake +CFRelease +CFRetain +CFSTR +CFStringCompare +CFStringCreateWithCharactersNoCopy +CFStringCreateWithCStringNoCopy +CFStringGetCharacterAtIndex +CFStringHasPrefix +CFStringHasSuffix +CFStringRef +CFu +CFURLCreateFromFSRef +CFURLRef +cg +CGDataProviderCreateWithData +CGDataProviderRef +CGDataProviderRelease +CGFloat +CGFont +CGFontCopyPostScriptName +CGFontCopyTableForTag +CGFontCreateWithDataProvider +CGFontRef +CGFontRelease +CGFontRetain +cgg +CGGlyph +cgi +cgit +CGJ +cGlyphs +CGPoint +ch +Chachapoyas +Chadian +Chaha +chain +Chain +chainContext +ChainContext +ChainContextApplyLookupContext +ChainContextClosureLookupContext +ChainContextCollectGlyphsLookupContext +ChainContextFormat +ChainContextPos +ChainContextSubst +chainCount +chaining +Chaining +ChainRule +ChainRuleSet +chains +Chains +ChainSubtable +Chakma +CHAKMA +Chaldean +Cham +CHAM +Chamorro +chance +chandas +change +Change +changed +changes +Changes +changing +channel +channels +Chap +chapter +Chapter +char +Char +CHAR +character +Character +CHARACTER +characterCode +characterize +characters +Characters +CHARACTERS +characterVariants +charmap +Charmap +charMirror +CHARPROP +chars +charset +Charset +CHARSET +CharsetID +charsetInfo +CharsetOffset +charstreing +charstring +CharString +charstrings +charStrings +CharStrings +charStringsInfo +charStringsOffset +CharstringType +charts +chattawa +CHATTAWA +Chattisgarhi +Chaungtha +Chavacano +cheaper +Chechen +check +Check +CHECK +checked +CHECKED +checking +Checking +checks +Checks +checksum +checkSum +CheckSum +checkSumAdjustment +Cherokee +CHEROKEE +Chetco +Chewa +Cheyenne +Chhattisgarhi +Chichewa +Chiga +Chiki +CHIKI +Chilcotin +child +children +Chile +Chilean +Chillus +Chiltepec +Chimborazo +Chin +China +Chinantec +Chinbon +Chincha +Chinese +Chipewyan +Chippewa +Chiquián +Chiricahua +Chiripá +Chittagonian +chj +chk +CHL +cho +Choctaw +choice +choices +choose +Choose +chooses +choosing +chop +Chop +chosen +CHOSEONG +chp +chq +chr +chris +Chris +chromium +Chromium +Chuanqiandian +Chukchi +Chukot +chunk +ChunkLen +chunks +Church +Chuukese +Chuvash +chy +chz +ci +CI +CID +cidCount +CIDCount +CIDFontRevision +CIDFontType +CIDFontVersion +CIN +cInChars +CIP +circle +CIRCLE +circles +circuit +CIRCUIT +circumflex +Circumflexsmall +circumstances +circumvents +citer +CITI +ciw +cja +CJCT +CJK +cjm +cjy +ck +CK +cka +ckb +ckt +cl +Clamp +clang +clarity +Clasen +class +Class +CLASS +classArray +classCount +classDef +ClassDef +ClassDefFormat +classes +classFormat +Classic +Classical +classifications +classified +classify +classTable +ClassTable +ClassType +ClassTypeNarrow +ClassTypeWide +classValue +classValueArray +clc +cld +cle +clean +Clean +cleanest +clear +Clear +cleared +clearing +clearly +clears +Clears +ClearType +ClearType™ +client +Client +clients +Clients +clig +clipping +CLIPPING +clockwise +close +Close +CLOSE +CloseHandle +closely +closer +closest +CLOSEST +closure +Closure +CLOSURE +closures +cluster +Cluster +CLUSTER +clustering +clusterMap +clusters +Clusters +CLUSTERS +clz +clzl +clzll +CM +CMAbv +cmake +cmap +CMAP +cmapsubtable +CmapSubtable +CmapSubtableFormat +CmapSubtableLongGroup +CmapSubtableLongSegmented +CmapSubtableTrimmed +cMaxGlyphs +cMaxItems +CMBlw +cmn +cmp +cmpexch +cmplexch +cmpswap +cmr +cn +Cn +cnb +cnh +cnk +cnl +cnt +cntrmask +cnw +co +Co +coa +Cocos +code +Code +CODE +codebase +coded +codepath +codepoint +CODEPOINT +codepoints +codepont +codes +Codethink +Coeng +COENG +coengs +col +Col +collect +Collect +COLLECT +collected +collection +Collection +collections +Collections +collects +Colombia +colon +COLON +colonmonetary +color +Color +COLOR +colorIdx +colorLabelsZ +ColorRecord +colorRecordIndicesZ +colorRecordsZ +colorRef +colors +colorType +Colour +colr +COLR +cols +COLS +columnCount +columnIndexTable +com +Comaltepec +combination +combinations +combine +COMBINE +combined +combiner +combiners +combining +Combining +COMBINING +come +comes +comfortably +coming +comma +COMMA +commainferior +command +Command +commands +commas +commasuperior +comment +Commenting +comments +commit +commitcomment +commits +common +Common +COMMON +commonly +Comorian +comp +compact +Compact +compar +compare +Compare +compared +compares +comparing +Comparing +comparison +compat +compatibility +Compatibility +COMPATIBILITY +compatible +COMPATIBLE +compensate +compilable +compile +Compile +compiled +compiler +Compiler +compilers +compiles +COMPILES +compiling +complains +complang +Complement +complete +Complete +completely +complex +Complex +COMPLEX +COMPLEXITY +complicated +component +COMPONENT +componentData +ComponentGlyph +components +Components +COMPONENTS +ComponentsArray +composable +compose +composed +COMPOSED +composePair +composes +Composes +composing +composite +COMPOSITE +CompositeGlyph +CompositeGlyphChain +composites +composition +Composition +COMPOSITION +COMPOUND +compressed +compressionMethod +comprise +comprising +comps +compute +Compute +Computes +computing +concepts +Concepts +conceptual +Conceptually +concern +concerned +Conchucos +cond +Cond +condensed +Condensed +CONDENSED +condition +Condition +conditional +conditionalAddGlyphAction +ConditionalAddGlyphAction +conditionally +ConditionFormat +conditions +Conditions +ConditionSet +config +CONFIG +configs +configuration +Configuration +configurations +configure +configured +conflicting +confused +confusing +Congo +conjunct +connect +CONNECT +connected +CONNECTED +connecting +connection +CONNECTION +connector +CONNECTOR +Cons +CONS +consecutive +CONSEQUENTIAL +Consequently +consider +Consider +considerably +consideration +considerations +considered +consist +consistent +consists +consonant +Consonant +CONSONANT +consonants +const +CONST +constant +CONSTANT +constants +Constants +constexpr +constituent +constitute +constitutes +constraints +CONSTRAINTS +construct +constructed +constructible +construction +constructor +constructors +Constructors +constructs +consult +consumed +contain +contained +container +containers +containing +contains +ContainsTextPosition +contemporary +content +CONTENT +contents +context +Context +CONTEXT +ContextApplyFuncs +ContextApplyLookupContext +ContextClosureFuncs +ContextClosureLookupContext +ContextCollectGlyphsFuncs +ContextCollectGlyphsLookupContext +ContextFormat +ContextPos +contexts +ContextSubst +contextual +Contextual +CONTEXTUAL +ContextualSubtable +contiguous +continuation +CONTINUATION +continuations +continue +continues +contour +Contour +contours +contract +CONTRACT +contrary +contrast +contributing +Contributor +control +Control +CONTROL +controlling +controls +convenience +conveniences +convenient +conventions +Conversely +conversion +Conversion +Conversions +convert +Convert +converted +Converted +converters +convertible +converting +Converts +convoluted +coord +coordinate +Coordinate +coordinates +Coordinates +COORDINATES +coordinatesZ +coordPoint +coords +coorinates +cop +copied +Copied +copies +Copies +Coptic +COPTIC +copy +Copy +COPY +copyable +copying +copyright +Copyright +COPYRIGHT +coq +Coquille +core +CoreGraphics +coretext +CoreText +CORETEXT +corner +CORNER +corners +Cornish +Corongo +correct +correction +correctionHeight +correctly +Correctly +correctness +correlate +correspond +corresponding +corresponds +corrupt +Corsican +cost +Costa +costs +costy +cotfRecords +could +Could +count +Count +COUNT +countChar +counted +counter +counting +Counting +CountMask +countries +counts +couple +Courier +course +courtesy +cover +coverage +Coverage +COVERAGE +coverageFormat +CoverageFormat +coverageZ +covered +COVERED +covering +covers +cp +CP +cpa +cpal +CPAL +CPALV +cpe +cpf +cplusplus +cpp +cppreference +CPR +CPrf +cpx +cqd +cqu +cr +CRAMPED +cRanges +crap +Crap +CRAP +CrapHelper +CrapOrNull +CrapOrNullHelper +CrapPool +crash +crashes +crazy +crbug +create +Create +CREATE +CreateCustomFontFileReference +created +CreateFile +CREATEFILE +CreateFileMapping +CreateFileMappingFromApp +CreateFileW +CreateFontFace +CreateFontIndirectW +creates +Creates +CreateStreamFromKey +CreateTextAnalyzer +creating +Creating +creation +creator +Cree +Creek +Creole +creoles +Creoles +crh +Crimean +Crioulo +criteria +CRITICAL +crj +crk +crl +crm +Croatia +Croatian +cross +Cross +crossStream +CrossStream +crp +crucial +crx +cs +Cs +CS +csa +csb +csh +Csmall +cso +csop +CSOPSET +CSR +CSS +CSType +csw +cswh +csy +ct +ctc +ctd +cte +CTFont +CTFontCopyAttribute +CTFontCopyGraphicsFont +CTFontCopyName +CTFontCopyPostScriptName +CTFontCreateCopyWithAttributes +CTFontCreateUIFontForLanguage +ctfontcreatewithgraphicsfont +CTFontCreateWithGraphicsFont +CTFontDescriptorCreateWithAttributes +CTFontDescriptorCreateWithNameAndSize +CTFontDescriptorRef +CTFontGetPlatformFont +CTFontGetSize +CTFontRef +CTFontUIFontType +ctg +CTGetCoreTextVersion +ctl +CTLineGetGlyphRuns +CTLineGetTrailingWhitespaceWidth +CTLineRef +ctlPoints +ctor +CTRunGetAttributes +CTRunGetGlyphCount +CTRunGetGlyphs +CTRunGetGlyphsPtr +CTRunGetPositions +CTRunGetPositionsPtr +CTRunGetStatus +CTRunGetStringIndices +CTRunGetStringIndicesPtr +CTRunGetStringRange +CTRunGetTypographicBounds +CTRunRef +CTRunStatus +cts +CTTypesetterCreateLine +CTTypesetterCreateWithAttributedStringAndOptions +CTTypesetterRef +CTYPE +ctz +ctzl +ctzll +cu +Cu +cuc +cuk +cumulated +Cumulative +Cuneiform +CUNEIFORM +cur +curEntry +Curiously +CURISVE +CURLY +curr +curradv +curradvx +curradvy +currAnchor +currAnchorPoint +currclus +currControlPoint +currency +Currency +CURRENCY +current +Current +CurrentCategory +currentIndex +currentInsertBefore +CurrentInsertBefore +currentInsertCount +CurrentInsertCount +currentInsertIndex +currentInsertList +CurrentIsKashidaLike +currently +Currently +currX +currY +curs +cursive +Cursive +CURSIVE +CursivePos +CursivePosFormat +cursor +Cursor +cursoring +curve +CURVE +Cusco +custom +Custom +customization +customize +customizing +Customizing +CustomRange +cut +CUT +cutting +cv +cvn +CVT +cvXX +cwd +CWS +cx +cy +CYGWIN +Cypriot +CYPRIOT +Cyrillic +CYRILLIC +Czech +czh +czo +czt +d +D +da +DA +Daai +DAD +DAFu +dagesh +DAGESH +dagger +daggerdbl +DAHAL +Dai +DAL +DALATH +DALET +dam +DAMAGE +DAMAGES +damma +dammatan +Dan +dance +dangerous +Dangme +Danish +dao +dap +dar +Dargwa +Dari +dark +DARK +Darkhat +Darussalam +Darwazi +DASH +data +Data +DATA +dataArray +dataArrayLen +Database +dataLen +dataLength +DataMap +dataMaps +dataOffset +dataset +dataSets +dataSize +dataSizeArray +dataZ +Date +DAu +David +Daw +dax +day +Dayi +db +DB +DBAu +DBBu +DBCu +DBEu +DBF +dbfo +DBL +DBu +dc +DC +DCA +DCAu +DCBu +DCFu +dcroat +DCu +DCx +DCxA +DCxAB +DCxBA +dd +DD +DDA +DDAHAL +DDAL +DDAu +DDCu +DDD +DDDu +dde +DDEu +ddf +DDF +DDFu +DDu +de +DE +deabc +Dead +DEAD +deal +deallocate +Deallocate +DEALLOCATE +deallocation +dealt +DEAu +Debian +debug +Debug +DEBUG +debugging +Debugging +dec +decay +decender +decide +Decide +deciding +DECIMAL +decipoints +decision +decisions +declaration +declarations +declare +Declare +DECLARE +declared +declaring +decls +DECLS +declspec +decltype +declval +decode +DECODE +decomp +decompose +Decompose +DECOMPOSE +decomposed +DECOMPOSED +decomposedglyphs +decomposes +decomposing +decomposition +DECOMPOSITION +decompositionAction +DecompositionAction +decompositions +decompressed +deconstructed +DECORATIVE +DecorativeBorders +decrease +decreased +decreases +Decreases +decreasing +deduce +deduced +deduces +deem +deemed +deep +def +DEF +defaul +default +Default +DEFAULT +defaultBaseline +defaultFlags +defaultIndex +DefaultJstfLangSys +defaultLangSys +DefaultLangSys +defaultMinMax +defaults +defaultUVS +DefaultUVS +defaultValue +defaultVertOriginY +defaultWidthX +define +DEFINE +defined +Defined +DEFINED +defines +Defines +defining +Defining +definition +Definition +definitions +Definitions +DEFu +Degexit +degree +DEGREE +degrees +Dehong +DejaVu +del +delayed +delete +DELETE +DeleteCriticalSection +deleted +DELETED +DeleteObject +deleting +delimited +DELIMITED +delta +Delta +DELTA +deltaFormat +deltaGlyphID +deltas +DELTAS +DeltaSetIndexMap +DeltaValue +deltaValueZ +Democratic +demonstrate +den +DENIED +Denmark +denom +DENOM +DENOMINATOR +denote +density +depend +dependant +dependencies +dependency +dependent +Dependent +DEPENDENT +depending +depends +deprecated +Deprecated +DEPRECATED +depth +deref +dereference +dereferenced +dereferencing +derived +Derived +DerivedCoreProperties +desc +descendent +descender +DESCENDER +descending +descent +DESCENT +describe +described +describes +describing +description +Description +DESCRIPTION +descriptor +descriptors +Deseret +DESERET +deserialize +DESERIALIZE +design +Design +DESIGN +designated +designates +designAxesOffset +designAxisCount +designAxisSize +designed +designer +DESIGNER +designSize +desirable +desired +desktop +dest +destroy +Destroy +DESTROY +destroyed +destroying +destroys +Destroys +destruct +destructed +destructible +destruction +desubroutinize +detail +detailed +details +detect +detected +determine +Determine +determined +determines +determining +Determining +DEu +dev +DEVA +devanagari +Devanagari +DEVANAGARI +devel +develop +developed +developer +developers +developing +development +Deviate +device +Device +DeviceHeader +DeviceRecord +devices +deviceTable +devnet +df +DF +DFAu +dfde +DFDu +dfe +DFF +DFFu +dflt +DFLT +dfont +DFont +DFontTag +DFu +dgo +dgr +Dhangu +dhd +dhg +Dhivehi +Dhofari +Dhundari +Dhuwal +Dhuwaya +diacritic +Diacritical +diacritics +DIACRITICS +diagnostic +DIAGNOSTIC +DIAGONAL +DIAMOND +dib +dict +Dict +DICT +dictionary +dicts +dictsSize +dictval +DICTVAL +did +didn +Didn +didnt +dieresis +Dieresissmall +diff +DIFF +differ +difference +differences +different +Different +differentiate +differently +differing +differs +difficult +difficulty +digest +DIGEST +digests +digit +Digit +DIGIT +digital +Digits +dik +dimensional +dimensions +Dimli +din +Dingbats +DINGBATS +Dinka +dip +DIPHTHONG +diq +dir +DIR +direct +DIRECT +direction +Direction +DIRECTION +directional +directionality +directions +directive +directly +directory +directwrite +DirectWrite +DIRECTWRITE +dirty +disable +Disable +DISABLE +disabled +disableFlags +disables +disabling +disallow +disallows +disambiguated +Disc +discard +Discarding +discards +discern +DISCLAIMS +discover +Discovered +discretionary +discussed +Discussion +dispatch +Dispatch +DISPATCH +displacement +display +Display +DISPLAY +displaying +dist +distance +Distance +DISTANCE +distances +distinct +distinction +distinguish +distinguishes +distinguishing +distribute +distribution +ditto +div +DIV +Divehi +divert +divide +DIVIDE +divided +divisible +diw +Djambarrpuyngu +dje +djr +dks +DLBAR +dlig +dll +DLL +dllexport +dm +dng +dnj +dnom +do +Do +DO +DOACHASHMEE +doc +Doc +docbook +DocBook +docbookx +docs +DOCTYPE +document +Document +documentation +DOCUMENTATION +documented +documents +Documents +does +Does +doesn +Doesn +doesnt +Dogra +DOGRA +Dogri +Dogrib +doi +doing +dollar +dollarinferior +dollaroldstyle +dollarsuperior +Domain +dominant +Dominican +Domino +don +Don +DON +done +Done +DONE +Dong +dont +DONT +DontAdvance +Dos +DoS +dot +Dot +DOT +dotaccent +Dotaccentsmall +dotlessi +dotsection +dotted +DOTTED +dottedcircle +DOTTEDCIRCLE +Dotyali +double +DOUBLE +down +DOWN +download +downloaded +Downloading +downstream +downward +DPI +DR +dragons +drain +draw +Draw +drawing +Drawing +DRAWINGS +drawn +Drepper +drh +drive +driver +Driver +drop +Drop +DROP +dropped +dropping +drops +drw +ds +dsb +Dsmall +dsohowto +dst +dsuperior +dtd +DTD +dtor +dty +du +Du +dual +Duano +duct +ductile +ductileGlyphAction +DuctileGlyphAction +ductility +due +duj +DUL +dumb +dumber +Dumbest +dummy +Dummy +Dungan +dup +dupe +duplicate +DUPLICATE +duplicated +Duplicated +duplicates +duplication +DUPLOYAN +dupped +duration +during +During +Dutch +dv +dw +dwFeatures +dwFileAttributes +dwFileFlags +DWORD +dwrite +DWrite +DWRITE +DWriteCreateFactory +dwriteFactory +DWriteFontFileLoader +DWriteFontFileStream +dwSecurityQosFlags +dwSize +dwu +dwy +dx +Dx +DxA +DxAB +DxBA +dy +DYEH +dynamic +dyu +Dyula +dz +Dzongkha +e +E +ea +EA +EAAu +EABu +each +Each +eachother +EACu +eacute +Eacute +Eacutesmall +EADu +eae +EAEu +EAFu +eager +earlier +Earlier +early +EARLY +easier +East +eastasian +Eastern +easy +EAu +eb +EB +EBAu +EBBu +EBCu +ebdt +EBDT +EBDu +EBEu +EBFu +Ebira +eblc +Ebrahim +EBu +ec +EC +ECAu +ECBu +ECCu +ECD +ecde +ECDu +ECE +ECEu +ecfb +ECFu +ecircumflex +Ecircumflex +Ecircumflexsmall +ECu +Ecuador +ed +ED +eda +EDAu +EDBu +EDCu +EDDu +EDEu +EDFu +edge +edges +edieresis +Edieresis +Edieresissmall +edit +edits +EDITS +Edo +EDu +ee +EE +EEAu +EEBu +EECu +EEDu +EEEEEELLLLLLLLuuuuuuuuGGGGGGGEEEEEEEE +EEELLLGGGGEEEE +EEEu +EEFu +EEu +ef +EF +EFEu +EFF +effect +effectively +effects +efficiency +efficiently +effort +efi +Efik +EFu +eg +Eg +egrave +Egrave +Egravesmall +Egypt +Egyptian +EGYPTIAN +eight +EIGHT +EIGHTEEN +eightinferior +eightoldstyle +eightsuperior +EINTR +either +Either +EK +ekk +el +El +ELBASAN +ELEM +element +ELEMENT +elements +Elements +ELEVEN +elidable +ELIDABLE +elidedFallbackNameID +elie +Elie +elif +ellipsis +ELLIPSIS +else +Else +elt +ELT +ELYMAIC +em +EM +embed +embedded +EmbeddedPeakTuple +embedding +EMBEDDING +emboldening +EMBOX +emdash +emk +emoji +Emoji +EMOJI +emphasis +empty +Empty +EMPTY +ems +en +EN +enable +Enable +enabled +ENABLED +enableFlags +enables +enabling +enb +enc +Enclosed +enclosing +ENCLOSING +encode +ENCODE +encoded +Encoded +encoder +Encodes +encoding +Encoding +ENCODING +encodingID +EncodingID +encodingOffset +EncodingOffset +encodingrec +EncodingRecIter +encodingRecord +EncodingRecord +encodings +encounter +encountered +end +End +END +endash +endchar +endCharCode +endcode +endCode +endConnectorLength +endCoord +endCount +endcp +ended +endGlyphID +endGlyphIndex +endian +Endian +ENDIAN +endif +ending +EndPtr +endPtsOfContours +ends +endSize +Enets +enf +enforce +engine +Engine +engines +English +ENGRAVED +enh +ENHANCEMENTS +enlarge +Enlarge +enough +ensure +Ensure +ensures +enter +EnterCriticalSection +entire +entirely +entirety +ENTITY +entries +Entries +entry +Entry +entryAnchor +EntryAnchor +EntryData +EntryExit +entryExitRecord +EntryExitRecord +entrySelector +EntryT +entryTable +enum +enumerate +enumerated +Enumerates +enumeration +enumerations +enums +env +ENV +environment +eo +eof +EOT +epsilon +eq +equal +EQUAL +equality +equally +equals +Equatorial +equivalent +Eric +err +Err +Erratic +erratically +erring +errno +ERRNO +error +Error +ERROR +errors +errs +Erzya +es +ESC +escape +escapes +eScript +Esfahbod +esg +esi +esk +Esmall +esp +especially +Esperanto +esque +essence +essentially +Estero +estimate +estimated +estimates +Estonia +Estonian +Estrangela +Estrangelo +esu +esuperior +et +etc +eth +Eth +Ethiopia +Ethiopic +ETHIOPIC +Ethsmall +eto +Eton +eu +Eu +eval +evaluate +evaluating +eve +even +Even +EVEN +Evenki +event +EVENT +events +eventual +ever +every +Every +everyone +everything +evidence +evident +evn +Ewe +ewo +Ewondo +exact +exactly +examine +examines +examining +example +examples +exceed +exceeds +except +Except +exception +exceptional +exceptions +excess +excessive +exch +exchange +exclam +EXCLAMATION +exclamdown +exclamdownsmall +exclamsmall +excluded +exclusion +exclusive +Exclusive +exclusivity +exe +executable +execute +EXECUTE +exhaust +exist +existence +existent +existing +EXISTING +exists +exit +exitAnchor +ExitAnchor +exp +EXP +expand +expanded +EXPANDED +ExpansionFactor +expect +expected +expects +expensive +experience +experimental +experimentally +expert +EXPERT +ExpertCharset +ExpertEncoding +ExpertSubsetCharset +explanation +explicit +EXPLICIT +explicitLevel +explicitly +explore +exponent +EXPONENTS +export +EXPORT +exported +exports +expose +Expose +exposed +exposing +Exposing +expr +EXPR +express +Express +expressed +expression +expressions +extend +Extend +extended +Extended +EXTENDED +extendedShapeCoverage +ExtendedTypes +Extender +EXTENDER +ExtenderGlyph +extenderGlyphs +ExtenderGlyphs +extensibility +extension +Extension +extensionDisableGPOS +extensionDisableGSUB +extensionEnableGPOS +extensionEnableGSUB +ExtensionFormat +extensionJstfMax +extensionLookupType +extensionOffset +ExtensionOffset +ExtensionPos +extensions +Extensions +EXTENSIONS +ExtensionSubst +extensively +extent +Extent +extents +EXTENTS +extern +EXTERN +external +externally +externs +extlang +extra +Extra +EXTRA +extract +extracted +EXTRAS +extreme +extremely +eye +Eye +eyelash +eyo +f +F +fa +FA +FAAu +fabs +FABu +FAbv +faca +face +Face +FACE +faceBlob +Facebook +faces +FACESIZE +facet +faceType +facilities +facility +fact +factor +FACTOR +factors +factory +FACTORY +factoryType +FACu +FADu +FAEu +FAFu +fail +Fail +FAIL +failed +Failed +FAILED +failing +FAILLING +fails +Fails +failure +FAILURE +failures +fairly +Falam +fall +Fall +fallback +Fallback +FALLBACK +falling +falls +fallthrough +FALLTHROUGH +false +FALSE +family +Family +FAMILY +FamilyBlues +familyName +FamilyName +FamilyOtherBlues +fan +Fancy +Fang +Fanti +faq +far +Faroe +Faroese +Farsi +FARSI +fashion +fast +faster +fat +fatha +FATHA +fathatan +FAu +faulty +favor +fb +FB +fba +FBAu +FBBu +FBCu +FBDu +fbe +FBFu +fbl +FBlw +FBu +fc +FC +FCAu +fcc +FCCu +FCDu +FCEu +FCFu +fclose +fcntl +FCu +fd +FD +fda +fdArray +FDArray +FDArrayInfo +FDArrayOffset +FDAu +fdcount +fdCount +FDDu +fde +FDEFs +FDEu +FDF +FDFu +FDIndex +fdmap +fds +fdsc +FDSC +fdselect +fdSelect +FDSelect +FDSELECT +FDSelectInfo +FDSelectOffset +FDu +fe +Fe +FE +feat +FEAT +FeatMinMaxRecord +featMinMaxRecords +FeatMinMaxRecords +feats +featUILableNameID +featUITooltipTextNameID +feature +Feature +FEATURE +featureCount +featureFlags +featureIndex +featureList +FeatureList +FeatureName +featureNameCount +featureParams +FeatureParams +FeatureParamsCharacterVariants +FeatureParamsSize +FeatureParamsStylisticSet +featureRangeLengths +features +Features +FEATURES +featureSetting +FeatureTableSubstitution +FeatureTableSubstitutionRecord +featureTableTag +featureType +featureval +FeatureVariationRecord +FeatureVariations +featureVars +featureZ +FEAu +Feb +February +FEBu +FECu +Fedora +FEDu +fee +feed +feel +fees +FEFF +FEH +fence +feof +ferror +fetch +Fetch +fetched +fetches +Fetches +fetching +FetchNextRun +FEu +few +fewer +ff +FF +FFA +FFAu +ffbfea +ffcdf +FFCu +FFEu +FFF +FFFD +FFFF +FFFFF +FFFFFF +FFFFFFF +FFFFFFFFULL +FFFFFu +FFFFu +FFFFULL +FFFu +ffi +ffl +ffm +FFu +FFULL +ffuncs +ffunctions +fh +fi +fi +fid +field +fields +FIFTEEN +figure +FIGURE +figuredash +Fijian +fil +file +File +FILE +filename +fileOffset +files +fileSize +fileType +Filipino +fill +Fill +filled +FILLER +filling +Filling +filter +Filter +filtered +filtering +filterMethod +filterRangeMaxValue +filterRangeMinValue +filters +FIN +fina +FINA +final +Final +FINAL +finalcode +finalize +finalizer +Finalizer +finally +Finally +finaLookup +finaSubLookup +find +Find +FINDFONT +finding +finds +Finds +fine +fini +finish +Finish +finished +finite +Finland +Finnish +Firefox +first +First +FIRST +firstAxis +firstAxisSegmentMaps +firstChain +firstDeviceRecord +firstGlyph +firstGlyphIndex +FirstGlyphs +firstLayerIdx +firstPairValueRecord +firstParamUILabelNameID +firstSubtable +firstSubTable +fit +FITNESS +fitting +five +FIVE +fiveeighths +fiveinferior +fiveoldstyle +fivesuperior +fix +Fix +fixed +Fixed +FIXED +fixedcs +FixedType +FixedVersion +Fixes +fixup +Fixup +fj +fl +flag +Flag +FLAG +flags +Flags +FLAGS +flat +flatStr +flatten +Flatten +FLATTENED +flattener +fLayoutRTL +Flemish +Fleurons +FLEURONS +flex +flip +flm +float +floating +fLogicalOrder +flooded +floor +florin +flow +Flowery +FLT +flush +fly +FM +FMAbv +FMBlw +fMergeNeutralItems +fmp +FMPst +fmt +fNoGlyphIndex +fo +folded +follow +followed +Followed +following +Following +follows +fon +Fon +fonipa +fonnapa +font +Font +FONT +FontBBox +fontconfig +Fontconfig +FontConfig +FontDescriptor +fontdict +FontDict +fontdicts +fontDicts +fontDictStr +fontDirectionHint +fontEmSize +fontFace +fontFile +fontFileKey +fontFileLoader +fontFileReferenceKey +fontFileReferenceKeySize +fontFileStream +FontInfo +fontlab +FontMatrix +fontName +FontName +fontRevision +fonts +Fonts +fontSzr +fonttools +fontTools +FontTools +foo +fopen +for +For +FOR +forbid +Force +ForceBold +forced +foreach +foreground +Forest +forget +forgives +fork +Fork +form +Form +FORM +format +Format +FORMAT +formatReserved +formats +Formats +formatting +formed +former +Former +formerly +forms +Forms +FORMS +FORMULA +forum +forw +forward +FORWARD +forwards +found +Found +FOUND +Foundation +four +Four +FOUR +fourinferior +fouroldstyle +foursuperior +FOURTEEN +fourth +fOverrideDirection +fp +fprintf +FPst +fr +frac +FRACT +fraction +FRACTION +fractional +fractions +FRACTIONS +fragmentContext +fragmentSize +fragmentStart +frame +framework +franc +France +frc +fread +Frédéric +free +Free +FREE +freed +freedesktop +Freedesktop +freeing +FreeLibrary +freelocale +freely +freetype +FreeType +FREETYPE +fref +French +frequent +frequently +fribidi +friend +Frisian +Friulian +from +From +FROM +fromCoord +FromGlyphs +front +frozen +frp +fRTL +fscale +Fsmall +fsref +FSRef +fsSelection +fstat +fsType +ft +FT +FTStringRange +fu +Fu +fub +fuc +fue +fuf +fuh +fui +Fujian +Fukien +Fulah +fulfilled +Fulfulde +full +Full +FULL +fullAdvance +fullName +FullName +fullset +Fullwidth +FULLWIDTH +fully +Fully +fun +func +Func +FUNC +FUNCOBJ +funcs +Funcs +FUNCS +FUNCSIG +function +Function +FUNCTION +functionality +functions +Functions +FuncType +funcZ +fundamental +fundamentals +FUnit +FUnits +fuq +fur +further +Furthermore +Futa +future +fuv +Fuzhou +fuzz +fvar +FVAR +FVSes +FWIDTH +FWORD +fy +FYROM +g +G +ga +Ga +gaa +Gade +Gaelic +GAF +gag +Gagauz +Gah +Gahri +Galice +Galician +Galla +Gallurese +gan +Gan +Ganda +Ganja +gap +GAP +gaps +garbage +Garhwali +Garo +Garret +Garshuni +gasp +GASP +GaspRange +gaspRanges +gather +gaw +gax +gaz +GB +gbm +GBoxedCopyFunc +GBoxedFreeFunc +gbreve +Gbreve +gbytes +GBytes +gc +GC +gcc +GCC +gce +gchar +gconstpointer +gd +gda +gdef +GDEF +gdi +GDI +Ge +Geez +Gemination +GEMINATION +gen +Gen +GEN +general +General +GENERAL +generally +Generally +generate +generated +GenerateResults +generates +Generates +Generating +generic +Generic +GENERIC +geok +Geok +Geometric +Georgia +Georgian +GEORGIAN +German +germandbls +Germany +get +Get +GET +GetCharVariantIndex +getCombiningClass +GetDC +getenv +GETENV +GetFileSize +GetFileSizeEx +GetFontData +GetGlyphPlacements +getglyphs +GetGlyphs +getIntPropertyMaxValue +getIntPropertyValue +GetJustificationOpportunities +getjustifiedglyphs +GetJustifiedGlyphs +GetLastWriteTime +GetLocaleName +GetModuleHandle +getNFCInstance +getNFDInstance +GetNumberSubstitution +getpagesize +GETPAGESIZE +GetParagraphReadingDirection +GetProcAddress +getRawDecomposition +gets +Gets +getScript +GetScriptProperties +getShortName +getter +getters +GetTextAtPosition +GetTextBeforePosition +getting +Getting +gez +gfxShapedWord +ggo +GHAIN +Gheg +GHUNNA +gid +GID +gidDDD +gids +gih +Gikuyu +gil +Gilaki +Gilbertese +Gilyak +GIMEL +ginfo +git +Githabul +github +GitHub +give +Give +given +Given +gives +giving +gju +gkp +gl +Glagolitic +GLAGOLITIC +gld +glib +GLib +GLIB +glibc +GLIBC +glk +global +Global +GLOBAL +GlobalSubr +globalsubrs +globalSubrs +globalSubrsInfo +glue +glyf +GLYF +glyid +glyID +glyph +Glyph +GLYPH +glyphAdvances +GlyphAnchors +glyphArray +glyphAssembly +GlyphAssembly +GlyphBitmapDataFormat +glyphClassDef +GlyphClasses +glyphConstruction +glyphCount +glyphDataFormat +glyphDataOffsets +glyphFormat +GlyphHeader +glyphid +glyphId +glyphID +GlyphID +GLYPHID +glyphIdArray +glyphIdArrayLength +GlyphIDs +glyphIndex +glyphIndices +glyphMetrics +glyphNameIndex +glyphOffsets +GLYPHPROP +glyphProperties +glyphs +Glyphs +GLYPHS +glyphset +GlyphVarData +GM +gmappedfile +GMT +gn +gname +gnn +gno +gnome +gnu +GNU +GNUC +gnw +go +goal +GOAL +goals +Goan +gobject +GObject +GOBJECT +GOFFSET +gog +Gogo +going +gom +gon +Gondi +GONDI +gone +good +google +Google +Goronzy +got +Got +Gothic +GOTHIC +goto +gpos +GPOS +GPOSProxy +gr +grab +graduated +grained +granted +GRANTED +Grantha +GRANTHA +granular +granularly +graph +grapheme +Grapheme +GRAPHEME +graphemes +Graphemes +GRAPHEMES +graphic +graphics +Graphics +graphicType +graphite +Graphite +GRAPHITE +grave +Gravesmall +greater +greaterequal +Greece +Greek +GREEK +green +Green +Greenland +Greenlandic +greg +grep +grface +Grid +GRID +Grigori +grigorig +group +GROUP +grouped +Groupings +grouprecord +groups +grow +growFlags +growing +grows +growth +grt +gru +gsize +Gsmall +gsub +GSUB +gsubgpos +GSUBGPOS +GSUBProxy +gsw +gt +gtk +GTK +gtype +GType +gu +Guarani +Guaraní +guarantee +guaranteed +guarantees +guards +Guatemala +guc +GUEH +guess +guessing +guf +gug +gui +Guibei +Guibian +guillemotleft +guillemotright +guilsinglleft +guilsinglright +Guinea +Guiyang +Gujarati +GUJARATI +Gujari +Guji +GUJR +guk +Gulf +Gumatj +Gumuz +gun +GUnicodeScript +GUnicodeType +Gunjala +GUNJALA +Gupapuyngu +Gurage +Gurmukhi +GURMUKHI +GURU +Gusii +guz +gv +gvar +GVAR +gwi +Gwich +GX +gxFontDescriptor +gzip +h +H +ha +haa +hack +HACKMEM +had +Hadothi +Hadrami +hae +HAFS +HAH +Haible +HAIR +Haitian +Haji +hak +Hakha +Hakka +Halam +halant +Halant +HALANT +halants +half +Half +HALF +Halfwidth +HALFWIDTH +Halh +HALN +Hamer +Hammer +HAMZA +HAMZAH +Han +HAN +hand +Hand +handed +handing +handle +Handle +HANDLE +handled +handler +handles +handling +hang +hanging +Hanging +HANGING +hangul +Hangul +HANGUL +Hanifi +HANIFI +HANJA +hans +Hans +hant +Hant +Hanunoo +HANUNOO +happen +happened +happening +happens +Happens +happier +happy +har +Harari +Harauti +hard +harder +hardest +harfbuzz +HarfBuzz +HarffBuzz +Haryanvi +has +Has +HAS +hash +hashmap +Hat +hataf +HATRAN +HAU +Hausa +have +Have +HAVE +having +haw +Hawaiian +hAxis +hay +Haya +haz +Hazaragi +hb +HB +HBASELINE +HBFixed +HBGlyphID16 +HBINT +hbot +hbotABCD +hbsc +hbshape +hbsubset +HBUCHAR +HBUINT +HBUSHORT +hbview +hdc +HDC +hdmx +HDMX +he +HE +hea +head +Head +HEAD +header +Header +HEADER +headerfile +headers +Headers +headerSize +HeadlessArrayOf +heap +heavier +heavily +hebrew +Hebrew +HEBREW +HEH +height +HEIGHT +heightCount +heights +help +helper +Helper +helpers +helpful +helps +hence +Hence +here +Here +hereby +Herero +HEREUNDER +Herzegovina +HET +heuristic +Hexagram +hflex +hfont +HFONT +hh +HH +hhcurveto +hhea +HHEA +hi +hidden +HIDDEN +hide +HIDE +hiding +HIEROGLYPHS +high +High +HIGH +higher +highest +Highland +HIGHLEVEL +highlight +highlighting +highly +Hijazi +hil +Hiligaynon +himalaya +Himalaya +Hindi +Hindko +hinstLib +hint +hinted +hinting +HINTING +HintingDevice +hintmask +hints +Hiragana +HIRAGANA +Hiri +hiriq +HIRIQ +historical +HISTORICAL +history +hit +hji +hk +HK +hlineto +hlt +hma +hmc +hmd +hme +hmetrics +hmg +hmh +hmi +hmj +hml +hmm +hmn +HMODULE +Hmong +HMONG +hmoveto +hmp +hmq +hms +hmtx +HMTX +hmtxvmtx +hmw +hmy +hmz +HN +hnd +hne +hnj +hno +ho +Ho +hoc +hoi +hoj +HOJO +Hokkien +Hoklo +holam +HOLAM +hold +holder +Holder +HOLDER +holding +holds +Holikachuk +home +Homebrew +Honduras +Hong +Hongshuihe +hood +hook +Hook +hooks +horiBearingX +horiBearingY +horiz +HORIZ +horizData +horizGlyphCount +horizGlyphCoverage +horizontal +Horizontal +HORIZONTAL +horizontally +Horned +Hosken +Hosny +hosted +house +House +how +however +However +HOWEVER +HP +hr +HRESULT +hrm +hsb +Hsmall +hsn +hstem +hstemhm +ht +hTemplateFile +html +http +https +hu +Huallaga +Huamalíes +Huánuco +Huaylas +Huaylla +huge +Huishui +Huizhou +huj +human +Humm +Hungarian +HUNGARIAN +hungarumlaut +Hungarumlautsmall +Hungary +hup +Hupa +hvar +HVAR +HVARTag +HVARVVAR +hvcurveto +HVM +hy +hyphen +HYPHEN +hyphenation +hypheninferior +HYPHENS +hyphensuperior +hyw +hz +i +I +ia +iacute +Iacute +Iacutesmall +iba +Iban +ibb +Ibibio +IBMCPP +ibmxl +ibo +ic +Iceland +Icelandic +iCharPos +iche +icircumflex +Icircumflex +Icircumflexsmall +icu +ICU +id +ID +ida +Idakho +idDelta +idea +ideal +Ideally +ideas +IDEFs +idempotent +identical +identically +identification +identified +identifier +identifiers +identifies +identify +identifying +identity +IDEO +Ideograms +Ideographic +IDEOGRAPHIC +Ideographs +IDEOGRAPHS +idieresis +Idieresis +Idieresissmall +Ido +Idotaccent +idRangeOffset +ids +IDs +IDWriteFactory +IDWriteFontFace +IDWriteFontFile +IDWriteFontFileLoader +IDWriteFontFileStream +IDWriteNumberSubstitution +IDWriteTextAnalysisSink +IDWriteTextAnalysisSource +IDWriteTextAnalyzer +idx +ie +Ie +ietf +IETF +if +If +IF +IFACEMETHOD +IFACEMETHODIMP +ifdef +ifelse +iff +ifndef +ig +Igalia +igb +Igbo +ignorable +Ignorable +IGNORABLE +ignorables +IGNORABLES +ignore +Ignore +IGNORE +IgnoreBaseGlyphs +ignored +Ignored +IGNORED +IgnoreFlags +IgnoreLigatures +IgnoreMarks +ignoring +igrave +Igrave +Igravesmall +IHDR +ii +II +iid +IID +ijc +ijo +Ijo +ik +ike +ikt +ill +illegal +ILLUMINATED +illustrates +ilo +Ilokano +Iloko +image +imageDataOffset +imageFormat +imageOffsetsZ +images +imagine +Imbabura +IMC +immediately +immutable +IMPERIAL +impl +IMPL +implement +Implement +IMPLEMENT +implementation +IMPLEMENTATION +implementations +Implementations +implemented +IMPLEMENTED +implementing +Implementing +implementor +implements +Implements +implicit +IMPLICIT +implied +IMPLIED +implies +important +Important +impose +impossible +improve +improved +iMu +in +In +IN +inaccuracy +Inari +inc +Inc +inch +INCIDENTAL +Incidentally +include +Include +included +includes +Includes +including +Including +INCLUDING +inclusion +inclusive +incoming +incompatible +incomplete +inconsistencies +incorporating +incorrect +incorrectly +incorrectness +increase +Increase +increased +increases +Increases +increasing +incrementally +incurs +IND +indeed +indefinitely +indented +independent +Independent +INDEPENDENT +independently +index +Index +INDEX +IndexArray +indexed +Indexed +indexes +indexFormat +indexing +IndexMask +IndexOf +IndexSubtable +IndexSubtableArray +indexSubtableArrayOffset +IndexSubtableFormat +IndexSubtableHeader +IndexSubtableRecord +indexSubtablesZ +indexTablesSize +indexToLocFormat +India +indic +Indic +INDIC +indicate +indicated +indicates +Indicates +indicating +indication +indices +indicies +IndicPositionalCategory +IndicShapingInvalidCluster +IndicSMatraCategory +IndicSyllabicCategory +IndicSyllableCategory +INDIRECT +indirection +individual +Individual +individually +indivisible +Indonesia +Indonesian +inds +industry +IndV +indx +indy +ineffective +inefficient +INEQUALITY +inert +INERT +inf +infer +Infer +INFERIORS +inferred +infinite +infinitum +infinity +info +INFO +informaltable +informatimago +information +Information +infos +infrequent +ing +Ingush +inh +INHERENT +inherit +Inherit +INHERITED +inherits +inhibit +init +Init +INIT +initial +Initial +INITIAL +Initialization +initialize +InitializeCriticalSection +InitializeCriticalSectionEx +initialized +initializer +INITIALIZER +initializers +initially +Initially +initialRandomSeed +initiated +initLookup +initmediSubLookup +initpos +initSubLookup +InitT +ink +inline +inner +innerIndex +inout +INOUT +inplace +input +Input +INPUT +inputClassDef +inputCount +inputs +inputX +inputZ +Inremental +ins +insane +INSCRIPTIONAL +insert +Insert +INSERT +inserted +insertion +Insertion +insertionAction +insertions +InsertionSubtable +inserts +inside +Inside +inspect +inspecting +inspects +install +Install +installed +installing +Installing +instance +INSTANCE +instanceCoords +instanceCount +InstanceRecord +instances +Instances +instanceSize +INSTANTIATE +instantiated +instead +Instead +instruct +instruction +instructionLength +instructions +Instructions +INSTRUCTIONS +INSUFFICIENT +int +Int +INT +integer +Integer +INTEGER +integers +integral +integrate +integrating +integration +Integration +intel +INTEL +intended +intentional +intentionally +Intentionally +Inter +interact +InterCharacter +interest +interested +interface +interfaces +interfering +interim +Interix +interlaceMethod +Interlingua +Interlingue +InterlockedCompareExchangePointer +InterlockedExchange +InterlockedExchangeAdd +intermediate +intermediateEndTuple +IntermediateRegion +intermediateStartTuple +intermixed +intern +internal +Internal +INTERNAL +internally +Internally +internals +International +INTERNATIONAL +InternationalSymbols +interp +INTERP +interpolate +Interpolate +interpolation +interpret +interpretation +Interpretation +interpreter +interpreting +INTERROBANG +intersect +intersections +intersects +INTERSECTS +interviews +intl +into +intOp +intptr +intrin +intro +introduced +Introduced +introducing +Introduction +ints +IntType +INTTYPE +inttypes +intuition +Inuinnaqtun +Inuktitut +Inupiaq +Inupiat +Inupiatun +invalid +Invalid +INVALID +invert +INVERTED +inverts +investigated +investigation +invisible +Invisible +INVISIBLE +invocation +invoke +invoked +invol +involve +involved +involves +io +iOS +iota +IOTA +ip +IPA +IPHONE +Iran +Iranian +IranNastaliq +Iraq +Ireland +Irish +irrelevant +is +Is +IS +Isaac +ISALNUM +ISALPHA +ISC +isCombinedS +isCombiningL +isCombiningT +isCombiningV +isEmojiFont +isFixedPitch +ish +isHangulTone +isiXhosa +isiZulu +isL +Islamic +Islands +Ismall +isn +iso +ISO +ISOAdobeCharset +ISOL +ISOLATE +isolated +ISOLATED +Israel +isRightToLeft +ISSPACE +issue +issuecomment +issues +issuetracker +isSupported +isT +Isukha +isuperior +isV +it +It +ital +Italian +italic +Italic +ITALIC +italicAngle +ItalicAngle +italics +Italics +italicsCorrection +Italy +item +Item +ITEM +itemCount +itemizedlist +itemizer +items +Items +ITEMS +ItemSize +iter +Iter +ITER +iterable +Iterable +iterables +iterate +Iterate +iterated +iteration +iterations +iterator +Iterator +IteratorIn +IteratorOut +iterators +iters +its +Its +ITS +itself +iu +IUnknown +ivs +iw +Izon +izzi +j +J +ja +jak +Jakun +jam +Jamaica +Jamaican +Jambi +jamo +Jamo +January +Japan +Japanese +Jauja +Javanese +JAVANESE +jax +jbo +jct +JEEM +JEH +jg +ji +Jicarilla +Jing +Jinyu +JIS +JMO +job +join +joiner +Joiner +JOINER +joiners +JOINERS +joining +Joining +JOINING +Jonathan +Jordan +jpg +Jsmall +json +JSON +jstf +JSTF +JstfLangSys +JstfLangSysRecords +JstfMax +JstfModList +JstfPriority +JstfScript +JstfScripts +jt +Jula +JUNGSEONG +Junín +junk +just +Just +JUST +justClass +JustClass +justClassTable +justification +Justification +JUSTIFICATION +JustificationCategory +justificationCharacter +JustificationHeader +justificationOpportunities +justified +justifiedGlyphAdvances +justifiedGlyphOffsets +justify +JustifyGlyphAdvances +JustWidthDeltaEntry +jv +jw +jy +k +K +ka +kaa +kab +Kabardian +Kabras +Kabuverdianu +Kabyle +Kachchi +Kachhi +KAF +Kaithi +KAITHI +Kalenjin +Kalmyk +Kalo +Kalpak +kam +Kamba +Kambaata +kana +KANA +Kanaq +Kanauji +Kanbun +Kangri +Kangxi +Kanji +Kannada +KANNADA +Kanuri +Kaqchikel +kar +Kara +Karachay +Karaim +Karakalpak +Karelian +Karen +kashida +Kashida +KashidaLike +Kashmiri +Kashubian +Kaska +kasra +kasratan +Katakana +KATAKANA +Katanga +Kato +kau +Kaur +Kayah +KAYAH +Kazakh +Kazakhstan +Kazim +kb +kbd +kby +kByte +kca +kCFAllocatorDefault +kCFAllocatorNull +kCFBooleanTrue +kCFCompareEqualTo +kCFNumberIntType +kCFStringEncodingUTF +kCFTypeArrayCallBacks +kCFTypeDictionaryKeyCallBacks +kCFTypeDictionaryValueCallBacks +kCTFontAttributeName +kCTFontCascadeListAttribute +kCTFontEmphasizedSystemFontType +kCTFontFeatureSelectorIdentifierKey +kCTFontFeatureSettingsAttribute +kCTFontFeatureTypeIdentifierKey +kCTFontPostScriptNameKey +kCTFontSystemFontType +kCTFontUIFontEmphasizedSystem +kCTFontUIFontSystem +kCTFontURLAttribute +kCTKernAttributeName +kCTLanguageAttributeName +kCTRunStatusNonMonotonic +kCTRunStatusRightToLeft +kCTTypesetterOptionForcedEmbeddingLevel +kCTVersionNumber +kCTVerticalFormsAttributeName +kde +kdr +kdt +kea +Kebena +Kedah +keep +Keep +keeping +KEHEH +Keith +Keiyo +kek +Kekchi +Kentohe +Kenya +kept +Kerinci +kern +KERN +KernAAT +KernAATSubTableHeader +kernAction +kernActionIndex +KERNEL +kernIndex +kerning +Kerning +KERNING +kerningv +KernOT +KernOTSubTableHeader +KernPair +kerns +KernSubTable +KernSubTableFormat +KernSubTableHeader +kernValue +kernValueCount +kernValueZ +kerx +KERX +KerxSubTable +KerxSubTableFormat +KerxSubTableHeader +KerxTable +kerxTupleKern +Kew +kex +key +Key +KEY +keys +kfa +KFGQPC +kfr +kfx +kfy +kg +kha +KHAH +Khakas +Khakass +Khaled +Khamti +Khanty +Kharoshthi +KHAROSHTHI +Khasi +Khayo +khb +Khengkha +Khimi +khk +khmer +Khmer +KHMER +KhmerUI +Khojki +KHOJKI +Khorasani +Khowar +kht +Khudawadi +KHUDAWADI +Khumi +Khutsuri +khw +ki +kick +KIKAKUI +Kikongo +Kikuyu +Kildin +Killer +KILLER +Kimbundu +kind +kinda +kinds +Kingdom +kinoho +kINVALID +Kinyarwanda +Kiowa +Kipsigis +Kirghiz +KIRGHIZ +Kiribati +Kirmanjki +Kisa +Kisi +Kisii +Kissi +Kistane +Kiswahili +Kita +Kituba +kiu +Kiwai +kj +kjd +kjh +kjp +kjz +kk +kkz +kl +klass +klasses +kln +km +kMaxCallLimit +kmb +kmr +kmw +kmz +kn +knc +KNDA +kng +knn +know +knowing +Knowing +knowledge +known +Known +knows +Knuth +ko +Ko +Kodagu +Kodava +koi +kok +Kokni +Kölsch +Komi +Komo +Komso +Kong +Kongo +Konkani +Konso +Konyanka +Koongo +Koorete +Korea +Korean +Koryak +kos +Kosraean +Kota +koy +Koyukon +kpe +Kpelle +kpv +kpy +kqs +kqy +kr +krc +kri +Krio +krl +krt +kru +Krymchak +ks +ksh +kShort +kSizeLimit +Ksmall +kss +ksw +ktb +ktu +ktw +ku +Kuanyama +Kubu +Kui +Kukna +Kullu +Kulvi +kum +Kumaoni +Kumyk +Kumzari +Kuna +Kurdish +Kurukh +Kuskokwim +Kutai +kuu +Kuwait +Kuy +kv +kvb +kvr +kw +KW +kwy +kxc +kxd +kxu +ky +Kyrgyz +Kyrgyzstan +kyu +kZero +l +L +la +La +Laari +label +LABEL +labels +lack +lad +Ladakhi +Ladin +Ladino +Lahuli +laid +Lak +Laki +Lalana +Lam +LAM +lamAlefLigaturesSubLookup +Lambadi +Lambani +Lambayeque +LAMED +lamInitLigature +lamLigatureSet +Lampung +lang +langs +langsys +langSys +LangSys +LANGSYS +LangSysRecords +LangSysTag +LangTag +language +Language +LANGUAGE +LanguageGroup +languageID +languages +Languages +LANGUAGES +languagetags +Lanka +Lao +LAO +large +Large +LARGE +larger +largest +Largest +LArrayOf +lash +last +Last +LAST +lastCode +lastGlyphIndex +LastResort +lastWriteTime +LATE +later +latest +latg +Latg +Latgalian +latin +Latin +LATIN +latn +latter +Latvia +Latvian +Lauricocha +Lawoi +lay +layer +Layer +layering +LayerRecord +layers +layersZ +layout +Layout +LAYOUT +LayoutRTL +lays +Laz +lazily +lazy +Lazy +lb +LBAR +LBase +LBASE +lbe +lbj +lbl +LC +lcar +LCAR +lcarFormat +lce +lcf +LCount +LCOUNT +ld +ldi +Le +LE +lead +LEADER +leading +Leading +LEADING +leadingBearingX +Lealao +LEAN +leans +least +leave +Leave +LeaveCriticalSection +leaving +Lebanon +Leboa +left +Left +LEFT +leftC +leftClass +leftClassCount +leftClassTable +leftSide +legacy +Legacy +LEGACY +legally +legit +Lemberg +len +LEN +LENG +length +Length +LENGTH +lengthed +lengths +lengthy +lenient +lenM +lenP +LENTICULAR +LenType +Leone +Lepcha +LEPCHA +less +lessequal +let +Let +lets +Lets +letter +Letter +LETTER +Letterlike +letters +Letters +Levantine +level +Level +LEVEL +levels +leverage +lexicographic +lez +Lezghian +Lezgi +lf +LF +lfCharSet +lfFaceName +lfHeight +lg +lhs +Lhs +li +Li +LI +LIABLE +Lianshan +libc +libcairo +Liberia +libfreetype +libglib +libharfbuzz +libkern +Libon +libraries +Libraries +library +Library +LibreOffice +libs +libstdc +libtool +Libya +Libyan +license +LICENSE +lidentity +Liechtenstein +lif +life +lifecycle +lifecycles +lig +LIG +liga +ligAction +LigActionFlags +ligActionIndex +LigActionLast +LigActionOffset +LigActionStore +ligActionTable +ligate +ligated +LIGATED +ligating +ligation +ligature +Ligature +LIGATURE +ligatureArray +LigatureArray +LigatureAttach +ligatureCoverage +LigatureCoverage +ligatureData +LigatureEntry +LigatureEntryT +LigatureGlyph +ligatures +Ligatures +LIGATURES +ligatureSet +LigatureSet +LigatureSetOffsets +LigatureSetOffsetsArray +LigatureSubst +LigatureSubstFormat +LigatureSubtable +ligbase +LIGBASE +LigCaretClassEntry +ligCaretList +LigCaretList +ligGlyph +LigGlyph +light +Light +LIGHT +lighter +lightweight +ligs +Ligurian +lij +like +Like +likely +Likewise +Lima +Limbu +LIMBU +Limburgish +limit +Limit +LIMIT +limitation +limited +Limited +LIMITED +limiting +limits +lindex +line +LINE +linear +Linear +LINEAR +linearly +Linebreak +lineBreakpoints +lineGap +lines +lineWidth +Lingala +lingo +linguistic +LINGUISTIC +linguistically +linguistics +link +linked +linkedValue +linking +links +linux +Linux +Lipan +lis +list +List +LIST +listed +listinfo +listitem +lists +Lisu +LISU +literal +Literary +Lithuania +Lithuanian +little +LITTLE +Liujiang +Liuqian +liw +ljmo +LJMO +ljp +lkb +lki +lklug +lko +lks +ll +Ll +lld +LLLEEEEEEEGGGG +LLONG +LLVM +lm +Lm +lmn +lmo +ln +LNNOffsetTo +lo +Lo +load +Load +LOAD +loaded +loader +loaders +loading +LoadLibrary +loc +loca +local +locale +Locale +LOCALE +localeName +localized +localsubr +LocalSubr +localsubrs +localSubrs +localSubrsInfos +Locate +located +location +Location +locations +lock +lockable +Lockable +locl +locName +LOffsetArrayOf +LOffsetLArrayOf +LOffsetTo +log +LOG +LOGFONT +logfontw +LOGFONTW +logic +logical +Logical +logicalnot +LogicalOrder +Logo +logograms +Logooli +LOGOS +Logudorese +Lohar +lohit +Lohit +Loja +lojban +Lojban +lom +Loma +Lombard +Lomwe +Loncong +lone +Lonely +long +Long +LONG +LONGDATETIME +longer +longint +longintdict +LongMetric +longMetricZ +longword +look +Look +lookahead +lookaheadClassDef +lookaheadCount +lookaheadX +looked +looking +Looking +looks +Looks +lookup +Lookup +LOOKUP +lookupCount +lookupFlag +LookupFlag +LookupFlags +LookupFormat +lookupIndex +lookupList +LookupList +lookupListIndex +lookupOffset +lookupOrderZ +lookupRecord +LookupRecord +LookupRecords +lookupRecordX +lookups +Lookups +LOOKUPS +LookupSegmentArray +LookupSegmentSingle +LookupSingle +lookupTable +lookupType +LookupType +lookupX +loop +Loop +loops +loose +loosely +Lortie +lose +LOSS +lossless +lost +lot +Lots +low +Low +LOW +lower +Lower +LOWER +lowercase +LOWERCASE +lowercased +lowerLimit +lowest +lowestRecPPEM +Lowland +LowPart +lozenge +lParameter +lps +lpSecurityAttributes +LR +lrc +lri +lrm +lsb +lsbMap +lsearch +lslash +Lslash +Lslashsmall +lsm +Lsmall +lsuperior +lt +Lt +ltag +LTAG +ltg +lto +ltr +LTR +lts +lu +Lu +Lü +lua +Luba +Lubu +Lucian +Lue +LUE +Lule +Lulua +luo +Luo +Luopohe +Luri +lus +Lushai +lux +Luxembourg +Luxembourgish +luy +Luyia +luz +lv +LV +lvalue +lvalues +lvs +LVT +lwg +lwsync +lx +Lycian +LYCIAN +Lydian +LYDIAN +lzh +lzz +m +M +ma +Maasina +MAbv +mac +Mac +MAC +Macao +Macedonia +Macedonian +machine +MACHINE +machinery +MACHINERY +machines +Macintosh +macos +macOS +MacPorts +macro +macrolanguage +macroman +MACROMAN +macron +Macronsmall +macros +Macros +macStyle +mad +MADDA +MADDAH +made +Madura +Madurese +mag +Magahi +magic +magicNumber +Mahafaly +Mahajani +MAHAJANI +Mahjong +mai +MAI +mailing +mailman +main +Main +MAIN +mainly +maintain +maintained +MAINTENANCE +MAITAIKHU +Maithili +Majang +major +Major +MAJOR +mak +Makasar +MAKASAR +make +Make +MAKE +makeotf +makeOTF +MakeOTF +makes +Makes +Makhuwa +making +Making +Makonde +MAKSURA +Malagasy +Malay +Malayalam +MALAYALAM +Malaysia +Maldives +Maldivian +Male +Malinke +malloc +Malta +Maltese +Malvi +mam +Mam +man +Manado +manage +managed +management +manages +Manchu +Mandaic +MANDAIC +Mandar +Mandarin +Mandingo +Mandinka +Manga +Manichaean +MANICHAEAN +manifest +Manifest +MANIFEST +manifestData +ManifestLookup +Maninka +Maninkakan +Manipuri +manner +manpage +Mansi +manual +Manual +MANUAL +manufacturer +MANUFACTURER +Manx +many +Many +Maore +Maori +map +Map +MAP +mapCount +mapDataZ +mapLen +mapped +mapper +mapping +Mapping +mappings +maps +Maps +Mapudungun +MapViewOfFile +MapViewOfFileFromApp +Mara +Marachi +Marama +Marathi +MARBUTA +March +Marchen +MARCHEN +margins +Margos +Mari +mark +Mark +MARK +markAnchor +markAnchorPoint +markArray +MarkArray +markAttachClassDef +MarkAttachmentType +markBase +MarkBase +MarkBasePos +MarkBasePosFormat +MarkCategory +markControlPoint +markCoverage +MarkCoverage +marked +markedInsertBefore +MarkedInsertBefore +markedInsertCount +MarkedInsertCount +markedInsertIndex +markedInsertList +MarkedIsKashidaLike +markers +markFilteringSet +markFilteringSetX +MarkFirst +MarkGlyph +MarkGlyphSets +markGlyphSetsDef +MarkGlyphSetsFormat +markIndex +marking +MarkLast +markLig +MarkLig +MarkLigPos +MarkLigPosFormat +markMark +MarkMark +MarkMarkPos +MarkMarkPosFormat +MarkRecord +MarkRecords +marks +Marks +MARKS +Markweeta +markX +markY +Marma +Marshallese +Martin +Martín +Marwari +Masaram +MASARAM +Mashan +Masikoro +mask +Mask +MASK +masks +Masks +master +Master +MASTERS +match +Match +MATCH +matched +matcher +matches +matching +material +math +Math +MATH +mathConstants +MathConstants +mathematical +Mathematical +MATHEMATICAL +mathematics +MathGlyphAssembly +MathGlyphConstruction +mathGlyphInfo +MathGlyphInfo +MathGlyphPartRecord +mathGlyphVariantRecord +MathGlyphVariantRecord +MathGlyphVariantRecords +mathItalicsCorrectionInfo +MathItalicsCorrectionInfo +mathKern +MathKern +mathKernCoverage +mathKernInfo +MathKernInfo +MathKernInfoRecord +mathKernInfoRecords +MathKernInfoRecords +MathSymbols +mathTopAccentAttachment +MathTopAccentAttachment +MathValueRecord +mathValueRecords +MathValueRecords +mathValueRecordsZ +mathVariants +MathVariants +matra +Matra +MATRA +matras +Matras +matrix +Matrix +matrixZ +matter +Matthias +Mattole +Matu +max +Max +MAX +maxBeforeBL +maxComponentDepth +maxComponentElements +maxCompositeContours +maxCompositePoints +maxContours +maxCoord +MaxDebugDepth +maxExtent +maxFunctionDefs +maxGlyphCount +maximum +Maximum +maximumLimit +maximums +maxInstructionDefs +maxMemType +maxp +MAXP +maxPoints +maxpV +maxSizeOfInstructions +maxStackElements +maxStorage +maxTwilightPoints +maxVal +maxValue +maxWidth +maxZones +may +May +MAY +Mayan +maybe +Maybe +MAYBE +Mayek +MAYEK +Mayo +Mazanderani +mb +MB +mbarrier +Mbembe +mBidiLevel +MBlw +mbo +Mbo +mbstowcs +Mbundu +Mbyá +Mc +mcm +mct +mCurrentRun +md +MD +mdash +mData +mdf +mdr +mdy +me +Me +mean +MEAN +meaning +meaningfully +meanings +means +Meanwhile +measurable +measure +MEASURE +measured +measuring +mechanical +mechanism +MED +MEDEFAIDRIN +medi +MEDI +medial +Medial +MEDIAL +median +medifinaLamAlefSubLookup +mediLookup +mediSubLookup +Medium +MEDIUM +Medumba +MEEM +Meetei +MEETEI +Meh +MEM +memaccess +member +members +Members +memcmp +memcpy +memmove +memoize +memory +Memory +MEMORY +MemoryBarrier +memset +men +Mende +MENDE +Mengisa +mentioned +mentions +menu +meo +mer +MERCHANTABILITY +merge +Merge +merged +merger +merges +merging +Merging +MEROITIC +Meru +Merwari +Mescalero +Mesopotamian +message +MESSAGE +messaging +messed +meta +Meta +META +metadata +Metadata +metamorphosis +Metamorphosis +meteg +method +methods +Methods +metric +METRIC +metricDataFormat +metrics +Metrics +METRICS +Mewari +Mewati +Mexico +mfa +mfb +mfe +mFontFileStream +mg +mGlyphCount +mGlyphStart +mh +MH +mhr +mhv +mi +Miao +MIAO +Michiharu +micro +MICRO +microsoft +Microsoft +microsqoft +MicroType +mid +middle +Middle +midnight +might +Might +min +Min +MIN +minAdvanceSB +minAfterBL +Minangkabau +minConnectorOverlap +minCoord +mind +mingw +MinGW +MINGW +minHeight +MINI +minimal +minimum +Minimum +minimumLimit +minimums +Minjangbal +Minjungbal +minLeadingBearing +minlen +minMax +MinMax +minMaxCoord +minMemType +Minnan +minor +MINOR +minOriginSB +minorVersion +minstd +minTrailingBearing +minus +MINUS +minVal +minValue +minVersion +Minz +Mirandese +Miraya +mirror +mirroring +Mirroring +misc +Misc +Miscellaneous +MISMATCH +mIsSideways +missing +MIT +mix +mixed +mixin +Mixin +Mixing +mixture +Mizo +mk +mkmk +mku +mkw +ml +ML +mLocaleName +mlq +MLYM +mm +MM +mman +MMAN +mmap +Mmap +MMAP +mmr +mn +Mn +mnc +mnemonics +mni +mnk +mnp +mns +mnw +mo +mod +MOD +mode +Mode +MODE +model +models +Modern +modes +Modi +MODI +modification +Modification +modifications +MODIFICATIONS +modified +Modified +MODIFIED +modifiedClusterMap +modifiedGlyphAdvances +modifiedGlyphIndices +modifiedGlyphOffsets +modifier +Modifier +MODIFIER +modifiers +modify +Modify +modifying +Modifying +MODIFYING +modulo +moh +Mohawk +Moksha +Moldavian +Moldova +Moldovan +Moluccan +Mon +Monaco +Mongolia +mongolian +Mongolian +MONGOLIAN +Mono +monospaced +MONOSPACED +monotone +MONOTONE +monotonic +monotonically +monster +Months +Moose +more +More +MORE +MoreToolbox +Morisyen +Moroccan +Morocco +morphHeader +mort +MORT +mortmorx +morx +MORX +mos +Mossi +most +Most +mostly +Motorola +Motu +move +Move +moved +moves +moveto +moving +Moving +mozilla +Mozilla +mpe +MPre +mprotect +MPROTECT +MPst +mqg +mr +MR +mReadingDirection +mrh +mrj +Mro +MRO +mRunHead +ms +MS +msc +MSC +mScript +msdn +msg +MSG +msgidx +msgstr +msh +msi +mSize +Msmall +msuperior +MSVC +mt +MT +mText +mTextLength +mTextStart +mtr +mtx +mu +much +mui +MUJ +mul +MulFix +Muller +mult +Multani +MULTANI +multi +multiple +Multiple +MULTIPLE +MultipleSubst +MultipleSubstFormat +multiplication +multiplicative +multiplied +MULTIPLIED +multiply +MULTIPLY +multiplying +mults +Mundari +munmap +mup +muq +mus +Muscogee +Musi +Musical +MUSICAL +must +Must +MUST +mutable +mutex +MUTEX +mutually +mvar +MVAR +mvb +mve +mvf +MW +Mwali +mwk +mwl +mwr +mww +my +MY +myanmar +Myanmar +MYANMAR +mym +mymr +myn +myq +myv +mzn +n +N +na +NABATAEAN +Nacional +nag +Naga +Nagari +Nagri +NAGRI +nags +nah +Nahuatl +naive +Najdi +nalf +nalfType +name +Name +NAME +named +Named +nameid +nameID +NameID +nameids +nameIndex +NameIndex +nameIndexOffset +nameList +namely +Namely +nameOffset +NameRecord +nameRecordZ +names +Names +NAMES +nameSIDs +namespace +Namespace +NAMESPACE +namesX +namesZ +nameTag +Naming +nan +Nan +Nanai +Nandi +Nandinagari +NANDINAGARI +nap +Napo +NARROW +narrowing +nasalization +Naskapi +native +NativeFontResourceDWrite +natural +Nauru +Nauruan +navajo +Navajo +nb +NC +nClasses +nCodes +NCount +NCOUNT +nd +Nd +ndash +Ndau +ndc +Ndebele +NDEBUG +Ndonga +nds +Ndzwani +ne +Neapolitan +necessarily +necessary +need +Need +needed +needing +needs +Needs +neg +NEG +negation +negative +Negative +NEGATIVE +Negeri +NEGLIGENCE +negotiate +neighboring +neither +nel +Nenets +Neo +Nepal +Nepali +nesting +NESTING +net +NetBSD +Netherlands +neuter +neutrals +never +Never +nevertheless +new +New +NEW +Newa +NEWA +Newari +newBits +newCount +newer +newlocale +NEWLOCALE +newly +newRun +newState +next +Next +NEXT +nextRun +NFC +NFD +ng +NG +nga +Ngawn +Ngazidja +Ngbaka +ngl +ngo +NGOEH +Ngoni +nhd +nibble +Nibble +nibbles +Nicaragua +nice +Niger +Nigeria +Nigerian +nikhahit +Nikhahit +NIKHAHIT +nil +NIL +Nimadi +nindex +nine +NINE +nineinferior +nineoldstyle +ninesuperior +NINETEEN +niq +Nirmala +Nisi +niu +Niuean +niv +NJ +Njua +njz +NKD +nko +NKo +NKO +Nkoo +nl +Nl +NLCCHARACTERS +nle +nLeft +nmemb +nn +NNOffsetTo +no +No +NO +nod +node +nodes +noe +noErr +nog +Nogai +nominal +NOMINAL +nominalValue +nominalWidthX +non +Non +NON +Nonaka +NonAlphabetic +nonbreakingspace +noncontextual +Noncontextual +NoncontextualSubtable +nonDefault +nonDefaultUVS +NonDefaultUVS +none +NONE +nonexistent +Nong +nonliteral +nonmarkingreturn +nonmonotonic +nonnull +nonspacing +NonStop +nonzero +NOON +NOP +noporpoise +nor +Nor +NORESERVE +Norfolk +normal +NORMAL +normalization +Normalization +NORMALIZATION +normalize +NORMALIZE +normalized +normalizer +normally +Normally +North +NORTH +Northeastern +Northern +Northwest +Northwestern +Norway +Norwegian +noStretchValue +not +Not +NOT +notable +notably +Notably +Notation +notdef +NOTDEF +NotDefault +note +Note +NOTE +noted +notequal +notes +NOTES +nothing +Nothing +notice +Notice +noticeably +notification +notified +notifiers +NOTIMPL +noting +notionally +Noto +NotoSerif +nounihan +nov +novalidate +NOVAR +Novial +now +Now +np +npi +nqo +nr +nRanges +nSettings +nSizes +nsk +NSLanguage +Nsmall +nso +nSubrs +nsuperior +nSups +ntilde +Ntilde +Ntildesmall +nTracks +Nüa +Nuke +NUKT +nukta +Nukta +NUKTA +nul +NUL +null +Null +NULL +nullable +NullHelper +NullPool +NullPriority +nullptr +num +Num +NUM +numBaseGlyphs +number +Number +NUMBER +numberOfContours +numberOfFaces +numberOfIndexSubtables +numberOfLongMetrics +numbers +Numbers +NUMBERS +numbersign +numberSubstitution +numBlends +numColorRecords +numColors +numeral +NUMERAL +numerals +Numerals +numeration +NUMERATOR +numeric +numerical +Numerical +numGlyphs +numLayers +numNamedParameters +numOfHMetrics +numPalettes +numParameters +numr +numRecords +numScriptCode +numTables +numValues +NUN +nUnits +NUSHU +Nuskhuri +nv +ny +Nyala +Nyamwezi +Nyanja +Nyankole +nyd +NYEH +NYIAKENG +Nyishi +nym +nyn +Nynorsk +Nyore +nza +o +O +oacute +Oacute +Oacutesmall +oasis +OASIS +obj +OBJ +object +Object +OBJECT +objects +Objects +objidx +OBLIGATION +oblique +Oblique +OBLIQUE +obliqueing +obscure +Obsolete +obsoleted +ObsoleteTypes +obtained +obvious +oc +occasionally +Occitan +occupancy +occupy +occurrence +occurrences +occurring +occurs +ocircumflex +Ocircumflex +Ocircumflexsmall +odd +oddly +Odia +odieresis +Odieresis +Odieresissmall +oe +OE +OEM +OEsmall +of +Of +OF +off +Off +OFF +offer +offers +offload +offs +OFFS +offset +Offset +OFFSET +OffsetArrayOf +offsetArrayZ +OffsetListOf +offsetof +offsets +Offsets +OffsetTable +OffsetTables +OffsetTo +offsetToAxisValueOffsets +offsetToIndex +offsetToSubtable +OffsetType +offsetZ +offSize +ofs +Ofs +often +Often +og +Ogham +OGHAM +ogonek +Ogoneksmall +ograve +Ograve +Ogravesmall +Oh +Oirat +oj +ojb +ojc +ojg +Oji +Ojibwa +Ojibway +Ojitlán +ojs +ojw +ok +Ok +OK +oki +Okiek +okm +Ol +OL +old +Old +OLD +older +Older +OLDER +om +Oman +Omani +Omega +omitted +on +On +ON +once +Once +one +One +ONE +OneByteIntFirst +OneByteIntLast +onedotenleader +oneeighth +onefitted +onehalf +oneinferior +oneoldstyle +onequarter +ones +onesuperior +onethird +only +Only +ONLY +onto +OOP +op +Op +OP +opaque +Opaque +opbd +OPBD +opbdFormat +opcode +OpCode +opcodes +opeator +open +Open +OPEN +OpenBSD +opentype +OpenType +OPENTYPE +OpenTypeFontFace +OpenTypeFontFile +OpenTypeTable +operand +operands +operate +operates +operating +operation +operations +Operations +operator +Operator +OPERATOR +operators +Operators +opportunities +OPPORTUNITY +opposite +ops +OPS +opset +OPSET +opStart +opstr +OPSTR +opsz +opszr +optical +Optical +OPTICAL +OpticalBounds +OpticalSize +optimal +optimally +optimization +optimizations +optimize +Optimize +OPTIMIZE +optimized +opting +option +OPTION +optional +Optional +OPTIONAL +optionally +options +OPTIONS +opts +or +Or +OR +oracle +Orang +orc +order +Order +ORDER +ordered +orderedlist +ordering +ordfeminine +ordinal +ORDINALS +ordmasculine +org +Organization +oriented +orig +origin +Origin +original +originally +Originally +originated +origins +origRun +Oriya +ORIYA +Orma +orn +ORNAMENT +ORNAMENTS +Oromo +ors +orthogonal +orthographic +Orthographic +orthographically +ory +ORYA +os +OS +OSAGE +OSAtomic +OSAtomicAdd +OSAtomicCompareAndSwap +OSAtomicCompareAndSwapPtrBarrier +oslash +Oslash +Oslashsmall +Osmall +Osmanya +OSMANYA +OSMemoryBarrier +Ossetian +OSStatus +osuperior +ot +OT +otf +otFeatureTag +OTFontFileVal +OTHeader +other +Other +OTHER +OtherBlues +others +otherwise +Otherwise +OTHERWISE +otilde +Otilde +Otildesmall +otspec +Ottawa +OTTO +otw +Ouch +OUCH +ought +our +Our +ourself +ourselves +out +Out +OUT +outbuffer +outcome +outer +Outer +outerIndex +outline +Outline +OUTLINE +OUTLINED +outlines +OUTOFMEMORY +outOfRange +output +Output +OutputArray +outside +outward +over +OVERBAR +overflow +OVERFLOW +Overflowed +overflows +Overflows +overhead +overlap +OVERLAP +OVERLAPPED +overlapping +OVERLAPPING +OVERLAY +overloaded +overloading +overridden +override +Override +OVERRIDE +overriden +overrides +Overrides +overriding +overstrike +Overstruck +OVERSTRUCK +overview +overwrite +Owen +own +owned +ownership +Ozumacín +p +P +pa +Pa +PA +pABC +Pacaraos +pack +package +packages +packed +pad +padauk +Padauk +padded +padding +PADMA +pag +page +PAGE +pages +pagesize +PAGESIZE +Pahari +PAHAWH +Pahlavi +PAHLAVI +PaintType +pair +Pair +Paired +pairing +PairPos +PairPosFormat +pairs +Pairs +pairSet +PairSet +PairValueRecord +PairValueRecords +pairwise +Paite +Pakistan +Palantla +Palauan +Palaung +Palestinian +palette +Palette +PALETTE +paletteFlagsZ +paletteLabelsZ +palettes +Pali +PALMYRENE +Palpa +pam +Pampanga +Pampangan +Panama +Panao +Pangasinan +pango +Pango +Panjabi +panose +Pao +pap +Papiamento +Papiamentu +para +paragraph +PARAGRAPH +paragraphs +Paraguay +Paraguayan +parallel +param +PARAM +parameter +parameters +Parameters +PARAMETERS +params +PARAMS +paren +parenleft +parenleftinferior +parenleftsuperior +parenright +parenrightinferior +parenrightsuperior +parent +Parent +parentheses +PARENTHESIS +parity +parse +parsed +parser +PARSER +parses +Parses +parsing +part +Part +PART +partFlags +PartFlags +PARTHIAN +partial +Partial +partialdiff +PARTIALIZE +partially +PARTIALLY +partically +participate +participates +particular +PARTICULAR +particularly +partRecords +parts +Parts +PARTY +Pascal +Pasco +Pashto +pass +Pass +passed +PASSED +passes +passing +passthru +past +Pastaza +paste +PASTE +patah +path +PATH +Pattani +pattern +patterns +Patterns +pau +PAU +pause +pauses +pb +pbt +pbu +Pc +PC +pcc +pcd +pce +pcGlyphs +pCharProps +pchars +pcItems +pck +pcTable +pd +Pd +pdc +pdefault +pdf +PDF +pdfs +pe +Pe +PE +peak +peakCoord +peakTuple +peculiarities +peculiarity +pedantic +Pedi +peek +PEH +PEHEH +Pekal +pel +pend +Pennsylvania +people +People +per +Per +PER +percent +Percent +PERCENT +percentage +percentScaleDown +perfect +perform +Perform +performAction +PerformAction +performance +PERFORMANCE +performed +performing +Performing +performs +Performs +perhaps +period +PERIOD +periodcentered +periodinferior +PERIODS +periodsuperior +peripheral +Peripheral +permanently +PERMIC +permissible +permission +Permission +permissions +permissive +permitted +permute +Permyak +perpendicular +Persian +PERSIAN +persistent +person +perspective +pertaining +perthousand +Peru +pes +PETITE +Pf +pg +pga +pglyph +pGlyphProps +pGoffset +pgwide +Phags +PHAGS +Phaistos +Phake +Phalaa +phantom +PHANTOM +phantoms +phase +Phase +phases +Phases +phi +Philippines +PHINTHU +phk +Phoenician +PHOENICIAN +PHONE +Phonetic +phrase +PHRASE +PHRU +pi +Pi +PI +piAdvance +Picard +PiCharacters +pick +picks +pictographic +Pictographic +Pictures +PICTURES +Pidgin +pidgins +piece +piecemeal +pieces +Piemontese +pih +pinfo +pipes +Pisin +Pitcairn +pItems +pivot +Pivot +pivots +pixel +Pixel +pixels +pixelSize +pj +pk +PK +pkey +pkg +pkgconfig +pko +pl +place +Place +placed +placeholder +Placeholder +PLACEHOLDER +placeholders +placement +PLACEMENT +placements +plain +Plains +plan +Plan +PLAN +plane +Plane +planes +planned +planner +planning +plans +Plans +Plateau +platform +Platform +platformID +platforms +ple +please +plen +plevel +pll +plookups +plp +plt +plus +PLUS +plusminus +pms +pnb +png +PNG +PNGHeader +po +Po +Pocomchi +poh +Pohnpeian +point +Point +POINT +pointed +pointer +Pointer +pointers +pointing +points +POINTS +POISON +Pökoot +Poland +Polish +polyton +polytonic +Polytonic +pon +pool +POOL +pools +poor +pop +popcount +popcountl +popcountll +popped +pops +populate +Populate +population +Poqomchi +port +Port +portability +portal +Portugal +Portuguese +pos +Pos +POS +positinoing +position +Position +POSITION +Positional +positioned +positioning +Positioning +positions +POSITIONS +positive +Positive +POSIX +PosLookup +PosLookupSubTable +possibility +POSSIBILITY +possible +Possible +possibly +post +Post +POST +PosTable +postcompensation +PostcompensationActionChain +posted +postfix +Postfixed +POSTFIXED +postponing +postprocess +postscript +Postscript +PostScript +POSTSCRIPT +postScriptNameIDX +postV +potential +potentially +potfRecords +Pournader +pOutGlyphProps +pow +power +powers +pp +ppa +ppc +ppem +PPEM +ppemX +ppemY +PPI +ppObject +pPos +pr +practical +practice +pragma +PRAGMA +pragmas +PRC +pre +Pre +PRE +precede +precedence +preceding +Preceding +PRECEDING +precious +precision +precomposed +Pred +predef +predefined +Predicate +predicates +predictable +pref +PREF +prefer +Prefer +preferable +preference +preferences +preferred +Preferred +prefers +prefix +PREFIX +prefixed +Prefixed +PREFIXED +preloadAll +prepare +Prepare +preparing +preprocess +preprocessor +PREREQ +pres +PRES +presence +present +PRESENT +presentation +Presentation +preserve +PRESERVE +preserved +preserving +presForm +presidential +pressure +PRETTY +prev +PREV +prevent +PREVENT +prevented +preventing +prevents +previous +previously +Previously +Pri +primarily +primary +prime +primitives +PRIMITIVES +Principality +print +Print +printed +printer +Printer +printf +PRINTF +printing +prints +prior +priorities +prioritize +priority +Priority +priv +private +Private +PRIVATE +PrivateDict +privateDictInfo +privateDictInfos +privateDicts +privateDictsOffset +privateInfos +PrivatePointNumbers +privDictStr +PRIVDICTVAL +privInfo +PRIVOPSET +privSzr +pro +probable +probably +Probably +Probing +problem +problems +Procedure +proceed +proceeding +process +Process +processed +processes +processing +procs +produce +produced +Produced +produces +product +Profile +PROFITS +program +programlisting +programming +programs +Programs +Proj +project +projection +Projection +promise +promotion +propagate +Propagate +proper +properly +properties +PROPERTIES +property +PROPORTIONAL +proportionally +props +PROPS +PROT +protected +Protection +prototypes +provenc +Provençal +proves +provide +PROVIDE +provided +PROVIDED +provider +provides +Provides +Province +proxy +Proxy +prs +prune +ps +Ps +PS +psa +Psalter +PSALTER +psc +psControl +pScriptTags +pse +pseudo +Psmall +psState +pst +pstf +PSTF +PString +PSTS +psva +pt +PT +ptem +pthread +PTHREAD +ptr +PTR +ptrdiff +Pu +pua +PUA +PUACHUE +public +Public +PUBLIC +publicly +published +Puerto +Pulaar +Pular +pull +Pull +punctuation +Punctuation +PUNCTUATION +Punjabi +Puno +pure +Pure +PURE +purely +purpose +PURPOSE +purposes +push +Push +put +Put +puts +pv +pval +pwcChars +pwcInChars +pwGlyphs +pwLogClust +pwo +Pwo +pwOutGlyphs +px +py +Python +q +Q +Qaai +QAF +qamats +QAMATS +Qatar +Qiandong +Qimant +Qiubei +QOF +Qsmall +qsort +QSORT +QType +qu +QUAD +QuadPart +qualifiers +quantity +QUARTER +qub +qubuts +quc +qud +Quechua +queried +queries +query +querying +QueryInterface +question +QUESTION +questiondown +questiondownsmall +questions +questionsmall +quf +qug +quh +Quichua +quick +quickly +quicksort +Quicksort +Quiotepec +quite +quk +qul +quot +quotation +quotations +quote +quotedbl +quotedblbase +quotedblleft +quotedblright +quoteleft +quoteright +QUOTES +quotesinglbase +quotesingle +Quotient +Quoting +qup +qur +qus +qut +quw +qux +quy +quz +qva +qvc +qve +qvh +qvi +qvj +qvl +qvm +qvn +qvo +qvp +qvs +qvw +qvz +qwa +qwc +qwh +qws +qxa +qxc +qxh +qxl +qxn +qxo +qxp +qxr +qxt +qxu +qxw +r +R +ra +Ra +RA +race +races +radical +RADICAL +radicalDegreeBottomRaisePercent +Radicals +rafe +RAFE +rag +ragel +Raise +RAISE +raises +raj +Rajasthani +Rakhine +ran +rand +random +Random +RANDOM +randomize +Randomly +range +Range +RANGE +rangeCount +rangeEnd +rangeGaspBehavior +rangeMaxPPEM +rangeMaxValue +rangeMinValue +rangeoffset +rangeOffset +rangeRecord +RangeRecord +ranges +Ranges +RANGES +rangeShift +RangeShift +rangeStart +Ranglong +rar +rare +RARE +Rarely +Rarotongan +rasterizer +rate +rather +ratio +raw +rb +rbb +rbl +RC +rclt +rcRangeChars +RCU +rcurveline +RD +RDONLY +re +Re +reach +reaches +read +Read +READ +readable +reader +ReadFileFragment +READING +readingDirection +readjusting +readonly +READONLY +ready +real +Real +realistic +reality +realloc +reallocate +Reallocate +reallocating +really +Really +rearranged +rearrangement +Rearrangement +REARRANGEMENT +RearrangementSubtable +reason +reasons +reassign +reassigned +reassignment +reassignSIDs +rebuild +REBUS +rec +recalculated +recategorize +receive +recent +recently +Recognition +recognizable +recognize +recognized +recognizes +recom +recommended +recompose +recomposed +recomposing +recomposition +reconfiguration +reconfigured +reconfiguring +record +Record +RECORD +RecordArrayOf +recording +RecordList +RecordListOf +records +Records +Recover +recovery +recreate +recurring +recurse +recursed +Recursed +recursing +recursion +recursive +recursively +Recursively +red +Red +redefine +redefined +redirected +redone +Redu +reduce +redundant +reenabling +ref +refcount +refer +reference +Reference +REFERENCE +referenced +referenceGlyph +references +referred +referring +refers +REFIID +refine +refinements +reflect +reflecting +reflects +Reformed +refs +REGARD +regardless +Regex +region +Region +regionCount +regionIndices +regions +register +Register +REGISTER +registered +RegisterFontFileLoader +registers +registry +Registry +regular +Regular +REGULAR +REH +reinterpret +rej +Rejang +REJANG +reject +Reject +rejection +rel +REL +related +relation +relationship +relative +relax +relaxed +RELAXED +release +Release +RELEASE +released +releasedc +ReleaseDC +ReleaseFileFragment +ReleaseFontTable +releases +relevant +relicensed +relies +relocating +rely +Rely +relying +remain +remainder +remained +remaining +remains +remap +remapping +remaps +Remarks +remember +Remember +remembered +Removable +removal +remove +Remove +REMOVE +removed +RemoveFontMemResourceEx +removing +rename +render +rendered +renderer +rendering +renders +renum +renumber +Renumber +renumbering +renumbers +reorder +Reorder +reordered +REORDERED +reordering +Reordering +reorders +Reorders +Repack +repeat +REPEAT +repeated +repeatedAddGlyphAction +RepeatedAddGlyphAction +repeating +REPEATING +reph +Reph +REPH +repha +Repha +REPHA +replace +Replace +replaced +REPLACEME +replacement +REPLACEMENT +replaces +Replaces +replacing +replicate +report +Report +reports +Reports +repositioned +repositioning +repository +represent +representation +representations +represented +representing +represents +Represents +reproduces +Republic +reqFeatureIndex +request +requested +requests +require +Require +required +REQUIRED +requirement +requirements +requires +Requires +requiring +res +resCountM +research +reserved +Reserved +RESERVED +reservedESC +ReservedESC +reservedPad +reset +Reset +Resets +RESH +reshaping +reside +resize +resizing +resolution +resolutions +resolve +Resolved +resolvedLevel +resolver +resolves +resort +Resort +resource +Resource +ResourceForkHeader +ResourceMap +ResourceRecord +resources +resourcesZ +ResourceTypeRecord +respect +respective +respectively +responsibilities +responsibility +responsible +resreved +rest +RESTORE +restrict +restructuring +result +resulted +resulting +RESULTING +results +resume +ret +Ret +RET +retain +Retain +retained +retains +RETAINS +retired +retrieve +retrieved +retrieves +Retrieves +retry +return +Return +RETURN +returned +Returned +returning +returns +Returns +reuse +reused +reusing +reverse +REVERSE +reverseChainContextSingle +ReverseChainSingle +ReverseChainSingleSubst +ReverseChainSingleSubstFormat +reversed +Reverses +reversing +revert +revised +rewind +Rewind +rewinding +Rewinding +Rewrite +rfHeader +RHA +RHEL +rhs +Rhs +ri +ria +Riang +Rica +Rico +ridentity +Rieger +rif +right +Right +RIGHT +rightC +rightClass +rightClassCount +rightClassTable +rightSide +RightToLeft +Rinconada +ring +Ringsmall +Ripuarian +rise +RISE +RISH +risking +rit +Ritarungo +rki +RKRF +rkw +rl +rlig +rligLookup +rligMarksLookup +rlinecurve +rlineto +rm +RM +rmc +rmf +rml +rmn +rmo +rmoveto +rmw +rmy +rmz +rn +rnl +RNOON +ro +Ro +RO +road +Robatic +Roberts +rock +Rod +Roderick +Rohingya +ROHINGYA +role +roll +rom +Roman +ROMAN +Romani +Romania +Romanian +ROMANIZATION +Romansh +Romany +room +root +rooted +Roozbeh +ros +ROS +rotate +rotated +Rotuman +round +ROUND +ROUNDED +roundf +ROUNDF +rounding +rounds +routine +routines +roux +Roux +row +rowCount +rowIndexTable +rows +rowWidth +royalty +RP +rpc +rphf +RPHF +rpRangeProperties +RRA +rrcurveto +RREH +RS +rsb +rsbMap +Rsmall +rsuperior +Rsv +rt +rtl +RTL +rtlm +rtm +ru +Ruanda +RUBY +Ruching +rue +rule +Rule +RULE +rules +Rules +ruleSet +RuleSet +rulesets +ruleSets +Rumai +run +Run +RUN +Rundi +runHead +Runic +RUNIC +running +runs +runtime +Runtime +rup +rupiah +Russia +Russian +Rusyn +rvalue +rvalues +Rvalues +rw +Rwanda +rwr +Ryan +s +S +sa +Saamia +Sabah +Sabaot +Sad +SAD +Sadri +safe +safely +safest +sah +Saharan +said +Saidi +Saint +Sakalava +sake +Sakha +sakot +SAKOT +sal +Salasaca +salt +Salvador +sam +Samaritan +SAMARITAN +Sambalpuri +same +Same +SAME +SAMEKH +Sami +Samoan +Samogitian +sample +SAMPLE +sampleTextNameId +sampleTextNameID +San +Sanaani +sandboxed +sane +Sango +sanitization +sanitizations +sanitize +Sanitize +SANITIZE +sanitized +sanitizer +sanitizing +Sanitizing +sanity +Sankaran +Sans +Sanskrit +Santa +Santali +Santiago +santization +SAR +sara +SARA +Saraiki +Sardinian +Sarsi +sas +Sasak +Sascha +Sassarese +sat +Saterfriesisch +Saterland +Saudi +Saurashtra +SAURASHTRA +save +Save +SAVE +saved +savedprops +Saves +saw +Saxon +say +Sayisi +says +sb +SBase +SBASE +SBitLineMetrics +sbix +SBIX +SBIXGlyph +SBIXStrike +sbl +SBL +SBLHebrewUserManual +sc +Sc +SC +Scalable +scalar +Scalar +scalars +scale +SCALE +scaled +SCALED +scalef +scaler +scaling +scan +Scan +sCapHeight +scaron +Scaron +Scaronsmall +scedilla +Scedilla +scenario +scenarios +SCHAR +sched +SCHED +scheme +Schouten +science +SCIENTIFIC +sck +scn +sco +sconsumed +scope +score +Scots +Scottish +scount +SCount +SCOUNT +scratch +SCRATCH +screen +script +Script +SCRIPT +scriptAnalysis +scriptCode +ScriptExtensions +ScriptFreeCache +ScriptItemize +ScriptItemizeOpenType +scriptList +ScriptList +ScriptPlace +ScriptPlaceOpenType +scriptProperties +scripts +Scripts +SCRIPTS +ScriptShape +ScriptShapeOpenType +ScriptTag +scripttags +scs +scursor +sd +SD +sDageshForms +sdc +sdh +SDL +sdn +se +seac +search +Search +SEARCH +searched +searching +searchRange +Sebat +sec +second +Second +secondGlyph +seconds +sect +section +SECTION +sections +security +sed +see +See +seeing +seek +seem +seems +Seems +seen +SEEN +seenCrossStream +seequence +sees +seg +segcount +segCount +segCountX +segment +Segment +SEGMENT +segmented +segmenting +SegmentMaps +segments +segol +seh +Seigo +sek +Sekani +Sekota +sel +select +Select +selected +selecting +selection +selections +selectively +SelectObject +selector +Selector +SELECTOR +selectors +Selectors +SELECTORs +selectorToDisable +selectorToEnable +selects +Seletar +self +Selkup +semantic +semantical +SEMANTICS +Sembilan +semi +SEMI +Semibold +semicolon +SEMICOLON +Sena +send +sending +Senegal +sense +sensible +sensitive +SENSITIVE +sensitivity +sent +sentence +Senthang +sentinel +SENTINEL +separate +separated +separately +separation +Separator +SEPARATOR +seq +sequence +Sequence +sequenceIndex +sequences +SEQUENCES +sequential +sequentially +Serbia +Serbian +Serer +serial +serialization +serialize +Serialize +SERIALIZE +serialized +serializer +SERIALIZER +Serializes +series +Serif +serve +servers +serves +Sesotho +set +Set +SET +SetBidiLevel +SetComponent +SetCurrentRun +SetLineBreakpoints +setlocale +SETLOCALE +SetMark +SetNumberSubstitution +sets +Sets +SETS +SetScriptAnalysis +Setswana +setter +setters +setting +Setting +SettingName +settings +settingTableZ +setup +Setup +seven +SEVEN +seveneighths +seveninferior +sevenoldstyle +sevensuperior +SEVENTEEN +several +severely +Severn +sez +sFamilyClass +sfinae +SFINAE +sfm +SFNSDisplay +SFNSText +sfnt +Sfnt +SFNT +sfntVersion +sg +sga +sgc +sgi +sgs +sgw +sh +sha +shadda +Shadda +SHADDA +shaddaLigature +shaddaLigatureSet +shaddaLigaturesSubLookup +Shadow +SHADOW +shall +SHALL +shallow +Shan +shape +Shape +SHAPE +shaped +shaper +Shaper +SHAPER +shaperprefs +shapers +Shapers +SHAPERS +shapes +Shapes +SHAPES +shaping +Shaping +SHAPING +Sharada +SHARADA +share +SHARE +shared +Shared +SHARED +SharedPointNumbers +sharedTupleCount +sharedTuples +Shavian +SHAVIAN +SHEEN +Sheeter +Shekhawati +shell +SHELL +sheva +shi +shift +Shift +SHIFT +shifted +Shifter +SHIFTER +shifting +Shifting +Shihhi +shin +SHIN +ship +shipped +shn +Shona +short +Short +SHORT +shortCount +shortcut +shortcuts +shortest +shortfall +Shorthand +SHORTHAND +shortint +shoudln +should +Should +shouldn +Shouldn +show +SHOW +showing +shows +shrink +Shrinkage +shrinkageDisableGPOS +shrinkageDisableGSUB +shrinkageEnableGPOS +shrinkageEnableGSUB +shrinkageJstfMax +shrinkFlags +SHRT +shu +Shua +shuffle +Shurishkar +shut +Shut +Shwe +si +Sibe +SIBLING +Sichuan +Sicilian +sid +SID +Sidamo +Siddham +SIDDHAM +side +sidebearing +sides +sidmap +sids +SIDs +Sierra +sign +Sign +signature +signed +Signed +SIGNED +signedness +significance +significant +significantly +signifying +signs +SIGNWRITING +Sihuas +Siksika +sil +SIL +silently +Silesian +silf +Silf +SILF +Silt +Silte +Simalungun +similar +Similar +similarly +Similarly +SIMP +simple +Simple +SIMPLE +SimpleGlyph +simpler +simplest +simplicity +simplification +simplified +Simplified +SIMPLIFIED +simplifies +Simplifies +simplify +simply +SIMULATIONS +sin +SIN +since +Since +Sindhi +Singapore +single +Single +SINGLE +SinglePos +SinglePosFormat +SingleSubst +SingleSubstFormat +singleton +singletons +singular +SINH +sinhala +Sinhala +SINHALA +Sinhalese +sink +Sink +sinks +Sinte +SIOT +Sit +site +sites +situations +six +SIX +sixinferior +sixoldstyle +sixsuperior +sixteen +SIXTEEN +Siyin +size +Size +SIZE +sized +SIZED +sizeDeviceRecord +sizeof +sizes +sizeTable +sizeTables +sizing +sjd +sjo +sk +Sk +SKEWED +skg +skip +Skip +SKIP +skippable +skipped +skipping +Skipping +skippy +Skolt +skr +sl +SL +slant +Slant +SLANT +slanted +slash +SLASH +SLASHED +Slave +Slavey +Slavonic +slen +slightly +slim +slnt +slope +slot +slots +Slovak +Slovakia +Slovenia +Slovenian +slower +sm +Sm +SM +sma +SMAbv +small +Small +SMALL +smaller +smallest +Smallest +SmallGlyphMetrics +smart +SMART +SMBlw +smj +smn +smoking +sms +SMVD +sn +snap +snapshot +sniff +snk +snprintf +so +So +Sochiapam +Sodo +SOFT +software +Software +SOFTWARE +Soga +Sogdian +SOGDIAN +Solaris +SOLARIS +solely +solution +Somali +some +Some +Somebody +somefunc +somehow +Somehow +someone +something +sometime +sometimes +somewhat +SOMPENG +Songe +Soninke +soon +sop +SORA +Sorbian +sort +Sort +SORT +sorted +Sorted +SORTED +SortedArrayOf +sortedness +SortedUnsizedArrayOf +sorting +SORTING +Sotho +SOUND +sounds +source +Source +SOURCE +sourceware +South +SOUTH +Southeast +Southeastern +Southern +Southwestern +Soyombo +SOYOMBO +spac +space +Space +SPACE +spaced +spaces +Spaces +spacing +Spacing +SPACING +Spain +Spanish +spans +speaking +spec +Spec +special +Special +SPECIAL +Specialization +specializations +Specializations +Specialize +specially +Specials +specific +specifically +SPECIFICALLY +specification +specifications +specificed +specifics +specified +specifies +specify +specifying +specs +speculative +speed +Speed +speeds +split +Split +SplitCurrentRun +splitPoint +splitPosition +Spoon +SPOT +Spread +spreadsheets +Spring +spv +spy +sq +sqrt +Square +SQUARE +SQUARED +squeezing +sr +src +Sri +sro +srr +srs +ss +ssh +Ssmall +SSOT +ssuperior +ssxx +ssXX +st +stable +stack +Stack +STACK +Stacker +STACKER +stacking +stackoverflow +stage +stages +STAGES +stale +standalone +standard +Standard +StandardEncoding +standardheaderfiles +standards +stands +start +Start +START +startCharCode +startcode +startCode +startConnectorLength +startCoord +startCount +started +Started +starter +Starter +startGlyph +startGlyphID +startGlyphIndex +starting +Starting +StartOfText +starts +startSize +startUnicodeValue +stat +STAT +StatAxisRecord +state +State +STATE +stateArrayTable +stateHeader +statement +states +States +STATES +StateTable +StateTableDriver +stateTableOffset +static +Static +STATIC +status +stay +stays +stch +STCH +std +STD +stdarg +stddef +stderr +stdGlyph +StdHW +stdint +stdio +stdlib +STDMETHODCALLTYPE +STDMETHODIMP +StdVW +stem +StemSnapH +StemSnapV +step +STEP +stepping +steps +sterling +stHeader +still +Still +stmt +STMT +stop +Stop +STOP +stopped +stops +storage +Storage +store +Store +STORE +stored +Stored +storing +stq +str +STR +straight +Straight +straightforward +strategic +strategy +Strategy +strbuf +strchr +strcmp +strcpy +strdup +stream +streams +strerror +stretch +Stretch +STRETCH +stretchable +stretched +stretchGlyphAction +StretchGlyphAction +stretching +Stribley +Strict +STRICT +strictly +stride +strike +strikeout +STRIKEOUT +strikes +string +String +STRING +stringIndex +StringIndex +stringIndexInfo +stringOffset +strings +strlen +strncmp +strncpy +stroke +Strokes +StrokeWidth +strong +strongly +strstr +strtod +STRTOD +strtol +strtoul +struct +StructAfter +StructAtOffset +StructAtOffsetOrNull +StructAtOffsetUnaligned +structs +STRUCTS +structure +Structure +structures +Studio +stuff +stupid +stv +style +Style +STYLE +stylistic +Stylistic +STYLISTIC +stylisticSet +sTypoAscender +sTypoDescender +sTypoLineGap +su +sub +SUB +subarray +subclass +Subclass +subclasses +subdivided +subdivision +subfamily +SUBFAMILY +subfamilyID +subfamilyNameID +subFeatureFlags +subfonts +SubFormat +subject +subjoined +Subjoined +SUBJOINED +subjoining +sublookup +SUBLOOKUP +SubLookupOffsets +SubLookupOffsetsArray +submerged +subpixel +subr +subrecord +subroffset +subroutine +Subroutine +subroutines +subrs +Subrs +SUBRS +subrsOffset +subs +subscript +Subscript +SUBSCRIPT +subscripts +Subscripts +subsequence +subsequences +subsequent +subset +Subset +SUBSET +SubsetGlyph +Subsets +subsetted +subsetter +SUBSETTER +subsetting +subst +SUBST +substantially +substGlyph +substitued +substitute +Substitute +SUBSTITUTE +substituted +SUBSTITUTED +substitutes +substituteX +substitution +Substitution +SUBSTITUTION +substitutions +substitutionTables +SubstLookup +SubstLookupSubTable +substr +substring +substThreshold +subsystem +subtable +subTable +Subtable +SubTable +subtableCount +SubTableFlags +subtableGlyphCoverageArray +SubTableHeader +subtables +Subtables +SubTables +SUBTABLES +SubtableType +subtag +SUBTAG +subtags +subtending +subtract +subtracting +subtraction +succeed +succeeded +SUCCEEDED +Succeeding +SUCCEEDING +succeeds +success +successful +successfully +successive +such +SUCH +sucks +Sudanese +sudo +sufficient +SUFFICIENT +suggest +suggested +Suggested +suggests +suit +suitable +suite +Suite +suk +Sukuma +sukun +SUKUN +sum +Sum +summation +Summer +sun +Sundanese +SUNDANESE +SUNPRO +SUPER +superimposing +SUPERIORS +superscript +SUPERSCRIPT +superscripts +Superscripts +supp +suppData +suppEncData +SuppEncData +SuppEncoding +supplement +Supplement +Supplemental +supplied +supply +supplying +support +Support +SUPPORT +supported +Supported +SUPPORTED +supporting +supports +Supports +supposed +supposedly +suppress +supps +suq +sure +Suri +surprise +surprises +surrogate +SURROGATE +Surrogates +surrounding +survive +survived +survives +sutable +Sutu +sv +sva +Svan +svg +SVG +svgDoc +svgDocEntries +svgDocLength +SVGDocumentIndexEntry +SVR +sw +Swadaya +Swahili +Swampy +swap +Swap +SWAP +swaplp +swapped +swaps +SWASH +SWASHES +Swati +swb +swc +Sweden +Swedish +Sweep +swh +switch +Switch +switched +switches +Switzerland +swv +sxHeight +sxu +syc +syl +Sylheti +Syllabary +syllabic +Syllabic +SYLLABIC +Syllabics +SYLLABICS +syllable +Syllable +SYLLABLE +syllables +Syllables +Syloti +SYLOTI +SYM +symbol +Symbol +SYMBOL +symbolic +symbols +Symbols +SYMBOLS +symmetric +sync +synchronize +syntatically +syntax +Syntax +synthesize +synthesizes +synthesizing +SyntheticBase +syr +Syrc +syre +Syre +Syria +syriac +Syriac +SYRIAC +syrj +Syrj +syrn +Syrn +sys +SYS +sysconf +SYSCONF +system +System +SYSTEM +systems +sz +szl +t +T +ta +Ta +taa +tab +Tabasaran +Tabassaran +table +Table +TABLE +tableCount +tablelist +TableRecord +TableRecords +tables +Tables +TABLES +tableTag +TableType +Tachelhit +Tachoni +tag +Tag +TAG +Tagalog +TAGALOG +Tagbanwa +TAGBANWA +tagFeature +tagged +Tagin +Tagish +tagLangSys +tagRanges +tags +Tags +TAGS +tagScript +TAH +Tahaggart +Tahitian +Tahltan +tahoma +Tahoma +tahomabd +Tai +TAI +tail +Tail +tailored +Taiwan +Taiwanese +Tajik +Tajiki +Tajikistan +take +Take +taken +takes +taking +Takri +TAKRI +TALL +Tamahaq +Tamajaq +Tamajeq +Tamashek +Tamasheq +Tamazight +Tamil +TAMIL +TAML +Tanacross +Tanaina +Tanana +TANDEM +Tandroy +Tangshewi +TANGUT +Tanosy +Tanzania +taq +tarball +Tarball +target +TARGET +TargetConditionals +targs +Tarifit +tasks +Tatar +tator +tau +TAV +Tawallammat +Tawr +Tày +Tayart +Taylor +TB +TBase +TBASE +tbl +tbody +TBR +tcb +tce +TCHEH +TCHEHEH +TCodepoint +TCount +TCOUNT +tcp +tcy +tcz +tdd +tdx +te +tec +technetwork +Technical +technicalities +Technically +Tedim +TEH +TEHEH +telecom +tell +tells +TELU +Telugu +TELUGU +tem +Temne +temp +templ +template +Template +templates +temporarily +temporary +tempting +Temuan +TEN +Tena +Tenggarong +Tepetotutla +Tepinapa +Terik +term +terminal +Terminal +terminate +terminated +terminates +terminating +termination +Termination +TerminationWordCount +terminator +terminology +Terminology +terms +Tesaka +test +Test +TEST +tested +testing +Testing +tests +Tests +tet +TET +Tetum +text +Text +TEXT +TextAnalysis +textAnalyzer +textLength +textPosition +textProperties +TEXTRANGE +textString +textual +tfn +tg +tgj +tgroup +tgx +th +TH +tha +Thaana +THAANA +Thado +thai +Thai +THAI +Thailand +THAL +Tham +THAM +than +THAN +THANTHAKHAT +that +That +the +The +THE +thead +THEH +their +them +themed +themselves +then +Then +theory +thep +there +There +therefore +Therefore +these +These +they +They +thickness +THICKNESS +THIN +thing +things +Things +thingy +think +third +Third +THIRD +THIRTEEN +this +This +THIS +thiz +THO +thorn +Thorn +Thornsmall +those +though +thread +Thread +threading +threads +threadsafe +threadsafely +three +Three +THREE +threeeighths +threeinferior +threeoldstyle +threequarters +threequartersemdash +threesuperior +through +throughout +throw +tht +thus +thv +thz +ti +Tibetan +TIBETAN +Tidy +tiff +Tifinagh +TIFINAGH +tig +tight +Tigon +Tigre +Tigrinya +tilde +Tildesmall +tile +tiles +Tiles +time +timeouts +times +Times +timesbi +timesi +Timne +tindex +Tiny +TINY +Tirhuta +TIRHUTA +Tiriki +title +TITLECASE +TITLING +tiv +Tiv +tjmo +TJMO +tk +tkg +tl +TL +Tlacoatzintepec +tlen +tlist +TLookup +TLookupList +TLR +TM +tmh +tmp +tmw +tn +tnf +to +To +TO +Toba +Tobago +TObject +toCoord +tod +today +Todo +TODO +toe +together +ToGlyphs +toi +tok +Tok +tol +Tolowa +TOLOWER +Toma +tombstone +tombstones +tone +Tone +TONE +tones +Tonga +Tongan +too +took +toolkit +tools +tooltip +top +Top +TOP +topAccentAttachment +topAccentCoverage +topdict +topDict +TopDict +topDictIndex +TopDictIndex +topDictInfo +topDictModSIDs +topDictSize +topDictStr +toplevel +topographical +Topographical +topologically +topSide +topSzr +Torki +TORTIOUS +TORTOISE +Tosk +total +Total +totalDataSize +totally +touch +TOUCH +touches +TOUPPER +towards +tpi +tr +TR +trace +Trace +TRACE +tracing +track +Track +trackData +TrackData +tracking +Tracking +trackNameID +tracks +trackTable +trackTableEntry +TrackTableEntry +TRAD +trade +trademark +TRADEMARK +trademarks +Traditional +TRADITIONAL +trailed +trailing +trak +TRAK +trampoline +trans +TRANSCODING +transcription +transfer +Transfer +transform +Transform +transformation +transformed +transforming +transient +Transient +transition +transitioning +transitive +translatation +translate +translating +translation +TRANSLITERATION +transparent +traversal +treat +treated +treatment +treats +tree +trees +TRI +trick +trickiest +tricky +Tricky +tried +tries +Tries +trigger +triggered +triggers +trim +trimmable +trimmed +Trimming +Trinidad +trivial +Trivial +trivially +trouble +troubleshooting +tru +true +TRUE +TrueTag +TrueType +TRUETYPE +TrueTypeTag +truly +truncate +truncated +try +Try +TryGetFontTable +trying +Trying +ts +Ts +TSA +TSADI +tsb +tsere +Tsetsaut +Tshangla +Tsimihety +tsj +Tsmall +Tsonga +Tsotso +TSubTable +tsuperior +Tswana +tt +TTA +ttb +TTB +ttc +TTC +ttcf +ttcHeader +TTCHeader +TTCHeaderVersion +ttcTag +TTCTag +TTEH +TTEHEH +ttf +ttm +ttq +Tugen +Tulu +tum +Tumari +Tumbuka +Tundra +Tunisia +Tunisian +tuple +Tuple +tupleCount +tupleIndex +TupleIndexMask +tuples +tupleVarCount +TupleVarCount +TupleVarHeader +tupleVarHeaders +Tupple +TuppleIndex +Turkey +Turkic +TURKIC +Turkish +Turkmen +Turkmenistan +turn +Turn +Turner +turning +turns +Turns +Turoyo +Tusi +Tutchone +Tututni +tuu +Tuvalu +Tuvin +Tuvinian +tuy +tvalue +tvl +tw +tweaks +TWELVE +TWENTY +Twi +twice +twilight +two +Two +TWO +TwoByteNegInt +TwoBytePosInt +twodotenleader +twoinferior +twooldstyle +twosuperior +twothirds +tx +txc +txt +txy +ty +tying +Typ +type +Type +TYPE +typed +typedef +typedefs +typeface +typefaces +typeList +typename +TypeName +types +Types +TYPES +typesetter +typesetting +typical +typically +typo +TYPO +typographic +Typographic +TYPOGRAPHIC +typographical +typography +Typography +typos +tyv +tyz +tze +tzm +tzo +Tzotzil +u +U +uacute +Uacute +Uacutesmall +UARRAY +ub +uBidiLevel +ubl +Ubuntu +ubyte +ubytes +ucd +UCD +ucdn +UCDN +uchar +UChar +UCHAR +ucircumflex +Ucircumflex +Ucircumflexsmall +ucs +udieresis +Udieresis +Udieresissmall +udm +Udmurt +ue +UE +UEE +UErrorCode +ufuncs +ufunctions +UFWORD +ug +Ugaritic +UGARITIC +UGC +Ugh +ugly +Ugly +UGLY +ugrave +Ugrave +Ugravesmall +UHEADLESSARRAY +UI +UIDBase +Uighur +uiLabelNameId +uiNameID +uint +UINT +uintptr +UIPC +UISC +uk +Ukraine +Ukrainian +ULBAR +ulCodePageRange +ulink +ULL +ULLONG +ULong +ULONG +Ulrich +ultimately +ULTRA +ulUnicodeOS +ulUnicodeRange +umb +Umbundu +Umm +un +unassigned +UNASSIGNED +unbounded +UNBOUNDED +unchanged +UNCLASSIFIED +UnclassifiedGlyph +unclear +unconditionalAddGlyphAction +UnconditionalAddGlyphAction +unconditionally +UNCONNECTED +unconst +und +undef +UNDEF +undefined +UNDEFINED +under +Under +UNDERBAR +underflow +undergone +underline +Underline +UNDERLINE +underlinePosition +UnderlinePosition +underlineThickness +UnderlineThickness +underlining +underlying +underneath +underscore +UNDERSCORE +understand +understandable +understood +undertake +Undetermined +undo +undocumented +Undocumented +unequal +UNFITTED +unforced +unformed +unfortunate +unfortunately +Unfortunately +unhide +uni +unichar +UniChar +unicode +Unicode +UNICODE +UnicodeData +unicodes +UNICODES +unicodeValue +UnicodeValueRange +Unified +uniform +uninitialized +union +UNION +Unión +unions +unique +UNIQUE +UniqueID +uniscribe +Uniscribe +UNISCRIBE +unistd +UNISTD +unit +United +units +Units +unitSize +unitsPerEm +uniUUUU +Universal +unix +unknown +Unknown +UNKNOWN +unless +Unless +unlike +unlikely +unlimited +UnlimiteGap +unloaded +unlock +Unmake +unmap +UnmapViewOfFile +unmarked +unnecessary +unnormalize +unoffset +unorm +UNormalizer +unpack +unpadded +unpop +unprocessed +unr +unrecognized +unref +unreferenced +UnregisterFontFileLoader +unsafe +Unsafe +UNSAFE +UNSCALED +unset +unsigned +Unsized +UnsizedArrayOf +UnsizedByteStr +UnsizedOffsetArrayOf +UnsizedOffsetListOf +unsorted +UNSUCCESSFUL +unsupported +UNSUPPORTED +unsure +UNTAG +until +untouched +unused +Unused +UNUSED +unusual +unwanted +unwise +unzip +uoffset +up +UP +update +updated +UPDATES +upem +UPEM +upon +upper +Upper +UPPER +uppercase +UPPERCASE +upperLimit +upright +UPROPS +upward +ur +Urak +Urdu +urk +url +URL +Uruguay +us +USABLE +usage +usBreakChar +uscript +USCRIPT +UScriptCode +usDefaultChar +use +Use +USE +used +Used +useful +Useful +useless +UseMarkFilteringSet +user +User +USER +userfeatures +users +uses +Uses +usFirstCharIndex +USHRT +Usila +using +Using +USING +usLastCharIndex +usLowerOpticalPointSize +Usmall +usMaxContext +usp +USP +ustr +ustring +usually +Usually +usUpperOpticalPointSize +usWeightClass +usWidthClass +usWinAscent +usWinDescent +utf +UTF +Uthmanic +util +utilities +Utilities +utility +utilize +UTS +uu +UU +UUID +UuidCreate +uuidof +UVCRDOypOtijlMDLNNyyLk +uversion +uvs +UVS +UVSMapping +Uyghur +uz +Uzbek +Uzbeki +Uzbekistan +uzn +uzs +v +V +va +VA +VAbv +VAH +Vai +VAI +Vakhi +val +Val +VAL +ValCount +valFormat +valid +Valid +VALID +validate +validated +Validator +validity +Valle +valuable +value +Value +VALUE +valueArrayZ +valueCount +valueFormat +ValueFormat +valueFormats +valueIndex +valueNameID +ValueRecord +valueRecordCount +ValueRecords +valueRecordSize +values +Values +VALUES +ValuesAreLong +valueSize +valuesZ +valueTag +ValueType +var +Var +VAR +vararg +varation +VarData +varDataSize +variable +Variable +variablelist +variables +variant +Variant +VARIANT +variantGlyph +variants +Variants +variation +Variation +VARIATION +variationAxis +VariationAxisRecord +VariationDevice +variations +Variations +VARIATIONS +VariationSelectorRecord +variationsTag +VariationStore +VariationValueRecord +varidx +varIdx +varies +variety +varika +variour +various +Various +varlistentry +varname +varRecords +VarRegionAxis +VarRegionList +vars +varSelector +VarSizedBinSearchArrayOf +VarSizedBinSearchHeader +varStore +varStoreOffset +vary +vattu +vatu +Vatu +VATU +VAV +vAxis +VBAR +VBase +VBASE +VBlw +VCount +VCOUNT +vcpkg +VD +ve +VE +vec +vector +Vector +VECTOR +vectorization +vectorized +vedic +Vedic +VEH +ven +Venda +VENDOR +Venetian +Venezuela +VER +Verb +verbatim +verify +versa +version +Version +VERSION +versions +versionZ +vert +VERT +vertAdvance +vertBearingX +vertBearingY +vertData +vertGlyphCount +vertGlyphCoverage +vertical +Vertical +VERTICAL +vertically +VertOriginMetric +vertOriginY +vertYOrigins +very +vfprintf +vhcurveto +vhea +vi +VI +via +vice +Viet +VIET +Vietnam +Vietnamese +view +vindex +vINVALID +violation +virama +Virama +VIRAMA +viramas +virtual +Virtualizing +vis +Visarga +VISARGA +VISATTR +visibility +VISIBILITY +visible +visibly +visit +visited +visitLangSys +visitScript +vista +Vista +visual +Visual +VISUAL +vjmo +VJMO +vkk +vkt +Vlaams +Vlax +vlineto +vls +VM +VMAbv +VMBlw +vmoveto +VMPre +VMPst +vmtx +vmw +vo +Vo +VOICED +VOICING +void +Void +VOID +VOL +Volapük +volatile +volt +vorg +VORG +vorgMap +Võro +vowel +Vowel +VOWEL +vowels +Vowels +vp +VPre +VPst +VRBAR +vrinda +vro +Vs +VS +vsindex +vsindexcs +vsindexdict +Vsmall +vsnprintf +vstem +vstemhm +vstore +vstoreOffset +vtable +vv +vvar +VVAR +VVARTag +vvcurveto +w +W +wa +Wa +Wagdi +Wailaki +walk +Walk +walks +Wall +Walloon +Wanca +WANCHO +Wang +Wanga +want +wanted +wants +war +WARANG +Waray +warn +warning +Warning +WARNING +warnings +Warnings +WARRANTIES +was +Was +WASLA +wasn +waste +Watch +watchout +Watchout +WAVY +WAW +way +ways +Wayuu +wbm +wbr +Wbuiltin +Wc +Wcast +WCE +wchar +WCHAR +Wclass +Wconversion +wdcTable +Wdelete +Wdeprecated +Wdisabled +Wdouble +wdRecord +wdth +we +We +WE +weak +weight +Weight +WEIGHT +weights +weird +welcome +well +Welsh +Wembedded +went +were +Werner +West +Western +Weverything +Wextra +Wformat +wght +what +What +WHAT +whatever +Whatever +whatsoever +WHATSOEVER +when +When +whenever +Whenever +where +whereas +Whereas +WheresData +WheresFace +wherever +whether +Whether +WHETHER +which +Which +while +While +white +WHITE +Whitelist +whitespace +Whitespace +who +whole +whose +why +Why +wide +widely +width +Width +WIDTH +WidthDeltaCluster +WidthDeltaPair +widthMax +widths +widthsZ +Wignored +wiki +wikipedia +wild +will +Will +willing +willis +wilson +Wilson +Wimplicit +win +Win +WIN +WINAPI +windows +Windows +Winit +Winjected +WINNT +Winter +Wipe +wish +wishes +with +With +WITH +within +Within +without +Without +WJ +wlc +wle +wlk +Wlogical +Wmaybe +Wmissing +Wnested +wni +wo +Wolane +Wold +Wolof +won +Woods +word +Word +WORD +wording +wordOffsetToIndex +words +WORDS +work +Work +worked +working +Working +works +Works +world +worry +worrying +worth +would +Would +WOULD +wouldbn +wouldn +Wow +Wpacked +Wpointer +Wpragmas +wrap +wrapper +wrappers +wrapping +Wredundant +Wreorder +writable +Writable +WRITABLE +write +Write +WRITE +writer +writing +written +Written +wrong +wrote +wry +Ws +wsg +Wshadow +Wsign +Wsmall +Wstrict +Wstring +Wswitch +Wtautological +wtm +Wtype +Wu +Wundef +Wunknown +Wunneeded +Wunsafe +Wunused +wuu +Wvla +ww +Wwrite +WWS +www +x +X +xa +xA +xaa +xAA +xAAF +xAB +xabc +xABC +xABFFu +xAC +xAdvance +xAdvDevice +xAFF +xal +XALLOCATE +Xamtanga +xan +xAu +xAvgCharWidth +xb +xB +xBA +xBB +xBFF +xBytes +xc +xC +xCD +xCFF +Xconstructor +xCoordinate +xCULL +xD +xDBFFu +xDC +xDE +xDeviceTable +xDFDFDFDF +xDFDFDFDFu +xDFF +xDFFF +xDFFFu +xDFu +xe +xE +xEFF +xEFu +xF +xFAFF +xFB +xFBA +xFBAAu +xFBABu +xFBACu +xFBADu +xFBAEu +xFBAFu +xFBB +xFBD +xFBDAu +xFBDBu +xFBDCu +xFBDDu +xFBDEu +xFBDFu +xFBE +xFBFCu +xFBFDu +xFBFEu +xFBFFu +xFDFF +xfe +xFE +xFEA +xFEAAu +xFEABu +xFEACu +xFEADu +xFEAEu +xFEAFu +xFEB +xFEBAu +xFEBBu +xFEBCu +xFEBDu +xFEBEu +xFEBFu +xFEC +xFECAu +xFECBu +xFECCu +xFECDu +xFECEu +xFECFu +xFED +xFEDAu +xFEDBu +xFEDCu +xFEDDu +xFEDEu +xFEDFu +xFEE +xFEEAu +xFEEBu +xFEECu +xFEEDu +xFEEEu +xFEEFu +xFEF +xFEFAu +xFEFBu +xFEFCu +xFEFF +xFEFFu +xff +xFF +xFFEF +xFFF +xFFFC +xFFFD +xFFFDu +xFFFE +xFFFF +xFFFFD +xFFFFF +xFFFFFF +xFFFFFFFF +xFFFFFFFFFFFFFull +xFFFFFFFFu +xFFFFu +xFFu +xFu +Xgroup +xh +Xhb +Xhosa +xi +Xian +Xiang +Xiangxi +Xibe +XInclude +xjb +xkf +xlocale +XLOCALE +xMax +xMin +xml +XML +xmlns +xmm +xmv +xmw +xnr +xOffset +xog +xor +XP +xpe +xPlacement +xPlaDevice +xscale +xsl +Xsmall +xst +Xuan +XUID +xwo +xx +XXX +XY +y +Y +Ya +yacute +Yacute +Yacutesmall +yAdvance +yAdvDevice +Yakut +Yanahuanca +Yang +Yangbye +yao +Yao +yap +Yapese +Yarowilca +Yauyos +Yay +yb +ybd +yBytes +yCoordinate +ydd +yDeviceTable +ydieresis +Ydieresis +Ydieresissmall +YEH +Yemen +yen +yes +Yes +YES +yet +Ygroup +yi +Yi +YI +Yiddish +YIDDISH +yield +YIELD +yields +yih +Yijing +YING +yMax +yMin +yml +yo +YO +YOD +yOffset +Yongbei +Yongnan +Yoruba +yos +Yos +Yosemite +you +You +Youjiang +your +yourinputtext +yourself +yPlacement +yPlaDevice +yrk +yscale +Ysmall +yStrikeoutPosition +yStrikeoutSize +ySubscriptXOffset +ySubscriptXSize +ySubscriptYOffset +ySubscriptYSize +ySuperscriptXOffset +ySuperscriptXSize +ySuperscriptYOffset +ySuperscriptYSize +YU +yue +Yue +Yugoslav +yum +Yupik +z +Z +za +ZAH +ZAIN +Zambia +Zamboanga +Zanabazar +ZANABAZAR +Zande +Zarma +zawgyi +Zawgyi +ZAWGYI +ZAYIN +Zazaki +zcaron +Zcaron +Zcaronsmall +zch +zdj +zea +Zealand +Zealandic +Zeeuws +zeh +zero +Zero +ZERO +zeroed +zeroinferior +zeroing +zeroint +zerooldstyle +zeros +zerosuperior +zgb +zgh +zgm +zgn +zh +zhd +zhe +zhn +Zhong +Zhuang +Zimbabwe +zip +zipped +zk +Zl +zlj +zlm +zln +zlq +zmi +zne +zo +zom +zone +Zotung +Zou +Zp +zqe +Zs +zsm +Zsmall +zu +Zulu +zum +Zuojiang +zwj +ZWJ +zwnj +ZWNJ +zyb +zyg +zyj +zyn +Zyrian +zza +zzj diff --git a/libs/harfbuzz/perf/texts/fa-thelittleprince.txt b/libs/harfbuzz/perf/texts/fa-thelittleprince.txt new file mode 100644 index 00000000..3c56869a --- /dev/null +++ b/libs/harfbuzz/perf/texts/fa-thelittleprince.txt @@ -0,0 +1,923 @@ +شازده کوچولو +اثر آنتوان دو سن‌تگزوپه‌ری +برگردان احمد شاملو + + +اهدانام‌چه +به لئون ورث Leon Werth +از بچه‌ها عذر می‌خواهم که این کتاب را به یکی از بزرگ‌ترها هدیه کرده‌ام. برای این کار یک دلیل حسابی دارم: این «بزرگ‌تر» به‌ترین دوست من تو همه دنیا است. یک دلیل دیگرم هم آن که این «بزرگ‌تر» همه چیز را می‌تواند بفهمد حتا کتاب‌هایی را که برای بچه‌ها نوشته باشند. عذر سومم این است که این «بزرگ‌تر» تو فرانسه زندگی می‌کند و آن‌جا گشنگی و تشنگی می‌کشد و سخت محتاج دلجویی است. اگر همه‌ی این عذرها کافی نباشد اجازه می‌خواهم این کتاب را تقدیم آن بچه‌ای کنم که این آدم‌بزرگ یک روزی بوده. آخر هر آدم بزرگی هم روزی روزگاری بچه‌ای بوده (گیرم کم‌تر کسی از آن‌ها این را به یاد می‌آورد). پس من هم اهدانام‌چه‌ام را به این شکل تصحیح می‌کنم: + +به لئون ورث +موقعی که پسربچه بود +آنتوان دو سن‌تگزوپه‌ری + +من هم برگردان فارسی این شعر بزرگ را به دو بچه‌ی دوست‌داشتنی دیگر تقدیم می‌کنم: دکتر جهانگیر کازرونی و دکتر محمدجواد گلبن + +احمد شاملو + +۱ +یک بار شش سالم که بود تو کتابی به اسم قصه‌های واقعی -که درباره‌ی جنگل بِکر نوشته شده بود- تصویر محشری دیدم از یک مار بوآ که داشت حیوانی را می‌بلعید. آن تصویر یک چنین چیزی بود: + +یک مار بوآ که دارد حیوانی را می‌بلعد +تو کتاب آمده بود که: «مارهای بوآ شکارشان را همین جور درسته قورت می‌دهند. بی این که بجوندش. بعد دیگر نمی‌توانند از جا بجنبند و تمام شش ماهی را که هضمش طول می‌کشد می‌گیرند می‌خوابند». + +این را که خواندم، راجع به چیزهایی که تو جنگل اتفاق می‌افتد کلی فکر کردم و دست آخر توانستم با یک مداد رنگی اولین نقاشیم را از کار درآرم. یعنی نقاشی شماره‌ی یکم را که این جوری بود: + +نقاشی شماره‌ی یکم — مار شبیه به کلاه +شاهکارم را نشان بزرگ‌تر ها دادم و پرسیدم از دیدنش ترس‌تان بر می‌دارد؟ +جوابم دادند: -چرا کلاه باید آدم را بترساند؟ + +نقاشی من کلاه نبود، یک مار بوآ بود که داشت یک فیل را هضم می‌کرد. آن وقت برای فهم بزرگ‌ترها برداشتم توی شکم بوآ را کشیدم. آخر همیشه باید به آن‌ها توضیحات داد. نقاشی دومم این جوری بود: + +نقاشی دوم — مار و فیل درونش +بزرگ‌ترها بم گفتند کشیدن مار بوآی باز یا بسته را بگذارم کنار و عوضش حواسم را بیش‌تر جمع جغرافی و تاریخ و حساب و دستور زبان کنم. و این جوری شد که تو شش سالگی دور کار ظریف نقاشی را قلم گرفتم. از این که نقاشی شماره‌ی یک و نقاشی شماره‌ی دو ام یخ‌شان نگرفت دلسرد شده بودم. بزرگ‌ترها اگر به خودشان باشد هیچ وقت نمی‌توانند از چیزی سر درآرند. برای بچه‌ها هم خسته کننده است که همین جور مدام هر چیزی را به آن‌ها توضیح بدهند. + +ناچار شدم برای خودم کار دیگری پیدا کنم و این بود که رفتم خلبانی یاد گرفتم. بگویی نگویی تا حالا به همه جای دنیا پرواز کرده ام و راستی راستی جغرافی خیلی بم خدمت کرده. می‌توانم به یک نظر چین و آریزونا را از هم تمیز بدهم. اگر آدم تو دل شب سرگردان شده باشد جغرافی خیلی به دادش می‌رسد. + +از این راه است که من تو زندگیم با گروه گروه آدم‌های حسابی برخورد داشته‌ام. پیش خیلی از بزرگ‌ترها زندگی کرده‌ام و آن‌ها را از خیلی نزدیک دیده‌ام گیرم این موضوع باعث نشده در باره‌ی آن‌ها عقیده‌ی بهتری پیدا کنم. + +هر وقت یکی‌شان را گیر آورده‌ام که یک خرده روشن بین به نظرم آمده با نقاشی شماره‌ی یکم که هنوز هم دارمش محکش زده‌ام ببینم راستی راستی چیزی بارش هست یا نه. اما او هم طبق معمول در جوابم در آمده که: «این یک کلاه است». آن وقت دیگر من هم نه از مارهای بوآ باش اختلاط کرده‌ام نه از جنگل‌های بکر دست نخورده نه از ستاره‌ها. خودم را تا حد او آورده‌ام پایین و باش از بریج و گلف و سیاست و انواع کرات حرف زده‌ام. او هم از این که با یک چنین شخص معقولی آشنایی به هم رسانده سخت خوش‌وقت شده. + +۲ +این جوری بود که روزگارم تو تنهایی می‌گذشت بی این که راستی راستی یکی را داشته باشم که باش دو کلمه حرف بزنم، تااین که زد و شش سال پیش در کویر صحرا حادثه‌یی برایم اتفاق افتاد؛ یک چیز موتور هواپیمایم شکسته بود و چون نه تعمیرکاری همراهم بود نه مسافری یکه و تنها دست به کار شدم تا از پس چنان تعمیر مشکلی برآیم. مساله‌ی مرگ و زندگی بود. آبی که داشتم زورکی هشت روز را کفاف می‌داد. + +شب اول را هزار میل دورتر از هر آبادی مسکونی رو ماسه‌ها به روز آوردم پرت افتاده‌تر از هر کشتی شکسته‌یی که وسط اقیانوس به تخته پاره‌یی چسبیده باشد. پس لابد می‌توانید حدس بزنید چه جور هاج و واج ماندم وقتی کله‌ی آفتاب به شنیدن صدای ظریف عجیبی که گفت: «بی زحمت یک برّه برام بکش!» از خواب پریدم. +-ها؟ +-یک برّه برام بکش... + +چنان از جا جستم که انگار صاعقه بم زده. خوب که چشم‌هام را مالیدم و نگاه کردم آدم کوچولوی بسیار عجیبی را دیدم که با وقار تمام تو نخ من بود. این به‌ترین شکلی است که بعد ها توانستم از او در آرم، گیرم البته آن‌چه من کشیده‌ام کجا و خود او کجا! تقصیر من چیست؟ بزرگ‌تر ها تو شش سالگی از نقاشی دل‌سردم کردند و جز بوآی باز و بسته یاد نگرفتم چیزی بکشم. + +با چشم‌هایی که از تعجب گرد شده بود به این حضور ناگهانی خیره شدم. یادتان نرود که من از نزدیک‌ترین آبادی مسکونی هزار میل فاصله داشتم و این آدمی‌زاد کوچولوی من هم اصلا به نظر نمی‌آمد که راه گم کرده باشد یا از خستگی دم مرگ باشد یا از گشنگی دم مرگ باشد یا از تشنگی دم مرگ باشد یا از وحشت دم مرگ باشد. هیچ چیزش به بچه‌یی نمی‌بُرد که هزار میل دور از هر آبادی مسکونی تو دل صحرا گم شده باشد. + +این بهترین شکلی است که بعدها از او در آوردم. +وقتی بالاخره صدام در آمد، گفتم: +-آخه... تو این جا چه می‌کنی؟ +و آن وقت او خیلی آرام، مثل یک چیز خیلی جدی، دوباره در آمد که: +-بی زحمت واسه‌ی من یک برّه بکش. +آدم وقتی تحت تاثیر شدید رازی قرار گرفت جرات نافرمانی نمی‌کند. گرچه تو آن نقطه‌ی هزار میل دورتر از هر آبادی مسکونی و با قرار داشتن در معرض خطر مرگ این نکته در نظرم بی معنی جلوه کرد باز کاغذ و خودنویسی از جیبم در آوردم اما تازه یادم آمد که آن‌چه من یاد گرفته‌ام بیش‌تر جغرافیا و تاریخ و حساب و دستور زبان است، و با کج خلقی مختصری به آن موجود کوچولو گفتم نقاشی بلد نیستم. +بم جواب داد: -عیب ندارد، یک بَرّه برام بکش. + +از آن‌جایی که هیچ وقت تو عمرم بَرّه نکشیده بودم یکی از آن دو تا نقاشی‌ای را که بلد بودم برایش کشیدم. آن بوآی بسته را. ولی چه یکه‌ای خوردم وقتی آن موجود کوچولو در آمد که: -نه! نه! فیلِ تو شکم یک بوآ نمی‌خواهم. بوآ خیلی خطرناک است فیل جا تنگ کن. خانه‌ی من خیلی کوچولوست، من یک بره لازم دارم. برام یک بره بکش.بره‌ی مریض +-خب، کشیدم. +با دقت نگاهش کرد و گفت: +-نه! این که همین حالاش هم حسابی مریض است. یکی دیگر بکش.قوچ +-کشیدم. +لبخند با نمکی زد و در نهایت گذشت گفت: +-خودت که می‌بینی... این بره نیست، قوچ است. شاخ دارد نه...بره‌ی پیر +باز نقاشی را عوض کردم. +آن را هم مثل قبلی ها رد کرد: +-این یکی خیلی پیر است... من یک بره می‌خواهم که مدت ها عمر کند... + +باری چون عجله داشتم که موتورم را پیاده کنم رو بی حوصلگی جعبه‌ای کشیدم که دیواره‌اش سه تا سوراخ داشت، و از دهنم پرید که:جعبه +-این یک جعبه است. بره‌ای که می‌خواهی این تو است. + +و چه قدر تعجب کردم از این که دیدم داور کوچولوی من قیافه‌اش از هم باز شد و گفت: +-آها... این درست همان چیزی است که می‌خواستم! فکر می‌کنی این بره خیلی علف بخواهد؟ +-چطور مگر؟ +-آخر جای من خیلی تنگ است... +-هر چه باشد حتماً بسش است. بره‌یی که بت داده‌ام خیلی کوچولوست. +-آن قدرهاهم کوچولو نیست... اِه! گرفته خوابیده... + +و این جوری بود که من با شهریار کوچولو آشنا شدم. + +۳ +خیلی طول کشید تا توانستم بفهمم از کجا آمده. شهریار کوچولو که مدام مرا سوال پیچ می‌کرد خودش انگار هیچ وقت سوال‌های مرا نمی‌شنید. فقط چیزهایی که جسته گریخته از دهنش می‌پرید کم کم همه چیز را به من آشکار کرد. مثلا اول بار که هواپیمای مرا دید (راستی من هواپیما نقاشی نمی‌کنم، سختم است.) ازم پرسید: +-این چیز چیه؟ +-این «چیز» نیست: این پرواز می‌کند. هواپیماست. هواپیمای من است. + +و از این که به‌اش می‌فهماندم من کسی‌ام که پرواز می‌کنم به خود می‌بالیدم. +حیرت زده گفت: -چی؟ تو از آسمان افتاده‌ای؟ +با فروتنی گفتم: -آره. +گفت: -اوه، این دیگر خیلی عجیب است! +و چنان قهقهه‌ی ملوسی سر داد که مرا حسابی از جا در برد. راستش من دلم می‌خواهد دیگران گرفتاری‌هایم را جدی بگیرند. +خنده‌هایش را که کرد گفت: -خب، پس تو هم از آسمان می‌آیی! اهل کدام سیاره‌ای؟... + +بفهمی نفهمی نور مبهمی به معمای حضورش تابید. یکهو پرسیدم: +-پس تو از یک سیاره‌ی دیگر آمده‌ای؟ +آرام سرش را تکان داد بی این که چشم از هواپیما بردارد. + +اما جوابم را نداد، تو نخ هواپیما رفته بود و آرام آرام سر تکان می‌داد. +گفت: -هر چه باشد با این نباید از جای خیلی دوری آمده باشی... + +مدت درازی تو خیال فرو رفت، بعد بره‌اش را از جیب در آورد و محو تماشای آن گنج گرانبها شد.تصویری از شهریار کوچولو بر روی زمین +فکر می‌کنید از این نیمچه اعتراف «سیاره‌ی دیگر»ِ او چه هیجانی به من دست داد؟ زیر پاش نشستم که حرف بیشتری از زبانش بکشم: +-تو از کجا می‌آیی آقا کوچولوی من؟ خانه‌ات کجاست؟ بره‌ی مرا می‌خواهی کجا ببری؟ +مدتی در سکوت به فکر فرورفت و بعد در جوابم گفت: +-حسن جعبه‌ای که بم داده‌ای این است که شب‌ها می‌تواند خانه‌اش بشود. +-معلوم است... اما اگر بچه‌ی خوبی باشی یک ریسمان هم بِت می‌دهم که روزها ببندیش. یک ریسمان با یک میخ طویله... +انگار از پیش‌نهادم جا خورد، چون که گفت: +-ببندمش؟ چه فکر ها! +-آخر اگر نبندیش راه می‌افتد می‌رود گم می‌شود. + +دوست کوچولوی من دوباره غش غش خنده را سر داد: +-مگر کجا می‌تواند برود؟ +-خدا می‌داند. راستِ شکمش را می‌گیرد و می‌رود... +-بگذار برود...اوه، خانه‌ی من آن‌قدر کوچک است! +و شاید با یک خرده اندوه در آمد که: +-یک‌راست هم که بگیرد برود جای دوری نمی‌رود... + +۴ +به این ترتیب از یک موضوع خیلی مهم دیگر هم سر در آوردم: این که سیاره‌ی او کمی از یک خانه‌ی معمولی بزرگ‌تر بود.این نکته آن‌قدرها به حیرتم نینداخت. می‌دانستم گذشته از سیاره‌های بزرگی مثل زمین و کیوان و تیر و ناهید که هرکدام برای خودشان اسمی دارند، صدها سیاره‌ی دیگر هم هست که بعضی‌شان از بس کوچکند با دوربین نجومی هم به هزار زحمت دیده می‌شوند و هرگاه اخترشناسی یکی‌شان را کشف کند به جای اسم شماره‌ای به‌اش می‌دهد. مثلا اسمش را می‌گذارد «اخترک ۳۲۵۱». + +دلایل قاطعی دارم که ثابت می‌کند شهریار کوچولو از اخترک ب۶۱۲ آمده‌بود. + +شهریار کوچولو بر اخترکِ ب۶۱۲ +این اخترک را فقط یک بار به سال ۱۹۰۹ یک اخترشناس ترک توانسته بود ببیند اخترشناسِ ترک در حالِ دیدنِ اخترکِ ب۶۱۲که تو یک کنگره‌ی بین‌المللی نجوم هم با کشفش هیاهوی زیادی به راه انداخت اما واسه خاطر لباسی که تنش بود هیچ کس حرفش را باور نکرد. اخترشناسِ ترک در کنگره‌ی بین‌المللیِ نجوم، با لباس قدیمیآدم بزرگ‌ها این جوری‌اند! + +بختِ اخترک ب۶۱۲ زد و، ترک مستبدی ملتش را به ضرب دگنک وادار به پوشیدن لباس اروپایی‌ها کرد. اخترشناس به سال ۱۹۲۰ دوباره، و این بار با سر و وضع آراسته برای کشفش ارائه‌ی دلیل کرد و این بار همه جانب او را گرفتند.اخترشناسِ ترک در کنگره‌ی بین‌المللیِ نجوم، با لباس جدید +به خاطر آدم بزرگ‌هاست که من این جزئیات را در باب اخترکِ ب۶۱۲ برای‌تان نقل می‌کنم یا شماره‌اش را می‌گویم چون که آن‌ها عاشق عدد و رقم‌اند. وقتی با آن‌ها از یک دوست تازه‌تان حرف بزنید هیچ وقت ازتان درباره‌ی چیزهای اساسی‌اش سوال نمی‌کنند که هیج وقت نمی‌پرسند «آهنگ صداش چه‌طور است؟ چه بازی‌هایی را بیشتر دوست دارد؟ پروانه جمع می‌کند یا نه؟» -می‌پرسند: «چند سالش است؟ چند تا برادر دارد؟ وزنش چه‌قدر است؟ پدرش چه‌قدر حقوق می‌گیرد؟» و تازه بعد از این سوال‌ها است که خیال می‌کنند طرف را شناخته‌اند. + +اگر به آدم بزرگ‌ها بگویید یک خانه‌ی قشنگ دیدم از آجر قرمز که جلو پنجره‌هاش غرقِ شمعدانی و بامش پر از کبوتر بود محال است بتوانند مجسمش کنند. باید حتماً به‌شان گفت یک خانه‌ی صد میلیون تومنی دیدم تا صداشان بلند بشود که: -وای چه قشنگ! + +یا مثلا اگر به‌شان بگویید «دلیل وجودِ شهریارِ کوچولو این که تودل‌برو بود و می‌خندید و دلش یک بره می‌خواست و بره خواستن، خودش بهترین دلیل وجود داشتن هر کسی است» شانه بالا می‌اندازند و باتان مثل بچ‌ه‌ها رفتار می‌کنند! اما اگر به‌شان بگویید «سیاره‌ای که ازش آمده‌بود اخترک ب۶۱۲ است» بی‌معطلی قبول می‌کنند و دیگر هزار جور چیز ازتان نمی‌پرسند. این جوری‌اند دیگر. نباید ازشان دل‌خور شد. بچه‌ها باید نسبت به آدم بزرگ‌ها گذشت داشته باشند. + +اما البته ماها که مفهوم حقیقی زندگی را درک می‌کنیم می‌خندیم به ریش هرچه عدد و رقم است! چیزی که من دلم می‌خواست این بود که این ماجرا را مثل قصه‌ی پریا نقل کنم. دلم می‌خواست بگویم: «یکی بود یکی نبود. روزی روزگاری یه شهریار کوچولو بود که تو اخترکی زندگی می‌کرد همه‌اش یه خورده از خودش بزرگ‌تر و واسه خودش پیِ دوستِ هم‌زبونی می‌گشت...»، آن هایی که مفهوم حقیقی زندگی را درک کرده‌اند واقعیت قضیه را با این لحن بیشتر حس می‌کنند. آخر من دوست ندارم کسی کتابم را سرسری بخواند. خدا می‌داند با نقل این خاطرات چه بار غمی روی دلم می‌نشیند. شش سالی می‌شود که دوستم با بَرّه‌اش رفته. این که این جا می‌کوشم او را وصف کنم برای آن است که از خاطرم نرود. فراموش کردن یک دوست خیلی غم‌انگیز است. همه کس که دوستی ندارد. من هم می‌توانم مثل آدم بزرگ‌ها بشوم که فقط اعداد و ارقام چشم‌شان را می‌گیرد. و باز به همین دلیل است که رفته‌ام یک جعبه رنگ و چند تا مداد خریده‌ام. تو سن و سال من واسه کسی که جز کشیدنِ یک بوآی باز یا یک بوآی بسته هیچ کار دیگری نکرده -و تازه آن هم در شش سالگی- دوباره به نقاشی رو کردن از آن حرف‌هاست! البته تا آن‌جا که بتوانم سعی می‌کنم چیزهایی که می‌کشم تا حد ممکن شبیه باشد. گیرم به موفقیت خودم اطمینان چندانی ندارم. یکیش شبیه از آب در می‌آید یکیش نه. سرِ قدّ و قواره‌اش هم حرف است. یک جا زیادی بلند درش آورده‌ام یک جا زیادی کوتاه. از رنگ لباسش هم مطمئن نیستم. خب، رو حدس و گمان پیش رفته‌ام؛ کاچی به زِ هیچی. و دست آخر گفته باشم که تو بعضِ جزئیات مهم‌ترش هم دچار اشتباه شده‌ام. اما در این مورد دیگر باید ببخشید: دوستم زیر بار هیچ جور شرح و توصیفی نمی‌رفت. شاید مرا هم مثل خودش می‌پنداشت. اما از بختِ بد، دیدن بره‌ها از پشتِ جعبه از من بر نمی‌آید. نکند من هم یک خرده به آدم بزرگ‌ها رفته‌ام؟ «باید پیر شده باشم». + +۵ +هر روزی که می‌گذشت از اخترک و از فکرِ عزیمت و از سفر و این حرف‌ها چیزهای تازه‌ای دست‌گیرم می‌شد که همه‌اش معلولِ بازتاب‌هایِ اتفاقی بود. و از همین راه بود که روز سوم از ماجرایِ تلخِ بائوباب ها سردرآوردم. + +این بار هم بَرّه باعثش شد، چون شهریار کوچولو که انگار سخت دودل مانده‌بود ناگهان ازم پرسید: +-بَرّه‌ها بته‌ها را هم می‌خورند دیگر، مگر نه؟ +-آره. همین جور است. +-آخ! چه خوشحال شدم! + +نتوانستم بفهمم این موضوع که بَرّه‌ها بوته‌ها را هم می‌خورند اهمیتش کجاست اما شهریار کوچولو درآمد که: +-پس لابد بائوباب ها را هم می‌خورند دیگر؟ + +یک گله فیل که در اخترکِ ب۶۱۲ روی هم چیده شده‌اندمن برایش توضیح دادم که بائوباب بُتّه نیست. درخت است و از ساختمان یک معبد هم گنده‌تر، و اگر یک گَلّه فیل هم با خودش ببرد حتا یک درخت بائوباب را هم نمی‌توانند بخورند. +از فکر یک گَلّه فیل به خنده افتاد و گفت: -باید چیدشان روی هم. +اما با فرزانگی تمام متذکر شد که: -بائوباب هم از بُتِّگی شروع می‌کند به بزرگ شدن. +-درست است. اما نگفتی چرا دلت می‌خواهد بره‌هایت نهال‌های بائوباب را بخورند؟ +گفت: -دِ! معلوم است! + +و این را چنان گفت که انگار موضوع از آفتاب هم روشن‌تر است؛ منتها من برای این که به تنهایی از این راز سر در آرم ناچار شدم حسابی کَلّه را به کار بیندازم. + +راستش این که تو اخترکِ شهریار کوچولو هم مثل سیارات دیگر هم گیاهِ خوب به هم می‌رسید هم گیاهِ بد. یعنی هم تخمِ خوب گیاه‌های خوب به هم می‌رسید، هم تخمِ بدِ گیاه‌هایِ بد. اما تخم گیاه‌ها نامریی‌اند. آن‌ها تو حرمِ تاریک خاک به خواب می‌روند تا یکی‌شان هوس بیدار شدن به سرش بزند. آن وقت کش و قوسی می‌آید و اول با کم رویی شاخکِ باریکِ خوشگل و بی‌آزاری به طرف خورشید می‌دواند. اگر این شاخک شاخکِ تربچه‌ای گلِ سرخی چیزی باشد می‌شود گذاشت برای خودش رشد کند اما اگر گیاهِ بدی باشد آدم باید به مجردی که دستش را خواند ریشه‌کنش کند. + +باری، تو سیاره‌ی شهریار کوچولو گیاه تخمه‌های وحشتناکی به هم می‌رسید. یعنی تخم درختِ بائوباب که خاکِ سیاره حسابی ازشان لطمه خورده بود. بائوباب هم اگر دیر به‌اش برسند دیگر هیچ جور نمی‌شود حریفش شد: تمام سیاره را می‌گیرد و با ریشه‌هایش سوراخ سوراخش می‌کند و اگر سیاره خیلی کوچولو باشد و بائوباب‌ها خیلی زیاد باشند پاک از هم متلاشیش می‌کنند. + +شهریار کوچولو در حال نظافت ِ اخترکششهریار کوچولو بعدها یک روز به من گفت: «این، یک امر انضباطی است. صبح به صبح بعد از نظافتِ خود باید با دفت تمام به نظافتِ اخترک پرداخت. آدم باید خودش را مجبور کند که به مجردِ تشخیص دادن بائوباب‌ها از بته‌های گلِ سرخ که تا کوچولواَند عین هم‌اَند با دقت ریشه‌کن‌شان بکند. کار کسل‌کننده‌ای هست اما هیچ مشکل نیست.» + +یک روز هم بم توصیه کرد سعی کنم هر جور شده یک نقاشی حسابی از کار درآرم که بتواند قضیه را به بچه‌های سیاره‌ی من هم حالی کند. گفت اگر یک روز بروند سفر ممکن است به دردشان بخورد. پاره‌ای وقت‌ها پشت گوش انداختن کار ایرادی ندارد اما اگر پای بائوباب در میان باشد گاوِ آدم می‌زاید. اخترکی را سراغ دارم که یک تنبل‌باشی ساکنش بود و برای کندن سه تا نهال بائوباب امروز و فردا کرد...». + +آن وقت من با استفاده از چیزهایی که گفت شکل آن اخترک را کشیدم. + +اخترکِ تنبل‌باشی با سه درختِ بائوباب +هیچ دوست ندارم اندرزگویی کنم. اما خطر بائوباب‌ها آن‌قدر کم شناخته شده و سر راهِ کسی که تو چنان اخترکی سرگیدان بشود آن قدر خطر به کمین نشسته که این مرتبه را از رویه‌ی همیشگی خودم دست بر می‌دارم و می‌گویم: «بچه‌ها! هوای بائوباب‌ها را داشته باشید!» + +اگر من سرِ این نقاشی این همه به خودم فشار آورده‌ام فقط برای آن بوده که دوستانم را متوجه خطری کنم که از مدت‌ها پیش بیخ گوش‌شان بوده و مثلِ خودِ من ازش غافل بوده‌اند. درسی که با این نقاشی داده‌ام به زحمتش می‌ارزد. حالا ممکن است شما از خودتان بپرسید: «پس چرا هیچ کدام از بقیه‌ی نقاشی‌های این کتاب هیبتِ تصویرِ بائوباب‌ها را ندارد؟» -خب، جوابش خیلی ساده است: من زور خودم را زده‌ام اما نتوانسته‌ام از کار درشان بیاورم. اما عکس بائوباب‌ها را که می‌کشیدم احساس می‌کردم قضیه خیلی فوریت دارد و به این دلیل شور بَرَم داشته بود. + +۶ +آخ، شهریار کوچولو! این جوری بود که من کَم کَمَک از زندگیِ محدود و دل‌گیر تو سر درآوردم. تا مدت‌ها تنها سرگرمیِ تو تماشای زیباییِ غروب آفتاب بوده. به این نکته‌ی تازه صبح روز چهارم بود که پی بردم؛ یعنی وقتی که به من گفتی: +-غروب آفتاب را خیلی دوست دارم. برویم فرورفتن آفتاب را تماشا کنیم...شهریار کوچولو در اخترکش مشغولِ تماشای غروبِ آفتاب +-هوم، حالاها باید صبر کنی... +-واسه چی صبر کنم؟ +-صبر کنی که آفتاب غروب کند. + +اول سخت حیرت کردی بعد از خودت خنده‌ات گرفت و برگشتی به من گفتی: +-همه‌اش خیال می‌کنم تو اخترکِ خودمم! +-راستش موقعی که تو آمریکا ظهر باشد همه می‌دانند تو فرانسه تازه آفتاب دارد غروب می‌کند. کافی است آدم بتواند در یک دقیقه خودش را برساند به فرانسه تا بتواند غروب آفتاب را تماشا کند. متاسفانه فرانسه کجا این‌جا کجا! اما رو اخترک تو که به آن کوچکی است همین‌قدر که چند قدمی صندلیت را جلو بکشی می‌توانی هرقدر دلت خواست غروب تماشا کنی. +-یک روز چهل و سه بار غروب آفتاب را تماشا کردم! +و کمی بعد گفت: +-خودت که می‌دانی... وقتی آدم خیلی دلش گرفته باشد از تماشای غروب لذت می‌برد. +-پس خدا می‌داند آن روز چهل و سه غروبه چه‌قدر دلت گرفته بوده. + +اما مسافر کوچولو جوابم را نداد. + +۷ +روز پنجم باز سرِ گوسفند از یک راز دیگر زندگی شهریار کوچولو سر در آوردم. مثل چیزی که مدت‌ه‌ا تو دلش به‌اش فکر کرده باشد یک‌هو بی مقدمه از من پرسید: +-گوسفندی که بُتّه ها را بخورد گل ها را هم می‌خورد؟ +-گوسفند هرچه گیرش بیاید می‌خورد. +-حتا گل‌هایی را هم که خار دارند؟ +-آره، حتا گل‌هایی را هم که خار دارند. +-پس خارها فایده‌شان چیست؟ + +من چه می‌دانستم؟ یکی از آن: سخت گرفتار باز کردن یک مهره‌ی سفتِ موتور بودم. از این که یواش یواش بو می‌بردم خرابیِ کار به آن سادگی‌ها هم که خیال می‌کردم نیست برج زهرمار شده‌بودم و ذخیره‌ی آبم هم که داشت ته می‌کشید بیش‌تر به وحشتم می‌انداخت. +-پس خارها فایده‌شان چسیت؟ + +شهریار کوچولو وقتی سوالی را می‌کشید وسط دیگر به این مفتی‌ها دست بر نمی‌داشت. مهره پاک کلافه‌ام کرده بود. همین جور سرسری پراندم که: +-خارها به درد هیچ کوفتی نمی‌خورند. آن‌ها فقط نشانه‌ی بدجنسی گل‌ها هستند. +-دِ! +و پس از لحظه‌یی سکوت با یک جور کینه درآمد که: +-حرفت را باور نمی‌کنم! گل‌ها ضعیفند. بی شیله‌پیله‌اند. سعی می‌کنند یک جوری تهِ دل خودشان را قرص کنند. این است که خیال می‌کنند با آن خارها چیزِ ترسناکِ وحشت‌آوری می‌شوند... + +لام تا کام به‌اش جواب ندادم. در آن لحظه داشتم تو دلم می‌گفتم: «اگر این مهره‌ی لعنتی همین جور بخواهد لج کند با یک ضربه‌ی چکش حسابش را می‌رسم.» اما شهریار کوچولو دوباره افکارم را به هم ریخت: +-تو فکر می‌کنی گل‌ها... +من باز همان جور بی‌توجه گفتم: +-ای داد بیداد! ای داد بیداد! نه، من هیچ کوفتی فکر نمی‌کنم! آخر من گرفتار هزار مساله‌ی مهم‌تر از آنم! +هاج و واج نگاهم کرد و گفت: +-مساله‌ی مهم! + +مرا می‌دید که چکش به دست با دست و بالِ سیاه روی چیزی که خیلی هم به نظرش زشت می‌آمد خم شده‌ام. +-مثل آدم بزرگ‌ها حرف می‌زنی! +از شنیدنِ این حرف خجل شدم اما او همین جور بی‌رحمانه می‌گفت: +-تو همه چیز را به هم می‌ریزی... همه چیز را قاتی می‌کنی! +حسابی از کوره در رفته‌بود. + +موهای طلایی طلائیش تو باد می‌جنبید. +-اخترکی را سراغ دارم که یک آقا سرخ روئه توش زندگی می‌کند. او هیچ وقت یک گل را بو نکرده، هیچ وقت یک ستاره‌را تماشا نکرده هیچ وقت کسی را دوست نداشته هیچ وقت جز جمع زدن عددها کاری نکرده. او هم مثل تو صبح تا شب کارش همین است که بگوید: «من یک آدم مهمم! یک آدم مهمم!» این را بگوید و از غرور به خودش باد کند. اما خیال کرده: او آدم نیست، یک قارچ است! +-یک چی؟ +-یک قارچ!گلِ سرخ +حالا دیگر رنگش از فرط خشم مثل گچ سفید شده‌بود: +-کرورها سال است که گل‌ها خار می‌سازند و با وجود این کرورها سال است که برّه‌ها گل‌ها را می‌خورند. آن وقت هیچ مهم نیست آدم بداند پس چرا گل‌ها واسه ساختنِ خارهایی که هیچ وقتِ خدا به هیچ دردی نمی‌خورند این قدر به خودشان زحمت می‌دهند؟ جنگ میان برّه‌ها و گل‌ها هیچ مهم نیست؟ این موضوع از آن جمع زدن‌های آقا سرخ‌روئه‌یِ شکم‌گنده مهم‌تر و جدی‌تر نیست؟ اگر من گلی را بشناسم که تو همه‌ی دنیا تک است و جز رو اخترک خودم هیچ جای دیگر پیدا نمیشه و ممکن است یک روز صبح یک برّه کوچولو، مفت و مسلم، بی این که بفهمد چه‌کار دارد می‌کند به یک ضرب پاک از میان ببردش چی؟ یعنی این هم هیچ اهمیتی ندارد؟ اگر کسی گلی را دوست داشته باشد که تو کرورها و کرورها ستاره فقط یک دانه ازش هست واسه احساس وشبختی همین قدر بس است که نگاهی به آن همه ستاره بیندازد و با خودش بگوید: «گل من یک جایی میان آن ستاره‌هاست»، اما اگر برّه گل را بخورد برایش مثل این است که یکهو تمام آن ستاره‌ها پِتّی کنند و خاموش بشوند. یعنی این هم هیچ اهمیتی ندارد؟ +دیگر نتوانست چیزی بگوید و ناگهان هِق هِق کنان زد زیر گریه. + +حالا دیگر شب شده‌بود. اسباب و ابزارم را کنار انداخته‌بودم. دیگر چکش و مهره و تشنگی و مرگ به نظرم مضحک می‌آمد. رو ستاره‌ای، رو سیاره‌ای، رو سیاره‌ی من، زمین، شهریارِ کوچولویی بود که احتیاج به دلداری داشت! به آغوشش گرفتم مثل گهواره تابش دادم به‌اش گفتم: «گلی که تو دوست داری تو خطر نیست. خودم واسه گوسفندت یک پوزه‌بند می‌کشم... خودم واسه گفت یک تجیر می‌کشم... خودم...» بیش از این نمی‌دانستم چه بگویم. خودم را سخت چُلمَن و بی دست و پا حس می‌کردم. نمی‌دانستم چه‌طور باید خودم را به‌اش برسانم یا به‌اش بپیوندم...p چه دیار اسرارآمیزی است دیار اشک! + +۸ +راه شناختن آن گل را خیلی زود پیدا کردم: +تو اخترکِ شهریار کوچولو همیشه یک مشت گل‌های خیلی ساده در می‌آمده. گل‌هایی با یک ردیف گلبرگ که جای چندانی نمی‌گرفته، دست و پاگیرِ کسی نمی‌شده. صبحی سر و کله‌شان میان علف‌ها پیدا می‌شده شب از میان می‌رفته‌اند. اما این یکی یک روز از دانه‌ای جوانه زده بود که خدا می‌دانست از کجا آمده رود و شهریار کوچولو با جان و دل از این شاخکِ نازکی که به هیچ کدام از شاخک‌های دیگر نمی‌رفت مواظبت کرده‌بود. بعید بنود که این هم نوعِ تازه‌ای از بائوباب باشد اما بته خیلی زود از رشد بازماند و دست‌به‌کارِ آوردن گل شد. شهریار کوچولو که موقعِ نیش زدن آن غنچه‌ی بزرگ حاضر و ناظر بود به دلش افتاد که باید چیز معجزه‌آسایی از آن بیرون بیاید. اما گل تو پناهِ خوابگاهِ سبزش سر فرصت دست اندکار خودآرایی بود تا هرچه زیباتر جلوه‌کند. رنگ‌هایش را با وسواس تمام انتخاب می‌کرد سر صبر لباس می‌پوشید و گلبرگ‌ها را یکی یکی به خودش می‌بست. دلش نمی‌خواست مثل شقایق‌ها با جامه‌ی مچاله و پر چروک بیرون بیاید. + +شهریار کوچولو و گلِ سرخنمی‌خواست جز در اوج درخشندگی زیبائیش رو نشان بدهد!... + +هوه، بله عشوه‌گری تمام عیار بود! آرایشِ پر راز و رمزش روزها و روزها طول کشید تا آن که سرانجام یک روز صبح درست با بر آمدن آفتاب نقاب از چهره برداشت و با این که با آن همه دقت و ظرافت روی آرایش و پیرایش خودش کار کرده بود خمیازه‌کشان گفت: +-اوه، تازه همین حالا از خواب پا شده‌ام... عذر می‌خواهم که موهام این جور آشفته‌است... + +شهریار کوچولو نتوانست جلو خودش را بگیرد و از ستایش او خودداری کند: +-وای چه‌قدر زیبائید! +گل به نرمی گفت: +-چرا که نه؟ من و آفتاب تو یک لحظه به دنیا آمدیم... +شهریار کوچولو شستش خبردار شد که طرف آن‌قدرها هم اهل شکسته‌نفسی نیست اما راستی که چه‌قدر هیجان انگیز بود! +-به نظرم وقت خوردن ناشتایی است. بی زحمت برایم فکری بکنید. +و شهریار کوچولوی مشوش و در هم یک آبپاش آب خنک آورده به گل داده‌بود.شهریار کوچولو در حالِ تماشای گلِ سرخ +با این حساب، هنوزهیچی نشده با آن خودپسندیش که بفهمی‌نفهمی از ضعفش آب می‌خورد دل او را شکسته بود. مثلا یک روز که داشت راجع به چهارتا خارش حرف می‌زد یک‌هو در آمده بود که:ببر و گلِ سرخ +-نکند ببرها با آن چنگال‌های تیزشان بیایند سراغم! +شهریار کوچولو ازش ایراد گرفته‌بود که: +-تو اخترک من ببر به هم نمی‌رسد. تازه ببرها که علف‌خوار نیستند. +گل به گلایه جواب داده بود: +-من که علف نیستم. +و شهریار کوچولو گفته بود: +-عذر می‌خواهم... +-من از ببرها هیچ ترسی ندارم اما از جریان هوا وحشت می‌کنم. تو دستگاه‌تان تجیر به هم نمی‌رسد؟شهریار کوچولو در حالِ پوشاندنِ گلِ سرخ +شهریار کوچولو تو دلش گفت: «وحشت از جریان هوا... این که واسه یک گیاه تعریفی ندارد... چه مرموز است این گل!» +-شب مرا بگذارید زیر یک سرپوش. این جا هواش خیلی سرد است. چه جای بدی افتادم! جایی که پیش از این بودم...شهریار کوچولو در حالِ گذاشتنِ سرپوش روی گلِ سرخ +اما حرفش را خورده بود. آخر، آمدنا هنوز به شکل دانه بود. امکان نداشت توانسته‌باشد دنیاهای دیگری را بشناسد. شرم‌سار از این که گذاشته بود سر به هم بافتن دروغی به این آشکاری مچش گیربیفتد دو سه بار سرفه کرده بود تا اهمالِ شهریار کوچولو را به‌اش یادآور شود: +-تجیر کو پس؟ +-داشتم می‌رفتم اما شما داشتید صحبت می‌کردید! +و با وجود این زورکی بنا کرده‌بود به سرفه کردن تا او احساس پشیمانی کند. + +به این ترتیب شهریار کوچولو با همه‌ی حسن نیّتی که از عشقش آب می‌خورد همان اول کار به او بد گمان شده‌بود. حرف‌های بی سر و تهش را جدی گرفته‌بود و سخت احساس شوربختی می‌کرد. + +یک روز دردِدل کنان به من گفت: -حقش بود به حرف‌هاش گوش نمی‌دادم. هیچ وقت نباید به حرف گل‌ها گوش داد. گل را فقط باید بوئید و تماشا کرد. گلِ من تمامِ اخترکم را معطر می‌کرد گیرم من بلد نبودم چه‌جوری از آن لذت ببرم. قضیه‌ی چنگال‌های ببر که آن جور دَمَغم کرده‌بود می‌بایست دلم را نرم کرده باشد...» + +یک روز دیگر هم به من گفت: «آن روزها نتوانستم چیزی بفهمم. من بایست روی کرد و کارِ او در باره‌اش قضاوت می‌کردم نه روی گفتارش... عطرآگینم می‌کرد. دلم را روشن می‌کرد. نمی‌بایست ازش بگریزم. می‌بایست به مهر و محبتی که پشتِ آن کلک‌های معصومانه‌اش پنهان بود پی می‌بردم. گل‌ها پُرَند از این جور تضادها. اما خب دیگر، من خام‌تر از آن بودم که راهِ دوست داشتنش را بدانم!». + +۹ +گمان کنم شهریار کوچولو برای فرارش از مهاجرت پرنده‌های وحشی استفاده کرد. + +گمان کنم شهریار کوچولو برای فرارش از مهاجرت پرنده‌های وحشی استفاده کرد. +صبح روز حرکت، اخترکش را آن جور که باید مرتب کرد، آتش‌فشان‌های فعالش را با دقت پاک و دوده‌گیری کرد: شهریار کوچولو در حالِ پاک کردنِ آتش‌فشان.دو تا آتش‌فشان فعال داشت که برای گرم کردن ناشتایی خیلی خوب بود. یک آتش‌فشان خاموش هم داشت. منتها به قول خودش «آدم کف دستش را که بو نکرده!» این بود که آتش‌فشان خاموش را هم پاک کرد. آتش‌فشان که پاک باشد مرتب و یک هوا می‌سوزد و یک‌هو گُر نمی‌زند. آتش‌فشان هم عین‌هو بخاری یک‌هو اَلُو می‌زند. البته ما رو سیاره‌مان زمین کوچک‌تر از آن هستیم که آتش‌فشان‌هامان را پاک و دوده‌گیری کنیم و برای همین است که گاهی آن جور اسباب زحمت‌مان می‌شوند. + +شهریار کوچولو با دل‌ِگرفته آخرین نهال‌های بائوباب را هم ریشه‌کن کرد. فکر می‌کرد دیگر هیچ وقت نباید برگردد. اما آن روز صبح گرچه از این کارهای معمولیِ هر روزه کُلّی لذت برد موقعی که آخرین آب را پای گل داد و خواست بگذاردش زیرِ سرپوش چیزی نمانده‌بود که اشکش سرازیر شود. +به گل گفت: -خدا نگهدار! +اما او جوابش را نداد. +دوباره گفت: -خدا نگهدار! +گل سرفه‌کرد، گیرم این سرفه اثر چائیدن نبود. بالاخره به زبان آمد و گفت: +-من سبک مغز بودم. ازت عذر می‌خواهم. سعی کن خوشبخت باشی. +از این که به سرکوفت و سرزنش‌های همیشگی برنخورد حیرت کرد و سرپوش به دست هاج‌وواج ماند. از این محبتِ آرام سر در نمی‌آورد. +گل به‌اش گفت: -خب دیگر، دوستت دارم. اگر تو روحت هم از این موضوع خبردار نشد تقصیر من است. باشد، زیاد مهم نیست. اما تو هم مثل من بی‌عقل بودی... سعی کن خوشبخت بشوی... این سرپوش را هم بگذار کنار، دیگر به دردم نمی‌خورد. +-آخر، باد... +-آن قدرهاهم سَرمائو نیستم... هوای خنک شب برای سلامتیم خوب است. خدانکرده گُلم آخر. +-آخر حیوانات... +-اگر خواسته‌باشم با شب‌پره‌ها آشنا بشوم جز این که دو سه تا کرمِ حشره را تحمل کنم چاره‌ای ندارم. شب‌پره باید خیلی قشنگ باشد. جز آن کی به دیدنم می‌آید؟ تو که می‌روی به آن دور دورها. از بابتِ درنده‌ها هم هیچ کَکَم نمی‌گزد: «من هم برای خودم چنگ و پنجه‌ای دارم». +و با سادگی تمام چهارتا خارش را نشان داد. بعد گفت: +-دست‌دست نکن دیگر! این کارت خلق آدم را تنگ می‌کند. حالا که تصمیم گرفته‌ای بروی برو! + +و این را گفت، چون که نمی‌خواست شهریار کوچولو گریه‌اش را ببیند. گلی بود تا این حد خودپسند... + +۱۰ +خودش را در منطقه‌ی اخترک‌های ۳۲۵، ۳۲۶، ۳۲۷، ۳۲۸، ۳۲۹ و ۳۳۰ دید. این بود که هم برای سرگرمی و هم برای چیزیادگرفتن بنا کرد یکی‌یکی‌شان را سیاحت کردن. + +اخترکِ اول مسکن پادشاهی بود که با شنلی از مخمل ارغوانی قاقم بر اورنگی بسیار ساده و در عین حال پرشکوه نشسته بود و همین که چشمش به شهریار کوچولو افتاد داد زد: +-خب، این هم رعیت! +شهریار کوچولو از خودش پرسید: -او که تا حالا هیچ وقت مرا ندیده چه جوری می‌تواند بشناسدم؟ +دیگر اینش را نخوانده‌بود که دنیابرای پادشاهان به نحو عجیبی ساده شده و تمام مردم فقط یک مشت رعیت به حساب می‌آیند. + +پادشاه در سیاره‌اش +پادشاه که می‌دید بالاخره شاهِ کسی شده و از این بابت کبکش خروس می‌خواند گفت: -بیا جلو بهتر ببینیمت. شهریار کوچولو با چشم پیِ جایی گشت که بنشیند اما شنلِ قاقمِ حضرتِ پادشاهی تمام اخترک را دربرگرفته‌بود. ناچار همان طور سر پا ماند و چون سخت خسته بود به دهن‌دره افتاد. +شاه به‌اش گفت: -خمیازه کشیدن در حضرتِ سلطان از نزاکت به دور است. این کار را برایت قدغن می‌کنم. شهریار کوچولو که سخت خجل شده‌بود در آمد که: +-نمی‌توانم جلوِ خودم را بگیرم. راه درازی طی‌کرده‌ام و هیچ هم نخوابیده‌ام... +پادشاه گفت: -خب خب، پس بِت امر می‌کنم خمیازه بکشی. سال‌هاست خمیازه‌کشیدن کسی را ندیده‌ام برایم تازگی دارد. یاالله باز هم خمیازه بکش. این یک امر است. +شهریار کوچولو گفت: -آخر این جوری من دست و پایم را گم می‌کنم... دیگر نمی‌توانم. +شاه گفت: -هوم! هوم! خب، پس من به‌ات امر می‌کنم که گاهی خمیازه بکشی گاهی نه. +تند و نامفهوم حرف می‌زد و انگار خلقش حسابی تنگ بود. + +پادشاه فقط دربند این بود که مطیع فرمانش باشند. در مورد نافرمانی‌ها هم هیچ نرمشی از خودش نشان نمی‌داد. یک پادشاهِ تمام عیار بود گیرم چون زیادی خوب بود اوامری که صادر می‌کرد اوامری بود منطقی. مثلا خیلی راحت در آمد که: «اگر من به یکی از سردارانم امر کنم تبدیل به یکی از این مرغ‌های دریایی بشود و یارو اطاعت نکند تقسیر او نیست که، تقصیر خودم است». +شهریار کوچولو در نهایت ادب پرسید: -اجازه می‌فرمایید بنشینم؟ +پادشاه که در نهایتِ شکوه و جلال چینی از شنل قاقمش را جمع می‌کرد گفت: -به‌ات امر می‌کنیم بنشینی. + +منتها شهریار کوچولو مانده‌بود حیران: آخر آن اخترک کوچک‌تر از آن بود که تصورش را بشود کرد. واقعا این پادشاه به چی سلطنت می‌کرد؟ گفت: -قربان عفو می‌فرمایید که ازتان سوال می‌کنم... +پادشاه با عجله گفت: -به‌ات امر می‌کنیم از ما سوال کنی. +-شما قربان به چی سلطنت می‌فرمایید؟ +پادشاه خیلی ساده گفت: -به همه چی. +-به همه‌چی؟ +پادشاه با حرکتی قاطع به اخترک خودش و اخترک‌های دیگر و باقی ستاره‌ها اشاره کرد. +شهریار کوچولو پرسید: -یعنی به همه‌ی این ها؟ +شاه جواب داد: -به همه‌ی این ها. +آخر او فقط یک پادشاه معمولی نبود که، یک پادشاهِ جهانی بود. +-آن وقت ستاره‌ها هم سربه‌فرمان‌تانند؟ +پادشاه گفت: -البته که هستند. همه‌شان بی‌درنگ هر فرمانی را اطاعت می‌کنند. ما نافرمانی را مطلقا تحمل نمی‌کنیم. + +یک چنین قدرتی شهریار کوچولو را به شدت متعجب کرد. اگر خودش چنین قدرتی می‌داشت بی این که حتا صندلیش را یک ذره تکان بدهد روزی چهل و چهار بار که هیچ روزی هفتاد بار و حتا صدبار و دویست‌بار غروب آفتاب را تماشا می‌کرد! و چون بفهمی نفهمی از یادآوریِ اخترکش که به امان خدا ول‌کرده‌بود غصه‌اش شد جراتی به خودش داد که از پادشاه درخواست محبتی بکند: +-دلم می‌خواست یک غروب آفتاب تماشا کنم... در حقم التفات بفرمایید امر کنید خورشید غروب کند. +-اگر ما به یک سردار امر کنیم مثل شب‌پره از این گل به آن گل بپرد یا قصه‌ی سوزناکی بنویسد یا به شکل مرغ دریایی در آید و او امریه را اجرا نکند کدام یکی‌مان مقصریم، ما یا او؟ +شهریار کوچولو نه گذاشت، نه برداشت، گفت: -شما. +پادشاه گفت: -حرف ندارد. باید از هر کسی چیزی را توقع داشت که ازش ساخته باشد. قدرت باید پیش از هر چیز به عقل متکی باشد. اگر تو به ملتت فرمان بدهی که بروند خودشان را بیندازند تو دریا انقلاب می‌کنند. حق داریم توقع اطاعت داشته باشیم چون اوامرمان عاقلانه است. +شهریار کوچولو که هیچ وقت چیزی را که پرسیده بود فراموش نمی‌کرد گفت: -غروب آفتاب من چی؟ +-تو هم به غروب آفتابت می‌رسی. امریه‌اش را صادر می‌کنیم. منتها با شَمِّ حکمرانی‌مان منتظریم زمینه‌اش فراهم بشود. +شهریار کوچولو پرسید: -کِی فراهم می‌شود؟ +پادشاه بعد از آن که تقویم کَت و کلفتی را نگاه کرد جواب داد: +-هوم! هوم! حدودِ... حدودِ... غروب. حدودِ ساعت هفت و چهل دقیقه... و آن وقت تو با چشم‌های خودت می‌بینی که چه‌طور فرمان ما اجرا می‌شود! + +شهریار کوچولو خمیازه کشید. از این که تماشای آفتاب غروب از کیسه‌اش رفته‌بود تاسف می‌خورد. از آن گذشته دلش هم کمی گرفته‌بود. این بود که به پادشاه گفت: +-من دیگر این‌جا کاری ندارم. می‌خواهم بروم. +شاه که دلش برای داشتن یک رعیت غنج می‌زد گفت: +-نرو! نرو! وزیرت می‌کنیم. +-وزیرِ چی؟ +-وزیرِ دادگستری! +-آخر این جا کسی نیست که محاکمه بشود. +پادشاه گفت: -معلوم نیست. ما که هنوز گشتی دور قلمرومان نزده‌ایم. خیلی پیر شده‌ایم، برای کالسکه جا نداریم. پیاده‌روی هم خسته‌مان می‌کند. +شهریار کوچولو که خم شده‌بود تا نگاهی هم به آن طرف اخترک بیندازد گفت: -بَه! من نگاه کرده‌ام، آن طرف هم دیارالبشری نیست. +پادشاه به‌اش جواب داد: -خب، پس خودت را محاکمه کن. این کار مشکل‌تر هم هست. محاکمه کردن خود از محاکمه‌کردن دیگران خیلی مشکل تر است. اگر توانستی در مورد خودت قضاوت درستی بکنی معلوم می‌شود یک فرزانه‌ی تمام عیاری. +شهریار کوچولو گفت: -من هر جا باشم می‌توانم خودم را محاکمه کنم، چه احتیاجی است این جا بمانم؟ پادشاه گفت: -هوم! هوم! فکر می‌کنیم یک جایی تو اخترک ما یک موش پیر هست. صدایش را شب ها می‌شنویم. می‌توانی او را به محاکمه بکشی و گاه‌گاهی هم به اعدام محکومش کنی. در این صورت زندگی او به عدالت تو بستگی پیدا می‌کند. گیرم تو هر دفعه عفوش می‌کنی تا همیشه زیر چاق داشته باشیش. آخر یکی بیش‌تر نیست که. +شهریار کوچولو جواب داد: -من از حکم اعدام خوشم نمی‌آید. فکر می‌کنم دیگر باید بروم. +پادشاه گفت: -نه! + +اما شهریار کوچولو که آماده‌ی حرکت شده بود و ضمنا هم هیچ دلش نمی‌خواست اسباب ناراحتی سلطان پیر بشود گفت: +-اگر اعلی‌حضرت مایلند اوامرشان دقیقا اجرا بشود می‌توانند فرمان خردمندانه‌ای در مورد بنده صادر بفرمایند. مثلا می‌توانند به بنده امر کنند ظرف یک دقیقه راه بیفتم. تصور می‌کنم زمینه‌اش هم آماده باشد... +چون پادشاه جوابی نداد شهریار کوچولو اول دو دل ماند اما بعد آهی کشید و به راه افتاد. +آن‌وقت پادشاه با شتاب فریاد زد: -سفیر خودمان فرمودیمت! +حالت بسیار شکوهمندی داشت. + +شهریار کوچولو همان طور که می‌رفت تو دلش می‌گفت: -این آدم بزرگ‌ها راستی راستی چه‌قدر عجیبند! + +۱۱ +اخترک دوم مسکن آدم خود پسندی بود. +خود پسند چشمش که به شهریار کوچولو افتاد از همان دور داد زد: -به‌به! این هم یک ستایشگر که دارد می‌آید مرا ببیند! + +خودپسند در سیاره‌اش +آخر برای خودپسندها دیگران فقط یک مشت ستایش‌گرند. +شهریار کوچولو گفت: -سلام! چه کلاه عجیب غریبی سرتان گذاشته‌اید! +خود پسند جواب داد: -مال اظهار تشکر است. منظورم موقعی است که هلهله‌ی ستایشگرهایم بلند می‌شود. گیرم متاسفانه تنابنده‌ای گذارش به این طرف‌ها نمی‌افتد. +شهریار کوچولو که چیزی حالیش نشده بود گفت: +-چی؟ +خودپسند گفت: -دست‌هایت را بزن به هم دیگر. +شهریار کوچولو دست زد و خودپسند کلاهش را برداشت و متواضعانه از او تشکر کرد. +شهریار کوچولو با خودش گفت: «دیدنِ این تفریحش خیلی بیش‌تر از دیدنِ پادشاه‌است». و دوباره بنا کرد دست‌زدن و خودپسند با برداشتن کلاه بنا کرد تشکر کردن. + +پس از پنج دقیقه‌ای شهریار کوچولو که از این بازی یک‌نواخت خسته شده بود پرسید: -چه کار باید کرد که کلاه از سرت بیفتد؟ +اما خودپسند حرفش را نشنید. آخر آن‌ها جز ستایش خودشان چیزی را نمی‌شنوند. +از شهریار کوچولو پرسید: -تو راستی راستی به من با چشم ستایش و تحسین نگاه می‌کنی؟ +-ستایش و تحسین یعنی چه؟ +-یعنی قبول این که من خوش‌قیافه‌ترین و خوش‌پوش‌ترین و ثروت‌مندترین و باهوش‌ترین مرد این اخترکم. +-آخر روی این اخترک که فقط خودتی و کلاهت. +-با وجود این ستایشم کن. این لطف را در حق من بکن. +شهریار کوچولو نیم‌چه شانه‌ای بالا انداخت و گفت: -خب، ستایشت کردم. اما آخر واقعا چیِ این برایت جالب است؟ + +شهریار کوچولو به راه افتاد و همان طور که می‌رفت تو دلش می‌گفت: -این آدم بزرگ‌ها راستی راستی چه‌قدر عجیبند! + +۱۲ +تو اخترک بعدی می‌خواره‌ای می‌نشست. دیدار کوتاه بود اما شهریار کوچولو را به غم بزرگی فرو برد. + +می‌خواره در سیاره‌اش + +به می‌خواره که صُم‌بُکم پشت یک مشت بطری خالی و یک مشت بطری پر نشسته بود گفت: -چه کار داری می‌کنی؟ +می‌خواره با لحن غم‌زده‌ای جواب داد: -مِی می‌زنم. +شهریار کوچولو پرسید: -مِی می‌زنی که چی؟ +می‌خواره جواب داد: -که فراموش کنم. +شهریار کوچولو که حالا دیگر دلش برای او می‌سوخت پرسید: -چی را فراموش کنی؟ +می‌خواره همان طور که سرش را می‌انداخت پایین گفت: -سر شکستگیم را. +شهریار کوچولو که دلش می‌خواست دردی از او دوا کند پرسید: -سرشکستگی از چی؟ +می‌خواره جواب داد: -سرشکستگیِ می‌خواره بودنم را. +این را گفت و قال را کند و به کلی خاموش شد. و شهریار کوچولو مات و مبهوت راهش را گرفت و رفت و همان جور که می‌رفت تو دلش می‌گفت: -این آدم بزرگ‌ها راستی‌راستی چه‌قدر عجیبند! + +۱۳ +اخترک چهارم اخترک مرد تجارت‌پیشه بود. این بابا چنان مشغول و گرفتار بود که با ورود شهریار کوچولو حتا سرش را هم بلند نکرد. + +مردِ تجارت‌پیشه در سیاره‌اش +شهریار کوچولو گفت: -سلام. آتش‌سیگارتان خاموش شده. +-سه و دو می‌کند پنج. پنج و هفت دوازده و سه پانزده. سلام. پانزده و هفت بیست و دو. بیست و دو و شش بیست و هشت. وقت ندارم روشنش کنم. بیست و شش و پنج سی و یک. اوف! پس جمعش می‌کند پانصدویک میلیون و ششصد و بیست و دو هزار هفتصد و سی و یک. +-پانصد میلیون چی؟ +-ها؟ هنوز این جایی تو؟ پانصد و یک میلیون چیز. چه می‌دانم، آن قدر کار سرم ریخته که!... من یک مرد جدی هستم و با حرف‌های هشت‌من‌نه‌شاهی سر و کار ندارم!... دو و پنج هفت... +شهریار کوچولو که وقتی چیزی می‌پرسید دیگر تا جوابش را نمی‌گرفت دست بردار نبود دوباره پرسید: +-پانصد و یک میلیون چی؟ +تاجر پیشه سرش را بلند کرد: +-تو این پنجاه و چهار سالی که ساکن این اخترکم همه‌اش سه بار گرفتار مودماغ شده‌ام. اولیش بیست و دو سال پیش یک سوسک بود که خدا می‌داند از کدام جهنم پیدایش شد. صدای وحشت‌ناکی از خودش در می‌آورد که باعث شد تو یک جمع چهار جا اشتباه کنم. دفعه‌ی دوم یازده سال پیش بود که استخوان درد بی‌چاره‌ام کرد. من ورزش نمی‌کنم. وقت یللی‌تللی هم ندارم. آدمی هستم جدی... این هم بار سومش!... کجا بودم؟ پانصد و یک میلیون و... +-این همه میلیون چی؟ +تاجرپیشه فهمید که نباید امید خلاصی داشته باشد. گفت: -میلیون‌ها از این چیزهای کوچولویی که پاره‌ای وقت‌ها تو هوا دیده می‌شود. +-مگس؟ +-نه بابا. این چیزهای کوچولوی براق. +-زنبور عسل؟ +-نه بابا! همین چیزهای کوچولوی طلایی که وِلِنگارها را به عالم هپروت می‌برد. گیرم من شخصا آدمی هستم جدی که وقتم را صرف خیال‌بافی نمی‌کنم. +-آها، ستاره؟ +-خودش است: ستاره. +-خب پانصد میلیون ستاره به چه دردت می‌خورد؟ +-پانصد و یک میلیون و ششصد و بیست و دو هزار و هفتصد و سی و یکی. من جدیّم و دقیق. +-خب، به چه دردت می‌خورند؟ +-به چه دردم می‌خورند؟ +-ها. +-هیچی تصاحب‌شان می‌کنم. +-ستاره‌ها را؟ +-آره خب. +-آخر من به یک پادشاهی برخوردم که... +-پادشاه‌ها تصاحب نمی‌کنند بل‌که به‌اش «سلطنت» می‌کنند. این دو تا با هم خیلی فرق دارد. +-خب، حالا تو آن‌ها را تصاحب می‌کنی که چی بشود؟ +-که دارا بشوم. +-خب دارا شدن به چه کارت می‌خورد؟ +-به این کار که، اگر کسی ستاره‌ای پیدا کرد من ازش بخرم. +شهریار کوچولو با خودش گفت: «این بابا هم منطقش یک خرده به منطق آن دائم‌الخمره می‌بَرَد.» با وجود این باز ازش پرسید: +-چه جوری می‌شود یک ستاره را صاحب شد؟ +تاجرپیشه بی درنگ با اَخم و تَخم پرسید: -این ستاره‌ها مال کی‌اند؟ +-چه می‌دانم؟ مال هیچ کس. +-پس مال منند، چون من اول به این فکر افتادم. +-همین کافی است؟ +-البته که کافی است. اگر تو یک جواهر پیدا کنی که مال هیچ کس نباشد می‌شود مال تو. اگر جزیره‌ای کشف کنی که مال هیچ کس نباشد می‌شود مال تو. اگر فکری به کله‌ات بزند که تا آن موقع به سر کسی نزده به اسم خودت ثبتش می‌کنی و می‌شود مال تو. من هم ستاره‌ها را برای این صاحب شده‌ام که پیش از من هیچ کس به فکر نیفتاده بود آن‌ها را مالک بشود. +شهریار کوچولو گفت: -این ها همه‌اش درست. منتها چه کارشان می‌کنی؟ +تاجر پیشه گفت: -اداره‌شان می‌کنم، همین جور می‌شمارم‌شان و می‌شمارم‌شان. البته کار مشکلی است ولی خب دیگر، من آدمی هستم بسیار جدی. +شهریار کوچولو که هنوز این حرف تو کَتَش نرفته‌بود گفت: +-اگر من یک شال گردن ابریشمی داشته باشم می‌توانم بپیچم دور گردنم با خودم ببرمش. اگر یک گل داشته باشم می‌توانم بچینم با خودم ببرمش. اما تو که نمی‌توانی ستاره‌ها را بچینی! +-نه. اما می‌توانم بگذارم‌شان تو بانک. +-اینی که گفتی یعنی چه؟ +-یعنی این که تعداد ستاره‌هایم را رو یک تکه کاغذ می‌نویسم می‌گذارم تو کشو درش را قفل می‌کنم. +-همه‌اش همین؟ +-آره همین کافی است. + +شهریار کوچولو فکر کرد «جالب است. یک خرده هم شاعرانه است. اما کاری نیست که آن قدرها جدیش بشود گرفت». آخر تعبیر او از چیزهای جدی با تعبیر آدم‌های بزرگ فرق می‌کرد. +باز گفت: -من یک گل دارم که هر روز آبش می‌دهم. سه تا هم آتش‌فشان دارم که هفته‌ای یک بار پاک و دوده‌گیری‌شان می‌کنم. آخر آتش‌فشان خاموشه را هم پاک می‌کنم. آدم کفِ دستش را که بو نکرده! رو این حساب، هم برای آتش‌فشان‌ها و هم برای گل این که من صاحب‌شان باشم فایده دارد. تو چه فایده‌ای به حال ستاره‌ها داری؟ + +تاجرپیشه دهن باز کرد که جوابی بدهد اما چیزی پیدا نکرد. و شهریار کوچولو راهش را گرفت و رفت و همان جور که می‌رفت تو دلش می‌گفت: -این آدم بزرگ‌ها راستی راستی چه‌قدر عجیبند! + +۱۴ +اخترکِ پنجم چیز غریبی بود. از همه‌ی اخترک‌های دیگر کوچک‌تر بود، یعنی فقط به اندازه‌ی یک فانوس پایه‌دار و یک فانوس‌بان جا داشت. + +فانوس‌بان در حالِ روشن کردنِ فانوس در سیاره‌اش +شهریار کوچولو از این راز سر در نیاورد که یک جا میان آسمان خدا تو اخترکی که نه خانه‌ای روش هست نه آدمی، حکمت وجودی یک فانوس و یک فانوس‌بان چه می‌تواند باشد. با وجود این تو دلش گفت: +-خیلی احتمال دارد که این بابا عقلش پاره‌سنگ ببرد. اما به هر حال از پادشاه و خودپسند و تاجرپیشه و مسته کم عقل‌تر نیست. دست کم کاری که می‌کند یک معنایی دارد. فانوسش را که روشن می‌کند عین‌هو مثل این است که یک ستاره‌ی دیگر یا یک گل به دنیا می‌آورد و خاموشش که می‌کند پنداری گل یا ستاره‌ای را می‌خواباند. سرگرمی زیبایی است و چیزی که زیبا باشد بی گفت‌وگو مفید هم هست. + +وقتی رو اخترک پایین آمد با ادب فراوان به فانوس‌بان سلام کرد: +-سلام. واسه چی فانوس را خاموش کردی؟ +-دستور است. صبح به خیر! +-دستور چیه؟ +-این است که فانوسم را خاموش کنم. شب خوش! +و دوباره فانوس را روشن کرد. +-پس چرا روشنش کردی باز؟ +فانوس‌بان جواب داد: -خب دستور است دیگر. +شهریار کوچولو گفت: -اصلا سر در نمیارم. +فانوس‌بان گفت: -چیز سر در آوردنی‌یی توش نیست که. دستور دستور است. روز بخیر! +و باز فانوس را خاموش کرد. +بعد با دستمال شطرنجی قرمزی عرق پیشانیش را خشکاند و گفت: +-کار جان‌فرسایی دارم. پیش‌تر ها معقول بود: صبح خاموشش می‌کردم و شب که می‌شد روشنش می‌کردم. باقی روز را فرصت داشتم که استراحت کنم و باقی شب را هم می‌توانستم بگیرم بخوابم... +-بعدش دستور عوض شد؟ +فانوس‌بان گفت: -دستور عوض نشد و بدبختی من هم از همین جاست: سیاره سال به سال گردشش تندتر و تندتر شده اما دستور همان جور به قوت خودش باقی مانده است. +-خب؟ +-حالا که سیاره دقیقه‌ای یک بار دور خودش می‌گردد دیگر من یک ثانیه هم فرصت استراحت ندارم: دقیقه‌ای یک بار فانوس را روشن می‌کنم یک بار خاموش. +-چه عجیب است! تو اخترک تو شبانه روز همه‌اش یک دقیقه طول می‌کشد! +فانوس‌بان گفت: -هیچ هم عجیب نیست. الان یک ماه تمام است که ما داریم با هم اختلاط می‌کنیم. +-یک ماه؟ +-آره. سی دقیقه. سی روز! شب خوش! +و دوباره فانوس را روشن کرد. + +شهریار کوچولو به فانوس‌بان نگاه کرد و حس کرد این مرد را که تا این حد به دستور وفادار است دوست می‌دارد. یادِ آفتاب‌غروب‌هایی افتاد که آن وقت‌ها خودش با جابه‌جا کردن صندلیش دنبال می‌کرد. برای این که دستی زیر بال دوستش کرده باشد گفت: +-می‌دانی؟ یک راهی بلدم که می‌توانی هر وقت دلت بخواهد استراحت کنی. +فانوس‌بان گفت: -آرزوش را دارم. +آخر آدم می‌تواند هم به دستور وفادار بماند هم تنبلی کند. +شهریار کوچولو دنبال حرفش را گرفت و گفت: +-تو، اخترکت آن‌قدر کوچولوست که با سه تا شلنگ برداشتن می‌توانی یک بار دور بزنیش. اگر آن اندازه که لازم است یواش راه بروی می‌توانی کاری کنی که مدام تو آفتاب بمانی. پس هر وقت خواستی استراحت کنی شروع می‌کنی به راه‌رفتن... به این ترتیب روز هرقدر که بخواهی برایت کِش می‌آید. +فانوس‌بان گفت: -این کار گرهی از بدبختی من وا نمی‌کند. تنها چیزی که تو زندگی آرزویش را دارم یک چرت خواب است. +شهریار کوچولو گفت: -این یکی را دیگر باید بگذاری در کوزه. +فانوس‌بان گفت: -آره. باید بگذارمش در کوزه... صبح بخیر! +و فانوس را خاموش کرد. + +شهریار کوچولو میان راه با خودش گفت: گرچه آن‌های دیگر، یعنی خودپسنده و تاجره اگر این را می‌دیدند دستش می‌انداختند و تحقیرش می‌کردند، هر چه نباشد کار این یکی به نظر من کم‌تر از کار آن‌ها بی‌معنی و مضحک است. شاید به خاطر این که دست کم این یکی به چیزی جز خودش مشغول است. + +از حسرت آهی کشید و همان طور با خودش گفت: +-این تنها کسی بود که من می‌توانستم باش دوست بشوم. گیرم اخترکش راستی راستی خیلی کوچولو است و دو نفر روش جا نمی‌گیرند. + +چیزی که جرات اعترافش را نداشت حسرت او بود به این اخترک کوچولویی که، بخصوص، به هزار و چهارصد و چهل بار غروب آفتاب در هر بیست و چهار ساعت برکت پیدا کرده بود. + +۱۵ +اخترک ششم اخترکی بود ده بار فراخ‌تر، و آقاپیره‌ای توش بود که کتاب‌های کَت‌وکلفت می‌نوشت. + +جغرافی‌دان در سیاره‌اش +همین که چشمش به شهریار کوچولو افتاد با خودش گفت: +-خب، این هم یک کاشف! +شهریار کوچولو لب میز نشست و نفس نفس زد. نه این که راه زیادی طی کرده بود؟ +آقا پیره به‌اش گفت: -از کجا می‌آیی؟ +شهریار کوچولو گفت: -این کتاب به این کلفتی چی است؟ شما این‌جا چه‌کار می‌کنید؟ +آقا پیره گفت: -من جغرافی‌دانم. +-جغرافی‌دان چه باشد؟ +-جغرافی‌دان به دانشمندی می‌گویند که جای دریاها و رودخانه‌ها و شهرها و کوه‌ها و بیابان‌ها را می‌داند. +شهریار کوچولو گفت: -محشر است. یک کار درست و حسابی است. +و به اخترک جغرافی‌دان، این سو و آن‌سو نگاهی انداخت. تا آن وقت اخترکی به این عظمت ندیده‌بود. +-اخترک‌تان خیلی قشنگ است. اقیانوس هم دارد؟ +جغرافی‌دان گفت: -از کجا بدانم؟ +شهریار کوچولو گفت: -عجب! (بد جوری جا خورده بود) کوه چه‌طور؟ +جغرافی‌دان گفت: -از کجا بدانم؟ +-شهر، رودخانه، بیابان؟ +جغرافی‌دان گفت: از این‌ها هم خبری ندارم. +-آخر شما جغرافی‌دانید؟ +جغرافی‌دان گفت: -درست است ولی کاشف که نیستم. من حتا یک نفر کاشف هم ندارم. کار جغرافی‌دان نیست که دوره‌بیفتد برود شهرها و رودخانه‌ها و کوه‌ها و دریاها و اقیانوس‌ها و بیابان‌ها را بشمرد. مقام جغرافی‌دان برتر از آن است که دوره بیفتد و ول‌بگردد. اصلا از اتاق کارش پا بیرون نمی‌گذارد بلکه کاشف‌ها را آن تو می‌پذیرد ازشان سوالات می‌کند و از خاطرات‌شان یادداشت بر می‌دارد و اگر خاطرات یکی از آن‌ها به نظرش جالب آمد دستور می‌دهد روی خُلقیات آن کاشف تحقیقاتی صورت بگیرد. +-برای چه؟ +-برای این که اگر کاشفی گنده‌گو باشد کار کتاب‌های جغرافیا را به فاجعه می‌کشاند. هکذا کاشفی که اهل پیاله باشد. +-آن دیگر چرا؟ +b-چون آدم‌های دائم‌الخمر همه چیز را دوتا می‌بینند. آن وقت جغرافی‌دان برمی‌دارد جایی که یک کوه +بیشتر نیست می‌نویسد دو کوه. +شهریار کوچولو گفت: -پس من یک بابایی را می‌شناسم که کاشف هجوی از آب در می‌آید. +-بعید نیست. بنابراین، بعد از آن که کاملا ثابت شد پالان کاشف کج نیست تحقیقاتی هم روی کشفی که کرده انجام می‌گیرد. +-یعنی می‌روند می‌بینند؟ +-نه، این کار گرفتاریش زیاد است. از خود کاشف می‌خواهند دلیل بیاورد. مثلا اگر پای کشف یک کوه بزرگ در میان بود ازش می‌خواهند سنگ‌های گنده‌ای از آن کوه رو کند. +جغرافی‌دان ناگهان به هیجان در آمد و گفت: -راستی تو داری از راه دوری می‌آیی! تو کاشفی! باید چند و چون اخترکت را برای من بگویی. +و با این حرف دفتر و دستکش را باز کرد و مدادش را تراشید. معمولا خاطرات کاشف‌ها را اول بامداد یادداشت می‌کنند و دست نگه می‌دارند تا دلیل اقامه کند، آن وقت با جوهر می‌نویسند. +گفت: -خب؟ +شهریار کوچولو گفت: -اخترک من چیز چندان جالبی ندارد. آخر خیلی کوچک است. سه تا آتش‌فشان دارم که دوتاش فعال است یکیش خاموش. اما، خب دیگر، آدم کف دستش را که بو نکرده. +جغرافی‌دان هم گفت: -آدم چه می‌داند چه پیش می‌آید. +-یک گل هم دارم. +-نه، نه، ما دیگر گل ها را یادداشت نمی‌کنیم. +-چرا؟ گل که زیباتر است. +-برای این که گل‌ها فانی‌اند. +-فانی یعنی چی؟ +جغرافی‌دان گفت: -کتاب‌های جغرافیا از کتاب‌های دیگر گران‌بهاترست و هیچ وقت هم از اعتبار نمی‌افتد. بسیار به ندرت ممکن است یک کوه جا عوض کند. بسیار به ندرت ممکن است آب یک اقیانوس خالی شود. ما فقط چیزهای پایدار را می‌نویسیم. +شهریار کوچولو تو حرف او دوید و گفت: -اما آتش‌فشان‌های خاموش می‌توانند از نو بیدار بشوند. فانی را نگفتید یعنی چه؟ +جغرافی‌دان گفت: -آتش‌فشان چه روشن باشد چه خاموش برای ما فرقی نمی‌کند. آن‌چه به حساب می‌آید خود کوه است که تغییر پیدا نمی‌کند. +شهریار کوچولو که تو تمام عمرش وقتی چیزی از کسی می‌پرسید دیگر دست بردار نبود دوباره سوال کرد: -فانی یعنی چه؟ +-یعنی چیزی که در آینده تهدید به نابودی شود. +-گل من هم در آینده نابود می‌شود؟ +-البته که می‌شود. +شهریار کوچولو در دل گفت: «گل من فانی است و جلو دنیا برای دفاع از خودش جز چهارتا خار هیچی ندارد، و آن وقت مرا بگو که او را توی اخترکم تک و تنها رها کرده‌ام!» +این اولین باری بود که دچار پریشانی و اندوه می‌شد اما توانست به خودش مسلط بشود. پرسید: -شما به من دیدن کجا را توصیه می‌کنید؟ +جغرافی‌دان به‌اش جواب داد: -سیاره‌ی زمین. شهرت خوبی دارد... + +و شهریار کوچولو هم چنان که به گلش فکر می‌کرد به راه افتاد. + +۱۶ +لاجرم، زمین، سیاره‌ی هفتم شد. + +زمین، فلان و بهمان سیاره نیست. رو پهنه‌ی زمین یک‌صد و یازده پادشاه (البته بامحاسبه‌ی پادشاهان سیاه‌پوست)، هفت هزار جغرافی‌دان، نه‌صد هزار تاجرپیشه، پانزده کرور می‌خواره و شش‌صد و بیست و دو کرور خودپسند و به عبارت دیگر حدود دو میلیارد آدم بزرگ زندگی می‌کند. برای آن‌که از حجم زمین مقیاسی به دست‌تان بدهم بگذارید به‌تان بگویم که پیش از اختراع برق مجبور بودند در مجموع شش قاره‌ی زمین وسایل زندگیِ لشکری جانانه شامل یکصد و شصت و دو هزار و پانصد و یازده نفر فانوس‌بان را تامین کنند. + +روشن شدن فانوس‌ها از دور خیلی باشکوه بود. حرکات این لشکر مثل حرکات یک باله‌ی تو اپرا مرتب و منظم بود. اول از همه نوبت فانوس‌بان‌های زلاندنو و استرالیا بود. این‌ها که فانوس‌هاشان را روشن می‌کردند، می‌رفتند می‌گرفتند می‌خوابیدند آن وقت نوبت فانوس‌بان‌های چین و سیبری می‌رسید که به رقص درآیند. بعد، این‌ها با تردستی تمام به پشت صحنه می‌خزیدند و جا را برای فانوس‌بان‌های ترکیه و هفت پَرکَنِه‌ی هند خالی می کردند. بعد نوبت به فانوس‌بان‌های آمریکای‌جنوبی می‌شد. و آخر سر هم نوبت فانوس‌بان‌های افریقا و اروپا می‌رسد و بعد نوبت فانوس‌بان‌های آمریکای شمالی بود. و هیچ وقتِ خدا هم هیچ‌کدام این‌ها در ترتیب ورودشان به صحنه دچار اشتباه نمی‌شدند. چه شکوهی داشت! میان این جمع عظیم فقط نگه‌بانِ تنها فانوسِ قطب شمال و همکارش نگه‌بانِ تنها فانوسِ قطب جنوب بودند که عمری به بطالت و بی‌هودگی می‌گذراندند: آخر آن‌ها سالی به سالی همه‌اش دو بار کار می‌کردند. + +۱۷ +آدمی که اهل اظهار لحیه باشد بفهمی نفهمی می‌افتد به چاخان کردن. من هم تو تعریف قضیه‌ی فانوس‌بان‌ها برای شما آن‌قدرهاروراست نبودم. می‌ترسم به آن‌هایی که زمین ما را نمی‌سناسند تصور نادرستی داده باشم. انسان‌ها رو پهنه‌ی زمین جای خیلی کمی را اشغال می‌کنند. اگر همه‌ی دو میلیارد نفری که رو کره‌ی زمین زندگی می‌کنند بلند بشوند و مثل موقعی که به تظاهرات می‌روند یک خورده جمع و جور بایستند راحت و بی‌درپسر تو میدانی به مساحت بیست میل در بیست میل جا می‌گیرند. همه‌ی جامعه‌ی بشری را می‌شود یک‌جا روی کوچک‌ترین جزیره‌ی اقیانوس آرام کُپه کرد. + +البته گفت‌وگو ندارد که آدم بزرگ‌ها حرف‌تان را باور نمی‌کنند. آخر تصور آن‌ها این است که کلی جا اشغال کرده‌اند، نه این‌که مثل بائوباب‌ها خودشان را خیلی مهم می‌بینند؟ بنابراین به‌شان پیش‌نهاد می‌کنید که بنشینند حساب کنند. آن‌ها هم که عاشق اعداد و ارقامند، پس این پیش‌نهاد حسابی کیفورشان می‌کند. اما شما را به خدا بی‌خودی وقت خودتان را سر این جریمه‌ی مدرسه به هدر ندهید. این کار دو قاز هم نمی‌ارزد. به من که اطمینان دارید. شهریار کوچولو پاش که به زمین رسید از این که دیارالبشری دیده نمی‌شد سخت هاج و واج ماند. + +شهریار کوچولو وسطِ کویر +تازه داشت از این فکر که شاید سیاره را عوضی گرفته ترسش بر می‌داشت که چنبره‌ی مهتابی رنگی رو ماسه‌ها جابه‌جا شد.شهریار کوچولو و مار +شهریار کوچولو همین‌جوری سلام کرد. +مار گفت: -سلام. +شهریار کوچولو پرسید: -رو چه سیاره‌ای پایین آمده‌ام؟ +مار جواب داد: -رو زمین تو قاره‌ی آفریقا. +-عجب! پس رو زمین انسان به هم نمی‌رسد؟ +مار گفت: -این‌جا کویر است. تو کویر کسی زندگی نمی‌کند. زمین بسیار وسیع است. +شهریار کوچولو رو سنگی نشست و به آسمان نگاه کرد. گفت: -به خودم می‌گویم ستاره‌ها واسه این روشنند که هرکسی بتواند یک روز مال خودش را پیدا کند!... اخترک مرا نگاه! درست بالا سرمان است... اما چه‌قدر دور است! +مار گفت: -قشنگ است. این‌جا آمده‌ای چه کار؟ +شهریار کوچولو گفت: -با یک گل بگومگویم شده. +مار گفت: -عجب! +و هر دوشان خاموش ماندند. +دست آخر شهریار کوچولو درآمد که: -آدم‌ها کجاند؟ آدم تو کویر یک خرده احساس تنهایی می‌کند. +مار گفت: -پیش آدم‌ها هم احساس تنهایی می‌کنی. +شهریار کوچولو مدت درازی تو نخ او رفت و آخر سر به‌اش گفت: -تو چه جانور بامزه‌ای هستی! مثل یک انگشت، باریکی. +مار گفت: -عوضش از انگشت هر پادشاهی مقتدرترم. +شهریار کوچولو لب‌خندی زد و گفت: -نه چندان... پا هم که نداری. حتا راه هم نمی‌تونی بری... +-من می‌تونم تو را به چنان جای دوری ببرم که با هیچ کشتی‌یی هم نتونی بری. +مار این را گفت و دور قوزک پای شهریار کوچولو پیچید. عین یک خلخال طلا. و باز درآمد که: -هر کسی را لمس کنم به خاکی که ازش درآمده بر می‌گردانم اما تو پاکی و از یک سیّاره‌ی دیگر آمده‌ای... +شهریار کوچولو جوابی بش نداد. +-تو رو این زمین خارایی آن‌قدر ضعیفی که به حالت رحمم می‌آید. روزی‌روزگاری اگر دلت خیلی هوای اخترکت را کرد بیا من کمکت کنم... من می‌توانم... +شهریار کوچولو گفت: -آره تا تهش را خواندم. اما راستی تو چرا همه‌ی حرف‌هایت را به صورت معما درمی‌آری؟ + +مار گفت: -حلّال همه‌ی معماهام من. +و هر دوشان خاموش شدند. + +۱۸ +شهریار کوچولو کویر را از پاشنه درکرد و جز یک گل به هیچی برنخورد: یک گل سه گل‌برگه. یک گلِ ناچیز. + +یک گُل وسطِ کویر + +شهریار کوچولو گفت: -سلام. +گل گفت: -سلام. +شهریار کوچولو با ادب پرسید: -آدم‌ها کجاند؟ +گل روزی روزگاری عبور کاروانی را دیده‌بود. این بود که گفت: -آدم‌ها؟ گمان کنم ازشان شش هفت تایی باشد. سال‌ها پیش دیدم‌شان. منتها خدا می‌داند کجا می‌شود پیداشان کرد. باد این‌ور و آن‌ور می‌بَرَدشان؛ نه این که ریشه ندارند؟ بی‌ریشگی هم حسابی اسباب دردسرشان شده. +شهریار کوچولو گفت: -خداحافظ. +گل گفت: -خداحافظ. + +۱۹ +از کوه بلندی بالا رفت. + +شهریار کوچولو بر قله‌ی کوهِ بلند +تنها کوه‌هایی که به عمرش دیده بود سه تا آتش‌فشان‌های اخترک خودش بود که تا سر زانویش می‌رسید و از آن یکی که خاموش بود جای چارپایه استفاده می‌کرد. این بود که با خودش گفت: «از سر یک کوه به این بلندی می‌توانم به یک نظر همه‌ی سیاره و همه‌ی آدم‌ها را ببینم...» اما جز نوکِ تیزِ صخره‌های نوک‌تیز چیزی ندید. +همین جوری گفت: -سلام. +طنین به‌اش جواب داد: -سلام... سلام... سلام... +شهریار کوچولو گفت: -کی هستید شما؟ +طنین به‌اش جواب داد: -کی هستید شما... کی هستید شما... کی هستید شما... +گفت: -با من دوست بشوید. من تک و تنهام. +طنین به‌اش جواب داد: -من تک و تنهام... من تک و تنهام... من تک و تنهام... +آن‌وقت با خودش فکر کرد: «چه سیاره‌ی عجیبی! خشک‌ِخشک و تیزِتیز و شورِشور. این آدم‌هاش که یک ذره قوه‌ی تخیل ندارند و هر چه را بشنوند عینا تکرار می‌کنند... تو اخترک خودم گلی داشتم که همیشه اول او حرف می‌زد...» + +۲۰ +اما سرانجام، بعد از مدت‌ها راه رفتن از میان ریگ‌ها و صخره‌ها و برف‌ها به جاده‌ای برخورد. و هر جاده‌ای یک‌راست می‌رود سراغ آدم‌ها. +گفت: -سلام. +و مخاطبش گلستان پرگلی بود. + +شهریار کوچولو در گلستانِ پرگل + +گل‌ها گفتند: -سلام. +شهریار کوچولو رفت تو بحرشان. همه‌شان عین گل خودش بودند. حیرت‌زده ازشان پرسید: -شماها کی هستید؟ +گفتند: -ما گل سرخیم. + +آهی کشید و سخت احساس شوربختی کرد. گلش به او گفته بود که از نوع او تو تمام عالم فقط همان یکی هست و حالا پنج‌هزارتا گل، همه مثل هم، فقط تو یک گلستان! فکر کرد: «اگر گل من این را می‌دید بدجور از رو می‌رفت. پشت سر هم بنا می‌کرد سرفه‌کردن و، برای این‌که از هُوشدن نجات پیدا کند خودش را به مردن می‌زد و من هم مجبور می‌شدم وانمود کنم به پرستاریش، وگرنه برای سرشکسته کردنِ من هم شده بود راستی راستی می‌مرد...» و باز تو دلش گفت: «مرا باش که فقط بایک دانه گل خودم را دولت‌مندِ عالم خیال می‌کردم در صورتی‌که آن‌چه دارم فقط یک گل معمولی است. با آن گل و آن سه تا آتش‌فشان که تا سرِ زانومَند و شاید هم یکی‌شان تا ابد خاموش بماند شهریارِ چندان پُرشوکتی به حساب نمی‌آیم.» + +شهریار کوچولو در حالِ احساسِ شوربختی +رو سبزه‌ها دراز شد و حالا گریه نکن کی گریه‌کن. + +۲۱ +آن وقت بود که سر و کله‌ی روباه پیدا شد. + +شهریار کوچولو و روباه + +روباه گفت: -سلام. +شهریار کوچولو برگشت اما کسی را ندید. با وجود این با ادب تمام گفت: -سلام. +صداگفت: -من این‌جام، زیر درخت سیب... +شهریار کوچولو گفت: -کی هستی تو؟ عجب خوشگلی! +روباه گفت: -یک روباهم من. +شهریار کوچولو گفت: -بیا با من بازی کن. نمی‌دانی چه قدر دلم گرفته... +روباه گفت: -نمی‌توانم بات بازی کنم. هنوز اهلیم نکرده‌اند آخر. +شهریار کوچولو آهی کشید و گفت: -معذرت می‌خواهم. +اما فکری کرد و پرسید: -اهلی کردن یعنی چه؟ +روباه گفت: -تو اهل این‌جا نیستی. پی چی می‌گردی؟ +شهریار کوچولو گفت: -پی آدم‌ها می‌گردم. نگفتی اهلی کردن یعنی چه؟ +روباه گفت: -آدم‌ها تفنگ دارند و شکار می‌کنند. اینش اسباب دلخوری است! اما مرغ و ماکیان هم پرورش می‌دهند و خیرشان فقط همین است. تو پی مرغ می‌کردی؟ +شهریار کوچولو گفت: -نَه، پیِ دوست می‌گردم. اهلی کردن یعنی چی؟ +روباه گفت: -یک چیزی است که پاک فراموش شده. معنیش ایجاد علاقه کردن است. +-ایجاد علاقه کردن؟ +روباه گفت: -معلوم است. تو الان واسه من یک پسر بچه‌ای مثل صد هزار پسر بچه‌ی دیگر. نه من هیچ احتیاجی به تو دارم نه تو هیچ احتیاجی به من. من هم واسه تو یک روباهم مثل صد هزار روباه دیگر. اما اگر منو اهلی کردی هر دوتامان به هم احتیاج پیدا می‌کنیم. تو واسه من میان همه‌ی عالم موجود یگانه‌ای می‌شوی من واسه تو. +شهریار کوچولو گفت: -کم‌کم دارد دستگیرم می‌شود. یک گلی هست که گمانم مرا اهلی کرده باشد. +روباه گفت: -بعید نیست. رو این کره‌ی زمین هزار جور چیز می‌شود دید. +شهریار کوچولو گفت: -اوه نه! آن رو کره‌ی زمین نیست. +روباه که انگار حسابی حیرت کرده بود گفت: -رو یک سیاره‌ی دیگر است؟ +-آره. +شکارچی-تو آن سیاره شکارچی هم هست؟ +-نه. +-محشر است! مرغ و ماکیان چه‌طور؟ +-نه. +روباه آه‌کشان گفت: -همیشه‌ی خدا یک پای بساط لنگ است! +اما پی حرفش را گرفت و گفت: -زندگی یک‌نواختی دارم. من مرغ‌ها را شکار می‌کنم آدم‌ها مرا. همه‌ی مرغ‌ها عین همند همه‌ی آدم‌ها هم عین همند. این وضع یک خرده خلقم را تنگ می‌کند. اما اگر تو منو اهلی کنی انگار که زندگیم را چراغان کرده باشی. آن وقت صدای پایی را می‌شناسم که باهر صدای پای دیگر فرق می‌کند: صدای پای دیگران مرا وادار می‌کند تو هفت تا سوراخ قایم بشوم اما صدای پای تو مثل نغمه‌ای مرا از سوراخم می‌کشد بیرون. تازه، نگاه کن آن‌جا آن گندم‌زار را می‌بینی؟ برای من که نان بخور نیستم گندم چیز بی‌فایده‌ای است. پس گندم‌زار هم مرا به یاد چیزی نمی‌اندازد. اسباب تاسف است. اما تو موهات رنگ طلا است. پس وقتی اهلیم کردی محشر می‌شود! گندم که طلایی رنگ است مرا به یاد تو می‌اندازد و صدای باد را هم که تو گندم‌زار می‌پیچد دوست خواهم داشت... +خاموش شد و مدت درازی شهریار کوچولو را نگاه کرد. آن وقت گفت: -اگر دلت می‌خواهد منو اهلی کن! +شهریار کوچولو جواب داد: -دلم که خیلی می‌خواهد، اما وقتِ چندانی ندارم. باید بروم دوستانی پیدا کنم و از کلی چیزها سر در آرم. +روباه گفت: -آدم فقط از چیزهایی که اهلی کند می‌تواند سر در آرد. انسان‌ها دیگر برای سر در آوردن از چیزها وقت ندارند. همه چیز را همین جور حاضر آماده از دکان‌ها می‌خرند. اما چون دکانی نیست که دوست معامله کند آدم‌ها مانده‌اند بی‌دوست... تو اگر دوست می‌خواهی خب منو اهلی کن! +شهریار کوچولو پرسید: -راهش چیست؟ +روباه جواب داد: -باید خیلی خیلی حوصله کنی. اولش یک خرده دورتر از من می‌گیری این جوری میان علف‌ها می‌نشینی. من زیر چشمی نگاهت می‌کنم و تو لام‌تاکام هیچی نمی‌گویی، چون تقصیر همه‌ی سؤِتفاهم‌ها زیر سر زبان است. عوضش می‌توانی هر روز یک خرده نزدیک‌تر بنشینی. + +روباه در حالِ انتظارفردای آن روز دوباره شهریار کوچولو آمد. +روباه گفت: -کاش سر همان ساعت دیروز آمده بودی. اگر مثلا سر ساعت چهار بعد از ظهر بیایی من از ساعت سه تو دلم قند آب می‌شود و هر چه ساعت جلوتر برود بیش‌تر احساس شادی و خوشبختی می‌کنم. ساعت چهار که شد دلم بنا می‌کند شور زدن و نگران شدن. آن وقت است که قدرِ خوشبختی را می‌فهمم! اما اگر تو وقت و بی وقت بیایی من از کجا بدانم چه ساعتی باید دلم را برای دیدارت آماده کنم؟... هر چیزی برای خودش قاعده‌ای دارد. +شهریار کوچولو گفت: -قاعده یعنی چه؟ +روباه گفت: -این هم از آن چیزهایی است که پاک از خاطرها رفته. این همان چیزی است که باعث می‌شود فلان روز با باقی روزها و فلان ساعت با باقی ساعت‌ها فرق کند. مثلا شکارچی‌های ما میان خودشان رسمی دارند و آن این است که پنج‌شنبه‌ها را با دخترهای ده می‌روند رقص. پس پنج‌شنبه‌ها بَرّه‌کشانِ من است: برای خودم گردش‌کنان می‌روم تا دم مُوِستان. حالا اگر شکارچی‌ها وقت و بی وقت می‌رقصیدند همه‌ی روزها شبیه هم می‌شد و منِ بیچاره دیگر فرصت و فراغتی نداشتم. + +به این ترتیب شهریار کوچولو روباه را اهلی کرد. +لحظه‌ی جدایی که نزدیک شد روباه گفت: -آخ! نمی‌توانم جلو اشکم را بگیرم. +شهریار کوچولو گفت: -تقصیر خودت است. من که بدت را نمی‌خواستم، خودت خواستی اهلیت کنم. +روباه گفت: -همین طور است. +شهریار کوچولو گفت: -آخر اشکت دارد سرازیر می‌شود! +روباه گفت: -همین طور است. +-پس این ماجرا فایده‌ای به حال تو نداشته. +روباه گفت: -چرا، واسه خاطرِ رنگ گندم. +بعد گفت: -برو یک بار دیگر گل‌ها را ببین تا بفهمی که گلِ خودت تو عالم تک است. برگشتنا با هم وداع می‌کنیم و من به عنوان هدیه رازی را به‌ات می‌گویم. +شهریار کوچولو بار دیگر به تماشای گل‌ها رفت و به آن‌ها گفت: -شما سرِ سوزنی به گل من نمی‌مانید و هنوز هیچی نیستید. نه کسی شما را اهلی کرده نه شما کسی را. درست همان جوری هستید که روباه من بود: روباهی بود مثل صدهزار روباه دیگر. او را دوست خودم کردم و حالا تو همه‌ی عالم تک است. +گل‌ها حسابی از رو رفتند. +شهریار کوچولو دوباره درآمد که: -خوشگلید اما خالی هستید. برای‌تان نمی‌شود مُرد. گفت‌وگو ندارد که گلِ مرا هم فلان ره‌گذر می‌بیند مثل شما. اما او به تنهایی از همه‌ی شما سر است چون فقط اوست که آبش داده‌ام، چون فقط اوست که زیر حبابش گذاشته‌ام، چون فقط اوست که با تجیر برایش حفاظ درست کرده‌ام، چون فقط اوست که حشراتش را کشته‌ام (جز دو سه‌تایی که می‌بایست شب‌پره بشوند)، چون فقط اوست که پای گِلِه‌گزاری‌ها یا خودنمایی‌ها و حتا گاهی پای بُغ کردن و هیچی نگفتن‌هاش نشسته‌ام، چون او گلِ من است. +و برگشت پیش روباه. +گفت: -خدانگه‌دار! +روباه گفت: -خدانگه‌دار!... و اما رازی که گفتم خیلی ساده است: +جز با دل هیچی را چنان که باید نمی‌شود دید. نهاد و گوهر را چشمِ سَر نمی‌بیند. +شهریار کوچولو برای آن که یادش بماند تکرار کرد: -نهاد و گوهر را چشمِ سَر نمی‌بیند. +-ارزش گل تو به قدرِ عمری است که به پاش صرف کرده‌ای. +شهریار کوچولو برای آن که یادش بماند تکرار کرد: -به قدر عمری است که به پاش صرف کرده‌ام. +روباه گفت: -انسان‌ها این حقیقت را فراموش کرده‌اند اما تو نباید فراموشش کنی. تو تا زنده‌ای نسبت به چیزی که اهلی کرده‌ای مسئولی. تو مسئول گُلِتی... + +شهریار کوچولو برای آن که یادش بماند تکرار کرد: -من مسئول گُلمَم. + +۲۲ +شهریار کوچولو گفت: -سلام. +سوزن‌بان گفت: -سلام. +شهریار کوچولو گفت: -تو چه کار می‌کنی این‌جا؟ +سوزن‌بان گفت: -مسافرها را به دسته‌های هزارتایی تقسیم می‌کنم و قطارهایی را که می‌بَرَدشان گاهی به سمت راست می‌فرستم گاهی به سمت چپ. و همان دم سریع‌السیری با چراغ‌های روشن و غرّشی رعدوار اتاقک سوزن‌بانی را به لرزه انداخت. +-عجب عجله‌ای دارند! پیِ چی می‌روند؟ +سوزن‌بان گفت: -از خودِ آتش‌کارِ لکوموتیف هم بپرسی نمی‌داند! +سریع‌السیر دیگری با چراغ‌های روشن غرّید و در جهت مخالف گذشت . +شهریار کوچولو پرسید: -برگشتند که؟ +سوزن‌بان گفت: -این‌ها اولی‌ها نیستند. آن‌ها رفتند این‌ها برمی‌گردند. +-جایی را که بودند خوش نداشتند؟ +سوزن‌بان گفت: -آدمی‌زاد هیچ وقت جایی را که هست خوش ندارد. +و رعدِ سریع‌السیرِ نورانیِ ثالثی غرّید. +شهریار کوچولو پرسید: -این‌ها دارند مسافرهای اولی را دنبال می‌کنند؟ +سوزن‌بان گفت: -این‌ها هیچ چیزی را دنبال نمی‌کنند. آن تو یا خواب‌شان می‌بَرَد یا دهن‌دره می‌کنند. فقط بچه‌هاند که دماغ‌شان را فشار می‌دهند به شیشه‌ها. +شهریار کوچولو گفت: -فقط بچه‌هاند که می‌دانند پیِ چی می‌گردند. بچه‌هاند که کُلّی وقت صرف یک عروسک پارچه‌ای می‌کنند و عروسک برای‌شان آن قدر اهمیت به هم می‌رساند که اگر یکی آن را ازشان کِش برود می‌زنند زیر گریه... +سوزن‌بان گفت: -بخت، یارِ بچه‌هاست. + +۲۳ +شهریار کوچولو گفت: -سلام! +پیله‌ور گفت: -سلام. +این بابا فروشنده‌ی حَب‌های ضد تشنگی بود. خریدار هفته‌ای یک حب می‌انداخت بالا و دیگر تشنگی بی تشنگی. +شهریار کوچولو پرسید: -این‌ها را می‌فروشی که چی؟ +پیله‌ور گفت: -باعث صرفه‌جویی کُلّی وقت است. کارشناس‌های خبره نشسته‌اند دقیقا حساب کرده‌اند که با خوردن این حب‌ها هفته‌ای پنجاه و سه دقیقه وقت صرفه‌جویی می‌شود. +-خب، آن وقت آن پنجاه و سه دقیقه را چه کار می‌کنند؟ +ـ هر چی دل‌شان خواست... + +چشمه +شهریار کوچولو تو دلش گفت: «من اگر پنجاه و سه دقیقه وقتِ زیادی داشته باشم خوش‌خوشک به طرفِ یک چشمه می‌روم...» + +۲۴ +هشتمین روزِ خرابی هواپیمام تو کویر بود که، در حال نوشیدنِ آخرین چک‌ّه‌ی ذخیره‌ی آبم به قضیه‌ی پیله‌وره گوش داده بودم. به شهریار کوچولو گفتم: +-خاطرات تو راستی راستی زیباند اما من هنوز از پسِ تعمیر هواپیما برنیامده‌ام، یک چکه آب هم ندارم. و راستی که من هم اگر می‌توانستم خوش‌خوشک به طرف چشمه‌ای بروم سعادتی احساس می‌کردم که نگو! +درآمد که: -دوستم روباه... +گفتم: -آقا کوچولو، دورِ روباه را قلم بگیر! +-واسه چی؟ +-واسه این که تشنگی کارمان را می سازد. واسه این! +از استدلال من چیزی حالیش نشد و در جوابم گفت: +-حتا اگر آدم دَمِ مرگ باشد هم داشتن یک دوست عالی است. من که از داشتن یک دوستِ روباه خیلی خوشحالم... +به خودم گفتم نمی‌تواند میزان خطر را تخمین بزند: آخر او هیچ وقت نه تشنه‌اش می‌شود نه گشنه‌اش. یه ذره آفتاب بسش است... +اما او به من نگاه کرد و در جواب فکرم گفت: -من هم تشنه‌م است... بگردیم یک چاه پیدا کنیم... +از سرِ خستگی حرکتی کردم: -این جوری تو کویرِ برهوت رو هوا پیِ چاه گشتن احمقانه است. +و با وجود این به راه افتادیم. + +پس از ساعت‌ها که در سکوت راه رفتیم شب شد و ستاره‌ها یکی یکی درآمدند. من که از زور تشنگی تب کرده بودم انگار آن‌ها را خواب می‌دیدم. حرف‌های شهریار کوچولو تو ذهنم می‌رقصید. +ازش پرسیدم: -پس تو هم تشنه‌ات هست، ها؟ +اما او به سوآلِ من جواب نداد فقط در نهایت سادگی گفت: -آب ممکن است برای دلِ من هم خوب باشد... +از حرفش چیزی دستگیرم نشد اما ساکت ماندم. می‌دانستم از او نباید حرف کشید. +خسته شده بود. گرفت نشست. من هم کنارش نشستم. پس از مدتی سکوت گفت: +-قشنگیِ ستاره‌ها واسه خاطرِ گلی است که ما نمی‌بینیمش... +گفتم: -همین طور است +و بدون حرف در مهتاب غرق تماشای چین و شکن‌های شن شدم. +باز گفت: -کویر زیباست. + +و حق با او بود. من همیشه عاشق کویر بوده‌ام. آدم بالای توده‌ای شن لغزان می‌نشیند، هیچی نمی‌بیند و هیچی نمی‌شنود اما با وجود این چیزی توی سکوت برق‌برق می‌زند. +شهریار کوچولو گفت: -چیزی که کویر را زیبا می‌کند این است که یک جایی یک چاه قایم کرده... +از این‌که ناگهان به راز آن درخشش اسرارآمیزِ شن پی بردم حیرت‌زده شدم. بچگی‌هام تو خانه‌ی کهنه‌سازی می‌نشستیم که معروف بود تو آن گنجی چال کرده‌اند. البته نگفته پیداست که هیچ وقت کسی آن را پیدا نکرد و شاید حتا اصلا کسی دنبالش نگشت اما فکرش همه‌ی اهل خانه را تردماغ می‌کرد: «خانه‌ی ما تهِ دلش رازی پنهان کرده بود...» +گفتم: -آره. چه خانه باشد چه ستاره، چه کویر، چیزی که اسباب زیبایی‌اش می‌شود نامریی است! +گفت: -خوشحالم که با روباه من توافق داری. + +چون خوابش برده بود بغلش کردم و راه افتادم. دست و دلم می‌لرزید.انگار چیز شکستنیِ بسیار گران‌بهایی را روی دست می‌بردم. حتا به نظرم می‌آمد که تو تمام عالم چیزی شکستنی‌تر از آن هم به نظر نمی‌رسد. تو روشنی مهتاب به آن پیشانی رنگ‌پریده و آن چشم‌های بسته و آن طُرّه‌های مو که باد می‌جنباند نگاه کردم و تو دلم گفتم: «آن چه می‌بینم صورت ظاهری بیش‌تر نیست. مهم‌ترش را با چشم نمی‌شود دید...» +باز، چون دهان نیمه‌بازش طرح کم‌رنگِ نیمه‌لبخندی را داشت به خود گفتم: «چیزی که تو شهریار کوچولوی خوابیده مرا به این شدت متاثر می‌کند وفاداری اوست به یک گل: او تصویرِ گل سرخی است که مثل شعله‌ی چراغی حتا در خوابِ ناز هم که هست تو وجودش می‌درخشد...» و آن وقت او را باز هم شکننده‌تر دیدم. حس کردم باید خیلی مواظبش باشم: به شعله‌ی چراغی می‌مانست که یک وزش باد هم می‌توانست خاموشش کند. +و همان طور در حال راه رفتن بود که دمدمه‌ی سحر چاه را پیداکردم. + +۲۵ +شهریار کوچولو درآمد که: -آدم‌ها!... می‌چپند تو قطارهای تندرو اما نمی‌دانند دنبال چی می‌گردند. این است که بنامی‌کنند دور خودشان چرخک‌زدن. +و بعد گفت: -این هم کار نشد... +چاهی که به‌اش رسیده‌بودیم اصلا به چاه‌های کویری نمی‌مانست. چاه کویری یک چاله‌ی ساده است وسط شن‌ها. این یکی به چاه‌های واحه‌ای می‌مانست اما آن دوروبر واحه‌ای نبود و من فکر کردم دارم خواب می‌بینم. +گفتم: -عجیب است! قرقره و سطل و تناب، همه‌چیز روبه‌راه است. +خندید تناب را گرفت و قرقره را به کار انداخت + +شهریار کوچولو در حالِ کشیدنِ آب از چاه +و قرقره مثل بادنمای کهنه‌ای که تا مدت‌ها پس از خوابیدنِ باد می‌نالد به ناله‌درآمد. +گفت: -می‌شنوی؟ ما داریم این چاه را از خواب بیدار می‌کنیم و او دارد برای‌مان آواز می‌خواند... +دلم نمی‌خواست او تلاش و تقلا کند. بش گفتم: -بدهش به من. برای تو زیادی سنگین است. +سطل را آرام تا طوقه‌ی چاه آوردم بالا و آن‌جا کاملا در تعادل نگهش داشتم. از حاصل کار شاد بودم. خسته و شاد. آواز قرقره را همان‌طور تو گوشم داشتم و تو آب که هنوز می‌لرزید لرزش خورشید را می‌دیدم. +گفت: -بده من، که تشنه‌ی این آبم. +ومن تازه توانستم بفهمم پی چه چیز می‌گشته! + +سطل را تا لب‌هایش بالا بردم. با چشم‌های بسته نوشید. آبی بود به شیرینیِ عیدی. این آب به کُلّی چیزی بود سوایِ هرگونه خوردنی. زاییده‌ی راه رفتنِ زیر ستاره‌ها و سرود قرقره و تقلای بازوهای من بود. مثل یک چشم روشنی برای دل خوب بود. پسر بچه که بودم هم، چراغ درخت عید و موسیقیِ نماز نیمه‌شب عید کریسمس و لطف لب‌خنده‌ها عیدیی را که بم می‌دادند درست به همین شکل آن همه جلا و جلوه می‌بخشید. +گفت: -مردم سیاره‌ی تو ور می‌دارند پنج هزار تا گل را تو یک گلستان می‌کارند، و آن یک دانه‌ای را که پِیَش می‌گردند آن وسط پیدا نمی‌کنند... +گفتم: -پیدایش نمی‌کنند. +-با وجود این، چیزی که پیَش می‌گردند ممکن است فقط تو یک گل یا تو یک جرعه آب پیدا بشود... +جواب دادم: -گفت‌وگو ندارد. +باز گفت: -گیرم چشمِ سَر کور است، باید با چشم دل پی‌اش گشت. +من هم سیراب شده بودم. راحت نفس می‌کشیدم. وقتی آفتاب درمی‌آید شن به رنگ عسل است. من هم از این رنگ عسلی لذت می‌بردم. چرا می‌بایست در زحمت باشم... +شهریار کوچولو که باز گرفته بود کنار من نشسته بود با لطف بم گفت: -هِی! قولت قول باشد ها! +-کدام قول؟ +-یادت است؟ یک پوزه‌بند برای بَرّه‌ام... آخر من مسئول گلمَم! +طرح‌های اولیه‌ام را از جیب درآوردم. نگاه‌شان کرد و خندان‌خندان گفت: -بائوباب‌هات یک خرده شبیه کلم شده. +ای وای! مرا بگو که آن‌قدر به بائوباب‌هام می‌نازیدم. +-روباهت... گوش‌هاش بیش‌تر به شاخ می‌ماند... زیادی درازند! +و باز زد زیر خنده. +-آقا کوچولو داری بی‌انصافی می‌کنی. من جز بوآهای بسته و بوآهای باز چیزی بلد نبودم بکشم که. +گفت: -خب، مهم نیست. عوضش بچه‌ها سرشان تو حساب است. +با مداد یک پوزه‌بند کشیدم دادم دستش و با دلِ فشرده گفتم: +-تو خیالاتی به سر داری که من ازشان بی‌خبرم... +اما جواب مرا نداد. بم گفت: -می‌دانی؟ فردا سالِ به زمین آمدنِ من است. +بعد پس از لحظه‌ای سکوت دوباره گفت: -همین نزدیکی‌ها پایین آمدم. +و سرخ شد. + +و من از نو بی این که بدانم چرا غم عجیبی احساس کردم. با وجود این سوآلی به ذهنم رسید: -پس هشت روز پیش، آن روز صبح که تو تک و تنها هزار میل دورتر از هر آبادی وسطِ کویر به من برخوردی اتفاقی نبود: داشتی برمی‌گشتی به همان جایی که پایین‌آمدی... +دوباره سرخ شد +و من با دودلی به دنبال حرفم گفتم: +-شاید به مناسبت همین سال‌گرد؟... +باز سرخ شد. او هیچ وقت به سوآل‌هایی که ازش می‌شد جواب نمی‌داد اما وقتی کسی سرخ می‌شود معنیش این است که «بله»، مگر نه؟ +به‌اش گفتم: -آخر، من ترسم برداشته... +اما او حرفم را برید: +-دیگر تو باید بروی به کارت برسی. باید بروی سراغ موتورت. من همین‌جا منتظرت می‌مانم. فردا عصر برگرد... + +منتها من خاطر جمع نبودم. به یاد روباه افتادم: اگر آدم گذاشت اهلیش کنند بفهمی‌نفهمی خودش را به این خطر انداخته که کارش به گریه‌کردن بکشد. + +۲۶ +کنار چاه دیوارِ سنگی مخروبه‌ای بود. فردا عصر که از سرِ کار برگشتم از دور دیدم که آن بالا نشسته پاها را آویزان کرده، + +شهریار کوچولو نشسته بر دیوارِ سنگی و مار در پایینِ آن +و شنیدم که می‌گوید: +-پس یادت نمی‌آید؟ درست این نقطه نبود ها! +لابد صدای دیگری به‌اش جوابی داد، چون شهریار کوچولو در رَدِّ حرفش گفت: +-چرا چرا! روزش که درست همین امروز است گیرم محلش این جا نیست... +راهم را به طرف دیوار ادامه دادم. هنوز نه کسی به چشم خورده بود نه صدای کسی را شنیده بودم اما شهریار کوچولو باز در جواب درآمد که: +-... آره، معلوم است. خودت می‌توانی ببینی رَدِّ پاهایم روی شن از کجا شروع می‌شود. +همان جا منتظرم باش، تاریک که شد می‌آیم. +بیست متری دیوار بودم و هنوز چیزی نمی‌دیدم. پس از مختصر مکثی دوباره گفت: +-زهرت خوب هست؟ مطمئنی درد و زجرم را کِش نمی‌دهد؟ +با دل فشرده از راه ماندم اما هنوز از موضوع سر در نیاورده بودم. +گفت: -خب، حالا دیگر برو. دِ برو. می‌خواهم بیایم پایین! + +آن وقت من نگاهم را به پایین به پای دیوار انداختم و از جا جستم! یکی از آن مارهای زردی که تو سی ثانیه کَلَکِ آدم را می‌کنند، به طرف شهریار کوچولو قد راست کرده بود. من همان طور که به دنبال تپانچه دست به جیبم می‌بردم پا گذاشتم به دو، اما ماره از سر و صدای من مثل فواره‌ای که بنشیند آرام روی شن جاری شد و بی آن که چندان عجله‌ای از خودش نشان دهد باصدای خفیف فلزی لای سنگ‌ها خزید. +من درست به موقع به دیوار رسیدم و طفلکی شهریار کوچولو را که رنگش مثل برف پریده بود تو هوا بغل کردم. +-این دیگر چه حکایتی است! حالا دیگر با مارها حرف می‌زنی؟ +شال زردش را که مدام به گردن داشت باز کردم به شقیقه‌هایش آب زدم و جرعه‌ای به‌اش نوشاندم. اما حالا دیگر اصلا جرات نمی کردم ازش چیزی بپرسم. با وقار به من نگاه کرد و دستش را دور گردنم انداخت. حس کردم قلبش مثل قلب پرنده‌ای می‌زند که تیر خورده‌است و دارد می‌میرد. +گفت: -از این که کم و کسرِ لوازم ماشینت را پیدا کردی خوش‌حالم. حالا می‌توانی برگردی خانه‌ات... +-تو از کجا فهمیدی؟ +درست همان دم لب‌واکرده‌بودم بش خبر بدهم که علی‌رغم همه‌ی نومیدی‌ها تو کارم موفق شده‌ام! +به سوآل‌های من هیچ جوابی نداد اما گفت: -آخر من هم امروز بر می‌گردم خانه‌ام... +و بعد غم‌زده درآمد که: -گیرم راه من خیلی دورتر است... خیلی سخت‌تر است... + +حس می‌کردم اتفاق فوق‌العاده‌ای دارد می‌افتد. گرفتمش تو بغلم. عین یک بچه‌ی کوچولو. با وجود این به نظرم می‌آمد که او دارد به گردابی فرو می‌رود و برای نگه داشتنش از من کاری ساخته نیست... نگاه متینش به دوردست‌های دور راه کشیده بود. +گفت: بَرِّه‌ات را دارم. جعبه‌هه را هم واسه بره‌هه دارم. پوزه‌بنده را هم دارم. +و با دلِ گرفته لبخندی زد. +مدت درازی صبر کردم. حس کردم کم‌کمَک تنش دوباره دارد گرم می‌شود. +-عزیز کوچولوی من، وحشت کردی... +-امشب وحشت خیلی بیش‌تری چشم به‌راهم است. + +دوباره از احساسِ واقعه‌ای جبران ناپذیر یخ زدم. این فکر که دیگر هیچ وقت غش‌غش خنده‌ی او را نخواهم شنید برایم سخت تحمل‌ناپذیر بود. خنده‌ی او برای من به چشمه‌ای در دلِ کویر می‌مانست. +-کوچولوئَکِ من، دلم می‌خواهد باز هم غش‌غشِ خنده‌ات را بشنوم. +اما به‌ام گفت: -امشب درست می‌شود یک سال و اخترَکَم درست بالای همان نقطه‌ای می‌رسد که پارسال به زمین آمدم. +-کوچولوئک، این قضیه‌ی مار و میعاد و ستاره یک خواب آشفته بیش‌تر نیست. مگر نه؟ +به سوال من جوابی نداد اما گفت: -چیزی که مهم است با چشمِ سَر دیده نمی‌شود. +-مسلم است. +-در مورد گل هم همین‌طور است: اگر گلی را دوست داشته باشی که تو یک ستاره‌ی دیگر است، شب تماشای آسمان چه لطفی پیدا می‌کند: همه‌ی ستاره‌ها غرق گل می‌شوند! +-مسلم است... +-در مورد آب هم همین‌طور است. آبی که تو به من دادی به خاطر قرقره و ریسمان درست به یک موسیقی می‌مانست... یادت که هست... چه خوب بود. +-مسلم است... +-شب‌به‌شب ستاره‌ها را نگاه می‌کنی. اخترک من کوچولوتر از آن است که بتوانم جایش را نشانت بدهم. اما چه بهتر! آن هم برای تو می‌شود یکی از ستاره‌ها؛ و آن وقت تو دوست داری همه‌ی ستاره‌ها را تماشا کنی... همه‌شان می‌شوند دوست‌های تو... راستی می‌خواهم هدیه‌ای بت بدهم... +و غش غش خندید. +-آخ، کوچولوئک، کوچولوئک! من عاشقِ شنیدنِ این خنده‌ام! +-هدیه‌ی من هم درست همین است... درست مثل مورد آب. +-چی می‌خواهی بگویی؟ +-همه‌ی مردم ستاره دارند اما همه‌ی ستاره‌ها یک‌جور نیست: واسه آن‌هایی که به سفر می‌روند حکم راهنما را دارند واسه بعضی دیگر فقط یک مشت روشناییِ سوسوزن‌اند. برای بعضی که اهل دانشند هر ستاره یک معما است واسه آن بابای تاجر طلا بود. اما این ستاره‌ها همه‌شان زبان به کام کشیده و خاموشند. فقط تو یکی ستاره‌هایی خواهی داشت که تنابنده‌ای مِثلش را ندارد. +-چی می‌خواهی بگویی؟ +-نه این که من تو یکی از ستاره‌هام؟ نه این که من تو یکی از آن‌ها می‌خندم؟... خب، پس هر شب که به آسمان نگاه می‌کنی برایت مثل این خواهد بود که همه‌ی ستاره‌ها می‌خندند. پس تو ستاره‌هایی خواهی داشت که بلدند بخندند! +و باز خندید. +-و خاطرت که تسلا پیدا کرد (خب بالاخره آدمی‌زاد یک جوری تسلا پیدا می‌کند دیگر) از آشنایی با من خوش‌حال می‌شوی. دوست همیشگی من باقی می‌مانی و دلت می‌خواهد با من بخندی و پاره‌ای وقت‌هام واسه تفریح پنجره‌ی اتاقت را وا می‌کنی... دوستانت از این‌که می‌بینند تو به آسمان نگاه می‌کنی و می‌خندی حسابی تعجب می‌کنند آن وقت تو به‌شان می‌گویی: «آره، ستاره‌ها همیشه مرا خنده می‌اندازند!» و آن‌وقت آن‌ها یقین‌شان می‌شود که تو پاک عقلت را از دست داده‌ای. جان! می‌بینی چه کَلَکی به‌ات زده‌ام... +و باز زد زیر خنده. +-به آن می‌ماند که عوضِ ستاره یک مشت زنگوله بت داده باشم که بلدند بخندند... +دوباره خندید و بعد حالتی جدی به خودش گرفت: +-نه، من تنهات نمی‌گذارم. + +شهریار کوچولو تنها + +-ظاهر آدمی را پیدا می‌کنم که دارد درد می‌کشد... یک خرده هم مثل آدمی می‌شوم که دارد جان می‌کند. رو هم رفته این جوری‌ها است. نیا که این را نبینی. چه زحمتی است بی‌خود؟ +-تنهات نمی‌گذارم. +اندوه‌زده بود. +-این را بیش‌تر از بابت ماره می‌گویم که، نکند یک‌هو تو را هم بگزد. مارها خیلی خبیثند. حتا واسه خنده هم ممکن است آدم را نیش بزنند. +-تنهات نمی‌گذارم. +منتها یک چیز باعث خاطر جمعیش شد: +-گر چه، بار دوم که بخواهند بگزند دیگر زهر ندارند. +شب متوجه راه افتادنش نشدم. بی سر و صدا گریخت. +وقتی خودم را به‌اش رساندم با قیافه‌ی مصمم و قدم‌های محکم پیش می‌رفت. همین قدر گفت: -اِ! این‌جایی؟ +و دستم را گرفت. +اما باز بی‌قرار شد وگفت: -اشتباه کردی آمدی. رنج می‌بری. گرچه حقیقت این نیست، اما ظاهرِ یک مرده را پیدا می‌کنم. +من ساکت ماندم. +-خودت درک می‌کنی. راه خیلی دور است. نمی‌توانم این جسم را با خودم ببرم. خیلی سنگین است. +من ساکت ماندم. +-گیرم عینِ پوستِ کهنه‌ای می‌شود که دورش انداخته باشند؛ پوست کهنه که غصه ندارد، ها؟ +من ساکت ماندم. +کمی دل‌سرد شد اما باز هم سعی کرد: +-خیلی با مزه می‌شود، نه؟ من هم به ستاره‌ها نگاه می‌کنم. هم‌شان به صورت چاه‌هایی در می‌آیند با قرقره‌های زنگ زده. همه‌ی ستاره‌ها بم آب می‌دهند بخورم... +من ساکت ماندم. +-خیلی با مزه می‌شود. نه؟ تو صاحب هزار کرور زنگوله می‌شوی من صاحب هزار کرور فواره... +او هم ساکت شد، چرا که داشت گریه می‌کرد... +-خب، همین جاست. بگذار چند قدم خودم تنهایی بروم. +و گرفت نشست، چرا که می‌ترسید. + +شهریار کوچولو نشسته + +می‌دانی؟... گلم را می‌گویم... آخر من مسئولشم. تازه چه قدر هم لطیف است و چه قدر هم ساده و بی‌شیله‌پیله. برای آن که جلو همه‌ی عالم از خودش دفاع کند همه‌اش چی دارد مگر؟ چهارتا خار پِرپِرَک! + +من هم گرفتم نشستم. دیگر نمی‌توانستم سر پا بند بشوم. +گفت: -همین... همه‌اش همین و بس... +باز هم کمی دودلی نشان داد اما بالاخره پا شد و قدمی به جلو رفت. من قادر به حرکت نبودم. + +کنار قوزکِ پایش جرقه‌ی زردی جست و... فقط همین! یک دم بی‌حرکت ماند. فریادی نزد. مثل درختی که بیفتد آرام‌آرام به زمین افتاد که به وجود شن از آن هم صدایی بلند نشد. + +شهریار کوچولو در حالی که آرام‌آرام به زمین می‌افتد +۲۷ +شش سال گذشته است و من هنوز بابت این قضیه جایی لب‌ترنکرده‌ام. دوستانم از این که مرا دوباره زنده می‌دیدند سخت شاد شدند. من غم‌زده بودم اما به آن‌ها می‌گفتم اثر خستگی است. +حالا کمی تسلای خاطر پیدا کرده‌ام. یعنی نه کاملا... اما این را خوب می‌دانم که او به اخترکش برگشته. چون آفتاب که زد پیکرش را پیدا نکردم. پیکری هم نبود که چندان وزنی داشته باشد... و شب‌ها دوست دارم به ستاره‌ها گوش بدهم. عین هزار زنگوله‌اند. +اما موضوع خیلی مهمی که هست، من پاک یادم رفت به پوزه‌بندی که برای شهریار کوچولو کشیدم تسمه‌ی چرمی اضافه کنم و او ممکن نیست بتواند آن را به پوزه‌ی بَرّه ببندد. این است که از خودم می‌پرسم: «یعنی تو اخترکش چه اتفاقی افتاده؟ نکند بره‌هه گل را چریده باشد؟...» +گاه به خودم می‌گویم: «حتما نه، شهریار کوچولو هر شب گلش را زیر حباب شیشه‌ای می‌گذارد و هوای بره‌اش را هم دارد...» آن وقت است که خیالم راحت می‌شود و ستاره‌ها همه به شیرینی می‌خندند. +گاه به خودم می‌گویم: «همین کافی است که آدم یک بار حواسش نباشد... آمدیم و یک شب حباب یادش رفت یا بَرّه شب نصف‌شبی بی‌سروصدا از جعبه زد بیرون...» آن وقت است که زنگوله‌ها همه تبدیل به اشک می‌شوند!... + +یک راز خیلی خیلی بزرگ این جا هست: برای شما هم که او را دوست دارید، مثل من هیچ چیزِ عالم مهم‌تر از دانستن این نیست که تو فلان نقطه‌ای که نمی‌دانیم، فلان بره‌ای که نمی‌شماسیم گل سرخی را چریده یا نچریده... + +خب. آسمان را نگاه کنید و بپرسید: «بَرّه گل را چریده یا نچریده؟» و آن وقت با چشم‌های خودتان تفاوتش را ببینید... + +و محال است آدم بزرگ‌ها روح‌شان خبردار بشود که این موضوع چه قدر مهم است! + +بدونِ شهریار کوچولو +در نظر من این زیباترین و حزن‌انگیزترین منظره‌ی عالم است. این همان منظره‌ی دو صفحه پیش است گیرم آن را دوباره کشیده‌ام که به‌تر نشان‌تان بدهم: «ظهور شهریار کوچولو بر زمین در این جا بود؛ و بعد در همین جا هم بود که ناپدید شد». + +آن قدر به دقت این منظره را نگاه کنید که مطمئن بشوید اگر روزی تو آفریقا گذرتان به کویر صحرا افتاد حتما آن را خواهید شناخت. و اگر پاداد و گذارتان به آن جا افتاد به التماس ازتان می‌خواهم که عجله به خرج ندهید و درست زیر ستاره چند لحظه‌ای توقف کنید. آن وقت اگر بچه‌ای به طرف‌تان آمد، اگر خندید، اگر موهایش طلایی بود، اگر وقتی ازش سوالی کردید جوابی نداد، لابد حدس می‌زنید که کیست. در آن صورت لطف کنید و نگذارید من این جور افسرده خاطر بمانم: +بی درنگ بردارید به من بنویسید که او برگشته. + diff --git a/libs/harfbuzz/replace-enum-strings.cmake b/libs/harfbuzz/replace-enum-strings.cmake new file mode 100644 index 00000000..171667cc --- /dev/null +++ b/libs/harfbuzz/replace-enum-strings.cmake @@ -0,0 +1,21 @@ +# CMake script to replace items +# in sources generated by glib-mkenums + +FILE(READ ${ENUM_INPUT_SRC} enum_in) + +STRING(REPLACE + "_t_get_type" + "_get_type" + enum_out_tmp + "${enum_in}" + ) + +STRING(REPLACE + "_T (" + " (" + enum_out + "${enum_out_tmp}" + ) + +FILE(WRITE ${ENUM_OUTPUT_SRC} "${enum_out}") +FILE(REMOVE ${ENUM_INPUT_SRC}) \ No newline at end of file diff --git a/libs/harfbuzz/src/ArabicPUASimplified.txt b/libs/harfbuzz/src/ArabicPUASimplified.txt new file mode 100644 index 00000000..a74ef266 --- /dev/null +++ b/libs/harfbuzz/src/ArabicPUASimplified.txt @@ -0,0 +1,250 @@ +# +# Name: Legacy Simplified Arabic encoding +# +# Format: Three tab-separated columns +# Column #1 is the PUA code (in hex as 0xXXXX) +# Column #2 is the Unicode (in hex as 0xXXXX) +# Column #3 is the Unicode name (follows a comment sign, '#') +# +# The entries are in PUA order +# +0xF100 0x063B # ARABIC LETTER KEHEH WITH TWO DOTS ABOVE +0xF100 0x063C # ARABIC LETTER KEHEH WITH THREE DOTS BELOW +0xF100 0x063D # ARABIC LETTER FARSI YEH WITH INVERTED V +0xF100 0x063E # ARABIC LETTER FARSI YEH WITH TWO DOTS ABOVE +0xF100 0x063F # ARABIC LETTER FARSI YEH WITH THREE DOTS ABOVE +0xF100 0x0653 # ARABIC MADDAH ABOVE +0xF100 0x0654 # ARABIC HAMZA ABOVE +0xF100 0x0655 # ARABIC HAMZA BELOW +0xF100 0x0656 # ARABIC SUBSCRIPT ALEF +0xF100 0x0657 # ARABIC INVERTED DAMMA +0xF100 0x0658 # ARABIC MARK NOON GHUNNA +0xF100 0x0659 # ARABIC ZWARAKAY +0xF100 0x065A # ARABIC VOWEL SIGN SMALL V ABOVE +0xF100 0x065B # ARABIC VOWEL SIGN INVERTED SMALL V ABOVE +0xF100 0x065C # ARABIC VOWEL SIGN DOT BELOW +0xF100 0x065D # ARABIC REVERSED DAMMA +0xF100 0x065E # ARABIC FATHA WITH TWO DOTS +0xF10C 0x200C # ZERO WIDTH NON-JOINER +0xF10D 0x200D # ZERO WIDTH JOINER +0xF10E 0x200E # LEFT-TO-RIGHT MARK +0xF10F 0x200F # RIGHT-TO-LEFT MARK +0xF120 0x0020 # SPACE +0xF121 0x0021 # EXCLAMATION MARK +0xF122 0x0022 # QUOTATION MARK +0xF123 0x00AB # LEFT-POINTING DOUBLE ANGLE QUOTATION MARK +0xF124 0x00BB # RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK +0xF125 0x0025 # PERCENT SIGN +0xF126 0x00D7 # MULTIPLICATION SIGN +0xF127 0x00F7 # DIVISION SIGN +0xF128 0x0028 # LEFT PARENTHESIS +0xF129 0x0029 # RIGHT PARENTHESIS +0xF12A 0x002A # ASTERISK +0xF12B 0x002B # PLUS SIGN +0xF12C 0x060C # ARABIC COMMA +0xF12D 0x002D # HYPHEN-MINUS +0xF12E 0x002E # FULL STOP +0xF12F 0x002F # SOLIDUS +0xF130 0x0660 # ARABIC-INDIC DIGIT ZERO +0xF131 0x0661 # ARABIC-INDIC DIGIT ONE +0xF132 0x0662 # ARABIC-INDIC DIGIT TWO +0xF133 0x0663 # ARABIC-INDIC DIGIT THREE +0xF134 0x0664 # ARABIC-INDIC DIGIT FOUR +0xF135 0x0665 # ARABIC-INDIC DIGIT FIVE +0xF136 0x0666 # ARABIC-INDIC DIGIT SIX +0xF137 0x0667 # ARABIC-INDIC DIGIT SEVEN +0xF138 0x0668 # ARABIC-INDIC DIGIT EIGHT +0xF139 0x0669 # ARABIC-INDIC DIGIT NINE +0xF13A 0x003A # COLON +0xF13B 0x003B # SEMICOLON +0xF13B 0x061B # ARABIC SEMICOLON +0xF13C 0x2018 # LEFT SINGLE QUOTATION MARK +0xF13D 0x003D # EQUALS SIGN +0xF13E 0x2019 # RIGHT SINGLE QUOTATION MARK +0xF13F 0x003F # QUESTION MARK +0xF13F 0x061F # ARABIC QUESTION MARK +0xF141 0x0627 # ARABIC LETTER ALEF +0xF141 0xFE8D # ARABIC LETTER ALEF ISOLATED FORM +0xF142 0xFE8E # ARABIC LETTER ALEF FINAL FORM +0xF143 0x0623 # ARABIC LETTER ALEF WITH HAMZA ABOVE +0xF143 0xFE83 # ARABIC LETTER ALEF WITH HAMZA ABOVE ISOLATED FORM +0xF144 0xFE84 # ARABIC LETTER ALEF WITH HAMZA ABOVE FINAL FORM +0xF145 0x0622 # ARABIC LETTER ALEF WITH MADDA ABOVE +0xF145 0xFE81 # ARABIC LETTER ALEF WITH MADDA ABOVE ISOLATED FORM +0xF146 0xFE82 # ARABIC LETTER ALEF WITH MADDA ABOVE FINAL FORM +0xF147 0x0625 # ARABIC LETTER ALEF WITH HAMZA BELOW +0xF147 0xFE87 # ARABIC LETTER ALEF WITH HAMZA BELOW ISOLATED FORM +0xF148 0xFE88 # ARABIC LETTER ALEF WITH HAMZA BELOW FINAL FORM +0xF149 0xFE91 # ARABIC LETTER BEH INITIAL FORM +0xF149 0xFE92 # ARABIC LETTER BEH MEDIAL FORM +0xF14A 0x0628 # ARABIC LETTER BEH +0xF14A 0xFE8F # ARABIC LETTER BEH ISOLATED FORM +0xF14A 0xFE90 # ARABIC LETTER BEH FINAL FORM +0xF14B 0xFE97 # ARABIC LETTER TEH INITIAL FORM +0xF14B 0xFE98 # ARABIC LETTER TEH MEDIAL FORM +0xF14C 0x062A # ARABIC LETTER TEH +0xF14C 0xFE95 # ARABIC LETTER TEH ISOLATED FORM +0xF14C 0xFE96 # ARABIC LETTER TEH FINAL FORM +0xF14D 0xFE9B # ARABIC LETTER THEH INITIAL FORM +0xF14D 0xFE9C # ARABIC LETTER THEH MEDIAL FORM +0xF14E 0x062B # ARABIC LETTER THEH +0xF14E 0xFE99 # ARABIC LETTER THEH ISOLATED FORM +0xF14E 0xFE9A # ARABIC LETTER THEH FINAL FORM +0xF14F 0xFE9F # ARABIC LETTER JEEM INITIAL FORM +0xF14F 0xFEA0 # ARABIC LETTER JEEM MEDIAL FORM +0xF150 0xFE9E # ARABIC LETTER JEEM FINAL FORM +0xF151 0x062C # ARABIC LETTER JEEM +0xF151 0xFE9D # ARABIC LETTER JEEM ISOLATED FORM +0xF152 0xFEA3 # ARABIC LETTER HAH INITIAL FORM +0xF152 0xFEA4 # ARABIC LETTER HAH MEDIAL FORM +0xF153 0xFEA2 # ARABIC LETTER HAH FINAL FORM +0xF154 0x062D # ARABIC LETTER HAH +0xF154 0xFEA1 # ARABIC LETTER HAH ISOLATED FORM +0xF155 0xFEA7 # ARABIC LETTER KHAH INITIAL FORM +0xF155 0xFEA8 # ARABIC LETTER KHAH MEDIAL FORM +0xF156 0xFEA6 # ARABIC LETTER KHAH FINAL FORM +0xF157 0x062E # ARABIC LETTER KHAH +0xF157 0xFEA5 # ARABIC LETTER KHAH ISOLATED FORM +0xF158 0x062F # ARABIC LETTER DAL +0xF158 0xFEA9 # ARABIC LETTER DAL ISOLATED FORM +0xF158 0xFEAA # ARABIC LETTER DAL FINAL FORM +0xF159 0x0630 # ARABIC LETTER THAL +0xF159 0xFEAB # ARABIC LETTER THAL ISOLATED FORM +0xF159 0xFEAC # ARABIC LETTER THAL FINAL FORM +0xF15A 0x0631 # ARABIC LETTER REH +0xF15A 0xFEAD # ARABIC LETTER REH ISOLATED FORM +0xF15A 0xFEAE # ARABIC LETTER REH FINAL FORM +0xF15B 0x005B # LEFT SQUARE BRACKET +0xF15C 0x005C # REVERSE SOLIDUS +0xF15D 0x005D # RIGHT SQUARE BRACKET +0xF15E 0x002C # COMMA +0xF15E 0x066B # ARABIC DECIMAL SEPARATOR +0xF15E 0x066C # ARABIC THOUSANDS SEPARATOR +0xF15F 0x0640 # ARABIC TATWEEL +0xF160 0x0632 # ARABIC LETTER ZAIN +0xF160 0xFEAF # ARABIC LETTER ZAIN ISOLATED FORM +0xF160 0xFEB0 # ARABIC LETTER ZAIN FINAL FORM +0xF161 0xFEB3 # ARABIC LETTER SEEN INITIAL FORM +0xF161 0xFEB4 # ARABIC LETTER SEEN MEDIAL FORM +0xF162 0x0633 # ARABIC LETTER SEEN +0xF162 0xFEB1 # ARABIC LETTER SEEN ISOLATED FORM +0xF162 0xFEB2 # ARABIC LETTER SEEN FINAL FORM +0xF163 0xFEB7 # ARABIC LETTER SHEEN INITIAL FORM +0xF163 0xFEB8 # ARABIC LETTER SHEEN MEDIAL FORM +0xF164 0x0634 # ARABIC LETTER SHEEN +0xF164 0xFEB5 # ARABIC LETTER SHEEN ISOLATED FORM +0xF164 0xFEB6 # ARABIC LETTER SHEEN FINAL FORM +0xF165 0xFEBB # ARABIC LETTER SAD INITIAL FORM +0xF165 0xFEBC # ARABIC LETTER SAD MEDIAL FORM +0xF166 0x0635 # ARABIC LETTER SAD +0xF166 0xFEB9 # ARABIC LETTER SAD ISOLATED FORM +0xF166 0xFEBA # ARABIC LETTER SAD FINAL FORM +0xF167 0xFEBF # ARABIC LETTER DAD INITIAL FORM +0xF167 0xFEC0 # ARABIC LETTER DAD MEDIAL FORM +0xF168 0x0636 # ARABIC LETTER DAD +0xF168 0xFEBD # ARABIC LETTER DAD ISOLATED FORM +0xF168 0xFEBE # ARABIC LETTER DAD FINAL FORM +0xF169 0x0637 # ARABIC LETTER TAH +0xF169 0xFEC1 # ARABIC LETTER TAH ISOLATED FORM +0xF169 0xFEC2 # ARABIC LETTER TAH FINAL FORM +0xF169 0xFEC3 # ARABIC LETTER TAH INITIAL FORM +0xF169 0xFEC4 # ARABIC LETTER TAH MEDIAL FORM +0xF16A 0x0638 # ARABIC LETTER ZAH +0xF16A 0xFEC5 # ARABIC LETTER ZAH ISOLATED FORM +0xF16A 0xFEC6 # ARABIC LETTER ZAH FINAL FORM +0xF16A 0xFEC7 # ARABIC LETTER ZAH INITIAL FORM +0xF16A 0xFEC8 # ARABIC LETTER ZAH MEDIAL FORM +0xF16B 0xFECB # ARABIC LETTER AIN INITIAL FORM +0xF16C 0xFECC # ARABIC LETTER AIN MEDIAL FORM +0xF16D 0xFECA # ARABIC LETTER AIN FINAL FORM +0xF16E 0x0639 # ARABIC LETTER AIN +0xF16E 0xFEC9 # ARABIC LETTER AIN ISOLATED FORM +0xF16F 0xFECF # ARABIC LETTER GHAIN INITIAL FORM +0xF170 0xFED0 # ARABIC LETTER GHAIN MEDIAL FORM +0xF171 0xFECE # ARABIC LETTER GHAIN FINAL FORM +0xF172 0x063A # ARABIC LETTER GHAIN +0xF172 0xFECD # ARABIC LETTER GHAIN ISOLATED FORM +0xF173 0xFED3 # ARABIC LETTER FEH INITIAL FORM +0xF174 0xFED4 # ARABIC LETTER FEH MEDIAL FORM +0xF175 0x0641 # ARABIC LETTER FEH +0xF175 0xFED1 # ARABIC LETTER FEH ISOLATED FORM +0xF175 0xFED2 # ARABIC LETTER FEH FINAL FORM +0xF176 0xFED7 # ARABIC LETTER QAF INITIAL FORM +0xF177 0xFED8 # ARABIC LETTER QAF MEDIAL FORM +0xF178 0x0642 # ARABIC LETTER QAF +0xF178 0xFED5 # ARABIC LETTER QAF ISOLATED FORM +0xF178 0xFED6 # ARABIC LETTER QAF FINAL FORM +0xF179 0xFEDB # ARABIC LETTER KAF INITIAL FORM +0xF179 0xFEDC # ARABIC LETTER KAF MEDIAL FORM +0xF17A 0x0643 # ARABIC LETTER KAF +0xF17A 0xFED9 # ARABIC LETTER KAF ISOLATED FORM +0xF17A 0xFEDA # ARABIC LETTER KAF FINAL FORM +0xF17B 0xFEDF # ARABIC LETTER LAM INITIAL FORM +0xF17B 0xFEE0 # ARABIC LETTER LAM MEDIAL FORM +0xF17C 0x0644 # ARABIC LETTER LAM +0xF17C 0xFEDD # ARABIC LETTER LAM ISOLATED FORM +0xF17C 0xFEDE # ARABIC LETTER LAM FINAL FORM +0xF17D 0xFEE3 # ARABIC LETTER MEEM INITIAL FORM +0xF17D 0xFEE4 # ARABIC LETTER MEEM MEDIAL FORM +0xF17E 0x0645 # ARABIC LETTER MEEM +0xF17E 0xFEE1 # ARABIC LETTER MEEM ISOLATED FORM +0xF17E 0xFEE2 # ARABIC LETTER MEEM FINAL FORM +0xF17F 0xFEE7 # ARABIC LETTER NOON INITIAL FORM +0xF17F 0xFEE8 # ARABIC LETTER NOON MEDIAL FORM +0xF1A1 0xFEEB # ARABIC LETTER HEH INITIAL FORM +0xF1A2 0xFEEC # ARABIC LETTER HEH MEDIAL FORM +0xF1A3 0xFEEA # ARABIC LETTER HEH FINAL FORM +0xF1A4 0x0647 # ARABIC LETTER HEH +0xF1A4 0xFEE9 # ARABIC LETTER HEH ISOLATED FORM +0xF1A5 0x0648 # ARABIC LETTER WAW +0xF1A5 0xFEED # ARABIC LETTER WAW ISOLATED FORM +0xF1A5 0xFEEE # ARABIC LETTER WAW FINAL FORM +0xF1A6 0xFEF3 # ARABIC LETTER YEH INITIAL FORM +0xF1A6 0xFEF4 # ARABIC LETTER YEH MEDIAL FORM +0xF1A7 0xFEF2 # ARABIC LETTER YEH FINAL FORM +0xF1A8 0x064A # ARABIC LETTER YEH +0xF1A8 0xFEF1 # ARABIC LETTER YEH ISOLATED FORM +0xF1A9 0x0629 # ARABIC LETTER TEH MARBUTA +0xF1A9 0xFE93 # ARABIC LETTER TEH MARBUTA ISOLATED FORM +0xF1AA 0xFE94 # ARABIC LETTER TEH MARBUTA FINAL FORM +0xF1AB 0xFEF0 # ARABIC LETTER ALEF MAKSURA FINAL FORM +0xF1AC 0x0649 # ARABIC LETTER ALEF MAKSURA +0xF1AC 0xFEEF # ARABIC LETTER ALEF MAKSURA ISOLATED FORM +0xF1AD 0x0621 # ARABIC LETTER HAMZA +0xF1AE 0xFE8B # ARABIC LETTER YEH WITH HAMZA ABOVE INITIAL FORM +0xF1AE 0xFE8C # ARABIC LETTER YEH WITH HAMZA ABOVE MEDIAL FORM +0xF1AF 0xFE8A # ARABIC LETTER YEH WITH HAMZA ABOVE FINAL FORM +0xF1B0 0x0030 # DIGIT ZERO +0xF1B1 0x0031 # DIGIT ONE +0xF1B2 0x0032 # DIGIT TWO +0xF1B3 0x0033 # DIGIT THREE +0xF1B4 0x0034 # DIGIT FOUR +0xF1B5 0x0035 # DIGIT FIVE +0xF1B6 0x0036 # DIGIT SIX +0xF1B7 0x0037 # DIGIT SEVEN +0xF1B8 0x0038 # DIGIT EIGHT +0xF1B9 0x0039 # DIGIT NINE +0xF1BA 0x0626 # ARABIC LETTER YEH WITH HAMZA ABOVE +0xF1BA 0xFE89 # ARABIC LETTER YEH WITH HAMZA ABOVE ISOLATED FORM +0xF1BB 0x0624 # ARABIC LETTER WAW WITH HAMZA ABOVE +0xF1BB 0xFE85 # ARABIC LETTER WAW WITH HAMZA ABOVE ISOLATED FORM +0xF1BB 0xFE86 # ARABIC LETTER WAW WITH HAMZA ABOVE FINAL FORM +0xF1BC 0xFEFC # ARABIC LIGATURE LAM WITH ALEF FINAL FORM +0xF1BD 0xFEFB # ARABIC LIGATURE LAM WITH ALEF ISOLATED FORM +0xF1BE 0xFEF7 # ARABIC LIGATURE LAM WITH ALEF WITH HAMZA ABOVE ISOLATED FORM +0xF1BF 0xFEF8 # ARABIC LIGATURE LAM WITH ALEF WITH HAMZA ABOVE FINAL FORM +0xF1C0 0xFEF5 # ARABIC LIGATURE LAM WITH ALEF WITH MADDA ABOVE ISOLATED FORM +0xF1C1 0xFEF6 # ARABIC LIGATURE LAM WITH ALEF WITH MADDA ABOVE FINAL FORM +0xF1C2 0xFEF9 # ARABIC LIGATURE LAM WITH ALEF WITH HAMZA BELOW ISOLATED FORM +0xF1C3 0xFEFA # ARABIC LIGATURE LAM WITH ALEF WITH HAMZA BELOW FINAL FORM +0xF1C4 0x064E # ARABIC FATHA +0xF1C5 0x064F # ARABIC DAMMA +0xF1C6 0x0652 # ARABIC SUKUN +0xF1C7 0x064B # ARABIC FATHATAN +0xF1C8 0x064C # ARABIC DAMMATAN +0xF1C9 0x0651 # ARABIC SHADDA +0xF1CA 0x0650 # ARABIC KASRA +0xF1CB 0x064D # ARABIC KASRATAN +0xF1E1 0x0646 # ARABIC LETTER NOON +0xF1E1 0xFEE5 # ARABIC LETTER NOON ISOLATED FORM +0xF1E1 0xFEE6 # ARABIC LETTER NOON FINAL FORM diff --git a/libs/harfbuzz/src/ArabicPUATraditional.txt b/libs/harfbuzz/src/ArabicPUATraditional.txt new file mode 100644 index 00000000..e14b383c --- /dev/null +++ b/libs/harfbuzz/src/ArabicPUATraditional.txt @@ -0,0 +1,295 @@ +# +# Name: Legacy Traditional Arabic encoding +# +# Format: Three tab-separated columns +# Column #1 is the PUA code (in hex as 0xXXXX) +# Column #2 is the Unicode (in hex as 0xXXXX) +# Column #3 is the Unicode name (follows a comment sign, '#') +# +# The entries are in PUA order +# +0xF200 0x063B # ARABIC LETTER KEHEH WITH TWO DOTS ABOVE +0xF200 0x063C # ARABIC LETTER KEHEH WITH THREE DOTS BELOW +0xF200 0x063D # ARABIC LETTER FARSI YEH WITH INVERTED V +0xF200 0x063E # ARABIC LETTER FARSI YEH WITH TWO DOTS ABOVE +0xF200 0x063F # ARABIC LETTER FARSI YEH WITH THREE DOTS ABOVE +0xF200 0x0653 # ARABIC MADDAH ABOVE +0xF200 0x0654 # ARABIC HAMZA ABOVE +0xF200 0x0655 # ARABIC HAMZA BELOW +0xF200 0x0656 # ARABIC SUBSCRIPT ALEF +0xF200 0x0657 # ARABIC INVERTED DAMMA +0xF200 0x0658 # ARABIC MARK NOON GHUNNA +0xF200 0x0659 # ARABIC ZWARAKAY +0xF200 0x065A # ARABIC VOWEL SIGN SMALL V ABOVE +0xF200 0x065B # ARABIC VOWEL SIGN INVERTED SMALL V ABOVE +0xF200 0x065C # ARABIC VOWEL SIGN DOT BELOW +0xF200 0x065D # ARABIC REVERSED DAMMA +0xF200 0x065E # ARABIC FATHA WITH TWO DOTS +0xF202 0xFC08 # ARABIC LIGATURE BEH WITH MEEM ISOLATED FORM +0xF203 0xFC0E # ARABIC LIGATURE TEH WITH MEEM ISOLATED FORM +0xF204 0xFC12 # ARABIC LIGATURE THEH WITH MEEM ISOLATED FORM +0xF205 0xFC42 # ARABIC LIGATURE LAM WITH MEEM ISOLATED FORM +0xF206 0xFC4E # ARABIC LIGATURE NOON WITH MEEM ISOLATED FORM +0xF20C 0x200C # ZERO WIDTH NON-JOINER +0xF20D 0x200D # ZERO WIDTH JOINER +0xF20E 0x200E # LEFT-TO-RIGHT MARK +0xF20F 0x200F # RIGHT-TO-LEFT MARK +0xF210 0xFD88 # ARABIC LIGATURE LAM WITH MEEM WITH HAH INITIAL FORM +0xF212 0xFC3F # ARABIC LIGATURE LAM WITH JEEM ISOLATED FORM +0xF213 0xFC40 # ARABIC LIGATURE LAM WITH HAH ISOLATED FORM +0xF214 0xFC41 # ARABIC LIGATURE LAM WITH KHAH ISOLATED FORM +0xF215 0xFC6A # ARABIC LIGATURE BEH WITH REH FINAL FORM +0xF216 0xFC70 # ARABIC LIGATURE TEH WITH REH FINAL FORM +0xF217 0xFC91 # ARABIC LIGATURE YEH WITH REH FINAL FORM +0xF218 0xFCB0 # ARABIC LIGATURE SEEN WITH MEEM INITIAL FORM +0xF219 0xFD30 # ARABIC LIGATURE SHEEN WITH MEEM INITIAL FORM +0xF21A 0xFCCD # ARABIC LIGATURE LAM WITH HEH INITIAL FORM +0xF21C 0xFC44 # ARABIC LIGATURE LAM WITH YEH ISOLATED FORM +0xF21D 0xFC0A # ARABIC LIGATURE BEH WITH YEH ISOLATED FORM +0xF21E 0xFC10 # ARABIC LIGATURE TEH WITH YEH ISOLATED FORM +0xF21F 0xFC50 # ARABIC LIGATURE NOON WITH YEH ISOLATED FORM +0xF220 0x0020 # SPACE +0xF221 0x0021 # EXCLAMATION MARK +0xF222 0x0022 # QUOTATION MARK +0xF223 0x00AB # LEFT-POINTING DOUBLE ANGLE QUOTATION MARK +0xF224 0x00BB # RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK +0xF225 0x0025 # PERCENT SIGN +0xF226 0x00D7 # MULTIPLICATION SIGN +0xF227 0x00F7 # DIVISION SIGN +0xF228 0x0028 # LEFT PARENTHESIS +0xF229 0x0029 # RIGHT PARENTHESIS +0xF22A 0x002A # ASTERISK +0xF22B 0x002B # PLUS SIGN +0xF22C 0x060C # ARABIC COMMA +0xF22D 0x002D # HYPHEN-MINUS +0xF22E 0x002E # FULL STOP +0xF22F 0x002F # SOLIDUS +0xF230 0x0660 # ARABIC-INDIC DIGIT ZERO +0xF231 0x0661 # ARABIC-INDIC DIGIT ONE +0xF232 0x0662 # ARABIC-INDIC DIGIT TWO +0xF233 0x0663 # ARABIC-INDIC DIGIT THREE +0xF234 0x0664 # ARABIC-INDIC DIGIT FOUR +0xF235 0x0665 # ARABIC-INDIC DIGIT FIVE +0xF236 0x0666 # ARABIC-INDIC DIGIT SIX +0xF237 0x0667 # ARABIC-INDIC DIGIT SEVEN +0xF238 0x0668 # ARABIC-INDIC DIGIT EIGHT +0xF239 0x0669 # ARABIC-INDIC DIGIT NINE +0xF23A 0x003A # COLON +0xF23B 0x003B # SEMICOLON +0xF23B 0x061B # ARABIC SEMICOLON +0xF23C 0x201C # LEFT DOUBLE QUOTATION MARK +0xF23D 0x003D # EQUALS SIGN +0xF23E 0x201D # RIGHT DOUBLE QUOTATION MARK +0xF23F 0x003F # QUESTION MARK +0xF23F 0x061F # ARABIC QUESTION MARK +0xF241 0x0627 # ARABIC LETTER ALEF +0xF241 0xFE8D # ARABIC LETTER ALEF ISOLATED FORM +0xF242 0xFE8E # ARABIC LETTER ALEF FINAL FORM +0xF243 0x0623 # ARABIC LETTER ALEF WITH HAMZA ABOVE +0xF243 0xFE83 # ARABIC LETTER ALEF WITH HAMZA ABOVE ISOLATED FORM +0xF244 0xFE84 # ARABIC LETTER ALEF WITH HAMZA ABOVE FINAL FORM +0xF245 0x0622 # ARABIC LETTER ALEF WITH MADDA ABOVE +0xF245 0xFE81 # ARABIC LETTER ALEF WITH MADDA ABOVE ISOLATED FORM +0xF246 0xFE82 # ARABIC LETTER ALEF WITH MADDA ABOVE FINAL FORM +0xF247 0x0625 # ARABIC LETTER ALEF WITH HAMZA BELOW +0xF247 0xFE87 # ARABIC LETTER ALEF WITH HAMZA BELOW ISOLATED FORM +0xF248 0xFE88 # ARABIC LETTER ALEF WITH HAMZA BELOW FINAL FORM +0xF249 0xFE91 # ARABIC LETTER BEH INITIAL FORM +0xF24A 0xFE92 # ARABIC LETTER BEH MEDIAL FORM +0xF24B 0xFE90 # ARABIC LETTER BEH FINAL FORM +0xF24C 0x0628 # ARABIC LETTER BEH +0xF24C 0xFE8F # ARABIC LETTER BEH ISOLATED FORM +0xF24D 0xFE97 # ARABIC LETTER TEH INITIAL FORM +0xF24E 0xFE98 # ARABIC LETTER TEH MEDIAL FORM +0xF24F 0xFE96 # ARABIC LETTER TEH FINAL FORM +0xF250 0x062A # ARABIC LETTER TEH +0xF250 0xFE95 # ARABIC LETTER TEH ISOLATED FORM +0xF251 0xFE9B # ARABIC LETTER THEH INITIAL FORM +0xF252 0xFE9C # ARABIC LETTER THEH MEDIAL FORM +0xF253 0xFE9A # ARABIC LETTER THEH FINAL FORM +0xF254 0x062B # ARABIC LETTER THEH +0xF254 0xFE99 # ARABIC LETTER THEH ISOLATED FORM +0xF255 0xFE9F # ARABIC LETTER JEEM INITIAL FORM +0xF256 0xFEA0 # ARABIC LETTER JEEM MEDIAL FORM +0xF257 0xFE9E # ARABIC LETTER JEEM FINAL FORM +0xF258 0x062C # ARABIC LETTER JEEM +0xF258 0xFE9D # ARABIC LETTER JEEM ISOLATED FORM +0xF259 0xFEA3 # ARABIC LETTER HAH INITIAL FORM +0xF25A 0xFEA4 # ARABIC LETTER HAH MEDIAL FORM +0xF25B 0x005B # LEFT SQUARE BRACKET +0xF25C 0xFEA2 # ARABIC LETTER HAH FINAL FORM +0xF25D 0x005D # RIGHT SQUARE BRACKET +0xF25E 0x002C # COMMA +0xF25E 0x066B # ARABIC DECIMAL SEPARATOR +0xF25E 0x066C # ARABIC THOUSANDS SEPARATOR +0xF25F 0x0640 # ARABIC TATWEEL +0xF260 0x062D # ARABIC LETTER HAH +0xF260 0xFEA1 # ARABIC LETTER HAH ISOLATED FORM +0xF261 0xFEA7 # ARABIC LETTER KHAH INITIAL FORM +0xF262 0xFEA8 # ARABIC LETTER KHAH MEDIAL FORM +0xF263 0xFEA6 # ARABIC LETTER KHAH FINAL FORM +0xF264 0x062E # ARABIC LETTER KHAH +0xF264 0xFEA5 # ARABIC LETTER KHAH ISOLATED FORM +0xF265 0x062F # ARABIC LETTER DAL +0xF265 0xFEA9 # ARABIC LETTER DAL ISOLATED FORM +0xF266 0xFEAA # ARABIC LETTER DAL FINAL FORM +0xF267 0x0630 # ARABIC LETTER THAL +0xF267 0xFEAB # ARABIC LETTER THAL ISOLATED FORM +0xF268 0xFEAC # ARABIC LETTER THAL FINAL FORM +0xF269 0x0631 # ARABIC LETTER REH +0xF269 0xFEAD # ARABIC LETTER REH ISOLATED FORM +0xF26A 0xFEAE # ARABIC LETTER REH FINAL FORM +0xF26B 0x0632 # ARABIC LETTER ZAIN +0xF26B 0xFEAF # ARABIC LETTER ZAIN ISOLATED FORM +0xF26C 0xFEB0 # ARABIC LETTER ZAIN FINAL FORM +0xF26D 0xFEB3 # ARABIC LETTER SEEN INITIAL FORM +0xF26E 0xFEB4 # ARABIC LETTER SEEN MEDIAL FORM +0xF26F 0xFEB2 # ARABIC LETTER SEEN FINAL FORM +0xF270 0x0633 # ARABIC LETTER SEEN +0xF270 0xFEB1 # ARABIC LETTER SEEN ISOLATED FORM +0xF271 0xFEB7 # ARABIC LETTER SHEEN INITIAL FORM +0xF272 0xFEB8 # ARABIC LETTER SHEEN MEDIAL FORM +0xF273 0xFEB6 # ARABIC LETTER SHEEN FINAL FORM +0xF274 0x0634 # ARABIC LETTER SHEEN +0xF274 0xFEB5 # ARABIC LETTER SHEEN ISOLATED FORM +0xF275 0xFEBB # ARABIC LETTER SAD INITIAL FORM +0xF276 0xFEBC # ARABIC LETTER SAD MEDIAL FORM +0xF277 0xFEBA # ARABIC LETTER SAD FINAL FORM +0xF278 0x0635 # ARABIC LETTER SAD +0xF278 0xFEB9 # ARABIC LETTER SAD ISOLATED FORM +0xF279 0xFEBF # ARABIC LETTER DAD INITIAL FORM +0xF27A 0xFEC0 # ARABIC LETTER DAD MEDIAL FORM +0xF27B 0xFD3E # ORNATE LEFT PARENTHESIS +0xF27C 0xFEBE # ARABIC LETTER DAD FINAL FORM +0xF27D 0xFD3F # ORNATE RIGHT PARENTHESIS +0xF27E 0x0636 # ARABIC LETTER DAD +0xF27E 0xFEBD # ARABIC LETTER DAD ISOLATED FORM +0xF27F 0xFEC3 # ARABIC LETTER TAH INITIAL FORM +0xF280 0xFC9C # ARABIC LIGATURE BEH WITH JEEM INITIAL FORM +0xF281 0xFC9D # ARABIC LIGATURE BEH WITH HAH INITIAL FORM +0xF282 0xFC9E # ARABIC LIGATURE BEH WITH KHAH INITIAL FORM +0xF283 0xFCA1 # ARABIC LIGATURE TEH WITH JEEM INITIAL FORM +0xF284 0xFCA2 # ARABIC LIGATURE TEH WITH HAH INITIAL FORM +0xF285 0xFCA3 # ARABIC LIGATURE TEH WITH KHAH INITIAL FORM +0xF286 0xFCC9 # ARABIC LIGATURE LAM WITH JEEM INITIAL FORM +0xF287 0xFCCA # ARABIC LIGATURE LAM WITH HAH INITIAL FORM +0xF288 0xFCCB # ARABIC LIGATURE LAM WITH KHAH INITIAL FORM +0xF289 0xFCCE # ARABIC LIGATURE MEEM WITH JEEM INITIAL FORM +0xF28A 0xFCCF # ARABIC LIGATURE MEEM WITH HAH INITIAL FORM +0xF28B 0xFCD0 # ARABIC LIGATURE MEEM WITH KHAH INITIAL FORM +0xF28D 0xFCD2 # ARABIC LIGATURE NOON WITH JEEM INITIAL FORM +0xF28E 0xFCD3 # ARABIC LIGATURE NOON WITH HAH INITIAL FORM +0xF28F 0xFCDA # ARABIC LIGATURE YEH WITH JEEM INITIAL FORM +0xF290 0xFCDB # ARABIC LIGATURE YEH WITH HAH INITIAL FORM +0xF291 0xFCDC # ARABIC LIGATURE YEH WITH KHAH INITIAL FORM +0xF292 0xFC6D # ARABIC LIGATURE BEH WITH NOON FINAL FORM +0xF293 0xFC73 # ARABIC LIGATURE TEH WITH NOON FINAL FORM +0xF294 0xFC94 # ARABIC LIGATURE YEH WITH NOON FINAL FORM +0xF295 0xFC86 # ARABIC LIGATURE LAM WITH ALEF MAKSURA FINAL FORM +0xF296 0xFC9F # ARABIC LIGATURE BEH WITH MEEM INITIAL FORM +0xF297 0xFCA4 # ARABIC LIGATURE TEH WITH MEEM INITIAL FORM +0xF298 0xFCD5 # ARABIC LIGATURE NOON WITH MEEM INITIAL FORM +0xF299 0xFCDD # ARABIC LIGATURE YEH WITH MEEM INITIAL FORM +0xF29A 0xFCA8 # ARABIC LIGATURE JEEM WITH MEEM INITIAL FORM +0xF29B 0xFCAA # ARABIC LIGATURE HAH WITH MEEM INITIAL FORM +0xF29C 0xFCAC # ARABIC LIGATURE KHAH WITH MEEM INITIAL FORM +0xF29D 0xFCCC # ARABIC LIGATURE LAM WITH MEEM INITIAL FORM +0xF29E 0xFCD1 # ARABIC LIGATURE MEEM WITH MEEM INITIAL FORM +0xF29F 0xFC32 # ARABIC LIGATURE FEH WITH YEH ISOLATED FORM +0xF2A1 0xFEC2 # ARABIC LETTER TAH FINAL FORM +0xF2A2 0x0637 # ARABIC LETTER TAH +0xF2A2 0xFEC1 # ARABIC LETTER TAH ISOLATED FORM +0xF2A3 0x0638 # ARABIC LETTER ZAH +0xF2A3 0xFEC7 # ARABIC LETTER ZAH INITIAL FORM +0xF2A4 0xFEC8 # ARABIC LETTER ZAH MEDIAL FORM +0xF2A5 0xFEC6 # ARABIC LETTER ZAH FINAL FORM +0xF2A6 0xFEC5 # ARABIC LETTER ZAH ISOLATED FORM +0xF2A7 0xFECB # ARABIC LETTER AIN INITIAL FORM +0xF2A8 0xFECC # ARABIC LETTER AIN MEDIAL FORM +0xF2A9 0xFECA # ARABIC LETTER AIN FINAL FORM +0xF2AA 0x0639 # ARABIC LETTER AIN +0xF2AA 0xFEC9 # ARABIC LETTER AIN ISOLATED FORM +0xF2AB 0xFECF # ARABIC LETTER GHAIN INITIAL FORM +0xF2AC 0xFED0 # ARABIC LETTER GHAIN MEDIAL FORM +0xF2AD 0xFECE # ARABIC LETTER GHAIN FINAL FORM +0xF2AE 0x063A # ARABIC LETTER GHAIN +0xF2AE 0xFECD # ARABIC LETTER GHAIN ISOLATED FORM +0xF2AF 0xFED3 # ARABIC LETTER FEH INITIAL FORM +0xF2B0 0xFED4 # ARABIC LETTER FEH MEDIAL FORM +0xF2B1 0xFED2 # ARABIC LETTER FEH FINAL FORM +0xF2B2 0x0641 # ARABIC LETTER FEH +0xF2B2 0xFED1 # ARABIC LETTER FEH ISOLATED FORM +0xF2B3 0xFED7 # ARABIC LETTER QAF INITIAL FORM +0xF2B4 0xFED8 # ARABIC LETTER QAF MEDIAL FORM +0xF2B5 0xFED6 # ARABIC LETTER QAF FINAL FORM +0xF2B6 0x0642 # ARABIC LETTER QAF +0xF2B6 0xFED5 # ARABIC LETTER QAF ISOLATED FORM +0xF2B7 0xFEDB # ARABIC LETTER KAF INITIAL FORM +0xF2B8 0xFEDC # ARABIC LETTER KAF MEDIAL FORM +0xF2B9 0xFEDA # ARABIC LETTER KAF FINAL FORM +0xF2BA 0x0643 # ARABIC LETTER KAF +0xF2BA 0xFED9 # ARABIC LETTER KAF ISOLATED FORM +0xF2BB 0xFEDF # ARABIC LETTER LAM INITIAL FORM +0xF2BC 0xFEE0 # ARABIC LETTER LAM MEDIAL FORM +0xF2BD 0xFEDE # ARABIC LETTER LAM FINAL FORM +0xF2BE 0x0644 # ARABIC LETTER LAM +0xF2BE 0xFEDD # ARABIC LETTER LAM ISOLATED FORM +0xF2BF 0xFEE3 # ARABIC LETTER MEEM INITIAL FORM +0xF2C0 0xFEE4 # ARABIC LETTER MEEM MEDIAL FORM +0xF2C1 0xFEE2 # ARABIC LETTER MEEM FINAL FORM +0xF2C2 0x0645 # ARABIC LETTER MEEM +0xF2C2 0xFEE1 # ARABIC LETTER MEEM ISOLATED FORM +0xF2C3 0xFEE7 # ARABIC LETTER NOON INITIAL FORM +0xF2C4 0xFEE8 # ARABIC LETTER NOON MEDIAL FORM +0xF2C5 0xFEE6 # ARABIC LETTER NOON FINAL FORM +0xF2C6 0x0646 # ARABIC LETTER NOON +0xF2C6 0xFEE5 # ARABIC LETTER NOON ISOLATED FORM +0xF2C7 0xFEEB # ARABIC LETTER HEH INITIAL FORM +0xF2C8 0xFEEC # ARABIC LETTER HEH MEDIAL FORM +0xF2C9 0xFEEA # ARABIC LETTER HEH FINAL FORM +0xF2CA 0x0647 # ARABIC LETTER HEH +0xF2CA 0xFEE9 # ARABIC LETTER HEH ISOLATED FORM +0xF2CB 0x0648 # ARABIC LETTER WAW +0xF2CB 0xFEED # ARABIC LETTER WAW ISOLATED FORM +0xF2CC 0xFEEE # ARABIC LETTER WAW FINAL FORM +0xF2CD 0xFEF3 # ARABIC LETTER YEH INITIAL FORM +0xF2CE 0xFEF4 # ARABIC LETTER YEH MEDIAL FORM +0xF2CF 0xFEF2 # ARABIC LETTER YEH FINAL FORM +0xF2D0 0x064A # ARABIC LETTER YEH +0xF2D0 0xFEF1 # ARABIC LETTER YEH ISOLATED FORM +0xF2D1 0x0629 # ARABIC LETTER TEH MARBUTA +0xF2D1 0xFE93 # ARABIC LETTER TEH MARBUTA ISOLATED FORM +0xF2D2 0xFE94 # ARABIC LETTER TEH MARBUTA FINAL FORM +0xF2D3 0xFEF0 # ARABIC LETTER ALEF MAKSURA FINAL FORM +0xF2D4 0x0649 # ARABIC LETTER ALEF MAKSURA +0xF2D4 0xFEEF # ARABIC LETTER ALEF MAKSURA ISOLATED FORM +0xF2D5 0x0621 # ARABIC LETTER HAMZA +0xF2D6 0xFE8B # ARABIC LETTER YEH WITH HAMZA ABOVE INITIAL FORM +0xF2D7 0xFE8C # ARABIC LETTER YEH WITH HAMZA ABOVE MEDIAL FORM +0xF2D8 0xFE8A # ARABIC LETTER YEH WITH HAMZA ABOVE FINAL FORM +0xF2D9 0x0626 # ARABIC LETTER YEH WITH HAMZA ABOVE +0xF2D9 0xFE89 # ARABIC LETTER YEH WITH HAMZA ABOVE ISOLATED FORM +0xF2DA 0x0624 # ARABIC LETTER WAW WITH HAMZA ABOVE +0xF2DA 0xFE85 # ARABIC LETTER WAW WITH HAMZA ABOVE ISOLATED FORM +0xF2DB 0xFE86 # ARABIC LETTER WAW WITH HAMZA ABOVE FINAL FORM +0xF2DC 0xFEFB # ARABIC LIGATURE LAM WITH ALEF ISOLATED FORM +0xF2DD 0xFEFC # ARABIC LIGATURE LAM WITH ALEF FINAL FORM +0xF2DE 0xFEF7 # ARABIC LIGATURE LAM WITH ALEF WITH HAMZA ABOVE ISOLATED FORM +0xF2DF 0xFEF8 # ARABIC LIGATURE LAM WITH ALEF WITH HAMZA ABOVE FINAL FORM +0xF2E0 0xFEF5 # ARABIC LIGATURE LAM WITH ALEF WITH MADDA ABOVE ISOLATED FORM +0xF2E1 0xFEF6 # ARABIC LIGATURE LAM WITH ALEF WITH MADDA ABOVE FINAL FORM +0xF2E2 0xFEF9 # ARABIC LIGATURE LAM WITH ALEF WITH HAMZA BELOW ISOLATED FORM +0xF2E3 0xFEFA # ARABIC LIGATURE LAM WITH ALEF WITH HAMZA BELOW FINAL FORM +0xF2E4 0x064E # ARABIC FATHA +0xF2E5 0x064F # ARABIC DAMMA +0xF2E6 0x0652 # ARABIC SUKUN +0xF2E7 0x064B # ARABIC FATHATAN +0xF2E8 0x064C # ARABIC DAMMATAN +0xF2E9 0x0651 # ARABIC SHADDA +0xF2EA 0x0650 # ARABIC KASRA +0xF2EB 0x064D # ARABIC KASRATAN +0xF2EC 0xFC60 # ARABIC LIGATURE SHADDA WITH FATHA ISOLATED FORM +0xF2ED 0xFC61 # ARABIC LIGATURE SHADDA WITH DAMMA ISOLATED FORM +0xF2EF 0xFC5E # ARABIC LIGATURE SHADDA WITH DAMMATAN ISOLATED FORM +0xF2F0 0xFC62 # ARABIC LIGATURE SHADDA WITH KASRA ISOLATED FORM +0xF2F1 0xFEC4 # ARABIC LETTER TAH MEDIAL FORM diff --git a/libs/harfbuzz/src/Makefile.am b/libs/harfbuzz/src/Makefile.am new file mode 100644 index 00000000..225444e3 --- /dev/null +++ b/libs/harfbuzz/src/Makefile.am @@ -0,0 +1,522 @@ +# Process this file with automake to produce Makefile.in + +NULL = +SUBDIRS = +DIST_SUBDIRS = +BUILT_SOURCES = +EXTRA_DIST = +CLEANFILES = +DISTCLEANFILES = +MAINTAINERCLEANFILES = +DISTCHECK_CONFIGURE_FLAGS = --enable-introspection +TESTS = +check_PROGRAMS = + +EXTRA_DIST += harfbuzz.cc harfbuzz-subset.cc +EXTRA_DIST += meson.build +EXTRA_DIST += fix_get_types.py + +# Convenience targets: +lib: $(BUILT_SOURCES) libharfbuzz.la +libs: $(BUILT_SOURCES) $(lib_LTLIBRARIES) +tiny: + $(MAKE) $(AM_MAKEFLAGS) CPPFLAGS="-Os -DHB_TINY $(CPPFLAGS)" libs +tinyz: + $(MAKE) $(AM_MAKEFLAGS) CPPFLAGS="-Oz -DHB_TINY $(CPPFLAGS)" libs + +lib_LTLIBRARIES = libharfbuzz.la + +include Makefile.sources + +HBCFLAGS = +HBLIBS = +HBNONPCLIBS = +HBDEPS = +HBSOURCES = $(HB_BASE_sources) +HBSOURCES += $(HB_BASE_RAGEL_GENERATED_sources) +HBHEADERS = $(HB_BASE_headers) + +if HAVE_PTHREAD +HBCFLAGS += $(PTHREAD_CFLAGS) +HBNONPCLIBS += $(PTHREAD_LIBS) +endif + +if HAVE_GLIB +HBCFLAGS += $(GLIB_CFLAGS) +HBLIBS += $(GLIB_LIBS) +HBDEPS += $(GLIB_DEPS) +HBSOURCES += $(HB_GLIB_sources) +HBHEADERS += $(HB_GLIB_headers) +endif + +if HAVE_FREETYPE +HBCFLAGS += $(FREETYPE_CFLAGS) +HBLIBS += $(FREETYPE_LIBS) +HBDEPS += $(FREETYPE_DEPS) +HBSOURCES += $(HB_FT_sources) +HBHEADERS += $(HB_FT_headers) +endif + +if HAVE_GRAPHITE2 +HBCFLAGS += $(GRAPHITE2_CFLAGS) +HBLIBS += $(GRAPHITE2_LIBS) +HBDEPS += $(GRAPHITE2_DEPS) +HBSOURCES += $(HB_GRAPHITE2_sources) +HBHEADERS += $(HB_GRAPHITE2_headers) +endif + +if HAVE_UNISCRIBE +HBCFLAGS += $(UNISCRIBE_CFLAGS) +HBNONPCLIBS += $(UNISCRIBE_LIBS) +HBSOURCES += $(HB_UNISCRIBE_sources) +HBHEADERS += $(HB_UNISCRIBE_headers) +endif + +if HAVE_DIRECTWRITE +HBCFLAGS += $(DIRECTWRITE_CXXFLAGS) +HBNONPCLIBS += $(DIRECTWRITE_LIBS) +HBSOURCES += $(HB_DIRECTWRITE_sources) +HBHEADERS += $(HB_DIRECTWRITE_headers) +endif + +if HAVE_GDI +HBCFLAGS += $(GDI_CXXFLAGS) +HBNONPCLIBS += $(GDI_LIBS) +HBSOURCES += $(HB_GDI_sources) +HBHEADERS += $(HB_GDI_headers) +endif + +if HAVE_CORETEXT +HBCFLAGS += $(CORETEXT_CFLAGS) +HBNONPCLIBS += $(CORETEXT_LIBS) +HBSOURCES += $(HB_CORETEXT_sources) +HBHEADERS += $(HB_CORETEXT_headers) +endif + + +BUILT_SOURCES += \ + hb-version.h + +$(srcdir)/hb-version.h: hb-version.h.in $(top_srcdir)/configure.ac + $(AM_V_GEN) $(SED) \ + -e 's/[@]HB_VERSION_MAJOR@/$(HB_VERSION_MAJOR)/' \ + -e 's/[@]HB_VERSION_MINOR@/$(HB_VERSION_MINOR)/' \ + -e 's/[@]HB_VERSION_MICRO@/$(HB_VERSION_MICRO)/' \ + -e 's/[@]HB_VERSION@/$(HB_VERSION)/' \ + "$<" > "$@" || ($(RM) "$@"; false) + +# Put the library together + +HBLIBS += $(HBNONPCLIBS) + +if OS_WIN32 +export_symbols = -export-symbols harfbuzz.def +harfbuzz_def_dependency = harfbuzz.def +export_symbols_subset = -export-symbols harfbuzz-subset.def +harfbuzz_subset_def_dependency = harfbuzz-subset.def +export_symbols_icu = -export-symbols harfbuzz-icu.def +harfbuzz_icu_def_dependency = harfbuzz-icu.def +export_symbols_gobject = -export-symbols harfbuzz-gobject.def +harfbuzz_gobject_def_dependency = harfbuzz-gobject.def +chosen_linker = $(CXXLINK) +else +if WITH_LIBSTDCXX +chosen_linker = $(CXXLINK) +else +if HAVE_GCC +# Use a C linker for GCC, not C++; Don't link to libstdc++ +chosen_linker = $(LINK) +else +chosen_linker = $(CXXLINK) +endif +endif +endif + +@CODE_COVERAGE_RULES@ + +base_link_flags = $(AM_LDFLAGS) -lm -version-info $(HB_LIBTOOL_VERSION_INFO) -no-undefined +libharfbuzz_la_LINK = $(chosen_linker) $(libharfbuzz_la_LDFLAGS) +libharfbuzz_la_SOURCES = $(HBSOURCES) $(HBHEADERS) +libharfbuzz_la_CPPFLAGS = $(HBCFLAGS) $(CODE_COVERAGE_CFLAGS) +libharfbuzz_la_LDFLAGS = $(base_link_flags) $(export_symbols) $(CODE_COVERAGE_LDFLAGS) +libharfbuzz_la_LIBADD = $(HBLIBS) +EXTRA_libharfbuzz_la_DEPENDENCIES = $(harfbuzz_def_dependency) +pkginclude_HEADERS = $(HBHEADERS) +nodist_pkginclude_HEADERS = +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = harfbuzz.pc +cmakedir = $(libdir)/cmake/harfbuzz +cmake_DATA = harfbuzz-config.cmake +EXTRA_DIST += hb-version.h.in harfbuzz.pc.in harfbuzz-config.cmake.in + +lib_LTLIBRARIES += libharfbuzz-subset.la +libharfbuzz_subset_la_LINK = $(chosen_linker) $(libharfbuzz_subset_la_LDFLAGS) +libharfbuzz_subset_la_SOURCES = $(HB_SUBSET_sources) +libharfbuzz_subset_la_CPPFLAGS = $(HBCFLAGS) $(CODE_COVERAGE_CFLAGS) +libharfbuzz_subset_la_LDFLAGS = $(base_link_flags) $(export_symbols_subset) $(CODE_COVERAGE_LDFLAGS) +libharfbuzz_subset_la_LIBADD = libharfbuzz.la +EXTRA_libharfbuzz_subset_la_DEPENDENCIES = $(harfbuzz_subset_def_dependency) +pkginclude_HEADERS += $(HB_SUBSET_headers) +pkgconfig_DATA += harfbuzz-subset.pc +EXTRA_DIST += harfbuzz-subset.pc.in + +harfbuzz-subset.cc: Makefile.sources + $(AM_V_GEN) \ + LANG=C; \ + for f in \ + $(HB_BASE_sources) \ + $(HB_SUBSET_sources) \ + ; do echo '#include "'$$f'"'; done | \ + sort -u | \ + grep '[.]cc"' > $(srcdir)/harfbuzz-subset.cc \ + || ($(RM) $(srcdir)/harfbuzz-subset.cc; false) +BUILT_SOURCES += harfbuzz-subset.cc + +if HAVE_ICU +if HAVE_ICU_BUILTIN +HBCFLAGS += $(ICU_CFLAGS) +HBLIBS += $(ICU_LIBS) +HBSOURCES += $(HB_ICU_sources) +HBHEADERS += $(HB_ICU_headers) +else +lib_LTLIBRARIES += libharfbuzz-icu.la +libharfbuzz_icu_la_SOURCES = $(HB_ICU_sources) +libharfbuzz_icu_la_CPPFLAGS = $(HBCFLAGS) $(ICU_CFLAGS) $(CODE_COVERAGE_CFLAGS) +libharfbuzz_icu_la_LDFLAGS = $(base_link_flags) $(export_symbols_icu) $(CODE_COVERAGE_LDFLAGS) +libharfbuzz_icu_la_LIBADD = $(ICU_LIBS) libharfbuzz.la +EXTRA_libharfbuzz_icu_la_DEPENDENCIES = $(harfbuzz_icu_def_dependency) +pkginclude_HEADERS += $(HB_ICU_headers) +pkgconfig_DATA += harfbuzz-icu.pc +endif +endif +EXTRA_DIST += harfbuzz-icu.pc.in + +if HAVE_GOBJECT +lib_LTLIBRARIES += libharfbuzz-gobject.la +libharfbuzz_gobject_la_LINK = $(chosen_linker) $(libharfbuzz_gobject_la_LDFLAGS) +libharfbuzz_gobject_la_SOURCES = $(HB_GOBJECT_DIST_sources) +nodist_libharfbuzz_gobject_la_SOURCES = $(HB_GOBJECT_NODIST_sources) +libharfbuzz_gobject_la_CPPFLAGS = $(HBCFLAGS) $(GOBJECT_CFLAGS) $(CODE_COVERAGE_CFLAGS) +libharfbuzz_gobject_la_LDFLAGS = $(base_link_flags) $(CODE_COVERAGE_LDFLAGS) +libharfbuzz_gobject_la_LIBADD = $(GOBJECT_LIBS) libharfbuzz.la +EXTRA_libharfbuzz_gobject_la_DEPENDENCIES = $(harfbuzz_gobject_def_dependency) +pkginclude_HEADERS += $(HB_GOBJECT_DIST_headers) +nodist_pkginclude_HEADERS += $(HB_GOBJECT_NODIST_headers) +pkgconfig_DATA += harfbuzz-gobject.pc + +BUILT_SOURCES += \ + $(HB_GOBJECT_ENUM_sources) \ + $(HB_GOBJECT_ENUM_headers) \ + $(NULL) +DISTCLEANFILES += \ + $(HB_GOBJECT_ENUM_sources) \ + $(HB_GOBJECT_ENUM_headers) \ + $(NULL) +hb-gobject-enums.%: hb-gobject-enums.%.tmpl $(HBHEADERS) + $(AM_V_GEN) PYTHONIOENCODING=UTF-8 $(GLIB_MKENUMS) \ + --identifier-prefix hb_ --symbol-prefix hb_gobject \ + --template $^ | \ + sed 's/_t_get_type/_get_type/g; s/_T (/ (/g' > "$@" \ + || ($(RM) "$@"; false) +endif +EXTRA_DIST += \ + harfbuzz-gobject.pc.in \ + hb-gobject-enums.cc.tmpl \ + hb-gobject-enums.h.tmpl \ + $(NULL) + + +%.pc: %.pc.in $(top_builddir)/config.status + $(AM_V_GEN) \ + $(SED) -e 's@%prefix%@$(prefix)@g' \ + -e 's@%exec_prefix%@$(exec_prefix)@g' \ + -e 's@%libdir%@$(libdir)@g' \ + -e 's@%includedir%@$(includedir)@g' \ + -e 's@%libs_private%@$(HBNONPCLIBS)@g' \ + -e 's@%requires_private%@$(HBDEPS)@g' \ + -e 's@%VERSION%@$(VERSION)@g' \ + "$<" > "$@" \ + || ($(RM) "$@"; false) + +CLEANFILES += $(pkgconfig_DATA) + + +DEF_FILES = harfbuzz.def harfbuzz-subset.def harfbuzz-icu.def harfbuzz-deprecated-symbols.txt +if HAVE_GOBJECT +DEF_FILES += harfbuzz-gobject.def +endif +check: $(DEF_FILES) # For check-symbols.sh +CLEANFILES += $(DEF_FILES) +harfbuzz.def: $(top_builddir)/config.status +harfbuzz.def: $(HBHEADERS) + $(AM_V_GEN) $(srcdir)/gen-def.py "$@" $^ +harfbuzz-subset.def: $(HB_SUBSET_headers) + $(AM_V_GEN) $(srcdir)/gen-def.py "$@" $^ +harfbuzz-icu.def: $(HB_ICU_headers) + $(AM_V_GEN) $(srcdir)/gen-def.py "$@" $^ +harfbuzz-gobject.def: $(HB_GOBJECT_headers) + $(AM_V_GEN) $(srcdir)/gen-def.py "$@" $^ +harfbuzz-deprecated-symbols.txt: $(srcdir)/hb-deprecated.h + $(AM_V_GEN) PLAIN_LIST=1 $(srcdir)/gen-def.py "$@" $^ + + +GENERATORS = \ + gen-arabic-joining-list.py \ + gen-arabic-table.py \ + gen-def.py \ + gen-emoji-table.py \ + gen-harfbuzzcc.py \ + gen-hb-version.py \ + gen-indic-table.py \ + gen-os2-unicode-ranges.py \ + gen-ragel-artifacts.py \ + gen-tag-table.py \ + gen-ucd-table.py \ + gen-use-table.py \ + gen-vowel-constraints.py \ + $(NULL) +EXTRA_DIST += $(GENERATORS) + +built-sources: $(BUILT_SOURCES) + +.PHONY: built-sources + +RAGEL_GENERATED = \ + $(patsubst %,$(srcdir)/%,$(HB_BASE_RAGEL_GENERATED_sources)) \ + $(NULL) +BUILT_SOURCES += $(RAGEL_GENERATED) +EXTRA_DIST += \ + $(HB_BASE_RAGEL_sources) \ + $(NULL) +# We decided to add ragel-generated files to git... +#MAINTAINERCLEANFILES += $(RAGEL_GENERATED) +$(srcdir)/%.hh: $(srcdir)/%.rl + $(AM_V_GEN)(cd $(srcdir) && $(RAGEL) -e -F1 -o "$*.hh" "$*.rl") \ + || ($(RM) "$@"; false) + +harfbuzz.cc: Makefile.sources + $(AM_V_GEN) \ + LANG=C; \ + for f in \ + $(HB_BASE_sources) \ + $(HB_GLIB_sources) \ + $(HB_FT_sources) \ + $(HB_GRAPHITE2_sources) \ + $(HB_UNISCRIBE_sources) \ + $(HB_GDI_sources) \ + $(HB_DIRECTWRITE_sources) \ + $(HB_CORETEXT_sources) \ + ; do echo '#include "'$$f'"'; done | \ + sort -u | \ + grep '[.]cc"' > $(srcdir)/harfbuzz.cc \ + || ($(RM) $(srcdir)/harfbuzz.cc; false) +BUILT_SOURCES += harfbuzz.cc + +noinst_PROGRAMS = \ + main \ + test \ + test-buffer-serialize \ + test-ot-meta \ + test-ot-name \ + test-ot-glyphname \ + test-gpos-size-params \ + test-gsub-would-substitute \ + test-use-table \ + $(NULL) +bin_PROGRAMS = + +main_SOURCES = main.cc +main_CPPFLAGS = $(HBCFLAGS) +main_LDADD = libharfbuzz.la $(HBLIBS) + +test_SOURCES = test.cc +test_CPPFLAGS = $(HBCFLAGS) $(FREETYPE_CFLAGS) +test_LDADD = libharfbuzz.la $(HBLIBS) $(FREETYPE_LIBS) + +test_buffer_serialize_SOURCES = test-buffer-serialize.cc +test_buffer_serialize_CPPFLAGS = $(HBCFLAGS) +test_buffer_serialize_LDADD = libharfbuzz.la $(HBLIBS) + +test_ot_meta_SOURCES = test-ot-meta.cc +test_ot_meta_CPPFLAGS = $(HBCFLAGS) +test_ot_meta_LDADD = libharfbuzz.la $(HBLIBS) + +test_ot_name_SOURCES = test-ot-name.cc +test_ot_name_CPPFLAGS = $(HBCFLAGS) +test_ot_name_LDADD = libharfbuzz.la $(HBLIBS) + +test_ot_glyphname_SOURCES = test-ot-glyphname.cc +test_ot_glyphname_CPPFLAGS = $(HBCFLAGS) +test_ot_glyphname_LDADD = libharfbuzz.la $(HBLIBS) + +test_use_table_SOURCES = test-use-table.cc +test_use_table_CPPFLAGS = $(HBCFLAGS) +test_use_table_LDADD = libharfbuzz.la $(HBLIBS) + +test_gpos_size_params_SOURCES = test-gpos-size-params.cc +test_gpos_size_params_CPPFLAGS = $(HBCFLAGS) +test_gpos_size_params_LDADD = libharfbuzz.la $(HBLIBS) + +test_gsub_would_substitute_SOURCES = test-gsub-would-substitute.cc +test_gsub_would_substitute_CPPFLAGS = $(HBCFLAGS) $(FREETYPE_CFLAGS) +test_gsub_would_substitute_LDADD = libharfbuzz.la $(HBLIBS) $(FREETYPE_LIBS) + +COMPILED_TESTS = \ + test-algs \ + test-array \ + test-bimap \ + test-iter \ + test-machinery \ + test-map \ + test-number \ + test-ot-tag \ + test-priority-queue \ + test-set \ + test-serialize \ + test-unicode-ranges \ + test-vector \ + test-repacker \ + test-classdef-graph \ + $(NULL) +COMPILED_TESTS_CPPFLAGS = $(HBCFLAGS) -DMAIN -UNDEBUG +COMPILED_TESTS_LDADD = libharfbuzz.la $(HBLIBS) +check_PROGRAMS += $(COMPILED_TESTS) +TESTS += $(COMPILED_TESTS) + +test_algs_SOURCES = test-algs.cc hb-static.cc +test_algs_CPPFLAGS = $(COMPILED_TESTS_CPPFLAGS) +test_algs_LDADD = $(COMPILED_TESTS_LDADD) + +test_array_SOURCES = test-array.cc +test_array_CPPFLAGS = $(HBCFLAGS) +test_array_LDADD = libharfbuzz.la $(HBLIBS) + +test_bimap_SOURCES = test-bimap.cc hb-static.cc +test_bimap_CPPFLAGS = $(COMPILED_TESTS_CPPFLAGS) +test_bimap_LDADD = $(COMPILED_TESTS_LDADD) + +test_iter_SOURCES = test-iter.cc hb-static.cc +test_iter_CPPFLAGS = $(COMPILED_TESTS_CPPFLAGS) +test_iter_LDADD = $(COMPILED_TESTS_LDADD) + +test_machinery_SOURCES = test-machinery.cc hb-static.cc +test_machinery_CPPFLAGS = $(COMPILED_TESTS_CPPFLAGS) +test_machinery_LDADD = $(COMPILED_TESTS_LDADD) + +test_map_SOURCES = test-map.cc hb-static.cc +test_map_CPPFLAGS = $(COMPILED_TESTS_CPPFLAGS) +test_map_LDADD = $(COMPILED_TESTS_LDADD) + +test_number_SOURCES = test-number.cc hb-number.cc +test_number_CPPFLAGS = $(COMPILED_TESTS_CPPFLAGS) +test_number_LDADD = $(COMPILED_TESTS_LDADD) + +test_ot_tag_SOURCES = hb-ot-tag.cc +test_ot_tag_CPPFLAGS = $(COMPILED_TESTS_CPPFLAGS) +test_ot_tag_LDADD = $(COMPILED_TESTS_LDADD) + +test_priority_queue_SOURCES = test-priority-queue.cc hb-static.cc +test_priority_queue_CPPFLAGS = $(HBCFLAGS) +test_priority_queue_LDADD = libharfbuzz.la $(HBLIBS) + +test_repacker_SOURCES = test-repacker.cc hb-static.cc graph/gsubgpos-context.cc +test_repacker_CPPFLAGS = $(HBCFLAGS) +test_repacker_LDADD = libharfbuzz.la libharfbuzz-subset.la $(HBLIBS) + +test_classdef_graph_SOURCES = graph/test-classdef-graph.cc hb-static.cc graph/gsubgpos-context.cc +test_classdef_graph_CPPFLAGS = $(HBCFLAGS) +test_classdef_graph_LDADD = libharfbuzz.la libharfbuzz-subset.la $(HBLIBS) + +test_set_SOURCES = test-set.cc hb-static.cc +test_set_CPPFLAGS = $(COMPILED_TESTS_CPPFLAGS) +test_set_LDADD = $(COMPILED_TESTS_LDADD) + +test_serialize_SOURCES = test-serialize.cc hb-static.cc +test_serialize_CPPFLAGS = $(COMPILED_TESTS_CPPFLAGS) +test_serialize_LDADD = $(COMPILED_TESTS_LDADD) + +test_unicode_ranges_SOURCES = test-unicode-ranges.cc +test_unicode_ranges_CPPFLAGS = $(COMPILED_TESTS_CPPFLAGS) +test_unicode_ranges_LDADD = $(COMPILED_TESTS_LDADD) + +test_vector_SOURCES = test-vector.cc hb-static.cc +test_vector_CPPFLAGS = $(COMPILED_TESTS_CPPFLAGS) +test_vector_LDADD = $(COMPILED_TESTS_LDADD) + +dist_check_SCRIPTS = \ + check-c-linkage-decls.py \ + check-externs.py \ + check-header-guards.py \ + check-includes.py \ + check-static-inits.py \ + check-symbols.py \ + $(NULL) +TESTS += $(dist_check_SCRIPTS) + +if !WITH_LIBSTDCXX +dist_check_SCRIPTS += \ + check-libstdc++.py \ + $(NULL) +endif + +TESTS_ENVIRONMENT = \ + srcdir="$(srcdir)" \ + base_srcdir="$(srcdir)" \ + builddir="$(builddir)" \ + MAKE="$(MAKE) $(AM_MAKEFLAGS)" \ + HBSOURCES="$(HBSOURCES)" \ + HBHEADERS="$(HBHEADERS)" \ + LDD="$(LDD)" \ + NM="$(NM)" \ + OBJDUMP="$(OBJDUMP)" \ + OTOOL="$(OTOOL)" \ + $(NULL) + +if HAVE_INTROSPECTION + +-include $(INTROSPECTION_MAKEFILE) +INTROSPECTION_GIRS = HarfBuzz-0.0.gir # What does the 0 mean anyway?! +INTROSPECTION_SCANNER_ARGS = \ + -I$(srcdir) \ + --warn-all --verbose \ + --namespace=HarfBuzz \ + --nsversion=0.0 \ + --symbol-prefix=hb \ + --symbol-prefix=hb_gobject \ + --identifier-prefix=hb_ \ + --pkg-export=harfbuzz-gobject \ + --c-include=hb-gobject.h +INTROSPECTION_COMPILER_ARGS = --includedir=$(srcdir) +INTROSPECTION_SCANNER_ENV = CC="$(CC)" + +HarfBuzz-0.0.gir: libharfbuzz.la libharfbuzz-gobject.la +HarfBuzz_0_0_gir_INCLUDES = GObject-2.0 freetype2-2.0 +HarfBuzz_0_0_gir_CFLAGS = \ + $(INCLUDES) \ + $(HBCFLAGS) \ + -DHB_NO_SINGLE_HEADER_ERROR \ + -DHAVE_GOBJECT \ + -DHB_EXTERN= \ + $(NULL) +HarfBuzz_0_0_gir_LIBS = \ + libharfbuzz.la \ + libharfbuzz-gobject.la \ + $(NULL) +HarfBuzz_0_0_gir_FILES = \ + $(HBHEADERS) \ + $(HBSOURCES) \ + $(HB_GOBJECT_sources) \ + $(HB_GOBJECT_headers) \ + $(NULL) + +girdir = $(datadir)/gir-1.0 +gir_DATA = $(INTROSPECTION_GIRS) + +typelibdir = $(libdir)/girepository-1.0 +typelib_DATA = $(INTROSPECTION_GIRS:.gir=.typelib) + +CLEANFILES += $(gir_DATA) $(typelib_DATA) + +endif + +-include $(top_srcdir)/git.mk diff --git a/libs/harfbuzz/src/Makefile.sources b/libs/harfbuzz/src/Makefile.sources new file mode 100644 index 00000000..6c891eac --- /dev/null +++ b/libs/harfbuzz/src/Makefile.sources @@ -0,0 +1,375 @@ +# Base and default-included sources and headers + +HB_BASE_sources = \ + hb-aat-layout-ankr-table.hh \ + hb-aat-layout-bsln-table.hh \ + hb-aat-layout-common.hh \ + hb-aat-layout-feat-table.hh \ + hb-aat-layout-just-table.hh \ + hb-aat-layout-kerx-table.hh \ + hb-aat-layout-morx-table.hh \ + hb-aat-layout-opbd-table.hh \ + hb-aat-layout-trak-table.hh \ + hb-aat-layout.cc \ + hb-aat-layout.hh \ + hb-aat-ltag-table.hh \ + hb-aat-map.cc \ + hb-aat-map.hh \ + hb-algs.hh \ + hb-array.hh \ + hb-atomic.hh \ + hb-bimap.hh \ + hb-bit-page.hh \ + hb-bit-set.hh \ + hb-bit-set-invertible.hh \ + hb-blob.cc \ + hb-blob.hh \ + hb-buffer-serialize.cc \ + hb-buffer-verify.cc \ + hb-buffer.cc \ + hb-buffer.hh \ + hb-cache.hh \ + hb-cff-interp-common.hh \ + hb-cff-interp-cs-common.hh \ + hb-cff-interp-dict-common.hh \ + hb-cff1-interp-cs.hh \ + hb-cff2-interp-cs.hh \ + hb-common.cc \ + hb-config.hh \ + hb-debug.hh \ + hb-dispatch.hh \ + hb-draw.cc \ + hb-draw.hh \ + hb-face.cc \ + hb-face.hh \ + hb-fallback-shape.cc \ + hb-font.cc \ + hb-font.hh \ + hb-iter.hh \ + hb-kern.hh \ + hb-machinery.hh \ + hb-map.cc \ + hb-map.hh \ + hb-meta.hh \ + hb-ms-feature-ranges.hh \ + hb-mutex.hh \ + hb-null.hh \ + hb-number.cc \ + hb-number.hh \ + hb-object.hh \ + hb-open-file.hh \ + hb-open-type.hh \ + hb-ot-cff-common.hh \ + hb-ot-cff1-std-str.hh \ + hb-ot-cff1-table.cc \ + hb-ot-cff1-table.hh \ + hb-ot-cff2-table.cc \ + hb-ot-cff2-table.hh \ + hb-ot-cmap-table.hh \ + hb-ot-color-cbdt-table.hh \ + hb-ot-color-colr-table.hh \ + hb-ot-color-cpal-table.hh \ + hb-ot-color-sbix-table.hh \ + hb-ot-color-svg-table.hh \ + hb-ot-color.cc \ + hb-ot-face-table-list.hh \ + hb-ot-face.cc \ + hb-ot-face.hh \ + hb-ot-font.cc \ + hb-ot-gasp-table.hh \ + hb-ot-glyf-table.hh \ + hb-ot-hdmx-table.hh \ + hb-ot-head-table.hh \ + hb-ot-hhea-table.hh \ + hb-ot-hmtx-table.hh \ + hb-ot-kern-table.hh \ + hb-ot-layout-base-table.hh \ + hb-ot-layout-common.hh \ + hb-ot-layout-gdef-table.hh \ + hb-ot-layout-gpos-table.hh \ + hb-ot-layout-gsub-table.hh \ + OT/glyf/glyf.hh \ + OT/glyf/glyf-helpers.hh \ + OT/glyf/loca.hh \ + OT/glyf/path-builder.hh \ + OT/glyf/Glyph.hh \ + OT/glyf/GlyphHeader.hh \ + OT/glyf/SimpleGlyph.hh \ + OT/glyf/CompositeGlyph.hh \ + OT/glyf/SubsetGlyph.hh \ + OT/Layout/types.hh \ + OT/Layout/Common/Coverage.hh \ + OT/Layout/Common/CoverageFormat1.hh \ + OT/Layout/Common/CoverageFormat2.hh \ + OT/Layout/Common/RangeRecord.hh \ + OT/Layout/GPOS/AnchorFormat1.hh \ + OT/Layout/GPOS/AnchorFormat2.hh \ + OT/Layout/GPOS/AnchorFormat3.hh \ + OT/Layout/GPOS/Anchor.hh \ + OT/Layout/GPOS/AnchorMatrix.hh \ + OT/Layout/GPOS/ChainContextPos.hh \ + OT/Layout/GPOS/Common.hh \ + OT/Layout/GPOS/ContextPos.hh \ + OT/Layout/GPOS/CursivePosFormat1.hh \ + OT/Layout/GPOS/CursivePos.hh \ + OT/Layout/GPOS/ExtensionPos.hh \ + OT/Layout/GPOS/GPOS.hh \ + OT/Layout/GPOS/LigatureArray.hh \ + OT/Layout/GPOS/MarkArray.hh \ + OT/Layout/GPOS/MarkBasePosFormat1.hh \ + OT/Layout/GPOS/MarkBasePos.hh \ + OT/Layout/GPOS/MarkLigPosFormat1.hh \ + OT/Layout/GPOS/MarkLigPos.hh \ + OT/Layout/GPOS/MarkMarkPosFormat1.hh \ + OT/Layout/GPOS/MarkMarkPos.hh \ + OT/Layout/GPOS/MarkRecord.hh \ + OT/Layout/GPOS/PairPosFormat1.hh \ + OT/Layout/GPOS/PairPosFormat2.hh \ + OT/Layout/GPOS/PairPos.hh \ + OT/Layout/GPOS/PairSet.hh \ + OT/Layout/GPOS/PairValueRecord.hh \ + OT/Layout/GPOS/PosLookup.hh \ + OT/Layout/GPOS/PosLookupSubTable.hh \ + OT/Layout/GPOS/SinglePosFormat1.hh \ + OT/Layout/GPOS/SinglePosFormat2.hh \ + OT/Layout/GPOS/SinglePos.hh \ + OT/Layout/GPOS/ValueFormat.hh \ + OT/Layout/GSUB/AlternateSet.hh \ + OT/Layout/GSUB/AlternateSubstFormat1.hh \ + OT/Layout/GSUB/AlternateSubst.hh \ + OT/Layout/GSUB/ChainContextSubst.hh \ + OT/Layout/GSUB/Common.hh \ + OT/Layout/GSUB/ContextSubst.hh \ + OT/Layout/GSUB/ExtensionSubst.hh \ + OT/Layout/GSUB/GSUB.hh \ + OT/Layout/GSUB/Ligature.hh \ + OT/Layout/GSUB/LigatureSet.hh \ + OT/Layout/GSUB/LigatureSubstFormat1.hh \ + OT/Layout/GSUB/LigatureSubst.hh \ + OT/Layout/GSUB/MultipleSubstFormat1.hh \ + OT/Layout/GSUB/MultipleSubst.hh \ + OT/Layout/GSUB/ReverseChainSingleSubstFormat1.hh \ + OT/Layout/GSUB/ReverseChainSingleSubst.hh \ + OT/Layout/GSUB/Sequence.hh \ + OT/Layout/GSUB/SingleSubstFormat1.hh \ + OT/Layout/GSUB/SingleSubstFormat2.hh \ + OT/Layout/GSUB/SingleSubst.hh \ + OT/Layout/GSUB/SubstLookup.hh \ + OT/Layout/GSUB/SubstLookupSubTable.hh \ + hb-ot-layout-gsubgpos.hh \ + hb-ot-layout-jstf-table.hh \ + hb-ot-layout.cc \ + hb-ot-layout.hh \ + hb-ot-map.cc \ + hb-ot-map.hh \ + hb-ot-math-table.hh \ + hb-ot-math.cc \ + hb-ot-maxp-table.hh \ + hb-ot-meta-table.hh \ + hb-ot-meta.cc \ + hb-ot-metrics.cc \ + hb-ot-metrics.hh \ + hb-ot-name-language-static.hh \ + hb-ot-name-language.hh \ + hb-ot-name-table.hh \ + hb-ot-name.cc \ + hb-ot-os2-table.hh \ + hb-ot-os2-unicode-ranges.hh \ + hb-ot-post-macroman.hh \ + hb-ot-post-table.hh \ + hb-ot-shaper-arabic-fallback.hh \ + hb-ot-shaper-arabic-joining-list.hh \ + hb-ot-shaper-arabic-pua.hh \ + hb-ot-shaper-arabic-table.hh \ + hb-ot-shaper-arabic-win1256.hh \ + hb-ot-shaper-arabic.cc \ + hb-ot-shaper-arabic.hh \ + hb-ot-shaper-default.cc \ + hb-ot-shaper-hangul.cc \ + hb-ot-shaper-hebrew.cc \ + hb-ot-shaper-indic-table.cc \ + hb-ot-shaper-indic.cc \ + hb-ot-shaper-indic.hh \ + hb-ot-shaper-khmer.cc \ + hb-ot-shaper-myanmar.cc \ + hb-ot-shaper-syllabic.cc \ + hb-ot-shaper-syllabic.hh \ + hb-ot-shaper-thai.cc \ + hb-ot-shaper-use-table.hh \ + hb-ot-shaper-use.cc \ + hb-ot-shaper-vowel-constraints.cc \ + hb-ot-shaper-vowel-constraints.hh \ + hb-ot-shaper.hh \ + hb-ot-shape-fallback.cc \ + hb-ot-shape-fallback.hh \ + hb-ot-shape-normalize.cc \ + hb-ot-shape-normalize.hh \ + hb-ot-shape.cc \ + hb-ot-shape.hh \ + hb-ot-stat-table.hh \ + hb-ot-tag-table.hh \ + hb-ot-tag.cc \ + hb-ot-var-avar-table.hh \ + hb-ot-var-common.hh \ + hb-ot-var-fvar-table.hh \ + hb-ot-var-gvar-table.hh \ + hb-ot-var-hvar-table.hh \ + hb-ot-var-mvar-table.hh \ + hb-ot-var.cc \ + hb-ot-vorg-table.hh \ + hb-pool.hh \ + hb-sanitize.hh \ + hb-serialize.hh \ + hb-set-digest.hh \ + hb-set.cc \ + hb-set.hh \ + hb-shape-plan.cc \ + hb-shape-plan.hh \ + hb-shape.cc \ + hb-shaper-impl.hh \ + hb-shaper-list.hh \ + hb-shaper.cc \ + hb-shaper.hh \ + hb-static.cc \ + hb-string-array.hh \ + hb-style.cc \ + hb-ucd-table.hh \ + hb-ucd.cc \ + hb-unicode-emoji-table.hh \ + hb-unicode.cc \ + hb-unicode.hh \ + hb-utf.hh \ + hb-vector.hh \ + hb-priority-queue.hh \ + hb.hh \ + $(NULL) + +HB_BASE_RAGEL_GENERATED_sources = \ + hb-buffer-deserialize-json.hh \ + hb-buffer-deserialize-text.hh \ + hb-number-parser.hh \ + hb-ot-shaper-indic-machine.hh \ + hb-ot-shaper-khmer-machine.hh \ + hb-ot-shaper-myanmar-machine.hh \ + hb-ot-shaper-use-machine.hh \ + $(NULL) +HB_BASE_RAGEL_sources = \ + hb-buffer-deserialize-json.rl \ + hb-buffer-deserialize-text.rl \ + hb-number-parser.rl \ + hb-ot-shaper-indic-machine.rl \ + hb-ot-shaper-khmer-machine.rl \ + hb-ot-shaper-myanmar-machine.rl \ + hb-ot-shaper-use-machine.rl \ + $(NULL) + +HB_BASE_headers = \ + hb-aat-layout.h \ + hb-aat.h \ + hb-blob.h \ + hb-buffer.h \ + hb-common.h \ + hb-cplusplus.hh \ + hb-deprecated.h \ + hb-draw.h \ + hb-face.h \ + hb-font.h \ + hb-map.h \ + hb-ot-color.h \ + hb-ot-deprecated.h \ + hb-ot-font.h \ + hb-ot-layout.h \ + hb-ot-math.h \ + hb-ot-meta.h \ + hb-ot-metrics.h \ + hb-ot-name.h \ + hb-ot-shape.h \ + hb-ot-var.h \ + hb-ot.h \ + hb-set.h \ + hb-shape-plan.h \ + hb-shape.h \ + hb-style.h \ + hb-unicode.h \ + hb-version.h \ + hb.h \ + $(NULL) + +# Optional Sources and Headers with external deps + +HB_FT_sources = hb-ft.cc +HB_FT_headers = hb-ft.h + +HB_GLIB_sources = hb-glib.cc +HB_GLIB_headers = hb-glib.h + +HB_GRAPHITE2_sources = hb-graphite2.cc +HB_GRAPHITE2_headers = hb-graphite2.h + +# System-dependent sources and headers + +HB_CORETEXT_sources = hb-coretext.cc +HB_CORETEXT_headers = hb-coretext.h + +HB_DIRECTWRITE_sources = hb-directwrite.cc +HB_DIRECTWRITE_headers = hb-directwrite.h + +HB_GDI_sources = hb-gdi.cc +HB_GDI_headers = hb-gdi.h + +HB_UNISCRIBE_sources = hb-uniscribe.cc +HB_UNISCRIBE_headers = hb-uniscribe.h + +# Sources for libharfbuzz-gobject and libharfbuzz-icu +HB_ICU_sources = hb-icu.cc +HB_ICU_headers = hb-icu.h + +# Sources for libharfbuzz-subset +HB_SUBSET_sources = \ + hb-number.cc \ + hb-number.hh \ + hb-ot-cff1-table.cc \ + hb-ot-cff2-table.cc \ + hb-ot-color-colrv1-closure.hh \ + hb-ot-post-table-v2subset.hh \ + hb-static.cc \ + hb-subset-cff-common.cc \ + hb-subset-cff-common.hh \ + hb-subset-cff1.cc \ + hb-subset-cff1.hh \ + hb-subset-cff2.cc \ + hb-subset-cff2.hh \ + hb-subset-input.cc \ + hb-subset-input.hh \ + hb-subset-accelerator.hh \ + hb-subset-plan.cc \ + hb-subset-plan.hh \ + hb-subset-repacker.cc \ + hb-subset.cc \ + hb-subset.hh \ + hb-repacker.hh \ + graph/graph.hh \ + graph/gsubgpos-graph.hh \ + graph/gsubgpos-context.hh \ + graph/gsubgpos-context.cc \ + graph/coverage-graph.hh \ + graph/classdef-graph.hh \ + graph/pairpos-graph.hh \ + graph/markbasepos-graph.hh \ + graph/split-helpers.hh \ + graph/serialize.hh \ + $(NULL) + +HB_SUBSET_headers = \ + hb-subset.h \ + hb-subset-repacker.h \ + $(NULL) + +HB_GOBJECT_DIST_sources = hb-gobject-structs.cc +HB_GOBJECT_DIST_headers = hb-gobject.h hb-gobject-structs.h +HB_GOBJECT_ENUM_sources = hb-gobject-enums.cc +HB_GOBJECT_ENUM_headers = hb-gobject-enums.h +HB_GOBJECT_NODIST_sources = $(HB_GOBJECT_ENUM_sources) +HB_GOBJECT_NODIST_headers = $(HB_GOBJECT_ENUM_headers) +HB_GOBJECT_sources = $(HB_GOBJECT_DIST_sources) $(HB_GOBJECT_NODIST_sources) +HB_GOBJECT_headers = $(HB_GOBJECT_DIST_headers) $(HB_GOBJECT_NODIST_headers) diff --git a/libs/harfbuzz/src/OT/Layout/Common/Coverage.hh b/libs/harfbuzz/src/OT/Layout/Common/Coverage.hh new file mode 100644 index 00000000..eef89a28 --- /dev/null +++ b/libs/harfbuzz/src/OT/Layout/Common/Coverage.hh @@ -0,0 +1,338 @@ +/* + * Copyright © 2007,2008,2009 Red Hat, Inc. + * Copyright © 2010,2012 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. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod, Garret Rieger + */ + +#ifndef OT_LAYOUT_COMMON_COVERAGE_HH +#define OT_LAYOUT_COMMON_COVERAGE_HH + +#include "../types.hh" +#include "CoverageFormat1.hh" +#include "CoverageFormat2.hh" + +namespace OT { +namespace Layout { +namespace Common { + +template +static inline void Coverage_serialize (hb_serialize_context_t *c, + Iterator it); + +struct Coverage +{ + + protected: + union { + HBUINT16 format; /* Format identifier */ + CoverageFormat1_3 format1; + CoverageFormat2_4 format2; +#ifndef HB_NO_BORING_EXPANSION + CoverageFormat1_3format3; + CoverageFormat2_4format4; +#endif + } u; + public: + DEFINE_SIZE_UNION (2, format); + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (!u.format.sanitize (c)) return_trace (false); + switch (u.format) + { + case 1: return_trace (u.format1.sanitize (c)); + case 2: return_trace (u.format2.sanitize (c)); +#ifndef HB_NO_BORING_EXPANSION + case 3: return_trace (u.format3.sanitize (c)); + case 4: return_trace (u.format4.sanitize (c)); +#endif + default:return_trace (true); + } + } + + /* Has interface. */ + static constexpr unsigned SENTINEL = NOT_COVERED; + typedef unsigned int value_t; + value_t operator [] (hb_codepoint_t k) const { return get (k); } + bool has (hb_codepoint_t k) const { return (*this)[k] != SENTINEL; } + /* Predicate. */ + bool operator () (hb_codepoint_t k) const { return has (k); } + + unsigned int get (hb_codepoint_t k) const { return get_coverage (k); } + unsigned int get_coverage (hb_codepoint_t glyph_id) const + { + switch (u.format) { + case 1: return u.format1.get_coverage (glyph_id); + case 2: return u.format2.get_coverage (glyph_id); +#ifndef HB_NO_BORING_EXPANSION + case 3: return u.format3.get_coverage (glyph_id); + case 4: return u.format4.get_coverage (glyph_id); +#endif + default:return NOT_COVERED; + } + } + + unsigned get_population () const + { + switch (u.format) { + case 1: return u.format1.get_population (); + case 2: return u.format2.get_population (); +#ifndef HB_NO_BORING_EXPANSION + case 3: return u.format3.get_population (); + case 4: return u.format4.get_population (); +#endif + default:return NOT_COVERED; + } + } + + template + bool serialize (hb_serialize_context_t *c, Iterator glyphs) + { + TRACE_SERIALIZE (this); + if (unlikely (!c->extend_min (this))) return_trace (false); + + unsigned count = 0; + unsigned num_ranges = 0; + hb_codepoint_t last = (hb_codepoint_t) -2; + for (auto g: glyphs) + { + if (last + 1 != g) + num_ranges++; + last = g; + count++; + } + u.format = count <= num_ranges * 3 ? 1 : 2; + +#ifndef HB_NO_BORING_EXPANSION + if (count && last > 0xFFFFu) + u.format += 2; +#endif + + switch (u.format) + { + case 1: return_trace (u.format1.serialize (c, glyphs)); + case 2: return_trace (u.format2.serialize (c, glyphs)); +#ifndef HB_NO_BORING_EXPANSION + case 3: return_trace (u.format3.serialize (c, glyphs)); + case 4: return_trace (u.format4.serialize (c, glyphs)); +#endif + default:return_trace (false); + } + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto it = + + iter () + | hb_filter (c->plan->glyph_map_gsub) + | hb_map_retains_sorting (c->plan->glyph_map_gsub) + ; + + // Cache the iterator result as it will be iterated multiple times + // by the serialize code below. + hb_sorted_vector_t glyphs (it); + Coverage_serialize (c->serializer, glyphs.iter ()); + return_trace (bool (glyphs)); + } + + bool intersects (const hb_set_t *glyphs) const + { + switch (u.format) + { + case 1: return u.format1.intersects (glyphs); + case 2: return u.format2.intersects (glyphs); +#ifndef HB_NO_BORING_EXPANSION + case 3: return u.format3.intersects (glyphs); + case 4: return u.format4.intersects (glyphs); +#endif + default:return false; + } + } + bool intersects_coverage (const hb_set_t *glyphs, unsigned int index) const + { + switch (u.format) + { + case 1: return u.format1.intersects_coverage (glyphs, index); + case 2: return u.format2.intersects_coverage (glyphs, index); +#ifndef HB_NO_BORING_EXPANSION + case 3: return u.format3.intersects_coverage (glyphs, index); + case 4: return u.format4.intersects_coverage (glyphs, index); +#endif + default:return false; + } + } + + /* Might return false if array looks unsorted. + * Used for faster rejection of corrupt data. */ + template + bool collect_coverage (set_t *glyphs) const + { + switch (u.format) + { + case 1: return u.format1.collect_coverage (glyphs); + case 2: return u.format2.collect_coverage (glyphs); +#ifndef HB_NO_BORING_EXPANSION + case 3: return u.format3.collect_coverage (glyphs); + case 4: return u.format4.collect_coverage (glyphs); +#endif + default:return false; + } + } + + template + void intersect_set (const hb_set_t &glyphs, IterableOut&& intersect_glyphs) const + { + switch (u.format) + { + case 1: return u.format1.intersect_set (glyphs, intersect_glyphs); + case 2: return u.format2.intersect_set (glyphs, intersect_glyphs); +#ifndef HB_NO_BORING_EXPANSION + case 3: return u.format3.intersect_set (glyphs, intersect_glyphs); + case 4: return u.format4.intersect_set (glyphs, intersect_glyphs); +#endif + default:return ; + } + } + + struct iter_t : hb_iter_with_fallback_t + { + static constexpr bool is_sorted_iterator = true; + iter_t (const Coverage &c_ = Null (Coverage)) + { + memset (this, 0, sizeof (*this)); + format = c_.u.format; + switch (format) + { + case 1: u.format1.init (c_.u.format1); return; + case 2: u.format2.init (c_.u.format2); return; +#ifndef HB_NO_BORING_EXPANSION + case 3: u.format3.init (c_.u.format3); return; + case 4: u.format4.init (c_.u.format4); return; +#endif + default: return; + } + } + bool __more__ () const + { + switch (format) + { + case 1: return u.format1.__more__ (); + case 2: return u.format2.__more__ (); +#ifndef HB_NO_BORING_EXPANSION + case 3: return u.format3.__more__ (); + case 4: return u.format4.__more__ (); +#endif + default:return false; + } + } + void __next__ () + { + switch (format) + { + case 1: u.format1.__next__ (); break; + case 2: u.format2.__next__ (); break; +#ifndef HB_NO_BORING_EXPANSION + case 3: u.format3.__next__ (); break; + case 4: u.format4.__next__ (); break; +#endif + default: break; + } + } + typedef hb_codepoint_t __item_t__; + __item_t__ __item__ () const { return get_glyph (); } + + hb_codepoint_t get_glyph () const + { + switch (format) + { + case 1: return u.format1.get_glyph (); + case 2: return u.format2.get_glyph (); +#ifndef HB_NO_BORING_EXPANSION + case 3: return u.format3.get_glyph (); + case 4: return u.format4.get_glyph (); +#endif + default:return 0; + } + } + bool operator != (const iter_t& o) const + { + if (unlikely (format != o.format)) return true; + switch (format) + { + case 1: return u.format1 != o.u.format1; + case 2: return u.format2 != o.u.format2; +#ifndef HB_NO_BORING_EXPANSION + case 3: return u.format3 != o.u.format3; + case 4: return u.format4 != o.u.format4; +#endif + default:return false; + } + } + iter_t __end__ () const + { + iter_t it = {}; + it.format = format; + switch (format) + { + case 1: it.u.format1 = u.format1.__end__ (); break; + case 2: it.u.format2 = u.format2.__end__ (); break; +#ifndef HB_NO_BORING_EXPANSION + case 3: it.u.format3 = u.format3.__end__ (); break; + case 4: it.u.format4 = u.format4.__end__ (); break; +#endif + default: break; + } + return it; + } + + private: + unsigned int format; + union { +#ifndef HB_NO_BORING_EXPANSION + CoverageFormat2_4::iter_t format4; /* Put this one first since it's larger; helps shut up compiler. */ + CoverageFormat1_3::iter_t format3; +#endif + CoverageFormat2_4::iter_t format2; /* Put this one first since it's larger; helps shut up compiler. */ + CoverageFormat1_3::iter_t format1; + } u; + }; + iter_t iter () const { return iter_t (*this); } +}; + +template +static inline void +Coverage_serialize (hb_serialize_context_t *c, + Iterator it) +{ c->start_embed ()->serialize (c, it); } + +} +} +} + +#endif // #ifndef OT_LAYOUT_COMMON_COVERAGE_HH diff --git a/libs/harfbuzz/src/OT/Layout/Common/CoverageFormat1.hh b/libs/harfbuzz/src/OT/Layout/Common/CoverageFormat1.hh new file mode 100644 index 00000000..82fd48dc --- /dev/null +++ b/libs/harfbuzz/src/OT/Layout/Common/CoverageFormat1.hh @@ -0,0 +1,126 @@ +/* + * Copyright © 2007,2008,2009 Red Hat, Inc. + * Copyright © 2010,2012 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. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod, Garret Rieger + */ + + +#ifndef OT_LAYOUT_COMMON_COVERAGEFORMAT1_HH +#define OT_LAYOUT_COMMON_COVERAGEFORMAT1_HH + +namespace OT { +namespace Layout { +namespace Common { + +#define NOT_COVERED ((unsigned int) -1) + +template +struct CoverageFormat1_3 +{ + friend struct Coverage; + + protected: + HBUINT16 coverageFormat; /* Format identifier--format = 1 */ + SortedArray16Of + glyphArray; /* Array of GlyphIDs--in numerical order */ + public: + DEFINE_SIZE_ARRAY (4, glyphArray); + + private: + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (glyphArray.sanitize (c)); + } + + unsigned int get_coverage (hb_codepoint_t glyph_id) const + { + unsigned int i; + glyphArray.bfind (glyph_id, &i, HB_NOT_FOUND_STORE, NOT_COVERED); + return i; + } + + unsigned get_population () const + { + return glyphArray.len; + } + + template + bool serialize (hb_serialize_context_t *c, Iterator glyphs) + { + TRACE_SERIALIZE (this); + return_trace (glyphArray.serialize (c, glyphs)); + } + + bool intersects (const hb_set_t *glyphs) const + { + /* TODO Speed up, using hb_set_next() and bsearch()? */ + for (const auto& g : glyphArray.as_array ()) + if (glyphs->has (g)) + return true; + return false; + } + bool intersects_coverage (const hb_set_t *glyphs, unsigned int index) const + { return glyphs->has (glyphArray[index]); } + + template + void intersect_set (const hb_set_t &glyphs, IterableOut&& intersect_glyphs) const + { + unsigned count = glyphArray.len; + for (unsigned i = 0; i < count; i++) + if (glyphs.has (glyphArray[i])) + intersect_glyphs << glyphArray[i]; + } + + template + bool collect_coverage (set_t *glyphs) const + { return glyphs->add_sorted_array (glyphArray.as_array ()); } + + public: + /* Older compilers need this to be public. */ + struct iter_t + { + void init (const struct CoverageFormat1_3 &c_) { c = &c_; i = 0; } + bool __more__ () const { return i < c->glyphArray.len; } + void __next__ () { i++; } + hb_codepoint_t get_glyph () const { return c->glyphArray[i]; } + bool operator != (const iter_t& o) const + { return i != o.i; } + iter_t __end__ () const { iter_t it; it.init (*c); it.i = c->glyphArray.len; return it; } + + private: + const struct CoverageFormat1_3 *c; + unsigned int i; + }; + private: +}; + +} +} +} + +#endif // #ifndef OT_LAYOUT_COMMON_COVERAGEFORMAT1_HH diff --git a/libs/harfbuzz/src/OT/Layout/Common/CoverageFormat2.hh b/libs/harfbuzz/src/OT/Layout/Common/CoverageFormat2.hh new file mode 100644 index 00000000..974d0946 --- /dev/null +++ b/libs/harfbuzz/src/OT/Layout/Common/CoverageFormat2.hh @@ -0,0 +1,233 @@ +/* + * Copyright © 2007,2008,2009 Red Hat, Inc. + * Copyright © 2010,2012 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. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod, Garret Rieger + */ + +#ifndef OT_LAYOUT_COMMON_COVERAGEFORMAT2_HH +#define OT_LAYOUT_COMMON_COVERAGEFORMAT2_HH + +#include "RangeRecord.hh" + +namespace OT { +namespace Layout { +namespace Common { + +template +struct CoverageFormat2_4 +{ + friend struct Coverage; + + protected: + HBUINT16 coverageFormat; /* Format identifier--format = 2 */ + SortedArray16Of> + rangeRecord; /* Array of glyph ranges--ordered by + * Start GlyphID. rangeCount entries + * long */ + public: + DEFINE_SIZE_ARRAY (4, rangeRecord); + + private: + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (rangeRecord.sanitize (c)); + } + + unsigned int get_coverage (hb_codepoint_t glyph_id) const + { + const RangeRecord &range = rangeRecord.bsearch (glyph_id); + return likely (range.first <= range.last) + ? (unsigned int) range.value + (glyph_id - range.first) + : NOT_COVERED; + } + + unsigned get_population () const + { + typename Types::large_int ret = 0; + for (const auto &r : rangeRecord) + ret += r.get_population (); + return ret > UINT_MAX ? UINT_MAX : (unsigned) ret; + } + + template + bool serialize (hb_serialize_context_t *c, Iterator glyphs) + { + TRACE_SERIALIZE (this); + if (unlikely (!c->extend_min (this))) return_trace (false); + + /* TODO(iter) Write more efficiently? */ + + unsigned num_ranges = 0; + hb_codepoint_t last = (hb_codepoint_t) -2; + for (auto g: glyphs) + { + if (last + 1 != g) + num_ranges++; + last = g; + } + + if (unlikely (!rangeRecord.serialize (c, num_ranges))) return_trace (false); + if (!num_ranges) return_trace (true); + + unsigned count = 0; + unsigned range = (unsigned) -1; + last = (hb_codepoint_t) -2; + for (auto g: glyphs) + { + if (last + 1 != g) + { + range++; + rangeRecord[range].first = g; + rangeRecord[range].value = count; + } + rangeRecord[range].last = g; + last = g; + count++; + } + + return_trace (true); + } + + bool intersects (const hb_set_t *glyphs) const + { + return hb_any (+ hb_iter (rangeRecord) + | hb_map ([glyphs] (const RangeRecord &range) { return range.intersects (*glyphs); })); + } + bool intersects_coverage (const hb_set_t *glyphs, unsigned int index) const + { + auto cmp = [] (const void *pk, const void *pr) -> int + { + unsigned index = * (const unsigned *) pk; + const RangeRecord &range = * (const RangeRecord *) pr; + if (index < range.value) return -1; + if (index > (unsigned int) range.value + (range.last - range.first)) return +1; + return 0; + }; + + auto arr = rangeRecord.as_array (); + unsigned idx; + if (hb_bsearch_impl (&idx, index, + arr.arrayZ, arr.length, sizeof (arr[0]), + (int (*)(const void *_key, const void *_item)) cmp)) + return arr.arrayZ[idx].intersects (*glyphs); + return false; + } + + template + void intersect_set (const hb_set_t &glyphs, IterableOut&& intersect_glyphs) const + { + for (const auto& range : rangeRecord) + { + hb_codepoint_t last = range.last; + for (hb_codepoint_t g = range.first - 1; + glyphs.next (&g) && g <= last;) + intersect_glyphs << g; + } + } + + template + bool collect_coverage (set_t *glyphs) const + { + for (const auto& range: rangeRecord) + if (unlikely (!range.collect_coverage (glyphs))) + return false; + return true; + } + + public: + /* Older compilers need this to be public. */ + struct iter_t + { + void init (const CoverageFormat2_4 &c_) + { + c = &c_; + coverage = 0; + i = 0; + j = c->rangeRecord.len ? c->rangeRecord[0].first : 0; + if (unlikely (c->rangeRecord[0].first > c->rangeRecord[0].last)) + { + /* Broken table. Skip. */ + i = c->rangeRecord.len; + j = 0; + } + } + bool __more__ () const { return i < c->rangeRecord.len; } + void __next__ () + { + if (j >= c->rangeRecord[i].last) + { + i++; + if (__more__ ()) + { + unsigned int old = coverage; + j = c->rangeRecord[i].first; + coverage = c->rangeRecord[i].value; + if (unlikely (coverage != old + 1)) + { + /* Broken table. Skip. Important to avoid DoS. + * Also, our callers depend on coverage being + * consecutive and monotonically increasing, + * ie. iota(). */ + i = c->rangeRecord.len; + j = 0; + return; + } + } + else + j = 0; + return; + } + coverage++; + j++; + } + hb_codepoint_t get_glyph () const { return j; } + bool operator != (const iter_t& o) const + { return i != o.i || j != o.j; } + iter_t __end__ () const + { + iter_t it; + it.init (*c); + it.i = c->rangeRecord.len; + it.j = 0; + return it; + } + + private: + const struct CoverageFormat2_4 *c; + unsigned int i, coverage; + hb_codepoint_t j; + }; + private: +}; + +} +} +} + +#endif // #ifndef OT_LAYOUT_COMMON_COVERAGEFORMAT2_HH diff --git a/libs/harfbuzz/src/OT/Layout/Common/RangeRecord.hh b/libs/harfbuzz/src/OT/Layout/Common/RangeRecord.hh new file mode 100644 index 00000000..a62629fa --- /dev/null +++ b/libs/harfbuzz/src/OT/Layout/Common/RangeRecord.hh @@ -0,0 +1,85 @@ +/* + * Copyright © 2007,2008,2009 Red Hat, Inc. + * Copyright © 2010,2012 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. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod, Garret Rieger + */ + +#ifndef OT_LAYOUT_COMMON_RANGERECORD_HH +#define OT_LAYOUT_COMMON_RANGERECORD_HH + +namespace OT { +namespace Layout { +namespace Common { + +template +struct RangeRecord +{ + typename Types::HBGlyphID first; /* First GlyphID in the range */ + typename Types::HBGlyphID last; /* Last GlyphID in the range */ + HBUINT16 value; /* Value */ + + DEFINE_SIZE_STATIC (2 + 2 * Types::size); + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + int cmp (hb_codepoint_t g) const + { return g < first ? -1 : g <= last ? 0 : +1; } + + unsigned get_population () const + { + if (unlikely (last < first)) return 0; + return (last - first + 1); + } + + bool intersects (const hb_set_t &glyphs) const + { return glyphs.intersects (first, last); } + + template + bool collect_coverage (set_t *glyphs) const + { return glyphs->add_range (first, last); } +}; + +} +} +} + +// TODO(garretrieger): This was previously implemented using +// DECLARE_NULL_NAMESPACE_BYTES_TEMPLATE1 (OT, RangeRecord, 9); +// but that only works when there is only a single namespace level. +// The macro should probably be fixed so it can work in this situation. +extern HB_INTERNAL const unsigned char _hb_Null_OT_RangeRecord[9]; +template +struct Null> { + static OT::Layout::Common::RangeRecord const & get_null () { + return *reinterpret_cast *> (_hb_Null_OT_RangeRecord); + } +}; + + +#endif // #ifndef OT_LAYOUT_COMMON_RANGERECORD_HH diff --git a/libs/harfbuzz/src/OT/Layout/GPOS/Anchor.hh b/libs/harfbuzz/src/OT/Layout/GPOS/Anchor.hh new file mode 100644 index 00000000..49e76e77 --- /dev/null +++ b/libs/harfbuzz/src/OT/Layout/GPOS/Anchor.hh @@ -0,0 +1,83 @@ +#ifndef OT_LAYOUT_GPOS_ANCHOR_HH +#define OT_LAYOUT_GPOS_ANCHOR_HH + +#include "AnchorFormat1.hh" +#include "AnchorFormat2.hh" +#include "AnchorFormat3.hh" + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +struct Anchor +{ + protected: + union { + HBUINT16 format; /* Format identifier */ + AnchorFormat1 format1; + AnchorFormat2 format2; + AnchorFormat3 format3; + } u; + public: + DEFINE_SIZE_UNION (2, format); + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (!u.format.sanitize (c)) return_trace (false); + switch (u.format) { + case 1: return_trace (u.format1.sanitize (c)); + case 2: return_trace (u.format2.sanitize (c)); + case 3: return_trace (u.format3.sanitize (c)); + default:return_trace (true); + } + } + + void get_anchor (hb_ot_apply_context_t *c, hb_codepoint_t glyph_id, + float *x, float *y) const + { + *x = *y = 0; + switch (u.format) { + case 1: u.format1.get_anchor (c, glyph_id, x, y); return; + case 2: u.format2.get_anchor (c, glyph_id, x, y); return; + case 3: u.format3.get_anchor (c, glyph_id, x, y); return; + default: return; + } + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + switch (u.format) { + case 1: return_trace (bool (reinterpret_cast (u.format1.copy (c->serializer)))); + case 2: + if (c->plan->flags & HB_SUBSET_FLAGS_NO_HINTING) + { + // AnchorFormat 2 just containins extra hinting information, so + // if hints are being dropped convert to format 1. + return_trace (bool (reinterpret_cast (u.format1.copy (c->serializer)))); + } + return_trace (bool (reinterpret_cast (u.format2.copy (c->serializer)))); + case 3: return_trace (u.format3.subset (c)); + default:return_trace (false); + } + } + + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const + { + switch (u.format) { + case 1: case 2: + return; + case 3: + u.format3.collect_variation_indices (c); + return; + default: return; + } + } +}; + +} +} +} + +#endif // OT_LAYOUT_GPOS_ANCHOR_HH diff --git a/libs/harfbuzz/src/OT/Layout/GPOS/AnchorFormat1.hh b/libs/harfbuzz/src/OT/Layout/GPOS/AnchorFormat1.hh new file mode 100644 index 00000000..738cc31b --- /dev/null +++ b/libs/harfbuzz/src/OT/Layout/GPOS/AnchorFormat1.hh @@ -0,0 +1,46 @@ +#ifndef OT_LAYOUT_GPOS_ANCHORFORMAT1_HH +#define OT_LAYOUT_GPOS_ANCHORFORMAT1_HH + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +struct AnchorFormat1 +{ + protected: + HBUINT16 format; /* Format identifier--format = 1 */ + FWORD xCoordinate; /* Horizontal value--in design units */ + FWORD yCoordinate; /* Vertical value--in design units */ + public: + DEFINE_SIZE_STATIC (6); + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + void get_anchor (hb_ot_apply_context_t *c, hb_codepoint_t glyph_id HB_UNUSED, + float *x, float *y) const + { + hb_font_t *font = c->font; + *x = font->em_fscale_x (xCoordinate); + *y = font->em_fscale_y (yCoordinate); + } + + AnchorFormat1* copy (hb_serialize_context_t *c) const + { + TRACE_SERIALIZE (this); + AnchorFormat1* out = c->embed (this); + if (!out) return_trace (out); + out->format = 1; + return_trace (out); + } +}; + + +} +} +} + +#endif // OT_LAYOUT_GPOS_ANCHORFORMAT1_HH diff --git a/libs/harfbuzz/src/OT/Layout/GPOS/AnchorFormat2.hh b/libs/harfbuzz/src/OT/Layout/GPOS/AnchorFormat2.hh new file mode 100644 index 00000000..70b4d19f --- /dev/null +++ b/libs/harfbuzz/src/OT/Layout/GPOS/AnchorFormat2.hh @@ -0,0 +1,58 @@ +#ifndef OT_LAYOUT_GPOS_ANCHORFORMAT2_HH +#define OT_LAYOUT_GPOS_ANCHORFORMAT2_HH + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +struct AnchorFormat2 +{ + + protected: + HBUINT16 format; /* Format identifier--format = 2 */ + FWORD xCoordinate; /* Horizontal value--in design units */ + FWORD yCoordinate; /* Vertical value--in design units */ + HBUINT16 anchorPoint; /* Index to glyph contour point */ + public: + DEFINE_SIZE_STATIC (8); + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + void get_anchor (hb_ot_apply_context_t *c, hb_codepoint_t glyph_id, + float *x, float *y) const + { + hb_font_t *font = c->font; + +#ifdef HB_NO_HINTING + *x = font->em_fscale_x (xCoordinate); + *y = font->em_fscale_y (yCoordinate); + return; +#endif + + unsigned int x_ppem = font->x_ppem; + unsigned int y_ppem = font->y_ppem; + hb_position_t cx = 0, cy = 0; + bool ret; + + ret = (x_ppem || y_ppem) && + font->get_glyph_contour_point_for_origin (glyph_id, anchorPoint, HB_DIRECTION_LTR, &cx, &cy); + *x = ret && x_ppem ? cx : font->em_fscale_x (xCoordinate); + *y = ret && y_ppem ? cy : font->em_fscale_y (yCoordinate); + } + + AnchorFormat2* copy (hb_serialize_context_t *c) const + { + TRACE_SERIALIZE (this); + return_trace (c->embed (this)); + } +}; + +} +} +} + +#endif // OT_LAYOUT_GPOS_ANCHORFORMAT2_HH diff --git a/libs/harfbuzz/src/OT/Layout/GPOS/AnchorFormat3.hh b/libs/harfbuzz/src/OT/Layout/GPOS/AnchorFormat3.hh new file mode 100644 index 00000000..2e30ab33 --- /dev/null +++ b/libs/harfbuzz/src/OT/Layout/GPOS/AnchorFormat3.hh @@ -0,0 +1,100 @@ +#ifndef OT_LAYOUT_GPOS_ANCHORFORMAT3_HH +#define OT_LAYOUT_GPOS_ANCHORFORMAT3_HH + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +struct AnchorFormat3 +{ + protected: + HBUINT16 format; /* Format identifier--format = 3 */ + FWORD xCoordinate; /* Horizontal value--in design units */ + FWORD yCoordinate; /* Vertical value--in design units */ + Offset16To + xDeviceTable; /* Offset to Device table for X + * coordinate-- from beginning of + * Anchor table (may be NULL) */ + Offset16To + yDeviceTable; /* Offset to Device table for Y + * coordinate-- from beginning of + * Anchor table (may be NULL) */ + public: + DEFINE_SIZE_STATIC (10); + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && xDeviceTable.sanitize (c, this) && yDeviceTable.sanitize (c, this)); + } + + void get_anchor (hb_ot_apply_context_t *c, hb_codepoint_t glyph_id HB_UNUSED, + float *x, float *y) const + { + hb_font_t *font = c->font; + *x = font->em_fscale_x (xCoordinate); + *y = font->em_fscale_y (yCoordinate); + + if (font->x_ppem || font->num_coords) + *x += (this+xDeviceTable).get_x_delta (font, c->var_store, c->var_store_cache); + if (font->y_ppem || font->num_coords) + *y += (this+yDeviceTable).get_y_delta (font, c->var_store, c->var_store_cache); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->start_embed (*this); + if (unlikely (!out)) return_trace (false); + if (unlikely (!c->serializer->embed (format))) return_trace (false); + if (unlikely (!c->serializer->embed (xCoordinate))) return_trace (false); + if (unlikely (!c->serializer->embed (yCoordinate))) return_trace (false); + + unsigned x_varidx = xDeviceTable ? (this+xDeviceTable).get_variation_index () : HB_OT_LAYOUT_NO_VARIATIONS_INDEX; + if (c->plan->layout_variation_idx_delta_map->has (x_varidx)) + { + int delta = hb_second (c->plan->layout_variation_idx_delta_map->get (x_varidx)); + if (delta != 0) + { + if (!c->serializer->check_assign (out->xCoordinate, xCoordinate + delta, + HB_SERIALIZE_ERROR_INT_OVERFLOW)) + return_trace (false); + } + } + + unsigned y_varidx = yDeviceTable ? (this+yDeviceTable).get_variation_index () : HB_OT_LAYOUT_NO_VARIATIONS_INDEX; + if (c->plan->layout_variation_idx_delta_map->has (y_varidx)) + { + int delta = hb_second (c->plan->layout_variation_idx_delta_map->get (y_varidx)); + if (delta != 0) + { + if (!c->serializer->check_assign (out->yCoordinate, yCoordinate + delta, + HB_SERIALIZE_ERROR_INT_OVERFLOW)) + return_trace (false); + } + } + + if (c->plan->all_axes_pinned) + return_trace (c->serializer->check_assign (out->format, 1, HB_SERIALIZE_ERROR_INT_OVERFLOW)); + + if (!c->serializer->embed (xDeviceTable)) return_trace (false); + if (!c->serializer->embed (yDeviceTable)) return_trace (false); + + out->xDeviceTable.serialize_copy (c->serializer, xDeviceTable, this, 0, hb_serialize_context_t::Head, c->plan->layout_variation_idx_delta_map); + out->yDeviceTable.serialize_copy (c->serializer, yDeviceTable, this, 0, hb_serialize_context_t::Head, c->plan->layout_variation_idx_delta_map); + return_trace (out); + } + + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const + { + (this+xDeviceTable).collect_variation_indices (c); + (this+yDeviceTable).collect_variation_indices (c); + } +}; + + +} +} +} + +#endif // OT_LAYOUT_GPOS_ANCHORFORMAT3_HH diff --git a/libs/harfbuzz/src/OT/Layout/GPOS/AnchorMatrix.hh b/libs/harfbuzz/src/OT/Layout/GPOS/AnchorMatrix.hh new file mode 100644 index 00000000..c442efa1 --- /dev/null +++ b/libs/harfbuzz/src/OT/Layout/GPOS/AnchorMatrix.hh @@ -0,0 +1,77 @@ +#ifndef OT_LAYOUT_GPOS_ANCHORMATRIX_HH +#define OT_LAYOUT_GPOS_ANCHORMATRIX_HH + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +struct AnchorMatrix +{ + HBUINT16 rows; /* Number of rows */ + UnsizedArrayOf> + matrixZ; /* Matrix of offsets to Anchor tables-- + * from beginning of AnchorMatrix table */ + public: + DEFINE_SIZE_ARRAY (2, matrixZ); + + bool sanitize (hb_sanitize_context_t *c, unsigned int cols) const + { + TRACE_SANITIZE (this); + if (!c->check_struct (this)) return_trace (false); + if (unlikely (hb_unsigned_mul_overflows (rows, cols))) return_trace (false); + unsigned int count = rows * cols; + if (!c->check_array (matrixZ.arrayZ, count)) return_trace (false); + for (unsigned int i = 0; i < count; i++) + if (!matrixZ[i].sanitize (c, this)) return_trace (false); + return_trace (true); + } + + const Anchor& get_anchor (unsigned int row, unsigned int col, + unsigned int cols, bool *found) const + { + *found = false; + if (unlikely (row >= rows || col >= cols)) return Null (Anchor); + *found = !matrixZ[row * cols + col].is_null (); + return this+matrixZ[row * cols + col]; + } + + template + void collect_variation_indices (hb_collect_variation_indices_context_t *c, + Iterator index_iter) const + { + for (unsigned i : index_iter) + (this+matrixZ[i]).collect_variation_indices (c); + } + + template + bool subset (hb_subset_context_t *c, + unsigned num_rows, + Iterator index_iter) const + { + TRACE_SUBSET (this); + + auto *out = c->serializer->start_embed (this); + + if (!index_iter) return_trace (false); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + + out->rows = num_rows; + for (const unsigned i : index_iter) + { + auto *offset = c->serializer->embed (matrixZ[i]); + if (!offset) return_trace (false); + offset->serialize_subset (c, matrixZ[i], this); + } + + return_trace (true); + } +}; + + +} +} +} + +#endif /* OT_LAYOUT_GPOS_ANCHORMATRIX_HH */ diff --git a/libs/harfbuzz/src/OT/Layout/GPOS/ChainContextPos.hh b/libs/harfbuzz/src/OT/Layout/GPOS/ChainContextPos.hh new file mode 100644 index 00000000..d551ac2a --- /dev/null +++ b/libs/harfbuzz/src/OT/Layout/GPOS/ChainContextPos.hh @@ -0,0 +1,14 @@ +#ifndef OT_LAYOUT_GPOS_CHAINCONTEXTPOS_HH +#define OT_LAYOUT_GPOS_CHAINCONTEXTPOS_HH + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +struct ChainContextPos : ChainContext {}; + +} +} +} + +#endif /* OT_LAYOUT_GPOS_CHAINCONTEXTPOS_HH */ diff --git a/libs/harfbuzz/src/OT/Layout/GPOS/Common.hh b/libs/harfbuzz/src/OT/Layout/GPOS/Common.hh new file mode 100644 index 00000000..40819745 --- /dev/null +++ b/libs/harfbuzz/src/OT/Layout/GPOS/Common.hh @@ -0,0 +1,33 @@ +#ifndef OT_LAYOUT_GPOS_COMMON_HH +#define OT_LAYOUT_GPOS_COMMON_HH + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +enum attach_type_t { + ATTACH_TYPE_NONE = 0X00, + + /* Each attachment should be either a mark or a cursive; can't be both. */ + ATTACH_TYPE_MARK = 0X01, + ATTACH_TYPE_CURSIVE = 0X02, +}; + +/* buffer **position** var allocations */ +#define attach_chain() var.i16[0] /* glyph to which this attaches to, relative to current glyphs; negative for going back, positive for forward. */ +#define attach_type() var.u8[2] /* attachment type */ +/* Note! if attach_chain() is zero, the value of attach_type() is irrelevant. */ + +template +static void SinglePos_serialize (hb_serialize_context_t *c, + const SrcLookup *src, + Iterator it, + const hb_hashmap_t> *layout_variation_idx_delta_map, + bool all_axes_pinned); + + +} +} +} + +#endif // OT_LAYOUT_GPOS_COMMON_HH diff --git a/libs/harfbuzz/src/OT/Layout/GPOS/ContextPos.hh b/libs/harfbuzz/src/OT/Layout/GPOS/ContextPos.hh new file mode 100644 index 00000000..2a01eaa3 --- /dev/null +++ b/libs/harfbuzz/src/OT/Layout/GPOS/ContextPos.hh @@ -0,0 +1,14 @@ +#ifndef OT_LAYOUT_GPOS_CONTEXTPOS_HH +#define OT_LAYOUT_GPOS_CONTEXTPOS_HH + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +struct ContextPos : Context {}; + +} +} +} + +#endif /* OT_LAYOUT_GPOS_CONTEXTPOS_HH */ diff --git a/libs/harfbuzz/src/OT/Layout/GPOS/CursivePos.hh b/libs/harfbuzz/src/OT/Layout/GPOS/CursivePos.hh new file mode 100644 index 00000000..c105cfb0 --- /dev/null +++ b/libs/harfbuzz/src/OT/Layout/GPOS/CursivePos.hh @@ -0,0 +1,35 @@ +#ifndef OT_LAYOUT_GPOS_CURSIVEPOS_HH +#define OT_LAYOUT_GPOS_CURSIVEPOS_HH + +#include "CursivePosFormat1.hh" + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +struct CursivePos +{ + protected: + union { + HBUINT16 format; /* Format identifier */ + CursivePosFormat1 format1; + } u; + + public: + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const + { + TRACE_DISPATCH (this, u.format); + if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ()); + switch (u.format) { + case 1: return_trace (c->dispatch (u.format1, std::forward (ds)...)); + default:return_trace (c->default_return_value ()); + } + } +}; + +} +} +} + +#endif /* OT_LAYOUT_GPOS_CURSIVEPOS_HH */ diff --git a/libs/harfbuzz/src/OT/Layout/GPOS/CursivePosFormat1.hh b/libs/harfbuzz/src/OT/Layout/GPOS/CursivePosFormat1.hh new file mode 100644 index 00000000..7f58fac8 --- /dev/null +++ b/libs/harfbuzz/src/OT/Layout/GPOS/CursivePosFormat1.hh @@ -0,0 +1,301 @@ +#ifndef OT_LAYOUT_GPOS_CURSIVEPOSFORMAT1_HH +#define OT_LAYOUT_GPOS_CURSIVEPOSFORMAT1_HH + +#include "Anchor.hh" + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +struct EntryExitRecord +{ + friend struct CursivePosFormat1; + + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (entryAnchor.sanitize (c, base) && exitAnchor.sanitize (c, base)); + } + + void collect_variation_indices (hb_collect_variation_indices_context_t *c, + const void *src_base) const + { + (src_base+entryAnchor).collect_variation_indices (c); + (src_base+exitAnchor).collect_variation_indices (c); + } + + EntryExitRecord* subset (hb_subset_context_t *c, + const void *src_base) const + { + TRACE_SERIALIZE (this); + auto *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (nullptr); + + out->entryAnchor.serialize_subset (c, entryAnchor, src_base); + out->exitAnchor.serialize_subset (c, exitAnchor, src_base); + return_trace (out); + } + + protected: + Offset16To + entryAnchor; /* Offset to EntryAnchor table--from + * beginning of CursivePos + * subtable--may be NULL */ + Offset16To + exitAnchor; /* Offset to ExitAnchor table--from + * beginning of CursivePos + * subtable--may be NULL */ + public: + DEFINE_SIZE_STATIC (4); +}; + +static void +reverse_cursive_minor_offset (hb_glyph_position_t *pos, unsigned int i, hb_direction_t direction, unsigned int new_parent) { + int chain = pos[i].attach_chain(), type = pos[i].attach_type(); + if (likely (!chain || 0 == (type & ATTACH_TYPE_CURSIVE))) + return; + + pos[i].attach_chain() = 0; + + unsigned int j = (int) i + chain; + + /* Stop if we see new parent in the chain. */ + if (j == new_parent) + return; + + reverse_cursive_minor_offset (pos, j, direction, new_parent); + + if (HB_DIRECTION_IS_HORIZONTAL (direction)) + pos[j].y_offset = -pos[i].y_offset; + else + pos[j].x_offset = -pos[i].x_offset; + + pos[j].attach_chain() = -chain; + pos[j].attach_type() = type; +} + + +struct CursivePosFormat1 +{ + protected: + HBUINT16 format; /* Format identifier--format = 1 */ + Offset16To + coverage; /* Offset to Coverage table--from + * beginning of subtable */ + Array16Of + entryExitRecord; /* Array of EntryExit records--in + * Coverage Index order */ + public: + DEFINE_SIZE_ARRAY (6, entryExitRecord); + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (coverage.sanitize (c, this) && entryExitRecord.sanitize (c, this)); + } + + bool intersects (const hb_set_t *glyphs) const + { return (this+coverage).intersects (glyphs); } + + void closure_lookups (hb_closure_lookups_context_t *c) const {} + + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const + { + + hb_zip (this+coverage, entryExitRecord) + | hb_filter (c->glyph_set, hb_first) + | hb_map (hb_second) + | hb_apply ([&] (const EntryExitRecord& record) { record.collect_variation_indices (c, this); }) + ; + } + + void collect_glyphs (hb_collect_glyphs_context_t *c) const + { if (unlikely (!(this+coverage).collect_coverage (c->input))) return; } + + const Coverage &get_coverage () const { return this+coverage; } + + bool apply (hb_ot_apply_context_t *c) const + { + TRACE_APPLY (this); + hb_buffer_t *buffer = c->buffer; + + const EntryExitRecord &this_record = entryExitRecord[(this+coverage).get_coverage (buffer->cur().codepoint)]; + if (!this_record.entryAnchor) return_trace (false); + + hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input; + skippy_iter.reset (buffer->idx, 1); + unsigned unsafe_from; + if (!skippy_iter.prev (&unsafe_from)) + { + buffer->unsafe_to_concat_from_outbuffer (unsafe_from, buffer->idx + 1); + return_trace (false); + } + + const EntryExitRecord &prev_record = entryExitRecord[(this+coverage).get_coverage (buffer->info[skippy_iter.idx].codepoint)]; + if (!prev_record.exitAnchor) + { + buffer->unsafe_to_concat_from_outbuffer (skippy_iter.idx, buffer->idx + 1); + return_trace (false); + } + + unsigned int i = skippy_iter.idx; + unsigned int j = buffer->idx; + + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->message (c->font, + "cursive attaching glyph at %d to glyph at %d", + i, j); + } + + buffer->unsafe_to_break (i, j + 1); + float entry_x, entry_y, exit_x, exit_y; + (this+prev_record.exitAnchor).get_anchor (c, buffer->info[i].codepoint, &exit_x, &exit_y); + (this+this_record.entryAnchor).get_anchor (c, buffer->info[j].codepoint, &entry_x, &entry_y); + + hb_glyph_position_t *pos = buffer->pos; + + hb_position_t d; + /* Main-direction adjustment */ + switch (c->direction) { + case HB_DIRECTION_LTR: + pos[i].x_advance = roundf (exit_x) + pos[i].x_offset; + + d = roundf (entry_x) + pos[j].x_offset; + pos[j].x_advance -= d; + pos[j].x_offset -= d; + break; + case HB_DIRECTION_RTL: + d = roundf (exit_x) + pos[i].x_offset; + pos[i].x_advance -= d; + pos[i].x_offset -= d; + + pos[j].x_advance = roundf (entry_x) + pos[j].x_offset; + break; + case HB_DIRECTION_TTB: + pos[i].y_advance = roundf (exit_y) + pos[i].y_offset; + + d = roundf (entry_y) + pos[j].y_offset; + pos[j].y_advance -= d; + pos[j].y_offset -= d; + break; + case HB_DIRECTION_BTT: + d = roundf (exit_y) + pos[i].y_offset; + pos[i].y_advance -= d; + pos[i].y_offset -= d; + + pos[j].y_advance = roundf (entry_y); + break; + case HB_DIRECTION_INVALID: + default: + break; + } + + /* Cross-direction adjustment */ + + /* We attach child to parent (think graph theory and rooted trees whereas + * the root stays on baseline and each node aligns itself against its + * parent. + * + * Optimize things for the case of RightToLeft, as that's most common in + * Arabic. */ + unsigned int child = i; + unsigned int parent = j; + hb_position_t x_offset = entry_x - exit_x; + hb_position_t y_offset = entry_y - exit_y; + if (!(c->lookup_props & LookupFlag::RightToLeft)) + { + unsigned int k = child; + child = parent; + parent = k; + x_offset = -x_offset; + y_offset = -y_offset; + } + + /* If child was already connected to someone else, walk through its old + * chain and reverse the link direction, such that the whole tree of its + * previous connection now attaches to new parent. Watch out for case + * where new parent is on the path from old chain... + */ + reverse_cursive_minor_offset (pos, child, c->direction, parent); + + pos[child].attach_type() = ATTACH_TYPE_CURSIVE; + pos[child].attach_chain() = (int) parent - (int) child; + buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT; + if (likely (HB_DIRECTION_IS_HORIZONTAL (c->direction))) + pos[child].y_offset = y_offset; + else + pos[child].x_offset = x_offset; + + /* If parent was attached to child, separate them. + * https://github.com/harfbuzz/harfbuzz/issues/2469 + */ + if (unlikely (pos[parent].attach_chain() == -pos[child].attach_chain())) + { + pos[parent].attach_chain() = 0; + if (likely (HB_DIRECTION_IS_HORIZONTAL (c->direction))) + pos[parent].y_offset = 0; + else + pos[parent].x_offset = 0; + } + + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->message (c->font, + "cursive attached glyph at %d to glyph at %d", + i, j); + } + + buffer->idx++; + return_trace (true); + } + + template + void serialize (hb_subset_context_t *c, + Iterator it, + const void *src_base) + { + if (unlikely (!c->serializer->extend_min ((*this)))) return; + this->format = 1; + this->entryExitRecord.len = it.len (); + + for (const EntryExitRecord& entry_record : + it + | hb_map (hb_second)) + entry_record.subset (c, src_base); + + auto glyphs = + + it + | hb_map_retains_sorting (hb_first) + ; + + coverage.serialize_serialize (c->serializer, glyphs); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + const hb_set_t &glyphset = *c->plan->glyphset_gsub (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + auto *out = c->serializer->start_embed (*this); + if (unlikely (!out)) return_trace (false); + + auto it = + + hb_zip (this+coverage, entryExitRecord) + | hb_filter (glyphset, hb_first) + | hb_map_retains_sorting ([&] (hb_pair_t p) -> hb_pair_t + { return hb_pair (glyph_map[p.first], p.second);}) + ; + + bool ret = bool (it); + out->serialize (c, it, this); + return_trace (ret); + } +}; + + +} +} +} + +#endif /* OT_LAYOUT_GPOS_CURSIVEPOSFORMAT1_HH */ diff --git a/libs/harfbuzz/src/OT/Layout/GPOS/ExtensionPos.hh b/libs/harfbuzz/src/OT/Layout/GPOS/ExtensionPos.hh new file mode 100644 index 00000000..d1808ada --- /dev/null +++ b/libs/harfbuzz/src/OT/Layout/GPOS/ExtensionPos.hh @@ -0,0 +1,17 @@ +#ifndef OT_LAYOUT_GPOS_EXTENSIONPOS_HH +#define OT_LAYOUT_GPOS_EXTENSIONPOS_HH + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +struct ExtensionPos : Extension +{ + typedef struct PosLookupSubTable SubTable; +}; + +} +} +} + +#endif /* OT_LAYOUT_GPOS_EXTENSIONPOS_HH */ diff --git a/libs/harfbuzz/src/OT/Layout/GPOS/GPOS.hh b/libs/harfbuzz/src/OT/Layout/GPOS/GPOS.hh new file mode 100644 index 00000000..9493ec98 --- /dev/null +++ b/libs/harfbuzz/src/OT/Layout/GPOS/GPOS.hh @@ -0,0 +1,171 @@ +#ifndef OT_LAYOUT_GPOS_GPOS_HH +#define OT_LAYOUT_GPOS_GPOS_HH + +#include "../../../hb-ot-layout-common.hh" +#include "../../../hb-ot-layout-gsubgpos.hh" +#include "Common.hh" +#include "PosLookup.hh" + +namespace OT { + +using Layout::GPOS_impl::PosLookup; + +namespace Layout { + +static void +propagate_attachment_offsets (hb_glyph_position_t *pos, + unsigned int len, + unsigned int i, + hb_direction_t direction, + unsigned nesting_level = HB_MAX_NESTING_LEVEL); + +/* + * GPOS -- Glyph Positioning + * https://docs.microsoft.com/en-us/typography/opentype/spec/gpos + */ + +struct GPOS : GSUBGPOS +{ + static constexpr hb_tag_t tableTag = HB_OT_TAG_GPOS; + + using Lookup = PosLookup; + + const PosLookup& get_lookup (unsigned int i) const + { return static_cast (GSUBGPOS::get_lookup (i)); } + + static inline void position_start (hb_font_t *font, hb_buffer_t *buffer); + static inline void position_finish_advances (hb_font_t *font, hb_buffer_t *buffer); + static inline void position_finish_offsets (hb_font_t *font, hb_buffer_t *buffer); + + bool subset (hb_subset_context_t *c) const + { + hb_subset_layout_context_t l (c, tableTag); + return GSUBGPOS::subset (&l); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (GSUBGPOS::sanitize (c)); + } + + HB_INTERNAL bool is_blocklisted (hb_blob_t *blob, + hb_face_t *face) const; + + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const + { + for (unsigned i = 0; i < GSUBGPOS::get_lookup_count (); i++) + { + if (!c->gpos_lookups->has (i)) continue; + const PosLookup &l = get_lookup (i); + l.dispatch (c); + } + } + + void closure_lookups (hb_face_t *face, + const hb_set_t *glyphs, + hb_set_t *lookup_indexes /* IN/OUT */) const + { GSUBGPOS::closure_lookups (face, glyphs, lookup_indexes); } + + typedef GSUBGPOS::accelerator_t accelerator_t; +}; + + +static void +propagate_attachment_offsets (hb_glyph_position_t *pos, + unsigned int len, + unsigned int i, + hb_direction_t direction, + unsigned nesting_level) +{ + /* Adjusts offsets of attached glyphs (both cursive and mark) to accumulate + * offset of glyph they are attached to. */ + int chain = pos[i].attach_chain(), type = pos[i].attach_type(); + if (likely (!chain)) + return; + + pos[i].attach_chain() = 0; + + unsigned int j = (int) i + chain; + + if (unlikely (j >= len)) + return; + + if (unlikely (!nesting_level)) + return; + + propagate_attachment_offsets (pos, len, j, direction, nesting_level - 1); + + assert (!!(type & GPOS_impl::ATTACH_TYPE_MARK) ^ !!(type & GPOS_impl::ATTACH_TYPE_CURSIVE)); + + if (type & GPOS_impl::ATTACH_TYPE_CURSIVE) + { + if (HB_DIRECTION_IS_HORIZONTAL (direction)) + pos[i].y_offset += pos[j].y_offset; + else + pos[i].x_offset += pos[j].x_offset; + } + else /*if (type & GPOS_impl::ATTACH_TYPE_MARK)*/ + { + pos[i].x_offset += pos[j].x_offset; + pos[i].y_offset += pos[j].y_offset; + + assert (j < i); + if (HB_DIRECTION_IS_FORWARD (direction)) + for (unsigned int k = j; k < i; k++) { + pos[i].x_offset -= pos[k].x_advance; + pos[i].y_offset -= pos[k].y_advance; + } + else + for (unsigned int k = j + 1; k < i + 1; k++) { + pos[i].x_offset += pos[k].x_advance; + pos[i].y_offset += pos[k].y_advance; + } + } +} + +void +GPOS::position_start (hb_font_t *font HB_UNUSED, hb_buffer_t *buffer) +{ + unsigned int count = buffer->len; + for (unsigned int i = 0; i < count; i++) + buffer->pos[i].attach_chain() = buffer->pos[i].attach_type() = 0; +} + +void +GPOS::position_finish_advances (hb_font_t *font HB_UNUSED, hb_buffer_t *buffer HB_UNUSED) +{ + //_hb_buffer_assert_gsubgpos_vars (buffer); +} + +void +GPOS::position_finish_offsets (hb_font_t *font, hb_buffer_t *buffer) +{ + _hb_buffer_assert_gsubgpos_vars (buffer); + + unsigned int len; + hb_glyph_position_t *pos = hb_buffer_get_glyph_positions (buffer, &len); + hb_direction_t direction = buffer->props.direction; + + /* Handle attachments */ + if (buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT) + for (unsigned i = 0; i < len; i++) + propagate_attachment_offsets (pos, len, i, direction); + + if (unlikely (font->slant)) + { + for (unsigned i = 0; i < len; i++) + if (unlikely (pos[i].y_offset)) + pos[i].x_offset += _hb_roundf (font->slant_xy * pos[i].y_offset); + } +} + +} + +struct GPOS_accelerator_t : Layout::GPOS::accelerator_t { + GPOS_accelerator_t (hb_face_t *face) : Layout::GPOS::accelerator_t (face) {} +}; + +} + +#endif /* OT_LAYOUT_GPOS_GPOS_HH */ diff --git a/libs/harfbuzz/src/OT/Layout/GPOS/LigatureArray.hh b/libs/harfbuzz/src/OT/Layout/GPOS/LigatureArray.hh new file mode 100644 index 00000000..a2d807cc --- /dev/null +++ b/libs/harfbuzz/src/OT/Layout/GPOS/LigatureArray.hh @@ -0,0 +1,56 @@ +#ifndef OT_LAYOUT_GPOS_LIGATUREARRAY_HH +#define OT_LAYOUT_GPOS_LIGATUREARRAY_HH + +namespace OT { +namespace Layout { +namespace GPOS_impl { + + +typedef AnchorMatrix LigatureAttach; /* component-major-- + * in order of writing direction--, + * mark-minor-- + * ordered by class--zero-based. */ + +/* Array of LigatureAttach tables ordered by LigatureCoverage Index */ +struct LigatureArray : List16OfOffset16To +{ + template + bool subset (hb_subset_context_t *c, + Iterator coverage, + unsigned class_count, + const hb_map_t *klass_mapping) const + { + TRACE_SUBSET (this); + const hb_set_t &glyphset = *c->plan->glyphset_gsub (); + + auto *out = c->serializer->start_embed (this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + + for (const auto _ : + hb_zip (coverage, *this) + | hb_filter (glyphset, hb_first)) + { + auto *matrix = out->serialize_append (c->serializer); + if (unlikely (!matrix)) return_trace (false); + + const LigatureAttach& src = (this + _.second); + auto indexes = + + hb_range (src.rows * class_count) + | hb_filter ([=] (unsigned index) { return klass_mapping->has (index % class_count); }) + ; + matrix->serialize_subset (c, + _.second, + this, + src.rows, + indexes); + } + return_trace (this->len); + } +}; + + +} +} +} + +#endif /* OT_LAYOUT_GPOS_LIGATUREARRAY_HH */ diff --git a/libs/harfbuzz/src/OT/Layout/GPOS/MarkArray.hh b/libs/harfbuzz/src/OT/Layout/GPOS/MarkArray.hh new file mode 100644 index 00000000..cb5e8b26 --- /dev/null +++ b/libs/harfbuzz/src/OT/Layout/GPOS/MarkArray.hh @@ -0,0 +1,128 @@ +#ifndef OT_LAYOUT_GPOS_MARKARRAY_HH +#define OT_LAYOUT_GPOS_MARKARRAY_HH + +#include "AnchorMatrix.hh" +#include "MarkRecord.hh" + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +struct MarkArray : Array16Of /* Array of MarkRecords--in Coverage order */ +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (Array16Of::sanitize (c, this)); + } + + bool apply (hb_ot_apply_context_t *c, + unsigned int mark_index, unsigned int glyph_index, + const AnchorMatrix &anchors, unsigned int class_count, + unsigned int glyph_pos) const + { + TRACE_APPLY (this); + hb_buffer_t *buffer = c->buffer; + const MarkRecord &record = Array16Of::operator[](mark_index); + unsigned int mark_class = record.klass; + + const Anchor& mark_anchor = this + record.markAnchor; + bool found; + const Anchor& glyph_anchor = anchors.get_anchor (glyph_index, mark_class, class_count, &found); + /* If this subtable doesn't have an anchor for this base and this class, + * return false such that the subsequent subtables have a chance at it. */ + if (unlikely (!found)) return_trace (false); + + float mark_x, mark_y, base_x, base_y; + + buffer->unsafe_to_break (glyph_pos, buffer->idx + 1); + mark_anchor.get_anchor (c, buffer->cur().codepoint, &mark_x, &mark_y); + glyph_anchor.get_anchor (c, buffer->info[glyph_pos].codepoint, &base_x, &base_y); + + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->message (c->font, + "attaching mark glyph at %d to glyph at %d", + c->buffer->idx, glyph_pos); + } + + hb_glyph_position_t &o = buffer->cur_pos(); + o.x_offset = roundf (base_x - mark_x); + o.y_offset = roundf (base_y - mark_y); + o.attach_type() = ATTACH_TYPE_MARK; + o.attach_chain() = (int) glyph_pos - (int) buffer->idx; + buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT; + + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->message (c->font, + "attached mark glyph at %d to glyph at %d", + c->buffer->idx, glyph_pos); + } + + buffer->idx++; + return_trace (true); + } + + template + bool subset (hb_subset_context_t *c, + Iterator coverage, + const hb_map_t *klass_mapping) const + { + TRACE_SUBSET (this); + const hb_set_t &glyphset = *c->plan->glyphset_gsub (); + + auto* out = c->serializer->start_embed (this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + + auto mark_iter = + + hb_zip (coverage, this->iter ()) + | hb_filter (glyphset, hb_first) + | hb_map (hb_second) + ; + + unsigned new_length = 0; + for (const auto& mark_record : mark_iter) { + if (unlikely (!mark_record.subset (c, this, klass_mapping))) + return_trace (false); + new_length++; + } + + if (unlikely (!c->serializer->check_assign (out->len, new_length, + HB_SERIALIZE_ERROR_ARRAY_OVERFLOW))) + return_trace (false); + + return_trace (true); + } +}; + +HB_INTERNAL inline +void Markclass_closure_and_remap_indexes (const Coverage &mark_coverage, + const MarkArray &mark_array, + const hb_set_t &glyphset, + hb_map_t* klass_mapping /* INOUT */) +{ + hb_set_t orig_classes; + + + hb_zip (mark_coverage, mark_array) + | hb_filter (glyphset, hb_first) + | hb_map (hb_second) + | hb_map (&MarkRecord::get_class) + | hb_sink (orig_classes) + ; + + unsigned idx = 0; + for (auto klass : orig_classes.iter ()) + { + if (klass_mapping->has (klass)) continue; + klass_mapping->set (klass, idx); + idx++; + } +} + +} +} +} + +#endif /* OT_LAYOUT_GPOS_MARKARRAY_HH */ diff --git a/libs/harfbuzz/src/OT/Layout/GPOS/MarkBasePos.hh b/libs/harfbuzz/src/OT/Layout/GPOS/MarkBasePos.hh new file mode 100644 index 00000000..c99b6b2e --- /dev/null +++ b/libs/harfbuzz/src/OT/Layout/GPOS/MarkBasePos.hh @@ -0,0 +1,41 @@ +#ifndef OT_LAYOUT_GPOS_MARKBASEPOS_HH +#define OT_LAYOUT_GPOS_MARKBASEPOS_HH + +#include "MarkBasePosFormat1.hh" + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +struct MarkBasePos +{ + protected: + union { + HBUINT16 format; /* Format identifier */ + MarkBasePosFormat1_2 format1; +#ifndef HB_NO_BORING_EXPANSION + MarkBasePosFormat1_2 format2; +#endif + } u; + + public: + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const + { + TRACE_DISPATCH (this, u.format); + if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ()); + switch (u.format) { + case 1: return_trace (c->dispatch (u.format1, std::forward (ds)...)); +#ifndef HB_NO_BORING_EXPANSION + case 2: return_trace (c->dispatch (u.format2, std::forward (ds)...)); +#endif + default:return_trace (c->default_return_value ()); + } + } +}; + +} +} +} + +#endif /* OT_LAYOUT_GPOS_MARKBASEPOS_HH */ diff --git a/libs/harfbuzz/src/OT/Layout/GPOS/MarkBasePosFormat1.hh b/libs/harfbuzz/src/OT/Layout/GPOS/MarkBasePosFormat1.hh new file mode 100644 index 00000000..ebb8c31c --- /dev/null +++ b/libs/harfbuzz/src/OT/Layout/GPOS/MarkBasePosFormat1.hh @@ -0,0 +1,219 @@ +#ifndef OT_LAYOUT_GPOS_MARKBASEPOSFORMAT1_HH +#define OT_LAYOUT_GPOS_MARKBASEPOSFORMAT1_HH + +#include "MarkArray.hh" + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +typedef AnchorMatrix BaseArray; /* base-major-- + * in order of BaseCoverage Index--, + * mark-minor-- + * ordered by class--zero-based. */ + +template +struct MarkBasePosFormat1_2 +{ + protected: + HBUINT16 format; /* Format identifier--format = 1 */ + typename Types::template OffsetTo + markCoverage; /* Offset to MarkCoverage table--from + * beginning of MarkBasePos subtable */ + typename Types::template OffsetTo + baseCoverage; /* Offset to BaseCoverage table--from + * beginning of MarkBasePos subtable */ + HBUINT16 classCount; /* Number of classes defined for marks */ + typename Types::template OffsetTo + markArray; /* Offset to MarkArray table--from + * beginning of MarkBasePos subtable */ + typename Types::template OffsetTo + baseArray; /* Offset to BaseArray table--from + * beginning of MarkBasePos subtable */ + + public: + DEFINE_SIZE_STATIC (4 + 4 * Types::size); + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + markCoverage.sanitize (c, this) && + baseCoverage.sanitize (c, this) && + markArray.sanitize (c, this) && + baseArray.sanitize (c, this, (unsigned int) classCount)); + } + + bool intersects (const hb_set_t *glyphs) const + { + return (this+markCoverage).intersects (glyphs) && + (this+baseCoverage).intersects (glyphs); + } + + void closure_lookups (hb_closure_lookups_context_t *c) const {} + + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const + { + + hb_zip (this+markCoverage, this+markArray) + | hb_filter (c->glyph_set, hb_first) + | hb_map (hb_second) + | hb_apply ([&] (const MarkRecord& record) { record.collect_variation_indices (c, &(this+markArray)); }) + ; + + hb_map_t klass_mapping; + Markclass_closure_and_remap_indexes (this+markCoverage, this+markArray, *c->glyph_set, &klass_mapping); + + unsigned basecount = (this+baseArray).rows; + auto base_iter = + + hb_zip (this+baseCoverage, hb_range (basecount)) + | hb_filter (c->glyph_set, hb_first) + | hb_map (hb_second) + ; + + hb_sorted_vector_t base_indexes; + for (const unsigned row : base_iter) + { + + hb_range ((unsigned) classCount) + | hb_filter (klass_mapping) + | hb_map ([&] (const unsigned col) { return row * (unsigned) classCount + col; }) + | hb_sink (base_indexes) + ; + } + (this+baseArray).collect_variation_indices (c, base_indexes.iter ()); + } + + void collect_glyphs (hb_collect_glyphs_context_t *c) const + { + if (unlikely (!(this+markCoverage).collect_coverage (c->input))) return; + if (unlikely (!(this+baseCoverage).collect_coverage (c->input))) return; + } + + const Coverage &get_coverage () const { return this+markCoverage; } + + bool apply (hb_ot_apply_context_t *c) const + { + TRACE_APPLY (this); + hb_buffer_t *buffer = c->buffer; + unsigned int mark_index = (this+markCoverage).get_coverage (buffer->cur().codepoint); + if (likely (mark_index == NOT_COVERED)) return_trace (false); + + /* Now we search backwards for a non-mark glyph */ + hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input; + skippy_iter.reset (buffer->idx, 1); + skippy_iter.set_lookup_props (LookupFlag::IgnoreMarks); + do { + unsigned unsafe_from; + if (!skippy_iter.prev (&unsafe_from)) + { + buffer->unsafe_to_concat_from_outbuffer (unsafe_from, buffer->idx + 1); + return_trace (false); + } + + /* We only want to attach to the first of a MultipleSubst sequence. + * https://github.com/harfbuzz/harfbuzz/issues/740 + * Reject others... + * ...but stop if we find a mark in the MultipleSubst sequence: + * https://github.com/harfbuzz/harfbuzz/issues/1020 */ + if (!_hb_glyph_info_multiplied (&buffer->info[skippy_iter.idx]) || + 0 == _hb_glyph_info_get_lig_comp (&buffer->info[skippy_iter.idx]) || + (skippy_iter.idx == 0 || + _hb_glyph_info_is_mark (&buffer->info[skippy_iter.idx - 1]) || + !_hb_glyph_info_multiplied (&buffer->info[skippy_iter.idx - 1]) || + _hb_glyph_info_get_lig_id (&buffer->info[skippy_iter.idx]) != + _hb_glyph_info_get_lig_id (&buffer->info[skippy_iter.idx - 1]) || + _hb_glyph_info_get_lig_comp (&buffer->info[skippy_iter.idx]) != + _hb_glyph_info_get_lig_comp (&buffer->info[skippy_iter.idx - 1]) + 1 + )) + break; + skippy_iter.reject (); + } while (true); + + /* Checking that matched glyph is actually a base glyph by GDEF is too strong; disabled */ + //if (!_hb_glyph_info_is_base_glyph (&buffer->info[skippy_iter.idx])) { return_trace (false); } + + unsigned int base_index = (this+baseCoverage).get_coverage (buffer->info[skippy_iter.idx].codepoint); + if (base_index == NOT_COVERED) + { + buffer->unsafe_to_concat_from_outbuffer (skippy_iter.idx, buffer->idx + 1); + return_trace (false); + } + + return_trace ((this+markArray).apply (c, mark_index, base_index, this+baseArray, classCount, skippy_iter.idx)); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + const hb_set_t &glyphset = *c->plan->glyphset_gsub (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + out->format = format; + + hb_map_t klass_mapping; + Markclass_closure_and_remap_indexes (this+markCoverage, this+markArray, glyphset, &klass_mapping); + + if (!klass_mapping.get_population ()) return_trace (false); + out->classCount = klass_mapping.get_population (); + + auto mark_iter = + + hb_zip (this+markCoverage, this+markArray) + | hb_filter (glyphset, hb_first) + ; + + hb_sorted_vector_t new_coverage; + + mark_iter + | hb_map (hb_first) + | hb_map (glyph_map) + | hb_sink (new_coverage) + ; + + if (!out->markCoverage.serialize_serialize (c->serializer, new_coverage.iter ())) + return_trace (false); + + out->markArray.serialize_subset (c, markArray, this, + (this+markCoverage).iter (), + &klass_mapping); + + unsigned basecount = (this+baseArray).rows; + auto base_iter = + + hb_zip (this+baseCoverage, hb_range (basecount)) + | hb_filter (glyphset, hb_first) + ; + + new_coverage.reset (); + + base_iter + | hb_map (hb_first) + | hb_map (glyph_map) + | hb_sink (new_coverage) + ; + + if (!out->baseCoverage.serialize_serialize (c->serializer, new_coverage.iter ())) + return_trace (false); + + hb_sorted_vector_t base_indexes; + for (const unsigned row : + base_iter + | hb_map (hb_second)) + { + + hb_range ((unsigned) classCount) + | hb_filter (klass_mapping) + | hb_map ([&] (const unsigned col) { return row * (unsigned) classCount + col; }) + | hb_sink (base_indexes) + ; + } + + out->baseArray.serialize_subset (c, baseArray, this, + base_iter.len (), + base_indexes.iter ()); + + return_trace (true); + } +}; + + +} +} +} + +#endif /* OT_LAYOUT_GPOS_MARKBASEPOSFORMAT1_HH */ diff --git a/libs/harfbuzz/src/OT/Layout/GPOS/MarkLigPos.hh b/libs/harfbuzz/src/OT/Layout/GPOS/MarkLigPos.hh new file mode 100644 index 00000000..8a4de9ff --- /dev/null +++ b/libs/harfbuzz/src/OT/Layout/GPOS/MarkLigPos.hh @@ -0,0 +1,41 @@ +#ifndef OT_LAYOUT_GPOS_MARKLIGPOS_HH +#define OT_LAYOUT_GPOS_MARKLIGPOS_HH + +#include "MarkLigPosFormat1.hh" + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +struct MarkLigPos +{ + protected: + union { + HBUINT16 format; /* Format identifier */ + MarkLigPosFormat1_2 format1; +#ifndef HB_NO_BORING_EXPANSION + MarkLigPosFormat1_2 format2; +#endif + } u; + + public: + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const + { + TRACE_DISPATCH (this, u.format); + if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ()); + switch (u.format) { + case 1: return_trace (c->dispatch (u.format1, std::forward (ds)...)); +#ifndef HB_NO_BORING_EXPANSION + case 2: return_trace (c->dispatch (u.format2, std::forward (ds)...)); +#endif + default:return_trace (c->default_return_value ()); + } + } +}; + +} +} +} + +#endif /* OT_LAYOUT_GPOS_MARKLIGPOS_HH */ diff --git a/libs/harfbuzz/src/OT/Layout/GPOS/MarkLigPosFormat1.hh b/libs/harfbuzz/src/OT/Layout/GPOS/MarkLigPosFormat1.hh new file mode 100644 index 00000000..1a802123 --- /dev/null +++ b/libs/harfbuzz/src/OT/Layout/GPOS/MarkLigPosFormat1.hh @@ -0,0 +1,206 @@ +#ifndef OT_LAYOUT_GPOS_MARKLIGPOSFORMAT1_HH +#define OT_LAYOUT_GPOS_MARKLIGPOSFORMAT1_HH + +#include "LigatureArray.hh" + +namespace OT { +namespace Layout { +namespace GPOS_impl { + + +template +struct MarkLigPosFormat1_2 +{ + protected: + HBUINT16 format; /* Format identifier--format = 1 */ + typename Types::template OffsetTo + markCoverage; /* Offset to Mark Coverage table--from + * beginning of MarkLigPos subtable */ + typename Types::template OffsetTo + ligatureCoverage; /* Offset to Ligature Coverage + * table--from beginning of MarkLigPos + * subtable */ + HBUINT16 classCount; /* Number of defined mark classes */ + typename Types::template OffsetTo + markArray; /* Offset to MarkArray table--from + * beginning of MarkLigPos subtable */ + typename Types::template OffsetTo + ligatureArray; /* Offset to LigatureArray table--from + * beginning of MarkLigPos subtable */ + public: + DEFINE_SIZE_STATIC (4 + 4 * Types::size); + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + markCoverage.sanitize (c, this) && + ligatureCoverage.sanitize (c, this) && + markArray.sanitize (c, this) && + ligatureArray.sanitize (c, this, (unsigned int) classCount)); + } + + bool intersects (const hb_set_t *glyphs) const + { + return (this+markCoverage).intersects (glyphs) && + (this+ligatureCoverage).intersects (glyphs); + } + + void closure_lookups (hb_closure_lookups_context_t *c) const {} + + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const + { + + hb_zip (this+markCoverage, this+markArray) + | hb_filter (c->glyph_set, hb_first) + | hb_map (hb_second) + | hb_apply ([&] (const MarkRecord& record) { record.collect_variation_indices (c, &(this+markArray)); }) + ; + + hb_map_t klass_mapping; + Markclass_closure_and_remap_indexes (this+markCoverage, this+markArray, *c->glyph_set, &klass_mapping); + + unsigned ligcount = (this+ligatureArray).len; + auto lig_iter = + + hb_zip (this+ligatureCoverage, hb_range (ligcount)) + | hb_filter (c->glyph_set, hb_first) + | hb_map (hb_second) + ; + + const LigatureArray& lig_array = this+ligatureArray; + for (const unsigned i : lig_iter) + { + hb_sorted_vector_t lig_indexes; + unsigned row_count = lig_array[i].rows; + for (unsigned row : + hb_range (row_count)) + { + + hb_range ((unsigned) classCount) + | hb_filter (klass_mapping) + | hb_map ([&] (const unsigned col) { return row * (unsigned) classCount + col; }) + | hb_sink (lig_indexes) + ; + } + + lig_array[i].collect_variation_indices (c, lig_indexes.iter ()); + } + } + + void collect_glyphs (hb_collect_glyphs_context_t *c) const + { + if (unlikely (!(this+markCoverage).collect_coverage (c->input))) return; + if (unlikely (!(this+ligatureCoverage).collect_coverage (c->input))) return; + } + + const Coverage &get_coverage () const { return this+markCoverage; } + + bool apply (hb_ot_apply_context_t *c) const + { + TRACE_APPLY (this); + hb_buffer_t *buffer = c->buffer; + unsigned int mark_index = (this+markCoverage).get_coverage (buffer->cur().codepoint); + if (likely (mark_index == NOT_COVERED)) return_trace (false); + + /* Now we search backwards for a non-mark glyph */ + hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input; + skippy_iter.reset (buffer->idx, 1); + skippy_iter.set_lookup_props (LookupFlag::IgnoreMarks); + unsigned unsafe_from; + if (!skippy_iter.prev (&unsafe_from)) + { + buffer->unsafe_to_concat_from_outbuffer (unsafe_from, buffer->idx + 1); + return_trace (false); + } + + /* Checking that matched glyph is actually a ligature by GDEF is too strong; disabled */ + //if (!_hb_glyph_info_is_ligature (&buffer->info[skippy_iter.idx])) { return_trace (false); } + + unsigned int j = skippy_iter.idx; + unsigned int lig_index = (this+ligatureCoverage).get_coverage (buffer->info[j].codepoint); + if (lig_index == NOT_COVERED) + { + buffer->unsafe_to_concat_from_outbuffer (skippy_iter.idx, buffer->idx + 1); + return_trace (false); + } + + const LigatureArray& lig_array = this+ligatureArray; + const LigatureAttach& lig_attach = lig_array[lig_index]; + + /* Find component to attach to */ + unsigned int comp_count = lig_attach.rows; + if (unlikely (!comp_count)) + { + buffer->unsafe_to_concat_from_outbuffer (skippy_iter.idx, buffer->idx + 1); + return_trace (false); + } + + /* We must now check whether the ligature ID of the current mark glyph + * is identical to the ligature ID of the found ligature. If yes, we + * can directly use the component index. If not, we attach the mark + * glyph to the last component of the ligature. */ + unsigned int comp_index; + unsigned int lig_id = _hb_glyph_info_get_lig_id (&buffer->info[j]); + unsigned int mark_id = _hb_glyph_info_get_lig_id (&buffer->cur()); + unsigned int mark_comp = _hb_glyph_info_get_lig_comp (&buffer->cur()); + if (lig_id && lig_id == mark_id && mark_comp > 0) + comp_index = hb_min (comp_count, _hb_glyph_info_get_lig_comp (&buffer->cur())) - 1; + else + comp_index = comp_count - 1; + + return_trace ((this+markArray).apply (c, mark_index, comp_index, lig_attach, classCount, j)); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + const hb_set_t &glyphset = *c->plan->glyphset_gsub (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + out->format = format; + + hb_map_t klass_mapping; + Markclass_closure_and_remap_indexes (this+markCoverage, this+markArray, glyphset, &klass_mapping); + + if (!klass_mapping.get_population ()) return_trace (false); + out->classCount = klass_mapping.get_population (); + + auto mark_iter = + + hb_zip (this+markCoverage, this+markArray) + | hb_filter (glyphset, hb_first) + ; + + auto new_mark_coverage = + + mark_iter + | hb_map_retains_sorting (hb_first) + | hb_map_retains_sorting (glyph_map) + ; + + if (!out->markCoverage.serialize_serialize (c->serializer, new_mark_coverage)) + return_trace (false); + + out->markArray.serialize_subset (c, markArray, this, + (this+markCoverage).iter (), + &klass_mapping); + + auto new_ligature_coverage = + + hb_iter (this + ligatureCoverage) + | hb_filter (glyphset) + | hb_map_retains_sorting (glyph_map) + ; + + if (!out->ligatureCoverage.serialize_serialize (c->serializer, new_ligature_coverage)) + return_trace (false); + + out->ligatureArray.serialize_subset (c, ligatureArray, this, + hb_iter (this+ligatureCoverage), classCount, &klass_mapping); + + return_trace (true); + } + +}; + +} +} +} + +#endif /* OT_LAYOUT_GPOS_MARKLIGPOSFORMAT1_HH */ diff --git a/libs/harfbuzz/src/OT/Layout/GPOS/MarkMarkPos.hh b/libs/harfbuzz/src/OT/Layout/GPOS/MarkMarkPos.hh new file mode 100644 index 00000000..74b5105c --- /dev/null +++ b/libs/harfbuzz/src/OT/Layout/GPOS/MarkMarkPos.hh @@ -0,0 +1,42 @@ +#ifndef OT_LAYOUT_GPOS_MARKMARKPOS_HH +#define OT_LAYOUT_GPOS_MARKMARKPOS_HH + +#include "MarkMarkPosFormat1.hh" + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +struct MarkMarkPos +{ + protected: + union { + HBUINT16 format; /* Format identifier */ + MarkMarkPosFormat1_2 format1; +#ifndef HB_NO_BORING_EXPANSION + MarkMarkPosFormat1_2 format2; +#endif + } u; + + public: + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const + { + TRACE_DISPATCH (this, u.format); + if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ()); + switch (u.format) { + case 1: return_trace (c->dispatch (u.format1, std::forward (ds)...)); +#ifndef HB_NO_BORING_EXPANSION + case 2: return_trace (c->dispatch (u.format2, std::forward (ds)...)); +#endif + default:return_trace (c->default_return_value ()); + } + } +}; + + +} +} +} + +#endif /* OT_LAYOUT_GPOS_MARKMARKPOS_HH */ diff --git a/libs/harfbuzz/src/OT/Layout/GPOS/MarkMarkPosFormat1.hh b/libs/harfbuzz/src/OT/Layout/GPOS/MarkMarkPosFormat1.hh new file mode 100644 index 00000000..fbcebb80 --- /dev/null +++ b/libs/harfbuzz/src/OT/Layout/GPOS/MarkMarkPosFormat1.hh @@ -0,0 +1,228 @@ +#ifndef OT_LAYOUT_GPOS_MARKMARKPOSFORMAT1_HH +#define OT_LAYOUT_GPOS_MARKMARKPOSFORMAT1_HH + +#include "MarkMarkPosFormat1.hh" + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +typedef AnchorMatrix Mark2Array; /* mark2-major-- + * in order of Mark2Coverage Index--, + * mark1-minor-- + * ordered by class--zero-based. */ + +template +struct MarkMarkPosFormat1_2 +{ + protected: + HBUINT16 format; /* Format identifier--format = 1 */ + typename Types::template OffsetTo + mark1Coverage; /* Offset to Combining Mark1 Coverage + * table--from beginning of MarkMarkPos + * subtable */ + typename Types::template OffsetTo + mark2Coverage; /* Offset to Combining Mark2 Coverage + * table--from beginning of MarkMarkPos + * subtable */ + HBUINT16 classCount; /* Number of defined mark classes */ + typename Types::template OffsetTo + mark1Array; /* Offset to Mark1Array table--from + * beginning of MarkMarkPos subtable */ + typename Types::template OffsetTo + mark2Array; /* Offset to Mark2Array table--from + * beginning of MarkMarkPos subtable */ + public: + DEFINE_SIZE_STATIC (4 + 4 * Types::size); + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + mark1Coverage.sanitize (c, this) && + mark2Coverage.sanitize (c, this) && + mark1Array.sanitize (c, this) && + mark2Array.sanitize (c, this, (unsigned int) classCount)); + } + + bool intersects (const hb_set_t *glyphs) const + { + return (this+mark1Coverage).intersects (glyphs) && + (this+mark2Coverage).intersects (glyphs); + } + + void closure_lookups (hb_closure_lookups_context_t *c) const {} + + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const + { + + hb_zip (this+mark1Coverage, this+mark1Array) + | hb_filter (c->glyph_set, hb_first) + | hb_map (hb_second) + | hb_apply ([&] (const MarkRecord& record) { record.collect_variation_indices (c, &(this+mark1Array)); }) + ; + + hb_map_t klass_mapping; + Markclass_closure_and_remap_indexes (this+mark1Coverage, this+mark1Array, *c->glyph_set, &klass_mapping); + + unsigned mark2_count = (this+mark2Array).rows; + auto mark2_iter = + + hb_zip (this+mark2Coverage, hb_range (mark2_count)) + | hb_filter (c->glyph_set, hb_first) + | hb_map (hb_second) + ; + + hb_sorted_vector_t mark2_indexes; + for (const unsigned row : mark2_iter) + { + + hb_range ((unsigned) classCount) + | hb_filter (klass_mapping) + | hb_map ([&] (const unsigned col) { return row * (unsigned) classCount + col; }) + | hb_sink (mark2_indexes) + ; + } + (this+mark2Array).collect_variation_indices (c, mark2_indexes.iter ()); + } + + void collect_glyphs (hb_collect_glyphs_context_t *c) const + { + if (unlikely (!(this+mark1Coverage).collect_coverage (c->input))) return; + if (unlikely (!(this+mark2Coverage).collect_coverage (c->input))) return; + } + + const Coverage &get_coverage () const { return this+mark1Coverage; } + + bool apply (hb_ot_apply_context_t *c) const + { + TRACE_APPLY (this); + hb_buffer_t *buffer = c->buffer; + unsigned int mark1_index = (this+mark1Coverage).get_coverage (buffer->cur().codepoint); + if (likely (mark1_index == NOT_COVERED)) return_trace (false); + + /* now we search backwards for a suitable mark glyph until a non-mark glyph */ + hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input; + skippy_iter.reset (buffer->idx, 1); + skippy_iter.set_lookup_props (c->lookup_props & ~(uint32_t)LookupFlag::IgnoreFlags); + unsigned unsafe_from; + if (!skippy_iter.prev (&unsafe_from)) + { + buffer->unsafe_to_concat_from_outbuffer (unsafe_from, buffer->idx + 1); + return_trace (false); + } + + if (!_hb_glyph_info_is_mark (&buffer->info[skippy_iter.idx])) + { + buffer->unsafe_to_concat_from_outbuffer (skippy_iter.idx, buffer->idx + 1); + return_trace (false); + } + + unsigned int j = skippy_iter.idx; + + unsigned int id1 = _hb_glyph_info_get_lig_id (&buffer->cur()); + unsigned int id2 = _hb_glyph_info_get_lig_id (&buffer->info[j]); + unsigned int comp1 = _hb_glyph_info_get_lig_comp (&buffer->cur()); + unsigned int comp2 = _hb_glyph_info_get_lig_comp (&buffer->info[j]); + + if (likely (id1 == id2)) + { + if (id1 == 0) /* Marks belonging to the same base. */ + goto good; + else if (comp1 == comp2) /* Marks belonging to the same ligature component. */ + goto good; + } + else + { + /* If ligature ids don't match, it may be the case that one of the marks + * itself is a ligature. In which case match. */ + if ((id1 > 0 && !comp1) || (id2 > 0 && !comp2)) + goto good; + } + + /* Didn't match. */ + buffer->unsafe_to_concat_from_outbuffer (skippy_iter.idx, buffer->idx + 1); + return_trace (false); + + good: + unsigned int mark2_index = (this+mark2Coverage).get_coverage (buffer->info[j].codepoint); + if (mark2_index == NOT_COVERED) + { + buffer->unsafe_to_concat_from_outbuffer (skippy_iter.idx, buffer->idx + 1); + return_trace (false); + } + + return_trace ((this+mark1Array).apply (c, mark1_index, mark2_index, this+mark2Array, classCount, j)); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + const hb_set_t &glyphset = *c->plan->glyphset_gsub (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + out->format = format; + + hb_map_t klass_mapping; + Markclass_closure_and_remap_indexes (this+mark1Coverage, this+mark1Array, glyphset, &klass_mapping); + + if (!klass_mapping.get_population ()) return_trace (false); + out->classCount = klass_mapping.get_population (); + + auto mark1_iter = + + hb_zip (this+mark1Coverage, this+mark1Array) + | hb_filter (glyphset, hb_first) + ; + + hb_sorted_vector_t new_coverage; + + mark1_iter + | hb_map (hb_first) + | hb_map (glyph_map) + | hb_sink (new_coverage) + ; + + if (!out->mark1Coverage.serialize_serialize (c->serializer, new_coverage.iter ())) + return_trace (false); + + out->mark1Array.serialize_subset (c, mark1Array, this, + (this+mark1Coverage).iter (), + &klass_mapping); + + unsigned mark2count = (this+mark2Array).rows; + auto mark2_iter = + + hb_zip (this+mark2Coverage, hb_range (mark2count)) + | hb_filter (glyphset, hb_first) + ; + + new_coverage.reset (); + + mark2_iter + | hb_map (hb_first) + | hb_map (glyph_map) + | hb_sink (new_coverage) + ; + + if (!out->mark2Coverage.serialize_serialize (c->serializer, new_coverage.iter ())) + return_trace (false); + + hb_sorted_vector_t mark2_indexes; + for (const unsigned row : + mark2_iter + | hb_map (hb_second)) + { + + hb_range ((unsigned) classCount) + | hb_filter (klass_mapping) + | hb_map ([&] (const unsigned col) { return row * (unsigned) classCount + col; }) + | hb_sink (mark2_indexes) + ; + } + + out->mark2Array.serialize_subset (c, mark2Array, this, mark2_iter.len (), mark2_indexes.iter ()); + + return_trace (true); + } +}; + + +} +} +} + +#endif /* OT_LAYOUT_GPOS_MARKMARKPOSFORMAT1_HH */ diff --git a/libs/harfbuzz/src/OT/Layout/GPOS/MarkRecord.hh b/libs/harfbuzz/src/OT/Layout/GPOS/MarkRecord.hh new file mode 100644 index 00000000..a7d489d2 --- /dev/null +++ b/libs/harfbuzz/src/OT/Layout/GPOS/MarkRecord.hh @@ -0,0 +1,52 @@ +#ifndef OT_LAYOUT_GPOS_MARKRECORD_HH +#define OT_LAYOUT_GPOS_MARKRECORD_HH + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +struct MarkRecord +{ + friend struct MarkArray; + + public: + HBUINT16 klass; /* Class defined for this mark */ + Offset16To + markAnchor; /* Offset to Anchor table--from + * beginning of MarkArray table */ + public: + DEFINE_SIZE_STATIC (4); + + unsigned get_class () const { return (unsigned) klass; } + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && markAnchor.sanitize (c, base)); + } + + MarkRecord *subset (hb_subset_context_t *c, + const void *src_base, + const hb_map_t *klass_mapping) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (nullptr); + + out->klass = klass_mapping->get (klass); + out->markAnchor.serialize_subset (c, markAnchor, src_base); + return_trace (out); + } + + void collect_variation_indices (hb_collect_variation_indices_context_t *c, + const void *src_base) const + { + (src_base+markAnchor).collect_variation_indices (c); + } +}; + + +} +} +} + +#endif /* OT_LAYOUT_GPOS_MARKRECORD_HH */ diff --git a/libs/harfbuzz/src/OT/Layout/GPOS/PairPos.hh b/libs/harfbuzz/src/OT/Layout/GPOS/PairPos.hh new file mode 100644 index 00000000..72bfc43d --- /dev/null +++ b/libs/harfbuzz/src/OT/Layout/GPOS/PairPos.hh @@ -0,0 +1,46 @@ +#ifndef OT_LAYOUT_GPOS_PAIRPOS_HH +#define OT_LAYOUT_GPOS_PAIRPOS_HH + +#include "PairPosFormat1.hh" +#include "PairPosFormat2.hh" + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +struct PairPos +{ + protected: + union { + HBUINT16 format; /* Format identifier */ + PairPosFormat1_3 format1; + PairPosFormat2_4 format2; +#ifndef HB_NO_BORING_EXPANSION + PairPosFormat1_3 format3; + PairPosFormat2_4 format4; +#endif + } u; + + public: + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const + { + TRACE_DISPATCH (this, u.format); + if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ()); + switch (u.format) { + case 1: return_trace (c->dispatch (u.format1, std::forward (ds)...)); + case 2: return_trace (c->dispatch (u.format2, std::forward (ds)...)); +#ifndef HB_NO_BORING_EXPANSION + case 3: return_trace (c->dispatch (u.format3, std::forward (ds)...)); + case 4: return_trace (c->dispatch (u.format4, std::forward (ds)...)); +#endif + default:return_trace (c->default_return_value ()); + } + } +}; + +} +} +} + +#endif // OT_LAYOUT_GPOS_PAIRPOS_HH diff --git a/libs/harfbuzz/src/OT/Layout/GPOS/PairPosFormat1.hh b/libs/harfbuzz/src/OT/Layout/GPOS/PairPosFormat1.hh new file mode 100644 index 00000000..ddf7313f --- /dev/null +++ b/libs/harfbuzz/src/OT/Layout/GPOS/PairPosFormat1.hh @@ -0,0 +1,199 @@ +#ifndef OT_LAYOUT_GPOS_PAIRPOSFORMAT1_HH +#define OT_LAYOUT_GPOS_PAIRPOSFORMAT1_HH + +#include "PairSet.hh" + +namespace OT { +namespace Layout { +namespace GPOS_impl { + + +template +struct PairPosFormat1_3 +{ + using PairSet = GPOS_impl::PairSet; + using PairValueRecord = GPOS_impl::PairValueRecord; + + protected: + HBUINT16 format; /* Format identifier--format = 1 */ + typename Types::template OffsetTo + coverage; /* Offset to Coverage table--from + * beginning of subtable */ + ValueFormat valueFormat[2]; /* [0] Defines the types of data in + * ValueRecord1--for the first glyph + * in the pair--may be zero (0) */ + /* [1] Defines the types of data in + * ValueRecord2--for the second glyph + * in the pair--may be zero (0) */ + Array16Of> + pairSet; /* Array of PairSet tables + * ordered by Coverage Index */ + public: + DEFINE_SIZE_ARRAY (8 + Types::size, pairSet); + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + + if (!c->check_struct (this)) return_trace (false); + + unsigned int len1 = valueFormat[0].get_len (); + unsigned int len2 = valueFormat[1].get_len (); + typename PairSet::sanitize_closure_t closure = + { + valueFormat, + len1, + 1 + len1 + len2 + }; + + return_trace (coverage.sanitize (c, this) && pairSet.sanitize (c, this, &closure)); + } + + bool intersects (const hb_set_t *glyphs) const + { + return + + hb_zip (this+coverage, pairSet) + | hb_filter (*glyphs, hb_first) + | hb_map (hb_second) + | hb_map ([glyphs, this] (const typename Types::template OffsetTo &_) + { return (this+_).intersects (glyphs, valueFormat); }) + | hb_any + ; + } + + void closure_lookups (hb_closure_lookups_context_t *c) const {} + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const + { + if ((!valueFormat[0].has_device ()) && (!valueFormat[1].has_device ())) return; + + auto it = + + hb_zip (this+coverage, pairSet) + | hb_filter (c->glyph_set, hb_first) + | hb_map (hb_second) + ; + + if (!it) return; + + it + | hb_map (hb_add (this)) + | hb_apply ([&] (const PairSet& _) { _.collect_variation_indices (c, valueFormat); }) + ; + } + + void collect_glyphs (hb_collect_glyphs_context_t *c) const + { + if (unlikely (!(this+coverage).collect_coverage (c->input))) return; + unsigned int count = pairSet.len; + for (unsigned int i = 0; i < count; i++) + (this+pairSet[i]).collect_glyphs (c, valueFormat); + } + + const Coverage &get_coverage () const { return this+coverage; } + + bool apply (hb_ot_apply_context_t *c) const + { + TRACE_APPLY (this); + hb_buffer_t *buffer = c->buffer; + unsigned int index = (this+coverage).get_coverage (buffer->cur().codepoint); + if (likely (index == NOT_COVERED)) return_trace (false); + + hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input; + skippy_iter.reset (buffer->idx, 1); + unsigned unsafe_to; + if (!skippy_iter.next (&unsafe_to)) + { + buffer->unsafe_to_concat (buffer->idx, unsafe_to); + return_trace (false); + } + + return_trace ((this+pairSet[index]).apply (c, valueFormat, skippy_iter.idx)); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + + const hb_set_t &glyphset = *c->plan->glyphset_gsub (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + out->format = format; + out->valueFormat[0] = valueFormat[0]; + out->valueFormat[1] = valueFormat[1]; + if (c->plan->flags & HB_SUBSET_FLAGS_NO_HINTING) + { + hb_pair_t newFormats = compute_effective_value_formats (glyphset); + out->valueFormat[0] = newFormats.first; + out->valueFormat[1] = newFormats.second; + } + + if (c->plan->all_axes_pinned) + { + out->valueFormat[0] = out->valueFormat[0].drop_device_table_flags (); + out->valueFormat[1] = out->valueFormat[1].drop_device_table_flags (); + } + + hb_sorted_vector_t new_coverage; + + + hb_zip (this+coverage, pairSet) + | hb_filter (glyphset, hb_first) + | hb_filter ([this, c, out] (const typename Types::template OffsetTo& _) + { + auto snap = c->serializer->snapshot (); + auto *o = out->pairSet.serialize_append (c->serializer); + if (unlikely (!o)) return false; + bool ret = o->serialize_subset (c, _, this, valueFormat, out->valueFormat); + if (!ret) + { + out->pairSet.pop (); + c->serializer->revert (snap); + } + return ret; + }, + hb_second) + | hb_map (hb_first) + | hb_map (glyph_map) + | hb_sink (new_coverage) + ; + + out->coverage.serialize_serialize (c->serializer, new_coverage.iter ()); + + return_trace (bool (new_coverage)); + } + + + hb_pair_t compute_effective_value_formats (const hb_set_t& glyphset) const + { + unsigned len1 = valueFormat[0].get_len (); + unsigned len2 = valueFormat[1].get_len (); + unsigned record_size = HBUINT16::static_size + Value::static_size * (len1 + len2); + + unsigned format1 = 0; + unsigned format2 = 0; + for (const auto & _ : + + hb_zip (this+coverage, pairSet) | hb_filter (glyphset, hb_first) | hb_map (hb_second)) + { + const PairSet& set = (this + _); + const PairValueRecord *record = &set.firstPairValueRecord; + + for (unsigned i = 0; i < set.len; i++) + { + if (record->intersects (glyphset)) + { + format1 = format1 | valueFormat[0].get_effective_format (record->get_values_1 ()); + format2 = format2 | valueFormat[1].get_effective_format (record->get_values_2 (valueFormat[0])); + } + record = &StructAtOffset (record, record_size); + } + } + + return hb_pair (format1, format2); + } +}; + + +} +} +} + +#endif // OT_LAYOUT_GPOS_PAIRPOSFORMAT1_HH diff --git a/libs/harfbuzz/src/OT/Layout/GPOS/PairPosFormat2.hh b/libs/harfbuzz/src/OT/Layout/GPOS/PairPosFormat2.hh new file mode 100644 index 00000000..83b093b9 --- /dev/null +++ b/libs/harfbuzz/src/OT/Layout/GPOS/PairPosFormat2.hh @@ -0,0 +1,334 @@ +#ifndef OT_LAYOUT_GPOS_PAIRPOSFORMAT2_HH +#define OT_LAYOUT_GPOS_PAIRPOSFORMAT2_HH + +#include "ValueFormat.hh" + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +template +struct PairPosFormat2_4 +{ + protected: + HBUINT16 format; /* Format identifier--format = 2 */ + typename Types::template OffsetTo + coverage; /* Offset to Coverage table--from + * beginning of subtable */ + ValueFormat valueFormat1; /* ValueRecord definition--for the + * first glyph of the pair--may be zero + * (0) */ + ValueFormat valueFormat2; /* ValueRecord definition--for the + * second glyph of the pair--may be + * zero (0) */ + typename Types::template OffsetTo + classDef1; /* Offset to ClassDef table--from + * beginning of PairPos subtable--for + * the first glyph of the pair */ + typename Types::template OffsetTo + classDef2; /* Offset to ClassDef table--from + * beginning of PairPos subtable--for + * the second glyph of the pair */ + HBUINT16 class1Count; /* Number of classes in ClassDef1 + * table--includes Class0 */ + HBUINT16 class2Count; /* Number of classes in ClassDef2 + * table--includes Class0 */ + ValueRecord values; /* Matrix of value pairs: + * class1-major, class2-minor, + * Each entry has value1 and value2 */ + public: + DEFINE_SIZE_ARRAY (10 + 3 * Types::size, values); + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (!(c->check_struct (this) + && coverage.sanitize (c, this) + && classDef1.sanitize (c, this) + && classDef2.sanitize (c, this))) return_trace (false); + + unsigned int len1 = valueFormat1.get_len (); + unsigned int len2 = valueFormat2.get_len (); + unsigned int stride = len1 + len2; + unsigned int record_size = valueFormat1.get_size () + valueFormat2.get_size (); + unsigned int count = (unsigned int) class1Count * (unsigned int) class2Count; + return_trace (c->check_range ((const void *) values, + count, + record_size) && + valueFormat1.sanitize_values_stride_unsafe (c, this, &values[0], count, stride) && + valueFormat2.sanitize_values_stride_unsafe (c, this, &values[len1], count, stride)); + } + + bool intersects (const hb_set_t *glyphs) const + { + return (this+coverage).intersects (glyphs) && + (this+classDef2).intersects (glyphs); + } + + void closure_lookups (hb_closure_lookups_context_t *c) const {} + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const + { + if (!intersects (c->glyph_set)) return; + if ((!valueFormat1.has_device ()) && (!valueFormat2.has_device ())) return; + + hb_set_t klass1_glyphs, klass2_glyphs; + if (!(this+classDef1).collect_coverage (&klass1_glyphs)) return; + if (!(this+classDef2).collect_coverage (&klass2_glyphs)) return; + + hb_set_t class1_set, class2_set; + for (const unsigned cp : + c->glyph_set->iter () | hb_filter (this + coverage)) + { + if (!klass1_glyphs.has (cp)) class1_set.add (0); + else + { + unsigned klass1 = (this+classDef1).get (cp); + class1_set.add (klass1); + } + } + + class2_set.add (0); + for (const unsigned cp : + c->glyph_set->iter () | hb_filter (klass2_glyphs)) + { + unsigned klass2 = (this+classDef2).get (cp); + class2_set.add (klass2); + } + + if (class1_set.is_empty () + || class2_set.is_empty () + || (class2_set.get_population() == 1 && class2_set.has(0))) + return; + + unsigned len1 = valueFormat1.get_len (); + unsigned len2 = valueFormat2.get_len (); + const hb_array_t values_array = values.as_array ((unsigned)class1Count * (unsigned) class2Count * (len1 + len2)); + for (const unsigned class1_idx : class1_set.iter ()) + { + for (const unsigned class2_idx : class2_set.iter ()) + { + unsigned start_offset = (class1_idx * (unsigned) class2Count + class2_idx) * (len1 + len2); + if (valueFormat1.has_device ()) + valueFormat1.collect_variation_indices (c, this, values_array.sub_array (start_offset, len1)); + + if (valueFormat2.has_device ()) + valueFormat2.collect_variation_indices (c, this, values_array.sub_array (start_offset+len1, len2)); + } + } + } + + void collect_glyphs (hb_collect_glyphs_context_t *c) const + { + if (unlikely (!(this+coverage).collect_coverage (c->input))) return; + if (unlikely (!(this+classDef2).collect_coverage (c->input))) return; + } + + const Coverage &get_coverage () const { return this+coverage; } + + bool apply (hb_ot_apply_context_t *c) const + { + TRACE_APPLY (this); + hb_buffer_t *buffer = c->buffer; + unsigned int index = (this+coverage).get_coverage (buffer->cur().codepoint); + if (likely (index == NOT_COVERED)) return_trace (false); + + hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input; + skippy_iter.reset (buffer->idx, 1); + unsigned unsafe_to; + if (!skippy_iter.next (&unsafe_to)) + { + buffer->unsafe_to_concat (buffer->idx, unsafe_to); + return_trace (false); + } + + unsigned int len1 = valueFormat1.get_len (); + unsigned int len2 = valueFormat2.get_len (); + unsigned int record_len = len1 + len2; + + unsigned int klass1 = (this+classDef1).get_class (buffer->cur().codepoint); + unsigned int klass2 = (this+classDef2).get_class (buffer->info[skippy_iter.idx].codepoint); + if (unlikely (klass1 >= class1Count || klass2 >= class2Count)) + { + buffer->unsafe_to_concat (buffer->idx, skippy_iter.idx + 1); + return_trace (false); + } + + const Value *v = &values[record_len * (klass1 * class2Count + klass2)]; + + bool applied_first = false, applied_second = false; + + + /* Isolate simple kerning and apply it half to each side. + * Results in better cursor positinoing / underline drawing. + * + * Disabled, because causes issues... :-( + * https://github.com/harfbuzz/harfbuzz/issues/3408 + * https://github.com/harfbuzz/harfbuzz/pull/3235#issuecomment-1029814978 + */ +#ifndef HB_SPLIT_KERN + if (0) +#endif + { + if (!len2) + { + const hb_direction_t dir = buffer->props.direction; + const bool horizontal = HB_DIRECTION_IS_HORIZONTAL (dir); + const bool backward = HB_DIRECTION_IS_BACKWARD (dir); + unsigned mask = horizontal ? ValueFormat::xAdvance : ValueFormat::yAdvance; + if (backward) + mask |= mask >> 2; /* Add eg. xPlacement in RTL. */ + /* Add Devices. */ + mask |= mask << 4; + + if (valueFormat1 & ~mask) + goto bail; + + /* Is simple kern. Apply value on an empty position slot, + * then split it between sides. */ + + hb_glyph_position_t pos{}; + if (valueFormat1.apply_value (c, this, v, pos)) + { + hb_position_t *src = &pos.x_advance; + hb_position_t *dst1 = &buffer->cur_pos().x_advance; + hb_position_t *dst2 = &buffer->pos[skippy_iter.idx].x_advance; + unsigned i = horizontal ? 0 : 1; + + hb_position_t kern = src[i]; + hb_position_t kern1 = kern >> 1; + hb_position_t kern2 = kern - kern1; + + if (!backward) + { + dst1[i] += kern1; + dst2[i] += kern2; + dst2[i + 2] += kern2; + } + else + { + dst1[i] += kern1; + dst1[i + 2] += src[i + 2] - kern2; + dst2[i] += kern2; + } + + applied_first = applied_second = kern != 0; + goto success; + } + goto boring; + } + } + bail: + + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->message (c->font, + "kerning glyphs at %d,%d", + c->buffer->idx, skippy_iter.idx); + } + + applied_first = valueFormat1.apply_value (c, this, v, buffer->cur_pos()); + applied_second = valueFormat2.apply_value (c, this, v + len1, buffer->pos[skippy_iter.idx]); + + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->message (c->font, + "kerned glyphs at %d,%d", + c->buffer->idx, skippy_iter.idx); + } + + success: + if (applied_first || applied_second) + buffer->unsafe_to_break (buffer->idx, skippy_iter.idx + 1); + else + boring: + buffer->unsafe_to_concat (buffer->idx, skippy_iter.idx + 1); + + + buffer->idx = skippy_iter.idx; + if (len2) + buffer->idx++; + + return_trace (true); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + out->format = format; + + hb_map_t klass1_map; + out->classDef1.serialize_subset (c, classDef1, this, &klass1_map, true, true, &(this + coverage)); + out->class1Count = klass1_map.get_population (); + + hb_map_t klass2_map; + out->classDef2.serialize_subset (c, classDef2, this, &klass2_map, true, false); + out->class2Count = klass2_map.get_population (); + + unsigned len1 = valueFormat1.get_len (); + unsigned len2 = valueFormat2.get_len (); + + hb_pair_t newFormats = hb_pair (valueFormat1, valueFormat2); + if (c->plan->flags & HB_SUBSET_FLAGS_NO_HINTING) + newFormats = compute_effective_value_formats (klass1_map, klass2_map); + + out->valueFormat1 = newFormats.first; + out->valueFormat2 = newFormats.second; + + if (c->plan->all_axes_pinned) + { + out->valueFormat1 = out->valueFormat1.drop_device_table_flags (); + out->valueFormat2 = out->valueFormat2.drop_device_table_flags (); + } + + for (unsigned class1_idx : + hb_range ((unsigned) class1Count) | hb_filter (klass1_map)) + { + for (unsigned class2_idx : + hb_range ((unsigned) class2Count) | hb_filter (klass2_map)) + { + unsigned idx = (class1_idx * (unsigned) class2Count + class2_idx) * (len1 + len2); + valueFormat1.copy_values (c->serializer, out->valueFormat1, this, &values[idx], c->plan->layout_variation_idx_delta_map); + valueFormat2.copy_values (c->serializer, out->valueFormat2, this, &values[idx + len1], c->plan->layout_variation_idx_delta_map); + } + } + + const hb_set_t &glyphset = *c->plan->glyphset_gsub (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + auto it = + + hb_iter (this+coverage) + | hb_filter (glyphset) + | hb_map_retains_sorting (glyph_map) + ; + + out->coverage.serialize_serialize (c->serializer, it); + return_trace (out->class1Count && out->class2Count && bool (it)); + } + + + hb_pair_t compute_effective_value_formats (const hb_map_t& klass1_map, + const hb_map_t& klass2_map) const + { + unsigned len1 = valueFormat1.get_len (); + unsigned len2 = valueFormat2.get_len (); + + unsigned format1 = 0; + unsigned format2 = 0; + + for (unsigned class1_idx : + hb_range ((unsigned) class1Count) | hb_filter (klass1_map)) + { + for (unsigned class2_idx : + hb_range ((unsigned) class2Count) | hb_filter (klass2_map)) + { + unsigned idx = (class1_idx * (unsigned) class2Count + class2_idx) * (len1 + len2); + format1 = format1 | valueFormat1.get_effective_format (&values[idx]); + format2 = format2 | valueFormat2.get_effective_format (&values[idx + len1]); + } + } + + return hb_pair (format1, format2); + } +}; + +} +} +} + +#endif // OT_LAYOUT_GPOS_PAIRPOSFORMAT2_HH diff --git a/libs/harfbuzz/src/OT/Layout/GPOS/PairSet.hh b/libs/harfbuzz/src/OT/Layout/GPOS/PairSet.hh new file mode 100644 index 00000000..aa48d933 --- /dev/null +++ b/libs/harfbuzz/src/OT/Layout/GPOS/PairSet.hh @@ -0,0 +1,189 @@ +#ifndef OT_LAYOUT_GPOS_PAIRSET_HH +#define OT_LAYOUT_GPOS_PAIRSET_HH + +#include "PairValueRecord.hh" + +namespace OT { +namespace Layout { +namespace GPOS_impl { + + +template +struct PairSet +{ + template + friend struct PairPosFormat1_3; + + using PairValueRecord = GPOS_impl::PairValueRecord; + + protected: + HBUINT16 len; /* Number of PairValueRecords */ + PairValueRecord firstPairValueRecord; + /* Array of PairValueRecords--ordered + * by GlyphID of the second glyph */ + public: + DEFINE_SIZE_MIN (2); + + struct sanitize_closure_t + { + const ValueFormat *valueFormats; + unsigned int len1; /* valueFormats[0].get_len() */ + unsigned int stride; /* 1 + len1 + len2 */ + }; + + bool sanitize (hb_sanitize_context_t *c, const sanitize_closure_t *closure) const + { + TRACE_SANITIZE (this); + if (!(c->check_struct (this) + && c->check_range (&firstPairValueRecord, + len, + HBUINT16::static_size, + closure->stride))) return_trace (false); + + unsigned int count = len; + const PairValueRecord *record = &firstPairValueRecord; + return_trace (closure->valueFormats[0].sanitize_values_stride_unsafe (c, this, &record->values[0], count, closure->stride) && + closure->valueFormats[1].sanitize_values_stride_unsafe (c, this, &record->values[closure->len1], count, closure->stride)); + } + + bool intersects (const hb_set_t *glyphs, + const ValueFormat *valueFormats) const + { + unsigned int len1 = valueFormats[0].get_len (); + unsigned int len2 = valueFormats[1].get_len (); + unsigned int record_size = HBUINT16::static_size * (1 + len1 + len2); + + const PairValueRecord *record = &firstPairValueRecord; + unsigned int count = len; + for (unsigned int i = 0; i < count; i++) + { + if (glyphs->has (record->secondGlyph)) + return true; + record = &StructAtOffset (record, record_size); + } + return false; + } + + void collect_glyphs (hb_collect_glyphs_context_t *c, + const ValueFormat *valueFormats) const + { + unsigned int len1 = valueFormats[0].get_len (); + unsigned int len2 = valueFormats[1].get_len (); + unsigned int record_size = HBUINT16::static_size * (1 + len1 + len2); + + const PairValueRecord *record = &firstPairValueRecord; + c->input->add_array (&record->secondGlyph, len, record_size); + } + + void collect_variation_indices (hb_collect_variation_indices_context_t *c, + const ValueFormat *valueFormats) const + { + unsigned len1 = valueFormats[0].get_len (); + unsigned len2 = valueFormats[1].get_len (); + unsigned record_size = HBUINT16::static_size * (1 + len1 + len2); + + const PairValueRecord *record = &firstPairValueRecord; + unsigned count = len; + for (unsigned i = 0; i < count; i++) + { + if (c->glyph_set->has (record->secondGlyph)) + { record->collect_variation_indices (c, valueFormats, this); } + + record = &StructAtOffset (record, record_size); + } + } + + bool apply (hb_ot_apply_context_t *c, + const ValueFormat *valueFormats, + unsigned int pos) const + { + TRACE_APPLY (this); + hb_buffer_t *buffer = c->buffer; + unsigned int len1 = valueFormats[0].get_len (); + unsigned int len2 = valueFormats[1].get_len (); + unsigned int record_size = HBUINT16::static_size * (1 + len1 + len2); + + const PairValueRecord *record = hb_bsearch (buffer->info[pos].codepoint, + &firstPairValueRecord, + len, + record_size); + if (record) + { + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->message (c->font, + "kerning glyphs at %d,%d", + c->buffer->idx, pos); + } + + bool applied_first = valueFormats[0].apply_value (c, this, &record->values[0], buffer->cur_pos()); + bool applied_second = valueFormats[1].apply_value (c, this, &record->values[len1], buffer->pos[pos]); + + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->message (c->font, + "kerned glyphs at %d,%d", + c->buffer->idx, pos); + } + + if (applied_first || applied_second) + buffer->unsafe_to_break (buffer->idx, pos + 1); + if (len2) + pos++; + + buffer->idx = pos; + return_trace (true); + } + buffer->unsafe_to_concat (buffer->idx, pos + 1); + return_trace (false); + } + + bool subset (hb_subset_context_t *c, + const ValueFormat valueFormats[2], + const ValueFormat newFormats[2]) const + { + TRACE_SUBSET (this); + auto snap = c->serializer->snapshot (); + + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + out->len = 0; + + const hb_set_t &glyphset = *c->plan->glyphset_gsub (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + unsigned len1 = valueFormats[0].get_len (); + unsigned len2 = valueFormats[1].get_len (); + unsigned record_size = HBUINT16::static_size + Value::static_size * (len1 + len2); + + typename PairValueRecord::context_t context = + { + this, + valueFormats, + newFormats, + len1, + &glyph_map, + c->plan->layout_variation_idx_delta_map + }; + + const PairValueRecord *record = &firstPairValueRecord; + unsigned count = len, num = 0; + for (unsigned i = 0; i < count; i++) + { + if (glyphset.has (record->secondGlyph) + && record->subset (c, &context)) num++; + record = &StructAtOffset (record, record_size); + } + + out->len = num; + if (!num) c->serializer->revert (snap); + return_trace (num); + } +}; + + +} +} +} + +#endif // OT_LAYOUT_GPOS_PAIRSET_HH diff --git a/libs/harfbuzz/src/OT/Layout/GPOS/PairValueRecord.hh b/libs/harfbuzz/src/OT/Layout/GPOS/PairValueRecord.hh new file mode 100644 index 00000000..32224777 --- /dev/null +++ b/libs/harfbuzz/src/OT/Layout/GPOS/PairValueRecord.hh @@ -0,0 +1,99 @@ +#ifndef OT_LAYOUT_GPOS_PAIRVALUERECORD_HH +#define OT_LAYOUT_GPOS_PAIRVALUERECORD_HH + +#include "ValueFormat.hh" + +namespace OT { +namespace Layout { +namespace GPOS_impl { + + +template +struct PairValueRecord +{ + template + friend struct PairSet; + + protected: + typename Types::HBGlyphID + secondGlyph; /* GlyphID of second glyph in the + * pair--first glyph is listed in the + * Coverage table */ + ValueRecord values; /* Positioning data for the first glyph + * followed by for second glyph */ + public: + DEFINE_SIZE_ARRAY (Types::size, values); + + int cmp (hb_codepoint_t k) const + { return secondGlyph.cmp (k); } + + struct context_t + { + const void *base; + const ValueFormat *valueFormats; + const ValueFormat *newFormats; + unsigned len1; /* valueFormats[0].get_len() */ + const hb_map_t *glyph_map; + const hb_hashmap_t> *layout_variation_idx_delta_map; + }; + + bool subset (hb_subset_context_t *c, + context_t *closure) const + { + TRACE_SERIALIZE (this); + auto *s = c->serializer; + auto *out = s->start_embed (*this); + if (unlikely (!s->extend_min (out))) return_trace (false); + + out->secondGlyph = (*closure->glyph_map)[secondGlyph]; + + closure->valueFormats[0].copy_values (s, + closure->newFormats[0], + closure->base, &values[0], + closure->layout_variation_idx_delta_map); + closure->valueFormats[1].copy_values (s, + closure->newFormats[1], + closure->base, + &values[closure->len1], + closure->layout_variation_idx_delta_map); + + return_trace (true); + } + + void collect_variation_indices (hb_collect_variation_indices_context_t *c, + const ValueFormat *valueFormats, + const void *base) const + { + unsigned record1_len = valueFormats[0].get_len (); + unsigned record2_len = valueFormats[1].get_len (); + const hb_array_t values_array = values.as_array (record1_len + record2_len); + + if (valueFormats[0].has_device ()) + valueFormats[0].collect_variation_indices (c, base, values_array.sub_array (0, record1_len)); + + if (valueFormats[1].has_device ()) + valueFormats[1].collect_variation_indices (c, base, values_array.sub_array (record1_len, record2_len)); + } + + bool intersects (const hb_set_t& glyphset) const + { + return glyphset.has(secondGlyph); + } + + const Value* get_values_1 () const + { + return &values[0]; + } + + const Value* get_values_2 (ValueFormat format1) const + { + return &values[format1.get_len ()]; + } +}; + + +} +} +} + +#endif // OT_LAYOUT_GPOS_PAIRVALUERECORD_HH diff --git a/libs/harfbuzz/src/OT/Layout/GPOS/PosLookup.hh b/libs/harfbuzz/src/OT/Layout/GPOS/PosLookup.hh new file mode 100644 index 00000000..c4e57bb5 --- /dev/null +++ b/libs/harfbuzz/src/OT/Layout/GPOS/PosLookup.hh @@ -0,0 +1,79 @@ +#ifndef OT_LAYOUT_GPOS_POSLOOKUP_HH +#define OT_LAYOUT_GPOS_POSLOOKUP_HH + +#include "PosLookupSubTable.hh" +#include "../../../hb-ot-layout-common.hh" + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +struct PosLookup : Lookup +{ + using SubTable = PosLookupSubTable; + + const SubTable& get_subtable (unsigned int i) const + { return Lookup::get_subtable (i); } + + bool is_reverse () const + { + return false; + } + + bool apply (hb_ot_apply_context_t *c) const + { + TRACE_APPLY (this); + return_trace (dispatch (c)); + } + + bool intersects (const hb_set_t *glyphs) const + { + hb_intersects_context_t c (glyphs); + return dispatch (&c); + } + + hb_collect_glyphs_context_t::return_t collect_glyphs (hb_collect_glyphs_context_t *c) const + { return dispatch (c); } + + hb_closure_lookups_context_t::return_t closure_lookups (hb_closure_lookups_context_t *c, unsigned this_index) const + { + if (c->is_lookup_visited (this_index)) + return hb_closure_lookups_context_t::default_return_value (); + + c->set_lookup_visited (this_index); + if (!intersects (c->glyphs)) + { + c->set_lookup_inactive (this_index); + return hb_closure_lookups_context_t::default_return_value (); + } + + hb_closure_lookups_context_t::return_t ret = dispatch (c); + return ret; + } + + template + void collect_coverage (set_t *glyphs) const + { + hb_collect_coverage_context_t c (glyphs); + dispatch (&c); + } + + template + static typename context_t::return_t dispatch_recurse_func (context_t *c, unsigned int lookup_index); + + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const + { return Lookup::dispatch (c, std::forward (ds)...); } + + bool subset (hb_subset_context_t *c) const + { return Lookup::subset (c); } + + bool sanitize (hb_sanitize_context_t *c) const + { return Lookup::sanitize (c); } +}; + +} +} +} + +#endif /* OT_LAYOUT_GPOS_POSLOOKUP_HH */ diff --git a/libs/harfbuzz/src/OT/Layout/GPOS/PosLookupSubTable.hh b/libs/harfbuzz/src/OT/Layout/GPOS/PosLookupSubTable.hh new file mode 100644 index 00000000..c19fbc32 --- /dev/null +++ b/libs/harfbuzz/src/OT/Layout/GPOS/PosLookupSubTable.hh @@ -0,0 +1,79 @@ +#ifndef OT_LAYOUT_GPOS_POSLOOKUPSUBTABLE_HH +#define OT_LAYOUT_GPOS_POSLOOKUPSUBTABLE_HH + +#include "SinglePos.hh" +#include "PairPos.hh" +#include "CursivePos.hh" +#include "MarkBasePos.hh" +#include "MarkLigPos.hh" +#include "MarkMarkPos.hh" +#include "ContextPos.hh" +#include "ChainContextPos.hh" +#include "ExtensionPos.hh" + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +struct PosLookupSubTable +{ + friend struct ::OT::Lookup; + friend struct PosLookup; + + enum Type { + Single = 1, + Pair = 2, + Cursive = 3, + MarkBase = 4, + MarkLig = 5, + MarkMark = 6, + Context = 7, + ChainContext = 8, + Extension = 9 + }; + + template + typename context_t::return_t dispatch (context_t *c, unsigned int lookup_type, Ts&&... ds) const + { + TRACE_DISPATCH (this, lookup_type); + switch (lookup_type) { + case Single: return_trace (u.single.dispatch (c, std::forward (ds)...)); + case Pair: return_trace (u.pair.dispatch (c, std::forward (ds)...)); + case Cursive: return_trace (u.cursive.dispatch (c, std::forward (ds)...)); + case MarkBase: return_trace (u.markBase.dispatch (c, std::forward (ds)...)); + case MarkLig: return_trace (u.markLig.dispatch (c, std::forward (ds)...)); + case MarkMark: return_trace (u.markMark.dispatch (c, std::forward (ds)...)); + case Context: return_trace (u.context.dispatch (c, std::forward (ds)...)); + case ChainContext: return_trace (u.chainContext.dispatch (c, std::forward (ds)...)); + case Extension: return_trace (u.extension.dispatch (c, std::forward (ds)...)); + default: return_trace (c->default_return_value ()); + } + } + + bool intersects (const hb_set_t *glyphs, unsigned int lookup_type) const + { + hb_intersects_context_t c (glyphs); + return dispatch (&c, lookup_type); + } + + protected: + union { + SinglePos single; + PairPos pair; + CursivePos cursive; + MarkBasePos markBase; + MarkLigPos markLig; + MarkMarkPos markMark; + ContextPos context; + ChainContextPos chainContext; + ExtensionPos extension; + } u; + public: + DEFINE_SIZE_MIN (0); +}; + +} +} +} + +#endif /* HB_OT_LAYOUT_GPOS_POSLOOKUPSUBTABLE_HH */ diff --git a/libs/harfbuzz/src/OT/Layout/GPOS/SinglePos.hh b/libs/harfbuzz/src/OT/Layout/GPOS/SinglePos.hh new file mode 100644 index 00000000..6dce3e63 --- /dev/null +++ b/libs/harfbuzz/src/OT/Layout/GPOS/SinglePos.hh @@ -0,0 +1,100 @@ +#ifndef OT_LAYOUT_GPOS_SINGLEPOS_HH +#define OT_LAYOUT_GPOS_SINGLEPOS_HH + +#include "SinglePosFormat1.hh" +#include "SinglePosFormat2.hh" + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +struct SinglePos +{ + protected: + union { + HBUINT16 format; /* Format identifier */ + SinglePosFormat1 format1; + SinglePosFormat2 format2; + } u; + + public: + template + unsigned get_format (Iterator glyph_val_iter_pairs) + { + hb_array_t first_val_iter = hb_second (*glyph_val_iter_pairs); + + for (const auto iter : glyph_val_iter_pairs) + for (const auto _ : hb_zip (iter.second, first_val_iter)) + if (_.first != _.second) + return 2; + + return 1; + } + + template + void serialize (hb_serialize_context_t *c, + const SrcLookup* src, + Iterator glyph_val_iter_pairs, + const hb_hashmap_t> *layout_variation_idx_delta_map, + bool all_axes_pinned) + { + if (unlikely (!c->extend_min (u.format))) return; + unsigned format = 2; + ValueFormat new_format = src->get_value_format (); + + if (all_axes_pinned) + new_format = new_format.drop_device_table_flags (); + + if (glyph_val_iter_pairs) + format = get_format (glyph_val_iter_pairs); + + u.format = format; + switch (u.format) { + case 1: u.format1.serialize (c, + src, + glyph_val_iter_pairs, + new_format, + layout_variation_idx_delta_map); + return; + case 2: u.format2.serialize (c, + src, + glyph_val_iter_pairs, + new_format, + layout_variation_idx_delta_map); + return; + default:return; + } + } + + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const + { + TRACE_DISPATCH (this, u.format); + if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ()); + switch (u.format) { + case 1: return_trace (c->dispatch (u.format1, std::forward (ds)...)); + case 2: return_trace (c->dispatch (u.format2, std::forward (ds)...)); + default:return_trace (c->default_return_value ()); + } + } +}; + + +template +static void +SinglePos_serialize (hb_serialize_context_t *c, + const SrcLookup *src, + Iterator it, + const hb_hashmap_t> *layout_variation_idx_delta_map, + bool all_axes_pinned) +{ c->start_embed ()->serialize (c, src, it, layout_variation_idx_delta_map, all_axes_pinned); } + + +} +} +} + +#endif /* OT_LAYOUT_GPOS_SINGLEPOS_HH */ diff --git a/libs/harfbuzz/src/OT/Layout/GPOS/SinglePosFormat1.hh b/libs/harfbuzz/src/OT/Layout/GPOS/SinglePosFormat1.hh new file mode 100644 index 00000000..b4c9fc3d --- /dev/null +++ b/libs/harfbuzz/src/OT/Layout/GPOS/SinglePosFormat1.hh @@ -0,0 +1,156 @@ +#ifndef OT_LAYOUT_GPOS_SINGLEPOSFORMAT1_HH +#define OT_LAYOUT_GPOS_SINGLEPOSFORMAT1_HH + +#include "Common.hh" +#include "ValueFormat.hh" + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +struct SinglePosFormat1 +{ + protected: + HBUINT16 format; /* Format identifier--format = 1 */ + Offset16To + coverage; /* Offset to Coverage table--from + * beginning of subtable */ + ValueFormat valueFormat; /* Defines the types of data in the + * ValueRecord */ + ValueRecord values; /* Defines positioning + * value(s)--applied to all glyphs in + * the Coverage table */ + public: + DEFINE_SIZE_ARRAY (6, values); + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + coverage.sanitize (c, this) && + valueFormat.sanitize_value (c, this, values)); + } + + bool intersects (const hb_set_t *glyphs) const + { return (this+coverage).intersects (glyphs); } + + void closure_lookups (hb_closure_lookups_context_t *c) const {} + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const + { + if (!valueFormat.has_device ()) return; + + hb_set_t intersection; + (this+coverage).intersect_set (*c->glyph_set, intersection); + if (!intersection) return; + + valueFormat.collect_variation_indices (c, this, values.as_array (valueFormat.get_len ())); + } + + void collect_glyphs (hb_collect_glyphs_context_t *c) const + { if (unlikely (!(this+coverage).collect_coverage (c->input))) return; } + + const Coverage &get_coverage () const { return this+coverage; } + + ValueFormat get_value_format () const { return valueFormat; } + + bool apply (hb_ot_apply_context_t *c) const + { + TRACE_APPLY (this); + hb_buffer_t *buffer = c->buffer; + unsigned int index = (this+coverage).get_coverage (buffer->cur().codepoint); + if (likely (index == NOT_COVERED)) return_trace (false); + + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->message (c->font, + "positioning glyph at %d", + c->buffer->idx); + } + + valueFormat.apply_value (c, this, values, buffer->cur_pos()); + + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->message (c->font, + "positioned glyph at %d", + c->buffer->idx); + } + + buffer->idx++; + return_trace (true); + } + + bool + position_single (hb_font_t *font, + hb_direction_t direction, + hb_codepoint_t gid, + hb_glyph_position_t &pos) const + { + unsigned int index = (this+coverage).get_coverage (gid); + if (likely (index == NOT_COVERED)) return false; + + /* This is ugly... */ + hb_buffer_t buffer; + buffer.props.direction = direction; + OT::hb_ot_apply_context_t c (1, font, &buffer); + + valueFormat.apply_value (&c, this, values, pos); + return true; + } + + template + void serialize (hb_serialize_context_t *c, + const SrcLookup *src, + Iterator it, + ValueFormat newFormat, + const hb_hashmap_t> *layout_variation_idx_delta_map) + { + if (unlikely (!c->extend_min (this))) return; + if (unlikely (!c->check_assign (valueFormat, + newFormat, + HB_SERIALIZE_ERROR_INT_OVERFLOW))) return; + + for (const hb_array_t& _ : + it | hb_map (hb_second)) + { + src->get_value_format ().copy_values (c, newFormat, src, &_, layout_variation_idx_delta_map); + // Only serialize the first entry in the iterator, the rest are assumed to + // be the same. + break; + } + + auto glyphs = + + it + | hb_map_retains_sorting (hb_first) + ; + + coverage.serialize_serialize (c, glyphs); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + const hb_set_t &glyphset = *c->plan->glyphset_gsub (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + hb_set_t intersection; + (this+coverage).intersect_set (glyphset, intersection); + + auto it = + + hb_iter (intersection) + | hb_map_retains_sorting (glyph_map) + | hb_zip (hb_repeat (values.as_array (valueFormat.get_len ()))) + ; + + bool ret = bool (it); + SinglePos_serialize (c->serializer, this, it, c->plan->layout_variation_idx_delta_map, c->plan->all_axes_pinned); + return_trace (ret); + } +}; + +} +} +} + +#endif /* OT_LAYOUT_GPOS_SINGLEPOSFORMAT1_HH */ diff --git a/libs/harfbuzz/src/OT/Layout/GPOS/SinglePosFormat2.hh b/libs/harfbuzz/src/OT/Layout/GPOS/SinglePosFormat2.hh new file mode 100644 index 00000000..c7795115 --- /dev/null +++ b/libs/harfbuzz/src/OT/Layout/GPOS/SinglePosFormat2.hh @@ -0,0 +1,176 @@ +#ifndef OT_LAYOUT_GPOS_SINGLEPOSFORMAT2_HH +#define OT_LAYOUT_GPOS_SINGLEPOSFORMAT2_HH + +#include "Common.hh" + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +struct SinglePosFormat2 +{ + protected: + HBUINT16 format; /* Format identifier--format = 2 */ + Offset16To + coverage; /* Offset to Coverage table--from + * beginning of subtable */ + ValueFormat valueFormat; /* Defines the types of data in the + * ValueRecord */ + HBUINT16 valueCount; /* Number of ValueRecords */ + ValueRecord values; /* Array of ValueRecords--positioning + * values applied to glyphs */ + public: + DEFINE_SIZE_ARRAY (8, values); + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + coverage.sanitize (c, this) && + valueFormat.sanitize_values (c, this, values, valueCount)); + } + + bool intersects (const hb_set_t *glyphs) const + { return (this+coverage).intersects (glyphs); } + + void closure_lookups (hb_closure_lookups_context_t *c) const {} + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const + { + if (!valueFormat.has_device ()) return; + + auto it = + + hb_zip (this+coverage, hb_range ((unsigned) valueCount)) + | hb_filter (c->glyph_set, hb_first) + ; + + if (!it) return; + + unsigned sub_length = valueFormat.get_len (); + const hb_array_t values_array = values.as_array (valueCount * sub_length); + + for (unsigned i : + it + | hb_map (hb_second)) + valueFormat.collect_variation_indices (c, this, values_array.sub_array (i * sub_length, sub_length)); + + } + + void collect_glyphs (hb_collect_glyphs_context_t *c) const + { if (unlikely (!(this+coverage).collect_coverage (c->input))) return; } + + const Coverage &get_coverage () const { return this+coverage; } + + ValueFormat get_value_format () const { return valueFormat; } + + bool apply (hb_ot_apply_context_t *c) const + { + TRACE_APPLY (this); + hb_buffer_t *buffer = c->buffer; + unsigned int index = (this+coverage).get_coverage (buffer->cur().codepoint); + if (likely (index == NOT_COVERED)) return_trace (false); + + if (unlikely (index >= valueCount)) return_trace (false); + + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->message (c->font, + "positioning glyph at %d", + c->buffer->idx); + } + + valueFormat.apply_value (c, this, + &values[index * valueFormat.get_len ()], + buffer->cur_pos()); + + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->message (c->font, + "positioned glyph at %d", + c->buffer->idx); + } + + buffer->idx++; + return_trace (true); + } + + bool + position_single (hb_font_t *font, + hb_direction_t direction, + hb_codepoint_t gid, + hb_glyph_position_t &pos) const + { + unsigned int index = (this+coverage).get_coverage (gid); + if (likely (index == NOT_COVERED)) return false; + if (unlikely (index >= valueCount)) return false; + + /* This is ugly... */ + hb_buffer_t buffer; + buffer.props.direction = direction; + OT::hb_ot_apply_context_t c (1, font, &buffer); + + valueFormat.apply_value (&c, this, + &values[index * valueFormat.get_len ()], + pos); + return true; + } + + + template + void serialize (hb_serialize_context_t *c, + const SrcLookup *src, + Iterator it, + ValueFormat newFormat, + const hb_hashmap_t> *layout_variation_idx_delta_map) + { + auto out = c->extend_min (this); + if (unlikely (!out)) return; + if (unlikely (!c->check_assign (valueFormat, newFormat, HB_SERIALIZE_ERROR_INT_OVERFLOW))) return; + if (unlikely (!c->check_assign (valueCount, it.len (), HB_SERIALIZE_ERROR_ARRAY_OVERFLOW))) return; + + + it + | hb_map (hb_second) + | hb_apply ([&] (hb_array_t _) + { src->get_value_format ().copy_values (c, newFormat, src, &_, layout_variation_idx_delta_map); }) + ; + + auto glyphs = + + it + | hb_map_retains_sorting (hb_first) + ; + + coverage.serialize_serialize (c, glyphs); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + const hb_set_t &glyphset = *c->plan->glyphset_gsub (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + unsigned sub_length = valueFormat.get_len (); + auto values_array = values.as_array (valueCount * sub_length); + + auto it = + + hb_zip (this+coverage, hb_range ((unsigned) valueCount)) + | hb_filter (glyphset, hb_first) + | hb_map_retains_sorting ([&] (const hb_pair_t& _) + { + return hb_pair (glyph_map[_.first], + values_array.sub_array (_.second * sub_length, + sub_length)); + }) + ; + + bool ret = bool (it); + SinglePos_serialize (c->serializer, this, it, c->plan->layout_variation_idx_delta_map, c->plan->all_axes_pinned); + return_trace (ret); + } +}; + + +} +} +} + +#endif /* OT_LAYOUT_GPOS_SINGLEPOSFORMAT2_HH */ diff --git a/libs/harfbuzz/src/OT/Layout/GPOS/ValueFormat.hh b/libs/harfbuzz/src/OT/Layout/GPOS/ValueFormat.hh new file mode 100644 index 00000000..26a40f01 --- /dev/null +++ b/libs/harfbuzz/src/OT/Layout/GPOS/ValueFormat.hh @@ -0,0 +1,394 @@ +#ifndef OT_LAYOUT_GPOS_VALUEFORMAT_HH +#define OT_LAYOUT_GPOS_VALUEFORMAT_HH + +#include "../../../hb-ot-layout-gsubgpos.hh" + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +typedef HBUINT16 Value; + +typedef UnsizedArrayOf ValueRecord; + +struct ValueFormat : HBUINT16 +{ + enum Flags { + xPlacement = 0x0001u, /* Includes horizontal adjustment for placement */ + yPlacement = 0x0002u, /* Includes vertical adjustment for placement */ + xAdvance = 0x0004u, /* Includes horizontal adjustment for advance */ + yAdvance = 0x0008u, /* Includes vertical adjustment for advance */ + xPlaDevice = 0x0010u, /* Includes horizontal Device table for placement */ + yPlaDevice = 0x0020u, /* Includes vertical Device table for placement */ + xAdvDevice = 0x0040u, /* Includes horizontal Device table for advance */ + yAdvDevice = 0x0080u, /* Includes vertical Device table for advance */ + ignored = 0x0F00u, /* Was used in TrueType Open for MM fonts */ + reserved = 0xF000u, /* For future use */ + + devices = 0x00F0u /* Mask for having any Device table */ + }; + +/* All fields are options. Only those available advance the value pointer. */ +#if 0 + HBINT16 xPlacement; /* Horizontal adjustment for + * placement--in design units */ + HBINT16 yPlacement; /* Vertical adjustment for + * placement--in design units */ + HBINT16 xAdvance; /* Horizontal adjustment for + * advance--in design units (only used + * for horizontal writing) */ + HBINT16 yAdvance; /* Vertical adjustment for advance--in + * design units (only used for vertical + * writing) */ + Offset16To xPlaDevice; /* Offset to Device table for + * horizontal placement--measured from + * beginning of PosTable (may be NULL) */ + Offset16To yPlaDevice; /* Offset to Device table for vertical + * placement--measured from beginning + * of PosTable (may be NULL) */ + Offset16To xAdvDevice; /* Offset to Device table for + * horizontal advance--measured from + * beginning of PosTable (may be NULL) */ + Offset16To yAdvDevice; /* Offset to Device table for vertical + * advance--measured from beginning of + * PosTable (may be NULL) */ +#endif + + IntType& operator = (uint16_t i) { v = i; return *this; } + + unsigned int get_len () const { return hb_popcount ((unsigned int) *this); } + unsigned int get_size () const { return get_len () * Value::static_size; } + + hb_vector_t get_device_table_indices () const { + unsigned i = 0; + hb_vector_t result; + unsigned format = *this; + + if (format & xPlacement) i++; + if (format & yPlacement) i++; + if (format & xAdvance) i++; + if (format & yAdvance) i++; + + if (format & xPlaDevice) result.push (i++); + if (format & yPlaDevice) result.push (i++); + if (format & xAdvDevice) result.push (i++); + if (format & yAdvDevice) result.push (i++); + + return result; + } + + bool apply_value (hb_ot_apply_context_t *c, + const void *base, + const Value *values, + hb_glyph_position_t &glyph_pos) const + { + bool ret = false; + unsigned int format = *this; + if (!format) return ret; + + hb_font_t *font = c->font; + bool horizontal = +#ifndef HB_NO_VERTICAL + HB_DIRECTION_IS_HORIZONTAL (c->direction) +#else + true +#endif + ; + + if (format & xPlacement) glyph_pos.x_offset += font->em_scale_x (get_short (values++, &ret)); + if (format & yPlacement) glyph_pos.y_offset += font->em_scale_y (get_short (values++, &ret)); + if (format & xAdvance) { + if (likely (horizontal)) glyph_pos.x_advance += font->em_scale_x (get_short (values, &ret)); + values++; + } + /* y_advance values grow downward but font-space grows upward, hence negation */ + if (format & yAdvance) { + if (unlikely (!horizontal)) glyph_pos.y_advance -= font->em_scale_y (get_short (values, &ret)); + values++; + } + + if (!has_device ()) return ret; + + bool use_x_device = font->x_ppem || font->num_coords; + bool use_y_device = font->y_ppem || font->num_coords; + + if (!use_x_device && !use_y_device) return ret; + + const VariationStore &store = c->var_store; + auto *cache = c->var_store_cache; + + /* pixel -> fractional pixel */ + if (format & xPlaDevice) { + if (use_x_device) glyph_pos.x_offset += (base + get_device (values, &ret)).get_x_delta (font, store, cache); + values++; + } + if (format & yPlaDevice) { + if (use_y_device) glyph_pos.y_offset += (base + get_device (values, &ret)).get_y_delta (font, store, cache); + values++; + } + if (format & xAdvDevice) { + if (horizontal && use_x_device) glyph_pos.x_advance += (base + get_device (values, &ret)).get_x_delta (font, store, cache); + values++; + } + if (format & yAdvDevice) { + /* y_advance values grow downward but font-space grows upward, hence negation */ + if (!horizontal && use_y_device) glyph_pos.y_advance -= (base + get_device (values, &ret)).get_y_delta (font, store, cache); + values++; + } + return ret; + } + + unsigned int get_effective_format (const Value *values) const + { + unsigned int format = *this; + for (unsigned flag = xPlacement; flag <= yAdvDevice; flag = flag << 1) { + if (format & flag) should_drop (*values++, (Flags) flag, &format); + } + + return format; + } + + template + unsigned int get_effective_format (Iterator it) const { + unsigned int new_format = 0; + + for (const hb_array_t& values : it) + new_format = new_format | get_effective_format (&values); + + return new_format; + } + + void copy_values (hb_serialize_context_t *c, + unsigned int new_format, + const void *base, + const Value *values, + const hb_hashmap_t> *layout_variation_idx_delta_map) const + { + unsigned int format = *this; + if (!format) return; + + HBINT16 *x_placement = nullptr, *y_placement = nullptr, *x_adv = nullptr, *y_adv = nullptr; + if (format & xPlacement) x_placement = copy_value (c, new_format, xPlacement, *values++); + if (format & yPlacement) y_placement = copy_value (c, new_format, yPlacement, *values++); + if (format & xAdvance) x_adv = copy_value (c, new_format, xAdvance, *values++); + if (format & yAdvance) y_adv = copy_value (c, new_format, yAdvance, *values++); + + if (format & xPlaDevice) + { + add_delta_to_value (x_placement, base, values, layout_variation_idx_delta_map); + copy_device (c, base, values++, layout_variation_idx_delta_map, new_format, xPlaDevice); + } + + if (format & yPlaDevice) + { + add_delta_to_value (y_placement, base, values, layout_variation_idx_delta_map); + copy_device (c, base, values++, layout_variation_idx_delta_map, new_format, yPlaDevice); + } + + if (format & xAdvDevice) + { + add_delta_to_value (x_adv, base, values, layout_variation_idx_delta_map); + copy_device (c, base, values++, layout_variation_idx_delta_map, new_format, xAdvDevice); + } + + if (format & yAdvDevice) + { + add_delta_to_value (y_adv, base, values, layout_variation_idx_delta_map); + copy_device (c, base, values++, layout_variation_idx_delta_map, new_format, yAdvDevice); + } + } + + HBINT16* copy_value (hb_serialize_context_t *c, + unsigned int new_format, + Flags flag, + Value value) const + { + // Filter by new format. + if (!(new_format & flag)) return nullptr; + return reinterpret_cast (c->copy (value)); + } + + void collect_variation_indices (hb_collect_variation_indices_context_t *c, + const void *base, + const hb_array_t& values) const + { + unsigned format = *this; + unsigned i = 0; + if (format & xPlacement) i++; + if (format & yPlacement) i++; + if (format & xAdvance) i++; + if (format & yAdvance) i++; + if (format & xPlaDevice) + { + (base + get_device (&(values[i]))).collect_variation_indices (c); + i++; + } + + if (format & ValueFormat::yPlaDevice) + { + (base + get_device (&(values[i]))).collect_variation_indices (c); + i++; + } + + if (format & ValueFormat::xAdvDevice) + { + + (base + get_device (&(values[i]))).collect_variation_indices (c); + i++; + } + + if (format & ValueFormat::yAdvDevice) + { + + (base + get_device (&(values[i]))).collect_variation_indices (c); + i++; + } + } + + unsigned drop_device_table_flags () const + { + unsigned format = *this; + for (unsigned flag = xPlaDevice; flag <= yAdvDevice; flag = flag << 1) + format = format & ~flag; + + return format; + } + + private: + bool sanitize_value_devices (hb_sanitize_context_t *c, const void *base, const Value *values) const + { + unsigned int format = *this; + + if (format & xPlacement) values++; + if (format & yPlacement) values++; + if (format & xAdvance) values++; + if (format & yAdvance) values++; + + if ((format & xPlaDevice) && !get_device (values++).sanitize (c, base)) return false; + if ((format & yPlaDevice) && !get_device (values++).sanitize (c, base)) return false; + if ((format & xAdvDevice) && !get_device (values++).sanitize (c, base)) return false; + if ((format & yAdvDevice) && !get_device (values++).sanitize (c, base)) return false; + + return true; + } + + static inline Offset16To& get_device (Value* value) + { + return *static_cast *> (value); + } + static inline const Offset16To& get_device (const Value* value, bool *worked=nullptr) + { + if (worked) *worked |= bool (*value); + return *static_cast *> (value); + } + + void add_delta_to_value (HBINT16 *value, + const void *base, + const Value *src_value, + const hb_hashmap_t> *layout_variation_idx_delta_map) const + { + if (!value) return; + unsigned varidx = (base + get_device (src_value)).get_variation_index (); + hb_pair_t *varidx_delta; + if (!layout_variation_idx_delta_map->has (varidx, &varidx_delta)) return; + + *value += hb_second (*varidx_delta); + } + + bool copy_device (hb_serialize_context_t *c, const void *base, + const Value *src_value, + const hb_hashmap_t> *layout_variation_idx_delta_map, + unsigned int new_format, Flags flag) const + { + // Filter by new format. + if (!(new_format & flag)) return true; + + Value *dst_value = c->copy (*src_value); + + if (!dst_value) return false; + if (*dst_value == 0) return true; + + *dst_value = 0; + c->push (); + if ((base + get_device (src_value)).copy (c, layout_variation_idx_delta_map)) + { + c->add_link (*dst_value, c->pop_pack ()); + return true; + } + else + { + c->pop_discard (); + return false; + } + } + + static inline const HBINT16& get_short (const Value* value, bool *worked=nullptr) + { + if (worked) *worked |= bool (*value); + return *reinterpret_cast (value); + } + + public: + + bool has_device () const + { + unsigned int format = *this; + return (format & devices) != 0; + } + + bool sanitize_value (hb_sanitize_context_t *c, const void *base, const Value *values) const + { + TRACE_SANITIZE (this); + return_trace (c->check_range (values, get_size ()) && (!has_device () || sanitize_value_devices (c, base, values))); + } + + bool sanitize_values (hb_sanitize_context_t *c, const void *base, const Value *values, unsigned int count) const + { + TRACE_SANITIZE (this); + unsigned int len = get_len (); + + if (!c->check_range (values, count, get_size ())) return_trace (false); + + if (!has_device ()) return_trace (true); + + for (unsigned int i = 0; i < count; i++) { + if (!sanitize_value_devices (c, base, values)) + return_trace (false); + values += len; + } + + return_trace (true); + } + + /* Just sanitize referenced Device tables. Doesn't check the values themselves. */ + bool sanitize_values_stride_unsafe (hb_sanitize_context_t *c, const void *base, const Value *values, unsigned int count, unsigned int stride) const + { + TRACE_SANITIZE (this); + + if (!has_device ()) return_trace (true); + + for (unsigned int i = 0; i < count; i++) { + if (!sanitize_value_devices (c, base, values)) + return_trace (false); + values += stride; + } + + return_trace (true); + } + + private: + + void should_drop (Value value, Flags flag, unsigned int* format) const + { + if (value) return; + *format = *format & ~flag; + } + +}; + +} +} +} + +#endif // #ifndef OT_LAYOUT_GPOS_VALUEFORMAT_HH diff --git a/libs/harfbuzz/src/OT/Layout/GSUB/AlternateSet.hh b/libs/harfbuzz/src/OT/Layout/GSUB/AlternateSet.hh new file mode 100644 index 00000000..4a9e9672 --- /dev/null +++ b/libs/harfbuzz/src/OT/Layout/GSUB/AlternateSet.hh @@ -0,0 +1,126 @@ +#ifndef OT_LAYOUT_GSUB_ALTERNATESET_HH +#define OT_LAYOUT_GSUB_ALTERNATESET_HH + +#include "Common.hh" + +namespace OT { +namespace Layout { +namespace GSUB_impl { + +template +struct AlternateSet +{ + protected: + Array16Of + alternates; /* Array of alternate GlyphIDs--in + * arbitrary order */ + public: + DEFINE_SIZE_ARRAY (2, alternates); + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (alternates.sanitize (c)); + } + + bool intersects (const hb_set_t *glyphs) const + { return hb_any (alternates, glyphs); } + + void closure (hb_closure_context_t *c) const + { c->output->add_array (alternates.arrayZ, alternates.len); } + + void collect_glyphs (hb_collect_glyphs_context_t *c) const + { c->output->add_array (alternates.arrayZ, alternates.len); } + + bool apply (hb_ot_apply_context_t *c) const + { + TRACE_APPLY (this); + unsigned int count = alternates.len; + + if (unlikely (!count)) return_trace (false); + + hb_mask_t glyph_mask = c->buffer->cur().mask; + hb_mask_t lookup_mask = c->lookup_mask; + + /* Note: This breaks badly if two features enabled this lookup together. */ + unsigned int shift = hb_ctz (lookup_mask); + unsigned int alt_index = ((lookup_mask & glyph_mask) >> shift); + + /* If alt_index is MAX_VALUE, randomize feature if it is the rand feature. */ + if (alt_index == HB_OT_MAP_MAX_VALUE && c->random) + { + /* Maybe we can do better than unsafe-to-break all; but since we are + * changing random state, it would be hard to track that. Good 'nough. */ + c->buffer->unsafe_to_break (0, c->buffer->len); + alt_index = c->random_number () % count + 1; + } + + if (unlikely (alt_index > count || alt_index == 0)) return_trace (false); + + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->sync_so_far (); + c->buffer->message (c->font, + "replacing glyph at %d (alternate substitution)", + c->buffer->idx); + } + + c->replace_glyph (alternates[alt_index - 1]); + + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->message (c->font, + "replaced glyph at %d (alternate substitution)", + c->buffer->idx - 1); + } + + return_trace (true); + } + + unsigned + get_alternates (unsigned start_offset, + unsigned *alternate_count /* IN/OUT. May be NULL. */, + hb_codepoint_t *alternate_glyphs /* OUT. May be NULL. */) const + { + if (alternates.len && alternate_count) + { + + alternates.sub_array (start_offset, alternate_count) + | hb_sink (hb_array (alternate_glyphs, *alternate_count)) + ; + } + return alternates.len; + } + + template + bool serialize (hb_serialize_context_t *c, + Iterator alts) + { + TRACE_SERIALIZE (this); + return_trace (alternates.serialize (c, alts)); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + const hb_set_t &glyphset = *c->plan->glyphset_gsub (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + auto it = + + hb_iter (alternates) + | hb_filter (glyphset) + | hb_map (glyph_map) + ; + + auto *out = c->serializer->start_embed (*this); + return_trace (out->serialize (c->serializer, it) && + out->alternates); + } +}; + +} +} +} + + +#endif /* OT_LAYOUT_GSUB_ALTERNATESET_HH */ diff --git a/libs/harfbuzz/src/OT/Layout/GSUB/AlternateSubst.hh b/libs/harfbuzz/src/OT/Layout/GSUB/AlternateSubst.hh new file mode 100644 index 00000000..37406179 --- /dev/null +++ b/libs/harfbuzz/src/OT/Layout/GSUB/AlternateSubst.hh @@ -0,0 +1,62 @@ +#ifndef OT_LAYOUT_GSUB_ALTERNATESUBST_HH +#define OT_LAYOUT_GSUB_ALTERNATESUBST_HH + +#include "AlternateSubstFormat1.hh" +#include "Common.hh" + +namespace OT { +namespace Layout { +namespace GSUB_impl { + +struct AlternateSubst +{ + protected: + union { + HBUINT16 format; /* Format identifier */ + AlternateSubstFormat1_2 format1; +#ifndef HB_NO_BORING_EXPANSION + AlternateSubstFormat1_2 format2; +#endif + } u; + public: + + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const + { + TRACE_DISPATCH (this, u.format); + if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ()); + switch (u.format) { + case 1: return_trace (c->dispatch (u.format1, std::forward (ds)...)); +#ifndef HB_NO_BORING_EXPANSION + case 2: return_trace (c->dispatch (u.format2, std::forward (ds)...)); +#endif + default:return_trace (c->default_return_value ()); + } + } + + /* TODO This function is unused and not updated to 24bit GIDs. Should be done by using + * iterators. While at it perhaps using iterator of arrays of hb_codepoint_t instead. */ + bool serialize (hb_serialize_context_t *c, + hb_sorted_array_t glyphs, + hb_array_t alternate_len_list, + hb_array_t alternate_glyphs_list) + { + TRACE_SERIALIZE (this); + if (unlikely (!c->extend_min (u.format))) return_trace (false); + unsigned int format = 1; + u.format = format; + switch (u.format) { + case 1: return_trace (u.format1.serialize (c, glyphs, alternate_len_list, alternate_glyphs_list)); + default:return_trace (false); + } + } + + /* TODO subset() should choose format. */ + +}; + +} +} +} + +#endif /* OT_LAYOUT_GSUB_ALTERNATESUBST_HH */ diff --git a/libs/harfbuzz/src/OT/Layout/GSUB/AlternateSubstFormat1.hh b/libs/harfbuzz/src/OT/Layout/GSUB/AlternateSubstFormat1.hh new file mode 100644 index 00000000..adec65d5 --- /dev/null +++ b/libs/harfbuzz/src/OT/Layout/GSUB/AlternateSubstFormat1.hh @@ -0,0 +1,128 @@ +#ifndef OT_LAYOUT_GSUB_ALTERNATESUBSTFORMAT1_HH +#define OT_LAYOUT_GSUB_ALTERNATESUBSTFORMAT1_HH + +#include "AlternateSet.hh" +#include "Common.hh" + +namespace OT { +namespace Layout { +namespace GSUB_impl { + +template +struct AlternateSubstFormat1_2 +{ + protected: + HBUINT16 format; /* Format identifier--format = 1 */ + typename Types::template OffsetTo + coverage; /* Offset to Coverage table--from + * beginning of Substitution table */ + Array16Of>> + alternateSet; /* Array of AlternateSet tables + * ordered by Coverage Index */ + public: + DEFINE_SIZE_ARRAY (2 + 2 * Types::size, alternateSet); + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (coverage.sanitize (c, this) && alternateSet.sanitize (c, this)); + } + + bool intersects (const hb_set_t *glyphs) const + { return (this+coverage).intersects (glyphs); } + + bool may_have_non_1to1 () const + { return false; } + + void closure (hb_closure_context_t *c) const + { + + hb_zip (this+coverage, alternateSet) + | hb_filter (c->parent_active_glyphs (), hb_first) + | hb_map (hb_second) + | hb_map (hb_add (this)) + | hb_apply ([c] (const AlternateSet &_) { _.closure (c); }) + ; + } + + void closure_lookups (hb_closure_lookups_context_t *c) const {} + + void collect_glyphs (hb_collect_glyphs_context_t *c) const + { + if (unlikely (!(this+coverage).collect_coverage (c->input))) return; + + hb_zip (this+coverage, alternateSet) + | hb_map (hb_second) + | hb_map (hb_add (this)) + | hb_apply ([c] (const AlternateSet &_) { _.collect_glyphs (c); }) + ; + } + + const Coverage &get_coverage () const { return this+coverage; } + + bool would_apply (hb_would_apply_context_t *c) const + { return c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED; } + + unsigned + get_glyph_alternates (hb_codepoint_t gid, + unsigned start_offset, + unsigned *alternate_count /* IN/OUT. May be NULL. */, + hb_codepoint_t *alternate_glyphs /* OUT. May be NULL. */) const + { return (this+alternateSet[(this+coverage).get_coverage (gid)]) + .get_alternates (start_offset, alternate_count, alternate_glyphs); } + + bool apply (hb_ot_apply_context_t *c) const + { + TRACE_APPLY (this); + + unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint); + if (likely (index == NOT_COVERED)) return_trace (false); + + return_trace ((this+alternateSet[index]).apply (c)); + } + + bool serialize (hb_serialize_context_t *c, + hb_sorted_array_t glyphs, + hb_array_t alternate_len_list, + hb_array_t alternate_glyphs_list) + { + TRACE_SERIALIZE (this); + if (unlikely (!c->extend_min (this))) return_trace (false); + if (unlikely (!alternateSet.serialize (c, glyphs.length))) return_trace (false); + for (unsigned int i = 0; i < glyphs.length; i++) + { + unsigned int alternate_len = alternate_len_list[i]; + if (unlikely (!alternateSet[i] + .serialize_serialize (c, alternate_glyphs_list.sub_array (0, alternate_len)))) + return_trace (false); + alternate_glyphs_list += alternate_len; + } + return_trace (coverage.serialize_serialize (c, glyphs)); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + const hb_set_t &glyphset = *c->plan->glyphset_gsub (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + out->format = format; + + hb_sorted_vector_t new_coverage; + + hb_zip (this+coverage, alternateSet) + | hb_filter (glyphset, hb_first) + | hb_filter (subset_offset_array (c, out->alternateSet, this), hb_second) + | hb_map (hb_first) + | hb_map (glyph_map) + | hb_sink (new_coverage) + ; + out->coverage.serialize_serialize (c->serializer, new_coverage.iter ()); + return_trace (bool (new_coverage)); + } +}; + +} +} +} + +#endif /* OT_LAYOUT_GSUB_ALTERNATESUBSTFORMAT1_HH */ diff --git a/libs/harfbuzz/src/OT/Layout/GSUB/ChainContextSubst.hh b/libs/harfbuzz/src/OT/Layout/GSUB/ChainContextSubst.hh new file mode 100644 index 00000000..08fd779f --- /dev/null +++ b/libs/harfbuzz/src/OT/Layout/GSUB/ChainContextSubst.hh @@ -0,0 +1,18 @@ +#ifndef OT_LAYOUT_GSUB_CHAINCONTEXTSUBST_HH +#define OT_LAYOUT_GSUB_CHAINCONTEXTSUBST_HH + +// TODO(garretrieger): move to new layout. +#include "../../../hb-ot-layout-gsubgpos.hh" +#include "Common.hh" + +namespace OT { +namespace Layout { +namespace GSUB_impl { + +struct ChainContextSubst : ChainContext {}; + +} +} +} + +#endif /* OT_LAYOUT_GSUB_CHAINCONTEXTSUBST_HH */ diff --git a/libs/harfbuzz/src/OT/Layout/GSUB/Common.hh b/libs/harfbuzz/src/OT/Layout/GSUB/Common.hh new file mode 100644 index 00000000..968bba04 --- /dev/null +++ b/libs/harfbuzz/src/OT/Layout/GSUB/Common.hh @@ -0,0 +1,21 @@ +#ifndef OT_LAYOUT_GSUB_COMMON_HH +#define OT_LAYOUT_GSUB_COMMON_HH + +#include "../../../hb-serialize.hh" +#include "../../../hb-ot-layout-gsubgpos.hh" + +namespace OT { +namespace Layout { +namespace GSUB_impl { + +typedef hb_pair_t hb_codepoint_pair_t; + +template +static void SingleSubst_serialize (hb_serialize_context_t *c, + Iterator it); + +} +} +} + +#endif /* OT_LAYOUT_GSUB_COMMON_HH */ diff --git a/libs/harfbuzz/src/OT/Layout/GSUB/ContextSubst.hh b/libs/harfbuzz/src/OT/Layout/GSUB/ContextSubst.hh new file mode 100644 index 00000000..9f8cb46b --- /dev/null +++ b/libs/harfbuzz/src/OT/Layout/GSUB/ContextSubst.hh @@ -0,0 +1,18 @@ +#ifndef OT_LAYOUT_GSUB_CONTEXTSUBST_HH +#define OT_LAYOUT_GSUB_CONTEXTSUBST_HH + +// TODO(garretrieger): move to new layout. +#include "../../../hb-ot-layout-gsubgpos.hh" +#include "Common.hh" + +namespace OT { +namespace Layout { +namespace GSUB_impl { + +struct ContextSubst : Context {}; + +} +} +} + +#endif /* OT_LAYOUT_GSUB_CONTEXTSUBST_HH */ diff --git a/libs/harfbuzz/src/OT/Layout/GSUB/ExtensionSubst.hh b/libs/harfbuzz/src/OT/Layout/GSUB/ExtensionSubst.hh new file mode 100644 index 00000000..831a7dfa --- /dev/null +++ b/libs/harfbuzz/src/OT/Layout/GSUB/ExtensionSubst.hh @@ -0,0 +1,22 @@ +#ifndef OT_LAYOUT_GSUB_EXTENSIONSUBST_HH +#define OT_LAYOUT_GSUB_EXTENSIONSUBST_HH + +// TODO(garretrieger): move to new layout. +#include "../../../hb-ot-layout-gsubgpos.hh" +#include "Common.hh" + +namespace OT { +namespace Layout { +namespace GSUB_impl { + +struct ExtensionSubst : Extension +{ + typedef struct SubstLookupSubTable SubTable; + bool is_reverse () const; +}; + +} +} +} + +#endif /* OT_LAYOUT_GSUB_EXTENSIONSUBST_HH */ diff --git a/libs/harfbuzz/src/OT/Layout/GSUB/GSUB.hh b/libs/harfbuzz/src/OT/Layout/GSUB/GSUB.hh new file mode 100644 index 00000000..900cf603 --- /dev/null +++ b/libs/harfbuzz/src/OT/Layout/GSUB/GSUB.hh @@ -0,0 +1,61 @@ +#ifndef OT_LAYOUT_GSUB_GSUB_HH +#define OT_LAYOUT_GSUB_GSUB_HH + +#include "../../../hb-ot-layout-gsubgpos.hh" +#include "Common.hh" +#include "SubstLookup.hh" + +namespace OT { + +using Layout::GSUB_impl::SubstLookup; + +namespace Layout { + +/* + * GSUB -- Glyph Substitution + * https://docs.microsoft.com/en-us/typography/opentype/spec/gsub + */ + +struct GSUB : GSUBGPOS +{ + using Lookup = SubstLookup; + + static constexpr hb_tag_t tableTag = HB_OT_TAG_GSUB; + + const SubstLookup& get_lookup (unsigned int i) const + { return static_cast (GSUBGPOS::get_lookup (i)); } + + bool subset (hb_subset_context_t *c) const + { + hb_subset_layout_context_t l (c, tableTag); + return GSUBGPOS::subset (&l); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (GSUBGPOS::sanitize (c)); + } + + HB_INTERNAL bool is_blocklisted (hb_blob_t *blob, + hb_face_t *face) const; + + void closure_lookups (hb_face_t *face, + const hb_set_t *glyphs, + hb_set_t *lookup_indexes /* IN/OUT */) const + { GSUBGPOS::closure_lookups (face, glyphs, lookup_indexes); } + + typedef GSUBGPOS::accelerator_t accelerator_t; +}; + + +} + +struct GSUB_accelerator_t : Layout::GSUB::accelerator_t { + GSUB_accelerator_t (hb_face_t *face) : Layout::GSUB::accelerator_t (face) {} +}; + + +} + +#endif /* OT_LAYOUT_GSUB_GSUB_HH */ diff --git a/libs/harfbuzz/src/OT/Layout/GSUB/Ligature.hh b/libs/harfbuzz/src/OT/Layout/GSUB/Ligature.hh new file mode 100644 index 00000000..6caa80e0 --- /dev/null +++ b/libs/harfbuzz/src/OT/Layout/GSUB/Ligature.hh @@ -0,0 +1,187 @@ +#ifndef OT_LAYOUT_GSUB_LIGATURE_HH +#define OT_LAYOUT_GSUB_LIGATURE_HH + +#include "Common.hh" + +namespace OT { +namespace Layout { +namespace GSUB_impl { + +template +struct Ligature +{ + protected: + typename Types::HBGlyphID + ligGlyph; /* GlyphID of ligature to substitute */ + HeadlessArrayOf + component; /* Array of component GlyphIDs--start + * with the second component--ordered + * in writing direction */ + public: + DEFINE_SIZE_ARRAY (Types::size + 2, component); + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (ligGlyph.sanitize (c) && component.sanitize (c)); + } + + bool intersects (const hb_set_t *glyphs) const + { return hb_all (component, glyphs); } + + void closure (hb_closure_context_t *c) const + { + if (!intersects (c->glyphs)) return; + c->output->add (ligGlyph); + } + + void collect_glyphs (hb_collect_glyphs_context_t *c) const + { + c->input->add_array (component.arrayZ, component.get_length ()); + c->output->add (ligGlyph); + } + + bool would_apply (hb_would_apply_context_t *c) const + { + if (c->len != component.lenP1) + return false; + + for (unsigned int i = 1; i < c->len; i++) + if (likely (c->glyphs[i] != component[i])) + return false; + + return true; + } + + bool apply (hb_ot_apply_context_t *c) const + { + TRACE_APPLY (this); + unsigned int count = component.lenP1; + + if (unlikely (!count)) return_trace (false); + + /* Special-case to make it in-place and not consider this + * as a "ligated" substitution. */ + if (unlikely (count == 1)) + { + + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->sync_so_far (); + c->buffer->message (c->font, + "replacing glyph at %d (ligature substitution)", + c->buffer->idx); + } + + c->replace_glyph (ligGlyph); + + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->message (c->font, + "replaced glyph at %d (ligature substitution)", + c->buffer->idx - 1); + } + + return_trace (true); + } + + unsigned int total_component_count = 0; + + unsigned int match_end = 0; + unsigned int match_positions[HB_MAX_CONTEXT_LENGTH]; + + if (likely (!match_input (c, count, + &component[1], + match_glyph, + nullptr, + &match_end, + match_positions, + &total_component_count))) + { + c->buffer->unsafe_to_concat (c->buffer->idx, match_end); + return_trace (false); + } + + unsigned pos = 0; + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + unsigned delta = c->buffer->sync_so_far (); + + pos = c->buffer->idx; + + char buf[HB_MAX_CONTEXT_LENGTH * 16] = {0}; + char *p = buf; + + match_end += delta; + for (unsigned i = 0; i < count; i++) + { + match_positions[i] += delta; + if (i) + *p++ = ','; + snprintf (p, sizeof(buf), "%u", match_positions[i]); + p += strlen(p); + } + + c->buffer->message (c->font, + "ligating glyphs at %s", + buf); + } + + ligate_input (c, + count, + match_positions, + match_end, + ligGlyph, + total_component_count); + + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->sync_so_far (); + c->buffer->message (c->font, + "ligated glyph at %d", + pos); + } + + return_trace (true); + } + + template + bool serialize (hb_serialize_context_t *c, + hb_codepoint_t ligature, + Iterator components /* Starting from second */) + { + TRACE_SERIALIZE (this); + if (unlikely (!c->extend_min (this))) return_trace (false); + ligGlyph = ligature; + if (unlikely (!component.serialize (c, components))) return_trace (false); + return_trace (true); + } + + bool subset (hb_subset_context_t *c, unsigned coverage_idx) const + { + TRACE_SUBSET (this); + const hb_set_t &glyphset = *c->plan->glyphset_gsub (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + if (!intersects (&glyphset) || !glyphset.has (ligGlyph)) return_trace (false); + // Ensure Coverage table is always packed after this. + c->serializer->add_virtual_link (coverage_idx); + + auto it = + + hb_iter (component) + | hb_map (glyph_map) + ; + + auto *out = c->serializer->start_embed (*this); + return_trace (out->serialize (c->serializer, + glyph_map[ligGlyph], + it)); } +}; + + +} +} +} + +#endif /* OT_LAYOUT_GSUB_LIGATURE_HH */ diff --git a/libs/harfbuzz/src/OT/Layout/GSUB/LigatureSet.hh b/libs/harfbuzz/src/OT/Layout/GSUB/LigatureSet.hh new file mode 100644 index 00000000..637cec71 --- /dev/null +++ b/libs/harfbuzz/src/OT/Layout/GSUB/LigatureSet.hh @@ -0,0 +1,119 @@ +#ifndef OT_LAYOUT_GSUB_LIGATURESET_HH +#define OT_LAYOUT_GSUB_LIGATURESET_HH + +#include "Common.hh" +#include "Ligature.hh" + +namespace OT { +namespace Layout { +namespace GSUB_impl { + +template +struct LigatureSet +{ + protected: + Array16OfOffset16To> + ligature; /* Array LigatureSet tables + * ordered by preference */ + public: + DEFINE_SIZE_ARRAY (2, ligature); + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (ligature.sanitize (c, this)); + } + + bool intersects (const hb_set_t *glyphs) const + { + return + + hb_iter (ligature) + | hb_map (hb_add (this)) + | hb_map ([glyphs] (const Ligature &_) { return _.intersects (glyphs); }) + | hb_any + ; + } + + void closure (hb_closure_context_t *c) const + { + + hb_iter (ligature) + | hb_map (hb_add (this)) + | hb_apply ([c] (const Ligature &_) { _.closure (c); }) + ; + } + + void collect_glyphs (hb_collect_glyphs_context_t *c) const + { + + hb_iter (ligature) + | hb_map (hb_add (this)) + | hb_apply ([c] (const Ligature &_) { _.collect_glyphs (c); }) + ; + } + + bool would_apply (hb_would_apply_context_t *c) const + { + return + + hb_iter (ligature) + | hb_map (hb_add (this)) + | hb_map ([c] (const Ligature &_) { return _.would_apply (c); }) + | hb_any + ; + } + + bool apply (hb_ot_apply_context_t *c) const + { + TRACE_APPLY (this); + unsigned int num_ligs = ligature.len; + for (unsigned int i = 0; i < num_ligs; i++) + { + const auto &lig = this+ligature[i]; + if (lig.apply (c)) return_trace (true); + } + + return_trace (false); + } + + bool serialize (hb_serialize_context_t *c, + hb_array_t ligatures, + hb_array_t component_count_list, + hb_array_t &component_list /* Starting from second for each ligature */) + { + TRACE_SERIALIZE (this); + if (unlikely (!c->extend_min (this))) return_trace (false); + if (unlikely (!ligature.serialize (c, ligatures.length))) return_trace (false); + for (unsigned int i = 0; i < ligatures.length; i++) + { + unsigned int component_count = (unsigned) hb_max ((int) component_count_list[i] - 1, 0); + if (unlikely (!ligature[i].serialize_serialize (c, + ligatures[i], + component_list.sub_array (0, component_count)))) + return_trace (false); + component_list += component_count; + } + return_trace (true); + } + + bool subset (hb_subset_context_t *c, unsigned coverage_idx) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + + + hb_iter (ligature) + | hb_filter (subset_offset_array (c, out->ligature, this, coverage_idx)) + | hb_drain + ; + + if (bool (out->ligature)) + // Ensure Coverage table is always packed after this. + c->serializer->add_virtual_link (coverage_idx); + + return_trace (bool (out->ligature)); + } +}; + +} +} +} + +#endif /* OT_LAYOUT_GSUB_LIGATURESET_HH */ diff --git a/libs/harfbuzz/src/OT/Layout/GSUB/LigatureSubst.hh b/libs/harfbuzz/src/OT/Layout/GSUB/LigatureSubst.hh new file mode 100644 index 00000000..63707972 --- /dev/null +++ b/libs/harfbuzz/src/OT/Layout/GSUB/LigatureSubst.hh @@ -0,0 +1,71 @@ +#ifndef OT_LAYOUT_GSUB_LIGATURESUBST_HH +#define OT_LAYOUT_GSUB_LIGATURESUBST_HH + +#include "Common.hh" +#include "LigatureSubstFormat1.hh" + +namespace OT { +namespace Layout { +namespace GSUB_impl { + +struct LigatureSubst +{ + protected: + union { + HBUINT16 format; /* Format identifier */ + LigatureSubstFormat1_2 format1; +#ifndef HB_NO_BORING_EXPANSION + LigatureSubstFormat1_2 format2; +#endif + } u; + + public: + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const + { + TRACE_DISPATCH (this, u.format); + if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ()); + switch (u.format) { + case 1: return_trace (c->dispatch (u.format1, std::forward (ds)...)); +#ifndef HB_NO_BORING_EXPANSION + case 2: return_trace (c->dispatch (u.format2, std::forward (ds)...)); +#endif + default:return_trace (c->default_return_value ()); + } + } + + /* TODO This function is only used by small GIDs, and not updated to 24bit GIDs. Should + * be done by using iterators. While at it perhaps using iterator of arrays of hb_codepoint_t + * instead. */ + bool serialize (hb_serialize_context_t *c, + hb_sorted_array_t first_glyphs, + hb_array_t ligature_per_first_glyph_count_list, + hb_array_t ligatures_list, + hb_array_t component_count_list, + hb_array_t component_list /* Starting from second for each ligature */) + { + TRACE_SERIALIZE (this); + if (unlikely (!c->extend_min (u.format))) return_trace (false); + unsigned int format = 1; + u.format = format; + switch (u.format) { + case 1: return_trace (u.format1.serialize (c, + first_glyphs, + ligature_per_first_glyph_count_list, + ligatures_list, + component_count_list, + component_list)); + default:return_trace (false); + } + } + + /* TODO subset() should choose format. */ + +}; + + +} +} +} + +#endif /* OT_LAYOUT_GSUB_LIGATURESUBST_HH */ diff --git a/libs/harfbuzz/src/OT/Layout/GSUB/LigatureSubstFormat1.hh b/libs/harfbuzz/src/OT/Layout/GSUB/LigatureSubstFormat1.hh new file mode 100644 index 00000000..32b642c3 --- /dev/null +++ b/libs/harfbuzz/src/OT/Layout/GSUB/LigatureSubstFormat1.hh @@ -0,0 +1,166 @@ +#ifndef OT_LAYOUT_GSUB_LIGATURESUBSTFORMAT1_HH +#define OT_LAYOUT_GSUB_LIGATURESUBSTFORMAT1_HH + +#include "Common.hh" +#include "LigatureSet.hh" + +namespace OT { +namespace Layout { +namespace GSUB_impl { + +template +struct LigatureSubstFormat1_2 +{ + protected: + HBUINT16 format; /* Format identifier--format = 1 */ + typename Types::template OffsetTo + coverage; /* Offset to Coverage table--from + * beginning of Substitution table */ + Array16Of>> + ligatureSet; /* Array LigatureSet tables + * ordered by Coverage Index */ + public: + DEFINE_SIZE_ARRAY (4 + Types::size, ligatureSet); + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (coverage.sanitize (c, this) && ligatureSet.sanitize (c, this)); + } + + bool intersects (const hb_set_t *glyphs) const + { + return + + hb_zip (this+coverage, ligatureSet) + | hb_filter (*glyphs, hb_first) + | hb_map (hb_second) + | hb_map ([this, glyphs] (const typename Types::template OffsetTo> &_) + { return (this+_).intersects (glyphs); }) + | hb_any + ; + } + + bool may_have_non_1to1 () const + { return true; } + + void closure (hb_closure_context_t *c) const + { + + hb_zip (this+coverage, ligatureSet) + | hb_filter (c->parent_active_glyphs (), hb_first) + | hb_map (hb_second) + | hb_map (hb_add (this)) + | hb_apply ([c] (const LigatureSet &_) { _.closure (c); }) + ; + + } + + void closure_lookups (hb_closure_lookups_context_t *c) const {} + + void collect_glyphs (hb_collect_glyphs_context_t *c) const + { + if (unlikely (!(this+coverage).collect_coverage (c->input))) return; + + + hb_zip (this+coverage, ligatureSet) + | hb_map (hb_second) + | hb_map (hb_add (this)) + | hb_apply ([c] (const LigatureSet &_) { _.collect_glyphs (c); }) + ; + } + + const Coverage &get_coverage () const { return this+coverage; } + + bool would_apply (hb_would_apply_context_t *c) const + { + unsigned int index = (this+coverage).get_coverage (c->glyphs[0]); + if (likely (index == NOT_COVERED)) return false; + + const auto &lig_set = this+ligatureSet[index]; + return lig_set.would_apply (c); + } + + bool apply (hb_ot_apply_context_t *c) const + { + TRACE_APPLY (this); + + unsigned int index = (this+coverage).get_coverage (c->buffer->cur ().codepoint); + if (likely (index == NOT_COVERED)) return_trace (false); + + const auto &lig_set = this+ligatureSet[index]; + return_trace (lig_set.apply (c)); + } + + bool serialize (hb_serialize_context_t *c, + hb_sorted_array_t first_glyphs, + hb_array_t ligature_per_first_glyph_count_list, + hb_array_t ligatures_list, + hb_array_t component_count_list, + hb_array_t component_list /* Starting from second for each ligature */) + { + TRACE_SERIALIZE (this); + if (unlikely (!c->extend_min (this))) return_trace (false); + if (unlikely (!ligatureSet.serialize (c, first_glyphs.length))) return_trace (false); + for (unsigned int i = 0; i < first_glyphs.length; i++) + { + unsigned int ligature_count = ligature_per_first_glyph_count_list[i]; + if (unlikely (!ligatureSet[i] + .serialize_serialize (c, + ligatures_list.sub_array (0, ligature_count), + component_count_list.sub_array (0, ligature_count), + component_list))) return_trace (false); + ligatures_list += ligature_count; + component_count_list += ligature_count; + } + return_trace (coverage.serialize_serialize (c, first_glyphs)); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + const hb_set_t &glyphset = *c->plan->glyphset_gsub (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + out->format = format; + + // Due to a bug in some older versions of windows 7 the Coverage table must be + // packed after the LigatureSet and Ligature tables, so serialize Coverage first + // which places it last in the packed order. + hb_set_t new_coverage; + + hb_zip (this+coverage, hb_iter (ligatureSet) | hb_map (hb_add (this))) + | hb_filter (glyphset, hb_first) + | hb_filter ([&] (const LigatureSet& _) { + return _.intersects (&glyphset); + }, hb_second) + | hb_map (hb_first) + | hb_sink (new_coverage); + + if (!c->serializer->push () + ->serialize (c->serializer, + + new_coverage.iter () | hb_map_retains_sorting (glyph_map))) + { + c->serializer->pop_discard (); + return_trace (false); + } + + unsigned coverage_idx = c->serializer->pop_pack (); + c->serializer->add_link (out->coverage, coverage_idx); + + + hb_zip (this+coverage, ligatureSet) + | hb_filter (new_coverage, hb_first) + | hb_map (hb_second) + // to ensure that the repacker always orders the coverage table after the LigatureSet + // and LigatureSubtable's they will be linked to the Coverage table via a virtual link + // the coverage table object idx is passed down to facilitate this. + | hb_apply (subset_offset_array (c, out->ligatureSet, this, coverage_idx)) + ; + + return_trace (bool (new_coverage)); + } +}; + +} +} +} + +#endif /* OT_LAYOUT_GSUB_LIGATURESUBSTFORMAT1_HH */ diff --git a/libs/harfbuzz/src/OT/Layout/GSUB/MultipleSubst.hh b/libs/harfbuzz/src/OT/Layout/GSUB/MultipleSubst.hh new file mode 100644 index 00000000..852ca3ea --- /dev/null +++ b/libs/harfbuzz/src/OT/Layout/GSUB/MultipleSubst.hh @@ -0,0 +1,62 @@ +#ifndef OT_LAYOUT_GSUB_MULTIPLESUBST_HH +#define OT_LAYOUT_GSUB_MULTIPLESUBST_HH + +#include "Common.hh" +#include "MultipleSubstFormat1.hh" + +namespace OT { +namespace Layout { +namespace GSUB_impl { + +struct MultipleSubst +{ + protected: + union { + HBUINT16 format; /* Format identifier */ + MultipleSubstFormat1_2 format1; +#ifndef HB_NO_BORING_EXPANSION + MultipleSubstFormat1_2 format2; +#endif + } u; + + public: + + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const + { + TRACE_DISPATCH (this, u.format); + if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ()); + switch (u.format) { + case 1: return_trace (c->dispatch (u.format1, std::forward (ds)...)); +#ifndef HB_NO_BORING_EXPANSION + case 2: return_trace (c->dispatch (u.format2, std::forward (ds)...)); +#endif + default:return_trace (c->default_return_value ()); + } + } + + template + bool serialize (hb_serialize_context_t *c, + Iterator it) + { + TRACE_SERIALIZE (this); + if (unlikely (!c->extend_min (u.format))) return_trace (false); + unsigned int format = 1; + u.format = format; + switch (u.format) { + case 1: return_trace (u.format1.serialize (c, it)); + default:return_trace (false); + } + } + + /* TODO subset() should choose format. */ + +}; + + +} +} +} + +#endif /* OT_LAYOUT_GSUB_MULTIPLESUBST_HH */ diff --git a/libs/harfbuzz/src/OT/Layout/GSUB/MultipleSubstFormat1.hh b/libs/harfbuzz/src/OT/Layout/GSUB/MultipleSubstFormat1.hh new file mode 100644 index 00000000..3b4bd116 --- /dev/null +++ b/libs/harfbuzz/src/OT/Layout/GSUB/MultipleSubstFormat1.hh @@ -0,0 +1,130 @@ +#ifndef OT_LAYOUT_GSUB_MULTIPLESUBSTFORMAT1_HH +#define OT_LAYOUT_GSUB_MULTIPLESUBSTFORMAT1_HH + +#include "Common.hh" +#include "Sequence.hh" + +namespace OT { +namespace Layout { +namespace GSUB_impl { + +template +struct MultipleSubstFormat1_2 +{ + protected: + HBUINT16 format; /* Format identifier--format = 1 */ + typename Types::template OffsetTo + coverage; /* Offset to Coverage table--from + * beginning of Substitution table */ + Array16Of>> + sequence; /* Array of Sequence tables + * ordered by Coverage Index */ + public: + DEFINE_SIZE_ARRAY (4 + Types::size, sequence); + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (coverage.sanitize (c, this) && sequence.sanitize (c, this)); + } + + bool intersects (const hb_set_t *glyphs) const + { return (this+coverage).intersects (glyphs); } + + bool may_have_non_1to1 () const + { return true; } + + void closure (hb_closure_context_t *c) const + { + + hb_zip (this+coverage, sequence) + | hb_filter (c->parent_active_glyphs (), hb_first) + | hb_map (hb_second) + | hb_map (hb_add (this)) + | hb_apply ([c] (const Sequence &_) { _.closure (c); }) + ; + } + + void closure_lookups (hb_closure_lookups_context_t *c) const {} + + void collect_glyphs (hb_collect_glyphs_context_t *c) const + { + if (unlikely (!(this+coverage).collect_coverage (c->input))) return; + + hb_zip (this+coverage, sequence) + | hb_map (hb_second) + | hb_map (hb_add (this)) + | hb_apply ([c] (const Sequence &_) { _.collect_glyphs (c); }) + ; + } + + const Coverage &get_coverage () const { return this+coverage; } + + bool would_apply (hb_would_apply_context_t *c) const + { return c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED; } + + bool apply (hb_ot_apply_context_t *c) const + { + TRACE_APPLY (this); + + unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint); + if (likely (index == NOT_COVERED)) return_trace (false); + + return_trace ((this+sequence[index]).apply (c)); + } + + template + bool serialize (hb_serialize_context_t *c, + Iterator it) + { + TRACE_SERIALIZE (this); + auto sequences = + + it + | hb_map (hb_second) + ; + auto glyphs = + + it + | hb_map_retains_sorting (hb_first) + ; + if (unlikely (!c->extend_min (this))) return_trace (false); + + if (unlikely (!sequence.serialize (c, sequences.length))) return_trace (false); + + for (auto& pair : hb_zip (sequences, sequence)) + { + if (unlikely (!pair.second + .serialize_serialize (c, pair.first))) + return_trace (false); + } + + return_trace (coverage.serialize_serialize (c, glyphs)); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + const hb_set_t &glyphset = *c->plan->glyphset_gsub (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + out->format = format; + + hb_sorted_vector_t new_coverage; + + hb_zip (this+coverage, sequence) + | hb_filter (glyphset, hb_first) + | hb_filter (subset_offset_array (c, out->sequence, this), hb_second) + | hb_map (hb_first) + | hb_map (glyph_map) + | hb_sink (new_coverage) + ; + out->coverage.serialize_serialize (c->serializer, new_coverage.iter ()); + return_trace (bool (new_coverage)); + } +}; + +} +} +} + + +#endif /* OT_LAYOUT_GSUB_MULTIPLESUBSTFORMAT1_HH */ diff --git a/libs/harfbuzz/src/OT/Layout/GSUB/ReverseChainSingleSubst.hh b/libs/harfbuzz/src/OT/Layout/GSUB/ReverseChainSingleSubst.hh new file mode 100644 index 00000000..48e208ef --- /dev/null +++ b/libs/harfbuzz/src/OT/Layout/GSUB/ReverseChainSingleSubst.hh @@ -0,0 +1,36 @@ +#ifndef OT_LAYOUT_GSUB_REVERSECHAINSINGLESUBST_HH +#define OT_LAYOUT_GSUB_REVERSECHAINSINGLESUBST_HH + +#include "Common.hh" +#include "ReverseChainSingleSubstFormat1.hh" + +namespace OT { +namespace Layout { +namespace GSUB_impl { + +struct ReverseChainSingleSubst +{ + protected: + union { + HBUINT16 format; /* Format identifier */ + ReverseChainSingleSubstFormat1 format1; + } u; + + public: + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const + { + TRACE_DISPATCH (this, u.format); + if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ()); + switch (u.format) { + case 1: return_trace (c->dispatch (u.format1, std::forward (ds)...)); + default:return_trace (c->default_return_value ()); + } + } +}; + +} +} +} + +#endif /* HB_OT_LAYOUT_GSUB_REVERSECHAINSINGLESUBST_HH */ diff --git a/libs/harfbuzz/src/OT/Layout/GSUB/ReverseChainSingleSubstFormat1.hh b/libs/harfbuzz/src/OT/Layout/GSUB/ReverseChainSingleSubstFormat1.hh new file mode 100644 index 00000000..a23e9202 --- /dev/null +++ b/libs/harfbuzz/src/OT/Layout/GSUB/ReverseChainSingleSubstFormat1.hh @@ -0,0 +1,244 @@ +#ifndef OT_LAYOUT_GSUB_REVERSECHAINSINGLESUBSTFORMAT1_HH +#define OT_LAYOUT_GSUB_REVERSECHAINSINGLESUBSTFORMAT1_HH + +#include "Common.hh" + +namespace OT { +namespace Layout { +namespace GSUB_impl { + +struct ReverseChainSingleSubstFormat1 +{ + protected: + HBUINT16 format; /* Format identifier--format = 1 */ + Offset16To + coverage; /* Offset to Coverage table--from + * beginning of table */ + Array16OfOffset16To + backtrack; /* Array of coverage tables + * in backtracking sequence, in glyph + * sequence order */ + Array16OfOffset16To + lookaheadX; /* Array of coverage tables + * in lookahead sequence, in glyph + * sequence order */ + Array16Of + substituteX; /* Array of substitute + * GlyphIDs--ordered by Coverage Index */ + public: + DEFINE_SIZE_MIN (10); + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (!(coverage.sanitize (c, this) && backtrack.sanitize (c, this))) + return_trace (false); + const auto &lookahead = StructAfter (backtrack); + if (!lookahead.sanitize (c, this)) + return_trace (false); + const auto &substitute = StructAfter (lookahead); + return_trace (substitute.sanitize (c)); + } + + bool intersects (const hb_set_t *glyphs) const + { + if (!(this+coverage).intersects (glyphs)) + return false; + + const auto &lookahead = StructAfter (backtrack); + + unsigned int count; + + count = backtrack.len; + for (unsigned int i = 0; i < count; i++) + if (!(this+backtrack[i]).intersects (glyphs)) + return false; + + count = lookahead.len; + for (unsigned int i = 0; i < count; i++) + if (!(this+lookahead[i]).intersects (glyphs)) + return false; + + return true; + } + + bool may_have_non_1to1 () const + { return false; } + + void closure (hb_closure_context_t *c) const + { + if (!intersects (c->glyphs)) return; + + const auto &lookahead = StructAfter (backtrack); + const auto &substitute = StructAfter (lookahead); + + + hb_zip (this+coverage, substitute) + | hb_filter (c->parent_active_glyphs (), hb_first) + | hb_map (hb_second) + | hb_sink (c->output) + ; + } + + void closure_lookups (hb_closure_lookups_context_t *c) const {} + + void collect_glyphs (hb_collect_glyphs_context_t *c) const + { + if (unlikely (!(this+coverage).collect_coverage (c->input))) return; + + unsigned int count; + + count = backtrack.len; + for (unsigned int i = 0; i < count; i++) + if (unlikely (!(this+backtrack[i]).collect_coverage (c->before))) return; + + const auto &lookahead = StructAfter (backtrack); + count = lookahead.len; + for (unsigned int i = 0; i < count; i++) + if (unlikely (!(this+lookahead[i]).collect_coverage (c->after))) return; + + const auto &substitute = StructAfter (lookahead); + count = substitute.len; + c->output->add_array (substitute.arrayZ, substitute.len); + } + + const Coverage &get_coverage () const { return this+coverage; } + + bool would_apply (hb_would_apply_context_t *c) const + { return c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED; } + + bool apply (hb_ot_apply_context_t *c) const + { + TRACE_APPLY (this); + if (unlikely (c->nesting_level_left != HB_MAX_NESTING_LEVEL)) + return_trace (false); /* No chaining to this type */ + + unsigned int index = (this+coverage).get_coverage (c->buffer->cur ().codepoint); + if (likely (index == NOT_COVERED)) return_trace (false); + + const auto &lookahead = StructAfter (backtrack); + const auto &substitute = StructAfter (lookahead); + + if (unlikely (index >= substitute.len)) return_trace (false); + + unsigned int start_index = 0, end_index = 0; + if (match_backtrack (c, + backtrack.len, (HBUINT16 *) backtrack.arrayZ, + match_coverage, this, + &start_index) && + match_lookahead (c, + lookahead.len, (HBUINT16 *) lookahead.arrayZ, + match_coverage, this, + c->buffer->idx + 1, &end_index)) + { + c->buffer->unsafe_to_break_from_outbuffer (start_index, end_index); + + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->message (c->font, + "replacing glyph at %d (reverse chaining substitution)", + c->buffer->idx); + } + + c->replace_glyph_inplace (substitute[index]); + + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->message (c->font, + "replaced glyph at %d (reverse chaining substitution)", + c->buffer->idx); + } + + /* Note: We DON'T decrease buffer->idx. The main loop does it + * for us. This is useful for preventing surprises if someone + * calls us through a Context lookup. */ + return_trace (true); + } + else + { + c->buffer->unsafe_to_concat_from_outbuffer (start_index, end_index); + return_trace (false); + } + } + + template + bool serialize_coverage_offset_array (hb_subset_context_t *c, Iterator it) const + { + TRACE_SERIALIZE (this); + auto *out = c->serializer->start_embed> (); + + if (unlikely (!c->serializer->allocate_size (HBUINT16::static_size))) + return_trace (false); + + for (auto& offset : it) { + auto *o = out->serialize_append (c->serializer); + if (unlikely (!o) || !o->serialize_subset (c, offset, this)) + return_trace (false); + } + + return_trace (true); + } + + template + bool serialize (hb_subset_context_t *c, + Iterator coverage_subst_iter, + BacktrackIterator backtrack_iter, + LookaheadIterator lookahead_iter) const + { + TRACE_SERIALIZE (this); + + auto *out = c->serializer->start_embed (this); + if (unlikely (!c->serializer->check_success (out))) return_trace (false); + if (unlikely (!c->serializer->embed (this->format))) return_trace (false); + if (unlikely (!c->serializer->embed (this->coverage))) return_trace (false); + + if (!serialize_coverage_offset_array (c, backtrack_iter)) return_trace (false); + if (!serialize_coverage_offset_array (c, lookahead_iter)) return_trace (false); + + auto *substitute_out = c->serializer->start_embed> (); + auto substitutes = + + coverage_subst_iter + | hb_map (hb_second) + ; + + auto glyphs = + + coverage_subst_iter + | hb_map_retains_sorting (hb_first) + ; + if (unlikely (! c->serializer->check_success (substitute_out->serialize (c->serializer, substitutes)))) + return_trace (false); + + if (unlikely (!out->coverage.serialize_serialize (c->serializer, glyphs))) + return_trace (false); + return_trace (true); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + const hb_set_t &glyphset = *c->plan->glyphset_gsub (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + const auto &lookahead = StructAfter (backtrack); + const auto &substitute = StructAfter (lookahead); + + auto it = + + hb_zip (this+coverage, substitute) + | hb_filter (glyphset, hb_first) + | hb_filter (glyphset, hb_second) + | hb_map_retains_sorting ([&] (hb_pair_t p) -> hb_codepoint_pair_t + { return hb_pair (glyph_map[p.first], glyph_map[p.second]); }) + ; + + return_trace (bool (it) && serialize (c, it, backtrack.iter (), lookahead.iter ())); + } +}; + +} +} +} + +#endif /* HB_OT_LAYOUT_GSUB_REVERSECHAINSINGLESUBSTFORMAT1_HH */ diff --git a/libs/harfbuzz/src/OT/Layout/GSUB/Sequence.hh b/libs/harfbuzz/src/OT/Layout/GSUB/Sequence.hh new file mode 100644 index 00000000..abf1c643 --- /dev/null +++ b/libs/harfbuzz/src/OT/Layout/GSUB/Sequence.hh @@ -0,0 +1,165 @@ +#ifndef OT_LAYOUT_GSUB_SEQUENCE_HH +#define OT_LAYOUT_GSUB_SEQUENCE_HH + +#include "Common.hh" + +namespace OT { +namespace Layout { +namespace GSUB_impl { + +template +struct Sequence +{ + protected: + Array16Of + substitute; /* String of GlyphIDs to substitute */ + public: + DEFINE_SIZE_ARRAY (2, substitute); + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (substitute.sanitize (c)); + } + + bool intersects (const hb_set_t *glyphs) const + { return hb_all (substitute, glyphs); } + + void closure (hb_closure_context_t *c) const + { c->output->add_array (substitute.arrayZ, substitute.len); } + + void collect_glyphs (hb_collect_glyphs_context_t *c) const + { c->output->add_array (substitute.arrayZ, substitute.len); } + + bool apply (hb_ot_apply_context_t *c) const + { + TRACE_APPLY (this); + unsigned int count = substitute.len; + + /* Special-case to make it in-place and not consider this + * as a "multiplied" substitution. */ + if (unlikely (count == 1)) + { + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->sync_so_far (); + c->buffer->message (c->font, + "replacing glyph at %d (multiple substitution)", + c->buffer->idx); + } + + c->replace_glyph (substitute.arrayZ[0]); + + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->message (c->font, + "replaced glyph at %d (multiple subtitution)", + c->buffer->idx - 1); + } + + return_trace (true); + } + /* Spec disallows this, but Uniscribe allows it. + * https://github.com/harfbuzz/harfbuzz/issues/253 */ + else if (unlikely (count == 0)) + { + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->sync_so_far (); + c->buffer->message (c->font, + "deleting glyph at %d (multiple substitution)", + c->buffer->idx); + } + + c->buffer->delete_glyph (); + + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->sync_so_far (); + c->buffer->message (c->font, + "deleted glyph at %d (multiple substitution)", + c->buffer->idx); + } + + return_trace (true); + } + + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->sync_so_far (); + c->buffer->message (c->font, + "multiplying glyph at %d", + c->buffer->idx); + } + + unsigned int klass = _hb_glyph_info_is_ligature (&c->buffer->cur()) ? + HB_OT_LAYOUT_GLYPH_PROPS_BASE_GLYPH : 0; + unsigned lig_id = _hb_glyph_info_get_lig_id (&c->buffer->cur()); + + for (unsigned int i = 0; i < count; i++) + { + /* If is attached to a ligature, don't disturb that. + * https://github.com/harfbuzz/harfbuzz/issues/3069 */ + if (!lig_id) + _hb_glyph_info_set_lig_props_for_component (&c->buffer->cur(), i); + c->output_glyph_for_component (substitute.arrayZ[i], klass); + } + c->buffer->skip_glyph (); + + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->sync_so_far (); + + char buf[HB_MAX_CONTEXT_LENGTH * 16] = {0}; + char *p = buf; + + for (unsigned i = c->buffer->idx - count; i < c->buffer->idx; i++) + { + if (buf < p) + *p++ = ','; + snprintf (p, sizeof(buf), "%u", i); + p += strlen(p); + } + + c->buffer->message (c->font, + "multiplied glyphs at %s", + buf); + } + + return_trace (true); + } + + template + bool serialize (hb_serialize_context_t *c, + Iterator subst) + { + TRACE_SERIALIZE (this); + return_trace (substitute.serialize (c, subst)); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + const hb_set_t &glyphset = *c->plan->glyphset_gsub (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + if (!intersects (&glyphset)) return_trace (false); + + auto it = + + hb_iter (substitute) + | hb_map (glyph_map) + ; + + auto *out = c->serializer->start_embed (*this); + return_trace (out->serialize (c->serializer, it)); + } +}; + + +} +} +} + + +#endif /* OT_LAYOUT_GSUB_SEQUENCE_HH */ diff --git a/libs/harfbuzz/src/OT/Layout/GSUB/SingleSubst.hh b/libs/harfbuzz/src/OT/Layout/GSUB/SingleSubst.hh new file mode 100644 index 00000000..6942e699 --- /dev/null +++ b/libs/harfbuzz/src/OT/Layout/GSUB/SingleSubst.hh @@ -0,0 +1,103 @@ +#ifndef OT_LAYOUT_GSUB_SINGLESUBST_HH +#define OT_LAYOUT_GSUB_SINGLESUBST_HH + +#include "Common.hh" +#include "SingleSubstFormat1.hh" +#include "SingleSubstFormat2.hh" + +namespace OT { +namespace Layout { +namespace GSUB_impl { + +struct SingleSubst +{ + protected: + union { + HBUINT16 format; /* Format identifier */ + SingleSubstFormat1_3 format1; + SingleSubstFormat2_4 format2; +#ifndef HB_NO_BORING_EXPANSION + SingleSubstFormat1_3 format3; + SingleSubstFormat2_4 format4; +#endif + } u; + + public: + + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const + { + TRACE_DISPATCH (this, u.format); + if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ()); + switch (u.format) { + case 1: return_trace (c->dispatch (u.format1, std::forward (ds)...)); + case 2: return_trace (c->dispatch (u.format2, std::forward (ds)...)); +#ifndef HB_NO_BORING_EXPANSION + case 3: return_trace (c->dispatch (u.format3, std::forward (ds)...)); + case 4: return_trace (c->dispatch (u.format4, std::forward (ds)...)); +#endif + default:return_trace (c->default_return_value ()); + } + } + + template + bool serialize (hb_serialize_context_t *c, + Iterator glyphs) + { + TRACE_SERIALIZE (this); + if (unlikely (!c->extend_min (u.format))) return_trace (false); + unsigned format = 2; + unsigned delta = 0; + if (glyphs) + { + format = 1; + hb_codepoint_t mask = 0xFFFFu; + +#ifndef HB_NO_BORING_EXPANSION + if (+ glyphs + | hb_map_retains_sorting (hb_first) + | hb_filter ([] (hb_codepoint_t gid) { return gid > 0xFFFFu; })) + { + format += 2; + mask = 0xFFFFFFu; + } +#endif + + auto get_delta = [=] (hb_codepoint_pair_t _) + { return (unsigned) (_.second - _.first) & mask; }; + delta = get_delta (*glyphs); + if (!hb_all (++(+glyphs), delta, get_delta)) format += 1; + } + + u.format = format; + switch (u.format) { + case 1: return_trace (u.format1.serialize (c, + + glyphs + | hb_map_retains_sorting (hb_first), + delta)); + case 2: return_trace (u.format2.serialize (c, glyphs)); +#ifndef HB_NO_BORING_EXPANSION + case 3: return_trace (u.format3.serialize (c, + + glyphs + | hb_map_retains_sorting (hb_first), + delta)); + case 4: return_trace (u.format4.serialize (c, glyphs)); +#endif + default:return_trace (false); + } + } +}; + +template +static void +SingleSubst_serialize (hb_serialize_context_t *c, + Iterator it) +{ c->start_embed ()->serialize (c, it); } + +} +} +} + +#endif /* OT_LAYOUT_GSUB_SINGLESUBST_HH */ diff --git a/libs/harfbuzz/src/OT/Layout/GSUB/SingleSubstFormat1.hh b/libs/harfbuzz/src/OT/Layout/GSUB/SingleSubstFormat1.hh new file mode 100644 index 00000000..13665d7b --- /dev/null +++ b/libs/harfbuzz/src/OT/Layout/GSUB/SingleSubstFormat1.hh @@ -0,0 +1,168 @@ +#ifndef OT_LAYOUT_GSUB_SINGLESUBSTFORMAT1_HH +#define OT_LAYOUT_GSUB_SINGLESUBSTFORMAT1_HH + +#include "Common.hh" + +namespace OT { +namespace Layout { +namespace GSUB_impl { + +template +struct SingleSubstFormat1_3 +{ + protected: + HBUINT16 format; /* Format identifier--format = 1 */ + typename Types::template OffsetTo + coverage; /* Offset to Coverage table--from + * beginning of Substitution table */ + typename Types::HBUINT + deltaGlyphID; /* Add to original GlyphID to get + * substitute GlyphID, modulo 0x10000 */ + + public: + DEFINE_SIZE_STATIC (2 + 2 * Types::size); + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (coverage.sanitize (c, this) && deltaGlyphID.sanitize (c)); + } + + hb_codepoint_t get_mask () const + { return (1 << (8 * Types::size)) - 1; } + + bool intersects (const hb_set_t *glyphs) const + { return (this+coverage).intersects (glyphs); } + + bool may_have_non_1to1 () const + { return false; } + + void closure (hb_closure_context_t *c) const + { + hb_codepoint_t d = deltaGlyphID; + hb_codepoint_t mask = get_mask (); + + /* Help fuzzer avoid this function as much. */ + unsigned pop = (this+coverage).get_population (); + if (pop >= mask) + return; + + hb_set_t intersection; + (this+coverage).intersect_set (c->parent_active_glyphs (), intersection); + + /* In degenerate fuzzer-found fonts, but not real fonts, + * this table can keep adding new glyphs in each round of closure. + * Refuse to close-over, if it maps glyph range to overlapping range. */ + hb_codepoint_t min_before = intersection.get_min (); + hb_codepoint_t max_before = intersection.get_max (); + hb_codepoint_t min_after = (min_before + d) & mask; + hb_codepoint_t max_after = (max_before + d) & mask; + if (pop >= max_before - min_before && + ((min_before <= min_after && min_after <= max_before) || + (min_before <= max_after && max_after <= max_before))) + return; + + + hb_iter (intersection) + | hb_map ([d, mask] (hb_codepoint_t g) { return (g + d) & mask; }) + | hb_sink (c->output) + ; + } + + void closure_lookups (hb_closure_lookups_context_t *c) const {} + + void collect_glyphs (hb_collect_glyphs_context_t *c) const + { + if (unlikely (!(this+coverage).collect_coverage (c->input))) return; + hb_codepoint_t d = deltaGlyphID; + hb_codepoint_t mask = get_mask (); + + + hb_iter (this+coverage) + | hb_map ([d, mask] (hb_codepoint_t g) { return (g + d) & mask; }) + | hb_sink (c->output) + ; + } + + const Coverage &get_coverage () const { return this+coverage; } + + bool would_apply (hb_would_apply_context_t *c) const + { return c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED; } + + bool apply (hb_ot_apply_context_t *c) const + { + TRACE_APPLY (this); + hb_codepoint_t glyph_id = c->buffer->cur().codepoint; + unsigned int index = (this+coverage).get_coverage (glyph_id); + if (likely (index == NOT_COVERED)) return_trace (false); + + hb_codepoint_t d = deltaGlyphID; + hb_codepoint_t mask = get_mask (); + + glyph_id = (glyph_id + d) & mask; + + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->sync_so_far (); + c->buffer->message (c->font, + "replacing glyph at %d (single substitution)", + c->buffer->idx); + } + + c->replace_glyph (glyph_id); + + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->message (c->font, + "replaced glyph at %d (single substitution)", + c->buffer->idx - 1); + } + + return_trace (true); + } + + template + bool serialize (hb_serialize_context_t *c, + Iterator glyphs, + unsigned delta) + { + TRACE_SERIALIZE (this); + if (unlikely (!c->extend_min (this))) return_trace (false); + if (unlikely (!coverage.serialize_serialize (c, glyphs))) return_trace (false); + c->check_assign (deltaGlyphID, delta, HB_SERIALIZE_ERROR_INT_OVERFLOW); + return_trace (true); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + const hb_set_t &glyphset = *c->plan->glyphset_gsub (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + hb_codepoint_t d = deltaGlyphID; + hb_codepoint_t mask = get_mask (); + + hb_set_t intersection; + (this+coverage).intersect_set (glyphset, intersection); + + auto it = + + hb_iter (intersection) + | hb_map_retains_sorting ([d, mask] (hb_codepoint_t g) { + return hb_codepoint_pair_t (g, + (g + d) & mask); }) + | hb_filter (glyphset, hb_second) + | hb_map_retains_sorting ([&] (hb_codepoint_pair_t p) -> hb_codepoint_pair_t + { return hb_pair (glyph_map[p.first], glyph_map[p.second]); }) + ; + + bool ret = bool (it); + SingleSubst_serialize (c->serializer, it); + return_trace (ret); + } +}; + +} +} +} + + +#endif /* OT_LAYOUT_GSUB_SINGLESUBSTFORMAT1_HH */ diff --git a/libs/harfbuzz/src/OT/Layout/GSUB/SingleSubstFormat2.hh b/libs/harfbuzz/src/OT/Layout/GSUB/SingleSubstFormat2.hh new file mode 100644 index 00000000..54162997 --- /dev/null +++ b/libs/harfbuzz/src/OT/Layout/GSUB/SingleSubstFormat2.hh @@ -0,0 +1,135 @@ +#ifndef OT_LAYOUT_GSUB_SINGLESUBSTFORMAT2_HH +#define OT_LAYOUT_GSUB_SINGLESUBSTFORMAT2_HH + +#include "Common.hh" + +namespace OT { +namespace Layout { +namespace GSUB_impl { + +template +struct SingleSubstFormat2_4 +{ + protected: + HBUINT16 format; /* Format identifier--format = 2 */ + typename Types::template OffsetTo + coverage; /* Offset to Coverage table--from + * beginning of Substitution table */ + Array16Of + substitute; /* Array of substitute + * GlyphIDs--ordered by Coverage Index */ + + public: + DEFINE_SIZE_ARRAY (4 + Types::size, substitute); + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (coverage.sanitize (c, this) && substitute.sanitize (c)); + } + + bool intersects (const hb_set_t *glyphs) const + { return (this+coverage).intersects (glyphs); } + + bool may_have_non_1to1 () const + { return false; } + + void closure (hb_closure_context_t *c) const + { + + hb_zip (this+coverage, substitute) + | hb_filter (c->parent_active_glyphs (), hb_first) + | hb_map (hb_second) + | hb_sink (c->output) + ; + } + + void closure_lookups (hb_closure_lookups_context_t *c) const {} + + void collect_glyphs (hb_collect_glyphs_context_t *c) const + { + if (unlikely (!(this+coverage).collect_coverage (c->input))) return; + + hb_zip (this+coverage, substitute) + | hb_map (hb_second) + | hb_sink (c->output) + ; + } + + const Coverage &get_coverage () const { return this+coverage; } + + bool would_apply (hb_would_apply_context_t *c) const + { return c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED; } + + bool apply (hb_ot_apply_context_t *c) const + { + TRACE_APPLY (this); + unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint); + if (likely (index == NOT_COVERED)) return_trace (false); + + if (unlikely (index >= substitute.len)) return_trace (false); + + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->sync_so_far (); + c->buffer->message (c->font, + "replacing glyph at %d (single substitution)", + c->buffer->idx); + } + + c->replace_glyph (substitute[index]); + + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + c->buffer->message (c->font, + "replaced glyph at %d (single substitution)", + c->buffer->idx - 1); + } + + return_trace (true); + } + + template + bool serialize (hb_serialize_context_t *c, + Iterator it) + { + TRACE_SERIALIZE (this); + auto substitutes = + + it + | hb_map (hb_second) + ; + auto glyphs = + + it + | hb_map_retains_sorting (hb_first) + ; + if (unlikely (!c->extend_min (this))) return_trace (false); + if (unlikely (!substitute.serialize (c, substitutes))) return_trace (false); + if (unlikely (!coverage.serialize_serialize (c, glyphs))) return_trace (false); + return_trace (true); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + const hb_set_t &glyphset = *c->plan->glyphset_gsub (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + auto it = + + hb_zip (this+coverage, substitute) + | hb_filter (glyphset, hb_first) + | hb_filter (glyphset, hb_second) + | hb_map_retains_sorting ([&] (hb_pair_t p) -> hb_codepoint_pair_t + { return hb_pair (glyph_map[p.first], glyph_map[p.second]); }) + ; + + bool ret = bool (it); + SingleSubst_serialize (c->serializer, it); + return_trace (ret); + } +}; + +} +} +} + +#endif /* OT_LAYOUT_GSUB_SINGLESUBSTFORMAT2_HH */ diff --git a/libs/harfbuzz/src/OT/Layout/GSUB/SubstLookup.hh b/libs/harfbuzz/src/OT/Layout/GSUB/SubstLookup.hh new file mode 100644 index 00000000..d49dcc0e --- /dev/null +++ b/libs/harfbuzz/src/OT/Layout/GSUB/SubstLookup.hh @@ -0,0 +1,220 @@ +#ifndef OT_LAYOUT_GSUB_SUBSTLOOKUP_HH +#define OT_LAYOUT_GSUB_SUBSTLOOKUP_HH + +#include "Common.hh" +#include "SubstLookupSubTable.hh" + +namespace OT { +namespace Layout { +namespace GSUB_impl { + +struct SubstLookup : Lookup +{ + using SubTable = SubstLookupSubTable; + + bool sanitize (hb_sanitize_context_t *c) const + { return Lookup::sanitize (c); } + + const SubTable& get_subtable (unsigned int i) const + { return Lookup::get_subtable (i); } + + static inline bool lookup_type_is_reverse (unsigned int lookup_type) + { return lookup_type == SubTable::ReverseChainSingle; } + + bool is_reverse () const + { + unsigned int type = get_type (); + if (unlikely (type == SubTable::Extension)) + return get_subtable (0).u.extension.is_reverse (); + return lookup_type_is_reverse (type); + } + + bool may_have_non_1to1 () const + { + hb_have_non_1to1_context_t c; + return dispatch (&c); + } + + bool apply (hb_ot_apply_context_t *c) const + { + TRACE_APPLY (this); + return_trace (dispatch (c)); + } + + bool intersects (const hb_set_t *glyphs) const + { + hb_intersects_context_t c (glyphs); + return dispatch (&c); + } + + hb_closure_context_t::return_t closure (hb_closure_context_t *c, unsigned int this_index) const + { + if (!c->should_visit_lookup (this_index)) + return hb_closure_context_t::default_return_value (); + + c->set_recurse_func (dispatch_closure_recurse_func); + + hb_closure_context_t::return_t ret = dispatch (c); + + c->flush (); + + return ret; + } + + hb_closure_lookups_context_t::return_t closure_lookups (hb_closure_lookups_context_t *c, unsigned this_index) const + { + if (c->is_lookup_visited (this_index)) + return hb_closure_lookups_context_t::default_return_value (); + + c->set_lookup_visited (this_index); + if (!intersects (c->glyphs)) + { + c->set_lookup_inactive (this_index); + return hb_closure_lookups_context_t::default_return_value (); + } + + hb_closure_lookups_context_t::return_t ret = dispatch (c); + return ret; + } + + hb_collect_glyphs_context_t::return_t collect_glyphs (hb_collect_glyphs_context_t *c) const + { + c->set_recurse_func (dispatch_recurse_func); + return dispatch (c); + } + + template + void collect_coverage (set_t *glyphs) const + { + hb_collect_coverage_context_t c (glyphs); + dispatch (&c); + } + + bool would_apply (hb_would_apply_context_t *c, + const hb_ot_layout_lookup_accelerator_t *accel) const + { + if (unlikely (!c->len)) return false; + if (!accel->may_have (c->glyphs[0])) return false; + return dispatch (c); + } + + template + bool serialize_single (hb_serialize_context_t *c, + uint32_t lookup_props, + Glyphs glyphs, + Substitutes substitutes) + { + TRACE_SERIALIZE (this); + if (unlikely (!Lookup::serialize (c, SubTable::Single, lookup_props, 1))) return_trace (false); + if (c->push ()->u.single.serialize (c, hb_zip (glyphs, substitutes))) + { + c->add_link (get_subtables ()[0], c->pop_pack ()); + return_trace (true); + } + c->pop_discard (); + return_trace (false); + } + + template + bool serialize (hb_serialize_context_t *c, + uint32_t lookup_props, + Iterator it) + { + TRACE_SERIALIZE (this); + if (unlikely (!Lookup::serialize (c, SubTable::Multiple, lookup_props, 1))) return_trace (false); + if (c->push ()->u.multiple. + serialize (c, it)) + { + c->add_link (get_subtables ()[0], c->pop_pack ()); + return_trace (true); + } + c->pop_discard (); + return_trace (false); + } + + bool serialize_alternate (hb_serialize_context_t *c, + uint32_t lookup_props, + hb_sorted_array_t glyphs, + hb_array_t alternate_len_list, + hb_array_t alternate_glyphs_list) + { + TRACE_SERIALIZE (this); + if (unlikely (!Lookup::serialize (c, SubTable::Alternate, lookup_props, 1))) return_trace (false); + + if (c->push ()->u.alternate. + serialize (c, + glyphs, + alternate_len_list, + alternate_glyphs_list)) + { + c->add_link (get_subtables ()[0], c->pop_pack ()); + return_trace (true); + } + c->pop_discard (); + return_trace (false); + } + + bool serialize_ligature (hb_serialize_context_t *c, + uint32_t lookup_props, + hb_sorted_array_t first_glyphs, + hb_array_t ligature_per_first_glyph_count_list, + hb_array_t ligatures_list, + hb_array_t component_count_list, + hb_array_t component_list /* Starting from second for each ligature */) + { + TRACE_SERIALIZE (this); + if (unlikely (!Lookup::serialize (c, SubTable::Ligature, lookup_props, 1))) return_trace (false); + if (c->push ()->u.ligature. + serialize (c, + first_glyphs, + ligature_per_first_glyph_count_list, + ligatures_list, + component_count_list, + component_list)) + { + c->add_link (get_subtables ()[0], c->pop_pack ()); + return_trace (true); + } + c->pop_discard (); + return_trace (false); + } + + template + static inline typename context_t::return_t dispatch_recurse_func (context_t *c, unsigned int lookup_index); + + static inline typename hb_closure_context_t::return_t closure_glyphs_recurse_func (hb_closure_context_t *c, unsigned lookup_index, hb_set_t *covered_seq_indices, unsigned seq_index, unsigned end_index); + + static inline hb_closure_context_t::return_t dispatch_closure_recurse_func (hb_closure_context_t *c, unsigned lookup_index, hb_set_t *covered_seq_indices, unsigned seq_index, unsigned end_index) + { + if (!c->should_visit_lookup (lookup_index)) + return hb_empty_t (); + + hb_closure_context_t::return_t ret = closure_glyphs_recurse_func (c, lookup_index, covered_seq_indices, seq_index, end_index); + + /* While in theory we should flush here, it will cause timeouts because a recursive + * lookup can keep growing the glyph set. Skip, and outer loop will retry up to + * HB_CLOSURE_MAX_STAGES time, which should be enough for every realistic font. */ + //c->flush (); + + return ret; + } + + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const + { return Lookup::dispatch (c, std::forward (ds)...); } + + bool subset (hb_subset_context_t *c) const + { return Lookup::subset (c); } +}; + + +} +} +} + +#endif /* OT_LAYOUT_GSUB_SUBSTLOOKUP_HH */ diff --git a/libs/harfbuzz/src/OT/Layout/GSUB/SubstLookupSubTable.hh b/libs/harfbuzz/src/OT/Layout/GSUB/SubstLookupSubTable.hh new file mode 100644 index 00000000..a525fba0 --- /dev/null +++ b/libs/harfbuzz/src/OT/Layout/GSUB/SubstLookupSubTable.hh @@ -0,0 +1,77 @@ +#ifndef OT_LAYOUT_GSUB_SUBSTLOOKUPSUBTABLE_HH +#define OT_LAYOUT_GSUB_SUBSTLOOKUPSUBTABLE_HH + +#include "Common.hh" +#include "SingleSubst.hh" +#include "MultipleSubst.hh" +#include "AlternateSubst.hh" +#include "LigatureSubst.hh" +#include "ContextSubst.hh" +#include "ChainContextSubst.hh" +#include "ExtensionSubst.hh" +#include "ReverseChainSingleSubst.hh" + +namespace OT { +namespace Layout { +namespace GSUB_impl { + +struct SubstLookupSubTable +{ + friend struct ::OT::Lookup; + friend struct SubstLookup; + + protected: + union { + SingleSubst single; + MultipleSubst multiple; + AlternateSubst alternate; + LigatureSubst ligature; + ContextSubst context; + ChainContextSubst chainContext; + ExtensionSubst extension; + ReverseChainSingleSubst reverseChainContextSingle; + } u; + public: + DEFINE_SIZE_MIN (0); + + enum Type { + Single = 1, + Multiple = 2, + Alternate = 3, + Ligature = 4, + Context = 5, + ChainContext = 6, + Extension = 7, + ReverseChainSingle = 8 + }; + + template + typename context_t::return_t dispatch (context_t *c, unsigned int lookup_type, Ts&&... ds) const + { + TRACE_DISPATCH (this, lookup_type); + switch (lookup_type) { + case Single: return_trace (u.single.dispatch (c, std::forward (ds)...)); + case Multiple: return_trace (u.multiple.dispatch (c, std::forward (ds)...)); + case Alternate: return_trace (u.alternate.dispatch (c, std::forward (ds)...)); + case Ligature: return_trace (u.ligature.dispatch (c, std::forward (ds)...)); + case Context: return_trace (u.context.dispatch (c, std::forward (ds)...)); + case ChainContext: return_trace (u.chainContext.dispatch (c, std::forward (ds)...)); + case Extension: return_trace (u.extension.dispatch (c, std::forward (ds)...)); + case ReverseChainSingle: return_trace (u.reverseChainContextSingle.dispatch (c, std::forward (ds)...)); + default: return_trace (c->default_return_value ()); + } + } + + bool intersects (const hb_set_t *glyphs, unsigned int lookup_type) const + { + hb_intersects_context_t c (glyphs); + return dispatch (&c, lookup_type); + } +}; + + +} +} +} + +#endif /* HB_OT_LAYOUT_GSUB_SUBSTLOOKUPSUBTABLE_HH */ diff --git a/libs/harfbuzz/src/OT/Layout/types.hh b/libs/harfbuzz/src/OT/Layout/types.hh new file mode 100644 index 00000000..6a43403e --- /dev/null +++ b/libs/harfbuzz/src/OT/Layout/types.hh @@ -0,0 +1,66 @@ +/* + * Copyright © 2007,2008,2009 Red Hat, Inc. + * Copyright © 2010,2012 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. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod, Garret Rieger + */ + +#ifndef OT_LAYOUT_TYPES_HH +#define OT_LAYOUT_TYPES_HH + +namespace OT { +namespace Layout { + +struct SmallTypes { + static constexpr unsigned size = 2; + using large_int = uint32_t; + using HBUINT = HBUINT16; + using HBGlyphID = HBGlyphID16; + using Offset = Offset16; + template + using OffsetTo = OT::Offset16To; + template + using ArrayOf = OT::Array16Of; + template + using SortedArrayOf = OT::SortedArray16Of; +}; + +struct MediumTypes { + static constexpr unsigned size = 3; + using large_int = uint64_t; + using HBUINT = HBUINT24; + using HBGlyphID = HBGlyphID24; + using Offset = Offset24; + template + using OffsetTo = OT::Offset24To; + template + using ArrayOf = OT::Array24Of; + template + using SortedArrayOf = OT::SortedArray24Of; +}; + +} +} + +#endif /* OT_LAYOUT_TYPES_HH */ diff --git a/libs/harfbuzz/src/OT/glyf/CompositeGlyph.hh b/libs/harfbuzz/src/OT/glyf/CompositeGlyph.hh new file mode 100644 index 00000000..fc8e309b --- /dev/null +++ b/libs/harfbuzz/src/OT/glyf/CompositeGlyph.hh @@ -0,0 +1,416 @@ +#ifndef OT_GLYF_COMPOSITEGLYPH_HH +#define OT_GLYF_COMPOSITEGLYPH_HH + + +#include "../../hb-open-type.hh" + + +namespace OT { +namespace glyf_impl { + + +struct CompositeGlyphRecord +{ + protected: + enum composite_glyph_flag_t + { + ARG_1_AND_2_ARE_WORDS = 0x0001, + ARGS_ARE_XY_VALUES = 0x0002, + ROUND_XY_TO_GRID = 0x0004, + WE_HAVE_A_SCALE = 0x0008, + MORE_COMPONENTS = 0x0020, + WE_HAVE_AN_X_AND_Y_SCALE = 0x0040, + WE_HAVE_A_TWO_BY_TWO = 0x0080, + WE_HAVE_INSTRUCTIONS = 0x0100, + USE_MY_METRICS = 0x0200, + OVERLAP_COMPOUND = 0x0400, + SCALED_COMPONENT_OFFSET = 0x0800, + UNSCALED_COMPONENT_OFFSET = 0x1000, +#ifndef HB_NO_BEYOND_64K + GID_IS_24BIT = 0x2000 +#endif + }; + + public: + unsigned int get_size () const + { + unsigned int size = min_size; + /* glyphIndex is 24bit instead of 16bit */ +#ifndef HB_NO_BEYOND_64K + if (flags & GID_IS_24BIT) size += HBGlyphID24::static_size - HBGlyphID16::static_size; +#endif + /* arg1 and 2 are int16 */ + if (flags & ARG_1_AND_2_ARE_WORDS) size += 4; + /* arg1 and 2 are int8 */ + else size += 2; + + /* One x 16 bit (scale) */ + if (flags & WE_HAVE_A_SCALE) size += 2; + /* Two x 16 bit (xscale, yscale) */ + else if (flags & WE_HAVE_AN_X_AND_Y_SCALE) size += 4; + /* Four x 16 bit (xscale, scale01, scale10, yscale) */ + else if (flags & WE_HAVE_A_TWO_BY_TWO) size += 8; + + return size; + } + + void drop_instructions_flag () { flags = (uint16_t) flags & ~WE_HAVE_INSTRUCTIONS; } + void set_overlaps_flag () + { + flags = (uint16_t) flags | OVERLAP_COMPOUND; + } + + bool has_instructions () const { return flags & WE_HAVE_INSTRUCTIONS; } + + bool has_more () const { return flags & MORE_COMPONENTS; } + bool is_use_my_metrics () const { return flags & USE_MY_METRICS; } + bool is_anchored () const { return !(flags & ARGS_ARE_XY_VALUES); } + void get_anchor_points (unsigned int &point1, unsigned int &point2) const + { + const auto *p = &StructAfter (flags); +#ifndef HB_NO_BEYOND_64K + if (flags & GID_IS_24BIT) + p += HBGlyphID24::static_size; + else +#endif + p += HBGlyphID16::static_size; + if (flags & ARG_1_AND_2_ARE_WORDS) + { + point1 = ((const HBUINT16 *) p)[0]; + point2 = ((const HBUINT16 *) p)[1]; + } + else + { + point1 = p[0]; + point2 = p[1]; + } + } + + void transform_points (contour_point_vector_t &points) const + { + float matrix[4]; + contour_point_t trans; + if (get_transformation (matrix, trans)) + { + if (scaled_offsets ()) + { + points.translate (trans); + points.transform (matrix); + } + else + { + points.transform (matrix); + points.translate (trans); + } + } + } + + unsigned compile_with_deltas (const contour_point_t &p_delta, + char *out) const + { + const HBINT8 *p = &StructAfter (flags); +#ifndef HB_NO_BEYOND_64K + if (flags & GID_IS_24BIT) + p += HBGlyphID24::static_size; + else +#endif + p += HBGlyphID16::static_size; + + unsigned len = get_size (); + unsigned len_before_val = (const char *)p - (const char *)this; + if (flags & ARG_1_AND_2_ARE_WORDS) + { + // no overflow, copy and update value with deltas + memcpy (out, this, len); + + const HBINT16 *px = reinterpret_cast (p); + HBINT16 *o = reinterpret_cast (out + len_before_val); + o[0] = px[0] + roundf (p_delta.x); + o[1] = px[1] + roundf (p_delta.y); + } + else + { + int new_x = p[0] + roundf (p_delta.x); + int new_y = p[1] + roundf (p_delta.y); + if (new_x <= 127 && new_x >= -128 && + new_y <= 127 && new_y >= -128) + { + memcpy (out, this, len); + HBINT8 *o = reinterpret_cast (out + len_before_val); + o[0] = new_x; + o[1] = new_y; + } + else + { + // int8 overflows after deltas applied + memcpy (out, this, len_before_val); + + //update flags + CompositeGlyphRecord *o = reinterpret_cast (out); + o->flags = flags | ARG_1_AND_2_ARE_WORDS; + out += len_before_val; + + HBINT16 new_value; + new_value = new_x; + memcpy (out, &new_value, HBINT16::static_size); + out += HBINT16::static_size; + + new_value = new_y; + memcpy (out, &new_value, HBINT16::static_size); + out += HBINT16::static_size; + + memcpy (out, p+2, len - len_before_val - 2); + len += 2; + } + } + return len; + } + + protected: + bool scaled_offsets () const + { return (flags & (SCALED_COMPONENT_OFFSET | UNSCALED_COMPONENT_OFFSET)) == SCALED_COMPONENT_OFFSET; } + + bool get_transformation (float (&matrix)[4], contour_point_t &trans) const + { + matrix[0] = matrix[3] = 1.f; + matrix[1] = matrix[2] = 0.f; + + const auto *p = &StructAfter (flags); +#ifndef HB_NO_BEYOND_64K + if (flags & GID_IS_24BIT) + p += HBGlyphID24::static_size; + else +#endif + p += HBGlyphID16::static_size; + int tx, ty; + if (flags & ARG_1_AND_2_ARE_WORDS) + { + tx = *(const HBINT16 *) p; + p += HBINT16::static_size; + ty = *(const HBINT16 *) p; + p += HBINT16::static_size; + } + else + { + tx = *p++; + ty = *p++; + } + if (is_anchored ()) tx = ty = 0; + + trans.init ((float) tx, (float) ty); + + { + const F2DOT14 *points = (const F2DOT14 *) p; + if (flags & WE_HAVE_A_SCALE) + { + matrix[0] = matrix[3] = points[0].to_float (); + return true; + } + else if (flags & WE_HAVE_AN_X_AND_Y_SCALE) + { + matrix[0] = points[0].to_float (); + matrix[3] = points[1].to_float (); + return true; + } + else if (flags & WE_HAVE_A_TWO_BY_TWO) + { + matrix[0] = points[0].to_float (); + matrix[1] = points[1].to_float (); + matrix[2] = points[2].to_float (); + matrix[3] = points[3].to_float (); + return true; + } + } + return tx || ty; + } + + public: + hb_codepoint_t get_gid () const + { +#ifndef HB_NO_BEYOND_64K + if (flags & GID_IS_24BIT) + return StructAfter (flags); + else +#endif + return StructAfter (flags); + } + void set_gid (hb_codepoint_t gid) + { +#ifndef HB_NO_BEYOND_64K + if (flags & GID_IS_24BIT) + StructAfter (flags) = gid; + else +#endif + /* TODO assert? */ + StructAfter (flags) = gid; + } + + protected: + HBUINT16 flags; + HBUINT24 pad; + public: + DEFINE_SIZE_MIN (4); +}; + +struct composite_iter_t : hb_iter_with_fallback_t +{ + typedef const CompositeGlyphRecord *__item_t__; + composite_iter_t (hb_bytes_t glyph_, __item_t__ current_) : + glyph (glyph_), current (nullptr), current_size (0) + { + set_current (current_); + } + + composite_iter_t () : glyph (hb_bytes_t ()), current (nullptr), current_size (0) {} + + item_t __item__ () const { return *current; } + bool __more__ () const { return current; } + void __next__ () + { + if (!current->has_more ()) { current = nullptr; return; } + + set_current (&StructAtOffset (current, current_size)); + } + composite_iter_t __end__ () const { return composite_iter_t (); } + bool operator != (const composite_iter_t& o) const + { return current != o.current; } + + + void set_current (__item_t__ current_) + { + if (!glyph.check_range (current_, CompositeGlyphRecord::min_size)) + { + current = nullptr; + current_size = 0; + return; + } + unsigned size = current_->get_size (); + if (!glyph.check_range (current_, size)) + { + current = nullptr; + current_size = 0; + return; + } + + current = current_; + current_size = size; + } + + private: + hb_bytes_t glyph; + __item_t__ current; + unsigned current_size; +}; + +struct CompositeGlyph +{ + const GlyphHeader &header; + hb_bytes_t bytes; + CompositeGlyph (const GlyphHeader &header_, hb_bytes_t bytes_) : + header (header_), bytes (bytes_) {} + + composite_iter_t iter () const + { return composite_iter_t (bytes, &StructAfter (header)); } + + unsigned int instructions_length (hb_bytes_t bytes) const + { + unsigned int start = bytes.length; + unsigned int end = bytes.length; + const CompositeGlyphRecord *last = nullptr; + for (auto &item : iter ()) + last = &item; + if (unlikely (!last)) return 0; + + if (last->has_instructions ()) + start = (char *) last - &bytes + last->get_size (); + if (unlikely (start > end)) return 0; + return end - start; + } + + /* Trimming for composites not implemented. + * If removing hints it falls out of that. */ + const hb_bytes_t trim_padding () const { return bytes; } + + void drop_hints () + { + for (const auto &_ : iter ()) + const_cast (_).drop_instructions_flag (); + } + + /* Chop instructions off the end */ + void drop_hints_bytes (hb_bytes_t &dest_start) const + { dest_start = bytes.sub_array (0, bytes.length - instructions_length (bytes)); } + + void set_overlaps_flag () + { + CompositeGlyphRecord& glyph_chain = const_cast ( + StructAfter (header)); + if (!bytes.check_range(&glyph_chain, CompositeGlyphRecord::min_size)) + return; + glyph_chain.set_overlaps_flag (); + } + + bool compile_bytes_with_deltas (const hb_bytes_t &source_bytes, + const contour_point_vector_t &deltas, + hb_bytes_t &dest_bytes /* OUT */) + { + if (source_bytes.length <= GlyphHeader::static_size || + header.numberOfContours != -1) + { + dest_bytes = hb_bytes_t (); + return true; + } + + unsigned source_len = source_bytes.length - GlyphHeader::static_size; + + /* try to allocate more memories than source glyph bytes + * in case that there might be an overflow for int8 value + * and we would need to use int16 instead */ + char *o = (char *) hb_calloc (source_len + source_len/2, sizeof (char)); + if (unlikely (!o)) return false; + + const CompositeGlyphRecord *c = reinterpret_cast (source_bytes.arrayZ + GlyphHeader::static_size); + auto it = composite_iter_t (hb_bytes_t ((const char *)c, source_len), c); + + char *p = o; + unsigned i = 0, source_comp_len = 0; + for (const auto &component : it) + { + /* last 4 points in deltas are phantom points and should not be included */ + if (i >= deltas.length - 4) return false; + + unsigned comp_len = component.get_size (); + if (component.is_anchored ()) + { + memcpy (p, &component, comp_len); + p += comp_len; + } + else + { + unsigned new_len = component.compile_with_deltas (deltas[i], p); + p += new_len; + } + i++; + source_comp_len += comp_len; + } + + //copy instructions if any + if (source_len > source_comp_len) + { + unsigned instr_len = source_len - source_comp_len; + memcpy (p, (const char *)c + source_comp_len, instr_len); + p += instr_len; + } + + unsigned len = p - o; + dest_bytes = hb_bytes_t (o, len); + return true; + } +}; + + +} /* namespace glyf_impl */ +} /* namespace OT */ + + +#endif /* OT_GLYF_COMPOSITEGLYPH_HH */ diff --git a/libs/harfbuzz/src/OT/glyf/Glyph.hh b/libs/harfbuzz/src/OT/glyf/Glyph.hh new file mode 100644 index 00000000..0b25659a --- /dev/null +++ b/libs/harfbuzz/src/OT/glyf/Glyph.hh @@ -0,0 +1,378 @@ +#ifndef OT_GLYF_GLYPH_HH +#define OT_GLYF_GLYPH_HH + + +#include "../../hb-open-type.hh" + +#include "GlyphHeader.hh" +#include "SimpleGlyph.hh" +#include "CompositeGlyph.hh" + + +namespace OT { + +struct glyf_accelerator_t; + +namespace glyf_impl { + + +enum phantom_point_index_t +{ + PHANTOM_LEFT = 0, + PHANTOM_RIGHT = 1, + PHANTOM_TOP = 2, + PHANTOM_BOTTOM = 3, + PHANTOM_COUNT = 4 +}; + +struct Glyph +{ + enum glyph_type_t { EMPTY, SIMPLE, COMPOSITE }; + + public: + composite_iter_t get_composite_iterator () const + { + if (type != COMPOSITE) return composite_iter_t (); + return CompositeGlyph (*header, bytes).iter (); + } + + const hb_bytes_t trim_padding () const + { + switch (type) { + case COMPOSITE: return CompositeGlyph (*header, bytes).trim_padding (); + case SIMPLE: return SimpleGlyph (*header, bytes).trim_padding (); + default: return bytes; + } + } + + void drop_hints () + { + switch (type) { + case COMPOSITE: CompositeGlyph (*header, bytes).drop_hints (); return; + case SIMPLE: SimpleGlyph (*header, bytes).drop_hints (); return; + default: return; + } + } + + void set_overlaps_flag () + { + switch (type) { + case COMPOSITE: CompositeGlyph (*header, bytes).set_overlaps_flag (); return; + case SIMPLE: SimpleGlyph (*header, bytes).set_overlaps_flag (); return; + default: return; + } + } + + void drop_hints_bytes (hb_bytes_t &dest_start, hb_bytes_t &dest_end) const + { + switch (type) { + case COMPOSITE: CompositeGlyph (*header, bytes).drop_hints_bytes (dest_start); return; + case SIMPLE: SimpleGlyph (*header, bytes).drop_hints_bytes (dest_start, dest_end); return; + default: return; + } + } + + void update_mtx (const hb_subset_plan_t *plan, + int xMin, int yMax, + const contour_point_vector_t &all_points) const + { + hb_codepoint_t new_gid = 0; + if (!plan->new_gid_for_old_gid (gid, &new_gid)) + return; + + unsigned len = all_points.length; + float leftSideX = all_points[len - 4].x; + float rightSideX = all_points[len - 3].x; + float topSideY = all_points[len - 2].y; + float bottomSideY = all_points[len - 1].y; + + int hori_aw = roundf (rightSideX - leftSideX); + if (hori_aw < 0) hori_aw = 0; + int lsb = roundf (xMin - leftSideX); + plan->hmtx_map->set (new_gid, hb_pair (hori_aw, lsb)); + + int vert_aw = roundf (topSideY - bottomSideY); + if (vert_aw < 0) vert_aw = 0; + int tsb = roundf (topSideY - yMax); + plan->vmtx_map->set (new_gid, hb_pair (vert_aw, tsb)); + } + + bool compile_header_bytes (const hb_subset_plan_t *plan, + const contour_point_vector_t &all_points, + hb_bytes_t &dest_bytes /* OUT */) const + { + GlyphHeader *glyph_header = nullptr; + if (type != EMPTY && all_points.length > 4) + { + glyph_header = (GlyphHeader *) hb_calloc (1, GlyphHeader::static_size); + if (unlikely (!glyph_header)) return false; + } + + int xMin = 0, xMax = 0; + int yMin = 0, yMax = 0; + if (all_points.length > 4) + { + xMin = xMax = roundf (all_points[0].x); + yMin = yMax = roundf (all_points[0].y); + } + + for (unsigned i = 1; i < all_points.length - 4; i++) + { + float rounded_x = roundf (all_points[i].x); + float rounded_y = roundf (all_points[i].y); + xMin = hb_min (xMin, rounded_x); + xMax = hb_max (xMax, rounded_x); + yMin = hb_min (yMin, rounded_y); + yMax = hb_max (yMax, rounded_y); + } + + update_mtx (plan, xMin, yMax, all_points); + + /*for empty glyphs: all_points only include phantom points. + *just update metrics and then return */ + if (!glyph_header) + return true; + + glyph_header->numberOfContours = header->numberOfContours; + glyph_header->xMin = xMin; + glyph_header->yMin = yMin; + glyph_header->xMax = xMax; + glyph_header->yMax = yMax; + + dest_bytes = hb_bytes_t ((const char *)glyph_header, GlyphHeader::static_size); + return true; + } + + bool compile_bytes_with_deltas (const hb_subset_plan_t *plan, + hb_font_t *font, + const glyf_accelerator_t &glyf, + hb_bytes_t &dest_start, /* IN/OUT */ + hb_bytes_t &dest_end /* OUT */) + { + contour_point_vector_t all_points, deltas; + if (!get_points (font, glyf, all_points, &deltas, false, false)) + return false; + + // .notdef, set type to empty so we only update metrics and don't compile bytes for + // it + if (gid == 0 && + !(plan->flags & HB_SUBSET_FLAGS_NOTDEF_OUTLINE)) + type = EMPTY; + + switch (type) { + case COMPOSITE: + if (!CompositeGlyph (*header, bytes).compile_bytes_with_deltas (dest_start, + deltas, + dest_end)) + return false; + break; + case SIMPLE: + if (!SimpleGlyph (*header, bytes).compile_bytes_with_deltas (all_points, + plan->flags & HB_SUBSET_FLAGS_NO_HINTING, + dest_end)) + return false; + break; + default: + /* set empty bytes for empty glyph + * do not use source glyph's pointers */ + dest_start = hb_bytes_t (); + dest_end = hb_bytes_t (); + break; + } + + if (!compile_header_bytes (plan, all_points, dest_start)) + { + dest_end.fini (); + return false; + } + return true; + } + + + /* Note: Recursively calls itself. + * all_points includes phantom points + */ + template + bool get_points (hb_font_t *font, const accelerator_t &glyf_accelerator, + contour_point_vector_t &all_points /* OUT */, + contour_point_vector_t *deltas = nullptr, /* OUT */ + bool shift_points_hori = true, + bool use_my_metrics = true, + bool phantom_only = false, + unsigned int depth = 0) const + { + if (unlikely (depth > HB_MAX_NESTING_LEVEL)) return false; + contour_point_vector_t stack_points; + bool inplace = type == SIMPLE && all_points.length == 0; + /* Load into all_points if it's empty, as an optimization. */ + contour_point_vector_t &points = inplace ? all_points : stack_points; + + switch (type) { + case COMPOSITE: + { + /* pseudo component points for each component in composite glyph */ + unsigned num_points = hb_len (CompositeGlyph (*header, bytes).iter ()); + if (unlikely (!points.resize (num_points))) return false; + break; + } + case SIMPLE: + if (unlikely (!SimpleGlyph (*header, bytes).get_contour_points (points, phantom_only))) + return false; + break; + } + + /* Init phantom points */ + if (unlikely (!points.resize (points.length + PHANTOM_COUNT))) return false; + hb_array_t phantoms = points.sub_array (points.length - PHANTOM_COUNT, PHANTOM_COUNT); + { + int lsb = 0; + int h_delta = glyf_accelerator.hmtx->get_leading_bearing_without_var_unscaled (gid, &lsb) ? + (int) header->xMin - lsb : 0; + int tsb = 0; + int v_orig = (int) header->yMax + +#ifndef HB_NO_VERTICAL + ((void) glyf_accelerator.vmtx->get_leading_bearing_without_var_unscaled (gid, &tsb), tsb) +#else + 0 +#endif + ; + unsigned h_adv = glyf_accelerator.hmtx->get_advance_without_var_unscaled (gid); + unsigned v_adv = +#ifndef HB_NO_VERTICAL + glyf_accelerator.vmtx->get_advance_without_var_unscaled (gid) +#else + - font->face->get_upem () +#endif + ; + phantoms[PHANTOM_LEFT].x = h_delta; + phantoms[PHANTOM_RIGHT].x = h_adv + h_delta; + phantoms[PHANTOM_TOP].y = v_orig; + phantoms[PHANTOM_BOTTOM].y = v_orig - (int) v_adv; + } + + if (deltas != nullptr && depth == 0 && type == COMPOSITE) + { + if (unlikely (!deltas->resize (points.length))) return false; + deltas->copy_vector (points); + } + +#ifndef HB_NO_VAR + glyf_accelerator.gvar->apply_deltas_to_points (gid, font, points.as_array ()); +#endif + + // mainly used by CompositeGlyph calculating new X/Y offset value so no need to extend it + // with child glyphs' points + if (deltas != nullptr && depth == 0 && type == COMPOSITE) + { + for (unsigned i = 0 ; i < points.length; i++) + { + deltas->arrayZ[i].x = points.arrayZ[i].x - deltas->arrayZ[i].x; + deltas->arrayZ[i].y = points.arrayZ[i].y - deltas->arrayZ[i].y; + } + } + + switch (type) { + case SIMPLE: + if (!inplace) + all_points.extend (points.as_array ()); + break; + case COMPOSITE: + { + contour_point_vector_t comp_points; + unsigned int comp_index = 0; + for (auto &item : get_composite_iterator ()) + { + comp_points.reset (); + if (unlikely (!glyf_accelerator.glyph_for_gid (item.get_gid ()) + .get_points (font, glyf_accelerator, comp_points, + deltas, shift_points_hori, use_my_metrics, phantom_only, depth + 1))) + return false; + + /* Apply component transformation & translation */ + item.transform_points (comp_points); + + /* Apply translation from gvar */ + comp_points.translate (points[comp_index]); + + if (item.is_anchored ()) + { + unsigned int p1, p2; + item.get_anchor_points (p1, p2); + if (likely (p1 < all_points.length && p2 < comp_points.length)) + { + contour_point_t delta; + delta.init (all_points[p1].x - comp_points[p2].x, + all_points[p1].y - comp_points[p2].y); + + comp_points.translate (delta); + } + } + + /* Copy phantom points from component if USE_MY_METRICS flag set */ + if (use_my_metrics && item.is_use_my_metrics ()) + for (unsigned int i = 0; i < PHANTOM_COUNT; i++) + phantoms[i] = comp_points[comp_points.length - PHANTOM_COUNT + i]; + + all_points.extend (comp_points.sub_array (0, comp_points.length - PHANTOM_COUNT)); + + comp_index++; + } + + all_points.extend (phantoms); + } break; + default: + all_points.extend (phantoms); + } + + if (depth == 0 && shift_points_hori) /* Apply at top level */ + { + /* Undocumented rasterizer behavior: + * Shift points horizontally by the updated left side bearing + */ + contour_point_t delta; + delta.init (-phantoms[PHANTOM_LEFT].x, 0.f); + if (delta.x) all_points.translate (delta); + } + + return !all_points.in_error (); + } + + bool get_extents_without_var_scaled (hb_font_t *font, const glyf_accelerator_t &glyf_accelerator, + hb_glyph_extents_t *extents) const + { + if (type == EMPTY) return true; /* Empty glyph; zero extents. */ + return header->get_extents_without_var_scaled (font, glyf_accelerator, gid, extents); + } + + hb_bytes_t get_bytes () const { return bytes; } + + Glyph () : bytes (), + header (bytes.as ()), + gid (-1), + type(EMPTY) + {} + + Glyph (hb_bytes_t bytes_, + hb_codepoint_t gid_ = (unsigned) -1) : bytes (bytes_), + header (bytes.as ()), + gid (gid_) + { + int num_contours = header->numberOfContours; + if (unlikely (num_contours == 0)) type = EMPTY; + else if (num_contours > 0) type = SIMPLE; + else type = COMPOSITE; /* negative numbers */ + } + + protected: + hb_bytes_t bytes; + const GlyphHeader *header; + hb_codepoint_t gid; + unsigned type; +}; + + +} /* namespace glyf_impl */ +} /* namespace OT */ + + +#endif /* OT_GLYF_GLYPH_HH */ diff --git a/libs/harfbuzz/src/OT/glyf/GlyphHeader.hh b/libs/harfbuzz/src/OT/glyf/GlyphHeader.hh new file mode 100644 index 00000000..e4a9168b --- /dev/null +++ b/libs/harfbuzz/src/OT/glyf/GlyphHeader.hh @@ -0,0 +1,50 @@ +#ifndef OT_GLYF_GLYPHHEADER_HH +#define OT_GLYF_GLYPHHEADER_HH + + +#include "../../hb-open-type.hh" + + +namespace OT { +namespace glyf_impl { + + +struct GlyphHeader +{ + bool has_data () const { return numberOfContours; } + + template + bool get_extents_without_var_scaled (hb_font_t *font, const accelerator_t &glyf_accelerator, + hb_codepoint_t gid, hb_glyph_extents_t *extents) const + { + /* Undocumented rasterizer behavior: shift glyph to the left by (lsb - xMin), i.e., xMin = lsb */ + /* extents->x_bearing = hb_min (glyph_header.xMin, glyph_header.xMax); */ + int lsb = hb_min (xMin, xMax); + (void) glyf_accelerator.hmtx->get_leading_bearing_without_var_unscaled (gid, &lsb); + extents->x_bearing = font->em_scale_x (lsb); + extents->y_bearing = font->em_scale_y (hb_max (yMin, yMax)); + extents->width = font->em_scale_x (hb_max (xMin, xMax) - hb_min (xMin, xMax)); + extents->height = font->em_scale_y (hb_min (yMin, yMax) - hb_max (yMin, yMax)); + + return true; + } + + HBINT16 numberOfContours; + /* If the number of contours is + * greater than or equal to zero, + * this is a simple glyph; if negative, + * this is a composite glyph. */ + FWORD xMin; /* Minimum x for coordinate data. */ + FWORD yMin; /* Minimum y for coordinate data. */ + FWORD xMax; /* Maximum x for coordinate data. */ + FWORD yMax; /* Maximum y for coordinate data. */ + public: + DEFINE_SIZE_STATIC (10); +}; + + +} /* namespace glyf_impl */ +} /* namespace OT */ + + +#endif /* OT_GLYF_GLYPHHEADER_HH */ diff --git a/libs/harfbuzz/src/OT/glyf/SimpleGlyph.hh b/libs/harfbuzz/src/OT/glyf/SimpleGlyph.hh new file mode 100644 index 00000000..d45f4eb3 --- /dev/null +++ b/libs/harfbuzz/src/OT/glyf/SimpleGlyph.hh @@ -0,0 +1,340 @@ +#ifndef OT_GLYF_SIMPLEGLYPH_HH +#define OT_GLYF_SIMPLEGLYPH_HH + + +#include "../../hb-open-type.hh" + + +namespace OT { +namespace glyf_impl { + + +struct SimpleGlyph +{ + enum simple_glyph_flag_t + { + FLAG_ON_CURVE = 0x01, + FLAG_X_SHORT = 0x02, + FLAG_Y_SHORT = 0x04, + FLAG_REPEAT = 0x08, + FLAG_X_SAME = 0x10, + FLAG_Y_SAME = 0x20, + FLAG_OVERLAP_SIMPLE = 0x40, + FLAG_RESERVED2 = 0x80 + }; + + const GlyphHeader &header; + hb_bytes_t bytes; + SimpleGlyph (const GlyphHeader &header_, hb_bytes_t bytes_) : + header (header_), bytes (bytes_) {} + + unsigned int instruction_len_offset () const + { return GlyphHeader::static_size + 2 * header.numberOfContours; } + + unsigned int length (unsigned int instruction_len) const + { return instruction_len_offset () + 2 + instruction_len; } + + unsigned int instructions_length () const + { + unsigned int instruction_length_offset = instruction_len_offset (); + if (unlikely (instruction_length_offset + 2 > bytes.length)) return 0; + + const HBUINT16 &instructionLength = StructAtOffset (&bytes, instruction_length_offset); + /* Out of bounds of the current glyph */ + if (unlikely (length (instructionLength) > bytes.length)) return 0; + return instructionLength; + } + + const hb_bytes_t trim_padding () const + { + /* based on FontTools _g_l_y_f.py::trim */ + const uint8_t *glyph = (uint8_t*) bytes.arrayZ; + const uint8_t *glyph_end = glyph + bytes.length; + /* simple glyph w/contours, possibly trimmable */ + glyph += instruction_len_offset (); + + if (unlikely (glyph + 2 >= glyph_end)) return hb_bytes_t (); + unsigned int num_coordinates = StructAtOffset (glyph - 2, 0) + 1; + unsigned int num_instructions = StructAtOffset (glyph, 0); + + glyph += 2 + num_instructions; + + unsigned int coord_bytes = 0; + unsigned int coords_with_flags = 0; + while (glyph < glyph_end) + { + uint8_t flag = *glyph; + glyph++; + + unsigned int repeat = 1; + if (flag & FLAG_REPEAT) + { + if (unlikely (glyph >= glyph_end)) return hb_bytes_t (); + repeat = *glyph + 1; + glyph++; + } + + unsigned int xBytes, yBytes; + xBytes = yBytes = 0; + if (flag & FLAG_X_SHORT) xBytes = 1; + else if ((flag & FLAG_X_SAME) == 0) xBytes = 2; + + if (flag & FLAG_Y_SHORT) yBytes = 1; + else if ((flag & FLAG_Y_SAME) == 0) yBytes = 2; + + coord_bytes += (xBytes + yBytes) * repeat; + coords_with_flags += repeat; + if (coords_with_flags >= num_coordinates) break; + } + + if (unlikely (coords_with_flags != num_coordinates)) return hb_bytes_t (); + return bytes.sub_array (0, bytes.length + coord_bytes - (glyph_end - glyph)); + } + + /* zero instruction length */ + void drop_hints () + { + GlyphHeader &glyph_header = const_cast (header); + (HBUINT16 &) StructAtOffset (&glyph_header, instruction_len_offset ()) = 0; + } + + void drop_hints_bytes (hb_bytes_t &dest_start, hb_bytes_t &dest_end) const + { + unsigned int instructions_len = instructions_length (); + unsigned int glyph_length = length (instructions_len); + dest_start = bytes.sub_array (0, glyph_length - instructions_len); + dest_end = bytes.sub_array (glyph_length, bytes.length - glyph_length); + } + + void set_overlaps_flag () + { + if (unlikely (!header.numberOfContours)) return; + + unsigned flags_offset = length (instructions_length ()); + if (unlikely (flags_offset + 1 > bytes.length)) return; + + HBUINT8 &first_flag = (HBUINT8 &) StructAtOffset (&bytes, flags_offset); + first_flag = (uint8_t) first_flag | FLAG_OVERLAP_SIMPLE; + } + + static bool read_flags (const HBUINT8 *&p /* IN/OUT */, + contour_point_vector_t &points_ /* IN/OUT */, + const HBUINT8 *end) + { + unsigned count = points_.length; + for (unsigned int i = 0; i < count;) + { + if (unlikely (p + 1 > end)) return false; + uint8_t flag = *p++; + points_.arrayZ[i++].flag = flag; + if (flag & FLAG_REPEAT) + { + if (unlikely (p + 1 > end)) return false; + unsigned int repeat_count = *p++; + unsigned stop = hb_min (i + repeat_count, count); + for (; i < stop;) + points_.arrayZ[i++].flag = flag; + } + } + return true; + } + + static bool read_points (const HBUINT8 *&p /* IN/OUT */, + contour_point_vector_t &points_ /* IN/OUT */, + const HBUINT8 *end, + float contour_point_t::*m, + const simple_glyph_flag_t short_flag, + const simple_glyph_flag_t same_flag) + { + int v = 0; + + unsigned count = points_.length; + for (unsigned i = 0; i < count; i++) + { + unsigned flag = points_[i].flag; + if (flag & short_flag) + { + if (unlikely (p + 1 > end)) return false; + if (flag & same_flag) + v += *p++; + else + v -= *p++; + } + else + { + if (!(flag & same_flag)) + { + if (unlikely (p + HBINT16::static_size > end)) return false; + v += *(const HBINT16 *) p; + p += HBINT16::static_size; + } + } + points_.arrayZ[i].*m = v; + } + return true; + } + + bool get_contour_points (contour_point_vector_t &points_ /* OUT */, + bool phantom_only = false) const + { + const HBUINT16 *endPtsOfContours = &StructAfter (header); + int num_contours = header.numberOfContours; + assert (num_contours); + /* One extra item at the end, for the instruction-count below. */ + if (unlikely (!bytes.check_range (&endPtsOfContours[num_contours]))) return false; + unsigned int num_points = endPtsOfContours[num_contours - 1] + 1; + + points_.alloc (num_points + 4); // Allocate for phantom points, to avoid a possible copy + if (!points_.resize (num_points)) return false; + if (phantom_only) return true; + + for (int i = 0; i < num_contours; i++) + points_[endPtsOfContours[i]].is_end_point = true; + + /* Skip instructions */ + const HBUINT8 *p = &StructAtOffset (&endPtsOfContours[num_contours + 1], + endPtsOfContours[num_contours]); + + if (unlikely ((const char *) p < bytes.arrayZ)) return false; /* Unlikely overflow */ + const HBUINT8 *end = (const HBUINT8 *) (bytes.arrayZ + bytes.length); + if (unlikely (p >= end)) return false; + + /* Read x & y coordinates */ + return read_flags (p, points_, end) + && read_points (p, points_, end, &contour_point_t::x, + FLAG_X_SHORT, FLAG_X_SAME) + && read_points (p, points_, end, &contour_point_t::y, + FLAG_Y_SHORT, FLAG_Y_SAME); + } + + static void encode_coord (int value, + uint8_t &flag, + const simple_glyph_flag_t short_flag, + const simple_glyph_flag_t same_flag, + hb_vector_t &coords /* OUT */) + { + if (value == 0) + { + flag |= same_flag; + } + else if (value >= -255 && value <= 255) + { + flag |= short_flag; + if (value > 0) flag |= same_flag; + else value = -value; + + coords.push ((uint8_t)value); + } + else + { + int16_t val = value; + coords.push (val >> 8); + coords.push (val & 0xff); + } + } + + static void encode_flag (uint8_t &flag, + uint8_t &repeat, + uint8_t &lastflag, + hb_vector_t &flags /* OUT */) + { + if (flag == lastflag && repeat != 255) + { + repeat = repeat + 1; + if (repeat == 1) + { + flags.push(flag); + } + else + { + unsigned len = flags.length; + flags[len-2] = flag | FLAG_REPEAT; + flags[len-1] = repeat; + } + } + else + { + repeat = 0; + flags.push (flag); + } + lastflag = flag; + } + + bool compile_bytes_with_deltas (const contour_point_vector_t &all_points, + bool no_hinting, + hb_bytes_t &dest_bytes /* OUT */) + { + if (header.numberOfContours == 0 || all_points.length <= 4) + { + dest_bytes = hb_bytes_t (); + return true; + } + //convert absolute values to relative values + unsigned num_points = all_points.length - 4; + + hb_vector_t flags, x_coords, y_coords; + if (unlikely (!flags.alloc (num_points))) return false; + if (unlikely (!x_coords.alloc (2*num_points))) return false; + if (unlikely (!y_coords.alloc (2*num_points))) return false; + + uint8_t lastflag = 0, repeat = 0; + int prev_x = 0.f, prev_y = 0.f; + + for (unsigned i = 0; i < num_points; i++) + { + uint8_t flag = all_points[i].flag; + flag &= FLAG_ON_CURVE + FLAG_OVERLAP_SIMPLE; + + float cur_x = roundf (all_points[i].x); + float cur_y = roundf (all_points[i].y); + encode_coord (cur_x - prev_x, flag, FLAG_X_SHORT, FLAG_X_SAME, x_coords); + encode_coord (cur_y - prev_y, flag, FLAG_Y_SHORT, FLAG_Y_SAME, y_coords); + if (i == 0) lastflag = flag + 1; //make lastflag != flag for the first point + encode_flag (flag, repeat, lastflag, flags); + + prev_x = cur_x; + prev_y = cur_y; + } + + unsigned len_before_instrs = 2 * header.numberOfContours + 2; + unsigned len_instrs = instructions_length (); + unsigned total_len = len_before_instrs + flags.length + x_coords.length + y_coords.length; + + if (!no_hinting) + total_len += len_instrs; + + char *p = (char *) hb_calloc (total_len, sizeof (char)); + if (unlikely (!p)) return false; + + const char *src = bytes.arrayZ + GlyphHeader::static_size; + char *cur = p; + memcpy (p, src, len_before_instrs); + + cur += len_before_instrs; + src += len_before_instrs; + + if (!no_hinting) + { + memcpy (cur, src, len_instrs); + cur += len_instrs; + } + + memcpy (cur, flags.arrayZ, flags.length); + cur += flags.length; + + memcpy (cur, x_coords.arrayZ, x_coords.length); + cur += x_coords.length; + + memcpy (cur, y_coords.arrayZ, y_coords.length); + + dest_bytes = hb_bytes_t (p, total_len); + return true; + } +}; + + +} /* namespace glyf_impl */ +} /* namespace OT */ + + +#endif /* OT_GLYF_SIMPLEGLYPH_HH */ diff --git a/libs/harfbuzz/src/OT/glyf/SubsetGlyph.hh b/libs/harfbuzz/src/OT/glyf/SubsetGlyph.hh new file mode 100644 index 00000000..88fc93c4 --- /dev/null +++ b/libs/harfbuzz/src/OT/glyf/SubsetGlyph.hh @@ -0,0 +1,85 @@ +#ifndef OT_GLYF_SUBSETGLYPH_HH +#define OT_GLYF_SUBSETGLYPH_HH + + +#include "../../hb-open-type.hh" + + +namespace OT { + +struct glyf_accelerator_t; + +namespace glyf_impl { + + +struct SubsetGlyph +{ + hb_codepoint_t old_gid; + Glyph source_glyph; + hb_bytes_t dest_start; /* region of source_glyph to copy first */ + hb_bytes_t dest_end; /* region of source_glyph to copy second */ + + bool serialize (hb_serialize_context_t *c, + bool use_short_loca, + const hb_subset_plan_t *plan) const + { + TRACE_SERIALIZE (this); + + hb_bytes_t dest_glyph = dest_start.copy (c); + dest_glyph = hb_bytes_t (&dest_glyph, dest_glyph.length + dest_end.copy (c).length); + unsigned int pad_length = use_short_loca ? padding () : 0; + DEBUG_MSG (SUBSET, nullptr, "serialize %d byte glyph, width %d pad %d", dest_glyph.length, dest_glyph.length + pad_length, pad_length); + + HBUINT8 pad; + pad = 0; + while (pad_length > 0) + { + c->embed (pad); + pad_length--; + } + + if (unlikely (!dest_glyph.length)) return_trace (true); + + /* update components gids */ + for (auto &_ : Glyph (dest_glyph).get_composite_iterator ()) + { + hb_codepoint_t new_gid; + if (plan->new_gid_for_old_gid (_.get_gid(), &new_gid)) + const_cast (_).set_gid (new_gid); + } + + if (plan->flags & HB_SUBSET_FLAGS_NO_HINTING) + Glyph (dest_glyph).drop_hints (); + + if (plan->flags & HB_SUBSET_FLAGS_SET_OVERLAPS_FLAG) + Glyph (dest_glyph).set_overlaps_flag (); + + return_trace (true); + } + + bool compile_bytes_with_deltas (const hb_subset_plan_t *plan, + hb_font_t *font, + const glyf_accelerator_t &glyf) + { return source_glyph.compile_bytes_with_deltas (plan, font, glyf, dest_start, dest_end); } + + void free_compiled_bytes () + { + dest_start.fini (); + dest_end.fini (); + } + + void drop_hints_bytes () + { source_glyph.drop_hints_bytes (dest_start, dest_end); } + + unsigned int length () const { return dest_start.length + dest_end.length; } + /* pad to 2 to ensure 2-byte loca will be ok */ + unsigned int padding () const { return length () % 2; } + unsigned int padded_size () const { return length () + padding (); } +}; + + +} /* namespace glyf_impl */ +} /* namespace OT */ + + +#endif /* OT_GLYF_SUBSETGLYPH_HH */ diff --git a/libs/harfbuzz/src/OT/glyf/glyf-helpers.hh b/libs/harfbuzz/src/OT/glyf/glyf-helpers.hh new file mode 100644 index 00000000..181c33d0 --- /dev/null +++ b/libs/harfbuzz/src/OT/glyf/glyf-helpers.hh @@ -0,0 +1,90 @@ +#ifndef OT_GLYF_GLYF_HELPERS_HH +#define OT_GLYF_GLYF_HELPERS_HH + + +#include "../../hb-open-type.hh" +#include "../../hb-subset-plan.hh" + +#include "loca.hh" + + +namespace OT { +namespace glyf_impl { + + +template +static void +_write_loca (IteratorIn&& it, bool short_offsets, IteratorOut&& dest) +{ + unsigned right_shift = short_offsets ? 1 : 0; + unsigned int offset = 0; + dest << 0; + + it + | hb_map ([=, &offset] (unsigned int padded_size) + { + offset += padded_size; + DEBUG_MSG (SUBSET, nullptr, "loca entry offset %d", offset); + return offset >> right_shift; + }) + | hb_sink (dest) + ; +} + +static bool +_add_head_and_set_loca_version (hb_subset_plan_t *plan, bool use_short_loca) +{ + hb_blob_t *head_blob = hb_sanitize_context_t ().reference_table (plan->source); + hb_blob_t *head_prime_blob = hb_blob_copy_writable_or_fail (head_blob); + hb_blob_destroy (head_blob); + + if (unlikely (!head_prime_blob)) + return false; + + head *head_prime = (head *) hb_blob_get_data_writable (head_prime_blob, nullptr); + head_prime->indexToLocFormat = use_short_loca ? 0 : 1; + bool success = plan->add_table (HB_OT_TAG_head, head_prime_blob); + + hb_blob_destroy (head_prime_blob); + return success; +} + +template +static bool +_add_loca_and_head (hb_subset_plan_t * plan, Iterator padded_offsets, bool use_short_loca) +{ + unsigned num_offsets = padded_offsets.len () + 1; + unsigned entry_size = use_short_loca ? 2 : 4; + char *loca_prime_data = (char *) hb_calloc (entry_size, num_offsets); + + if (unlikely (!loca_prime_data)) return false; + + DEBUG_MSG (SUBSET, nullptr, "loca entry_size %d num_offsets %d size %d", + entry_size, num_offsets, entry_size * num_offsets); + + if (use_short_loca) + _write_loca (padded_offsets, true, hb_array ((HBUINT16 *) loca_prime_data, num_offsets)); + else + _write_loca (padded_offsets, false, hb_array ((HBUINT32 *) loca_prime_data, num_offsets)); + + hb_blob_t *loca_blob = hb_blob_create (loca_prime_data, + entry_size * num_offsets, + HB_MEMORY_MODE_WRITABLE, + loca_prime_data, + hb_free); + + bool result = plan->add_table (HB_OT_TAG_loca, loca_blob) + && _add_head_and_set_loca_version (plan, use_short_loca); + + hb_blob_destroy (loca_blob); + return result; +} + + +} /* namespace glyf_impl */ +} /* namespace OT */ + + +#endif /* OT_GLYF_GLYF_HELPERS_HH */ diff --git a/libs/harfbuzz/src/OT/glyf/glyf.hh b/libs/harfbuzz/src/OT/glyf/glyf.hh new file mode 100644 index 00000000..5fb32f67 --- /dev/null +++ b/libs/harfbuzz/src/OT/glyf/glyf.hh @@ -0,0 +1,444 @@ +#ifndef OT_GLYF_GLYF_HH +#define OT_GLYF_GLYF_HH + + +#include "../../hb-open-type.hh" +#include "../../hb-ot-head-table.hh" +#include "../../hb-ot-hmtx-table.hh" +#include "../../hb-ot-var-gvar-table.hh" +#include "../../hb-draw.hh" + +#include "glyf-helpers.hh" +#include "Glyph.hh" +#include "SubsetGlyph.hh" +#include "loca.hh" +#include "path-builder.hh" + + +namespace OT { + + +/* + * glyf -- TrueType Glyph Data + * https://docs.microsoft.com/en-us/typography/opentype/spec/glyf + */ +#define HB_OT_TAG_glyf HB_TAG('g','l','y','f') + +struct glyf +{ + friend struct glyf_accelerator_t; + + static constexpr hb_tag_t tableTag = HB_OT_TAG_glyf; + + bool sanitize (hb_sanitize_context_t *c HB_UNUSED) const + { + TRACE_SANITIZE (this); + /* Runtime checks as eager sanitizing each glyph is costy */ + return_trace (true); + } + + /* requires source of SubsetGlyph complains the identifier isn't declared */ + template + bool serialize (hb_serialize_context_t *c, + Iterator it, + bool use_short_loca, + const hb_subset_plan_t *plan) + { + TRACE_SERIALIZE (this); + unsigned init_len = c->length (); + for (const auto &_ : it) _.serialize (c, use_short_loca, plan); + + /* As a special case when all glyph in the font are empty, add a zero byte + * to the table, so that OTS doesn’t reject it, and to make the table work + * on Windows as well. + * See https://github.com/khaledhosny/ots/issues/52 */ + if (init_len == c->length ()) + { + HBUINT8 empty_byte; + empty_byte = 0; + c->copy (empty_byte); + } + return_trace (true); + } + + /* Byte region(s) per glyph to output + unpadded, hints removed if so requested + If we fail to process a glyph we produce an empty (0-length) glyph */ + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + + glyf *glyf_prime = c->serializer->start_embed (); + if (unlikely (!c->serializer->check_success (glyf_prime))) return_trace (false); + + hb_vector_t glyphs; + _populate_subset_glyphs (c->plan, glyphs); + + if (!c->plan->pinned_at_default) + { + if (!_compile_subset_glyphs_with_deltas (c->plan, &glyphs)) + return_trace (false); + } + + auto padded_offsets = + + hb_iter (glyphs) + | hb_map (&glyf_impl::SubsetGlyph::padded_size) + ; + + unsigned max_offset = + padded_offsets | hb_reduce (hb_add, 0); + bool use_short_loca = max_offset < 0x1FFFF; + + + glyf_prime->serialize (c->serializer, hb_iter (glyphs), use_short_loca, c->plan); + if (!use_short_loca) { + padded_offsets = + + hb_iter (glyphs) + | hb_map (&glyf_impl::SubsetGlyph::length) + ; + } + + + if (!c->plan->pinned_at_default) + _free_compiled_subset_glyphs (&glyphs); + if (unlikely (c->serializer->in_error ())) return_trace (false); + return_trace (c->serializer->check_success (glyf_impl::_add_loca_and_head (c->plan, + padded_offsets, + use_short_loca))); + } + + void + _populate_subset_glyphs (const hb_subset_plan_t *plan, + hb_vector_t &glyphs /* OUT */) const; + + bool + _compile_subset_glyphs_with_deltas (const hb_subset_plan_t *plan, + hb_vector_t *glyphs /* OUT */) const; + + void _free_compiled_subset_glyphs (hb_vector_t *glyphs) const + { + for (auto _ : *glyphs) + _.free_compiled_bytes (); + } + + protected: + UnsizedArrayOf + dataZ; /* Glyphs data. */ + public: + DEFINE_SIZE_MIN (0); /* In reality, this is UNBOUNDED() type; but since we always + * check the size externally, allow Null() object of it by + * defining it _MIN instead. */ +}; + +struct glyf_accelerator_t +{ + glyf_accelerator_t (hb_face_t *face) + { + short_offset = false; + num_glyphs = 0; + loca_table = nullptr; + glyf_table = nullptr; +#ifndef HB_NO_VAR + gvar = nullptr; +#endif + hmtx = nullptr; +#ifndef HB_NO_VERTICAL + vmtx = nullptr; +#endif + const OT::head &head = *face->table.head; + if (head.indexToLocFormat > 1 || head.glyphDataFormat > 0) + /* Unknown format. Leave num_glyphs=0, that takes care of disabling us. */ + return; + short_offset = 0 == head.indexToLocFormat; + + loca_table = face->table.loca.get_blob (); // Needs no destruct! + glyf_table = hb_sanitize_context_t ().reference_table (face); +#ifndef HB_NO_VAR + gvar = face->table.gvar; +#endif + hmtx = face->table.hmtx; +#ifndef HB_NO_VERTICAL + vmtx = face->table.vmtx; +#endif + + num_glyphs = hb_max (1u, loca_table.get_length () / (short_offset ? 2 : 4)) - 1; + num_glyphs = hb_min (num_glyphs, face->get_num_glyphs ()); + } + ~glyf_accelerator_t () + { + glyf_table.destroy (); + } + + bool has_data () const { return num_glyphs; } + + protected: + template + bool get_points (hb_font_t *font, hb_codepoint_t gid, T consumer) const + { + if (gid >= num_glyphs) return false; + + /* Making this allocfree is not that easy + https://github.com/harfbuzz/harfbuzz/issues/2095 + mostly because of gvar handling in VF fonts, + perhaps a separate path for non-VF fonts can be considered */ + contour_point_vector_t all_points; + + bool phantom_only = !consumer.is_consuming_contour_points (); + if (unlikely (!glyph_for_gid (gid).get_points (font, *this, all_points, nullptr, true, true, phantom_only))) + return false; + + if (consumer.is_consuming_contour_points ()) + { + unsigned count = all_points.length; + assert (count >= glyf_impl::PHANTOM_COUNT); + count -= glyf_impl::PHANTOM_COUNT; + for (unsigned point_index = 0; point_index < count; point_index++) + consumer.consume_point (all_points[point_index]); + consumer.points_end (); + } + + /* Where to write phantoms, nullptr if not requested */ + contour_point_t *phantoms = consumer.get_phantoms_sink (); + if (phantoms) + for (unsigned i = 0; i < glyf_impl::PHANTOM_COUNT; ++i) + phantoms[i] = all_points[all_points.length - glyf_impl::PHANTOM_COUNT + i]; + + return true; + } + +#ifndef HB_NO_VAR + struct points_aggregator_t + { + hb_font_t *font; + hb_glyph_extents_t *extents; + contour_point_t *phantoms; + bool scaled; + + struct contour_bounds_t + { + contour_bounds_t () { min_x = min_y = FLT_MAX; max_x = max_y = -FLT_MAX; } + + void add (const contour_point_t &p) + { + min_x = hb_min (min_x, p.x); + min_y = hb_min (min_y, p.y); + max_x = hb_max (max_x, p.x); + max_y = hb_max (max_y, p.y); + } + + bool empty () const { return (min_x >= max_x) || (min_y >= max_y); } + + void get_extents (hb_font_t *font, hb_glyph_extents_t *extents, bool scaled) + { + if (unlikely (empty ())) + { + extents->width = 0; + extents->x_bearing = 0; + extents->height = 0; + extents->y_bearing = 0; + return; + } + if (scaled) + { + extents->x_bearing = font->em_scalef_x (min_x); + extents->width = font->em_scalef_x (max_x) - extents->x_bearing; + extents->y_bearing = font->em_scalef_y (max_y); + extents->height = font->em_scalef_y (min_y) - extents->y_bearing; + } + else + { + extents->x_bearing = roundf (min_x); + extents->width = roundf (max_x - extents->x_bearing); + extents->y_bearing = roundf (max_y); + extents->height = roundf (min_y - extents->y_bearing); + } + } + + protected: + float min_x, min_y, max_x, max_y; + } bounds; + + points_aggregator_t (hb_font_t *font_, hb_glyph_extents_t *extents_, contour_point_t *phantoms_, bool scaled_) + { + font = font_; + extents = extents_; + phantoms = phantoms_; + scaled = scaled_; + if (extents) bounds = contour_bounds_t (); + } + + void consume_point (const contour_point_t &point) { bounds.add (point); } + void points_end () { bounds.get_extents (font, extents, scaled); } + + bool is_consuming_contour_points () { return extents; } + contour_point_t *get_phantoms_sink () { return phantoms; } + }; + + public: + unsigned + get_advance_with_var_unscaled (hb_font_t *font, hb_codepoint_t gid, bool is_vertical) const + { + if (unlikely (gid >= num_glyphs)) return 0; + + bool success = false; + + contour_point_t phantoms[glyf_impl::PHANTOM_COUNT]; + if (font->num_coords) + success = get_points (font, gid, points_aggregator_t (font, nullptr, phantoms, false)); + + if (unlikely (!success)) + return +#ifndef HB_NO_VERTICAL + is_vertical ? vmtx->get_advance_without_var_unscaled (gid) : +#endif + hmtx->get_advance_without_var_unscaled (gid); + + float result = is_vertical + ? phantoms[glyf_impl::PHANTOM_TOP].y - phantoms[glyf_impl::PHANTOM_BOTTOM].y + : phantoms[glyf_impl::PHANTOM_RIGHT].x - phantoms[glyf_impl::PHANTOM_LEFT].x; + return hb_clamp (roundf (result), 0.f, (float) UINT_MAX / 2); + } + + bool get_leading_bearing_with_var_unscaled (hb_font_t *font, hb_codepoint_t gid, bool is_vertical, int *lsb) const + { + if (unlikely (gid >= num_glyphs)) return false; + + hb_glyph_extents_t extents; + + contour_point_t phantoms[glyf_impl::PHANTOM_COUNT]; + if (unlikely (!get_points (font, gid, points_aggregator_t (font, &extents, phantoms, false)))) + return false; + + *lsb = is_vertical + ? roundf (phantoms[glyf_impl::PHANTOM_TOP].y) - extents.y_bearing + : roundf (phantoms[glyf_impl::PHANTOM_LEFT].x); + return true; + } +#endif + + public: + bool get_extents (hb_font_t *font, hb_codepoint_t gid, hb_glyph_extents_t *extents) const + { + if (unlikely (gid >= num_glyphs)) return false; + +#ifndef HB_NO_VAR + if (font->num_coords) + return get_points (font, gid, points_aggregator_t (font, extents, nullptr, true)); +#endif + return glyph_for_gid (gid).get_extents_without_var_scaled (font, *this, extents); + } + + const glyf_impl::Glyph + glyph_for_gid (hb_codepoint_t gid, bool needs_padding_removal = false) const + { + if (unlikely (gid >= num_glyphs)) return glyf_impl::Glyph (); + + unsigned int start_offset, end_offset; + + if (short_offset) + { + const HBUINT16 *offsets = (const HBUINT16 *) loca_table->dataZ.arrayZ; + start_offset = 2 * offsets[gid]; + end_offset = 2 * offsets[gid + 1]; + } + else + { + const HBUINT32 *offsets = (const HBUINT32 *) loca_table->dataZ.arrayZ; + start_offset = offsets[gid]; + end_offset = offsets[gid + 1]; + } + + if (unlikely (start_offset > end_offset || end_offset > glyf_table.get_length ())) + return glyf_impl::Glyph (); + + glyf_impl::Glyph glyph (hb_bytes_t ((const char *) this->glyf_table + start_offset, + end_offset - start_offset), gid); + return needs_padding_removal ? glyf_impl::Glyph (glyph.trim_padding (), gid) : glyph; + } + + bool + get_path (hb_font_t *font, hb_codepoint_t gid, hb_draw_session_t &draw_session) const + { return get_points (font, gid, glyf_impl::path_builder_t (font, draw_session)); } + +#ifndef HB_NO_VAR + const gvar_accelerator_t *gvar; +#endif + const hmtx_accelerator_t *hmtx; +#ifndef HB_NO_VERTICAL + const vmtx_accelerator_t *vmtx; +#endif + + private: + bool short_offset; + unsigned int num_glyphs; + hb_blob_ptr_t loca_table; + hb_blob_ptr_t glyf_table; +}; + + +inline void +glyf::_populate_subset_glyphs (const hb_subset_plan_t *plan, + hb_vector_t& glyphs /* OUT */) const +{ + OT::glyf_accelerator_t glyf (plan->source); + unsigned num_glyphs = plan->num_output_glyphs (); + if (!glyphs.resize (num_glyphs)) return; + + for (auto p : plan->glyph_map->iter ()) + { + unsigned new_gid = p.second; + glyf_impl::SubsetGlyph& subset_glyph = glyphs.arrayZ[new_gid]; + subset_glyph.old_gid = p.first; + + if (unlikely (new_gid == 0 && + !(plan->flags & HB_SUBSET_FLAGS_NOTDEF_OUTLINE)) && + plan->pinned_at_default) + subset_glyph.source_glyph = glyf_impl::Glyph (); + else + subset_glyph.source_glyph = glyf.glyph_for_gid (subset_glyph.old_gid, true); + + if (plan->flags & HB_SUBSET_FLAGS_NO_HINTING) + subset_glyph.drop_hints_bytes (); + else + subset_glyph.dest_start = subset_glyph.source_glyph.get_bytes (); + } +} + +inline bool +glyf::_compile_subset_glyphs_with_deltas (const hb_subset_plan_t *plan, + hb_vector_t *glyphs /* OUT */) const +{ + OT::glyf_accelerator_t glyf (plan->source); + hb_font_t *font = hb_font_create (plan->source); + if (unlikely (!font)) return false; + + hb_vector_t vars; + if (unlikely (!vars.alloc (plan->user_axes_location->get_population ()))) + return false; + + for (auto _ : *plan->user_axes_location) + { + hb_variation_t var; + var.tag = _.first; + var.value = _.second; + vars.push (var); + } + + hb_font_set_variations (font, vars.arrayZ, plan->user_axes_location->get_population ()); + for (auto& subset_glyph : *glyphs) + { + if (!const_cast (subset_glyph).compile_bytes_with_deltas (plan, font, glyf)) + { + hb_font_destroy (font); + return false; + } + } + + hb_font_destroy (font); + return true; +} + + +} /* namespace OT */ + + +#endif /* OT_GLYF_GLYF_HH */ diff --git a/libs/harfbuzz/src/OT/glyf/loca.hh b/libs/harfbuzz/src/OT/glyf/loca.hh new file mode 100644 index 00000000..4481cba8 --- /dev/null +++ b/libs/harfbuzz/src/OT/glyf/loca.hh @@ -0,0 +1,43 @@ +#ifndef OT_GLYF_LOCA_HH +#define OT_GLYF_LOCA_HH + + +#include "../../hb-open-type.hh" + + +namespace OT { + + +/* + * loca -- Index to Location + * https://docs.microsoft.com/en-us/typography/opentype/spec/loca + */ +#define HB_OT_TAG_loca HB_TAG('l','o','c','a') + +struct loca +{ + friend struct glyf; + friend struct glyf_accelerator_t; + + static constexpr hb_tag_t tableTag = HB_OT_TAG_loca; + + bool sanitize (hb_sanitize_context_t *c HB_UNUSED) const + { + TRACE_SANITIZE (this); + return_trace (true); + } + + protected: + UnsizedArrayOf + dataZ; /* Location data. */ + public: + DEFINE_SIZE_MIN (0); /* In reality, this is UNBOUNDED() type; but since we always + * check the size externally, allow Null() object of it by + * defining it _MIN instead. */ +}; + + +} /* namespace OT */ + + +#endif /* OT_GLYF_LOCA_HH */ diff --git a/libs/harfbuzz/src/OT/glyf/path-builder.hh b/libs/harfbuzz/src/OT/glyf/path-builder.hh new file mode 100644 index 00000000..9bfc45a1 --- /dev/null +++ b/libs/harfbuzz/src/OT/glyf/path-builder.hh @@ -0,0 +1,135 @@ +#ifndef OT_GLYF_PATH_BUILDER_HH +#define OT_GLYF_PATH_BUILDER_HH + + +#include "../../hb.hh" + + +namespace OT { +namespace glyf_impl { + + +struct path_builder_t +{ + hb_font_t *font; + hb_draw_session_t *draw_session; + + struct optional_point_t + { + optional_point_t () {} + optional_point_t (float x_, float y_) : has_data (true), x (x_), y (y_) {} + operator bool () const { return has_data; } + + bool has_data = false; + float x = 0.; + float y = 0.; + + optional_point_t lerp (optional_point_t p, float t) + { return optional_point_t (x + t * (p.x - x), y + t * (p.y - y)); } + } first_oncurve, first_offcurve, last_offcurve; + + path_builder_t (hb_font_t *font_, hb_draw_session_t &draw_session_) + { + font = font_; + draw_session = &draw_session_; + first_oncurve = first_offcurve = last_offcurve = optional_point_t (); + } + + /* based on https://github.com/RazrFalcon/ttf-parser/blob/4f32821/src/glyf.rs#L287 + See also: + * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM01/Chap1.html + * https://stackoverflow.com/a/20772557 */ + void consume_point (const contour_point_t &point) + { + bool is_on_curve = point.flag & glyf_impl::SimpleGlyph::FLAG_ON_CURVE; + optional_point_t p (font->em_fscalef_x (point.x), font->em_fscalef_y (point.y)); + if (!first_oncurve) + { + if (is_on_curve) + { + first_oncurve = p; + draw_session->move_to (p.x, p.y); + } + else + { + if (first_offcurve) + { + optional_point_t mid = first_offcurve.lerp (p, .5f); + first_oncurve = mid; + last_offcurve = p; + draw_session->move_to (mid.x, mid.y); + } + else + first_offcurve = p; + } + } + else + { + if (last_offcurve) + { + if (is_on_curve) + { + draw_session->quadratic_to (last_offcurve.x, last_offcurve.y, + p.x, p.y); + last_offcurve = optional_point_t (); + } + else + { + optional_point_t mid = last_offcurve.lerp (p, .5f); + draw_session->quadratic_to (last_offcurve.x, last_offcurve.y, + mid.x, mid.y); + last_offcurve = p; + } + } + else + { + if (is_on_curve) + draw_session->line_to (p.x, p.y); + else + last_offcurve = p; + } + } + + if (point.is_end_point) + { + if (first_offcurve && last_offcurve) + { + optional_point_t mid = last_offcurve.lerp (first_offcurve, .5f); + draw_session->quadratic_to (last_offcurve.x, last_offcurve.y, + mid.x, mid.y); + last_offcurve = optional_point_t (); + /* now check the rest */ + } + + if (first_offcurve && first_oncurve) + draw_session->quadratic_to (first_offcurve.x, first_offcurve.y, + first_oncurve.x, first_oncurve.y); + else if (last_offcurve && first_oncurve) + draw_session->quadratic_to (last_offcurve.x, last_offcurve.y, + first_oncurve.x, first_oncurve.y); + else if (first_oncurve) + draw_session->line_to (first_oncurve.x, first_oncurve.y); + else if (first_offcurve) + { + float x = first_offcurve.x, y = first_offcurve.y; + draw_session->move_to (x, y); + draw_session->quadratic_to (x, y, x, y); + } + + /* Getting ready for the next contour */ + first_oncurve = first_offcurve = last_offcurve = optional_point_t (); + draw_session->close_path (); + } + } + void points_end () {} + + bool is_consuming_contour_points () { return true; } + contour_point_t *get_phantoms_sink () { return nullptr; } +}; + + +} /* namespace glyf_impl */ +} /* namespace OT */ + + +#endif /* OT_GLYF_PATH_BUILDER_HH */ diff --git a/libs/harfbuzz/src/check-c-linkage-decls.py b/libs/harfbuzz/src/check-c-linkage-decls.py new file mode 100644 index 00000000..fe18eda8 --- /dev/null +++ b/libs/harfbuzz/src/check-c-linkage-decls.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python3 + +import sys, os + +srcdir = os.getenv ('srcdir', os.path.dirname (__file__)) +base_srcdir = os.getenv ('base_srcdir', srcdir) + +os.chdir (srcdir) + +def removeprefix(s): + abs_path = os.path.join(base_srcdir, s) + return os.path.relpath(abs_path, srcdir) + + +HBHEADERS = [os.path.basename (x) for x in os.getenv ('HBHEADERS', '').split ()] or \ + [x for x in os.listdir ('.') if x.startswith ('hb') and x.endswith ('.h')] +HBSOURCES = [ + removeprefix(x) for x in os.getenv ('HBSOURCES', '').split () +] or [ + x for x in os.listdir ('.') if x.startswith ('hb') and x.endswith (('.cc', '.hh')) +] +stat = 0 + +for x in HBHEADERS: + with open (x, 'r', encoding='utf-8') as f: content = f.read () + if ('HB_BEGIN_DECLS' not in content) or ('HB_END_DECLS' not in content): + print ('Ouch, file %s does not have HB_BEGIN_DECLS / HB_END_DECLS, but it should' % x) + stat = 1 + +for x in HBSOURCES: + with open (x, 'r', encoding='utf-8') as f: content = f.read () + if ('HB_BEGIN_DECLS' in content) or ('HB_END_DECLS' in content): + print ('Ouch, file %s has HB_BEGIN_DECLS / HB_END_DECLS, but it shouldn\'t' % x) + stat = 1 + +sys.exit (stat) diff --git a/libs/harfbuzz/src/check-externs.py b/libs/harfbuzz/src/check-externs.py new file mode 100644 index 00000000..a64d2e5b --- /dev/null +++ b/libs/harfbuzz/src/check-externs.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python3 + +import sys, os, re + +os.chdir (os.getenv ('srcdir', os.path.dirname (__file__))) + +HBHEADERS = [os.path.basename (x) for x in os.getenv ('HBHEADERS', '').split ()] or \ + [x for x in os.listdir ('.') if x.startswith ('hb') and x.endswith ('.h')] + +stat = 0 + +print ('Checking that all public symbols are exported with HB_EXTERN') +for x in HBHEADERS: + with open (x, 'r', encoding='utf-8') as f: content = f.read () + for s in re.findall (r'\n.+\nhb_.+\n', content): + if not s.startswith ('\nHB_EXTERN '): + print ('failure on:', s) + stat = 1 + +sys.exit (stat) diff --git a/libs/harfbuzz/src/check-header-guards.py b/libs/harfbuzz/src/check-header-guards.py new file mode 100644 index 00000000..35ae6bef --- /dev/null +++ b/libs/harfbuzz/src/check-header-guards.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python3 + +import sys, os, re + +srcdir = os.getenv ('srcdir', os.path.dirname (__file__)) +base_srcdir = os.getenv ('base_srcdir', srcdir) + +os.chdir (srcdir) + +def removeprefix(s): + abs_path = os.path.join(base_srcdir, s) + return os.path.relpath(abs_path, srcdir) + + +HBHEADERS = [os.path.basename (x) for x in os.getenv ('HBHEADERS', '').split ()] or \ + [x for x in os.listdir ('.') if x.startswith ('hb') and x.endswith ('.h')] +HBSOURCES = [ + removeprefix(x) for x in os.getenv ('HBSOURCES', '').split () +] or [ + x for x in os.listdir ('.') if x.startswith ('hb') and x.endswith (('.cc', '.hh')) +] + + +stat = 0 + +for x in HBHEADERS + HBSOURCES: + if not x.endswith ('h') or x == 'hb-gobject-structs.h': continue + tag = x.upper ().replace ('.', '_').replace ('-', '_').replace(os.path.sep, '_').replace('/', '_') + with open (x, 'r', encoding='utf-8') as f: content = f.read () + if len (re.findall (tag + r'\b', content)) != 3: + print ('Ouch, header file %s does not have correct preprocessor guards. Expected: %s' % (x, tag)) + stat = 1 + +sys.exit (stat) diff --git a/libs/harfbuzz/src/check-includes.py b/libs/harfbuzz/src/check-includes.py new file mode 100644 index 00000000..fc95874b --- /dev/null +++ b/libs/harfbuzz/src/check-includes.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python3 + +import sys, os, re + +srcdir = os.getenv ('srcdir', os.path.dirname (__file__)) +base_srcdir = os.getenv ('base_srcdir', srcdir) + +os.chdir (srcdir) + +def removeprefix(s): + abs_path = os.path.join(base_srcdir, s) + return os.path.relpath(abs_path, srcdir) + +HBHEADERS = [os.path.basename (x) for x in os.getenv ('HBHEADERS', '').split ()] or \ + [x for x in os.listdir ('.') if x.startswith ('hb') and x.endswith ('.h')] + +HBSOURCES = [ + removeprefix(x) for x in os.getenv ('HBSOURCES', '').split () +] or [ + x for x in os.listdir ('.') if x.startswith ('hb') and x.endswith (('.cc', '.hh')) +] + + +stat = 0 + +print ('Checking that public header files #include "hb-common.h" or "hb.h" first (or none)') +for x in HBHEADERS: + if x == 'hb.h' or x == 'hb-common.h': continue + with open (x, 'r', encoding='utf-8') as f: content = f.read () + first = re.findall (r'#.*include.*', content)[0] + if first not in ['#include "hb.h"', '#include "hb-common.h"']: + print ('failure on %s' % x) + stat = 1 + +print ('Checking that source files #include a private header first (or none)') +for x in HBSOURCES: + with open (x, 'r', encoding='utf-8') as f: content = f.read () + includes = re.findall (r'#.*include.*', content) + if includes: + if not len (re.findall (r'".*\.hh"', includes[0])): + print ('failure on %s' % x) + stat = 1 + +print ('Checking that there is no #include ') +for x in HBHEADERS + HBSOURCES: + with open (x, 'r', encoding='utf-8') as f: content = f.read () + if re.findall ('#.*include.*<.*hb', content): + print ('failure on %s' % x) + stat = 1 + +sys.exit (stat) diff --git a/libs/harfbuzz/src/check-libstdc++.py b/libs/harfbuzz/src/check-libstdc++.py new file mode 100644 index 00000000..85b72653 --- /dev/null +++ b/libs/harfbuzz/src/check-libstdc++.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python3 + +import sys, os, shutil, subprocess + +os.chdir (os.getenv ('srcdir', os.path.dirname (__file__))) + +libs = os.getenv ('libs', '.libs') + +ldd = os.getenv ('LDD', shutil.which ('ldd')) +if not ldd: + otool = os.getenv ('OTOOL', shutil.which ('otool')) + if otool: + ldd = otool + ' -L' + else: + print ('check-libstdc++.py: \'ldd\' not found; skipping test') + sys.exit (77) + +stat = 0 +tested = False + +# harfbuzz-icu links to libstdc++ because icu does. +for soname in ['harfbuzz', 'harfbuzz-subset', 'harfbuzz-gobject']: + for suffix in ['so', 'dylib']: + so = os.path.join (libs, 'lib%s.%s' % (soname, suffix)) + if not os.path.exists (so): continue + + print ('Checking that we are not linking to libstdc++ or libc++ in %s' % so) + ldd_result = subprocess.check_output (ldd.split() + [so]) + if (b'libstdc++' in ldd_result) or (b'libc++' in ldd_result): + print ('Ouch, %s is linked to libstdc++ or libc++' % so) + stat = 1 + + tested = True + +if not tested: + print ('check-libstdc++.py: libharfbuzz shared library not found; skipping test') + sys.exit (77) + +sys.exit (stat) diff --git a/libs/harfbuzz/src/check-static-inits.py b/libs/harfbuzz/src/check-static-inits.py new file mode 100644 index 00000000..c6e2db1e --- /dev/null +++ b/libs/harfbuzz/src/check-static-inits.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python3 + +import sys, os, shutil, subprocess, glob, re + +builddir = os.getenv ('builddir', os.path.dirname (__file__)) +libs = os.getenv ('libs', '.libs') + +objdump = os.getenv ('OBJDUMP', shutil.which ('objdump')) +if not objdump: + print ('check-static-inits.py: \'ldd\' not found; skipping test') + sys.exit (77) + +if sys.version_info < (3, 5): + print ('check-static-inits.py: needs python 3.5 for recursive support in glob') + sys.exit (77) + +OBJS = glob.glob (os.path.join (builddir, libs, '**', '*hb*.o'), recursive=True) +if not OBJS: + print ('check-static-inits.py: object files not found; skipping test') + sys.exit (77) + +stat = 0 +tested = 0 + +for obj in OBJS: + result = subprocess.run(objdump.split () + ['-t', obj], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + + if result.returncode: + if result.stderr.find (b'not recognized') != -1: + # https://github.com/harfbuzz/harfbuzz/issues/3019 + print ('objdump %s returned "not recognized", skipping' % obj) + continue + print ('objdump %s returned error:\n%s' % (obj, result.stderr.decode ('utf-8'))) + stat = 2 + + result = result.stdout.decode ('utf-8') + + # Checking that no object file has static initializers + for l in re.findall (r'^.*\.[cd]tors.*$', result, re.MULTILINE): + if not re.match (r'.*\b0+\b', l): + print ('Ouch, %s has static initializers/finalizers' % obj) + stat = 1 + + # Checking that no object file has lazy static C++ constructors/destructors or other such stuff + if ('__cxa_' in result) and ('__ubsan_handle' not in result): + print ('Ouch, %s has lazy static C++ constructors/destructors or other such stuff' % obj) + stat = 1 + + tested += 1 + +sys.exit (stat if tested else 77) diff --git a/libs/harfbuzz/src/check-symbols.py b/libs/harfbuzz/src/check-symbols.py new file mode 100644 index 00000000..11ca28dc --- /dev/null +++ b/libs/harfbuzz/src/check-symbols.py @@ -0,0 +1,74 @@ +#!/usr/bin/env python3 + +import sys, os, shutil, subprocess, re, difflib + +os.environ['LC_ALL'] = 'C' # otherwise 'nm' prints in wrong order + +builddir = os.getenv ('builddir', os.path.dirname (__file__)) +libs = os.getenv ('libs', '.libs') + +IGNORED_SYMBOLS = '|'.join(['_fini', '_init', '_fdata', '_ftext', '_fbss', + '__bss_start', '__bss_start__', '__bss_end__', '_edata', '_end', '_bss_end__', + '__end__', '__gcov_.*', 'llvm_.*', 'flush_fn_list', 'writeout_fn_list', 'mangle_path', + 'lprofDirMode', 'reset_fn_list']) + +nm = os.getenv ('NM', shutil.which ('nm')) +if not nm: + print ('check-symbols.py: \'nm\' not found; skipping test') + sys.exit (77) + +cxxfilt = shutil.which ('c++filt') + +tested = False +stat = 0 + +for soname in ['harfbuzz', 'harfbuzz-subset', 'harfbuzz-icu', 'harfbuzz-gobject']: + for suffix in ['so', 'dylib']: + so = os.path.join (builddir, libs, 'lib%s.%s' % (soname, suffix)) + if not os.path.exists (so): continue + + # On macOS, C symbols are prefixed with _ + symprefix = '_' if suffix == 'dylib' else '' + + EXPORTED_SYMBOLS = [s.split ()[2] + for s in re.findall (r'^.+ [BCDGIRST] .+$', subprocess.check_output (nm.split() + [so]).decode ('utf-8'), re.MULTILINE) + if not re.match (r'.* %s(%s)\b' % (symprefix, IGNORED_SYMBOLS), s)] + + # run again c++filt also if is available + if cxxfilt: + EXPORTED_SYMBOLS = subprocess.check_output ( + [cxxfilt], input='\n'.join (EXPORTED_SYMBOLS).encode () + ).decode ('utf-8').splitlines () + + prefix = (symprefix + os.path.basename (so)).replace ('libharfbuzz', 'hb').replace ('-', '_').split ('.')[0] + + print ('Checking that %s does not expose internal symbols' % so) + suspicious_symbols = [x for x in EXPORTED_SYMBOLS if not re.match (r'^%s(_|$)' % prefix, x)] + if suspicious_symbols: + print ('Ouch, internal symbols exposed:', suspicious_symbols) + stat = 1 + + def_path = os.path.join (builddir, soname + '.def') + if not os.path.exists (def_path): + print ('\'%s\' not found; skipping' % def_path) + else: + print ('Checking that %s has the same symbol list as %s' % (so, def_path)) + with open (def_path, 'r', encoding='utf-8') as f: def_file = f.read () + diff_result = list (difflib.context_diff ( + def_file.splitlines (), + ['EXPORTS'] + [re.sub ('^%shb' % symprefix, 'hb', x) for x in EXPORTED_SYMBOLS] + + # cheat: copy the last line from the def file! + [def_file.splitlines ()[-1]] + )) + + if diff_result: + print ('\n'.join (diff_result)) + stat = 1 + + tested = True + +if not tested: + print ('check-symbols.py: no shared libraries found; skipping test') + sys.exit (77) + +sys.exit (stat) diff --git a/libs/harfbuzz/src/failing-alloc.c b/libs/harfbuzz/src/failing-alloc.c new file mode 100644 index 00000000..1bfb935b --- /dev/null +++ b/libs/harfbuzz/src/failing-alloc.c @@ -0,0 +1,65 @@ +/* + * Copyright © 2020 Ebrahim Byagowi + * + * 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. + */ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +int alloc_state = 0; + +__attribute__((no_sanitize("integer"))) +static int fastrand () +{ + if (!alloc_state) return 1; + /* Based on https://software.intel.com/content/www/us/en/develop/articles/fast-random-number-generator-on-the-intel-pentiumr-4-processor.html */ + alloc_state = (214013 * alloc_state + 2531011); + return (alloc_state >> 16) & 0x7FFF; +} + +void* hb_malloc_impl (size_t size) +{ + return (fastrand () % 16) ? malloc (size) : NULL; +} + +void* hb_calloc_impl (size_t nmemb, size_t size) +{ + return (fastrand () % 16) ? calloc (nmemb, size) : NULL; +} + +void* hb_realloc_impl (void *ptr, size_t size) +{ + return (fastrand () % 16) ? realloc (ptr, size) : NULL; +} + +void hb_free_impl (void *ptr) +{ + return free (ptr); +} + +#ifdef __cplusplus +} +#endif diff --git a/libs/harfbuzz/src/fix_get_types.py b/libs/harfbuzz/src/fix_get_types.py new file mode 100644 index 00000000..208b9dfc --- /dev/null +++ b/libs/harfbuzz/src/fix_get_types.py @@ -0,0 +1,15 @@ +#!/usr/bin/env python3 + +import re +import argparse + +parser = argparse.ArgumentParser () +parser.add_argument ('input') +parser.add_argument ('output') +args = parser.parse_args () + +with open (args.input, 'r') as inp, open (args.output, 'w') as out: + for l in inp.readlines (): + l = re.sub ('_t_get_type', '_get_type', l) + l = re.sub ('_T \(', ' (', l) + out.write (l) diff --git a/libs/harfbuzz/src/gen-arabic-joining-list.py b/libs/harfbuzz/src/gen-arabic-joining-list.py new file mode 100644 index 00000000..7ec7425f --- /dev/null +++ b/libs/harfbuzz/src/gen-arabic-joining-list.py @@ -0,0 +1,106 @@ +#!/usr/bin/env python3 + +"""usage: ./gen-arabic-joining-table.py ArabicShaping.txt Scripts.txt + +Input files: +* https://unicode.org/Public/UCD/latest/ucd/ArabicShaping.txt +* https://unicode.org/Public/UCD/latest/ucd/Scripts.txt +""" + +import os.path, sys + +if len (sys.argv) != 3: + sys.exit (__doc__) + +files = [open (x, encoding='utf-8') for x in sys.argv[1:]] + +headers = [[f.readline (), f.readline ()] for f in files] +while files[0].readline ().find ('##################') < 0: + pass + +def read (f): + mapping = {} + for line in f: + + j = line.find ('#') + if j >= 0: + line = line[:j] + + fields = [x.strip () for x in line.split (';')] + if len (fields) == 1: + continue + + uu = fields[0].split ('..') + start = int (uu[0], 16) + if len (uu) == 1: + end = start + else: + end = int (uu[1], 16) + + t = fields[1] + + for u in range (start, end + 1): + mapping[u] = t + + return mapping + +def read_joining_uu (f): + values = set () + for line in f: + + if line[0] == '#': + continue + + fields = [x.strip () for x in line.split (';')] + if len (fields) == 1: + continue + if fields[2] in {'T', 'U'}: + continue + + values.add (int (fields[0], 16)) + + return sorted (values) + +def print_has_arabic_joining (scripts, joining_uu): + + print ("static bool") + print ("has_arabic_joining (hb_script_t script)") + print ("{") + print (" /* List of scripts that have data in arabic-table. */") + print (" switch ((int) script)") + print (" {") + + for script in sorted ({scripts[u] for u in joining_uu if scripts[u] not in {'Common', 'Inherited'}}): + print (" case HB_SCRIPT_{}:".format (script.upper ())) + + print (" return true;") + print () + print (" default:") + print (" return false;") + print (" }") + print ("}") + print () + +print ("/* == Start of generated function == */") +print ("/*") +print (" * The following function is generated by running:") +print (" *") +print (" * ./gen-arabic-joining-list.py ArabicShaping.txt Scripts.txt") +print (" *") +print (" * on files with these headers:") +print (" *") +for h in headers: + for l in h: + print (" * %s" % (l.strip ())) +print (" */") +print () +print ("#ifndef HB_OT_SHAPER_ARABIC_JOINING_LIST_HH") +print ("#define HB_OT_SHAPER_ARABIC_JOINING_LIST_HH") +print () + +print_has_arabic_joining (read (files[1]), read_joining_uu (files[0])) + +print () +print ("#endif /* HB_OT_SHAPER_ARABIC_JOINING_LIST_HH */") +print () +print ("/* == End of generated function == */") diff --git a/libs/harfbuzz/src/gen-arabic-pua.py b/libs/harfbuzz/src/gen-arabic-pua.py new file mode 100644 index 00000000..4cf29008 --- /dev/null +++ b/libs/harfbuzz/src/gen-arabic-pua.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python3 + +"""usage: ./gen-arabic-pua.py +""" + +import packTab + + +print ("/* == Start of generated table == */") +print ("/*") +print (" * The following table is generated by running:") +print (" *") +print (" * ./gen-arabic-pua.py") +print (" *") +print (" */") +print () +print ("#ifndef HB_OT_SHAPER_ARABIC_PUA_HH") +print ("#define HB_OT_SHAPER_ARABIC_PUA_HH") +print () + +code = packTab.Code('_hb_arabic') + +for p in ("ArabicPUASimplified.txt", "ArabicPUATraditional.txt"): + with open (p, encoding='utf-8') as f: + fields = [l.split('\t') for l in f if l[:1] != '#'] + data = {int(fs[1], 16):int(fs[0], 16) for fs in fields} + sol = packTab.pack_table(data, compression=9) + sol.genCode(code, f'pua_{p[9:13].lower()}_map') + +code.print_c(linkage='static inline') + +print () +print ("#endif /* HB_OT_SHAPER_ARABIC_PUA_HH */") +print () +print ("/* == End of generated table == */") diff --git a/libs/harfbuzz/src/gen-arabic-table.py b/libs/harfbuzz/src/gen-arabic-table.py new file mode 100644 index 00000000..8278d7d6 --- /dev/null +++ b/libs/harfbuzz/src/gen-arabic-table.py @@ -0,0 +1,358 @@ +#!/usr/bin/env python3 + +"""usage: ./gen-arabic-table.py ArabicShaping.txt UnicodeData.txt Blocks.txt + +Input files: +* https://unicode.org/Public/UCD/latest/ucd/ArabicShaping.txt +* https://unicode.org/Public/UCD/latest/ucd/UnicodeData.txt +* https://unicode.org/Public/UCD/latest/ucd/Blocks.txt +""" + +import os.path, sys + +if len (sys.argv) != 4: + sys.exit (__doc__) + +files = [open (x, encoding='utf-8') for x in sys.argv[1:]] + +headers = [[files[0].readline (), files[0].readline ()], [files[2].readline (), files[2].readline ()]] +headers.append (["UnicodeData.txt does not have a header."]) +while files[0].readline ().find ('##################') < 0: + pass + +blocks = {} +def read_blocks(f): + global blocks + for line in f: + + j = line.find ('#') + if j >= 0: + line = line[:j] + + fields = [x.strip () for x in line.split (';')] + if len (fields) == 1: + continue + + uu = fields[0].split ('..') + start = int (uu[0], 16) + if len (uu) == 1: + end = start + else: + end = int (uu[1], 16) + + t = fields[1] + + for u in range (start, end + 1): + blocks[u] = t + +def print_joining_table(f): + + values = {} + for line in f: + + if line[0] == '#': + continue + + fields = [x.strip () for x in line.split (';')] + if len (fields) == 1: + continue + + u = int (fields[0], 16) + + if fields[3] in ["ALAPH", "DALATH RISH"]: + value = "JOINING_GROUP_" + fields[3].replace(' ', '_') + else: + value = "JOINING_TYPE_" + fields[2] + values[u] = value + + short_value = {} + for value in sorted (set ([v for v in values.values ()] + ['JOINING_TYPE_X'])): + short = ''.join(x[0] for x in value.split('_')[2:]) + assert short not in short_value.values() + short_value[value] = short + + print () + for value,short in short_value.items(): + print ("#define %s %s" % (short, value)) + + uu = sorted(values.keys()) + num = len(values) + all_blocks = set([blocks[u] for u in uu]) + + last = -100000 + ranges = [] + for u in uu: + if u - last <= 1+16*5: + ranges[-1][-1] = u + else: + ranges.append([u,u]) + last = u + + print () + print ("static const uint8_t joining_table[] =") + print ("{") + last_block = None + offset = 0 + for start,end in ranges: + + print () + print ("#define joining_offset_0x%04xu %d" % (start, offset)) + + for u in range(start, end+1): + + block = blocks.get(u, last_block) + value = values.get(u, "JOINING_TYPE_X") + + if block != last_block or u == start: + if u != start: + print () + if block in all_blocks: + print ("\n /* %s */" % block) + else: + print ("\n /* FILLER */") + last_block = block + if u % 32 != 0: + print () + print (" /* %04X */" % (u//32*32), " " * (u % 32), end="") + + if u % 32 == 0: + print () + print (" /* %04X */ " % u, end="") + print ("%s," % short_value[value], end="") + print () + + offset += end - start + 1 + print () + occupancy = num * 100. / offset + print ("}; /* Table items: %d; occupancy: %d%% */" % (offset, occupancy)) + print () + + page_bits = 12 + print () + print ("static unsigned int") + print ("joining_type (hb_codepoint_t u)") + print ("{") + print (" switch (u >> %d)" % page_bits) + print (" {") + pages = set([u>>page_bits for u in [s for s,e in ranges]+[e for s,e in ranges]]) + for p in sorted(pages): + print (" case 0x%0Xu:" % p) + for (start,end) in ranges: + if p not in [start>>page_bits, end>>page_bits]: continue + offset = "joining_offset_0x%04xu" % start + print (" if (hb_in_range (u, 0x%04Xu, 0x%04Xu)) return joining_table[u - 0x%04Xu + %s];" % (start, end, start, offset)) + print (" break;") + print ("") + print (" default:") + print (" break;") + print (" }") + print (" return X;") + print ("}") + print () + for value,short in short_value.items(): + print ("#undef %s" % (short)) + print () + +LIGATURES = ( + 0xF2EE, 0xFC08, 0xFC0E, 0xFC12, 0xFC32, 0xFC3F, 0xFC40, 0xFC41, 0xFC42, + 0xFC44, 0xFC4E, 0xFC5E, 0xFC60, 0xFC61, 0xFC62, 0xFC6A, 0xFC6D, 0xFC6F, + 0xFC70, 0xFC73, 0xFC75, 0xFC86, 0xFC8F, 0xFC91, 0xFC94, 0xFC9C, 0xFC9D, + 0xFC9E, 0xFC9F, 0xFCA1, 0xFCA2, 0xFCA3, 0xFCA4, 0xFCA8, 0xFCAA, 0xFCAC, + 0xFCB0, 0xFCC9, 0xFCCA, 0xFCCB, 0xFCCC, 0xFCCD, 0xFCCE, 0xFCCF, 0xFCD0, + 0xFCD1, 0xFCD2, 0xFCD3, 0xFCD5, 0xFCDA, 0xFCDB, 0xFCDC, 0xFCDD, 0xFD30, + 0xFD88, 0xFEF5, 0xFEF6, 0xFEF7, 0xFEF8, 0xFEF9, 0xFEFA, 0xFEFB, 0xFEFC, + 0xF201, 0xF211, 0xF2EE, +) + +def print_shaping_table(f): + + shapes = {} + ligatures = {} + names = {} + lines = f.readlines() + lines += [ + "F201;PUA ARABIC LIGATURE LELLAH ISOLATED FORM;Lo;0;AL; 0644 0644 0647;;;;N;;;;;", + "F211;PUA ARABIC LIGATURE LAM WITH MEEM WITH JEEM INITIAL FORM;Lo;0;AL; 0644 0645 062C;;;;N;;;;;", + "F2EE;PUA ARABIC LIGATURE SHADDA WITH FATHATAN ISOLATED FORM;Lo;0;AL; 0020 064B 0651;;;;N;;;;;", + ] + for line in lines: + + fields = [x.strip () for x in line.split (';')] + if fields[5][0:1] != '<': + continue + + items = fields[5].split (' ') + shape, items = items[0][1:-1], tuple (int (x, 16) for x in items[1:]) + c = int (fields[0], 16) + + if not shape in ['initial', 'medial', 'isolated', 'final']: + continue + + if len (items) != 1: + # Mark ligatures start with space and are in visual order, so we + # remove the space and reverse the items. + if items[0] == 0x0020: + items = items[:0:-1] + shape = None + # We only care about a subset of ligatures + if c not in LIGATURES: + continue + + # Save ligature + names[c] = fields[1] + if items not in ligatures: + ligatures[items] = {} + ligatures[items][shape] = c + else: + # Save shape + if items[0] not in names: + names[items[0]] = fields[1] + else: + names[items[0]] = os.path.commonprefix ([names[items[0]], fields[1]]).strip () + if items[0] not in shapes: + shapes[items[0]] = {} + shapes[items[0]][shape] = c + + print () + print ("static const uint16_t shaping_table[][4] =") + print ("{") + + keys = shapes.keys () + min_u, max_u = min (keys), max (keys) + for u in range (min_u, max_u + 1): + s = [shapes[u][shape] if u in shapes and shape in shapes[u] else 0 + for shape in ['initial', 'medial', 'final', 'isolated']] + value = ', '.join ("0x%04Xu" % c for c in s) + print (" {%s}, /* U+%04X %s */" % (value, u, names[u] if u in names else "")) + + print ("};") + print () + print ("#define SHAPING_TABLE_FIRST 0x%04Xu" % min_u) + print ("#define SHAPING_TABLE_LAST 0x%04Xu" % max_u) + print () + + ligas_2 = {} + ligas_3 = {} + ligas_mark_2 = {} + for key in ligatures.keys (): + for shape in ligatures[key]: + c = ligatures[key][shape] + if len(key) == 3: + if shape == 'isolated': + liga = (shapes[key[0]]['initial'], shapes[key[1]]['medial'], shapes[key[2]]['final']) + elif shape == 'final': + liga = (shapes[key[0]]['medial'], shapes[key[1]]['medial'], shapes[key[2]]['final']) + elif shape == 'initial': + liga = (shapes[key[0]]['initial'], shapes[key[1]]['medial'], shapes[key[2]]['medial']) + else: + raise Exception ("Unexpected shape", shape) + if liga[0] not in ligas_3: + ligas_3[liga[0]] = [] + ligas_3[liga[0]].append ((liga[1], liga[2], c)) + elif len(key) == 2: + if shape is None: + liga = key + if liga[0] not in ligas_mark_2: + ligas_mark_2[liga[0]] = [] + ligas_mark_2[liga[0]].append ((liga[1], c)) + continue + elif shape == 'isolated': + liga = (shapes[key[0]]['initial'], shapes[key[1]]['final']) + elif shape == 'final': + liga = (shapes[key[0]]['medial'], shapes[key[1]]['final']) + elif shape == 'initial': + liga = (shapes[key[0]]['initial'], shapes[key[1]]['medial']) + else: + raise Exception ("Unexpected shape", shape) + if liga[0] not in ligas_2: + ligas_2[liga[0]] = [] + ligas_2[liga[0]].append ((liga[1], c)) + else: + raise Exception ("Unexpected number of ligature components", key) + max_i = max (len (ligas_2[l]) for l in ligas_2) + print () + print ("static const struct ligature_set_t {") + print (" uint16_t first;") + print (" struct ligature_pairs_t {") + print (" uint16_t components[1];") + print (" uint16_t ligature;") + print (" } ligatures[%d];" % max_i) + print ("} ligature_table[] =") + print ("{") + for first in sorted (ligas_2.keys ()): + + print (" { 0x%04Xu, {" % (first)) + for liga in ligas_2[first]: + print (" { {0x%04Xu}, 0x%04Xu }, /* %s */" % (liga[0], liga[1], names[liga[1]])) + print (" }},") + + print ("};") + print () + + max_i = max (len (ligas_mark_2[l]) for l in ligas_mark_2) + print () + print ("static const struct ligature_mark_set_t {") + print (" uint16_t first;") + print (" struct ligature_pairs_t {") + print (" uint16_t components[1];") + print (" uint16_t ligature;") + print (" } ligatures[%d];" % max_i) + print ("} ligature_mark_table[] =") + print ("{") + for first in sorted (ligas_mark_2.keys ()): + + print (" { 0x%04Xu, {" % (first)) + for liga in ligas_mark_2[first]: + print (" { {0x%04Xu}, 0x%04Xu }, /* %s */" % (liga[0], liga[1], names[liga[1]])) + print (" }},") + + print ("};") + print () + + max_i = max (len (ligas_3[l]) for l in ligas_3) + print () + print ("static const struct ligature_3_set_t {") + print (" uint16_t first;") + print (" struct ligature_triplets_t {") + print (" uint16_t components[2];") + print (" uint16_t ligature;") + print (" } ligatures[%d];" % max_i) + print ("} ligature_3_table[] =") + print ("{") + for first in sorted (ligas_3.keys ()): + + print (" { 0x%04Xu, {" % (first)) + for liga in ligas_3[first]: + print (" { {0x%04Xu, 0x%04Xu}, 0x%04Xu}, /* %s */" % (liga[0], liga[1], liga[2], names[liga[2]])) + print (" }},") + + print ("};") + print () + + + +print ("/* == Start of generated table == */") +print ("/*") +print (" * The following table is generated by running:") +print (" *") +print (" * ./gen-arabic-table.py ArabicShaping.txt UnicodeData.txt Blocks.txt") +print (" *") +print (" * on files with these headers:") +print (" *") +for h in headers: + for l in h: + print (" * %s" % (l.strip())) +print (" */") +print () +print ("#ifndef HB_OT_SHAPER_ARABIC_TABLE_HH") +print ("#define HB_OT_SHAPER_ARABIC_TABLE_HH") +print () + +read_blocks (files[2]) +print_joining_table (files[0]) +print_shaping_table (files[1]) + +print () +print ("#endif /* HB_OT_SHAPER_ARABIC_TABLE_HH */") +print () +print ("/* == End of generated table == */") diff --git a/libs/harfbuzz/src/gen-def.py b/libs/harfbuzz/src/gen-def.py new file mode 100644 index 00000000..e751f524 --- /dev/null +++ b/libs/harfbuzz/src/gen-def.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python3 + +"usage: gen-def.py harfbuzz.def hb.h [hb-blob.h hb-buffer.h ...]" + +import os, re, sys + +if len (sys.argv) < 3: + sys.exit(__doc__) + +output_file = sys.argv[1] +header_paths = sys.argv[2:] + +headers_content = [] +for h in header_paths: + if h.endswith (".h"): + with open (h, encoding='utf-8') as f: headers_content.append (f.read ()) + +symbols = sorted (re.findall (r"^hb_\w+(?= \()", "\n".join (headers_content), re.M)) +if '--experimental-api' not in sys.argv: + # Move these to harfbuzz-sections.txt when got stable + experimental_symbols = \ +"""hb_subset_repack_or_fail +hb_subset_input_pin_axis_location +hb_subset_input_pin_axis_to_default +hb_subset_preprocess +""".splitlines () + symbols = [x for x in symbols if x not in experimental_symbols] +symbols = "\n".join (symbols) + +result = symbols if os.getenv ('PLAIN_LIST', '') else """EXPORTS +%s +LIBRARY lib%s-0.dll""" % (symbols, output_file.replace ('src/', '').replace ('.def', '')) + +with open (output_file, "w") as f: f.write (result) diff --git a/libs/harfbuzz/src/gen-emoji-table.py b/libs/harfbuzz/src/gen-emoji-table.py new file mode 100644 index 00000000..42a3fb8d --- /dev/null +++ b/libs/harfbuzz/src/gen-emoji-table.py @@ -0,0 +1,98 @@ +#!/usr/bin/env python3 + +"""usage: ./gen-emoji-table.py emoji-data.txt emoji-test.txt + +Input file: +* https://www.unicode.org/Public/UCD/latest/ucd/emoji/emoji-data.txt +* https://www.unicode.org/Public/emoji/latest/emoji-test.txt +""" + +import sys +from collections import OrderedDict +import packTab + +if len (sys.argv) != 3: + sys.exit (__doc__) + +f = open(sys.argv[1]) +header = [f.readline () for _ in range(10)] + +ranges = OrderedDict() +for line in f.readlines(): + line = line.strip() + if not line or line[0] == '#': + continue + rang, typ = [s.strip() for s in line.split('#')[0].split(';')[:2]] + + rang = [int(s, 16) for s in rang.split('..')] + if len(rang) > 1: + start, end = rang + else: + start = end = rang[0] + + if typ not in ranges: + ranges[typ] = [] + if ranges[typ] and ranges[typ][-1][1] == start - 1: + ranges[typ][-1] = (ranges[typ][-1][0], end) + else: + ranges[typ].append((start, end)) + + + +print ("/* == Start of generated table == */") +print ("/*") +print (" * The following tables are generated by running:") +print (" *") +print (" * ./gen-emoji-table.py emoji-data.txt") +print (" *") +print (" * on file with this header:") +print (" *") +for l in header: + print (" * %s" % (l.strip())) +print (" */") +print () +print ("#ifndef HB_UNICODE_EMOJI_TABLE_HH") +print ("#define HB_UNICODE_EMOJI_TABLE_HH") +print () +print ('#include "hb-unicode.hh"') +print () + +for typ, s in ranges.items(): + if typ != "Extended_Pictographic": continue + + arr = dict() + for start,end in s: + for i in range(start, end + 1): + arr[i] = 1 + + sol = packTab.pack_table(arr, 0, compression=9) + code = packTab.Code('_hb_emoji') + sol.genCode(code, 'is_'+typ) + code.print_c(linkage='static inline') + print() + +print () +print ("#endif /* HB_UNICODE_EMOJI_TABLE_HH */") +print () +print ("/* == End of generated table == */") + + +# Generate test file. +sequences = [] +with open(sys.argv[2]) as f: + for line in f.readlines(): + if "#" in line: + line = line[:line.index("#")] + if ";" in line: + line = line[:line.index(";")] + line = line.strip() + line = line.split(" ") + if len(line) < 2: + continue + sequences.append(line) + +with open("../test/shape/data/in-house/tests/emoji-clusters.tests", "w") as f: + for sequence in sequences: + f.write("../fonts/AdobeBlank2.ttf;--no-glyph-names --no-positions --font-funcs=ot") + f.write(";" + ",".join(sequence)) + f.write(";[" + "|".join("1=0" for c in sequence) + "]\n") diff --git a/libs/harfbuzz/src/gen-harfbuzzcc.py b/libs/harfbuzz/src/gen-harfbuzzcc.py new file mode 100644 index 00000000..22738404 --- /dev/null +++ b/libs/harfbuzz/src/gen-harfbuzzcc.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python3 + +"This tool is intended to be used from meson" + +import os, sys, shutil + +if len (sys.argv) < 3: + sys.exit (__doc__) + +OUTPUT = sys.argv[1] +CURRENT_SOURCE_DIR = sys.argv[2] + +# make sure input files are unique +sources = sorted(set(sys.argv[3:])) + +with open (OUTPUT, "wb") as f: + f.write ("".join ('#include "{}"\n'.format (os.path.relpath (os.path.abspath (x), CURRENT_SOURCE_DIR)) for x in sources if x.endswith (".cc")).encode ()) + +# copy it also to the source tree, but only if it has changed +baseline_filename = os.path.join (CURRENT_SOURCE_DIR, os.path.basename (OUTPUT)) +with open(baseline_filename, "rb") as baseline: + with open(OUTPUT, "rb") as generated: + if baseline.read() != generated.read(): + shutil.copyfile (OUTPUT, baseline_filename) diff --git a/libs/harfbuzz/src/gen-hb-version.py b/libs/harfbuzz/src/gen-hb-version.py new file mode 100644 index 00000000..06018edf --- /dev/null +++ b/libs/harfbuzz/src/gen-hb-version.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python3 + +"This tool is intended to be used from meson" + +import os, sys, shutil, re + +if len (sys.argv) < 4: + sys.exit(__doc__) + +version = sys.argv[1] +major, minor, micro = version.split (".") + +OUTPUT = sys.argv[2] +INPUT = sys.argv[3] +CURRENT_SOURCE_DIR = os.path.dirname(INPUT) + +try: + with open (OUTPUT, "r", encoding='utf-8') as old_output: + for line in old_output: + old_version = re.match (r"#define HB_VERSION_STRING \"(\d.\d.\d)\"", line) + if old_version and old_version[1] == version: + sys.exit () +except IOError: + pass + +with open (INPUT, "r", encoding='utf-8') as template: + with open (OUTPUT, "wb") as output: + output.write (template.read () + .replace ("@HB_VERSION_MAJOR@", major) + .replace ("@HB_VERSION_MINOR@", minor) + .replace ("@HB_VERSION_MICRO@", micro) + .replace ("@HB_VERSION@", version) + .encode ()) + +# copy it also to the source tree, but only if it has changed +baseline_filename = os.path.join (CURRENT_SOURCE_DIR, os.path.basename (OUTPUT)) +with open(baseline_filename, "rb") as baseline: + with open(OUTPUT, "rb") as generated: + if baseline.read() != generated.read(): + shutil.copyfile (OUTPUT, baseline_filename) diff --git a/libs/harfbuzz/src/gen-indic-table.py b/libs/harfbuzz/src/gen-indic-table.py new file mode 100644 index 00000000..0ff3ef4a --- /dev/null +++ b/libs/harfbuzz/src/gen-indic-table.py @@ -0,0 +1,696 @@ +#!/usr/bin/env python3 + +"""usage: ./gen-indic-table.py IndicSyllabicCategory.txt IndicPositionalCategory.txt Blocks.txt + +Input files: +* https://unicode.org/Public/UCD/latest/ucd/IndicSyllabicCategory.txt +* https://unicode.org/Public/UCD/latest/ucd/IndicPositionalCategory.txt +* https://unicode.org/Public/UCD/latest/ucd/Blocks.txt +""" + +import sys + +if len (sys.argv) != 4: + sys.exit (__doc__) + +ALLOWED_SINGLES = [0x00A0, 0x25CC] +ALLOWED_BLOCKS = [ + 'Basic Latin', + 'Latin-1 Supplement', + 'Devanagari', + 'Bengali', + 'Gurmukhi', + 'Gujarati', + 'Oriya', + 'Tamil', + 'Telugu', + 'Kannada', + 'Malayalam', + 'Myanmar', + 'Khmer', + 'Vedic Extensions', + 'General Punctuation', + 'Superscripts and Subscripts', + 'Devanagari Extended', + 'Myanmar Extended-B', + 'Myanmar Extended-A', +] + +files = [open (x, encoding='utf-8') for x in sys.argv[1:]] + +headers = [[f.readline () for i in range (2)] for f in files] + +unicode_data = [{} for _ in files] +for i, f in enumerate (files): + for line in f: + + j = line.find ('#') + if j >= 0: + line = line[:j] + + fields = [x.strip () for x in line.split (';')] + if len (fields) == 1: + continue + + uu = fields[0].split ('..') + start = int (uu[0], 16) + if len (uu) == 1: + end = start + else: + end = int (uu[1], 16) + + t = fields[1] + + for u in range (start, end + 1): + unicode_data[i][u] = t + +# Merge data into one dict: +defaults = ('Other', 'Not_Applicable', 'No_Block') +combined = {} +for i,d in enumerate (unicode_data): + for u,v in d.items (): + if i == 2 and not u in combined: + continue + if not u in combined: + combined[u] = list (defaults) + combined[u][i] = v +combined = {k:v for k,v in combined.items() if k in ALLOWED_SINGLES or v[2] in ALLOWED_BLOCKS} + + +# Convert categories & positions types + +categories = { + 'indic' : [ + 'X', + 'C', + 'V', + 'N', + 'H', + 'ZWNJ', + 'ZWJ', + 'M', + 'SM', + 'A', + 'VD', + 'PLACEHOLDER', + 'DOTTEDCIRCLE', + 'RS', + 'Repha', + 'Ra', + 'CM', + 'Symbol', + 'CS', + ], + 'khmer' : [ + 'VAbv', + 'VBlw', + 'VPre', + 'VPst', + + 'Robatic', + 'Xgroup', + 'Ygroup', + ], + 'myanmar' : [ + 'VAbv', + 'VBlw', + 'VPre', + 'VPst', + + 'IV', + 'As', + 'DB', + 'GB', + 'MH', + 'MR', + 'MW', + 'MY', + 'PT', + 'VS', + 'ML', + ], +} + +category_map = { + 'Other' : 'X', + 'Avagraha' : 'Symbol', + 'Bindu' : 'SM', + 'Brahmi_Joining_Number' : 'PLACEHOLDER', # Don't care. + 'Cantillation_Mark' : 'A', + 'Consonant' : 'C', + 'Consonant_Dead' : 'C', + 'Consonant_Final' : 'CM', + 'Consonant_Head_Letter' : 'C', + 'Consonant_Initial_Postfixed' : 'C', # TODO + 'Consonant_Killer' : 'M', # U+17CD only. + 'Consonant_Medial' : 'CM', + 'Consonant_Placeholder' : 'PLACEHOLDER', + 'Consonant_Preceding_Repha' : 'Repha', + 'Consonant_Prefixed' : 'X', # Don't care. + 'Consonant_Subjoined' : 'CM', + 'Consonant_Succeeding_Repha' : 'CM', + 'Consonant_With_Stacker' : 'CS', + 'Gemination_Mark' : 'SM', # https://github.com/harfbuzz/harfbuzz/issues/552 + 'Invisible_Stacker' : 'H', + 'Joiner' : 'ZWJ', + 'Modifying_Letter' : 'X', + 'Non_Joiner' : 'ZWNJ', + 'Nukta' : 'N', + 'Number' : 'PLACEHOLDER', + 'Number_Joiner' : 'PLACEHOLDER', # Don't care. + 'Pure_Killer' : 'M', # Is like a vowel matra. + 'Register_Shifter' : 'RS', + 'Syllable_Modifier' : 'SM', + 'Tone_Letter' : 'X', + 'Tone_Mark' : 'N', + 'Virama' : 'H', + 'Visarga' : 'SM', + 'Vowel' : 'V', + 'Vowel_Dependent' : 'M', + 'Vowel_Independent' : 'V', + 'Dotted_Circle' : 'DOTTEDCIRCLE', # Ours, not Unicode's + 'Ra' : 'Ra', # Ours, not Unicode's +} +position_map = { + 'Not_Applicable' : 'END', + + 'Left' : 'PRE_C', + 'Top' : 'ABOVE_C', + 'Bottom' : 'BELOW_C', + 'Right' : 'POST_C', + + # These should resolve to the position of the last part of the split sequence. + 'Bottom_And_Right' : 'POST_C', + 'Left_And_Right' : 'POST_C', + 'Top_And_Bottom' : 'BELOW_C', + 'Top_And_Bottom_And_Left' : 'BELOW_C', + 'Top_And_Bottom_And_Right' : 'POST_C', + 'Top_And_Left' : 'ABOVE_C', + 'Top_And_Left_And_Right' : 'POST_C', + 'Top_And_Right' : 'POST_C', + + 'Overstruck' : 'AFTER_MAIN', + 'Visual_order_left' : 'PRE_M', +} + +category_overrides = { + + # These are the variation-selectors. They only appear in the Myanmar grammar + # but are not Myanmar-specific + 0xFE00: 'VS', + 0xFE01: 'VS', + 0xFE02: 'VS', + 0xFE03: 'VS', + 0xFE04: 'VS', + 0xFE05: 'VS', + 0xFE06: 'VS', + 0xFE07: 'VS', + 0xFE08: 'VS', + 0xFE09: 'VS', + 0xFE0A: 'VS', + 0xFE0B: 'VS', + 0xFE0C: 'VS', + 0xFE0D: 'VS', + 0xFE0E: 'VS', + 0xFE0F: 'VS', + + # These appear in the OT Myanmar spec, but are not Myanmar-specific + 0x2015: 'PLACEHOLDER', + 0x2022: 'PLACEHOLDER', + 0x25FB: 'PLACEHOLDER', + 0x25FC: 'PLACEHOLDER', + 0x25FD: 'PLACEHOLDER', + 0x25FE: 'PLACEHOLDER', + + + # Indic + + 0x0930: 'Ra', # Devanagari + 0x09B0: 'Ra', # Bengali + 0x09F0: 'Ra', # Bengali + 0x0A30: 'Ra', # Gurmukhi No Reph + 0x0AB0: 'Ra', # Gujarati + 0x0B30: 'Ra', # Oriya + 0x0BB0: 'Ra', # Tamil No Reph + 0x0C30: 'Ra', # Telugu Reph formed only with ZWJ + 0x0CB0: 'Ra', # Kannada + 0x0D30: 'Ra', # Malayalam No Reph, Logical Repha + + # The following act more like the Bindus. + 0x0953: 'SM', + 0x0954: 'SM', + + # The following act like consonants. + 0x0A72: 'C', + 0x0A73: 'C', + 0x1CF5: 'C', + 0x1CF6: 'C', + + # TODO: The following should only be allowed after a Visarga. + # For now, just treat them like regular tone marks. + 0x1CE2: 'A', + 0x1CE3: 'A', + 0x1CE4: 'A', + 0x1CE5: 'A', + 0x1CE6: 'A', + 0x1CE7: 'A', + 0x1CE8: 'A', + + # TODO: The following should only be allowed after some of + # the nasalization marks, maybe only for U+1CE9..U+1CF1. + # For now, just treat them like tone marks. + 0x1CED: 'A', + + # The following take marks in standalone clusters, similar to Avagraha. + 0xA8F2: 'Symbol', + 0xA8F3: 'Symbol', + 0xA8F4: 'Symbol', + 0xA8F5: 'Symbol', + 0xA8F6: 'Symbol', + 0xA8F7: 'Symbol', + 0x1CE9: 'Symbol', + 0x1CEA: 'Symbol', + 0x1CEB: 'Symbol', + 0x1CEC: 'Symbol', + 0x1CEE: 'Symbol', + 0x1CEF: 'Symbol', + 0x1CF0: 'Symbol', + 0x1CF1: 'Symbol', + + 0x0A51: 'M', # https://github.com/harfbuzz/harfbuzz/issues/524 + + # According to ScriptExtensions.txt, these Grantha marks may also be used in Tamil, + # so the Indic shaper needs to know their categories. + 0x11301: 'SM', + 0x11302: 'SM', + 0x11303: 'SM', + 0x1133B: 'N', + 0x1133C: 'N', + + 0x0AFB: 'N', # https://github.com/harfbuzz/harfbuzz/issues/552 + 0x0B55: 'N', # https://github.com/harfbuzz/harfbuzz/issues/2849 + + 0x09FC: 'PLACEHOLDER', # https://github.com/harfbuzz/harfbuzz/pull/1613 + 0x0C80: 'PLACEHOLDER', # https://github.com/harfbuzz/harfbuzz/pull/623 + 0x0D04: 'PLACEHOLDER', # https://github.com/harfbuzz/harfbuzz/pull/3511 + + 0x25CC: 'DOTTEDCIRCLE', + + + # Khmer + + 0x179A: 'Ra', + + 0x17CC: 'Robatic', + 0x17C9: 'Robatic', + 0x17CA: 'Robatic', + + 0x17C6: 'Xgroup', + 0x17CB: 'Xgroup', + 0x17CD: 'Xgroup', + 0x17CE: 'Xgroup', + 0x17CF: 'Xgroup', + 0x17D0: 'Xgroup', + 0x17D1: 'Xgroup', + + 0x17C7: 'Ygroup', + 0x17C8: 'Ygroup', + 0x17DD: 'Ygroup', + 0x17D3: 'Ygroup', # Just guessing. Uniscribe doesn't categorize it. + + 0x17D9: 'PLACEHOLDER', # https://github.com/harfbuzz/harfbuzz/issues/2384 + + + # Myanmar + + # https://docs.microsoft.com/en-us/typography/script-development/myanmar#analyze + + 0x104E: 'C', # The spec says C, IndicSyllableCategory says Consonant_Placeholder + + 0x1004: 'Ra', + 0x101B: 'Ra', + 0x105A: 'Ra', + + 0x1032: 'A', + 0x1036: 'A', + + 0x103A: 'As', + + #0x1040: 'D0', # XXX The spec says D0, but Uniscribe doesn't seem to do. + + 0x103E: 'MH', + 0x1060: 'ML', + 0x103C: 'MR', + 0x103D: 'MW', + 0x1082: 'MW', + 0x103B: 'MY', + 0x105E: 'MY', + 0x105F: 'MY', + + 0x1063: 'PT', + 0x1064: 'PT', + 0x1069: 'PT', + 0x106A: 'PT', + 0x106B: 'PT', + 0x106C: 'PT', + 0x106D: 'PT', + 0xAA7B: 'PT', + + 0x1038: 'SM', + 0x1087: 'SM', + 0x1088: 'SM', + 0x1089: 'SM', + 0x108A: 'SM', + 0x108B: 'SM', + 0x108C: 'SM', + 0x108D: 'SM', + 0x108F: 'SM', + 0x109A: 'SM', + 0x109B: 'SM', + 0x109C: 'SM', + + 0x104A: 'PLACEHOLDER', +} +position_overrides = { + + 0x0A51: 'BELOW_C', # https://github.com/harfbuzz/harfbuzz/issues/524 + + 0x0B01: 'BEFORE_SUB', # Oriya Bindu is BeforeSub in the spec. +} + +def matra_pos_left(u, block): + return "PRE_M" +def matra_pos_right(u, block): + if block == 'Devanagari': return 'AFTER_SUB' + if block == 'Bengali': return 'AFTER_POST' + if block == 'Gurmukhi': return 'AFTER_POST' + if block == 'Gujarati': return 'AFTER_POST' + if block == 'Oriya': return 'AFTER_POST' + if block == 'Tamil': return 'AFTER_POST' + if block == 'Telugu': return 'BEFORE_SUB' if u <= 0x0C42 else 'AFTER_SUB' + if block == 'Kannada': return 'BEFORE_SUB' if u < 0x0CC3 or u > 0x0CD6 else 'AFTER_SUB' + if block == 'Malayalam': return 'AFTER_POST' + return 'AFTER_SUB' +def matra_pos_top(u, block): + # BENG and MLYM don't have top matras. + if block == 'Devanagari': return 'AFTER_SUB' + if block == 'Gurmukhi': return 'AFTER_POST' # Deviate from spec + if block == 'Gujarati': return 'AFTER_SUB' + if block == 'Oriya': return 'AFTER_MAIN' + if block == 'Tamil': return 'AFTER_SUB' + if block == 'Telugu': return 'BEFORE_SUB' + if block == 'Kannada': return 'BEFORE_SUB' + return 'AFTER_SUB' +def matra_pos_bottom(u, block): + if block == 'Devanagari': return 'AFTER_SUB' + if block == 'Bengali': return 'AFTER_SUB' + if block == 'Gurmukhi': return 'AFTER_POST' + if block == 'Gujarati': return 'AFTER_POST' + if block == 'Oriya': return 'AFTER_SUB' + if block == 'Tamil': return 'AFTER_POST' + if block == 'Telugu': return 'BEFORE_SUB' + if block == 'Kannada': return 'BEFORE_SUB' + if block == 'Malayalam': return 'AFTER_POST' + return "AFTER_SUB" +def indic_matra_position(u, pos, block): # Reposition matra + if pos == 'PRE_C': return matra_pos_left(u, block) + if pos == 'POST_C': return matra_pos_right(u, block) + if pos == 'ABOVE_C': return matra_pos_top(u, block) + if pos == 'BELOW_C': return matra_pos_bottom(u, block) + assert (False) + +def position_to_category(pos): + if pos == 'PRE_C': return 'VPre' + if pos == 'ABOVE_C': return 'VAbv' + if pos == 'BELOW_C': return 'VBlw' + if pos == 'POST_C': return 'VPst' + assert(False) + + +defaults = (category_map[defaults[0]], position_map[defaults[1]], defaults[2]) + +indic_data = {} +for k, (cat, pos, block) in combined.items(): + cat = category_map[cat] + pos = position_map[pos] + indic_data[k] = (cat, pos, block) + +for k,new_cat in category_overrides.items(): + (cat, pos, _) = indic_data.get(k, defaults) + indic_data[k] = (new_cat, pos, unicode_data[2][k]) + +# We only expect position for certain types +positioned_categories = ('CM', 'SM', 'RS', 'H', 'M') +for k, (cat, pos, block) in indic_data.items(): + if cat not in positioned_categories: + pos = 'END' + indic_data[k] = (cat, pos, block) + +# Position overrides are more complicated + +# Keep in sync with CONSONANT_FLAGS in the shaper +consonant_categories = ('C', 'CS', 'Ra','CM', 'V', 'PLACEHOLDER', 'DOTTEDCIRCLE') +smvd_categories = ('SM', 'VD', 'A', 'Symbol') +for k, (cat, pos, block) in indic_data.items(): + if cat in consonant_categories: + pos = 'BASE_C' + elif cat == 'M': + if block.startswith('Khmer') or block.startswith('Myanmar'): + cat = position_to_category(pos) + else: + pos = indic_matra_position(k, pos, block) + elif cat in smvd_categories: + pos = 'SMVD'; + indic_data[k] = (cat, pos, block) + +for k,new_pos in position_overrides.items(): + (cat, pos, _) = indic_data.get(k, defaults) + indic_data[k] = (cat, new_pos, unicode_data[2][k]) + + +values = [{_: 1} for _ in defaults] +for vv in indic_data.values(): + for i,v in enumerate(vv): + values[i][v] = values[i].get (v, 0) + 1 + + + + +# Move the outliers NO-BREAK SPACE and DOTTED CIRCLE out +singles = {} +for u in ALLOWED_SINGLES: + singles[u] = indic_data[u] + del indic_data[u] + +print ("/* == Start of generated table == */") +print ("/*") +print (" * The following table is generated by running:") +print (" *") +print (" * ./gen-indic-table.py IndicSyllabicCategory.txt IndicPositionalCategory.txt Blocks.txt") +print (" *") +print (" * on files with these headers:") +print (" *") +for h in headers: + for l in h: + print (" * %s" % (l.strip())) +print (" */") +print () +print ('#include "hb.hh"') +print () +print ('#ifndef HB_NO_OT_SHAPE') +print () +print ('#include "hb-ot-shaper-indic.hh"') +print () +print ('#pragma GCC diagnostic push') +print ('#pragma GCC diagnostic ignored "-Wunused-macros"') +print () + +# Print categories +for shaper in categories: + print ('#include "hb-ot-shaper-%s-machine.hh"' % shaper) +print () +done = {} +for shaper, shaper_cats in categories.items(): + print ('/* %s */' % shaper) + for cat in shaper_cats: + v = shaper[0].upper() + if cat not in done: + print ("#define OT_%s %s_Cat(%s)" % (cat, v, cat)) + done[cat] = v + else: + print ('static_assert (OT_%s == %s_Cat(%s), "");' % (cat, v, cat)) +print () + +# Shorten values +short = [{ + "Repha": 'Rf', + "PLACEHOLDER": 'GB', + "DOTTEDCIRCLE": 'DC', + "VPst": 'VR', + "VPre": 'VL', + "Robatic": 'Rt', + "Xgroup": 'Xg', + "Ygroup": 'Yg', + "As": 'As', +},{ + "END": 'X', + "BASE_C": 'C', + "ABOVE_C": 'T', + "BELOW_C": 'B', + "POST_C": 'R', + "PRE_C": 'L', + "PRE_M": 'LM', + "AFTER_MAIN": 'A', + "AFTER_SUB": 'AS', + "BEFORE_SUB": 'BS', + "AFTER_POST": 'AP', + "SMVD": 'SM', +}] +all_shorts = [{},{}] + +# Add some of the values, to make them more readable, and to avoid duplicates + +for i in range (2): + for v,s in short[i].items (): + all_shorts[i][s] = v + +what = ["OT", "POS"] +what_short = ["_OT", "_POS"] +cat_defs = [] +for i in range (2): + vv = sorted (values[i].keys ()) + for v in vv: + v_no_and = v.replace ('_And_', '_') + if v in short[i]: + s = short[i][v] + else: + s = ''.join ([c for c in v_no_and if ord ('A') <= ord (c) <= ord ('Z')]) + if s in all_shorts[i]: + raise Exception ("Duplicate short value alias", v, all_shorts[i][s]) + all_shorts[i][s] = v + short[i][v] = s + cat_defs.append ((what_short[i] + '_' + s, what[i] + '_' + (v.upper () if i else v), str (values[i][v]), v)) + +maxlen_s = max ([len (c[0]) for c in cat_defs]) +maxlen_l = max ([len (c[1]) for c in cat_defs]) +maxlen_n = max ([len (c[2]) for c in cat_defs]) +for s in what_short: + print () + for c in [c for c in cat_defs if s in c[0]]: + print ("#define %s %s /* %s chars; %s */" % + (c[0].ljust (maxlen_s), c[1].ljust (maxlen_l), c[2].rjust (maxlen_n), c[3])) +print () +print ('#pragma GCC diagnostic pop') +print () +print ("#define INDIC_COMBINE_CATEGORIES(S,M) ((S) | ((M) << 8))") +print () +print ("#define _(S,M) INDIC_COMBINE_CATEGORIES (%s_##S, %s_##M)" % tuple(what_short)) +print () +print () + +total = 0 +used = 0 +last_block = None +def print_block (block, start, end, data): + global total, used, last_block + if block and block != last_block: + print () + print () + print (" /* %s */" % block) + num = 0 + assert start % 8 == 0 + assert (end+1) % 8 == 0 + for u in range (start, end+1): + if u % 8 == 0: + print () + print (" /* %04X */" % u, end="") + if u in data: + num += 1 + d = data.get (u, defaults) + print ("%9s" % ("_(%s,%s)," % (short[0][d[0]], short[1][d[1]])), end="") + + total += end - start + 1 + used += num + if block: + last_block = block + +uu = sorted (indic_data) + +last = -100000 +num = 0 +offset = 0 +starts = [] +ends = [] +print ("static const uint16_t indic_table[] = {") +for u in uu: + if u <= last: + continue + block = indic_data[u][2] + + start = u//8*8 + end = start+1 + while end in uu and block == indic_data[end][2]: + end += 1 + end = (end-1)//8*8 + 7 + + if start != last + 1: + if start - last <= 1+16*3: + print_block (None, last+1, start-1, indic_data) + else: + if last >= 0: + ends.append (last + 1) + offset += ends[-1] - starts[-1] + print () + print () + print ("#define indic_offset_0x%04xu %d" % (start, offset)) + starts.append (start) + + print_block (block, start, end, indic_data) + last = end +ends.append (last + 1) +offset += ends[-1] - starts[-1] +print () +print () +occupancy = used * 100. / total +page_bits = 12 +print ("}; /* Table items: %d; occupancy: %d%% */" % (offset, occupancy)) +print () +print ("uint16_t") +print ("hb_indic_get_categories (hb_codepoint_t u)") +print ("{") +print (" switch (u >> %d)" % page_bits) +print (" {") +pages = set ([u>>page_bits for u in starts+ends+list (singles.keys ())]) +for p in sorted(pages): + print (" case 0x%0Xu:" % p) + for u,d in singles.items (): + if p != u>>page_bits: continue + print (" if (unlikely (u == 0x%04Xu)) return _(%s,%s);" % (u, short[0][d[0]], short[1][d[1]])) + for (start,end) in zip (starts, ends): + if p not in [start>>page_bits, end>>page_bits]: continue + offset = "indic_offset_0x%04xu" % start + print (" if (hb_in_range (u, 0x%04Xu, 0x%04Xu)) return indic_table[u - 0x%04Xu + %s];" % (start, end-1, start, offset)) + print (" break;") + print ("") +print (" default:") +print (" break;") +print (" }") +print (" return _(X,X);") +print ("}") +print () +print ("#undef _") +print ("#undef INDIC_COMBINE_CATEGORIES") +for i in range (2): + print () + vv = sorted (values[i].keys ()) + for v in vv: + print ("#undef %s_%s" % + (what_short[i], short[i][v])) +print () +print ('#endif') +print () +print ("/* == End of generated table == */") + +# Maintain at least 30% occupancy in the table */ +if occupancy < 30: + raise Exception ("Table too sparse, please investigate: ", occupancy) diff --git a/libs/harfbuzz/src/gen-os2-unicode-ranges.py b/libs/harfbuzz/src/gen-os2-unicode-ranges.py new file mode 100644 index 00000000..b1a34d47 --- /dev/null +++ b/libs/harfbuzz/src/gen-os2-unicode-ranges.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python3 + +"""Generates the code for a sorted unicode range array as used in hb-ot-os2-unicode-ranges.hh +Input is a tab separated list of unicode ranges from the otspec +(https://docs.microsoft.com/en-us/typography/opentype/spec/os2#ur). +""" + +import re +import sys + + +print ("""static OS2Range _hb_os2_unicode_ranges[] = +{""") + +args = sys.argv[1:] +input_file = args[0] + +with open (input_file, mode="r", encoding="utf-8") as f: + + all_ranges = [] + current_bit = 0 + while True: + line = f.readline().strip() + if not line: + break + fields = re.split(r'\t+', line) + if len(fields) == 3: + current_bit = fields[0] + fields = fields[1:] + elif len(fields) > 3: + raise Exception("bad input :(.") + + name = fields[0] + ranges = re.split("-", fields[1]) + if len(ranges) != 2: + raise Exception("bad input :(.") + + v = tuple((int(ranges[0], 16), int(ranges[1], 16), int(current_bit), name)) + all_ranges.append(v) + +all_ranges = sorted(all_ranges, key=lambda t: t[0]) + +for ranges in all_ranges: + start = ("0x%X" % ranges[0]).rjust(8) + end = ("0x%X" % ranges[1]).rjust(8) + bit = ("%s" % ranges[2]).rjust(3) + + print (" {%s, %s, %s}, // %s" % (start, end, bit, ranges[3])) + +print ("""};""") diff --git a/libs/harfbuzz/src/gen-ragel-artifacts.py b/libs/harfbuzz/src/gen-ragel-artifacts.py new file mode 100644 index 00000000..8bbb375b --- /dev/null +++ b/libs/harfbuzz/src/gen-ragel-artifacts.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python3 + +"This tool is intended to be used from meson" + +import os, os.path, sys, subprocess, shutil + +ragel = sys.argv[1] +if not ragel: + sys.exit ('You have to install ragel if you are going to develop HarfBuzz itself') + +if len (sys.argv) < 4: + sys.exit (__doc__) + +OUTPUT = sys.argv[2] +CURRENT_SOURCE_DIR = sys.argv[3] +INPUT = sys.argv[4] + +outdir = os.path.dirname (OUTPUT) +shutil.copy (INPUT, outdir) +rl = os.path.basename (INPUT) +hh = rl.replace ('.rl', '.hh') +subprocess.Popen (ragel.split() + ['-e', '-F1', '-o', hh, rl], cwd=outdir).wait () + +# copy it also to src/ +shutil.copyfile (os.path.join (outdir, hh), os.path.join (CURRENT_SOURCE_DIR, hh)) diff --git a/libs/harfbuzz/src/gen-tag-table.py b/libs/harfbuzz/src/gen-tag-table.py new file mode 100644 index 00000000..7e15c08c --- /dev/null +++ b/libs/harfbuzz/src/gen-tag-table.py @@ -0,0 +1,1216 @@ +#!/usr/bin/env python3 + +"""Generator of the mapping from OpenType tags to BCP 47 tags and vice +versa. + +It creates a ``const LangTag[]``, matching the tags from the OpenType +languages system tag list to the language subtags of the BCP 47 language +subtag registry, with some manual adjustments. The mappings are +supplemented with macrolanguages' sublanguages and retired codes' +replacements, according to BCP 47 and some manual additions where BCP 47 +omits a retired code entirely. + +Also generated is a function, ``hb_ot_ambiguous_tag_to_language``, +intended for use by ``hb_ot_tag_to_language``. It maps OpenType tags +back to BCP 47 tags. Ambiguous OpenType tags (those that correspond to +multiple BCP 47 tags) are listed here, except when the alphabetically +first BCP 47 tag happens to be the chosen disambiguated tag. In that +case, the fallback behavior will choose the right tag anyway. + +usage: ./gen-tag-table.py languagetags language-subtag-registry + +Input files: +* https://docs.microsoft.com/en-us/typography/opentype/spec/languagetags +* https://www.iana.org/assignments/language-subtag-registry/language-subtag-registry +""" + +import collections +import html +from html.parser import HTMLParser +import itertools +import re +import sys +import unicodedata + +if len (sys.argv) != 3: + sys.exit (__doc__) + +def expect (condition, message=None): + if not condition: + if message is None: + raise AssertionError + raise AssertionError (message) + +def write (s): + sys.stdout.flush () + sys.stdout.buffer.write (s.encode ('utf-8')) + +DEFAULT_LANGUAGE_SYSTEM = '' + +# from https://www-01.sil.org/iso639-3/iso-639-3.tab +ISO_639_3_TO_1 = { + 'aar': 'aa', + 'abk': 'ab', + 'afr': 'af', + 'aka': 'ak', + 'amh': 'am', + 'ara': 'ar', + 'arg': 'an', + 'asm': 'as', + 'ava': 'av', + 'ave': 'ae', + 'aym': 'ay', + 'aze': 'az', + 'bak': 'ba', + 'bam': 'bm', + 'bel': 'be', + 'ben': 'bn', + 'bis': 'bi', + 'bod': 'bo', + 'bos': 'bs', + 'bre': 'br', + 'bul': 'bg', + 'cat': 'ca', + 'ces': 'cs', + 'cha': 'ch', + 'che': 'ce', + 'chu': 'cu', + 'chv': 'cv', + 'cor': 'kw', + 'cos': 'co', + 'cre': 'cr', + 'cym': 'cy', + 'dan': 'da', + 'deu': 'de', + 'div': 'dv', + 'dzo': 'dz', + 'ell': 'el', + 'eng': 'en', + 'epo': 'eo', + 'est': 'et', + 'eus': 'eu', + 'ewe': 'ee', + 'fao': 'fo', + 'fas': 'fa', + 'fij': 'fj', + 'fin': 'fi', + 'fra': 'fr', + 'fry': 'fy', + 'ful': 'ff', + 'gla': 'gd', + 'gle': 'ga', + 'glg': 'gl', + 'glv': 'gv', + 'grn': 'gn', + 'guj': 'gu', + 'hat': 'ht', + 'hau': 'ha', + 'hbs': 'sh', + 'heb': 'he', + 'her': 'hz', + 'hin': 'hi', + 'hmo': 'ho', + 'hrv': 'hr', + 'hun': 'hu', + 'hye': 'hy', + 'ibo': 'ig', + 'ido': 'io', + 'iii': 'ii', + 'iku': 'iu', + 'ile': 'ie', + 'ina': 'ia', + 'ind': 'id', + 'ipk': 'ik', + 'isl': 'is', + 'ita': 'it', + 'jav': 'jv', + 'jpn': 'ja', + 'kal': 'kl', + 'kan': 'kn', + 'kas': 'ks', + 'kat': 'ka', + 'kau': 'kr', + 'kaz': 'kk', + 'khm': 'km', + 'kik': 'ki', + 'kin': 'rw', + 'kir': 'ky', + 'kom': 'kv', + 'kon': 'kg', + 'kor': 'ko', + 'kua': 'kj', + 'kur': 'ku', + 'lao': 'lo', + 'lat': 'la', + 'lav': 'lv', + 'lim': 'li', + 'lin': 'ln', + 'lit': 'lt', + 'ltz': 'lb', + 'lub': 'lu', + 'lug': 'lg', + 'mah': 'mh', + 'mal': 'ml', + 'mar': 'mr', + 'mkd': 'mk', + 'mlg': 'mg', + 'mlt': 'mt', + 'mol': 'mo', + 'mon': 'mn', + 'mri': 'mi', + 'msa': 'ms', + 'mya': 'my', + 'nau': 'na', + 'nav': 'nv', + 'nbl': 'nr', + 'nde': 'nd', + 'ndo': 'ng', + 'nep': 'ne', + 'nld': 'nl', + 'nno': 'nn', + 'nob': 'nb', + 'nor': 'no', + 'nya': 'ny', + 'oci': 'oc', + 'oji': 'oj', + 'ori': 'or', + 'orm': 'om', + 'oss': 'os', + 'pan': 'pa', + 'pli': 'pi', + 'pol': 'pl', + 'por': 'pt', + 'pus': 'ps', + 'que': 'qu', + 'roh': 'rm', + 'ron': 'ro', + 'run': 'rn', + 'rus': 'ru', + 'sag': 'sg', + 'san': 'sa', + 'sin': 'si', + 'slk': 'sk', + 'slv': 'sl', + 'sme': 'se', + 'smo': 'sm', + 'sna': 'sn', + 'snd': 'sd', + 'som': 'so', + 'sot': 'st', + 'spa': 'es', + 'sqi': 'sq', + 'srd': 'sc', + 'srp': 'sr', + 'ssw': 'ss', + 'sun': 'su', + 'swa': 'sw', + 'swe': 'sv', + 'tah': 'ty', + 'tam': 'ta', + 'tat': 'tt', + 'tel': 'te', + 'tgk': 'tg', + 'tgl': 'tl', + 'tha': 'th', + 'tir': 'ti', + 'ton': 'to', + 'tsn': 'tn', + 'tso': 'ts', + 'tuk': 'tk', + 'tur': 'tr', + 'twi': 'tw', + 'uig': 'ug', + 'ukr': 'uk', + 'urd': 'ur', + 'uzb': 'uz', + 'ven': 've', + 'vie': 'vi', + 'vol': 'vo', + 'wln': 'wa', + 'wol': 'wo', + 'xho': 'xh', + 'yid': 'yi', + 'yor': 'yo', + 'zha': 'za', + 'zho': 'zh', + 'zul': 'zu', +} + +class LanguageTag (object): + """A BCP 47 language tag. + + Attributes: + subtags (List[str]): The list of subtags in this tag. + grandfathered (bool): Whether this tag is grandfathered. If + ``true``, the entire lowercased tag is the ``language`` + and the other subtag fields are empty. + language (str): The language subtag. + script (str): The script subtag. + region (str): The region subtag. + variant (str): The variant subtag. + + Args: + tag (str): A BCP 47 language tag. + + """ + def __init__ (self, tag): + global bcp_47 + self.subtags = tag.lower ().split ('-') + self.grandfathered = tag.lower () in bcp_47.grandfathered + if self.grandfathered: + self.language = tag.lower () + self.script = '' + self.region = '' + self.variant = '' + else: + self.language = self.subtags[0] + self.script = self._find_first (lambda s: len (s) == 4 and s[0] > '9', self.subtags) + self.region = self._find_first (lambda s: len (s) == 2 and s[0] > '9' or len (s) == 3 and s[0] <= '9', self.subtags[1:]) + self.variant = self._find_first (lambda s: len (s) > 4 or len (s) == 4 and s[0] <= '9', self.subtags) + + def __str__(self): + return '-'.join(self.subtags) + + def __repr__ (self): + return 'LanguageTag(%r)' % str(self) + + @staticmethod + def _find_first (function, sequence): + try: + return next (iter (filter (function, sequence))) + except StopIteration: + return None + + def is_complex (self): + """Return whether this tag is too complex to represent as a + ``LangTag`` in the generated code. + + Complex tags need to be handled in + ``hb_ot_tags_from_complex_language``. + + Returns: + Whether this tag is complex. + """ + return not (len (self.subtags) == 1 + or self.grandfathered + and len (self.subtags[1]) != 3 + and ot.from_bcp_47[self.subtags[0]] == ot.from_bcp_47[self.language]) + + def get_group (self): + """Return the group into which this tag should be categorized in + ``hb_ot_tags_from_complex_language``. + + The group is the first letter of the tag, or ``'und'`` if this tag + should not be matched in a ``switch`` statement in the generated + code. + + Returns: + This tag's group. + """ + return ('und' + if (self.language == 'und' + or self.variant in bcp_47.prefixes and len (bcp_47.prefixes[self.variant]) == 1) + else self.language[0]) + +class OpenTypeRegistryParser (HTMLParser): + """A parser for the OpenType language system tag registry. + + Attributes: + header (str): The "last updated" line of the registry. + names (Mapping[str, str]): A map of language system tags to the + names they are given in the registry. + ranks (DefaultDict[str, int]): A map of language system tags to + numbers. If a single BCP 47 tag corresponds to multiple + OpenType tags, the tags are ordered in increasing order by + rank. The rank is based on the number of BCP 47 tags + associated with a tag, though it may be manually modified. + to_bcp_47 (DefaultDict[str, AbstractSet[str]]): A map of + OpenType language system tags to sets of BCP 47 tags. + from_bcp_47 (DefaultDict[str, AbstractSet[str]]): ``to_bcp_47`` + inverted. Its values start as unsorted sets; + ``sort_languages`` converts them to sorted lists. + from_bcp_47_uninherited (Optional[Dict[str, AbstractSet[str]]]): + A copy of ``from_bcp_47``. It starts as ``None`` and is + populated at the beginning of the first call to + ``inherit_from_macrolanguages``. + + """ + def __init__ (self): + HTMLParser.__init__ (self) + self.header = '' + self.names = {} + self.ranks = collections.defaultdict (int) + self.to_bcp_47 = collections.defaultdict (set) + self.from_bcp_47 = collections.defaultdict (set) + self.from_bcp_47_uninherited = None + # Whether the parser is in a element + self._td = False + # Whether the parser is after a
element within the current element + self._br = False + # The text of the elements of the current element. + self._current_tr = [] + + def handle_starttag (self, tag, attrs): + if tag == 'br': + self._br = True + elif tag == 'meta': + for attr, value in attrs: + if attr == 'name' and value == 'updated_at': + self.header = self.get_starttag_text () + break + elif tag == 'td': + self._td = True + self._current_tr.append ('') + elif tag == 'tr': + self._br = False + self._current_tr = [] + + def handle_endtag (self, tag): + if tag == 'td': + self._td = False + elif tag == 'tr' and self._current_tr: + expect (2 <= len (self._current_tr) <= 3) + name = self._current_tr[0].strip () + tag = self._current_tr[1].strip ("\t\n\v\f\r '") + rank = 0 + if len (tag) > 4: + expect (tag.endswith (' (deprecated)'), 'ill-formed OpenType tag: %s' % tag) + name += ' (deprecated)' + tag = tag.split (' ')[0] + rank = 1 + self.names[tag] = re.sub (' languages$', '', name) + if not self._current_tr[2]: + return + iso_codes = self._current_tr[2].strip () + self.to_bcp_47[tag].update (ISO_639_3_TO_1.get (code, code) for code in iso_codes.replace (' ', '').split (',')) + rank += 2 * len (self.to_bcp_47[tag]) + self.ranks[tag] = rank + + def handle_data (self, data): + if self._td and not self._br: + self._current_tr[-1] += data + + def handle_charref (self, name): + self.handle_data (html.unescape ('&#%s;' % name)) + + def handle_entityref (self, name): + self.handle_data (html.unescape ('&%s;' % name)) + + def parse (self, filename): + """Parse the OpenType language system tag registry. + + Args: + filename (str): The file name of the registry. + """ + with open (filename, encoding='utf-8') as f: + self.feed (f.read ()) + expect (self.header) + for tag, iso_codes in self.to_bcp_47.items (): + for iso_code in iso_codes: + self.from_bcp_47[iso_code].add (tag) + + def add_language (self, bcp_47_tag, ot_tag): + """Add a language as if it were in the registry. + + Args: + bcp_47_tag (str): A BCP 47 tag. If the tag is more than just + a language subtag, and if the language subtag is a + macrolanguage, then new languages are added corresponding + to the macrolanguages' individual languages with the + remainder of the tag appended. + ot_tag (str): An OpenType language system tag. + """ + global bcp_47 + self.to_bcp_47[ot_tag].add (bcp_47_tag) + self.from_bcp_47[bcp_47_tag].add (ot_tag) + if bcp_47_tag.lower () not in bcp_47.grandfathered: + try: + [macrolanguage, suffix] = bcp_47_tag.split ('-', 1) + if macrolanguage in bcp_47.macrolanguages: + s = set () + for language in bcp_47.macrolanguages[macrolanguage]: + if language.lower () not in bcp_47.grandfathered: + s.add ('%s-%s' % (language, suffix)) + bcp_47.macrolanguages['%s-%s' % (macrolanguage, suffix)] = s + except ValueError: + pass + + @staticmethod + def _remove_language (tag_1, dict_1, dict_2): + for tag_2 in dict_1.pop (tag_1): + dict_2[tag_2].remove (tag_1) + if not dict_2[tag_2]: + del dict_2[tag_2] + + def remove_language_ot (self, ot_tag): + """Remove an OpenType tag from the registry. + + Args: + ot_tag (str): An OpenType tag. + """ + self._remove_language (ot_tag, self.to_bcp_47, self.from_bcp_47) + + def remove_language_bcp_47 (self, bcp_47_tag): + """Remove a BCP 47 tag from the registry. + + Args: + bcp_47_tag (str): A BCP 47 tag. + """ + self._remove_language (bcp_47_tag, self.from_bcp_47, self.to_bcp_47) + + def inherit_from_macrolanguages (self): + """Copy mappings from macrolanguages to individual languages. + + If a BCP 47 tag for an individual mapping has no OpenType + mapping but its macrolanguage does, the mapping is copied to + the individual language. For example, als (Tosk Albanian) has no + explicit mapping, so it inherits from sq (Albanian) the mapping + to SQI. + + However, if an OpenType tag maps to a BCP 47 macrolanguage and + some but not all of its individual languages, the mapping is not + inherited from the macrolanguage to the missing individual + languages. For example, INUK (Nunavik Inuktitut) is mapped to + ike (Eastern Canadian Inuktitut) and iu (Inuktitut) but not to + ikt (Inuinnaqtun, which is an individual language of iu), so + this method does not add a mapping from ikt to INUK. + + If a BCP 47 tag for a macrolanguage has no OpenType mapping but + some of its individual languages do, their mappings are copied + to the macrolanguage. + """ + global bcp_47 + first_time = self.from_bcp_47_uninherited is None + if first_time: + self.from_bcp_47_uninherited = dict (self.from_bcp_47) + for macrolanguage, languages in dict (bcp_47.macrolanguages).items (): + ot_macrolanguages = { + ot_macrolanguage for ot_macrolanguage in self.from_bcp_47_uninherited.get (macrolanguage, set ()) + } + blocked_ot_macrolanguages = set () + if 'retired code' not in bcp_47.scopes.get (macrolanguage, ''): + for ot_macrolanguage in ot_macrolanguages: + round_trip_macrolanguages = { + l for l in self.to_bcp_47[ot_macrolanguage] + if 'retired code' not in bcp_47.scopes.get (l, '') + } + round_trip_languages = { + l for l in languages + if 'retired code' not in bcp_47.scopes.get (l, '') + } + intersection = round_trip_macrolanguages & round_trip_languages + if intersection and intersection != round_trip_languages: + blocked_ot_macrolanguages.add (ot_macrolanguage) + if ot_macrolanguages: + for ot_macrolanguage in ot_macrolanguages: + if ot_macrolanguage not in blocked_ot_macrolanguages: + for language in languages: + self.add_language (language, ot_macrolanguage) + if not blocked_ot_macrolanguages: + self.ranks[ot_macrolanguage] += 1 + elif first_time: + for language in languages: + if language in self.from_bcp_47_uninherited: + ot_macrolanguages |= self.from_bcp_47_uninherited[language] + else: + ot_macrolanguages.clear () + if not ot_macrolanguages: + break + for ot_macrolanguage in ot_macrolanguages: + self.add_language (macrolanguage, ot_macrolanguage) + + def sort_languages (self): + """Sort the values of ``from_bcp_47`` in ascending rank order.""" + for language, tags in self.from_bcp_47.items (): + self.from_bcp_47[language] = sorted (tags, + key=lambda t: (self.ranks[t] + rank_delta (language, t), t)) + +ot = OpenTypeRegistryParser () + +class BCP47Parser (object): + """A parser for the BCP 47 subtag registry. + + Attributes: + header (str): The "File-Date" line of the registry. + names (Mapping[str, str]): A map of subtags to the names they + are given in the registry. Each value is a + ``'\\n'``-separated list of names. + scopes (Mapping[str, str]): A map of language subtags to strings + suffixed to language names, including suffixes to explain + language scopes. + macrolanguages (DefaultDict[str, AbstractSet[str]]): A map of + language subtags to the sets of language subtags which + inherit from them. See + ``OpenTypeRegistryParser.inherit_from_macrolanguages``. + prefixes (DefaultDict[str, AbstractSet[str]]): A map of variant + subtags to their prefixes. + grandfathered (AbstractSet[str]): The set of grandfathered tags, + normalized to lowercase. + + """ + def __init__ (self): + self.header = '' + self.names = {} + self.scopes = {} + self.macrolanguages = collections.defaultdict (set) + self.prefixes = collections.defaultdict (set) + self.grandfathered = set () + + def parse (self, filename): + """Parse the BCP 47 subtag registry. + + Args: + filename (str): The file name of the registry. + """ + with open (filename, encoding='utf-8') as f: + subtag_type = None + subtag = None + deprecated = False + has_preferred_value = False + line_buffer = '' + for line in itertools.chain (f, ['']): + line = line.rstrip () + if line.startswith (' '): + line_buffer += line[1:] + continue + line, line_buffer = line_buffer, line + if line.startswith ('Type: '): + subtag_type = line.split (' ')[1] + deprecated = False + has_preferred_value = False + elif line.startswith ('Subtag: ') or line.startswith ('Tag: '): + subtag = line.split (' ')[1] + if subtag_type == 'grandfathered': + self.grandfathered.add (subtag.lower ()) + elif line.startswith ('Description: '): + description = line.split (' ', 1)[1].replace (' (individual language)', '') + description = re.sub (' (\(family\)|\((individual |macro)language\)|languages)$', '', + description) + if subtag in self.names: + self.names[subtag] += '\n' + description + else: + self.names[subtag] = description + elif subtag_type == 'language' or subtag_type == 'grandfathered': + if line.startswith ('Scope: '): + scope = line.split (' ')[1] + if scope == 'macrolanguage': + scope = ' [macrolanguage]' + elif scope == 'collection': + scope = ' [collection]' + else: + continue + self.scopes[subtag] = scope + elif line.startswith ('Deprecated: '): + self.scopes[subtag] = ' (retired code)' + self.scopes.get (subtag, '') + deprecated = True + elif deprecated and line.startswith ('Comments: see '): + # If a subtag is split into multiple replacement subtags, + # it essentially represents a macrolanguage. + for language in line.replace (',', '').split (' ')[2:]: + self._add_macrolanguage (subtag, language) + elif line.startswith ('Preferred-Value: '): + # If a subtag is deprecated in favor of a single replacement subtag, + # it is either a dialect or synonym of the preferred subtag. Either + # way, it is close enough to the truth to consider the replacement + # the macrolanguage of the deprecated language. + has_preferred_value = True + macrolanguage = line.split (' ')[1] + self._add_macrolanguage (macrolanguage, subtag) + elif not has_preferred_value and line.startswith ('Macrolanguage: '): + self._add_macrolanguage (line.split (' ')[1], subtag) + elif subtag_type == 'variant': + if line.startswith ('Deprecated: '): + self.scopes[subtag] = ' (retired code)' + self.scopes.get (subtag, '') + elif line.startswith ('Prefix: '): + self.prefixes[subtag].add (line.split (' ')[1]) + elif line.startswith ('File-Date: '): + self.header = line + expect (self.header) + + def _add_macrolanguage (self, macrolanguage, language): + global ot + if language not in ot.from_bcp_47: + for l in self.macrolanguages.get (language, set ()): + self._add_macrolanguage (macrolanguage, l) + if macrolanguage not in ot.from_bcp_47: + for ls in list (self.macrolanguages.values ()): + if macrolanguage in ls: + ls.add (language) + return + self.macrolanguages[macrolanguage].add (language) + + def remove_extra_macrolanguages (self): + """Make every language have at most one macrolanguage.""" + inverted = collections.defaultdict (list) + for macrolanguage, languages in self.macrolanguages.items (): + for language in languages: + inverted[language].append (macrolanguage) + for language, macrolanguages in inverted.items (): + if len (macrolanguages) > 1: + macrolanguages.sort (key=lambda ml: len (self.macrolanguages[ml])) + biggest_macrolanguage = macrolanguages.pop () + for macrolanguage in macrolanguages: + self._add_macrolanguage (biggest_macrolanguage, macrolanguage) + + def _get_name_piece (self, subtag): + """Return the first name of a subtag plus its scope suffix. + + Args: + subtag (str): A BCP 47 subtag. + + Returns: + The name form of ``subtag``. + """ + return self.names[subtag].split ('\n')[0] + self.scopes.get (subtag, '') + + def get_name (self, lt): + """Return the names of the subtags in a language tag. + + Args: + lt (LanguageTag): A BCP 47 language tag. + + Returns: + The name form of ``lt``. + """ + name = self._get_name_piece (lt.language) + if lt.script: + name += '; ' + self._get_name_piece (lt.script.title ()) + if lt.region: + name += '; ' + self._get_name_piece (lt.region.upper ()) + if lt.variant: + name += '; ' + self._get_name_piece (lt.variant) + return name + +bcp_47 = BCP47Parser () + +ot.parse (sys.argv[1]) +bcp_47.parse (sys.argv[2]) + +ot.add_language ('ary', 'MOR') + +ot.add_language ('ath', 'ATH') + +ot.add_language ('bai', 'BML') + +ot.ranks['BAL'] = ot.ranks['KAR'] + 1 + +ot.add_language ('ber', 'BBR') + +ot.remove_language_ot ('PGR') +ot.add_language ('el-polyton', 'PGR') + +bcp_47.macrolanguages['et'] = {'ekk'} + +bcp_47.names['flm'] = 'Falam Chin' +bcp_47.scopes['flm'] = ' (retired code)' +bcp_47.macrolanguages['flm'] = {'cfm'} + +ot.ranks['FNE'] = ot.ranks['TNE'] + 1 + +ot.add_language ('und-fonipa', 'IPPH') + +ot.add_language ('und-fonnapa', 'APPH') + +ot.remove_language_ot ('IRT') +ot.add_language ('ga-Latg', 'IRT') + +ot.add_language ('hy-arevmda', 'HYE') + +ot.remove_language_ot ('KGE') +ot.add_language ('und-Geok', 'KGE') + +bcp_47.macrolanguages['id'] = {'in'} + +bcp_47.macrolanguages['ijo'] = {'ijc'} + +ot.add_language ('kht', 'KHN') +ot.names['KHN'] = ot.names['KHT'] + ' (Microsoft fonts)' +ot.ranks['KHN'] = ot.ranks['KHT'] + 1 + +ot.ranks['LCR'] = ot.ranks['MCR'] + 1 + +ot.names['MAL'] = 'Malayalam Traditional' +ot.ranks['MLR'] += 1 + +bcp_47.names['mhv'] = 'Arakanese' +bcp_47.scopes['mhv'] = ' (retired code)' + +ot.add_language ('mnw-TH', 'MONT') + +ot.add_language ('no', 'NOR') + +ot.add_language ('oc-provenc', 'PRO') + +ot.remove_language_ot ('QUZ') +ot.add_language ('qu', 'QUZ') +ot.add_language ('qub', 'QWH') +ot.add_language ('qud', 'QVI') +ot.add_language ('qug', 'QVI') +ot.add_language ('qul', 'QUH') +ot.add_language ('qup', 'QVI') +ot.add_language ('qur', 'QWH') +ot.add_language ('qus', 'QUH') +ot.add_language ('quw', 'QVI') +ot.add_language ('qux', 'QWH') +ot.add_language ('qva', 'QWH') +ot.add_language ('qvh', 'QWH') +ot.add_language ('qvj', 'QVI') +ot.add_language ('qvl', 'QWH') +ot.add_language ('qvm', 'QWH') +ot.add_language ('qvn', 'QWH') +ot.add_language ('qvo', 'QVI') +ot.add_language ('qvp', 'QWH') +ot.add_language ('qvw', 'QWH') +ot.add_language ('qvz', 'QVI') +ot.add_language ('qwa', 'QWH') +ot.add_language ('qws', 'QWH') +ot.add_language ('qxa', 'QWH') +ot.add_language ('qxc', 'QWH') +ot.add_language ('qxh', 'QWH') +ot.add_language ('qxl', 'QVI') +ot.add_language ('qxn', 'QWH') +ot.add_language ('qxo', 'QWH') +ot.add_language ('qxr', 'QVI') +ot.add_language ('qxt', 'QWH') +ot.add_language ('qxw', 'QWH') + +bcp_47.macrolanguages['ro-MD'].add ('mo') + +ot.remove_language_ot ('SYRE') +ot.remove_language_ot ('SYRJ') +ot.remove_language_ot ('SYRN') +ot.add_language ('und-Syre', 'SYRE') +ot.add_language ('und-Syrj', 'SYRJ') +ot.add_language ('und-Syrn', 'SYRN') + +bcp_47.names['xst'] = "Silt'e" +bcp_47.scopes['xst'] = ' (retired code)' +bcp_47.macrolanguages['xst'] = {'stv', 'wle'} + +ot.add_language ('xwo', 'TOD') + +ot.remove_language_ot ('ZHH') +ot.remove_language_ot ('ZHP') +ot.remove_language_ot ('ZHT') +ot.remove_language_ot ('ZHTM') +bcp_47.macrolanguages['zh'].remove ('lzh') +bcp_47.macrolanguages['zh'].remove ('yue') +ot.add_language ('zh-Hant-MO', 'ZHH') +ot.add_language ('zh-Hant-MO', 'ZHTM') +ot.add_language ('zh-Hant-HK', 'ZHH') +ot.add_language ('zh-Hans', 'ZHS') +ot.add_language ('zh-Hant', 'ZHT') +ot.add_language ('zh-HK', 'ZHH') +ot.add_language ('zh-MO', 'ZHH') +ot.add_language ('zh-MO', 'ZHTM') +ot.add_language ('zh-TW', 'ZHT') +ot.add_language ('lzh', 'ZHT') +ot.add_language ('lzh-Hans', 'ZHS') +ot.add_language ('yue', 'ZHH') +ot.add_language ('yue-Hans', 'ZHS') + +bcp_47.macrolanguages['zom'] = {'yos'} + +def rank_delta (bcp_47, ot): + """Return a delta to apply to a BCP 47 tag's rank. + + Most OpenType tags have a constant rank, but a few have ranks that + depend on the BCP 47 tag. + + Args: + bcp_47 (str): A BCP 47 tag. + ot (str): An OpenType tag to. + + Returns: + A number to add to ``ot``'s rank when sorting ``bcp_47``'s + OpenType equivalents. + """ + if bcp_47 == 'ak' and ot == 'AKA': + return -1 + if bcp_47 == 'tw' and ot == 'TWI': + return -1 + return 0 + +disambiguation = { + 'ALT': 'alt', + 'ARK': 'rki', + 'ATH': 'ath', + 'BHI': 'bhb', + 'BLN': 'bjt', + 'BTI': 'beb', + 'CCHN': 'cco', + 'CMR': 'swb', + 'CPP': 'crp', + 'CRR': 'crx', + 'DUJ': 'dwu', + 'ECR': 'crj', + 'HAL': 'cfm', + 'HND': 'hnd', + 'HYE': 'hyw', + 'KIS': 'kqs', + 'KUI': 'uki', + 'LRC': 'bqi', + 'NDB': 'nd', + 'NIS': 'njz', + 'PLG': 'pce', + 'PRO': 'pro', + 'QIN': 'bgr', + 'QUH': 'quh', + 'QVI': 'qvi', + 'QWH': 'qwh', + 'SIG': 'stv', + 'SRB': 'sr', + 'SXT': 'xnj', + 'ZHH': 'zh-HK', + 'ZHS': 'zh-Hans', + 'ZHT': 'zh-Hant', + 'ZHTM': 'zh-MO', +} + +ot.inherit_from_macrolanguages () +bcp_47.remove_extra_macrolanguages () +ot.inherit_from_macrolanguages () +ot.names[DEFAULT_LANGUAGE_SYSTEM] = '*/' +ot.ranks[DEFAULT_LANGUAGE_SYSTEM] = max (ot.ranks.values ()) + 1 +for tricky_ot_tag in filter (lambda tag: re.match ('[A-Z]{3}$', tag), ot.names): + possible_bcp_47_tag = tricky_ot_tag.lower () + if possible_bcp_47_tag in bcp_47.names and not ot.from_bcp_47[possible_bcp_47_tag]: + ot.add_language (possible_bcp_47_tag, DEFAULT_LANGUAGE_SYSTEM) + bcp_47.macrolanguages[possible_bcp_47_tag] = set () +ot.sort_languages () + +print ('/* == Start of generated table == */') +print ('/*') +print (' * The following table is generated by running:') +print (' *') +print (' * %s languagetags language-subtag-registry' % sys.argv[0]) +print (' *') +print (' * on files with these headers:') +print (' *') +print (' * %s' % ot.header.strip ()) +print (' * %s' % bcp_47.header) +print (' */') +print () +print ('#ifndef HB_OT_TAG_TABLE_HH') +print ('#define HB_OT_TAG_TABLE_HH') +print () + +def hb_tag (tag): + """Convert a tag to ``HB_TAG`` form. + + Args: + tag (str): An OpenType tag. + + Returns: + A snippet of C++ representing ``tag``. + """ + if tag == DEFAULT_LANGUAGE_SYSTEM: + return 'HB_TAG_NONE\t ' + return "HB_TAG('%s','%s','%s','%s')" % tuple (('%-4s' % tag)[:4]) + +def get_variant_set (name): + """Return a set of variant language names from a name. + + Args: + name (str): A list of language names from the BCP 47 registry, + joined on ``'\\n'``. + + Returns: + A set of normalized language names. + """ + return set (unicodedata.normalize ('NFD', n.replace ('\u2019', "'")) + .encode ('ASCII', 'ignore') + .strip () + for n in re.split ('[\n(),]', name) if n) + +def language_name_intersection (a, b): + """Return the names in common between two language names. + + Args: + a (str): A list of language names from the BCP 47 registry, + joined on ``'\\n'``. + b (str): A list of language names from the BCP 47 registry, + joined on ``'\\n'``. + + Returns: + The normalized language names shared by ``a`` and ``b``. + """ + return get_variant_set (a).intersection (get_variant_set (b)) + +def get_matching_language_name (intersection, candidates): + return next (iter (c for c in candidates if not intersection.isdisjoint (get_variant_set (c)))) + +def same_tag (bcp_47_tag, ot_tags): + return len (bcp_47_tag) == 3 and len (ot_tags) == 1 and bcp_47_tag == ot_tags[0].lower () + +for language_len in (2, 3): + if language_len == 3: + print ('#ifndef HB_NO_LANGUAGE_LONG') + print ('static const LangTag ot_languages%d[] = {' % language_len) + for language, tags in sorted (ot.from_bcp_47.items ()): + if language == '' or '-' in language: + continue + if len(language) != language_len: continue + commented_out = same_tag (language, tags) + for i, tag in enumerate (tags, start=1): + print ('%s{%s,\t%s},' % ('/*' if commented_out else ' ', hb_tag (language), hb_tag (tag)), end='') + if commented_out: + print ('*/', end='') + print ('\t/* ', end='') + bcp_47_name = bcp_47.names.get (language, '') + bcp_47_name_candidates = bcp_47_name.split ('\n') + ot_name = ot.names[tag] + scope = bcp_47.scopes.get (language, '') + if tag == DEFAULT_LANGUAGE_SYSTEM: + write (f'{bcp_47_name_candidates[0]}{scope} != {ot.names[language.upper ()]}') + else: + intersection = language_name_intersection (bcp_47_name, ot_name) + if not intersection: + write ('%s%s -> %s' % (bcp_47_name_candidates[0], scope, ot_name)) + else: + name = get_matching_language_name (intersection, bcp_47_name_candidates) + bcp_47.names[language] = name + write ('%s%s' % (name if len (name) > len (ot_name) else ot_name, scope)) + print (' */') + print ('};') + if language_len == 3: + print ('#endif') + print () + +print ('/**') +print (' * hb_ot_tags_from_complex_language:') +print (' * @lang_str: a BCP 47 language tag to convert.') +print (' * @limit: a pointer to the end of the substring of @lang_str to consider for') +print (' * conversion.') +print (' * @count: maximum number of language tags to retrieve (IN) and actual number of') +print (' * language tags retrieved (OUT). If no tags are retrieved, it is not modified.') +print (' * @tags: array of size at least @language_count to store the language tag') +print (' * results') +print (' *') +print (' * Converts a multi-subtag BCP 47 language tag to language tags.') +print (' *') +print (' * Return value: Whether any language systems were retrieved.') +print (' **/') +print ('static inline bool') +print ('hb_ot_tags_from_complex_language (const char *lang_str,') +print ('\t\t\t\t const char *limit,') +print ('\t\t\t\t unsigned int *count /* IN/OUT */,') +print ('\t\t\t\t hb_tag_t *tags /* OUT */)') +print ('{') + +def print_subtag_matches (subtag, string, new_line): + if subtag: + if new_line: + print () + print ('\t&& ', end='') + print ('subtag_matches (%s, limit, "-%s", %i)' % (string, subtag, 1 + len (subtag)), end='') + +complex_tags = collections.defaultdict (list) +for initial, group in itertools.groupby ((lt_tags for lt_tags in [ + (LanguageTag (language), tags) + for language, tags in sorted (ot.from_bcp_47.items (), + key=lambda i: (-len (i[0]), i[0])) + ] if lt_tags[0].is_complex ()), + key=lambda lt_tags: lt_tags[0].get_group ()): + complex_tags[initial] += group + +# Calculate the min length of the subtags outside the switch +min_subtag_len = 100 +for initial, items in sorted (complex_tags.items ()): + if initial != 'und': + continue + for lt, tags in items: + if not tags: + continue + subtag_len = 0 + subtag_len += 1 + len (lt.script) if lt.script is not None else 0 + subtag_len += 1 + len (lt.region) if lt.region is not None else 0 + subtag_len += 1 + len (lt.variant) if lt.variant is not None else 0 + min_subtag_len = min(subtag_len, min_subtag_len) + +print (' if (limit - lang_str >= %d)' % (min_subtag_len + 2)) +print (' {') +print (" const char *p = strchr (lang_str, '-');") +print (" if (!p || p >= limit || limit - p < %i) goto out;" % min_subtag_len) +for initial, items in sorted (complex_tags.items ()): + if initial != 'und': + continue + for lt, tags in items: + if not tags: + continue + if lt.variant in bcp_47.prefixes: + expect (next (iter (bcp_47.prefixes[lt.variant])) == lt.language, + '%s is not a valid prefix of %s' % (lt.language, lt.variant)) + print (' if (', end='') + print_subtag_matches (lt.script, 'p', False) + print_subtag_matches (lt.region, 'p', False) + print_subtag_matches (lt.variant, 'p', False) + print (')') + print (' {') + write (' /* %s */' % bcp_47.get_name (lt)) + print () + if len (tags) == 1: + write (' tags[0] = %s; /* %s */' % (hb_tag (tags[0]), ot.names[tags[0]])) + print () + print (' *count = 1;') + else: + print (' hb_tag_t possible_tags[] = {') + for tag in tags: + write (' %s, /* %s */' % (hb_tag (tag), ot.names[tag])) + print () + print (' };') + print (' for (i = 0; i < %s && i < *count; i++)' % len (tags)) + print ('\ttags[i] = possible_tags[i];') + print (' *count = i;') + print (' return true;') + print (' }') +print (' }') +print ('out:') + +print (' switch (lang_str[0])') +print (' {') +for initial, items in sorted (complex_tags.items ()): + if initial == 'und': + continue + print (" case '%s':" % initial) + for lt, tags in items: + if not tags: + continue + print (' if (', end='') + script = lt.script + region = lt.region + if lt.grandfathered: + print ('0 == strcmp (&lang_str[1], "%s")' % lt.language[1:], end='') + else: + string_literal = lt.language[1:] + '-' + if script: + string_literal += script + script = None + if region: + string_literal += '-' + region + region = None + if string_literal[-1] == '-': + print ('0 == strncmp (&lang_str[1], "%s", %i)' % (string_literal, len (string_literal)), end='') + else: + print ('lang_matches (&lang_str[1], limit, "%s", %i)' % (string_literal, len (string_literal)), end='') + print_subtag_matches (script, 'lang_str', True) + print_subtag_matches (region, 'lang_str', True) + print_subtag_matches (lt.variant, 'lang_str', True) + print (')') + print (' {') + write (' /* %s */' % bcp_47.get_name (lt)) + print () + if len (tags) == 1: + write (' tags[0] = %s; /* %s */' % (hb_tag (tags[0]), ot.names[tags[0]])) + print () + print (' *count = 1;') + else: + print (' unsigned int i;') + print (' hb_tag_t possible_tags[] = {') + for tag in tags: + write ('\t%s, /* %s */' % (hb_tag (tag), ot.names[tag])) + print () + print (' };') + print (' for (i = 0; i < %s && i < *count; i++)' % len (tags)) + print ('\ttags[i] = possible_tags[i];') + print (' *count = i;') + print (' return true;') + print (' }') + print (' break;') + +print (' }') +print (' return false;') +print ('}') +print () +print ('/**') +print (' * hb_ot_ambiguous_tag_to_language') +print (' * @tag: A language tag.') +print (' *') +print (' * Converts @tag to a BCP 47 language tag if it is ambiguous (it corresponds to') +print (' * many language tags) and the best tag is not the alphabetically first, or if') +print (' * the best tag consists of multiple subtags, or if the best tag does not appear') +print (' * in #ot_languages.') +print (' *') +print (' * Return value: The #hb_language_t corresponding to the BCP 47 language tag,') +print (' * or #HB_LANGUAGE_INVALID if @tag is not ambiguous.') +print (' **/') +print ('static inline hb_language_t') +print ('hb_ot_ambiguous_tag_to_language (hb_tag_t tag)') +print ('{') +print (' switch (tag)') +print (' {') + +def verify_disambiguation_dict (): + """Verify and normalize ``disambiguation``. + + ``disambiguation`` is a map of ambiguous OpenType language system + tags to the particular BCP 47 tags they correspond to. This function + checks that all its keys really are ambiguous and that each key's + value is valid for that key. It checks that no ambiguous tag is + missing, except when it can figure out which BCP 47 tag is the best + by itself. + + It modifies ``disambiguation`` to remove keys whose values are the + same as those that the fallback would return anyway, and to add + ambiguous keys whose disambiguations it determined automatically. + + Raises: + AssertionError: Verification failed. + """ + global bcp_47 + global disambiguation + global ot + for ot_tag, bcp_47_tags in ot.to_bcp_47.items (): + if ot_tag == DEFAULT_LANGUAGE_SYSTEM: + primary_tags = [] + else: + primary_tags = list (t for t in bcp_47_tags if t not in bcp_47.grandfathered and ot.from_bcp_47.get (t)[0] == ot_tag) + if len (primary_tags) == 1: + expect (ot_tag not in disambiguation, 'unnecessary disambiguation for OT tag: %s' % ot_tag) + if '-' in primary_tags[0]: + disambiguation[ot_tag] = primary_tags[0] + else: + first_tag = sorted (t for t in bcp_47_tags if t not in bcp_47.grandfathered and ot_tag in ot.from_bcp_47.get (t))[0] + if primary_tags[0] != first_tag: + disambiguation[ot_tag] = primary_tags[0] + elif len (primary_tags) == 0: + expect (ot_tag not in disambiguation, 'There is no possible valid disambiguation for %s' % ot_tag) + else: + original_languages = [t for t in primary_tags if t in ot.from_bcp_47_uninherited and 'retired code' not in bcp_47.scopes.get (t, '')] + if len (original_languages) == 1: + macrolanguages = original_languages + else: + macrolanguages = [t for t in primary_tags if bcp_47.scopes.get (t) == ' [macrolanguage]'] + if len (macrolanguages) != 1: + macrolanguages = list (t for t in primary_tags if bcp_47.scopes.get (t) == ' [collection]') + if len (macrolanguages) != 1: + macrolanguages = list (t for t in primary_tags if 'retired code' not in bcp_47.scopes.get (t, '')) + if len (macrolanguages) != 1: + expect (ot_tag in disambiguation, 'ambiguous OT tag: %s %s' % (ot_tag, str (macrolanguages))) + expect (disambiguation[ot_tag] in bcp_47_tags, + '%s is not a valid disambiguation for %s' % (disambiguation[ot_tag], ot_tag)) + elif ot_tag not in disambiguation: + disambiguation[ot_tag] = macrolanguages[0] + different_bcp_47_tags = sorted (t for t in bcp_47_tags if not same_tag (t, ot.from_bcp_47.get (t))) + if different_bcp_47_tags and disambiguation[ot_tag] == different_bcp_47_tags[0] and '-' not in disambiguation[ot_tag]: + del disambiguation[ot_tag] + for ot_tag in disambiguation.keys (): + expect (ot_tag in ot.to_bcp_47, 'unknown OT tag: %s' % ot_tag) + +verify_disambiguation_dict () +for ot_tag, bcp_47_tag in sorted (disambiguation.items ()): + write (' case %s: /* %s */' % (hb_tag (ot_tag), ot.names[ot_tag])) + print () + write (' return hb_language_from_string (\"%s\", -1); /* %s */' % (bcp_47_tag, bcp_47.get_name (LanguageTag (bcp_47_tag)))) + print () + +print (' default:') +print (' return HB_LANGUAGE_INVALID;') +print (' }') +print ('}') + +print () +print ('#endif /* HB_OT_TAG_TABLE_HH */') +print () +print ('/* == End of generated table == */') + diff --git a/libs/harfbuzz/src/gen-ucd-table.py b/libs/harfbuzz/src/gen-ucd-table.py new file mode 100644 index 00000000..f4c099f8 --- /dev/null +++ b/libs/harfbuzz/src/gen-ucd-table.py @@ -0,0 +1,167 @@ +#!/usr/bin/env python3 + +"""usage: ./gen-ucd-table ucd.nounihan.grouped.xml [/path/to/hb-common.h] + +Input file: +* https://unicode.org/Public/UCD/latest/ucdxml/ucd.nounihan.grouped.zip +""" + +import sys, re +import logging +logging.basicConfig(format='%(levelname)s: %(message)s', level=logging.INFO) + +if len (sys.argv) not in (2, 3): + sys.exit (__doc__) + +# https://github.com/harfbuzz/packtab +import packTab +import packTab.ucdxml + +logging.info('Loading UCDXML...') +ucdxml = packTab.ucdxml.load_ucdxml(sys.argv[1]) +ucd = packTab.ucdxml.ucdxml_get_repertoire(ucdxml) + +hb_common_h = 'hb-common.h' if len (sys.argv) < 3 else sys.argv[2] + +logging.info('Preparing data tables...') + +gc = [u['gc'] for u in ucd] +ccc = [int(u['ccc']) for u in ucd] +bmg = [int(v, 16) - int(u) if v else 0 for u,v in enumerate(u['bmg'] for u in ucd)] +#gc_ccc_non0 = set((cat,klass) for cat,klass in zip(gc,ccc) if klass) +#gc_bmg_non0 = set((cat,mirr) for cat,mirr in zip(gc, bmg) if mirr) + +sc = [u['sc'] for u in ucd] + +dm = {i:tuple(int(v, 16) for v in u['dm'].split()) for i,u in enumerate(ucd) + if u['dm'] != '#' and u['dt'] == 'can' and not (0xAC00 <= i < 0xAC00+11172)} +ce = {i for i,u in enumerate(ucd) if u['Comp_Ex'] == 'Y'} + +assert not any(v for v in dm.values() if len(v) not in (1,2)) +dm1 = sorted(set(v for v in dm.values() if len(v) == 1)) +assert all((v[0] >> 16) in (0,2) for v in dm1) +dm1_p0_array = ['0x%04Xu' % (v[0] & 0xFFFF) for v in dm1 if (v[0] >> 16) == 0] +dm1_p2_array = ['0x%04Xu' % (v[0] & 0xFFFF) for v in dm1 if (v[0] >> 16) == 2] +dm1_order = {v:i+1 for i,v in enumerate(dm1)} + +dm2 = sorted((v+(i if i not in ce and not ccc[i] else 0,), v) + for i,v in dm.items() if len(v) == 2) + +filt = lambda v: ((v[0] & 0xFFFFF800) == 0x0000 and + (v[1] & 0xFFFFFF80) == 0x0300 and + (v[2] & 0xFFF0C000) == 0x0000) +dm2_u32_array = [v for v in dm2 if filt(v[0])] +dm2_u64_array = [v for v in dm2 if not filt(v[0])] +assert dm2_u32_array + dm2_u64_array == dm2 +dm2_u32_array = ["HB_CODEPOINT_ENCODE3_11_7_14 (0x%04Xu, 0x%04Xu, 0x%04Xu)" % v[0] for v in dm2_u32_array] +dm2_u64_array = ["HB_CODEPOINT_ENCODE3 (0x%04Xu, 0x%04Xu, 0x%04Xu)" % v[0] for v in dm2_u64_array] + +l = 1 + len(dm1_p0_array) + len(dm1_p2_array) +dm2_order = {v[1]:i+l for i,v in enumerate(dm2)} + +dm_order = {None: 0} +dm_order.update(dm1_order) +dm_order.update(dm2_order) + +gc_order = dict() +for i,v in enumerate(('Cc', 'Cf', 'Cn', 'Co', 'Cs', 'Ll', 'Lm', 'Lo', 'Lt', 'Lu', + 'Mc', 'Me', 'Mn', 'Nd', 'Nl', 'No', 'Pc', 'Pd', 'Pe', 'Pf', + 'Pi', 'Po', 'Ps', 'Sc', 'Sk', 'Sm', 'So', 'Zl', 'Zp', 'Zs',)): + gc_order[i] = v + gc_order[v] = i + +sc_order = dict() +sc_array = [] +sc_re = re.compile(r"\b(HB_SCRIPT_[_A-Z]*).*HB_TAG [(]'(.)','(.)','(.)','(.)'[)]") +for line in open(hb_common_h): + m = sc_re.search (line) + if not m: continue + name = m.group(1) + tag = ''.join(m.group(i) for i in range(2, 6)) + i = len(sc_array) + sc_order[tag] = i + sc_order[i] = tag + sc_array.append(name) + +DEFAULT = 3 +COMPACT = 5 +SLOPPY = 9 + + +logging.info('Generating output...') +print("/* == Start of generated table == */") +print("/*") +print(" * The following table is generated by running:") +print(" *") +print(" * ./gen-ucd-table.py ucd.nounihan.grouped.xml") +print(" *") +print(" * on file with this description:", ucdxml.description) +print(" */") +print() +print("#ifndef HB_UCD_TABLE_HH") +print("#define HB_UCD_TABLE_HH") +print() +print('#include "hb.hh"') +print() + +code = packTab.Code('_hb_ucd') +sc_array, _ = code.addArray('hb_script_t', 'sc_map', sc_array) +dm1_p0_array, _ = code.addArray('uint16_t', 'dm1_p0_map', dm1_p0_array) +dm1_p2_array, _ = code.addArray('uint16_t', 'dm1_p2_map', dm1_p2_array) +dm2_u32_array, _ = code.addArray('uint32_t', 'dm2_u32_map', dm2_u32_array) +dm2_u64_array, _ = code.addArray('uint64_t', 'dm2_u64_map', dm2_u64_array) +code.print_c(linkage='static inline') + +datasets = [ + ('gc', gc, 'Cn', gc_order), + ('ccc', ccc, 0, None), + ('bmg', bmg, 0, None), + ('sc', sc, 'Zzzz', sc_order), + ('dm', dm, None, dm_order), +] + +for compression in (DEFAULT, COMPACT, SLOPPY): + logging.info(' Compression=%d:' % compression) + print() + if compression == DEFAULT: + print('#ifndef HB_OPTIMIZE_SIZE') + elif compression == COMPACT: + print('#elif !defined(HB_NO_UCD_UNASSIGNED)') + else: + print('#else') + print() + + if compression == SLOPPY: + for i in range(len(gc)): + if (i % 128) and gc[i] == 'Cn': + gc[i] = gc[i - 1] + for i in range(len(gc) - 2, -1, -1): + if ((i + 1) % 128) and gc[i] == 'Cn': + gc[i] = gc[i + 1] + for i in range(len(sc)): + if (i % 128) and sc[i] == 'Zzzz': + sc[i] = sc[i - 1] + for i in range(len(sc) - 2, -1, -1): + if ((i + 1) % 128) and sc[i] == 'Zzzz': + sc[i] = sc[i + 1] + + + code = packTab.Code('_hb_ucd') + + for name,data,default,mapping in datasets: + sol = packTab.pack_table(data, default, mapping=mapping, compression=compression) + logging.info(' Dataset=%-8s FullCost=%d' % (name, sol.fullCost)) + sol.genCode(code, name) + + code.print_c(linkage='static inline') + + print() + +print('#endif') +print() + +print() +print("#endif /* HB_UCD_TABLE_HH */") +print() +print("/* == End of generated table == */") +logging.info('Done.') diff --git a/libs/harfbuzz/src/gen-use-table.py b/libs/harfbuzz/src/gen-use-table.py new file mode 100644 index 00000000..0600a338 --- /dev/null +++ b/libs/harfbuzz/src/gen-use-table.py @@ -0,0 +1,485 @@ +#!/usr/bin/env python3 +# flake8: noqa: F821 + +"""usage: ./gen-use-table.py IndicSyllabicCategory.txt IndicPositionalCategory.txt ArabicShaping.txt DerivedCoreProperties.txt UnicodeData.txt Blocks.txt Scripts.txt IndicSyllabicCategory-Additional.txt IndicPositionalCategory-Additional.txt + +Input files: +* https://unicode.org/Public/UCD/latest/ucd/IndicSyllabicCategory.txt +* https://unicode.org/Public/UCD/latest/ucd/IndicPositionalCategory.txt +* https://unicode.org/Public/UCD/latest/ucd/ArabicShaping.txt +* https://unicode.org/Public/UCD/latest/ucd/DerivedCoreProperties.txt +* https://unicode.org/Public/UCD/latest/ucd/UnicodeData.txt +* https://unicode.org/Public/UCD/latest/ucd/Blocks.txt +* https://unicode.org/Public/UCD/latest/ucd/Scripts.txt +* ms-use/IndicSyllabicCategory-Additional.txt +* ms-use/IndicPositionalCategory-Additional.txt +""" + +import sys + +if len (sys.argv) != 10: + sys.exit (__doc__) + +DISABLED_SCRIPTS = { + 'Arabic', + 'Lao', + 'Samaritan', + 'Syriac', + 'Thai', +} + +files = [open (x, encoding='utf-8') for x in sys.argv[1:]] + +headers = [[f.readline () for i in range (2)] for j,f in enumerate(files) if j != 4] +for j in range(7, 9): + for line in files[j]: + line = line.rstrip() + if not line: + break + headers[j - 1].append(line) +headers.append (["UnicodeData.txt does not have a header."]) + +unicode_data = [{} for _ in files] +values = [{} for _ in files] +for i, f in enumerate (files): + for line in f: + + j = line.find ('#') + if j >= 0: + line = line[:j] + + fields = [x.strip () for x in line.split (';')] + if len (fields) == 1: + continue + + uu = fields[0].split ('..') + start = int (uu[0], 16) + if len (uu) == 1: + end = start + else: + end = int (uu[1], 16) + + t = fields[1 if i not in [2, 4] else 2] + + if i == 2: + t = 'jt_' + t + elif i == 3 and t != 'Default_Ignorable_Code_Point': + continue + elif i == 7 and t == 'Consonant_Final_Modifier': + # TODO: https://github.com/MicrosoftDocs/typography-issues/issues/336 + t = 'Syllable_Modifier' + elif i == 8 and t == 'NA': + t = 'Not_Applicable' + + i0 = i if i < 7 else i - 7 + for u in range (start, end + 1): + unicode_data[i0][u] = t + values[i0][t] = values[i0].get (t, 0) + end - start + 1 + +defaults = ('Other', 'Not_Applicable', 'jt_X', '', 'Cn', 'No_Block', 'Unknown') + +# Merge data into one dict: +for i,v in enumerate (defaults): + values[i][v] = values[i].get (v, 0) + 1 +combined = {} +for i,d in enumerate (unicode_data): + for u,v in d.items (): + if not u in combined: + if i >= 4: + continue + combined[u] = list (defaults) + combined[u][i] = v +combined = {k: v for k, v in combined.items() if v[6] not in DISABLED_SCRIPTS} + + +property_names = [ + # General_Category + 'Cc', 'Cf', 'Cn', 'Co', 'Cs', 'Ll', 'Lm', 'Lo', 'Lt', 'Lu', 'Mc', + 'Me', 'Mn', 'Nd', 'Nl', 'No', 'Pc', 'Pd', 'Pe', 'Pf', 'Pi', 'Po', + 'Ps', 'Sc', 'Sk', 'Sm', 'So', 'Zl', 'Zp', 'Zs', + # Indic_Syllabic_Category + 'Other', + 'Bindu', + 'Visarga', + 'Avagraha', + 'Nukta', + 'Virama', + 'Pure_Killer', + 'Invisible_Stacker', + 'Vowel_Independent', + 'Vowel_Dependent', + 'Vowel', + 'Consonant_Placeholder', + 'Consonant', + 'Consonant_Dead', + 'Consonant_With_Stacker', + 'Consonant_Prefixed', + 'Consonant_Preceding_Repha', + 'Consonant_Succeeding_Repha', + 'Consonant_Subjoined', + 'Consonant_Medial', + 'Consonant_Final', + 'Consonant_Head_Letter', + 'Consonant_Initial_Postfixed', + 'Modifying_Letter', + 'Tone_Letter', + 'Tone_Mark', + 'Gemination_Mark', + 'Cantillation_Mark', + 'Register_Shifter', + 'Syllable_Modifier', + 'Consonant_Killer', + 'Non_Joiner', + 'Joiner', + 'Number_Joiner', + 'Number', + 'Brahmi_Joining_Number', + 'Symbol_Modifier', + 'Hieroglyph', + 'Hieroglyph_Joiner', + 'Hieroglyph_Segment_Begin', + 'Hieroglyph_Segment_End', + # Indic_Positional_Category + 'Not_Applicable', + 'Right', + 'Left', + 'Visual_Order_Left', + 'Left_And_Right', + 'Top', + 'Bottom', + 'Top_And_Bottom', + 'Top_And_Bottom_And_Left', + 'Top_And_Right', + 'Top_And_Left', + 'Top_And_Left_And_Right', + 'Bottom_And_Left', + 'Bottom_And_Right', + 'Top_And_Bottom_And_Right', + 'Overstruck', + # Joining_Type + 'jt_C', + 'jt_D', + 'jt_L', + 'jt_R', + 'jt_T', + 'jt_U', + 'jt_X', +] + +class PropertyValue(object): + def __init__(self, name_): + self.name = name_ + def __str__(self): + return self.name + def __eq__(self, other): + return self.name == (other if isinstance(other, str) else other.name) + def __ne__(self, other): + return not (self == other) + def __hash__(self): + return hash(str(self)) + +property_values = {} + +for name in property_names: + value = PropertyValue(name) + assert value not in property_values + assert value not in globals() + property_values[name] = value +globals().update(property_values) + + +def is_BASE(U, UISC, UDI, UGC, AJT): + return (UISC in [Number, Consonant, Consonant_Head_Letter, + Tone_Letter, + Vowel_Independent, + ] or + # TODO: https://github.com/MicrosoftDocs/typography-issues/issues/484 + AJT in [jt_C, jt_D, jt_L, jt_R] and UISC != Joiner or + (UGC == Lo and UISC in [Avagraha, Bindu, Consonant_Final, Consonant_Medial, + Consonant_Subjoined, Vowel, Vowel_Dependent])) +def is_BASE_NUM(U, UISC, UDI, UGC, AJT): + return UISC == Brahmi_Joining_Number +def is_BASE_OTHER(U, UISC, UDI, UGC, AJT): + if UISC == Consonant_Placeholder: return True + return U in [0x2015, 0x2022, 0x25FB, 0x25FC, 0x25FD, 0x25FE] +def is_CGJ(U, UISC, UDI, UGC, AJT): + # Also includes VARIATION_SELECTOR and ZWJ + return UISC == Joiner or UDI and UGC in [Mc, Me, Mn] +def is_CONS_FINAL(U, UISC, UDI, UGC, AJT): + return ((UISC == Consonant_Final and UGC != Lo) or + UISC == Consonant_Succeeding_Repha) +def is_CONS_FINAL_MOD(U, UISC, UDI, UGC, AJT): + return UISC == Syllable_Modifier +def is_CONS_MED(U, UISC, UDI, UGC, AJT): + # Consonant_Initial_Postfixed is new in Unicode 11; not in the spec. + return (UISC == Consonant_Medial and UGC != Lo or + UISC == Consonant_Initial_Postfixed) +def is_CONS_MOD(U, UISC, UDI, UGC, AJT): + return UISC in [Nukta, Gemination_Mark, Consonant_Killer] +def is_CONS_SUB(U, UISC, UDI, UGC, AJT): + return UISC == Consonant_Subjoined and UGC != Lo +def is_CONS_WITH_STACKER(U, UISC, UDI, UGC, AJT): + return UISC == Consonant_With_Stacker +def is_HALANT(U, UISC, UDI, UGC, AJT): + return UISC == Virama and not is_HALANT_OR_VOWEL_MODIFIER(U, UISC, UDI, UGC, AJT) +def is_HALANT_OR_VOWEL_MODIFIER(U, UISC, UDI, UGC, AJT): + # Split off of HALANT + return U == 0x0DCA +def is_HALANT_NUM(U, UISC, UDI, UGC, AJT): + return UISC == Number_Joiner +def is_HIEROGLYPH(U, UISC, UDI, UGC, AJT): + return UISC == Hieroglyph +def is_HIEROGLYPH_JOINER(U, UISC, UDI, UGC, AJT): + return UISC == Hieroglyph_Joiner +def is_HIEROGLYPH_SEGMENT_BEGIN(U, UISC, UDI, UGC, AJT): + return UISC == Hieroglyph_Segment_Begin +def is_HIEROGLYPH_SEGMENT_END(U, UISC, UDI, UGC, AJT): + return UISC == Hieroglyph_Segment_End +def is_INVISIBLE_STACKER(U, UISC, UDI, UGC, AJT): + # Split off of HALANT + return (UISC == Invisible_Stacker + and not is_SAKOT(U, UISC, UDI, UGC, AJT) + ) +def is_ZWNJ(U, UISC, UDI, UGC, AJT): + return UISC == Non_Joiner +def is_OTHER(U, UISC, UDI, UGC, AJT): + # Also includes BASE_IND and SYM + return ((UGC == Po or UISC in [Consonant_Dead, Joiner, Modifying_Letter, Other]) + and not is_BASE(U, UISC, UDI, UGC, AJT) + and not is_BASE_OTHER(U, UISC, UDI, UGC, AJT) + and not is_CGJ(U, UISC, UDI, UGC, AJT) + and not is_SYM_MOD(U, UISC, UDI, UGC, AJT) + and not is_Word_Joiner(U, UISC, UDI, UGC, AJT) + ) +def is_REPHA(U, UISC, UDI, UGC, AJT): + return UISC in [Consonant_Preceding_Repha, Consonant_Prefixed] +def is_SAKOT(U, UISC, UDI, UGC, AJT): + # Split off of HALANT + return U == 0x1A60 +def is_SYM_MOD(U, UISC, UDI, UGC, AJT): + return UISC == Symbol_Modifier +def is_VOWEL(U, UISC, UDI, UGC, AJT): + return (UISC == Pure_Killer or + UGC != Lo and UISC in [Vowel, Vowel_Dependent]) +def is_VOWEL_MOD(U, UISC, UDI, UGC, AJT): + return (UISC in [Tone_Mark, Cantillation_Mark, Register_Shifter, Visarga] or + UGC != Lo and UISC == Bindu) +def is_Word_Joiner(U, UISC, UDI, UGC, AJT): + # Also includes Rsv + return (UDI and U not in [0x115F, 0x1160, 0x3164, 0xFFA0, 0x1BCA0, 0x1BCA1, 0x1BCA2, 0x1BCA3] + and UISC == Other + and not is_CGJ(U, UISC, UDI, UGC, AJT) + ) or UGC == Cn + +use_mapping = { + 'B': is_BASE, + 'N': is_BASE_NUM, + 'GB': is_BASE_OTHER, + 'CGJ': is_CGJ, + 'F': is_CONS_FINAL, + 'FM': is_CONS_FINAL_MOD, + 'M': is_CONS_MED, + 'CM': is_CONS_MOD, + 'SUB': is_CONS_SUB, + 'CS': is_CONS_WITH_STACKER, + 'H': is_HALANT, + 'HVM': is_HALANT_OR_VOWEL_MODIFIER, + 'HN': is_HALANT_NUM, + 'IS': is_INVISIBLE_STACKER, + 'G': is_HIEROGLYPH, + 'J': is_HIEROGLYPH_JOINER, + 'SB': is_HIEROGLYPH_SEGMENT_BEGIN, + 'SE': is_HIEROGLYPH_SEGMENT_END, + 'ZWNJ': is_ZWNJ, + 'O': is_OTHER, + 'R': is_REPHA, + 'Sk': is_SAKOT, + 'SM': is_SYM_MOD, + 'V': is_VOWEL, + 'VM': is_VOWEL_MOD, + 'WJ': is_Word_Joiner, +} + +use_positions = { + 'F': { + 'Abv': [Top], + 'Blw': [Bottom], + 'Pst': [Right], + }, + 'M': { + 'Abv': [Top], + 'Blw': [Bottom, Bottom_And_Left, Bottom_And_Right], + 'Pst': [Right], + 'Pre': [Left, Top_And_Bottom_And_Left], + }, + 'CM': { + 'Abv': [Top], + 'Blw': [Bottom, Overstruck], + }, + 'V': { + 'Abv': [Top, Top_And_Bottom, Top_And_Bottom_And_Right, Top_And_Right], + 'Blw': [Bottom, Overstruck, Bottom_And_Right], + 'Pst': [Right], + 'Pre': [Left, Top_And_Left, Top_And_Left_And_Right, Left_And_Right], + }, + 'VM': { + 'Abv': [Top], + 'Blw': [Bottom, Overstruck], + 'Pst': [Right], + 'Pre': [Left], + }, + 'SM': { + 'Abv': [Top], + 'Blw': [Bottom], + }, + 'H': None, + 'HVM': None, + 'IS': None, + 'B': None, + 'FM': { + 'Abv': [Top], + 'Blw': [Bottom], + 'Pst': [Not_Applicable], + }, + 'R': None, + 'SUB': None, +} + +def map_to_use(data): + out = {} + items = use_mapping.items() + for U, (UISC, UIPC, AJT, UDI, UGC, UBlock, _) in data.items(): + + # Resolve Indic_Syllabic_Category + + # TODO: These don't have UISC assigned in Unicode 13.0.0, but have UIPC + if 0x1CE2 <= U <= 0x1CE8: UISC = Cantillation_Mark + + # Tibetan: + # TODO: These don't have UISC assigned in Unicode 13.0.0, but have UIPC + if 0x0F18 <= U <= 0x0F19 or 0x0F3E <= U <= 0x0F3F: UISC = Vowel_Dependent + + # TODO: U+1CED should only be allowed after some of + # the nasalization marks, maybe only for U+1CE9..U+1CF1. + if U == 0x1CED: UISC = Tone_Mark + + values = [k for k,v in items if v(U, UISC, UDI, UGC, AJT)] + assert len(values) == 1, "%s %s %s %s %s %s" % (hex(U), UISC, UDI, UGC, AJT, values) + USE = values[0] + + # Resolve Indic_Positional_Category + + # TODO: https://github.com/harfbuzz/harfbuzz/pull/1037 + # and https://github.com/harfbuzz/harfbuzz/issues/1631 + if U in [0x11302, 0x11303, 0x114C1]: UIPC = Top + + assert (UIPC in [Not_Applicable, Visual_Order_Left] or U == 0x0F7F or + USE in use_positions), "%s %s %s %s %s %s %s" % (hex(U), UIPC, USE, UISC, UDI, UGC, AJT) + + pos_mapping = use_positions.get(USE, None) + if pos_mapping: + values = [k for k,v in pos_mapping.items() if v and UIPC in v] + assert len(values) == 1, "%s %s %s %s %s %s %s %s" % (hex(U), UIPC, USE, UISC, UDI, UGC, AJT, values) + USE = USE + values[0] + + out[U] = (USE, UBlock) + return out + +use_data = map_to_use(combined) + +print ("/* == Start of generated table == */") +print ("/*") +print (" * The following table is generated by running:") +print (" *") +print (" * {} IndicSyllabicCategory.txt IndicPositionalCategory.txt ArabicShaping.txt DerivedCoreProperties.txt UnicodeData.txt Blocks.txt Scripts.txt IndicSyllabicCategory-Additional.txt IndicPositionalCategory-Additional.txt".format (sys.argv[0])) +print (" *") +print (" * on files with these headers:") +print (" *") +for h in headers: + for l in h: + print (" * %s" % (l.strip())) +print (" */") +print () +print ("#ifndef HB_OT_SHAPER_USE_TABLE_HH") +print ("#define HB_OT_SHAPER_USE_TABLE_HH") +print () +print ('#include "hb.hh"') +print () +print ('#include "hb-ot-shaper-use-machine.hh"') +print () + +total = 0 +used = 0 +last_block = None +def print_block (block, start, end, use_data): + global total, used, last_block + if block and block != last_block: + print () + print () + print (" /* %s */" % block) + if start % 16: + print (' ' * (20 + (start % 16 * 6)), end='') + num = 0 + assert start % 8 == 0 + assert (end+1) % 8 == 0 + for u in range (start, end+1): + if u % 16 == 0: + print () + print (" /* %04X */" % u, end='') + if u in use_data: + num += 1 + d = use_data.get (u) + if d is not None: + d = d[0] + elif u in unicode_data[4]: + d = 'O' + else: + d = 'WJ' + print ("%6s," % d, end='') + + total += end - start + 1 + used += num + if block: + last_block = block + +uu = sorted (use_data.keys ()) + +last = -100000 +num = 0 +offset = 0 +starts = [] +ends = [] +print ('#pragma GCC diagnostic push') +print ('#pragma GCC diagnostic ignored "-Wunused-macros"') +for k,v in sorted(use_mapping.items()): + if k in use_positions and use_positions[k]: continue + print ("#define %s USE(%s) /* %s */" % (k, k, v.__name__[3:])) +for k,v in sorted(use_positions.items()): + if not v: continue + for suf in v.keys(): + tag = k + suf + print ("#define %s USE(%s)" % (tag, tag)) +print ('#pragma GCC diagnostic pop') +print ("") + + +import packTab +data = {u:v[0] for u,v in use_data.items()} +code = packTab.Code('hb_use') +sol = packTab.pack_table(data, compression=5, default='O') +sol.genCode(code, f'get_category') +code.print_c(linkage='static inline') + +print () +for k in sorted(use_mapping.keys()): + if k in use_positions and use_positions[k]: continue + print ("#undef %s" % k) +for k,v in sorted(use_positions.items()): + if not v: continue + for suf in v.keys(): + tag = k + suf + print ("#undef %s" % tag) +print () +print () +print ("#endif /* HB_OT_SHAPER_USE_TABLE_HH */") +print ("/* == End of generated table == */") diff --git a/libs/harfbuzz/src/gen-vowel-constraints.py b/libs/harfbuzz/src/gen-vowel-constraints.py new file mode 100644 index 00000000..3c1f6211 --- /dev/null +++ b/libs/harfbuzz/src/gen-vowel-constraints.py @@ -0,0 +1,229 @@ +#!/usr/bin/env python3 + +"""Generator of the function to prohibit certain vowel sequences. + +It creates ``_hb_preprocess_text_vowel_constraints``, which inserts dotted +circles into sequences prohibited by the USE script development spec. +This function should be used as the ``preprocess_text`` of an +``hb_ot_shaper_t``. + +usage: ./gen-vowel-constraints.py ms-use/IndicShapingInvalidCluster.txt Scripts.txt + +Input file: +* https://unicode.org/Public/UCD/latest/ucd/Scripts.txt +""" + +import collections +def write (s): + sys.stdout.flush () + sys.stdout.buffer.write (s.encode ('utf-8')) +import sys + +if len (sys.argv) != 3: + sys.exit (__doc__) + +with open (sys.argv[2], encoding='utf-8') as f: + scripts_header = [f.readline () for i in range (2)] + scripts = {} + script_order = {} + for line in f: + j = line.find ('#') + if j >= 0: + line = line[:j] + fields = [x.strip () for x in line.split (';')] + if len (fields) == 1: + continue + uu = fields[0].split ('..') + start = int (uu[0], 16) + if len (uu) == 1: + end = start + else: + end = int (uu[1], 16) + script = fields[1] + for u in range (start, end + 1): + scripts[u] = script + if script not in script_order: + script_order[script] = start + +class ConstraintSet (object): + """A set of prohibited code point sequences. + + Args: + constraint (List[int]): A prohibited code point sequence. + + """ + def __init__ (self, constraint): + # Either a list or a dictionary. As a list of code points, it + # represents a prohibited code point sequence. As a dictionary, + # it represents a set of prohibited sequences, where each item + # represents the set of prohibited sequences starting with the + # key (a code point) concatenated with any of the values + # (ConstraintSets). + self._c = constraint + + def add (self, constraint): + """Add a constraint to this set.""" + if not constraint: + return + first = constraint[0] + rest = constraint[1:] + if isinstance (self._c, list): + if constraint == self._c[:len (constraint)]: + self._c = constraint + elif self._c != constraint[:len (self._c)]: + self._c = {self._c[0]: ConstraintSet (self._c[1:])} + if isinstance (self._c, dict): + if first in self._c: + self._c[first].add (rest) + else: + self._c[first] = ConstraintSet (rest) + + @staticmethod + def _indent (depth): + return (' ' * depth).replace (' ', '\t') + + def __str__ (self, index=0, depth=4): + s = [] + indent = self._indent (depth) + if isinstance (self._c, list): + if len (self._c) == 0: + assert index == 2, 'Cannot use `matched` for this constraint; the general case has not been implemented' + s.append ('{}matched = true;\n'.format (indent)) + elif len (self._c) == 1: + assert index == 1, 'Cannot use `matched` for this constraint; the general case has not been implemented' + s.append ('{}matched = 0x{:04X}u == buffer->cur ({}).codepoint;\n'.format (indent, next (iter (self._c)), index or '')) + else: + s.append ('{}if (0x{:04X}u == buffer->cur ({}).codepoint &&\n'.format (indent, self._c[0], index or '')) + if index: + s.append ('{}buffer->idx + {} < count &&\n'.format (self._indent (depth + 2), index + 1)) + for i, cp in enumerate (self._c[1:], start=1): + s.append ('{}0x{:04X}u == buffer->cur ({}).codepoint{}\n'.format ( + self._indent (depth + 2), cp, index + i, ')' if i == len (self._c) - 1 else ' &&')) + s.append ('{}{{\n'.format (indent)) + for i in range (index): + s.append ('{}(void) buffer->next_glyph ();\n'.format (self._indent (depth + 1))) + s.append ('{}matched = true;\n'.format (self._indent (depth + 1))) + s.append ('{}}}\n'.format (indent)) + else: + s.append ('{}switch (buffer->cur ({}).codepoint)\n'.format(indent, index or '')) + s.append ('{}{{\n'.format (indent)) + cases = collections.defaultdict (set) + for first, rest in sorted (self._c.items ()): + cases[rest.__str__ (index + 1, depth + 2)].add (first) + for body, labels in sorted (cases.items (), key=lambda b_ls: sorted (b_ls[1])[0]): + for i, cp in enumerate (sorted (labels)): + if i % 4 == 0: + s.append (self._indent (depth + 1)) + else: + s.append (' ') + s.append ('case 0x{:04X}u:{}'.format (cp, '\n' if i % 4 == 3 else '')) + if len (labels) % 4 != 0: + s.append ('\n') + s.append (body) + s.append ('{}break;\n'.format (self._indent (depth + 2))) + s.append ('{}}}\n'.format (indent)) + return ''.join (s) + +constraints = {} +with open (sys.argv[1], encoding='utf-8') as f: + constraints_header = [] + while True: + line = f.readline ().strip () + if line == '#': + break + constraints_header.append(line) + for line in f: + j = line.find ('#') + if j >= 0: + line = line[:j] + constraint = [int (cp, 16) for cp in line.split (';')[0].split ()] + if not constraint: continue + assert 2 <= len (constraint), 'Prohibited sequence is too short: {}'.format (constraint) + script = scripts[constraint[0]] + if script in constraints: + constraints[script].add (constraint) + else: + constraints[script] = ConstraintSet (constraint) + assert constraints, 'No constraints found' + +print ('/* == Start of generated functions == */') +print ('/*') +print (' * The following functions are generated by running:') +print (' *') +print (' * %s ms-use/IndicShapingInvalidCluster.txt Scripts.txt' % sys.argv[0]) +print (' *') +print (' * on files with these headers:') +print (' *') +for line in constraints_header: + print (' * %s' % line.strip ()) +print (' *') +for line in scripts_header: + print (' * %s' % line.strip ()) +print (' */') + +print () +print ('#include "hb.hh"') +print () +print ('#ifndef HB_NO_OT_SHAPE') +print () +print ('#include "hb-ot-shaper-vowel-constraints.hh"') +print () +print ('static void') +print ('_output_dotted_circle (hb_buffer_t *buffer)') +print ('{') +print (' (void) buffer->output_glyph (0x25CCu);') +print (' _hb_glyph_info_reset_continuation (&buffer->prev());') +print ('}') +print () +print ('static void') +print ('_output_with_dotted_circle (hb_buffer_t *buffer)') +print ('{') +print (' _output_dotted_circle (buffer);') +print (' (void) buffer->next_glyph ();') +print ('}') +print () + +print ('void') +print ('_hb_preprocess_text_vowel_constraints (const hb_ot_shape_plan_t *plan HB_UNUSED,') +print ('\t\t\t\t hb_buffer_t *buffer,') +print ('\t\t\t\t hb_font_t *font HB_UNUSED)') +print ('{') +print ('#ifdef HB_NO_OT_SHAPER_VOWEL_CONSTRAINTS') +print (' return;') +print ('#endif') +print (' if (buffer->flags & HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE)') +print (' return;') +print () +print (' /* UGLY UGLY UGLY business of adding dotted-circle in the middle of') +print (' * vowel-sequences that look like another vowel. Data for each script') +print (' * collected from the USE script development spec.') +print (' *') +print (' * https://github.com/harfbuzz/harfbuzz/issues/1019') +print (' */') +print (' buffer->clear_output ();') +print (' unsigned int count = buffer->len;') +print (' switch ((unsigned) buffer->props.script)') +print (' {') + +for script, constraints in sorted (constraints.items (), key=lambda s_c: script_order[s_c[0]]): + print (' case HB_SCRIPT_{}:'.format (script.upper ())) + print (' for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;)') + print (' {') + print ('\tbool matched = false;') + write (str (constraints)) + print ('\t(void) buffer->next_glyph ();') + print ('\tif (matched) _output_with_dotted_circle (buffer);') + print (' }') + print (' break;') + print () + +print (' default:') +print (' break;') +print (' }') +print (' buffer->sync ();') +print ('}') + +print () +print () +print ('#endif') +print ('/* == End of generated functions == */') diff --git a/libs/harfbuzz/src/graph/classdef-graph.hh b/libs/harfbuzz/src/graph/classdef-graph.hh new file mode 100644 index 00000000..0bda76ac --- /dev/null +++ b/libs/harfbuzz/src/graph/classdef-graph.hh @@ -0,0 +1,216 @@ +/* + * Copyright © 2022 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): Garret Rieger + */ + +#include "graph.hh" +#include "../hb-ot-layout-common.hh" + +#ifndef GRAPH_CLASSDEF_GRAPH_HH +#define GRAPH_CLASSDEF_GRAPH_HH + +namespace graph { + +struct ClassDefFormat1 : public OT::ClassDefFormat1_3 +{ + bool sanitize (graph_t::vertex_t& vertex) const + { + int64_t vertex_len = vertex.obj.tail - vertex.obj.head; + constexpr unsigned min_size = OT::ClassDefFormat1_3::min_size; + if (vertex_len < min_size) return false; + return vertex_len >= min_size + classValue.get_size () - classValue.len.get_size (); + } +}; + +struct ClassDefFormat2 : public OT::ClassDefFormat2_4 +{ + bool sanitize (graph_t::vertex_t& vertex) const + { + int64_t vertex_len = vertex.obj.tail - vertex.obj.head; + constexpr unsigned min_size = OT::ClassDefFormat2_4::min_size; + if (vertex_len < min_size) return false; + return vertex_len >= min_size + rangeRecord.get_size () - rangeRecord.len.get_size (); + } +}; + +struct ClassDef : public OT::ClassDef +{ + template + static bool add_class_def (gsubgpos_graph_context_t& c, + unsigned parent_id, + unsigned link_position, + It glyph_and_class, + unsigned max_size) + { + unsigned class_def_prime_id = c.graph.new_node (nullptr, nullptr); + auto& class_def_prime_vertex = c.graph.vertices_[class_def_prime_id]; + if (!make_class_def (c, glyph_and_class, class_def_prime_id, max_size)) + return false; + + auto* class_def_link = c.graph.vertices_[parent_id].obj.real_links.push (); + class_def_link->width = SmallTypes::size; + class_def_link->objidx = class_def_prime_id; + class_def_link->position = link_position; + class_def_prime_vertex.parents.push (parent_id); + + return true; + } + + template + static bool make_class_def (gsubgpos_graph_context_t& c, + It glyph_and_class, + unsigned dest_obj, + unsigned max_size) + { + char* buffer = (char*) hb_calloc (1, max_size); + hb_serialize_context_t serializer (buffer, max_size); + OT::ClassDef_serialize (&serializer, glyph_and_class); + serializer.end_serialize (); + if (serializer.in_error ()) + { + hb_free (buffer); + return false; + } + + hb_bytes_t class_def_copy = serializer.copy_bytes (); + c.add_buffer ((char *) class_def_copy.arrayZ); // Give ownership to the context, it will cleanup the buffer. + + auto& obj = c.graph.vertices_[dest_obj].obj; + obj.head = (char *) class_def_copy.arrayZ; + obj.tail = obj.head + class_def_copy.length; + + hb_free (buffer); + return true; + } + + bool sanitize (graph_t::vertex_t& vertex) const + { + int64_t vertex_len = vertex.obj.tail - vertex.obj.head; + if (vertex_len < OT::ClassDef::min_size) return false; + switch (u.format) + { + case 1: return ((ClassDefFormat1*)this)->sanitize (vertex); + case 2: return ((ClassDefFormat2*)this)->sanitize (vertex); +#ifndef HB_NO_BORING_EXPANSION + // Not currently supported + case 3: + case 4: +#endif + default: return false; + } + } +}; + + +struct class_def_size_estimator_t +{ + template + class_def_size_estimator_t (It glyph_and_class) + : gids_consecutive (true), num_ranges_per_class (), glyphs_per_class () + { + unsigned last_gid = (unsigned) -1; + for (auto p : + glyph_and_class) + { + unsigned gid = p.first; + unsigned klass = p.second; + + if (last_gid != (unsigned) -1 && gid != last_gid + 1) + gids_consecutive = false; + last_gid = gid; + + hb_set_t* glyphs; + if (glyphs_per_class.has (klass, &glyphs) && glyphs) { + glyphs->add (gid); + continue; + } + + hb_set_t new_glyphs; + new_glyphs.add (gid); + glyphs_per_class.set (klass, std::move (new_glyphs)); + } + + if (in_error ()) return; + + for (unsigned klass : glyphs_per_class.keys ()) + { + if (!klass) continue; // class 0 doesn't get encoded. + + const hb_set_t& glyphs = glyphs_per_class.get (klass); + hb_codepoint_t start = HB_SET_VALUE_INVALID; + hb_codepoint_t end = HB_SET_VALUE_INVALID; + + unsigned count = 0; + while (glyphs.next_range (&start, &end)) + count++; + + num_ranges_per_class.set (klass, count); + } + } + + // Incremental increase in the Coverage and ClassDef table size + // (worst case) if all glyphs associated with 'klass' were added. + unsigned incremental_coverage_size (unsigned klass) const + { + // Coverage takes 2 bytes per glyph worst case, + return 2 * glyphs_per_class.get (klass).get_population (); + } + + // Incremental increase in the Coverage and ClassDef table size + // (worst case) if all glyphs associated with 'klass' were added. + unsigned incremental_class_def_size (unsigned klass) const + { + // ClassDef takes 6 bytes per range + unsigned class_def_2_size = 6 * num_ranges_per_class.get (klass); + if (gids_consecutive) + { + // ClassDef1 takes 2 bytes per glyph, but only can be used + // when gids are consecutive. + return hb_min (2 * glyphs_per_class.get (klass).get_population (), class_def_2_size); + } + + return class_def_2_size; + } + + bool in_error () + { + if (num_ranges_per_class.in_error ()) return true; + if (glyphs_per_class.in_error ()) return true; + + for (const hb_set_t& s : glyphs_per_class.values ()) + { + if (s.in_error ()) return true; + } + return false; + } + + private: + bool gids_consecutive; + hb_hashmap_t num_ranges_per_class; + hb_hashmap_t glyphs_per_class; +}; + + +} + +#endif // GRAPH_CLASSDEF_GRAPH_HH diff --git a/libs/harfbuzz/src/graph/coverage-graph.hh b/libs/harfbuzz/src/graph/coverage-graph.hh new file mode 100644 index 00000000..3c1022f0 --- /dev/null +++ b/libs/harfbuzz/src/graph/coverage-graph.hh @@ -0,0 +1,152 @@ +/* + * Copyright © 2022 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): Garret Rieger + */ + +#include "graph.hh" +#include "../OT/Layout/Common/Coverage.hh" + +#ifndef GRAPH_COVERAGE_GRAPH_HH +#define GRAPH_COVERAGE_GRAPH_HH + +namespace graph { + +struct CoverageFormat1 : public OT::Layout::Common::CoverageFormat1_3 +{ + bool sanitize (graph_t::vertex_t& vertex) const + { + int64_t vertex_len = vertex.obj.tail - vertex.obj.head; + constexpr unsigned min_size = OT::Layout::Common::CoverageFormat1_3::min_size; + if (vertex_len < min_size) return false; + return vertex_len >= min_size + glyphArray.get_size () - glyphArray.len.get_size (); + } +}; + +struct CoverageFormat2 : public OT::Layout::Common::CoverageFormat2_4 +{ + bool sanitize (graph_t::vertex_t& vertex) const + { + int64_t vertex_len = vertex.obj.tail - vertex.obj.head; + constexpr unsigned min_size = OT::Layout::Common::CoverageFormat2_4::min_size; + if (vertex_len < min_size) return false; + return vertex_len >= min_size + rangeRecord.get_size () - rangeRecord.len.get_size (); + } +}; + +struct Coverage : public OT::Layout::Common::Coverage +{ + static Coverage* clone_coverage (gsubgpos_graph_context_t& c, + unsigned coverage_id, + unsigned new_parent_id, + unsigned link_position, + unsigned start, unsigned end) + + { + unsigned coverage_size = c.graph.vertices_[coverage_id].table_size (); + auto& coverage_v = c.graph.vertices_[coverage_id]; + Coverage* coverage_table = (Coverage*) coverage_v.obj.head; + if (!coverage_table || !coverage_table->sanitize (coverage_v)) + return nullptr; + + auto new_coverage = + + hb_zip (coverage_table->iter (), hb_range ()) + | hb_filter ([&] (hb_pair_t p) { + return p.second >= start && p.second < end; + }) + | hb_map_retains_sorting (hb_first) + ; + + return add_coverage (c, new_parent_id, link_position, new_coverage, coverage_size); + } + + template + static Coverage* add_coverage (gsubgpos_graph_context_t& c, + unsigned parent_id, + unsigned link_position, + It glyphs, + unsigned max_size) + { + unsigned coverage_prime_id = c.graph.new_node (nullptr, nullptr); + auto& coverage_prime_vertex = c.graph.vertices_[coverage_prime_id]; + if (!make_coverage (c, glyphs, coverage_prime_id, max_size)) + return nullptr; + + auto* coverage_link = c.graph.vertices_[parent_id].obj.real_links.push (); + coverage_link->width = SmallTypes::size; + coverage_link->objidx = coverage_prime_id; + coverage_link->position = link_position; + coverage_prime_vertex.parents.push (parent_id); + + return (Coverage*) coverage_prime_vertex.obj.head; + } + + template + static bool make_coverage (gsubgpos_graph_context_t& c, + It glyphs, + unsigned dest_obj, + unsigned max_size) + { + char* buffer = (char*) hb_calloc (1, max_size); + hb_serialize_context_t serializer (buffer, max_size); + OT::Layout::Common::Coverage_serialize (&serializer, glyphs); + serializer.end_serialize (); + if (serializer.in_error ()) + { + hb_free (buffer); + return false; + } + + hb_bytes_t coverage_copy = serializer.copy_bytes (); + c.add_buffer ((char *) coverage_copy.arrayZ); // Give ownership to the context, it will cleanup the buffer. + + auto& obj = c.graph.vertices_[dest_obj].obj; + obj.head = (char *) coverage_copy.arrayZ; + obj.tail = obj.head + coverage_copy.length; + + hb_free (buffer); + return true; + } + + bool sanitize (graph_t::vertex_t& vertex) const + { + int64_t vertex_len = vertex.obj.tail - vertex.obj.head; + if (vertex_len < OT::Layout::Common::Coverage::min_size) return false; + switch (u.format) + { + case 1: return ((CoverageFormat1*)this)->sanitize (vertex); + case 2: return ((CoverageFormat2*)this)->sanitize (vertex); +#ifndef HB_NO_BORING_EXPANSION + // Not currently supported + case 3: + case 4: +#endif + default: return false; + } + } +}; + + +} + +#endif // GRAPH_COVERAGE_GRAPH_HH diff --git a/libs/harfbuzz/src/graph/graph.hh b/libs/harfbuzz/src/graph/graph.hh new file mode 100644 index 00000000..79c7e690 --- /dev/null +++ b/libs/harfbuzz/src/graph/graph.hh @@ -0,0 +1,1258 @@ +/* + * Copyright © 2022 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): Garret Rieger + */ + +#include "../hb-set.hh" +#include "../hb-priority-queue.hh" +#include "../hb-serialize.hh" + +#ifndef GRAPH_GRAPH_HH +#define GRAPH_GRAPH_HH + +namespace graph { + +/** + * Represents a serialized table in the form of a graph. + * Provides methods for modifying and reordering the graph. + */ +struct graph_t +{ + struct vertex_t + { + hb_serialize_context_t::object_t obj; + int64_t distance = 0 ; + int64_t space = 0 ; + hb_vector_t parents; + unsigned start = 0; + unsigned end = 0; + unsigned priority = 0; + + void normalize () + { + obj.real_links.qsort (); + for (auto& l : obj.real_links) + { + for (unsigned i = 0; i < l.width; i++) + { + obj.head[l.position + i] = 0; + } + } + } + + bool equals (const vertex_t& other, + const graph_t& graph, + const graph_t& other_graph, + unsigned depth) const + { + if (!(as_bytes () == other.as_bytes ())) + { + DEBUG_MSG (SUBSET_REPACK, nullptr, + "vertex [%lu] bytes != [%lu] bytes, depth = %u", + (unsigned long) table_size (), + (unsigned long) other.table_size (), + depth); + + auto a = as_bytes (); + auto b = other.as_bytes (); + while (a || b) + { + DEBUG_MSG (SUBSET_REPACK, nullptr, + " 0x%x %s 0x%x", *a, (*a == *b) ? "==" : "!=", *b); + a++; + b++; + } + return false; + } + + return links_equal (obj.real_links, other.obj.real_links, graph, other_graph, depth); + } + + hb_bytes_t as_bytes () const + { + return hb_bytes_t (obj.head, table_size ()); + } + + friend void swap (vertex_t& a, vertex_t& b) + { + hb_swap (a.obj, b.obj); + hb_swap (a.distance, b.distance); + hb_swap (a.space, b.space); + hb_swap (a.parents, b.parents); + hb_swap (a.start, b.start); + hb_swap (a.end, b.end); + hb_swap (a.priority, b.priority); + } + + hb_hashmap_t + position_to_index_map () const + { + hb_hashmap_t result; + + for (const auto& l : obj.real_links) { + result.set (l.position, l.objidx); + } + + return result; + } + + bool is_shared () const + { + return parents.length > 1; + } + + unsigned incoming_edges () const + { + return parents.length; + } + + void remove_parent (unsigned parent_index) + { + for (unsigned i = 0; i < parents.length; i++) + { + if (parents[i] != parent_index) continue; + parents.remove (i); + break; + } + } + + void remove_real_link (unsigned child_index, const void* offset) + { + for (unsigned i = 0; i < obj.real_links.length; i++) + { + auto& link = obj.real_links.arrayZ[i]; + if (link.objidx != child_index) + continue; + + if ((obj.head + link.position) != offset) + continue; + + obj.real_links.remove (i); + return; + } + } + + void remap_parents (const hb_vector_t& id_map) + { + for (unsigned i = 0; i < parents.length; i++) + parents[i] = id_map[parents[i]]; + } + + void remap_parent (unsigned old_index, unsigned new_index) + { + for (unsigned i = 0; i < parents.length; i++) + { + if (parents[i] == old_index) + parents[i] = new_index; + } + } + + bool is_leaf () const + { + return !obj.real_links.length && !obj.virtual_links.length; + } + + bool raise_priority () + { + if (has_max_priority ()) return false; + priority++; + return true; + } + + bool has_max_priority () const { + return priority >= 3; + } + + size_t table_size () const { + return obj.tail - obj.head; + } + + int64_t modified_distance (unsigned order) const + { + // TODO(garretrieger): once priority is high enough, should try + // setting distance = 0 which will force to sort immediately after + // it's parent where possible. + + int64_t modified_distance = + hb_min (hb_max(distance + distance_modifier (), 0), 0x7FFFFFFFFFF); + if (has_max_priority ()) { + modified_distance = 0; + } + return (modified_distance << 18) | (0x003FFFF & order); + } + + int64_t distance_modifier () const + { + if (!priority) return 0; + int64_t table_size = obj.tail - obj.head; + + if (priority == 1) + return -table_size / 2; + + return -table_size; + } + + private: + bool links_equal (const hb_vector_t& this_links, + const hb_vector_t& other_links, + const graph_t& graph, + const graph_t& other_graph, + unsigned depth) const + { + auto a = this_links.iter (); + auto b = other_links.iter (); + + while (a && b) + { + const auto& link_a = *a; + const auto& link_b = *b; + + if (link_a.width != link_b.width || + link_a.is_signed != link_b.is_signed || + link_a.whence != link_b.whence || + link_a.position != link_b.position || + link_a.bias != link_b.bias) + return false; + + if (!graph.vertices_[link_a.objidx].equals ( + other_graph.vertices_[link_b.objidx], graph, other_graph, depth + 1)) + return false; + + a++; + b++; + } + + if (bool (a) != bool (b)) + return false; + + return true; + } + }; + + template + struct vertex_and_table_t + { + vertex_and_table_t () : index (0), vertex (nullptr), table (nullptr) + {} + + unsigned index; + vertex_t* vertex; + T* table; + + operator bool () { + return table && vertex; + } + }; + + /* + * A topological sorting of an object graph. Ordered + * in reverse serialization order (first object in the + * serialization is at the end of the list). This matches + * the 'packed' object stack used internally in the + * serializer + */ + template + graph_t (const T& objects) + : parents_invalid (true), + distance_invalid (true), + positions_invalid (true), + successful (true), + buffers () + { + num_roots_for_space_.push (1); + bool removed_nil = false; + vertices_.alloc (objects.length); + vertices_scratch_.alloc (objects.length); + for (unsigned i = 0; i < objects.length; i++) + { + // TODO(grieger): check all links point to valid objects. + + // If this graph came from a serialization buffer object 0 is the + // nil object. We don't need it for our purposes here so drop it. + if (i == 0 && !objects[i]) + { + removed_nil = true; + continue; + } + + vertex_t* v = vertices_.push (); + if (check_success (!vertices_.in_error ())) + v->obj = *objects[i]; + if (!removed_nil) continue; + // Fix indices to account for removed nil object. + for (auto& l : v->obj.all_links_writer ()) { + l.objidx--; + } + } + } + + ~graph_t () + { + vertices_.fini (); + for (char* b : buffers) + hb_free (b); + } + + bool operator== (const graph_t& other) const + { + return root ().equals (other.root (), *this, other, 0); + } + + // Sorts links of all objects in a consistent manner and zeroes all offsets. + void normalize () + { + for (auto& v : vertices_.writer ()) + v.normalize (); + } + + bool in_error () const + { + return !successful || + vertices_.in_error () || + num_roots_for_space_.in_error (); + } + + const vertex_t& root () const + { + return vertices_[root_idx ()]; + } + + unsigned root_idx () const + { + // Object graphs are in reverse order, the first object is at the end + // of the vector. Since the graph is topologically sorted it's safe to + // assume the first object has no incoming edges. + return vertices_.length - 1; + } + + const hb_serialize_context_t::object_t& object (unsigned i) const + { + return vertices_[i].obj; + } + + void add_buffer (char* buffer) + { + buffers.push (buffer); + } + + /* + * Adds a 16 bit link from parent_id to child_id + */ + template + void add_link (T* offset, + unsigned parent_id, + unsigned child_id) + { + auto& v = vertices_[parent_id]; + auto* link = v.obj.real_links.push (); + link->width = 2; + link->objidx = child_id; + link->position = (char*) offset - (char*) v.obj.head; + vertices_[child_id].parents.push (parent_id); + } + + /* + * Generates a new topological sorting of graph ordered by the shortest + * distance to each node if positions are marked as invalid. + */ + void sort_shortest_distance_if_needed () + { + if (!positions_invalid) return; + sort_shortest_distance (); + } + + + /* + * Generates a new topological sorting of graph ordered by the shortest + * distance to each node. + */ + void sort_shortest_distance () + { + positions_invalid = true; + + if (vertices_.length <= 1) { + // Graph of 1 or less doesn't need sorting. + return; + } + + update_distances (); + + hb_priority_queue_t queue; + hb_vector_t &sorted_graph = vertices_scratch_; + if (unlikely (!check_success (sorted_graph.resize (vertices_.length)))) return; + hb_vector_t id_map; + if (unlikely (!check_success (id_map.resize (vertices_.length)))) return; + + hb_vector_t removed_edges; + if (unlikely (!check_success (removed_edges.resize (vertices_.length)))) return; + update_parents (); + + queue.insert (root ().modified_distance (0), root_idx ()); + int new_id = root_idx (); + unsigned order = 1; + while (!queue.in_error () && !queue.is_empty ()) + { + unsigned next_id = queue.pop_minimum().second; + + hb_swap (sorted_graph[new_id], vertices_[next_id]); + const vertex_t& next = sorted_graph[new_id]; + + id_map[next_id] = new_id--; + + for (const auto& link : next.obj.all_links ()) { + removed_edges[link.objidx]++; + if (!(vertices_[link.objidx].incoming_edges () - removed_edges[link.objidx])) + // Add the order that the links were encountered to the priority. + // This ensures that ties between priorities objects are broken in a consistent + // way. More specifically this is set up so that if a set of objects have the same + // distance they'll be added to the topological order in the order that they are + // referenced from the parent object. + queue.insert (vertices_[link.objidx].modified_distance (order++), + link.objidx); + } + } + + check_success (!queue.in_error ()); + check_success (!sorted_graph.in_error ()); + + remap_all_obj_indices (id_map, &sorted_graph); + hb_swap (vertices_, sorted_graph); + + if (!check_success (new_id == -1)) + print_orphaned_nodes (); + } + + /* + * Finds the set of nodes (placed into roots) that should be assigned unique spaces. + * More specifically this looks for the top most 24 bit or 32 bit links in the graph. + * Some special casing is done that is specific to the layout of GSUB/GPOS tables. + */ + void find_space_roots (hb_set_t& visited, hb_set_t& roots) + { + int root_index = (int) root_idx (); + for (int i = root_index; i >= 0; i--) + { + if (visited.has (i)) continue; + + // Only real links can form 32 bit spaces + for (auto& l : vertices_[i].obj.real_links) + { + if (l.is_signed || l.width < 3) + continue; + + if (i == root_index && l.width == 3) + // Ignore 24bit links from the root node, this skips past the single 24bit + // pointer to the lookup list. + continue; + + if (l.width == 3) + { + // A 24bit offset forms a root, unless there is 32bit offsets somewhere + // in it's subgraph, then those become the roots instead. This is to make sure + // that extension subtables beneath a 24bit lookup become the spaces instead + // of the offset to the lookup. + hb_set_t sub_roots; + find_32bit_roots (l.objidx, sub_roots); + if (sub_roots) { + for (unsigned sub_root_idx : sub_roots) { + roots.add (sub_root_idx); + find_subgraph (sub_root_idx, visited); + } + continue; + } + } + + roots.add (l.objidx); + find_subgraph (l.objidx, visited); + } + } + } + + template + vertex_and_table_t as_table (unsigned parent, const void* offset, Ts... ds) + { + return as_table_from_index (index_for_offset (parent, offset), std::forward(ds)...); + } + + template + vertex_and_table_t as_mutable_table (unsigned parent, const void* offset, Ts... ds) + { + return as_table_from_index (mutable_index_for_offset (parent, offset), std::forward(ds)...); + } + + template + vertex_and_table_t as_table_from_index (unsigned index, Ts... ds) + { + if (index >= vertices_.length) + return vertex_and_table_t (); + + vertex_and_table_t r; + r.vertex = &vertices_[index]; + r.table = (T*) r.vertex->obj.head; + r.index = index; + if (!r.table) + return vertex_and_table_t (); + + if (!r.table->sanitize (*(r.vertex), std::forward(ds)...)) + return vertex_and_table_t (); + + return r; + } + + // Finds the object id of the object pointed to by the offset at 'offset' + // within object[node_idx]. + unsigned index_for_offset (unsigned node_idx, const void* offset) const + { + const auto& node = object (node_idx); + if (offset < node.head || offset >= node.tail) return -1; + + unsigned length = node.real_links.length; + for (unsigned i = 0; i < length; i++) + { + // Use direct access for increased performance, this is a hot method. + const auto& link = node.real_links.arrayZ[i]; + if (offset != node.head + link.position) + continue; + return link.objidx; + } + + return -1; + } + + // Finds the object id of the object pointed to by the offset at 'offset' + // within object[node_idx]. Ensures that the returned object is safe to mutate. + // That is, if the original child object is shared by parents other than node_idx + // it will be duplicated and the duplicate will be returned instead. + unsigned mutable_index_for_offset (unsigned node_idx, const void* offset) + { + unsigned child_idx = index_for_offset (node_idx, offset); + auto& child = vertices_[child_idx]; + for (unsigned p : child.parents) + { + if (p != node_idx) { + return duplicate (node_idx, child_idx); + } + } + + return child_idx; + } + + + /* + * Assign unique space numbers to each connected subgraph of 24 bit and/or 32 bit offset(s). + * Currently, this is implemented specifically tailored to the structure of a GPOS/GSUB + * (including with 24bit offsets) table. + */ + bool assign_spaces () + { + update_parents (); + + hb_set_t visited; + hb_set_t roots; + find_space_roots (visited, roots); + + // Mark everything not in the subgraphs of the roots as visited. This prevents + // subgraphs from being connected via nodes not in those subgraphs. + visited.invert (); + + if (!roots) return false; + + while (roots) + { + unsigned next = HB_SET_VALUE_INVALID; + if (unlikely (!check_success (!roots.in_error ()))) break; + if (!roots.next (&next)) break; + + hb_set_t connected_roots; + find_connected_nodes (next, roots, visited, connected_roots); + if (unlikely (!check_success (!connected_roots.in_error ()))) break; + + isolate_subgraph (connected_roots); + if (unlikely (!check_success (!connected_roots.in_error ()))) break; + + unsigned next_space = this->next_space (); + num_roots_for_space_.push (0); + for (unsigned root : connected_roots) + { + DEBUG_MSG (SUBSET_REPACK, nullptr, "Subgraph %u gets space %u", root, next_space); + vertices_[root].space = next_space; + num_roots_for_space_[next_space] = num_roots_for_space_[next_space] + 1; + distance_invalid = true; + positions_invalid = true; + } + + // TODO(grieger): special case for GSUB/GPOS use extension promotions to move 16 bit space + // into the 32 bit space as needed, instead of using isolation. + } + + + + return true; + } + + /* + * Isolates the subgraph of nodes reachable from root. Any links to nodes in the subgraph + * that originate from outside of the subgraph will be removed by duplicating the linked to + * object. + * + * Indices stored in roots will be updated if any of the roots are duplicated to new indices. + */ + bool isolate_subgraph (hb_set_t& roots) + { + update_parents (); + hb_map_t subgraph; + + // incoming edges to root_idx should be all 32 bit in length so we don't need to de-dup these + // set the subgraph incoming edge count to match all of root_idx's incoming edges + hb_set_t parents; + for (unsigned root_idx : roots) + { + subgraph.set (root_idx, wide_parents (root_idx, parents)); + find_subgraph (root_idx, subgraph); + } + + unsigned original_root_idx = root_idx (); + hb_map_t index_map; + bool made_changes = false; + for (auto entry : subgraph.iter ()) + { + const auto& node = vertices_[entry.first]; + unsigned subgraph_incoming_edges = entry.second; + + if (subgraph_incoming_edges < node.incoming_edges ()) + { + // Only de-dup objects with incoming links from outside the subgraph. + made_changes = true; + duplicate_subgraph (entry.first, index_map); + } + } + + if (!made_changes) + return false; + + if (original_root_idx != root_idx () + && parents.has (original_root_idx)) + { + // If the root idx has changed since parents was determined, update root idx in parents + parents.add (root_idx ()); + parents.del (original_root_idx); + } + + auto new_subgraph = + + subgraph.keys () + | hb_map([&] (unsigned node_idx) { + const unsigned *v; + if (index_map.has (node_idx, &v)) return *v; + return node_idx; + }) + ; + + remap_obj_indices (index_map, new_subgraph); + remap_obj_indices (index_map, parents.iter (), true); + + // Update roots set with new indices as needed. + unsigned next = HB_SET_VALUE_INVALID; + while (roots.next (&next)) + { + const unsigned *v; + if (index_map.has (next, &v)) + { + roots.del (next); + roots.add (*v); + } + } + + return true; + } + + void find_subgraph (unsigned node_idx, hb_map_t& subgraph) + { + for (const auto& link : vertices_[node_idx].obj.all_links ()) + { + const unsigned *v; + if (subgraph.has (link.objidx, &v)) + { + subgraph.set (link.objidx, *v + 1); + continue; + } + subgraph.set (link.objidx, 1); + find_subgraph (link.objidx, subgraph); + } + } + + void find_subgraph (unsigned node_idx, hb_set_t& subgraph) + { + if (subgraph.has (node_idx)) return; + subgraph.add (node_idx); + for (const auto& link : vertices_[node_idx].obj.all_links ()) + find_subgraph (link.objidx, subgraph); + } + + size_t find_subgraph_size (unsigned node_idx, hb_set_t& subgraph, unsigned max_depth = -1) + { + if (subgraph.has (node_idx)) return 0; + subgraph.add (node_idx); + + const auto& o = vertices_[node_idx].obj; + size_t size = o.tail - o.head; + if (max_depth == 0) + return size; + + for (const auto& link : o.all_links ()) + size += find_subgraph_size (link.objidx, subgraph, max_depth - 1); + return size; + } + + /* + * Finds the topmost children of 32bit offsets in the subgraph starting + * at node_idx. Found indices are placed into 'found'. + */ + void find_32bit_roots (unsigned node_idx, hb_set_t& found) + { + for (const auto& link : vertices_[node_idx].obj.all_links ()) + { + if (!link.is_signed && link.width == 4) { + found.add (link.objidx); + continue; + } + find_32bit_roots (link.objidx, found); + } + } + + /* + * Moves the child of old_parent_idx pointed to by old_offset to a new + * vertex at the new_offset. + */ + template + void move_child (unsigned old_parent_idx, + const O* old_offset, + unsigned new_parent_idx, + const O* new_offset) + { + distance_invalid = true; + positions_invalid = true; + + auto& old_v = vertices_[old_parent_idx]; + auto& new_v = vertices_[new_parent_idx]; + + unsigned child_id = index_for_offset (old_parent_idx, + old_offset); + + auto* new_link = new_v.obj.real_links.push (); + new_link->width = O::static_size; + new_link->objidx = child_id; + new_link->position = (const char*) new_offset - (const char*) new_v.obj.head; + + auto& child = vertices_[child_id]; + child.parents.push (new_parent_idx); + + old_v.remove_real_link (child_id, old_offset); + child.remove_parent (old_parent_idx); + } + + /* + * duplicates all nodes in the subgraph reachable from node_idx. Does not re-assign + * links. index_map is updated with mappings from old id to new id. If a duplication has already + * been performed for a given index, then it will be skipped. + */ + void duplicate_subgraph (unsigned node_idx, hb_map_t& index_map) + { + if (index_map.has (node_idx)) + return; + + index_map.set (node_idx, duplicate (node_idx)); + for (const auto& l : object (node_idx).all_links ()) { + duplicate_subgraph (l.objidx, index_map); + } + } + + /* + * Creates a copy of node_idx and returns it's new index. + */ + unsigned duplicate (unsigned node_idx) + { + positions_invalid = true; + distance_invalid = true; + + auto* clone = vertices_.push (); + auto& child = vertices_[node_idx]; + if (vertices_.in_error ()) { + return -1; + } + + clone->obj.head = child.obj.head; + clone->obj.tail = child.obj.tail; + clone->distance = child.distance; + clone->space = child.space; + clone->parents.reset (); + + unsigned clone_idx = vertices_.length - 2; + for (const auto& l : child.obj.real_links) + { + clone->obj.real_links.push (l); + vertices_[l.objidx].parents.push (clone_idx); + } + for (const auto& l : child.obj.virtual_links) + { + clone->obj.virtual_links.push (l); + vertices_[l.objidx].parents.push (clone_idx); + } + + check_success (!clone->obj.real_links.in_error ()); + check_success (!clone->obj.virtual_links.in_error ()); + + // The last object is the root of the graph, so swap back the root to the end. + // The root's obj idx does change, however since it's root nothing else refers to it. + // all other obj idx's will be unaffected. + hb_swap (vertices_[vertices_.length - 2], *clone); + + // Since the root moved, update the parents arrays of all children on the root. + for (const auto& l : root ().obj.all_links ()) + vertices_[l.objidx].remap_parent (root_idx () - 1, root_idx ()); + + return clone_idx; + } + + /* + * Creates a copy of child and re-assigns the link from + * parent to the clone. The copy is a shallow copy, objects + * linked from child are not duplicated. + */ + unsigned duplicate_if_shared (unsigned parent_idx, unsigned child_idx) + { + unsigned new_idx = duplicate (parent_idx, child_idx); + if (new_idx == (unsigned) -1) return child_idx; + return new_idx; + } + + + /* + * Creates a copy of child and re-assigns the link from + * parent to the clone. The copy is a shallow copy, objects + * linked from child are not duplicated. + */ + unsigned duplicate (unsigned parent_idx, unsigned child_idx) + { + update_parents (); + + unsigned links_to_child = 0; + for (const auto& l : vertices_[parent_idx].obj.all_links ()) + { + if (l.objidx == child_idx) links_to_child++; + } + + if (vertices_[child_idx].incoming_edges () <= links_to_child) + { + // Can't duplicate this node, doing so would orphan the original one as all remaining links + // to child are from parent. + DEBUG_MSG (SUBSET_REPACK, nullptr, " Not duplicating %d => %d", + parent_idx, child_idx); + return -1; + } + + DEBUG_MSG (SUBSET_REPACK, nullptr, " Duplicating %d => %d", + parent_idx, child_idx); + + unsigned clone_idx = duplicate (child_idx); + if (clone_idx == (unsigned) -1) return false; + // duplicate shifts the root node idx, so if parent_idx was root update it. + if (parent_idx == clone_idx) parent_idx++; + + auto& parent = vertices_[parent_idx]; + for (auto& l : parent.obj.all_links_writer ()) + { + if (l.objidx != child_idx) + continue; + + reassign_link (l, parent_idx, clone_idx); + } + + return clone_idx; + } + + + /* + * Adds a new node to the graph, not connected to anything. + */ + unsigned new_node (char* head, char* tail) + { + positions_invalid = true; + distance_invalid = true; + + auto* clone = vertices_.push (); + if (vertices_.in_error ()) { + return -1; + } + + clone->obj.head = head; + clone->obj.tail = tail; + clone->distance = 0; + clone->space = 0; + + unsigned clone_idx = vertices_.length - 2; + + // The last object is the root of the graph, so swap back the root to the end. + // The root's obj idx does change, however since it's root nothing else refers to it. + // all other obj idx's will be unaffected. + hb_swap (vertices_[vertices_.length - 2], *clone); + + // Since the root moved, update the parents arrays of all children on the root. + for (const auto& l : root ().obj.all_links ()) + vertices_[l.objidx].remap_parent (root_idx () - 1, root_idx ()); + + return clone_idx; + } + + /* + * Raises the sorting priority of all children. + */ + bool raise_childrens_priority (unsigned parent_idx) + { + DEBUG_MSG (SUBSET_REPACK, nullptr, " Raising priority of all children of %d", + parent_idx); + // This operation doesn't change ordering until a sort is run, so no need + // to invalidate positions. It does not change graph structure so no need + // to update distances or edge counts. + auto& parent = vertices_[parent_idx].obj; + bool made_change = false; + for (auto& l : parent.all_links_writer ()) + made_change |= vertices_[l.objidx].raise_priority (); + return made_change; + } + + void print_orphaned_nodes () + { + if (!DEBUG_ENABLED(SUBSET_REPACK)) return; + + DEBUG_MSG (SUBSET_REPACK, nullptr, "Graph is not fully connected."); + parents_invalid = true; + update_parents(); + + for (unsigned i = 0; i < root_idx (); i++) + { + const auto& v = vertices_[i]; + if (!v.parents) + DEBUG_MSG (SUBSET_REPACK, nullptr, "Node %u is orphaned.", i); + } + } + + unsigned num_roots_for_space (unsigned space) const + { + return num_roots_for_space_[space]; + } + + unsigned next_space () const + { + return num_roots_for_space_.length; + } + + void move_to_new_space (const hb_set_t& indices) + { + num_roots_for_space_.push (0); + unsigned new_space = num_roots_for_space_.length - 1; + + for (unsigned index : indices) { + auto& node = vertices_[index]; + num_roots_for_space_[node.space] = num_roots_for_space_[node.space] - 1; + num_roots_for_space_[new_space] = num_roots_for_space_[new_space] + 1; + node.space = new_space; + distance_invalid = true; + positions_invalid = true; + } + } + + unsigned space_for (unsigned index, unsigned* root = nullptr) const + { + const auto& node = vertices_[index]; + if (node.space) + { + if (root != nullptr) + *root = index; + return node.space; + } + + if (!node.parents) + { + if (root) + *root = index; + return 0; + } + + return space_for (node.parents[0], root); + } + + void err_other_error () { this->successful = false; } + + size_t total_size_in_bytes () const { + size_t total_size = 0; + for (unsigned i = 0; i < vertices_.length; i++) { + size_t size = vertices_[i].obj.tail - vertices_[i].obj.head; + total_size += size; + } + return total_size; + } + + + private: + + /* + * Returns the numbers of incoming edges that are 24 or 32 bits wide. + */ + unsigned wide_parents (unsigned node_idx, hb_set_t& parents) const + { + unsigned count = 0; + hb_set_t visited; + for (unsigned p : vertices_[node_idx].parents) + { + if (visited.has (p)) continue; + visited.add (p); + + // Only real links can be wide + for (const auto& l : vertices_[p].obj.real_links) + { + if (l.objidx == node_idx + && (l.width == 3 || l.width == 4) + && !l.is_signed) + { + count++; + parents.add (p); + } + } + } + return count; + } + + bool check_success (bool success) + { return this->successful && (success || ((void) err_other_error (), false)); } + + public: + /* + * Creates a map from objid to # of incoming edges. + */ + void update_parents () + { + if (!parents_invalid) return; + + for (unsigned i = 0; i < vertices_.length; i++) + vertices_[i].parents.reset (); + + for (unsigned p = 0; p < vertices_.length; p++) + { + for (auto& l : vertices_[p].obj.all_links ()) + { + vertices_[l.objidx].parents.push (p); + } + } + + parents_invalid = false; + } + + /* + * compute the serialized start and end positions for each vertex. + */ + void update_positions () + { + if (!positions_invalid) return; + + unsigned current_pos = 0; + for (int i = root_idx (); i >= 0; i--) + { + auto& v = vertices_[i]; + v.start = current_pos; + current_pos += v.obj.tail - v.obj.head; + v.end = current_pos; + } + + positions_invalid = false; + } + + /* + * Finds the distance to each object in the graph + * from the initial node. + */ + void update_distances () + { + if (!distance_invalid) return; + + // Uses Dijkstra's algorithm to find all of the shortest distances. + // https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm + // + // Implementation Note: + // Since our priority queue doesn't support fast priority decreases + // we instead just add new entries into the queue when a priority changes. + // Redundant ones are filtered out later on by the visited set. + // According to https://www3.cs.stonybrook.edu/~rezaul/papers/TR-07-54.pdf + // for practical performance this is faster then using a more advanced queue + // (such as a fibonacci queue) with a fast decrease priority. + for (unsigned i = 0; i < vertices_.length; i++) + { + if (i == vertices_.length - 1) + vertices_[i].distance = 0; + else + vertices_[i].distance = hb_int_max (int64_t); + } + + hb_priority_queue_t queue; + queue.insert (0, vertices_.length - 1); + + hb_vector_t visited; + visited.resize (vertices_.length); + + while (!queue.in_error () && !queue.is_empty ()) + { + unsigned next_idx = queue.pop_minimum ().second; + if (visited[next_idx]) continue; + const auto& next = vertices_[next_idx]; + int64_t next_distance = vertices_[next_idx].distance; + visited[next_idx] = true; + + for (const auto& link : next.obj.all_links ()) + { + if (visited[link.objidx]) continue; + + const auto& child = vertices_[link.objidx].obj; + unsigned link_width = link.width ? link.width : 4; // treat virtual offsets as 32 bits wide + int64_t child_weight = (child.tail - child.head) + + ((int64_t) 1 << (link_width * 8)) * (vertices_[link.objidx].space + 1); + int64_t child_distance = next_distance + child_weight; + + if (child_distance < vertices_[link.objidx].distance) + { + vertices_[link.objidx].distance = child_distance; + queue.insert (child_distance, link.objidx); + } + } + } + + check_success (!queue.in_error ()); + if (!check_success (queue.is_empty ())) + { + print_orphaned_nodes (); + return; + } + + distance_invalid = false; + } + + private: + /* + * Updates a link in the graph to point to a different object. Corrects the + * parents vector on the previous and new child nodes. + */ + void reassign_link (hb_serialize_context_t::object_t::link_t& link, + unsigned parent_idx, + unsigned new_idx) + { + unsigned old_idx = link.objidx; + link.objidx = new_idx; + vertices_[old_idx].remove_parent (parent_idx); + vertices_[new_idx].parents.push (parent_idx); + } + + /* + * Updates all objidx's in all links using the provided mapping. Corrects incoming edge counts. + */ + template + void remap_obj_indices (const hb_map_t& id_map, + Iterator subgraph, + bool only_wide = false) + { + if (!id_map) return; + for (unsigned i : subgraph) + { + for (auto& link : vertices_[i].obj.all_links_writer ()) + { + const unsigned *v; + if (!id_map.has (link.objidx, &v)) continue; + if (only_wide && !(link.width == 4 && !link.is_signed)) continue; + + reassign_link (link, i, *v); + } + } + } + + /* + * Updates all objidx's in all links using the provided mapping. + */ + void remap_all_obj_indices (const hb_vector_t& id_map, + hb_vector_t* sorted_graph) const + { + for (unsigned i = 0; i < sorted_graph->length; i++) + { + (*sorted_graph)[i].remap_parents (id_map); + for (auto& link : (*sorted_graph)[i].obj.all_links_writer ()) + { + link.objidx = id_map[link.objidx]; + } + } + } + + /* + * Finds all nodes in targets that are reachable from start_idx, nodes in visited will be skipped. + * For this search the graph is treated as being undirected. + * + * Connected targets will be added to connected and removed from targets. All visited nodes + * will be added to visited. + */ + void find_connected_nodes (unsigned start_idx, + hb_set_t& targets, + hb_set_t& visited, + hb_set_t& connected) + { + if (unlikely (!check_success (!visited.in_error ()))) return; + if (visited.has (start_idx)) return; + visited.add (start_idx); + + if (targets.has (start_idx)) + { + targets.del (start_idx); + connected.add (start_idx); + } + + const auto& v = vertices_[start_idx]; + + // Graph is treated as undirected so search children and parents of start_idx + for (const auto& l : v.obj.all_links ()) + find_connected_nodes (l.objidx, targets, visited, connected); + + for (unsigned p : v.parents) + find_connected_nodes (p, targets, visited, connected); + } + + public: + // TODO(garretrieger): make private, will need to move most of offset overflow code into graph. + hb_vector_t vertices_; + hb_vector_t vertices_scratch_; + private: + bool parents_invalid; + bool distance_invalid; + bool positions_invalid; + bool successful; + hb_vector_t num_roots_for_space_; + hb_vector_t buffers; +}; + +} + +#endif // GRAPH_GRAPH_HH diff --git a/libs/harfbuzz/src/graph/gsubgpos-context.cc b/libs/harfbuzz/src/graph/gsubgpos-context.cc new file mode 100644 index 00000000..b2044426 --- /dev/null +++ b/libs/harfbuzz/src/graph/gsubgpos-context.cc @@ -0,0 +1,70 @@ +/* + * Copyright © 2022 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): Garret Rieger + */ + +#include "gsubgpos-graph.hh" + +namespace graph { + +gsubgpos_graph_context_t::gsubgpos_graph_context_t (hb_tag_t table_tag_, + graph_t& graph_) + : table_tag (table_tag_), + graph (graph_), + lookup_list_index (0), + lookups () +{ + if (table_tag_ != HB_OT_TAG_GPOS + && table_tag_ != HB_OT_TAG_GSUB) + return; + + GSTAR* gstar = graph::GSTAR::graph_to_gstar (graph_); + if (gstar) { + gstar->find_lookups (graph, lookups); + lookup_list_index = gstar->get_lookup_list_index (graph_); + } +} + +unsigned gsubgpos_graph_context_t::create_node (unsigned size) +{ + char* buffer = (char*) hb_calloc (1, size); + if (!buffer) + return -1; + + add_buffer (buffer); + + return graph.new_node (buffer, buffer + size); +} + +unsigned gsubgpos_graph_context_t::num_non_ext_subtables () { + unsigned count = 0; + for (auto l : lookups.values ()) + { + if (l->is_extension (table_tag)) continue; + count += l->number_of_subtables (); + } + return count; +} + +} diff --git a/libs/harfbuzz/src/graph/gsubgpos-context.hh b/libs/harfbuzz/src/graph/gsubgpos-context.hh new file mode 100644 index 00000000..9fe9662e --- /dev/null +++ b/libs/harfbuzz/src/graph/gsubgpos-context.hh @@ -0,0 +1,61 @@ +/* + * Copyright © 2022 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): Garret Rieger + */ + +#include "graph.hh" +#include "../hb-ot-layout-gsubgpos.hh" + +#ifndef GRAPH_GSUBGPOS_CONTEXT_HH +#define GRAPH_GSUBGPOS_CONTEXT_HH + +namespace graph { + +struct Lookup; + +struct gsubgpos_graph_context_t +{ + hb_tag_t table_tag; + graph_t& graph; + unsigned lookup_list_index; + hb_hashmap_t lookups; + + + HB_INTERNAL gsubgpos_graph_context_t (hb_tag_t table_tag_, + graph_t& graph_); + + HB_INTERNAL unsigned create_node (unsigned size); + + void add_buffer (char* buffer) + { + graph.add_buffer (buffer); + } + + private: + HB_INTERNAL unsigned num_non_ext_subtables (); +}; + +} + +#endif // GRAPH_GSUBGPOS_CONTEXT diff --git a/libs/harfbuzz/src/graph/gsubgpos-graph.hh b/libs/harfbuzz/src/graph/gsubgpos-graph.hh new file mode 100644 index 00000000..e8d5bef9 --- /dev/null +++ b/libs/harfbuzz/src/graph/gsubgpos-graph.hh @@ -0,0 +1,414 @@ +/* + * Copyright © 2022 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): Garret Rieger + */ + +#include "graph.hh" +#include "../hb-ot-layout-gsubgpos.hh" +#include "../OT/Layout/GSUB/ExtensionSubst.hh" +#include "gsubgpos-context.hh" +#include "pairpos-graph.hh" +#include "markbasepos-graph.hh" + +#ifndef GRAPH_GSUBGPOS_GRAPH_HH +#define GRAPH_GSUBGPOS_GRAPH_HH + +namespace graph { + +struct Lookup; + +template +struct ExtensionFormat1 : public OT::ExtensionFormat1 +{ + void reset(unsigned type) + { + this->format = 1; + this->extensionLookupType = type; + this->extensionOffset = 0; + } + + bool sanitize (graph_t::vertex_t& vertex) const + { + int64_t vertex_len = vertex.obj.tail - vertex.obj.head; + return vertex_len >= OT::ExtensionFormat1::static_size; + } + + unsigned get_lookup_type () const + { + return this->extensionLookupType; + } + + unsigned get_subtable_index (graph_t& graph, unsigned this_index) const + { + return graph.index_for_offset (this_index, &this->extensionOffset); + } +}; + +struct Lookup : public OT::Lookup +{ + unsigned number_of_subtables () const + { + return subTable.len; + } + + bool sanitize (graph_t::vertex_t& vertex) const + { + int64_t vertex_len = vertex.obj.tail - vertex.obj.head; + if (vertex_len < OT::Lookup::min_size) return false; + return vertex_len >= this->get_size (); + } + + bool is_extension (hb_tag_t table_tag) const + { + return lookupType == extension_type (table_tag); + } + + bool make_extension (gsubgpos_graph_context_t& c, + unsigned this_index) + { + unsigned type = lookupType; + unsigned ext_type = extension_type (c.table_tag); + if (!ext_type || is_extension (c.table_tag)) + { + // NOOP + return true; + } + + DEBUG_MSG (SUBSET_REPACK, nullptr, + "Promoting lookup type %u (obj %u) to extension.", + type, + this_index); + + for (unsigned i = 0; i < subTable.len; i++) + { + unsigned subtable_index = c.graph.index_for_offset (this_index, &subTable[i]); + if (!make_subtable_extension (c, + this_index, + subtable_index)) + return false; + } + + lookupType = ext_type; + return true; + } + + bool split_subtables_if_needed (gsubgpos_graph_context_t& c, + unsigned this_index) + { + unsigned type = lookupType; + bool is_ext = is_extension (c.table_tag); + + if (c.table_tag != HB_OT_TAG_GPOS) + return true; + + if (!is_ext && + type != OT::Layout::GPOS_impl::PosLookupSubTable::Type::Pair && + type != OT::Layout::GPOS_impl::PosLookupSubTable::Type::MarkBase) + return true; + + hb_vector_t>> all_new_subtables; + for (unsigned i = 0; i < subTable.len; i++) + { + unsigned subtable_index = c.graph.index_for_offset (this_index, &subTable[i]); + unsigned parent_index = this_index; + if (is_ext) { + unsigned ext_subtable_index = subtable_index; + parent_index = ext_subtable_index; + ExtensionFormat1* extension = + (ExtensionFormat1*) + c.graph.object (ext_subtable_index).head; + if (!extension || !extension->sanitize (c.graph.vertices_[ext_subtable_index])) + continue; + + subtable_index = extension->get_subtable_index (c.graph, ext_subtable_index); + type = extension->get_lookup_type (); + if (type != OT::Layout::GPOS_impl::PosLookupSubTable::Type::Pair + && type != OT::Layout::GPOS_impl::PosLookupSubTable::Type::MarkBase) + continue; + } + + hb_vector_t new_sub_tables; + switch (type) + { + case 2: + new_sub_tables = split_subtable (c, parent_index, subtable_index); break; + case 4: + new_sub_tables = split_subtable (c, parent_index, subtable_index); break; + default: + break; + } + if (new_sub_tables.in_error ()) return false; + if (!new_sub_tables) continue; + hb_pair_t>* entry = all_new_subtables.push (); + entry->first = i; + entry->second = std::move (new_sub_tables); + } + + if (all_new_subtables) { + add_sub_tables (c, this_index, type, all_new_subtables); + } + + return true; + } + + template + hb_vector_t split_subtable (gsubgpos_graph_context_t& c, + unsigned parent_idx, + unsigned objidx) + { + T* sub_table = (T*) c.graph.object (objidx).head; + if (!sub_table || !sub_table->sanitize (c.graph.vertices_[objidx])) + return hb_vector_t (); + + return sub_table->split_subtables (c, parent_idx, objidx); + } + + void add_sub_tables (gsubgpos_graph_context_t& c, + unsigned this_index, + unsigned type, + hb_vector_t>>& subtable_ids) + { + bool is_ext = is_extension (c.table_tag); + auto& v = c.graph.vertices_[this_index]; + fix_existing_subtable_links (c, this_index, subtable_ids); + + unsigned new_subtable_count = 0; + for (const auto& p : subtable_ids) + new_subtable_count += p.second.length; + + size_t new_size = v.table_size () + + new_subtable_count * OT::Offset16::static_size; + char* buffer = (char*) hb_calloc (1, new_size); + c.add_buffer (buffer); + memcpy (buffer, v.obj.head, v.table_size()); + + v.obj.head = buffer; + v.obj.tail = buffer + new_size; + + Lookup* new_lookup = (Lookup*) buffer; + + unsigned shift = 0; + new_lookup->subTable.len = subTable.len + new_subtable_count; + for (const auto& p : subtable_ids) + { + unsigned offset_index = p.first + shift + 1; + shift += p.second.length; + + for (unsigned subtable_id : p.second) + { + if (is_ext) + { + unsigned ext_id = create_extension_subtable (c, subtable_id, type); + c.graph.vertices_[subtable_id].parents.push (ext_id); + subtable_id = ext_id; + } + + auto* link = v.obj.real_links.push (); + link->width = 2; + link->objidx = subtable_id; + link->position = (char*) &new_lookup->subTable[offset_index++] - + (char*) new_lookup; + c.graph.vertices_[subtable_id].parents.push (this_index); + } + } + + // Repacker sort order depends on link order, which we've messed up so resort it. + v.obj.real_links.qsort (); + + // The head location of the lookup has changed, invalidating the lookups map entry + // in the context. Update the map. + c.lookups.set (this_index, new_lookup); + } + + void fix_existing_subtable_links (gsubgpos_graph_context_t& c, + unsigned this_index, + hb_vector_t>>& subtable_ids) + { + auto& v = c.graph.vertices_[this_index]; + Lookup* lookup = (Lookup*) v.obj.head; + + unsigned shift = 0; + for (const auto& p : subtable_ids) + { + unsigned insert_index = p.first + shift; + unsigned pos_offset = p.second.length * OT::Offset16::static_size; + unsigned insert_offset = (char*) &lookup->subTable[insert_index] - (char*) lookup; + shift += p.second.length; + + for (auto& l : v.obj.all_links_writer ()) + { + if (l.position > insert_offset) l.position += pos_offset; + } + } + } + + unsigned create_extension_subtable (gsubgpos_graph_context_t& c, + unsigned subtable_index, + unsigned type) + { + unsigned extension_size = OT::ExtensionFormat1::static_size; + + unsigned ext_index = c.create_node (extension_size); + if (ext_index == (unsigned) -1) + return -1; + + auto& ext_vertex = c.graph.vertices_[ext_index]; + ExtensionFormat1* extension = + (ExtensionFormat1*) ext_vertex.obj.head; + extension->reset (type); + + // Make extension point at the subtable. + auto* l = ext_vertex.obj.real_links.push (); + + l->width = 4; + l->objidx = subtable_index; + l->position = 4; + + return ext_index; + } + + bool make_subtable_extension (gsubgpos_graph_context_t& c, + unsigned lookup_index, + unsigned subtable_index) + { + unsigned type = lookupType; + + unsigned ext_index = create_extension_subtable(c, subtable_index, type); + if (ext_index == (unsigned) -1) + return false; + + auto& lookup_vertex = c.graph.vertices_[lookup_index]; + for (auto& l : lookup_vertex.obj.real_links.writer ()) + { + if (l.objidx == subtable_index) + // Change lookup to point at the extension. + l.objidx = ext_index; + } + + // Make extension point at the subtable. + auto& ext_vertex = c.graph.vertices_[ext_index]; + auto& subtable_vertex = c.graph.vertices_[subtable_index]; + ext_vertex.parents.push (lookup_index); + subtable_vertex.remap_parent (lookup_index, ext_index); + + return true; + } + + private: + unsigned extension_type (hb_tag_t table_tag) const + { + switch (table_tag) + { + case HB_OT_TAG_GPOS: return 9; + case HB_OT_TAG_GSUB: return 7; + default: return 0; + } + } +}; + +template +struct LookupList : public OT::LookupList +{ + bool sanitize (const graph_t::vertex_t& vertex) const + { + int64_t vertex_len = vertex.obj.tail - vertex.obj.head; + if (vertex_len < OT::LookupList::min_size) return false; + return vertex_len >= OT::LookupList::item_size * this->len; + } +}; + +struct GSTAR : public OT::GSUBGPOS +{ + static GSTAR* graph_to_gstar (graph_t& graph) + { + const auto& r = graph.root (); + + GSTAR* gstar = (GSTAR*) r.obj.head; + if (!gstar || !gstar->sanitize (r)) + return nullptr; + + return gstar; + } + + const void* get_lookup_list_field_offset () const + { + switch (u.version.major) { + case 1: return u.version1.get_lookup_list_offset (); +#ifndef HB_NO_BORING_EXPANSION + case 2: return u.version2.get_lookup_list_offset (); +#endif + default: return 0; + } + } + + bool sanitize (const graph_t::vertex_t& vertex) + { + int64_t len = vertex.obj.tail - vertex.obj.head; + if (len < OT::GSUBGPOS::min_size) return false; + return len >= get_size (); + } + + void find_lookups (graph_t& graph, + hb_hashmap_t& lookups /* OUT */) + { + switch (u.version.major) { + case 1: find_lookups (graph, lookups); break; +#ifndef HB_NO_BORING_EXPANSION + case 2: find_lookups (graph, lookups); break; +#endif + } + } + + unsigned get_lookup_list_index (graph_t& graph) + { + return graph.index_for_offset (graph.root_idx (), + get_lookup_list_field_offset()); + } + + template + void find_lookups (graph_t& graph, + hb_hashmap_t& lookups /* OUT */) + { + unsigned lookup_list_idx = get_lookup_list_index (graph); + const LookupList* lookupList = + (const LookupList*) graph.object (lookup_list_idx).head; + if (!lookupList || !lookupList->sanitize (graph.vertices_[lookup_list_idx])) + return; + + for (unsigned i = 0; i < lookupList->len; i++) + { + unsigned lookup_idx = graph.index_for_offset (lookup_list_idx, &(lookupList->arrayZ[i])); + Lookup* lookup = (Lookup*) graph.object (lookup_idx).head; + if (!lookup || !lookup->sanitize (graph.vertices_[lookup_idx])) continue; + lookups.set (lookup_idx, lookup); + } + } +}; + + + + +} + +#endif /* GRAPH_GSUBGPOS_GRAPH_HH */ diff --git a/libs/harfbuzz/src/graph/markbasepos-graph.hh b/libs/harfbuzz/src/graph/markbasepos-graph.hh new file mode 100644 index 00000000..e42a6042 --- /dev/null +++ b/libs/harfbuzz/src/graph/markbasepos-graph.hh @@ -0,0 +1,510 @@ +/* + * Copyright © 2022 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): Garret Rieger + */ + +#ifndef GRAPH_MARKBASEPOS_GRAPH_HH +#define GRAPH_MARKBASEPOS_GRAPH_HH + +#include "split-helpers.hh" +#include "coverage-graph.hh" +#include "../OT/Layout/GPOS/MarkBasePos.hh" +#include "../OT/Layout/GPOS/PosLookupSubTable.hh" + +namespace graph { + +struct AnchorMatrix : public OT::Layout::GPOS_impl::AnchorMatrix +{ + bool sanitize (graph_t::vertex_t& vertex, unsigned class_count) const + { + int64_t vertex_len = vertex.obj.tail - vertex.obj.head; + if (vertex_len < AnchorMatrix::min_size) return false; + + return vertex_len >= AnchorMatrix::min_size + + OT::Offset16::static_size * class_count * this->rows; + } + + bool shrink (gsubgpos_graph_context_t& c, + unsigned this_index, + unsigned old_class_count, + unsigned new_class_count) + { + if (new_class_count >= old_class_count) return false; + auto& o = c.graph.vertices_[this_index].obj; + unsigned base_count = rows; + o.tail = o.head + + AnchorMatrix::min_size + + OT::Offset16::static_size * base_count * new_class_count; + + // Reposition links into the new indexing scheme. + for (auto& link : o.real_links.writer ()) + { + unsigned index = (link.position - 2) / 2; + unsigned base = index / old_class_count; + unsigned klass = index % old_class_count; + if (klass >= new_class_count) + // should have already been removed + return false; + + unsigned new_index = base * new_class_count + klass; + + link.position = (char*) &(this->matrixZ[new_index]) - (char*) this; + } + + return true; + } + + unsigned clone (gsubgpos_graph_context_t& c, + unsigned this_index, + unsigned start, + unsigned end, + unsigned class_count) + { + unsigned base_count = rows; + unsigned new_class_count = end - start; + unsigned size = AnchorMatrix::min_size + + OT::Offset16::static_size * new_class_count * rows; + unsigned prime_id = c.create_node (size); + if (prime_id == (unsigned) -1) return -1; + AnchorMatrix* prime = (AnchorMatrix*) c.graph.object (prime_id).head; + prime->rows = base_count; + + auto& o = c.graph.vertices_[this_index].obj; + int num_links = o.real_links.length; + for (int i = 0; i < num_links; i++) + { + const auto& link = o.real_links[i]; + unsigned old_index = (link.position - 2) / OT::Offset16::static_size; + unsigned klass = old_index % class_count; + if (klass < start || klass >= end) continue; + + unsigned base = old_index / class_count; + unsigned new_klass = klass - start; + unsigned new_index = base * new_class_count + new_klass; + + + unsigned child_idx = link.objidx; + c.graph.add_link (&(prime->matrixZ[new_index]), + prime_id, + child_idx); + + auto& child = c.graph.vertices_[child_idx]; + child.remove_parent (this_index); + + o.real_links.remove (i); + num_links--; + i--; + } + + return prime_id; + } +}; + +struct MarkArray : public OT::Layout::GPOS_impl::MarkArray +{ + bool sanitize (graph_t::vertex_t& vertex) const + { + int64_t vertex_len = vertex.obj.tail - vertex.obj.head; + unsigned min_size = MarkArray::min_size; + if (vertex_len < min_size) return false; + + return vertex_len >= get_size (); + } + + bool shrink (gsubgpos_graph_context_t& c, + const hb_hashmap_t& mark_array_links, + unsigned this_index, + unsigned new_class_count) + { + auto& o = c.graph.vertices_[this_index].obj; + for (const auto& link : o.real_links) + c.graph.vertices_[link.objidx].remove_parent (this_index); + o.real_links.reset (); + + unsigned new_index = 0; + for (const auto& record : this->iter ()) + { + unsigned klass = record.klass; + if (klass >= new_class_count) continue; + + (*this)[new_index].klass = klass; + unsigned position = (char*) &record.markAnchor - (char*) this; + unsigned* objidx; + if (!mark_array_links.has (position, &objidx)) + { + new_index++; + continue; + } + + c.graph.add_link (&(*this)[new_index].markAnchor, this_index, *objidx); + new_index++; + } + + this->len = new_index; + o.tail = o.head + MarkArray::min_size + + OT::Layout::GPOS_impl::MarkRecord::static_size * new_index; + return true; + } + + unsigned clone (gsubgpos_graph_context_t& c, + unsigned this_index, + const hb_hashmap_t& pos_to_index, + hb_set_t& marks, + unsigned start_class) + { + unsigned size = MarkArray::min_size + + OT::Layout::GPOS_impl::MarkRecord::static_size * + marks.get_population (); + unsigned prime_id = c.create_node (size); + if (prime_id == (unsigned) -1) return -1; + MarkArray* prime = (MarkArray*) c.graph.object (prime_id).head; + prime->len = marks.get_population (); + + + unsigned i = 0; + for (hb_codepoint_t mark : marks) + { + (*prime)[i].klass = (*this)[mark].klass - start_class; + unsigned offset_pos = (char*) &((*this)[mark].markAnchor) - (char*) this; + unsigned* anchor_index; + if (pos_to_index.has (offset_pos, &anchor_index)) + c.graph.move_child (this_index, + &((*this)[mark].markAnchor), + prime_id, + &((*prime)[i].markAnchor)); + + i++; + } + + return prime_id; + } +}; + +struct MarkBasePosFormat1 : public OT::Layout::GPOS_impl::MarkBasePosFormat1_2 +{ + bool sanitize (graph_t::vertex_t& vertex) const + { + int64_t vertex_len = vertex.obj.tail - vertex.obj.head; + return vertex_len >= MarkBasePosFormat1::static_size; + } + + hb_vector_t split_subtables (gsubgpos_graph_context_t& c, + unsigned parent_index, + unsigned this_index) + { + hb_set_t visited; + + const unsigned base_coverage_id = c.graph.index_for_offset (this_index, &baseCoverage); + const unsigned base_size = + OT::Layout::GPOS_impl::PairPosFormat1_3::min_size + + MarkArray::min_size + + AnchorMatrix::min_size + + c.graph.vertices_[base_coverage_id].table_size (); + + hb_vector_t class_to_info = get_class_info (c, this_index); + + unsigned class_count = classCount; + auto base_array = c.graph.as_table (this_index, + &baseArray, + class_count); + if (!base_array) return hb_vector_t (); + unsigned base_count = base_array.table->rows; + + unsigned partial_coverage_size = 4; + unsigned accumulated = base_size; + hb_vector_t split_points; + + for (unsigned klass = 0; klass < class_count; klass++) + { + class_info_t& info = class_to_info[klass]; + partial_coverage_size += OT::HBUINT16::static_size * info.marks.get_population (); + unsigned accumulated_delta = + OT::Layout::GPOS_impl::MarkRecord::static_size * info.marks.get_population () + + OT::Offset16::static_size * base_count; + + for (unsigned objidx : info.child_indices) + accumulated_delta += c.graph.find_subgraph_size (objidx, visited); + + accumulated += accumulated_delta; + unsigned total = accumulated + partial_coverage_size; + + if (total >= (1 << 16)) + { + split_points.push (klass); + accumulated = base_size + accumulated_delta; + partial_coverage_size = 4 + OT::HBUINT16::static_size * info.marks.get_population (); + visited.clear (); // node sharing isn't allowed between splits. + } + } + + + const unsigned mark_array_id = c.graph.index_for_offset (this_index, &markArray); + split_context_t split_context { + c, + this, + c.graph.duplicate_if_shared (parent_index, this_index), + std::move (class_to_info), + c.graph.vertices_[mark_array_id].position_to_index_map (), + }; + + return actuate_subtable_split (split_context, split_points); + } + + private: + + struct class_info_t { + hb_set_t marks; + hb_vector_t child_indices; + }; + + struct split_context_t { + gsubgpos_graph_context_t& c; + MarkBasePosFormat1* thiz; + unsigned this_index; + hb_vector_t class_to_info; + hb_hashmap_t mark_array_links; + + hb_set_t marks_for (unsigned start, unsigned end) + { + hb_set_t marks; + for (unsigned klass = start; klass < end; klass++) + { + + class_to_info[klass].marks.iter () + | hb_sink (marks) + ; + } + return marks; + } + + unsigned original_count () + { + return thiz->classCount; + } + + unsigned clone_range (unsigned start, unsigned end) + { + return thiz->clone_range (*this, this->this_index, start, end); + } + + bool shrink (unsigned count) + { + return thiz->shrink (*this, this->this_index, count); + } + }; + + hb_vector_t get_class_info (gsubgpos_graph_context_t& c, + unsigned this_index) + { + hb_vector_t class_to_info; + + unsigned class_count= classCount; + class_to_info.resize (class_count); + + auto mark_array = c.graph.as_table (this_index, &markArray); + if (!mark_array) return hb_vector_t (); + unsigned mark_count = mark_array.table->len; + for (unsigned mark = 0; mark < mark_count; mark++) + { + unsigned klass = (*mark_array.table)[mark].get_class (); + class_to_info[klass].marks.add (mark); + } + + for (const auto& link : mark_array.vertex->obj.real_links) + { + unsigned mark = (link.position - 2) / + OT::Layout::GPOS_impl::MarkRecord::static_size; + unsigned klass = (*mark_array.table)[mark].get_class (); + class_to_info[klass].child_indices.push (link.objidx); + } + + unsigned base_array_id = + c.graph.index_for_offset (this_index, &baseArray); + auto& base_array_v = c.graph.vertices_[base_array_id]; + + for (const auto& link : base_array_v.obj.real_links) + { + unsigned index = (link.position - 2) / OT::Offset16::static_size; + unsigned klass = index % class_count; + class_to_info[klass].child_indices.push (link.objidx); + } + + return class_to_info; + } + + bool shrink (split_context_t& sc, + unsigned this_index, + unsigned count) + { + DEBUG_MSG (SUBSET_REPACK, nullptr, + " Shrinking MarkBasePosFormat1 (%u) to [0, %u).", + this_index, + count); + + unsigned old_count = classCount; + if (count >= old_count) + return true; + + classCount = count; + + auto mark_coverage = sc.c.graph.as_mutable_table (this_index, + &markCoverage); + if (!mark_coverage) return false; + hb_set_t marks = sc.marks_for (0, count); + auto new_coverage = + + hb_zip (hb_range (), mark_coverage.table->iter ()) + | hb_filter (marks, hb_first) + | hb_map_retains_sorting (hb_second) + ; + if (!Coverage::make_coverage (sc.c, + new_coverage, + mark_coverage.index, + 4 + 2 * marks.get_population ())) + return false; + + + auto base_array = sc.c.graph.as_mutable_table (this_index, + &baseArray, + old_count); + if (!base_array || !base_array.table->shrink (sc.c, + base_array.index, + old_count, + count)) + return false; + + auto mark_array = sc.c.graph.as_mutable_table (this_index, + &markArray); + if (!mark_array || !mark_array.table->shrink (sc.c, + sc.mark_array_links, + mark_array.index, + count)) + return false; + + return true; + } + + // Create a new MarkBasePos that has all of the data for classes from [start, end). + unsigned clone_range (split_context_t& sc, + unsigned this_index, + unsigned start, unsigned end) const + { + DEBUG_MSG (SUBSET_REPACK, nullptr, + " Cloning MarkBasePosFormat1 (%u) range [%u, %u).", this_index, start, end); + + graph_t& graph = sc.c.graph; + unsigned prime_size = OT::Layout::GPOS_impl::MarkBasePosFormat1_2::static_size; + + unsigned prime_id = sc.c.create_node (prime_size); + if (prime_id == (unsigned) -1) return -1; + + MarkBasePosFormat1* prime = (MarkBasePosFormat1*) graph.object (prime_id).head; + prime->format = this->format; + unsigned new_class_count = end - start; + prime->classCount = new_class_count; + + unsigned base_coverage_id = + graph.index_for_offset (sc.this_index, &baseCoverage); + graph.add_link (&(prime->baseCoverage), prime_id, base_coverage_id); + graph.duplicate (prime_id, base_coverage_id); + + auto mark_coverage = sc.c.graph.as_table (this_index, + &markCoverage); + if (!mark_coverage) return false; + hb_set_t marks = sc.marks_for (start, end); + auto new_coverage = + + hb_zip (hb_range (), mark_coverage.table->iter ()) + | hb_filter (marks, hb_first) + | hb_map_retains_sorting (hb_second) + ; + if (!Coverage::add_coverage (sc.c, + prime_id, + 2, + + new_coverage, + marks.get_population () * 2 + 4)) + return -1; + + auto mark_array = + graph.as_table (sc.this_index, &markArray); + if (!mark_array) return -1; + unsigned new_mark_array = + mark_array.table->clone (sc.c, + mark_array.index, + sc.mark_array_links, + marks, + start); + graph.add_link (&(prime->markArray), prime_id, new_mark_array); + + unsigned class_count = classCount; + auto base_array = + graph.as_table (sc.this_index, &baseArray, class_count); + if (!base_array) return -1; + unsigned new_base_array = + base_array.table->clone (sc.c, + base_array.index, + start, end, this->classCount); + graph.add_link (&(prime->baseArray), prime_id, new_base_array); + + return prime_id; + } +}; + + +struct MarkBasePos : public OT::Layout::GPOS_impl::MarkBasePos +{ + hb_vector_t split_subtables (gsubgpos_graph_context_t& c, + unsigned parent_index, + unsigned this_index) + { + switch (u.format) { + case 1: + return ((MarkBasePosFormat1*)(&u.format1))->split_subtables (c, parent_index, this_index); +#ifndef HB_NO_BORING_EXPANSION + case 2: HB_FALLTHROUGH; + // Don't split 24bit PairPos's. +#endif + default: + return hb_vector_t (); + } + } + + bool sanitize (graph_t::vertex_t& vertex) const + { + int64_t vertex_len = vertex.obj.tail - vertex.obj.head; + if (vertex_len < u.format.get_size ()) return false; + + switch (u.format) { + case 1: + return ((MarkBasePosFormat1*)(&u.format1))->sanitize (vertex); +#ifndef HB_NO_BORING_EXPANSION + case 2: HB_FALLTHROUGH; +#endif + default: + // We don't handle format 3 and 4 here. + return false; + } + } +}; + + +} + +#endif // GRAPH_MARKBASEPOS_GRAPH_HH diff --git a/libs/harfbuzz/src/graph/pairpos-graph.hh b/libs/harfbuzz/src/graph/pairpos-graph.hh new file mode 100644 index 00000000..8040778e --- /dev/null +++ b/libs/harfbuzz/src/graph/pairpos-graph.hh @@ -0,0 +1,647 @@ +/* + * Copyright © 2022 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): Garret Rieger + */ + +#ifndef GRAPH_PAIRPOS_GRAPH_HH +#define GRAPH_PAIRPOS_GRAPH_HH + +#include "split-helpers.hh" +#include "coverage-graph.hh" +#include "classdef-graph.hh" +#include "../OT/Layout/GPOS/PairPos.hh" +#include "../OT/Layout/GPOS/PosLookupSubTable.hh" + +namespace graph { + +struct PairPosFormat1 : public OT::Layout::GPOS_impl::PairPosFormat1_3 +{ + bool sanitize (graph_t::vertex_t& vertex) const + { + int64_t vertex_len = vertex.obj.tail - vertex.obj.head; + unsigned min_size = OT::Layout::GPOS_impl::PairPosFormat1_3::min_size; + if (vertex_len < min_size) return false; + + return vertex_len >= + min_size + pairSet.get_size () - pairSet.len.get_size(); + } + + hb_vector_t split_subtables (gsubgpos_graph_context_t& c, + unsigned parent_index, + unsigned this_index) + { + hb_set_t visited; + + const unsigned coverage_id = c.graph.index_for_offset (this_index, &coverage); + const unsigned coverage_size = c.graph.vertices_[coverage_id].table_size (); + const unsigned base_size = OT::Layout::GPOS_impl::PairPosFormat1_3::min_size; + + unsigned partial_coverage_size = 4; + unsigned accumulated = base_size; + hb_vector_t split_points; + for (unsigned i = 0; i < pairSet.len; i++) + { + unsigned pair_set_index = pair_set_graph_index (c, this_index, i); + unsigned accumulated_delta = + c.graph.find_subgraph_size (pair_set_index, visited) + + SmallTypes::size; // for PairSet offset. + partial_coverage_size += OT::HBUINT16::static_size; + + accumulated += accumulated_delta; + unsigned total = accumulated + hb_min (partial_coverage_size, coverage_size); + + if (total >= (1 << 16)) + { + split_points.push (i); + accumulated = base_size + accumulated_delta; + partial_coverage_size = 6; + visited.clear (); // node sharing isn't allowed between splits. + } + } + + split_context_t split_context { + c, + this, + c.graph.duplicate_if_shared (parent_index, this_index), + }; + + return actuate_subtable_split (split_context, split_points); + } + + private: + + struct split_context_t { + gsubgpos_graph_context_t& c; + PairPosFormat1* thiz; + unsigned this_index; + + unsigned original_count () + { + return thiz->pairSet.len; + } + + unsigned clone_range (unsigned start, unsigned end) + { + return thiz->clone_range (this->c, this->this_index, start, end); + } + + bool shrink (unsigned count) + { + return thiz->shrink (this->c, this->this_index, count); + } + }; + + bool shrink (gsubgpos_graph_context_t& c, + unsigned this_index, + unsigned count) + { + DEBUG_MSG (SUBSET_REPACK, nullptr, + " Shrinking PairPosFormat1 (%u) to [0, %u).", + this_index, + count); + unsigned old_count = pairSet.len; + if (count >= old_count) + return true; + + pairSet.len = count; + c.graph.vertices_[this_index].obj.tail -= (old_count - count) * SmallTypes::size; + + auto coverage = c.graph.as_mutable_table (this_index, &this->coverage); + if (!coverage) return false; + + unsigned coverage_size = coverage.vertex->table_size (); + auto new_coverage = + + hb_zip (coverage.table->iter (), hb_range ()) + | hb_filter ([&] (hb_pair_t p) { + return p.second < count; + }) + | hb_map_retains_sorting (hb_first) + ; + + return Coverage::make_coverage (c, new_coverage, coverage.index, coverage_size); + } + + // Create a new PairPos including PairSet's from start (inclusive) to end (exclusive). + // Returns object id of the new object. + unsigned clone_range (gsubgpos_graph_context_t& c, + unsigned this_index, + unsigned start, unsigned end) const + { + DEBUG_MSG (SUBSET_REPACK, nullptr, + " Cloning PairPosFormat1 (%u) range [%u, %u).", this_index, start, end); + + unsigned num_pair_sets = end - start; + unsigned prime_size = OT::Layout::GPOS_impl::PairPosFormat1_3::min_size + + num_pair_sets * SmallTypes::size; + + unsigned pair_pos_prime_id = c.create_node (prime_size); + if (pair_pos_prime_id == (unsigned) -1) return -1; + + PairPosFormat1* pair_pos_prime = (PairPosFormat1*) c.graph.object (pair_pos_prime_id).head; + pair_pos_prime->format = this->format; + pair_pos_prime->valueFormat[0] = this->valueFormat[0]; + pair_pos_prime->valueFormat[1] = this->valueFormat[1]; + pair_pos_prime->pairSet.len = num_pair_sets; + + for (unsigned i = start; i < end; i++) + { + c.graph.move_child<> (this_index, + &pairSet[i], + pair_pos_prime_id, + &pair_pos_prime->pairSet[i - start]); + } + + unsigned coverage_id = c.graph.index_for_offset (this_index, &coverage); + if (!Coverage::clone_coverage (c, + coverage_id, + pair_pos_prime_id, + 2, + start, end)) + return -1; + + return pair_pos_prime_id; + } + + + + unsigned pair_set_graph_index (gsubgpos_graph_context_t& c, unsigned this_index, unsigned i) const + { + return c.graph.index_for_offset (this_index, &pairSet[i]); + } +}; + +struct PairPosFormat2 : public OT::Layout::GPOS_impl::PairPosFormat2_4 +{ + bool sanitize (graph_t::vertex_t& vertex) const + { + size_t vertex_len = vertex.table_size (); + unsigned min_size = OT::Layout::GPOS_impl::PairPosFormat2_4::min_size; + if (vertex_len < min_size) return false; + + const unsigned class1_count = class1Count; + return vertex_len >= + min_size + class1_count * get_class1_record_size (); + } + + hb_vector_t split_subtables (gsubgpos_graph_context_t& c, + unsigned parent_index, + unsigned this_index) + { + const unsigned base_size = OT::Layout::GPOS_impl::PairPosFormat2_4::min_size; + const unsigned class_def_2_size = size_of (c, this_index, &classDef2); + const Coverage* coverage = get_coverage (c, this_index); + const ClassDef* class_def_1 = get_class_def_1 (c, this_index); + auto gid_and_class = + + coverage->iter () + | hb_map_retains_sorting ([&] (hb_codepoint_t gid) { + return hb_pair_t (gid, class_def_1->get_class (gid)); + }) + ; + class_def_size_estimator_t estimator (gid_and_class); + + const unsigned class1_count = class1Count; + const unsigned class2_count = class2Count; + const unsigned class1_record_size = get_class1_record_size (); + + const unsigned value_1_len = valueFormat1.get_len (); + const unsigned value_2_len = valueFormat2.get_len (); + const unsigned total_value_len = value_1_len + value_2_len; + + unsigned accumulated = base_size; + unsigned coverage_size = 4; + unsigned class_def_1_size = 4; + unsigned max_coverage_size = coverage_size; + unsigned max_class_def_1_size = class_def_1_size; + + hb_vector_t split_points; + + hb_hashmap_t device_tables = get_all_device_tables (c, this_index); + hb_vector_t format1_device_table_indices = valueFormat1.get_device_table_indices (); + hb_vector_t format2_device_table_indices = valueFormat2.get_device_table_indices (); + bool has_device_tables = bool(format1_device_table_indices) || bool(format2_device_table_indices); + + hb_set_t visited; + for (unsigned i = 0; i < class1_count; i++) + { + unsigned accumulated_delta = class1_record_size; + coverage_size += estimator.incremental_coverage_size (i); + class_def_1_size += estimator.incremental_class_def_size (i); + max_coverage_size = hb_max (max_coverage_size, coverage_size); + max_class_def_1_size = hb_max (max_class_def_1_size, class_def_1_size); + + if (has_device_tables) { + for (unsigned j = 0; j < class2_count; j++) + { + unsigned value1_index = total_value_len * (class2_count * i + j); + unsigned value2_index = value1_index + value_1_len; + accumulated_delta += size_of_value_record_children (c, + device_tables, + format1_device_table_indices, + value1_index, + visited); + accumulated_delta += size_of_value_record_children (c, + device_tables, + format2_device_table_indices, + value2_index, + visited); + } + } + + accumulated += accumulated_delta; + unsigned total = accumulated + + coverage_size + class_def_1_size + class_def_2_size + // The largest object will pack last and can exceed the size limit. + - hb_max (hb_max (coverage_size, class_def_1_size), class_def_2_size); + if (total >= (1 << 16)) + { + split_points.push (i); + // split does not include i, so add the size for i when we reset the size counters. + accumulated = base_size + accumulated_delta; + coverage_size = 4 + estimator.incremental_coverage_size (i); + class_def_1_size = 4 + estimator.incremental_class_def_size (i); + visited.clear (); // node sharing isn't allowed between splits. + } + } + + split_context_t split_context { + c, + this, + c.graph.duplicate_if_shared (parent_index, this_index), + class1_record_size, + total_value_len, + value_1_len, + value_2_len, + max_coverage_size, + max_class_def_1_size, + device_tables, + format1_device_table_indices, + format2_device_table_indices + }; + + return actuate_subtable_split (split_context, split_points); + } + private: + + struct split_context_t + { + gsubgpos_graph_context_t& c; + PairPosFormat2* thiz; + unsigned this_index; + unsigned class1_record_size; + unsigned value_record_len; + unsigned value1_record_len; + unsigned value2_record_len; + unsigned max_coverage_size; + unsigned max_class_def_size; + + const hb_hashmap_t& device_tables; + const hb_vector_t& format1_device_table_indices; + const hb_vector_t& format2_device_table_indices; + + unsigned original_count () + { + return thiz->class1Count; + } + + unsigned clone_range (unsigned start, unsigned end) + { + return thiz->clone_range (*this, start, end); + } + + bool shrink (unsigned count) + { + return thiz->shrink (*this, count); + } + }; + + size_t get_class1_record_size () const + { + const size_t class2_count = class2Count; + return + class2_count * (valueFormat1.get_size () + valueFormat2.get_size ()); + } + + unsigned clone_range (split_context_t& split_context, + unsigned start, unsigned end) const + { + DEBUG_MSG (SUBSET_REPACK, nullptr, + " Cloning PairPosFormat2 (%u) range [%u, %u).", split_context.this_index, start, end); + + graph_t& graph = split_context.c.graph; + + unsigned num_records = end - start; + unsigned prime_size = OT::Layout::GPOS_impl::PairPosFormat2_4::min_size + + num_records * split_context.class1_record_size; + + unsigned pair_pos_prime_id = split_context.c.create_node (prime_size); + if (pair_pos_prime_id == (unsigned) -1) return -1; + + PairPosFormat2* pair_pos_prime = + (PairPosFormat2*) graph.object (pair_pos_prime_id).head; + pair_pos_prime->format = this->format; + pair_pos_prime->valueFormat1 = this->valueFormat1; + pair_pos_prime->valueFormat2 = this->valueFormat2; + pair_pos_prime->class1Count = num_records; + pair_pos_prime->class2Count = this->class2Count; + clone_class1_records (split_context, + pair_pos_prime_id, + start, + end); + + unsigned coverage_id = + graph.index_for_offset (split_context.this_index, &coverage); + unsigned class_def_1_id = + graph.index_for_offset (split_context.this_index, &classDef1); + auto& coverage_v = graph.vertices_[coverage_id]; + auto& class_def_1_v = graph.vertices_[class_def_1_id]; + Coverage* coverage_table = (Coverage*) coverage_v.obj.head; + ClassDef* class_def_1_table = (ClassDef*) class_def_1_v.obj.head; + if (!coverage_table + || !coverage_table->sanitize (coverage_v) + || !class_def_1_table + || !class_def_1_table->sanitize (class_def_1_v)) + return -1; + + auto klass_map = + + coverage_table->iter () + | hb_map_retains_sorting ([&] (hb_codepoint_t gid) { + return hb_pair_t (gid, class_def_1_table->get_class (gid)); + }) + | hb_filter ([&] (hb_codepoint_t klass) { + return klass >= start && klass < end; + }, hb_second) + | hb_map_retains_sorting ([&] (hb_pair_t gid_and_class) { + // Classes must be from 0...N so subtract start + return hb_pair_t (gid_and_class.first, gid_and_class.second - start); + }) + ; + + if (!Coverage::add_coverage (split_context.c, + pair_pos_prime_id, + 2, + + klass_map | hb_map_retains_sorting (hb_first), + split_context.max_coverage_size)) + return -1; + + // classDef1 + if (!ClassDef::add_class_def (split_context.c, + pair_pos_prime_id, + 8, + + klass_map, + split_context.max_class_def_size)) + return -1; + + // classDef2 + unsigned class_def_2_id = + graph.index_for_offset (split_context.this_index, &classDef2); + auto* class_def_link = graph.vertices_[pair_pos_prime_id].obj.real_links.push (); + class_def_link->width = SmallTypes::size; + class_def_link->objidx = class_def_2_id; + class_def_link->position = 10; + graph.vertices_[class_def_2_id].parents.push (pair_pos_prime_id); + graph.duplicate (pair_pos_prime_id, class_def_2_id); + + return pair_pos_prime_id; + } + + void clone_class1_records (split_context_t& split_context, + unsigned pair_pos_prime_id, + unsigned start, unsigned end) const + { + PairPosFormat2* pair_pos_prime = + (PairPosFormat2*) split_context.c.graph.object (pair_pos_prime_id).head; + + char* start_addr = ((char*)&values[0]) + start * split_context.class1_record_size; + unsigned num_records = end - start; + memcpy (&pair_pos_prime->values[0], + start_addr, + num_records * split_context.class1_record_size); + + if (!split_context.format1_device_table_indices + && !split_context.format2_device_table_indices) + // No device tables to move over. + return; + + unsigned class2_count = class2Count; + for (unsigned i = start; i < end; i++) + { + for (unsigned j = 0; j < class2_count; j++) + { + unsigned value1_index = split_context.value_record_len * (class2_count * i + j); + unsigned value2_index = value1_index + split_context.value1_record_len; + + unsigned new_value1_index = split_context.value_record_len * (class2_count * (i - start) + j); + unsigned new_value2_index = new_value1_index + split_context.value1_record_len; + + transfer_device_tables (split_context, + pair_pos_prime_id, + split_context.format1_device_table_indices, + value1_index, + new_value1_index); + + transfer_device_tables (split_context, + pair_pos_prime_id, + split_context.format2_device_table_indices, + value2_index, + new_value2_index); + } + } + } + + void transfer_device_tables (split_context_t& split_context, + unsigned pair_pos_prime_id, + const hb_vector_t& device_table_indices, + unsigned old_value_record_index, + unsigned new_value_record_index) const + { + PairPosFormat2* pair_pos_prime = + (PairPosFormat2*) split_context.c.graph.object (pair_pos_prime_id).head; + + for (unsigned i : device_table_indices) + { + OT::Offset16* record = (OT::Offset16*) &values[old_value_record_index + i]; + unsigned record_position = ((char*) record) - ((char*) this); + if (!split_context.device_tables.has (record_position)) continue; + + split_context.c.graph.move_child ( + split_context.this_index, + record, + pair_pos_prime_id, + (OT::Offset16*) &pair_pos_prime->values[new_value_record_index + i]); + } + } + + bool shrink (split_context_t& split_context, + unsigned count) + { + DEBUG_MSG (SUBSET_REPACK, nullptr, + " Shrinking PairPosFormat2 (%u) to [0, %u).", + split_context.this_index, + count); + unsigned old_count = class1Count; + if (count >= old_count) + return true; + + graph_t& graph = split_context.c.graph; + class1Count = count; + graph.vertices_[split_context.this_index].obj.tail -= + (old_count - count) * split_context.class1_record_size; + + auto coverage = + graph.as_mutable_table (split_context.this_index, &this->coverage); + if (!coverage) return false; + + auto class_def_1 = + graph.as_mutable_table (split_context.this_index, &classDef1); + if (!class_def_1) return false; + + auto klass_map = + + coverage.table->iter () + | hb_map_retains_sorting ([&] (hb_codepoint_t gid) { + return hb_pair_t (gid, class_def_1.table->get_class (gid)); + }) + | hb_filter ([&] (hb_codepoint_t klass) { + return klass < count; + }, hb_second) + ; + + auto new_coverage = + klass_map | hb_map_retains_sorting (hb_first); + if (!Coverage::make_coverage (split_context.c, + + new_coverage, + coverage.index, + // existing ranges my not be kept, worst case size is a format 1 + // coverage table. + 4 + new_coverage.len() * 2)) + return false; + + return ClassDef::make_class_def (split_context.c, + + klass_map, + class_def_1.index, + class_def_1.vertex->table_size ()); + } + + hb_hashmap_t + get_all_device_tables (gsubgpos_graph_context_t& c, + unsigned this_index) const + { + const auto& v = c.graph.vertices_[this_index]; + return v.position_to_index_map (); + } + + const Coverage* get_coverage (gsubgpos_graph_context_t& c, + unsigned this_index) const + { + unsigned coverage_id = c.graph.index_for_offset (this_index, &coverage); + auto& coverage_v = c.graph.vertices_[coverage_id]; + + Coverage* coverage_table = (Coverage*) coverage_v.obj.head; + if (!coverage_table || !coverage_table->sanitize (coverage_v)) + return &Null(Coverage); + return coverage_table; + } + + const ClassDef* get_class_def_1 (gsubgpos_graph_context_t& c, + unsigned this_index) const + { + unsigned class_def_1_id = c.graph.index_for_offset (this_index, &classDef1); + auto& class_def_1_v = c.graph.vertices_[class_def_1_id]; + + ClassDef* class_def_1_table = (ClassDef*) class_def_1_v.obj.head; + if (!class_def_1_table || !class_def_1_table->sanitize (class_def_1_v)) + return &Null(ClassDef); + return class_def_1_table; + } + + unsigned size_of_value_record_children (gsubgpos_graph_context_t& c, + const hb_hashmap_t& device_tables, + const hb_vector_t device_table_indices, + unsigned value_record_index, + hb_set_t& visited) + { + unsigned size = 0; + for (unsigned i : device_table_indices) + { + OT::Layout::GPOS_impl::Value* record = &values[value_record_index + i]; + unsigned record_position = ((char*) record) - ((char*) this); + unsigned* obj_idx; + if (!device_tables.has (record_position, &obj_idx)) continue; + size += c.graph.find_subgraph_size (*obj_idx, visited); + } + return size; + } + + unsigned size_of (gsubgpos_graph_context_t& c, + unsigned this_index, + const void* offset) const + { + const unsigned id = c.graph.index_for_offset (this_index, offset); + return c.graph.vertices_[id].table_size (); + } +}; + +struct PairPos : public OT::Layout::GPOS_impl::PairPos +{ + hb_vector_t split_subtables (gsubgpos_graph_context_t& c, + unsigned parent_index, + unsigned this_index) + { + switch (u.format) { + case 1: + return ((PairPosFormat1*)(&u.format1))->split_subtables (c, parent_index, this_index); + case 2: + return ((PairPosFormat2*)(&u.format2))->split_subtables (c, parent_index, this_index); +#ifndef HB_NO_BORING_EXPANSION + case 3: HB_FALLTHROUGH; + case 4: HB_FALLTHROUGH; + // Don't split 24bit PairPos's. +#endif + default: + return hb_vector_t (); + } + } + + bool sanitize (graph_t::vertex_t& vertex) const + { + int64_t vertex_len = vertex.obj.tail - vertex.obj.head; + if (vertex_len < u.format.get_size ()) return false; + + switch (u.format) { + case 1: + return ((PairPosFormat1*)(&u.format1))->sanitize (vertex); + case 2: + return ((PairPosFormat2*)(&u.format2))->sanitize (vertex); +#ifndef HB_NO_BORING_EXPANSION + case 3: HB_FALLTHROUGH; + case 4: HB_FALLTHROUGH; +#endif + default: + // We don't handle format 3 and 4 here. + return false; + } + } +}; + +} + +#endif // GRAPH_PAIRPOS_GRAPH_HH diff --git a/libs/harfbuzz/src/graph/serialize.hh b/libs/harfbuzz/src/graph/serialize.hh new file mode 100644 index 00000000..ecc6cc5a --- /dev/null +++ b/libs/harfbuzz/src/graph/serialize.hh @@ -0,0 +1,249 @@ +/* + * Copyright © 2022 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): Garret Rieger + */ + +#ifndef GRAPH_SERIALIZE_HH +#define GRAPH_SERIALIZE_HH + +namespace graph { + +struct overflow_record_t +{ + unsigned parent; + unsigned child; +}; + +inline +int64_t compute_offset ( + const graph_t& graph, + unsigned parent_idx, + const hb_serialize_context_t::object_t::link_t& link) +{ + const auto& parent = graph.vertices_[parent_idx]; + const auto& child = graph.vertices_[link.objidx]; + int64_t offset = 0; + switch ((hb_serialize_context_t::whence_t) link.whence) { + case hb_serialize_context_t::whence_t::Head: + offset = child.start - parent.start; break; + case hb_serialize_context_t::whence_t::Tail: + offset = child.start - parent.end; break; + case hb_serialize_context_t::whence_t::Absolute: + offset = child.start; break; + } + + assert (offset >= link.bias); + offset -= link.bias; + return offset; +} + +inline +bool is_valid_offset (int64_t offset, + const hb_serialize_context_t::object_t::link_t& link) +{ + if (unlikely (!link.width)) + // Virtual links can't overflow. + return link.is_signed || offset >= 0; + + if (link.is_signed) + { + if (link.width == 4) + return offset >= -((int64_t) 1 << 31) && offset < ((int64_t) 1 << 31); + else + return offset >= -(1 << 15) && offset < (1 << 15); + } + else + { + if (link.width == 4) + return offset >= 0 && offset < ((int64_t) 1 << 32); + else if (link.width == 3) + return offset >= 0 && offset < ((int32_t) 1 << 24); + else + return offset >= 0 && offset < (1 << 16); + } +} + +/* + * Will any offsets overflow on graph when it's serialized? + */ +inline bool +will_overflow (graph_t& graph, + hb_vector_t* overflows = nullptr) +{ + if (overflows) overflows->resize (0); + graph.update_positions (); + + const auto& vertices = graph.vertices_; + for (int parent_idx = vertices.length - 1; parent_idx >= 0; parent_idx--) + { + // Don't need to check virtual links for overflow + for (const auto& link : vertices[parent_idx].obj.real_links) + { + int64_t offset = compute_offset (graph, parent_idx, link); + if (is_valid_offset (offset, link)) + continue; + + if (!overflows) return true; + + overflow_record_t r; + r.parent = parent_idx; + r.child = link.objidx; + overflows->push (r); + } + } + + if (!overflows) return false; + return overflows->length; +} + +inline +void print_overflows (graph_t& graph, + const hb_vector_t& overflows) +{ + if (!DEBUG_ENABLED(SUBSET_REPACK)) return; + + graph.update_parents (); + int limit = 10; + for (const auto& o : overflows) + { + if (!limit--) break; + const auto& parent = graph.vertices_[o.parent]; + const auto& child = graph.vertices_[o.child]; + DEBUG_MSG (SUBSET_REPACK, nullptr, + " overflow from " + "%4d (%4d in, %4d out, space %2d) => " + "%4d (%4d in, %4d out, space %2d)", + o.parent, + parent.incoming_edges (), + parent.obj.real_links.length + parent.obj.virtual_links.length, + graph.space_for (o.parent), + o.child, + child.incoming_edges (), + child.obj.real_links.length + child.obj.virtual_links.length, + graph.space_for (o.child)); + } + if (overflows.length > 10) { + DEBUG_MSG (SUBSET_REPACK, nullptr, " ... plus %d more overflows.", overflows.length - 10); + } +} + +template inline void +serialize_link_of_type (const hb_serialize_context_t::object_t::link_t& link, + char* head, + hb_serialize_context_t* c) +{ + OT::Offset* offset = reinterpret_cast*> (head + link.position); + *offset = 0; + c->add_link (*offset, + // serializer has an extra nil object at the start of the + // object array. So all id's are +1 of what our id's are. + link.objidx + 1, + (hb_serialize_context_t::whence_t) link.whence, + link.bias); +} + +inline +void serialize_link (const hb_serialize_context_t::object_t::link_t& link, + char* head, + hb_serialize_context_t* c) +{ + switch (link.width) + { + case 0: + // Virtual links aren't serialized. + return; + case 4: + if (link.is_signed) + { + serialize_link_of_type (link, head, c); + } else { + serialize_link_of_type (link, head, c); + } + return; + case 2: + if (link.is_signed) + { + serialize_link_of_type (link, head, c); + } else { + serialize_link_of_type (link, head, c); + } + return; + case 3: + serialize_link_of_type (link, head, c); + return; + default: + // Unexpected link width. + assert (0); + } +} + +/* + * serialize graph into the provided serialization buffer. + */ +inline hb_blob_t* serialize (const graph_t& graph) +{ + hb_vector_t buffer; + size_t size = graph.total_size_in_bytes (); + if (!buffer.alloc (size)) { + DEBUG_MSG (SUBSET_REPACK, nullptr, "Unable to allocate output buffer."); + return nullptr; + } + hb_serialize_context_t c((void *) buffer, size); + + c.start_serialize (); + const auto& vertices = graph.vertices_; + for (unsigned i = 0; i < vertices.length; i++) { + c.push (); + + size_t size = vertices[i].obj.tail - vertices[i].obj.head; + char* start = c.allocate_size (size); + if (!start) { + DEBUG_MSG (SUBSET_REPACK, nullptr, "Buffer out of space."); + return nullptr; + } + + memcpy (start, vertices[i].obj.head, size); + + // Only real links needs to be serialized. + for (const auto& link : vertices[i].obj.real_links) + serialize_link (link, start, &c); + + // All duplications are already encoded in the graph, so don't + // enable sharing during packing. + c.pop_pack (false); + } + c.end_serialize (); + + if (c.in_error ()) { + DEBUG_MSG (SUBSET_REPACK, nullptr, "Error during serialization. Err flag: %d", + c.errors); + return nullptr; + } + + return c.copy_blob (); +} + +} // namespace graph + +#endif // GRAPH_SERIALIZE_HH diff --git a/libs/harfbuzz/src/graph/split-helpers.hh b/libs/harfbuzz/src/graph/split-helpers.hh new file mode 100644 index 00000000..61fd7c2d --- /dev/null +++ b/libs/harfbuzz/src/graph/split-helpers.hh @@ -0,0 +1,69 @@ +/* + * Copyright © 2022 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): Garret Rieger + */ + +#ifndef GRAPH_SPLIT_HELPERS_HH +#define GRAPH_SPLIT_HELPERS_HH + +namespace graph { + +template +HB_INTERNAL +hb_vector_t actuate_subtable_split (Context& split_context, + const hb_vector_t& split_points) +{ + hb_vector_t new_objects; + if (!split_points) + return new_objects; + + for (unsigned i = 0; i < split_points.length; i++) + { + unsigned start = split_points[i]; + unsigned end = (i < split_points.length - 1) + ? split_points[i + 1] + : split_context.original_count (); + unsigned id = split_context.clone_range (start, end); + + if (id == (unsigned) -1) + { + new_objects.reset (); + new_objects.allocated = -1; // mark error + return new_objects; + } + new_objects.push (id); + } + + if (!split_context.shrink (split_points[0])) + { + new_objects.reset (); + new_objects.allocated = -1; // mark error + } + + return new_objects; +} + +} + +#endif // GRAPH_SPLIT_HELPERS_HH diff --git a/libs/harfbuzz/src/graph/test-classdef-graph.cc b/libs/harfbuzz/src/graph/test-classdef-graph.cc new file mode 100644 index 00000000..55854ff5 --- /dev/null +++ b/libs/harfbuzz/src/graph/test-classdef-graph.cc @@ -0,0 +1,119 @@ +/* + * Copyright © 2022 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): Garret Rieger + */ + +#include "gsubgpos-context.hh" +#include "classdef-graph.hh" + +typedef hb_pair_t gid_and_class_t; +typedef hb_vector_t gid_and_class_list_t; + + +static bool incremental_size_is (const gid_and_class_list_t& list, unsigned klass, + unsigned cov_expected, unsigned class_def_expected) +{ + graph::class_def_size_estimator_t estimator (list.iter ()); + + unsigned result = estimator.incremental_coverage_size (klass); + if (result != cov_expected) + { + printf ("FAIL: coverage expected size %u but was %u\n", cov_expected, result); + return false; + } + + result = estimator.incremental_class_def_size (klass); + if (result != class_def_expected) + { + printf ("FAIL: class def expected size %u but was %u\n", class_def_expected, result); + return false; + } + + return true; +} + +static void test_class_and_coverage_size_estimates () +{ + gid_and_class_list_t empty = { + }; + assert (incremental_size_is (empty, 0, 0, 0)); + assert (incremental_size_is (empty, 1, 0, 0)); + + gid_and_class_list_t class_zero = { + {5, 0}, + }; + assert (incremental_size_is (class_zero, 0, 2, 0)); + + gid_and_class_list_t consecutive = { + {4, 0}, + {5, 0}, + {6, 1}, + {7, 1}, + {8, 2}, + {9, 2}, + {10, 2}, + {11, 2}, + }; + assert (incremental_size_is (consecutive, 0, 4, 0)); + assert (incremental_size_is (consecutive, 1, 4, 4)); + assert (incremental_size_is (consecutive, 2, 8, 6)); + + gid_and_class_list_t non_consecutive = { + {4, 0}, + {5, 0}, + + {6, 1}, + {7, 1}, + + {9, 2}, + {10, 2}, + {11, 2}, + {12, 2}, + }; + assert (incremental_size_is (non_consecutive, 0, 4, 0)); + assert (incremental_size_is (non_consecutive, 1, 4, 6)); + assert (incremental_size_is (non_consecutive, 2, 8, 6)); + + gid_and_class_list_t multiple_ranges = { + {4, 0}, + {5, 0}, + + {6, 1}, + {7, 1}, + + {9, 1}, + + {11, 1}, + {12, 1}, + {13, 1}, + }; + assert (incremental_size_is (multiple_ranges, 0, 4, 0)); + assert (incremental_size_is (multiple_ranges, 1, 2 * 6, 3 * 6)); +} + +int +main (int argc, char **argv) +{ + test_class_and_coverage_size_estimates (); +} diff --git a/libs/harfbuzz/src/harfbuzz-config.cmake.in b/libs/harfbuzz/src/harfbuzz-config.cmake.in new file mode 100644 index 00000000..304410d9 --- /dev/null +++ b/libs/harfbuzz/src/harfbuzz-config.cmake.in @@ -0,0 +1,86 @@ +# Set these variables so that the `${prefix}/lib` expands to something we can +# remove. +set(_harfbuzz_remove_string "REMOVE_ME") +set(exec_prefix "${_harfbuzz_remove_string}") +set(prefix "${_harfbuzz_remove_string}") + +# Compute the installation prefix by stripping components from our current +# location. +get_filename_component(_harfbuzz_prefix "${CMAKE_CURRENT_LIST_DIR}" DIRECTORY) +get_filename_component(_harfbuzz_prefix "${_harfbuzz_prefix}" DIRECTORY) +set(_harfbuzz_libdir "@libdir@") +string(REPLACE "${_harfbuzz_remove_string}/" "" _harfbuzz_libdir "${_harfbuzz_libdir}") +set(_harfbuzz_libdir_iter "${_harfbuzz_libdir}") +while (_harfbuzz_libdir_iter) + set(_harfbuzz_libdir_prev_iter "${_harfbuzz_libdir_iter}") + get_filename_component(_harfbuzz_libdir_iter "${_harfbuzz_libdir_iter}" DIRECTORY) + if (_harfbuzz_libdir_prev_iter STREQUAL _harfbuzz_libdir_iter) + break() + endif () + get_filename_component(_harfbuzz_prefix "${_harfbuzz_prefix}" DIRECTORY) +endwhile () +unset(_harfbuzz_libdir_iter) + +# Get the include subdir. +set(_harfbuzz_includedir "@includedir@") +string(REPLACE "${_harfbuzz_remove_string}/" "" _harfbuzz_includedir "${_harfbuzz_includedir}") + +# Extract version information from libtool. +set(_harfbuzz_version_info "@HB_LIBTOOL_VERSION_INFO@") +string(REPLACE ":" ";" _harfbuzz_version_info "${_harfbuzz_version_info}") +list(GET _harfbuzz_version_info 0 + _harfbuzz_current) +list(GET _harfbuzz_version_info 1 + _harfbuzz_revision) +list(GET _harfbuzz_version_info 2 + _harfbuzz_age) +unset(_harfbuzz_version_info) + +if (APPLE) + set(_harfbuzz_lib_suffix ".0${CMAKE_SHARED_LIBRARY_SUFFIX}") +elseif (UNIX) + set(_harfbuzz_lib_suffix "${CMAKE_SHARED_LIBRARY_SUFFIX}.0.${_harfbuzz_current}.${_harfbuzz_revision}") +else () + # Unsupported. + set(harfbuzz_FOUND 0) +endif () + +# Add the libraries. +add_library(harfbuzz::harfbuzz SHARED IMPORTED) +set_target_properties(harfbuzz::harfbuzz PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${_harfbuzz_prefix}/${_harfbuzz_includedir}/harfbuzz" + IMPORTED_LOCATION "${_harfbuzz_prefix}/${_harfbuzz_libdir}/libharfbuzz${_harfbuzz_lib_suffix}") + +add_library(harfbuzz::icu SHARED IMPORTED) +set_target_properties(harfbuzz::icu PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${_harfbuzz_prefix}/${_harfbuzz_includedir}/harfbuzz" + INTERFACE_LINK_LIBRARIES "harfbuzz::harfbuzz" + IMPORTED_LOCATION "${_harfbuzz_prefix}/${_harfbuzz_libdir}/libharfbuzz-icu${_harfbuzz_lib_suffix}") + +add_library(harfbuzz::subset SHARED IMPORTED) +set_target_properties(harfbuzz::subset PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${_harfbuzz_prefix}/${_harfbuzz_includedir}/harfbuzz" + INTERFACE_LINK_LIBRARIES "harfbuzz::harfbuzz" + IMPORTED_LOCATION "${_harfbuzz_prefix}/${_harfbuzz_libdir}/libharfbuzz-subset${_harfbuzz_lib_suffix}") + +# Only add the gobject library if it was built. +set(_harfbuzz_have_gobject "@have_gobject@") +if (_harfbuzz_have_gobject) + add_library(harfbuzz::gobject SHARED IMPORTED) + set_target_properties(harfbuzz::gobject PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${_harfbuzz_prefix}/${_harfbuzz_includedir}/harfbuzz" + INTERFACE_LINK_LIBRARIES "harfbuzz::harfbuzz" + IMPORTED_LOCATION "${_harfbuzz_prefix}/${_harfbuzz_libdir}/libharfbuzz-gobject${_harfbuzz_lib_suffix}") +endif () + +# Clean out variables we used in our scope. +unset(_harfbuzz_lib_suffix) +unset(_harfbuzz_current) +unset(_harfbuzz_revision) +unset(_harfbuzz_age) +unset(_harfbuzz_includedir) +unset(_harfbuzz_libdir) +unset(_harfbuzz_prefix) +unset(exec_prefix) +unset(prefix) +unset(_harfbuzz_remove_string) diff --git a/libs/harfbuzz/src/harfbuzz-gobject.pc.in b/libs/harfbuzz/src/harfbuzz-gobject.pc.in new file mode 100644 index 00000000..70083601 --- /dev/null +++ b/libs/harfbuzz/src/harfbuzz-gobject.pc.in @@ -0,0 +1,12 @@ +prefix=%prefix% +exec_prefix=%exec_prefix% +libdir=%libdir% +includedir=%includedir% + +Name: harfbuzz +Description: HarfBuzz text shaping library GObject integration +Version: %VERSION% + +Requires: harfbuzz gobject-2.0 glib-2.0 +Libs: -L${libdir} -lharfbuzz-gobject +Cflags: -I${includedir}/harfbuzz diff --git a/libs/harfbuzz/src/harfbuzz-icu.pc.in b/libs/harfbuzz/src/harfbuzz-icu.pc.in new file mode 100644 index 00000000..949869a3 --- /dev/null +++ b/libs/harfbuzz/src/harfbuzz-icu.pc.in @@ -0,0 +1,13 @@ +prefix=%prefix% +exec_prefix=%exec_prefix% +libdir=%libdir% +includedir=%includedir% + +Name: harfbuzz +Description: HarfBuzz text shaping library ICU integration +Version: %VERSION% + +Requires: harfbuzz +Requires.private: icu-uc +Libs: -L${libdir} -lharfbuzz-icu +Cflags: -I${includedir}/harfbuzz diff --git a/libs/harfbuzz/src/harfbuzz-subset.cc b/libs/harfbuzz/src/harfbuzz-subset.cc new file mode 100644 index 00000000..a43485e6 --- /dev/null +++ b/libs/harfbuzz/src/harfbuzz-subset.cc @@ -0,0 +1,57 @@ +#include "graph/gsubgpos-context.cc" +#include "hb-aat-layout.cc" +#include "hb-aat-map.cc" +#include "hb-blob.cc" +#include "hb-buffer-serialize.cc" +#include "hb-buffer-verify.cc" +#include "hb-buffer.cc" +#include "hb-common.cc" +#include "hb-draw.cc" +#include "hb-face.cc" +#include "hb-fallback-shape.cc" +#include "hb-font.cc" +#include "hb-map.cc" +#include "hb-number.cc" +#include "hb-ot-cff1-table.cc" +#include "hb-ot-cff2-table.cc" +#include "hb-ot-color.cc" +#include "hb-ot-face.cc" +#include "hb-ot-font.cc" +#include "hb-ot-layout.cc" +#include "hb-ot-map.cc" +#include "hb-ot-math.cc" +#include "hb-ot-meta.cc" +#include "hb-ot-metrics.cc" +#include "hb-ot-name.cc" +#include "hb-ot-shape-fallback.cc" +#include "hb-ot-shape-normalize.cc" +#include "hb-ot-shape.cc" +#include "hb-ot-shaper-arabic.cc" +#include "hb-ot-shaper-default.cc" +#include "hb-ot-shaper-hangul.cc" +#include "hb-ot-shaper-hebrew.cc" +#include "hb-ot-shaper-indic-table.cc" +#include "hb-ot-shaper-indic.cc" +#include "hb-ot-shaper-khmer.cc" +#include "hb-ot-shaper-myanmar.cc" +#include "hb-ot-shaper-syllabic.cc" +#include "hb-ot-shaper-thai.cc" +#include "hb-ot-shaper-use.cc" +#include "hb-ot-shaper-vowel-constraints.cc" +#include "hb-ot-tag.cc" +#include "hb-ot-var.cc" +#include "hb-set.cc" +#include "hb-shape-plan.cc" +#include "hb-shape.cc" +#include "hb-shaper.cc" +#include "hb-static.cc" +#include "hb-style.cc" +#include "hb-subset-cff-common.cc" +#include "hb-subset-cff1.cc" +#include "hb-subset-cff2.cc" +#include "hb-subset-input.cc" +#include "hb-subset-plan.cc" +#include "hb-subset-repacker.cc" +#include "hb-subset.cc" +#include "hb-ucd.cc" +#include "hb-unicode.cc" diff --git a/libs/harfbuzz/src/harfbuzz-subset.pc.in b/libs/harfbuzz/src/harfbuzz-subset.pc.in new file mode 100644 index 00000000..ca13c70e --- /dev/null +++ b/libs/harfbuzz/src/harfbuzz-subset.pc.in @@ -0,0 +1,12 @@ +prefix=%prefix% +exec_prefix=%exec_prefix% +libdir=%libdir% +includedir=%includedir% + +Name: harfbuzz subsetter +Description: HarfBuzz font subsetter +Version: %VERSION% + +Requires: harfbuzz = %VERSION% +Libs: -L${libdir} -lharfbuzz-subset +Cflags: -I${includedir}/harfbuzz diff --git a/libs/harfbuzz/src/harfbuzz.cc b/libs/harfbuzz/src/harfbuzz.cc new file mode 100644 index 00000000..fe4e21db --- /dev/null +++ b/libs/harfbuzz/src/harfbuzz.cc @@ -0,0 +1,56 @@ +#include "hb-aat-layout.cc" +#include "hb-aat-map.cc" +#include "hb-blob.cc" +#include "hb-buffer-serialize.cc" +#include "hb-buffer-verify.cc" +#include "hb-buffer.cc" +#include "hb-common.cc" +#include "hb-coretext.cc" +#include "hb-directwrite.cc" +#include "hb-draw.cc" +#include "hb-face.cc" +#include "hb-fallback-shape.cc" +#include "hb-font.cc" +#include "hb-ft.cc" +#include "hb-gdi.cc" +#include "hb-glib.cc" +#include "hb-graphite2.cc" +#include "hb-map.cc" +#include "hb-number.cc" +#include "hb-ot-cff1-table.cc" +#include "hb-ot-cff2-table.cc" +#include "hb-ot-color.cc" +#include "hb-ot-face.cc" +#include "hb-ot-font.cc" +#include "hb-ot-layout.cc" +#include "hb-ot-map.cc" +#include "hb-ot-math.cc" +#include "hb-ot-meta.cc" +#include "hb-ot-metrics.cc" +#include "hb-ot-name.cc" +#include "hb-ot-shape-fallback.cc" +#include "hb-ot-shape-normalize.cc" +#include "hb-ot-shape.cc" +#include "hb-ot-shaper-arabic.cc" +#include "hb-ot-shaper-default.cc" +#include "hb-ot-shaper-hangul.cc" +#include "hb-ot-shaper-hebrew.cc" +#include "hb-ot-shaper-indic-table.cc" +#include "hb-ot-shaper-indic.cc" +#include "hb-ot-shaper-khmer.cc" +#include "hb-ot-shaper-myanmar.cc" +#include "hb-ot-shaper-syllabic.cc" +#include "hb-ot-shaper-thai.cc" +#include "hb-ot-shaper-use.cc" +#include "hb-ot-shaper-vowel-constraints.cc" +#include "hb-ot-tag.cc" +#include "hb-ot-var.cc" +#include "hb-set.cc" +#include "hb-shape-plan.cc" +#include "hb-shape.cc" +#include "hb-shaper.cc" +#include "hb-static.cc" +#include "hb-style.cc" +#include "hb-ucd.cc" +#include "hb-unicode.cc" +#include "hb-uniscribe.cc" diff --git a/libs/harfbuzz/src/harfbuzz.pc.in b/libs/harfbuzz/src/harfbuzz.pc.in new file mode 100644 index 00000000..661251c2 --- /dev/null +++ b/libs/harfbuzz/src/harfbuzz.pc.in @@ -0,0 +1,13 @@ +prefix=%prefix% +exec_prefix=%exec_prefix% +libdir=%libdir% +includedir=%includedir% + +Name: harfbuzz +Description: HarfBuzz text shaping library +Version: %VERSION% + +Libs: -L${libdir} -lharfbuzz +Libs.private: -lm %libs_private% +Requires.private: %requires_private% +Cflags: -I${includedir}/harfbuzz diff --git a/libs/harfbuzz/src/hb-aat-layout-ankr-table.hh b/libs/harfbuzz/src/hb-aat-layout-ankr-table.hh new file mode 100644 index 00000000..63fac845 --- /dev/null +++ b/libs/harfbuzz/src/hb-aat-layout-ankr-table.hh @@ -0,0 +1,98 @@ +/* + * Copyright © 2018 Ebrahim Byagowi + * + * 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. + */ + +#ifndef HB_AAT_LAYOUT_ANKR_TABLE_HH +#define HB_AAT_LAYOUT_ANKR_TABLE_HH + +#include "hb-aat-layout-common.hh" + +/* + * ankr -- Anchor Point + * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6ankr.html + */ +#define HB_AAT_TAG_ankr HB_TAG('a','n','k','r') + + +namespace AAT { + +using namespace OT; + + +struct Anchor +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + public: + FWORD xCoordinate; + FWORD yCoordinate; + public: + DEFINE_SIZE_STATIC (4); +}; + +typedef Array32Of GlyphAnchors; + +struct ankr +{ + static constexpr hb_tag_t tableTag = HB_AAT_TAG_ankr; + + const Anchor &get_anchor (hb_codepoint_t glyph_id, + unsigned int i, + unsigned int num_glyphs) const + { + const NNOffset16To *offset = (this+lookupTable).get_value (glyph_id, num_glyphs); + if (!offset) + return Null (Anchor); + const GlyphAnchors &anchors = &(this+anchorData) + *offset; + return anchors[i]; + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + version == 0 && + c->check_range (this, anchorData) && + lookupTable.sanitize (c, this, &(this+anchorData)))); + } + + protected: + HBUINT16 version; /* Version number (set to zero) */ + HBUINT16 flags; /* Flags (currently unused; set to zero) */ + Offset32To>> + lookupTable; /* Offset to the table's lookup table */ + NNOffset32To + anchorData; /* Offset to the glyph data table */ + + public: + DEFINE_SIZE_STATIC (12); +}; + +} /* namespace AAT */ + + +#endif /* HB_AAT_LAYOUT_ANKR_TABLE_HH */ diff --git a/libs/harfbuzz/src/hb-aat-layout-bsln-table.hh b/libs/harfbuzz/src/hb-aat-layout-bsln-table.hh new file mode 100644 index 00000000..bf12d2e6 --- /dev/null +++ b/libs/harfbuzz/src/hb-aat-layout-bsln-table.hh @@ -0,0 +1,158 @@ +/* + * Copyright © 2018 Ebrahim Byagowi + * + * 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. + */ + +#ifndef HB_AAT_LAYOUT_BSLN_TABLE_HH +#define HB_AAT_LAYOUT_BSLN_TABLE_HH + +#include "hb-aat-layout-common.hh" + +/* + * bsln -- Baseline + * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6bsln.html + */ +#define HB_AAT_TAG_bsln HB_TAG('b','s','l','n') + + +namespace AAT { + + +struct BaselineTableFormat0Part +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + protected: + // Roman, Ideographic centered, Ideographic low, Hanging and Math + // are the default defined ones, but any other maybe accessed also. + HBINT16 deltas[32]; /* These are the FUnit distance deltas from + * the font's natural baseline to the other + * baselines used in the font. */ + public: + DEFINE_SIZE_STATIC (64); +}; + +struct BaselineTableFormat1Part +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + lookupTable.sanitize (c))); + } + + protected: + HBINT16 deltas[32]; /* ditto */ + Lookup + lookupTable; /* Lookup table that maps glyphs to their + * baseline values. */ + public: + DEFINE_SIZE_MIN (66); +}; + +struct BaselineTableFormat2Part +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + protected: + HBGlyphID16 stdGlyph; /* The specific glyph index number in this + * font that is used to set the baseline values. + * This is the standard glyph. + * This glyph must contain a set of control points + * (whose numbers are contained in the ctlPoints field) + * that are used to determine baseline distances. */ + HBUINT16 ctlPoints[32]; /* Set of control point numbers, + * associated with the standard glyph. + * A value of 0xFFFF means there is no corresponding + * control point in the standard glyph. */ + public: + DEFINE_SIZE_STATIC (66); +}; + +struct BaselineTableFormat3Part +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && lookupTable.sanitize (c))); + } + + protected: + HBGlyphID16 stdGlyph; /* ditto */ + HBUINT16 ctlPoints[32]; /* ditto */ + Lookup + lookupTable; /* Lookup table that maps glyphs to their + * baseline values. */ + public: + DEFINE_SIZE_MIN (68); +}; + +struct bsln +{ + static constexpr hb_tag_t tableTag = HB_AAT_TAG_bsln; + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (unlikely (!(c->check_struct (this) && defaultBaseline < 32))) + return_trace (false); + + switch (format) + { + case 0: return_trace (parts.format0.sanitize (c)); + case 1: return_trace (parts.format1.sanitize (c)); + case 2: return_trace (parts.format2.sanitize (c)); + case 3: return_trace (parts.format3.sanitize (c)); + default:return_trace (true); + } + } + + protected: + FixedVersion<>version; /* Version number of the Baseline table. */ + HBUINT16 format; /* Format of the baseline table. Only one baseline + * format may be selected for the font. */ + HBUINT16 defaultBaseline;/* Default baseline value for all glyphs. + * This value can be from 0 through 31. */ + union { + // Distance-Based Formats + BaselineTableFormat0Part format0; + BaselineTableFormat1Part format1; + // Control Point-based Formats + BaselineTableFormat2Part format2; + BaselineTableFormat3Part format3; + } parts; + public: + DEFINE_SIZE_MIN (8); +}; + +} /* namespace AAT */ + + +#endif /* HB_AAT_LAYOUT_BSLN_TABLE_HH */ diff --git a/libs/harfbuzz/src/hb-aat-layout-common.hh b/libs/harfbuzz/src/hb-aat-layout-common.hh new file mode 100644 index 00000000..6cbed826 --- /dev/null +++ b/libs/harfbuzz/src/hb-aat-layout-common.hh @@ -0,0 +1,886 @@ +/* + * Copyright © 2017 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 HB_AAT_LAYOUT_COMMON_HH +#define HB_AAT_LAYOUT_COMMON_HH + +#include "hb-aat-layout.hh" +#include "hb-open-type.hh" + +namespace OT { +struct GDEF; +}; + +namespace AAT { + +using namespace OT; + + +/* + * Lookup Table + */ + +template struct Lookup; + +template +struct LookupFormat0 +{ + friend struct Lookup; + + private: + const T* get_value (hb_codepoint_t glyph_id, unsigned int num_glyphs) const + { + if (unlikely (glyph_id >= num_glyphs)) return nullptr; + return &arrayZ[glyph_id]; + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (arrayZ.sanitize (c, c->get_num_glyphs ())); + } + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (arrayZ.sanitize (c, c->get_num_glyphs (), base)); + } + + protected: + HBUINT16 format; /* Format identifier--format = 0 */ + UnsizedArrayOf + arrayZ; /* Array of lookup values, indexed by glyph index. */ + public: + DEFINE_SIZE_UNBOUNDED (2); +}; + + +template +struct LookupSegmentSingle +{ + static constexpr unsigned TerminationWordCount = 2u; + + int cmp (hb_codepoint_t g) const + { return g < first ? -1 : g <= last ? 0 : +1 ; } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && value.sanitize (c)); + } + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && value.sanitize (c, base)); + } + + HBGlyphID16 last; /* Last GlyphID in this segment */ + HBGlyphID16 first; /* First GlyphID in this segment */ + T value; /* The lookup value (only one) */ + public: + DEFINE_SIZE_STATIC (4 + T::static_size); +}; + +template +struct LookupFormat2 +{ + friend struct Lookup; + + private: + const T* get_value (hb_codepoint_t glyph_id) const + { + const LookupSegmentSingle *v = segments.bsearch (glyph_id); + return v ? &v->value : nullptr; + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (segments.sanitize (c)); + } + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (segments.sanitize (c, base)); + } + + protected: + HBUINT16 format; /* Format identifier--format = 2 */ + VarSizedBinSearchArrayOf> + segments; /* The actual segments. These must already be sorted, + * according to the first word in each one (the last + * glyph in each segment). */ + public: + DEFINE_SIZE_ARRAY (8, segments); +}; + +template +struct LookupSegmentArray +{ + static constexpr unsigned TerminationWordCount = 2u; + + const T* get_value (hb_codepoint_t glyph_id, const void *base) const + { + return first <= glyph_id && glyph_id <= last ? &(base+valuesZ)[glyph_id - first] : nullptr; + } + + int cmp (hb_codepoint_t g) const + { return g < first ? -1 : g <= last ? 0 : +1; } + + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + first <= last && + valuesZ.sanitize (c, base, last - first + 1)); + } + template + bool sanitize (hb_sanitize_context_t *c, const void *base, Ts&&... ds) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + first <= last && + valuesZ.sanitize (c, base, last - first + 1, std::forward (ds)...)); + } + + HBGlyphID16 last; /* Last GlyphID in this segment */ + HBGlyphID16 first; /* First GlyphID in this segment */ + NNOffset16To> + valuesZ; /* A 16-bit offset from the start of + * the table to the data. */ + public: + DEFINE_SIZE_STATIC (6); +}; + +template +struct LookupFormat4 +{ + friend struct Lookup; + + private: + const T* get_value (hb_codepoint_t glyph_id) const + { + const LookupSegmentArray *v = segments.bsearch (glyph_id); + return v ? v->get_value (glyph_id, this) : nullptr; + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (segments.sanitize (c, this)); + } + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (segments.sanitize (c, this, base)); + } + + protected: + HBUINT16 format; /* Format identifier--format = 4 */ + VarSizedBinSearchArrayOf> + segments; /* The actual segments. These must already be sorted, + * according to the first word in each one (the last + * glyph in each segment). */ + public: + DEFINE_SIZE_ARRAY (8, segments); +}; + +template +struct LookupSingle +{ + static constexpr unsigned TerminationWordCount = 1u; + + int cmp (hb_codepoint_t g) const { return glyph.cmp (g); } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && value.sanitize (c)); + } + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && value.sanitize (c, base)); + } + + HBGlyphID16 glyph; /* Last GlyphID */ + T value; /* The lookup value (only one) */ + public: + DEFINE_SIZE_STATIC (2 + T::static_size); +}; + +template +struct LookupFormat6 +{ + friend struct Lookup; + + private: + const T* get_value (hb_codepoint_t glyph_id) const + { + const LookupSingle *v = entries.bsearch (glyph_id); + return v ? &v->value : nullptr; + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (entries.sanitize (c)); + } + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (entries.sanitize (c, base)); + } + + protected: + HBUINT16 format; /* Format identifier--format = 6 */ + VarSizedBinSearchArrayOf> + entries; /* The actual entries, sorted by glyph index. */ + public: + DEFINE_SIZE_ARRAY (8, entries); +}; + +template +struct LookupFormat8 +{ + friend struct Lookup; + + private: + const T* get_value (hb_codepoint_t glyph_id) const + { + return firstGlyph <= glyph_id && glyph_id - firstGlyph < glyphCount ? + &valueArrayZ[glyph_id - firstGlyph] : nullptr; + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && valueArrayZ.sanitize (c, glyphCount)); + } + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && valueArrayZ.sanitize (c, glyphCount, base)); + } + + protected: + HBUINT16 format; /* Format identifier--format = 8 */ + HBGlyphID16 firstGlyph; /* First glyph index included in the trimmed array. */ + HBUINT16 glyphCount; /* Total number of glyphs (equivalent to the last + * glyph minus the value of firstGlyph plus 1). */ + UnsizedArrayOf + valueArrayZ; /* The lookup values (indexed by the glyph index + * minus the value of firstGlyph). */ + public: + DEFINE_SIZE_ARRAY (6, valueArrayZ); +}; + +template +struct LookupFormat10 +{ + friend struct Lookup; + + private: + const typename T::type get_value_or_null (hb_codepoint_t glyph_id) const + { + if (!(firstGlyph <= glyph_id && glyph_id - firstGlyph < glyphCount)) + return Null (T); + + const HBUINT8 *p = &valueArrayZ[(glyph_id - firstGlyph) * valueSize]; + + unsigned int v = 0; + unsigned int count = valueSize; + for (unsigned int i = 0; i < count; i++) + v = (v << 8) | *p++; + + return v; + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + valueSize <= 4 && + valueArrayZ.sanitize (c, glyphCount * valueSize)); + } + + protected: + HBUINT16 format; /* Format identifier--format = 8 */ + HBUINT16 valueSize; /* Byte size of each value. */ + HBGlyphID16 firstGlyph; /* First glyph index included in the trimmed array. */ + HBUINT16 glyphCount; /* Total number of glyphs (equivalent to the last + * glyph minus the value of firstGlyph plus 1). */ + UnsizedArrayOf + valueArrayZ; /* The lookup values (indexed by the glyph index + * minus the value of firstGlyph). */ + public: + DEFINE_SIZE_ARRAY (8, valueArrayZ); +}; + +template +struct Lookup +{ + const T* get_value (hb_codepoint_t glyph_id, unsigned int num_glyphs) const + { + switch (u.format) { + case 0: return u.format0.get_value (glyph_id, num_glyphs); + case 2: return u.format2.get_value (glyph_id); + case 4: return u.format4.get_value (glyph_id); + case 6: return u.format6.get_value (glyph_id); + case 8: return u.format8.get_value (glyph_id); + default:return nullptr; + } + } + + const typename T::type get_value_or_null (hb_codepoint_t glyph_id, unsigned int num_glyphs) const + { + switch (u.format) { + /* Format 10 cannot return a pointer. */ + case 10: return u.format10.get_value_or_null (glyph_id); + default: + const T *v = get_value (glyph_id, num_glyphs); + return v ? *v : Null (T); + } + } + + typename T::type get_class (hb_codepoint_t glyph_id, + unsigned int num_glyphs, + unsigned int outOfRange) const + { + const T *v = get_value (glyph_id, num_glyphs); + return v ? *v : outOfRange; + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (!u.format.sanitize (c)) return_trace (false); + switch (u.format) { + case 0: return_trace (u.format0.sanitize (c)); + case 2: return_trace (u.format2.sanitize (c)); + case 4: return_trace (u.format4.sanitize (c)); + case 6: return_trace (u.format6.sanitize (c)); + case 8: return_trace (u.format8.sanitize (c)); + case 10: return_trace (u.format10.sanitize (c)); + default:return_trace (true); + } + } + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + if (!u.format.sanitize (c)) return_trace (false); + switch (u.format) { + case 0: return_trace (u.format0.sanitize (c, base)); + case 2: return_trace (u.format2.sanitize (c, base)); + case 4: return_trace (u.format4.sanitize (c, base)); + case 6: return_trace (u.format6.sanitize (c, base)); + case 8: return_trace (u.format8.sanitize (c, base)); + case 10: return_trace (false); /* We don't support format10 here currently. */ + default:return_trace (true); + } + } + + protected: + union { + HBUINT16 format; /* Format identifier */ + LookupFormat0 format0; + LookupFormat2 format2; + LookupFormat4 format4; + LookupFormat6 format6; + LookupFormat8 format8; + LookupFormat10 format10; + } u; + public: + DEFINE_SIZE_UNION (2, format); +}; +DECLARE_NULL_NAMESPACE_BYTES_TEMPLATE1 (AAT, Lookup, 2); + +enum { DELETED_GLYPH = 0xFFFF }; + +/* + * (Extended) State Table + */ + +template +struct Entry +{ + bool sanitize (hb_sanitize_context_t *c, unsigned int count) const + { + TRACE_SANITIZE (this); + /* Note, we don't recurse-sanitize data because we don't access it. + * That said, in our DEFINE_SIZE_STATIC we access T::static_size, + * which ensures that data has a simple sanitize(). To be determined + * if I need to remove that as well. + * + * HOWEVER! Because we are a template, our DEFINE_SIZE_STATIC + * assertion wouldn't be checked, hence the line below. */ + static_assert (T::static_size, ""); + + return_trace (c->check_struct (this)); + } + + public: + HBUINT16 newState; /* Byte offset from beginning of state table + * to the new state. Really?!?! Or just state + * number? The latter in morx for sure. */ + HBUINT16 flags; /* Table specific. */ + T data; /* Optional offsets to per-glyph tables. */ + public: + DEFINE_SIZE_STATIC (4 + T::static_size); +}; + +template <> +struct Entry +{ + bool sanitize (hb_sanitize_context_t *c, unsigned int count /*XXX Unused?*/) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + public: + HBUINT16 newState; /* Byte offset from beginning of state table to the new state. */ + HBUINT16 flags; /* Table specific. */ + public: + DEFINE_SIZE_STATIC (4); +}; + +template +struct StateTable +{ + typedef typename Types::HBUINT HBUINT; + typedef typename Types::HBUSHORT HBUSHORT; + typedef typename Types::ClassTypeNarrow ClassType; + + enum State + { + STATE_START_OF_TEXT = 0, + STATE_START_OF_LINE = 1, + }; + enum Class + { + CLASS_END_OF_TEXT = 0, + CLASS_OUT_OF_BOUNDS = 1, + CLASS_DELETED_GLYPH = 2, + CLASS_END_OF_LINE = 3, + }; + + int new_state (unsigned int newState) const + { return Types::extended ? newState : ((int) newState - (int) stateArrayTable) / (int) nClasses; } + + unsigned int get_class (hb_codepoint_t glyph_id, unsigned int num_glyphs) const + { + if (unlikely (glyph_id == DELETED_GLYPH)) return CLASS_DELETED_GLYPH; + return (this+classTable).get_class (glyph_id, num_glyphs, 1); + } + + const Entry *get_entries () const + { return (this+entryTable).arrayZ; } + + const Entry &get_entry (int state, unsigned int klass) const + { + if (unlikely (klass >= nClasses)) + klass = StateTable::CLASS_OUT_OF_BOUNDS; + + const HBUSHORT *states = (this+stateArrayTable).arrayZ; + const Entry *entries = (this+entryTable).arrayZ; + + unsigned int entry = states[state * nClasses + klass]; + DEBUG_MSG (APPLY, nullptr, "e%u", entry); + + return entries[entry]; + } + + bool sanitize (hb_sanitize_context_t *c, + unsigned int *num_entries_out = nullptr) const + { + TRACE_SANITIZE (this); + if (unlikely (!(c->check_struct (this) && + nClasses >= 4 /* Ensure pre-defined classes fit. */ && + classTable.sanitize (c, this)))) return_trace (false); + + const HBUSHORT *states = (this+stateArrayTable).arrayZ; + const Entry *entries = (this+entryTable).arrayZ; + + unsigned int num_classes = nClasses; + if (unlikely (hb_unsigned_mul_overflows (num_classes, states[0].static_size))) + return_trace (false); + unsigned int row_stride = num_classes * states[0].static_size; + + /* Apple 'kern' table has this peculiarity: + * + * "Because the stateTableOffset in the state table header is (strictly + * speaking) redundant, some 'kern' tables use it to record an initial + * state where that should not be StartOfText. To determine if this is + * done, calculate what the stateTableOffset should be. If it's different + * from the actual stateTableOffset, use it as the initial state." + * + * We implement this by calling the initial state zero, but allow *negative* + * states if the start state indeed was not the first state. Since the code + * is shared, this will also apply to 'mort' table. The 'kerx' / 'morx' + * tables are not affected since those address states by index, not offset. + */ + + int min_state = 0; + int max_state = 0; + unsigned int num_entries = 0; + + int state_pos = 0; + int state_neg = 0; + unsigned int entry = 0; + while (min_state < state_neg || state_pos <= max_state) + { + if (min_state < state_neg) + { + /* Negative states. */ + if (unlikely (hb_unsigned_mul_overflows (min_state, num_classes))) + return_trace (false); + if (unlikely (!c->check_range (&states[min_state * num_classes], + -min_state, + row_stride))) + return_trace (false); + if ((c->max_ops -= state_neg - min_state) <= 0) + return_trace (false); + { /* Sweep new states. */ + const HBUSHORT *stop = &states[min_state * num_classes]; + if (unlikely (stop > states)) + return_trace (false); + for (const HBUSHORT *p = states; stop < p; p--) + num_entries = hb_max (num_entries, *(p - 1) + 1u); + state_neg = min_state; + } + } + + if (state_pos <= max_state) + { + /* Positive states. */ + if (unlikely (!c->check_range (states, + max_state + 1, + row_stride))) + return_trace (false); + if ((c->max_ops -= max_state - state_pos + 1) <= 0) + return_trace (false); + { /* Sweep new states. */ + if (unlikely (hb_unsigned_mul_overflows ((max_state + 1), num_classes))) + return_trace (false); + const HBUSHORT *stop = &states[(max_state + 1) * num_classes]; + if (unlikely (stop < states)) + return_trace (false); + for (const HBUSHORT *p = &states[state_pos * num_classes]; p < stop; p++) + num_entries = hb_max (num_entries, *p + 1u); + state_pos = max_state + 1; + } + } + + if (unlikely (!c->check_array (entries, num_entries))) + return_trace (false); + if ((c->max_ops -= num_entries - entry) <= 0) + return_trace (false); + { /* Sweep new entries. */ + const Entry *stop = &entries[num_entries]; + for (const Entry *p = &entries[entry]; p < stop; p++) + { + int newState = new_state (p->newState); + min_state = hb_min (min_state, newState); + max_state = hb_max (max_state, newState); + } + entry = num_entries; + } + } + + if (num_entries_out) + *num_entries_out = num_entries; + + return_trace (true); + } + + protected: + HBUINT nClasses; /* Number of classes, which is the number of indices + * in a single line in the state array. */ + NNOffsetTo + classTable; /* Offset to the class table. */ + NNOffsetTo, HBUINT> + stateArrayTable;/* Offset to the state array. */ + NNOffsetTo>, HBUINT> + entryTable; /* Offset to the entry array. */ + + public: + DEFINE_SIZE_STATIC (4 * sizeof (HBUINT)); +}; + +template +struct ClassTable +{ + unsigned int get_class (hb_codepoint_t glyph_id, unsigned int outOfRange) const + { + unsigned int i = glyph_id - firstGlyph; + return i >= classArray.len ? outOfRange : classArray.arrayZ[i]; + } + unsigned int get_class (hb_codepoint_t glyph_id, + unsigned int num_glyphs HB_UNUSED, + unsigned int outOfRange) const + { + return get_class (glyph_id, outOfRange); + } + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && classArray.sanitize (c)); + } + protected: + HBGlyphID16 firstGlyph; /* First glyph index included in the trimmed array. */ + Array16Of classArray; /* The class codes (indexed by glyph index minus + * firstGlyph). */ + public: + DEFINE_SIZE_ARRAY (4, classArray); +}; + +struct ObsoleteTypes +{ + static constexpr bool extended = false; + typedef HBUINT16 HBUINT; + typedef HBUINT8 HBUSHORT; + typedef ClassTable ClassTypeNarrow; + typedef ClassTable ClassTypeWide; + + template + static unsigned int offsetToIndex (unsigned int offset, + const void *base, + const T *array) + { + /* https://github.com/harfbuzz/harfbuzz/issues/3483 */ + /* If offset is less than base, return an offset that would + * result in an address half a 32bit address-space away, + * to make sure sanitize fails even on 32bit builds. */ + if (unlikely (offset < unsigned ((const char *) array - (const char *) base))) + return INT_MAX / T::static_size; + + /* https://github.com/harfbuzz/harfbuzz/issues/2816 */ + return (offset - unsigned ((const char *) array - (const char *) base)) / T::static_size; + } + template + static unsigned int byteOffsetToIndex (unsigned int offset, + const void *base, + const T *array) + { + return offsetToIndex (offset, base, array); + } + template + static unsigned int wordOffsetToIndex (unsigned int offset, + const void *base, + const T *array) + { + return offsetToIndex (2 * offset, base, array); + } +}; +struct ExtendedTypes +{ + static constexpr bool extended = true; + typedef HBUINT32 HBUINT; + typedef HBUINT16 HBUSHORT; + typedef Lookup ClassTypeNarrow; + typedef Lookup ClassTypeWide; + + template + static unsigned int offsetToIndex (unsigned int offset, + const void *base HB_UNUSED, + const T *array HB_UNUSED) + { + return offset; + } + template + static unsigned int byteOffsetToIndex (unsigned int offset, + const void *base HB_UNUSED, + const T *array HB_UNUSED) + { + return offset / 2; + } + template + static unsigned int wordOffsetToIndex (unsigned int offset, + const void *base HB_UNUSED, + const T *array HB_UNUSED) + { + return offset; + } +}; + +template +struct StateTableDriver +{ + using StateTableT = StateTable; + using EntryT = Entry; + + StateTableDriver (const StateTableT &machine_, + hb_buffer_t *buffer_, + hb_face_t *face_) : + machine (machine_), + buffer (buffer_), + num_glyphs (face_->get_num_glyphs ()) {} + + template + void drive (context_t *c) + { + if (!c->in_place) + buffer->clear_output (); + + int state = StateTableT::STATE_START_OF_TEXT; + for (buffer->idx = 0; buffer->successful;) + { + unsigned int klass = buffer->idx < buffer->len ? + machine.get_class (buffer->info[buffer->idx].codepoint, num_glyphs) : + (unsigned) StateTableT::CLASS_END_OF_TEXT; + DEBUG_MSG (APPLY, nullptr, "c%u at %u", klass, buffer->idx); + const EntryT &entry = machine.get_entry (state, klass); + const int next_state = machine.new_state (entry.newState); + + /* Conditions under which it's guaranteed safe-to-break before current glyph: + * + * 1. There was no action in this transition; and + * + * 2. If we break before current glyph, the results will be the same. That + * is guaranteed if: + * + * 2a. We were already in start-of-text state; or + * + * 2b. We are epsilon-transitioning to start-of-text state; or + * + * 2c. Starting from start-of-text state seeing current glyph: + * + * 2c'. There won't be any actions; and + * + * 2c". We would end up in the same state that we were going to end up + * in now, including whether epsilon-transitioning. + * + * and + * + * 3. If we break before current glyph, there won't be any end-of-text action + * after previous glyph. + * + * This triples the transitions we need to look up, but is worth returning + * granular unsafe-to-break results. See eg.: + * + * https://github.com/harfbuzz/harfbuzz/issues/2860 + */ + const EntryT *wouldbe_entry; + bool safe_to_break = + /* 1. */ + !c->is_actionable (this, entry) + && + /* 2. */ + ( + /* 2a. */ + state == StateTableT::STATE_START_OF_TEXT + || + /* 2b. */ + ( + (entry.flags & context_t::DontAdvance) && + next_state == StateTableT::STATE_START_OF_TEXT + ) + || + /* 2c. */ + ( + wouldbe_entry = &machine.get_entry (StateTableT::STATE_START_OF_TEXT, klass) + , + /* 2c'. */ + !c->is_actionable (this, *wouldbe_entry) + && + /* 2c". */ + ( + next_state == machine.new_state (wouldbe_entry->newState) + && + (entry.flags & context_t::DontAdvance) == (wouldbe_entry->flags & context_t::DontAdvance) + ) + ) + ) + && + /* 3. */ + !c->is_actionable (this, machine.get_entry (state, StateTableT::CLASS_END_OF_TEXT)) + ; + + if (!safe_to_break && buffer->backtrack_len () && buffer->idx < buffer->len) + buffer->unsafe_to_break_from_outbuffer (buffer->backtrack_len () - 1, buffer->idx + 1); + + c->transition (this, entry); + + state = next_state; + DEBUG_MSG (APPLY, nullptr, "s%d", state); + + if (buffer->idx == buffer->len || unlikely (!buffer->successful)) + break; + + if (!(entry.flags & context_t::DontAdvance) || buffer->max_ops-- <= 0) + (void) buffer->next_glyph (); + } + + if (!c->in_place) + buffer->sync (); + } + + public: + const StateTableT &machine; + hb_buffer_t *buffer; + unsigned int num_glyphs; +}; + + +struct ankr; + +struct hb_aat_apply_context_t : + hb_dispatch_context_t +{ + const char *get_name () { return "APPLY"; } + template + return_t dispatch (const T &obj) { return obj.apply (this); } + static return_t default_return_value () { return false; } + bool stop_sublookup_iteration (return_t r) const { return r; } + + const hb_ot_shape_plan_t *plan; + hb_font_t *font; + hb_face_t *face; + hb_buffer_t *buffer; + hb_sanitize_context_t sanitizer; + const ankr *ankr_table; + const OT::GDEF *gdef_table; + + /* Unused. For debug tracing only. */ + unsigned int lookup_index; + + HB_INTERNAL hb_aat_apply_context_t (const hb_ot_shape_plan_t *plan_, + hb_font_t *font_, + hb_buffer_t *buffer_, + hb_blob_t *blob = const_cast (&Null (hb_blob_t))); + + HB_INTERNAL ~hb_aat_apply_context_t (); + + HB_INTERNAL void set_ankr_table (const AAT::ankr *ankr_table_); + + void set_lookup_index (unsigned int i) { lookup_index = i; } +}; + + +} /* namespace AAT */ + + +#endif /* HB_AAT_LAYOUT_COMMON_HH */ diff --git a/libs/harfbuzz/src/hb-aat-layout-feat-table.hh b/libs/harfbuzz/src/hb-aat-layout-feat-table.hh new file mode 100644 index 00000000..815a1fd2 --- /dev/null +++ b/libs/harfbuzz/src/hb-aat-layout-feat-table.hh @@ -0,0 +1,222 @@ +/* + * Copyright © 2018 Ebrahim Byagowi + * + * 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. + */ + +#ifndef HB_AAT_LAYOUT_FEAT_TABLE_HH +#define HB_AAT_LAYOUT_FEAT_TABLE_HH + +#include "hb-aat-layout-common.hh" + +/* + * feat -- Feature Name + * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6feat.html + */ +#define HB_AAT_TAG_feat HB_TAG('f','e','a','t') + + +namespace AAT { + + +struct SettingName +{ + friend struct FeatureName; + + int cmp (hb_aat_layout_feature_selector_t key) const + { return (int) key - (int) setting; } + + hb_aat_layout_feature_selector_t get_selector () const + { return (hb_aat_layout_feature_selector_t) (unsigned) setting; } + + hb_aat_layout_feature_selector_info_t get_info (hb_aat_layout_feature_selector_t default_selector) const + { + return { + nameIndex, + (hb_aat_layout_feature_selector_t) (unsigned int) setting, + default_selector == HB_AAT_LAYOUT_FEATURE_SELECTOR_INVALID + ? (hb_aat_layout_feature_selector_t) (setting + 1) + : default_selector, + 0 + }; + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + protected: + HBUINT16 setting; /* The setting. */ + NameID nameIndex; /* The name table index for the setting's name. */ + public: + DEFINE_SIZE_STATIC (4); +}; +DECLARE_NULL_NAMESPACE_BYTES (AAT, SettingName); + +struct feat; + +struct FeatureName +{ + int cmp (hb_aat_layout_feature_type_t key) const + { return (int) key - (int) feature; } + + enum { + Exclusive = 0x8000, /* If set, the feature settings are mutually exclusive. */ + NotDefault = 0x4000, /* If clear, then the setting with an index of 0 in + * the setting name array for this feature should + * be taken as the default for the feature + * (if one is required). If set, then bits 0-15 of this + * featureFlags field contain the index of the setting + * which is to be taken as the default. */ + IndexMask = 0x00FF /* If bits 30 and 31 are set, then these sixteen bits + * indicate the index of the setting in the setting name + * array for this feature which should be taken + * as the default. */ + }; + + unsigned int get_selector_infos (unsigned int start_offset, + unsigned int *selectors_count, /* IN/OUT. May be NULL. */ + hb_aat_layout_feature_selector_info_t *selectors, /* OUT. May be NULL. */ + unsigned int *pdefault_index, /* OUT. May be NULL. */ + const void *base) const + { + hb_array_t< const SettingName> settings_table = (base+settingTableZ).as_array (nSettings); + + static_assert (Index::NOT_FOUND_INDEX == HB_AAT_LAYOUT_NO_SELECTOR_INDEX, ""); + + hb_aat_layout_feature_selector_t default_selector = HB_AAT_LAYOUT_FEATURE_SELECTOR_INVALID; + unsigned int default_index = Index::NOT_FOUND_INDEX; + if (featureFlags & Exclusive) + { + default_index = (featureFlags & NotDefault) ? featureFlags & IndexMask : 0; + default_selector = settings_table[default_index].get_selector (); + } + if (pdefault_index) + *pdefault_index = default_index; + + if (selectors_count) + { + + settings_table.sub_array (start_offset, selectors_count) + | hb_map ([=] (const SettingName& setting) { return setting.get_info (default_selector); }) + | hb_sink (hb_array (selectors, *selectors_count)) + ; + } + return settings_table.length; + } + + hb_aat_layout_feature_type_t get_feature_type () const + { return (hb_aat_layout_feature_type_t) (unsigned int) feature; } + + hb_ot_name_id_t get_feature_name_id () const { return nameIndex; } + + bool is_exclusive () const { return featureFlags & Exclusive; } + + /* A FeatureName with no settings is meaningless */ + bool has_data () const { return nSettings; } + + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + (base+settingTableZ).sanitize (c, nSettings))); + } + + protected: + HBUINT16 feature; /* Feature type. */ + HBUINT16 nSettings; /* The number of records in the setting name array. */ + NNOffset32To> + settingTableZ; /* Offset in bytes from the beginning of this table to + * this feature's setting name array. The actual type of + * record this offset refers to will depend on the + * exclusivity value, as described below. */ + HBUINT16 featureFlags; /* Single-bit flags associated with the feature type. */ + HBINT16 nameIndex; /* The name table index for the feature's name. + * This index has values greater than 255 and + * less than 32768. */ + public: + DEFINE_SIZE_STATIC (12); +}; + +struct feat +{ + static constexpr hb_tag_t tableTag = HB_AAT_TAG_feat; + + bool has_data () const { return version.to_int (); } + + unsigned int get_feature_types (unsigned int start_offset, + unsigned int *count, + hb_aat_layout_feature_type_t *features) const + { + if (count) + { + + namesZ.as_array (featureNameCount).sub_array (start_offset, count) + | hb_map (&FeatureName::get_feature_type) + | hb_sink (hb_array (features, *count)) + ; + } + return featureNameCount; + } + + bool exposes_feature (hb_aat_layout_feature_type_t feature_type) const + { return get_feature (feature_type).has_data (); } + + const FeatureName& get_feature (hb_aat_layout_feature_type_t feature_type) const + { return namesZ.bsearch (featureNameCount, feature_type); } + + hb_ot_name_id_t get_feature_name_id (hb_aat_layout_feature_type_t feature) const + { return get_feature (feature).get_feature_name_id (); } + + unsigned int get_selector_infos (hb_aat_layout_feature_type_t feature_type, + unsigned int start_offset, + unsigned int *selectors_count, /* IN/OUT. May be NULL. */ + hb_aat_layout_feature_selector_info_t *selectors, /* OUT. May be NULL. */ + unsigned int *default_index /* OUT. May be NULL. */) const + { + return get_feature (feature_type).get_selector_infos (start_offset, selectors_count, selectors, + default_index, this); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + version.major == 1 && + namesZ.sanitize (c, featureNameCount, this))); + } + + protected: + FixedVersion<>version; /* Version number of the feature name table + * (0x00010000 for the current version). */ + HBUINT16 featureNameCount; + /* The number of entries in the feature name array. */ + HBUINT16 reserved1; /* Reserved (set to zero). */ + HBUINT32 reserved2; /* Reserved (set to zero). */ + SortedUnsizedArrayOf + namesZ; /* The feature name array. */ + public: + DEFINE_SIZE_ARRAY (12, namesZ); +}; + +} /* namespace AAT */ + +#endif /* HB_AAT_LAYOUT_FEAT_TABLE_HH */ diff --git a/libs/harfbuzz/src/hb-aat-layout-just-table.hh b/libs/harfbuzz/src/hb-aat-layout-just-table.hh new file mode 100644 index 00000000..8fd3990f --- /dev/null +++ b/libs/harfbuzz/src/hb-aat-layout-just-table.hh @@ -0,0 +1,417 @@ +/* + * Copyright © 2018 Ebrahim Byagowi + * + * 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. + */ + +#ifndef HB_AAT_LAYOUT_JUST_TABLE_HH +#define HB_AAT_LAYOUT_JUST_TABLE_HH + +#include "hb-aat-layout-common.hh" +#include "hb-ot-layout.hh" +#include "hb-open-type.hh" + +#include "hb-aat-layout-morx-table.hh" + +/* + * just -- Justification + * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6just.html + */ +#define HB_AAT_TAG_just HB_TAG('j','u','s','t') + + +namespace AAT { + +using namespace OT; + + +struct ActionSubrecordHeader +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + HBUINT16 actionClass; /* The JustClass value associated with this + * ActionSubrecord. */ + HBUINT16 actionType; /* The type of postcompensation action. */ + HBUINT16 actionLength; /* Length of this ActionSubrecord record, which + * must be a multiple of 4. */ + public: + DEFINE_SIZE_STATIC (6); +}; + +struct DecompositionAction +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + ActionSubrecordHeader + header; + F16DOT16 lowerLimit; /* If the distance factor is less than this value, + * then the ligature is decomposed. */ + F16DOT16 upperLimit; /* If the distance factor is greater than this value, + * then the ligature is decomposed. */ + HBUINT16 order; /* Numerical order in which this ligature will + * be decomposed; you may want infrequent ligatures + * to decompose before more frequent ones. The ligatures + * on the line of text will decompose in increasing + * value of this field. */ + Array16Of + decomposedglyphs; + /* Number of 16-bit glyph indexes that follow; + * the ligature will be decomposed into these glyphs. + * + * Array of decomposed glyphs. */ + public: + DEFINE_SIZE_ARRAY (18, decomposedglyphs); +}; + +struct UnconditionalAddGlyphAction +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + protected: + ActionSubrecordHeader + header; + HBGlyphID16 addGlyph; /* Glyph that should be added if the distance factor + * is growing. */ + + public: + DEFINE_SIZE_STATIC (8); +}; + +struct ConditionalAddGlyphAction +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + protected: + ActionSubrecordHeader + header; + F16DOT16 substThreshold; /* Distance growth factor (in ems) at which + * this glyph is replaced and the growth factor + * recalculated. */ + HBGlyphID16 addGlyph; /* Glyph to be added as kashida. If this value is + * 0xFFFF, no extra glyph will be added. Note that + * generally when a glyph is added, justification + * will need to be redone. */ + HBGlyphID16 substGlyph; /* Glyph to be substituted for this glyph if the + * growth factor equals or exceeds the value of + * substThreshold. */ + public: + DEFINE_SIZE_STATIC (14); +}; + +struct DuctileGlyphAction +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + protected: + ActionSubrecordHeader + header; + HBUINT32 variationAxis; /* The 4-byte tag identifying the ductile axis. + * This would normally be 0x64756374 ('duct'), + * but you may use any axis the font contains. */ + F16DOT16 minimumLimit; /* The lowest value for the ductility axis that + * still yields an acceptable appearance. Normally + * this will be 1.0. */ + F16DOT16 noStretchValue; /* This is the default value that corresponds to + * no change in appearance. Normally, this will + * be 1.0. */ + F16DOT16 maximumLimit; /* The highest value for the ductility axis that + * still yields an acceptable appearance. */ + public: + DEFINE_SIZE_STATIC (22); +}; + +struct RepeatedAddGlyphAction +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + protected: + ActionSubrecordHeader + header; + HBUINT16 flags; /* Currently unused; set to 0. */ + HBGlyphID16 glyph; /* Glyph that should be added if the distance factor + * is growing. */ + public: + DEFINE_SIZE_STATIC (10); +}; + +struct ActionSubrecord +{ + unsigned int get_length () const { return u.header.actionLength; } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (unlikely (!c->check_struct (this))) + return_trace (false); + + switch (u.header.actionType) + { + case 0: return_trace (u.decompositionAction.sanitize (c)); + case 1: return_trace (u.unconditionalAddGlyphAction.sanitize (c)); + case 2: return_trace (u.conditionalAddGlyphAction.sanitize (c)); + // case 3: return_trace (u.stretchGlyphAction.sanitize (c)); + case 4: return_trace (u.decompositionAction.sanitize (c)); + case 5: return_trace (u.decompositionAction.sanitize (c)); + default: return_trace (true); + } + } + + protected: + union { + ActionSubrecordHeader header; + DecompositionAction decompositionAction; + UnconditionalAddGlyphAction unconditionalAddGlyphAction; + ConditionalAddGlyphAction conditionalAddGlyphAction; + /* StretchGlyphAction stretchGlyphAction; -- Not supported by CoreText */ + DuctileGlyphAction ductileGlyphAction; + RepeatedAddGlyphAction repeatedAddGlyphAction; + } u; /* Data. The format of this data depends on + * the value of the actionType field. */ + public: + DEFINE_SIZE_UNION (6, header); +}; + +struct PostcompensationActionChain +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (unlikely (!c->check_struct (this))) + return_trace (false); + + unsigned int offset = min_size; + for (unsigned int i = 0; i < count; i++) + { + const ActionSubrecord& subrecord = StructAtOffset (this, offset); + if (unlikely (!subrecord.sanitize (c))) return_trace (false); + offset += subrecord.get_length (); + } + + return_trace (true); + } + + protected: + HBUINT32 count; + + public: + DEFINE_SIZE_STATIC (4); +}; + +struct JustWidthDeltaEntry +{ + enum Flags + { + Reserved1 =0xE000,/* Reserved. You should set these bits to zero. */ + UnlimiteGap =0x1000,/* The glyph can take unlimited gap. When this + * glyph participates in the justification process, + * it and any other glyphs on the line having this + * bit set absorb all the remaining gap. */ + Reserved2 =0x0FF0,/* Reserved. You should set these bits to zero. */ + Priority =0x000F /* The justification priority of the glyph. */ + }; + + enum Priority + { + Kashida = 0, /* Kashida priority. This is the highest priority + * during justification. */ + Whitespace = 1, /* Whitespace priority. Any whitespace glyphs (as + * identified in the glyph properties table) will + * get this priority. */ + InterCharacter = 2, /* Inter-character priority. Give this to any + * remaining glyphs. */ + NullPriority = 3 /* Null priority. You should set this priority for + * glyphs that only participate in justification + * after the above priorities. Normally all glyphs + * have one of the previous three values. If you + * don't want a glyph to participate in justification, + * and you don't want to set its factors to zero, + * you may instead assign it to the null priority. */ + }; + + protected: + F16DOT16 beforeGrowLimit;/* The ratio by which the advance width of the + * glyph is permitted to grow on the left or top side. */ + F16DOT16 beforeShrinkLimit; + /* The ratio by which the advance width of the + * glyph is permitted to shrink on the left or top side. */ + F16DOT16 afterGrowLimit; /* The ratio by which the advance width of the glyph + * is permitted to shrink on the left or top side. */ + F16DOT16 afterShrinkLimit; + /* The ratio by which the advance width of the glyph + * is at most permitted to shrink on the right or + * bottom side. */ + HBUINT16 growFlags; /* Flags controlling the grow case. */ + HBUINT16 shrinkFlags; /* Flags controlling the shrink case. */ + + public: + DEFINE_SIZE_STATIC (20); +}; + +struct WidthDeltaPair +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + protected: + HBUINT32 justClass; /* The justification category associated + * with the wdRecord field. Only 7 bits of + * this field are used. (The other bits are + * used as padding to guarantee longword + * alignment of the following record). */ + JustWidthDeltaEntry + wdRecord; /* The actual width delta record. */ + + public: + DEFINE_SIZE_STATIC (24); +}; + +typedef OT::Array32Of WidthDeltaCluster; + +struct JustificationCategory +{ + typedef void EntryData; + + enum Flags + { + SetMark =0x8000,/* If set, make the current glyph the marked + * glyph. */ + DontAdvance =0x4000,/* If set, don't advance to the next glyph before + * going to the new state. */ + MarkCategory =0x3F80,/* The justification category for the marked + * glyph if nonzero. */ + CurrentCategory =0x007F /* The justification category for the current + * glyph if nonzero. */ + }; + + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + morphHeader.sanitize (c) && + stHeader.sanitize (c))); + } + + protected: + ChainSubtable + morphHeader; /* Metamorphosis-style subtable header. */ + StateTable + stHeader; /* The justification insertion state table header */ + public: + DEFINE_SIZE_STATIC (30); +}; + +struct JustificationHeader +{ + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + justClassTable.sanitize (c, base, base) && + wdcTable.sanitize (c, base) && + pcTable.sanitize (c, base) && + lookupTable.sanitize (c, base))); + } + + protected: + Offset16To + justClassTable; /* Offset to the justification category state table. */ + Offset16To + wdcTable; /* Offset from start of justification table to start + * of the subtable containing the width delta factors + * for the glyphs in your font. + * + * The width delta clusters table. */ + Offset16To + pcTable; /* Offset from start of justification table to start + * of postcompensation subtable (set to zero if none). + * + * The postcompensation subtable, if present in the font. */ + Lookup> + lookupTable; /* Lookup table associating glyphs with width delta + * clusters. See the description of Width Delta Clusters + * table for details on how to interpret the lookup values. */ + + public: + DEFINE_SIZE_MIN (8); +}; + +struct just +{ + static constexpr hb_tag_t tableTag = HB_AAT_TAG_just; + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + + return_trace (likely (c->check_struct (this) && + version.major == 1 && + horizData.sanitize (c, this, this) && + vertData.sanitize (c, this, this))); + } + + protected: + FixedVersion<>version; /* Version of the justification table + * (0x00010000u for version 1.0). */ + HBUINT16 format; /* Format of the justification table (set to 0). */ + Offset16To + horizData; /* Byte offset from the start of the justification table + * to the header for tables that contain justification + * information for horizontal text. + * If you are not including this information, + * store 0. */ + Offset16To + vertData; /* ditto, vertical */ + + public: + DEFINE_SIZE_STATIC (10); +}; + +} /* namespace AAT */ + + +#endif /* HB_AAT_LAYOUT_JUST_TABLE_HH */ diff --git a/libs/harfbuzz/src/hb-aat-layout-kerx-table.hh b/libs/harfbuzz/src/hb-aat-layout-kerx-table.hh new file mode 100644 index 00000000..995492cd --- /dev/null +++ b/libs/harfbuzz/src/hb-aat-layout-kerx-table.hh @@ -0,0 +1,999 @@ +/* + * Copyright © 2018 Ebrahim Byagowi + * Copyright © 2018 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 HB_AAT_LAYOUT_KERX_TABLE_HH +#define HB_AAT_LAYOUT_KERX_TABLE_HH + +#include "hb-kern.hh" +#include "hb-aat-layout-ankr-table.hh" + +/* + * kerx -- Extended Kerning + * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6kerx.html + */ +#define HB_AAT_TAG_kerx HB_TAG('k','e','r','x') + + +namespace AAT { + +using namespace OT; + + +static inline int +kerxTupleKern (int value, + unsigned int tupleCount, + const void *base, + hb_aat_apply_context_t *c) +{ + if (likely (!tupleCount || !c)) return value; + + unsigned int offset = value; + const FWORD *pv = &StructAtOffset (base, offset); + if (unlikely (!c->sanitizer.check_array (pv, tupleCount))) return 0; + return *pv; +} + + +struct hb_glyph_pair_t +{ + hb_codepoint_t left; + hb_codepoint_t right; +}; + +struct KernPair +{ + int get_kerning () const { return value; } + + int cmp (const hb_glyph_pair_t &o) const + { + int ret = left.cmp (o.left); + if (ret) return ret; + return right.cmp (o.right); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + protected: + HBGlyphID16 left; + HBGlyphID16 right; + FWORD value; + public: + DEFINE_SIZE_STATIC (6); +}; + +template +struct KerxSubTableFormat0 +{ + int get_kerning (hb_codepoint_t left, hb_codepoint_t right, + hb_aat_apply_context_t *c = nullptr) const + { + hb_glyph_pair_t pair = {left, right}; + int v = pairs.bsearch (pair).get_kerning (); + return kerxTupleKern (v, header.tuple_count (), this, c); + } + + bool apply (hb_aat_apply_context_t *c) const + { + TRACE_APPLY (this); + + if (!c->plan->requested_kerning) + return false; + + if (header.coverage & header.Backwards) + return false; + + accelerator_t accel (*this, c); + hb_kern_machine_t machine (accel, header.coverage & header.CrossStream); + machine.kern (c->font, c->buffer, c->plan->kern_mask); + + return_trace (true); + } + + struct accelerator_t + { + const KerxSubTableFormat0 &table; + hb_aat_apply_context_t *c; + + accelerator_t (const KerxSubTableFormat0 &table_, + hb_aat_apply_context_t *c_) : + table (table_), c (c_) {} + + int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const + { return table.get_kerning (left, right, c); } + }; + + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (pairs.sanitize (c))); + } + + protected: + KernSubTableHeader header; + BinSearchArrayOf + pairs; /* Sorted kern records. */ + public: + DEFINE_SIZE_ARRAY (KernSubTableHeader::static_size + 16, pairs); +}; + + +template +struct Format1Entry; + +template <> +struct Format1Entry +{ + enum Flags + { + Push = 0x8000, /* If set, push this glyph on the kerning stack. */ + DontAdvance = 0x4000, /* If set, don't advance to the next glyph + * before going to the new state. */ + Reset = 0x2000, /* If set, reset the kerning data (clear the stack) */ + Reserved = 0x1FFF, /* Not used; set to 0. */ + }; + + struct EntryData + { + HBUINT16 kernActionIndex;/* Index into the kerning value array. If + * this index is 0xFFFF, then no kerning + * is to be performed. */ + public: + DEFINE_SIZE_STATIC (2); + }; + + static bool performAction (const Entry &entry) + { return entry.data.kernActionIndex != 0xFFFF; } + + static unsigned int kernActionIndex (const Entry &entry) + { return entry.data.kernActionIndex; } +}; +template <> +struct Format1Entry +{ + enum Flags + { + Push = 0x8000, /* If set, push this glyph on the kerning stack. */ + DontAdvance = 0x4000, /* If set, don't advance to the next glyph + * before going to the new state. */ + Offset = 0x3FFF, /* Byte offset from beginning of subtable to the + * value table for the glyphs on the kerning stack. */ + + Reset = 0x0000, /* Not supported? */ + }; + + typedef void EntryData; + + static bool performAction (const Entry &entry) + { return entry.flags & Offset; } + + static unsigned int kernActionIndex (const Entry &entry) + { return entry.flags & Offset; } +}; + +template +struct KerxSubTableFormat1 +{ + typedef typename KernSubTableHeader::Types Types; + typedef typename Types::HBUINT HBUINT; + + typedef Format1Entry Format1EntryT; + typedef typename Format1EntryT::EntryData EntryData; + + struct driver_context_t + { + static constexpr bool in_place = true; + enum + { + DontAdvance = Format1EntryT::DontAdvance, + }; + + driver_context_t (const KerxSubTableFormat1 *table_, + hb_aat_apply_context_t *c_) : + c (c_), + table (table_), + /* Apparently the offset kernAction is from the beginning of the state-machine, + * similar to offsets in morx table, NOT from beginning of this table, like + * other subtables in kerx. Discovered via testing. */ + kernAction (&table->machine + table->kernAction), + depth (0), + crossStream (table->header.coverage & table->header.CrossStream) {} + + bool is_actionable (StateTableDriver *driver HB_UNUSED, + const Entry &entry) + { return Format1EntryT::performAction (entry); } + void transition (StateTableDriver *driver, + const Entry &entry) + { + hb_buffer_t *buffer = driver->buffer; + unsigned int flags = entry.flags; + + if (flags & Format1EntryT::Reset) + depth = 0; + + if (flags & Format1EntryT::Push) + { + if (likely (depth < ARRAY_LENGTH (stack))) + stack[depth++] = buffer->idx; + else + depth = 0; /* Probably not what CoreText does, but better? */ + } + + if (Format1EntryT::performAction (entry) && depth) + { + unsigned int tuple_count = hb_max (1u, table->header.tuple_count ()); + + unsigned int kern_idx = Format1EntryT::kernActionIndex (entry); + kern_idx = Types::byteOffsetToIndex (kern_idx, &table->machine, kernAction.arrayZ); + const FWORD *actions = &kernAction[kern_idx]; + if (!c->sanitizer.check_array (actions, depth, tuple_count)) + { + depth = 0; + return; + } + + hb_mask_t kern_mask = c->plan->kern_mask; + + /* From Apple 'kern' spec: + * "Each pops one glyph from the kerning stack and applies the kerning value to it. + * The end of the list is marked by an odd value... */ + bool last = false; + while (!last && depth) + { + unsigned int idx = stack[--depth]; + int v = *actions; + actions += tuple_count; + if (idx >= buffer->len) continue; + + /* "The end of the list is marked by an odd value..." */ + last = v & 1; + v &= ~1; + + hb_glyph_position_t &o = buffer->pos[idx]; + + if (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction)) + { + if (crossStream) + { + /* The following flag is undocumented in the spec, but described + * in the 'kern' table example. */ + if (v == -0x8000) + { + o.attach_type() = OT::Layout::GPOS_impl::ATTACH_TYPE_NONE; + o.attach_chain() = 0; + o.y_offset = 0; + } + else if (o.attach_type()) + { + o.y_offset += c->font->em_scale_y (v); + buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT; + } + } + else if (buffer->info[idx].mask & kern_mask) + { + o.x_advance += c->font->em_scale_x (v); + o.x_offset += c->font->em_scale_x (v); + } + } + else + { + if (crossStream) + { + /* CoreText doesn't do crossStream kerning in vertical. We do. */ + if (v == -0x8000) + { + o.attach_type() = OT::Layout::GPOS_impl::ATTACH_TYPE_NONE; + o.attach_chain() = 0; + o.x_offset = 0; + } + else if (o.attach_type()) + { + o.x_offset += c->font->em_scale_x (v); + buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT; + } + } + else if (buffer->info[idx].mask & kern_mask) + { + o.y_advance += c->font->em_scale_y (v); + o.y_offset += c->font->em_scale_y (v); + } + } + } + } + } + + private: + hb_aat_apply_context_t *c; + const KerxSubTableFormat1 *table; + const UnsizedArrayOf &kernAction; + unsigned int stack[8]; + unsigned int depth; + bool crossStream; + }; + + bool apply (hb_aat_apply_context_t *c) const + { + TRACE_APPLY (this); + + if (!c->plan->requested_kerning && + !(header.coverage & header.CrossStream)) + return false; + + driver_context_t dc (this, c); + + StateTableDriver driver (machine, c->buffer, c->font->face); + driver.drive (&dc); + + return_trace (true); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + /* The rest of array sanitizations are done at run-time. */ + return_trace (likely (c->check_struct (this) && + machine.sanitize (c))); + } + + protected: + KernSubTableHeader header; + StateTable machine; + NNOffsetTo, HBUINT> kernAction; + public: + DEFINE_SIZE_STATIC (KernSubTableHeader::static_size + 5 * sizeof (HBUINT)); +}; + +template +struct KerxSubTableFormat2 +{ + typedef typename KernSubTableHeader::Types Types; + typedef typename Types::HBUINT HBUINT; + + int get_kerning (hb_codepoint_t left, hb_codepoint_t right, + hb_aat_apply_context_t *c) const + { + unsigned int num_glyphs = c->sanitizer.get_num_glyphs (); + unsigned int l = (this+leftClassTable).get_class (left, num_glyphs, 0); + unsigned int r = (this+rightClassTable).get_class (right, num_glyphs, 0); + + const UnsizedArrayOf &arrayZ = this+array; + unsigned int kern_idx = l + r; + kern_idx = Types::offsetToIndex (kern_idx, this, arrayZ.arrayZ); + const FWORD *v = &arrayZ[kern_idx]; + if (unlikely (!v->sanitize (&c->sanitizer))) return 0; + + return kerxTupleKern (*v, header.tuple_count (), this, c); + } + + bool apply (hb_aat_apply_context_t *c) const + { + TRACE_APPLY (this); + + if (!c->plan->requested_kerning) + return false; + + if (header.coverage & header.Backwards) + return false; + + accelerator_t accel (*this, c); + hb_kern_machine_t machine (accel, header.coverage & header.CrossStream); + machine.kern (c->font, c->buffer, c->plan->kern_mask); + + return_trace (true); + } + + struct accelerator_t + { + const KerxSubTableFormat2 &table; + hb_aat_apply_context_t *c; + + accelerator_t (const KerxSubTableFormat2 &table_, + hb_aat_apply_context_t *c_) : + table (table_), c (c_) {} + + int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const + { return table.get_kerning (left, right, c); } + }; + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + leftClassTable.sanitize (c, this) && + rightClassTable.sanitize (c, this) && + c->check_range (this, array))); + } + + protected: + KernSubTableHeader header; + HBUINT rowWidth; /* The width, in bytes, of a row in the table. */ + NNOffsetTo + leftClassTable; /* Offset from beginning of this subtable to + * left-hand class table. */ + NNOffsetTo + rightClassTable;/* Offset from beginning of this subtable to + * right-hand class table. */ + NNOffsetTo, HBUINT> + array; /* Offset from beginning of this subtable to + * the start of the kerning array. */ + public: + DEFINE_SIZE_STATIC (KernSubTableHeader::static_size + 4 * sizeof (HBUINT)); +}; + +template +struct KerxSubTableFormat4 +{ + typedef ExtendedTypes Types; + + struct EntryData + { + HBUINT16 ankrActionIndex;/* Either 0xFFFF (for no action) or the index of + * the action to perform. */ + public: + DEFINE_SIZE_STATIC (2); + }; + + struct driver_context_t + { + static constexpr bool in_place = true; + enum Flags + { + Mark = 0x8000, /* If set, remember this glyph as the marked glyph. */ + DontAdvance = 0x4000, /* If set, don't advance to the next glyph before + * going to the new state. */ + Reserved = 0x3FFF, /* Not used; set to 0. */ + }; + + enum SubTableFlags + { + ActionType = 0xC0000000, /* A two-bit field containing the action type. */ + Unused = 0x3F000000, /* Unused - must be zero. */ + Offset = 0x00FFFFFF, /* Masks the offset in bytes from the beginning + * of the subtable to the beginning of the control + * point table. */ + }; + + driver_context_t (const KerxSubTableFormat4 *table, + hb_aat_apply_context_t *c_) : + c (c_), + action_type ((table->flags & ActionType) >> 30), + ankrData ((HBUINT16 *) ((const char *) &table->machine + (table->flags & Offset))), + mark_set (false), + mark (0) {} + + bool is_actionable (StateTableDriver *driver HB_UNUSED, + const Entry &entry) + { return entry.data.ankrActionIndex != 0xFFFF; } + void transition (StateTableDriver *driver, + const Entry &entry) + { + hb_buffer_t *buffer = driver->buffer; + + if (mark_set && entry.data.ankrActionIndex != 0xFFFF && buffer->idx < buffer->len) + { + hb_glyph_position_t &o = buffer->cur_pos(); + switch (action_type) + { + case 0: /* Control Point Actions.*/ + { + /* Indexed into glyph outline. */ + /* Each action (record in ankrData) contains two 16-bit fields, so we must + double the ankrActionIndex to get the correct offset here. */ + const HBUINT16 *data = &ankrData[entry.data.ankrActionIndex * 2]; + if (!c->sanitizer.check_array (data, 2)) return; + unsigned int markControlPoint = *data++; + unsigned int currControlPoint = *data++; + hb_position_t markX = 0; + hb_position_t markY = 0; + hb_position_t currX = 0; + hb_position_t currY = 0; + if (!c->font->get_glyph_contour_point_for_origin (c->buffer->info[mark].codepoint, + markControlPoint, + HB_DIRECTION_LTR /*XXX*/, + &markX, &markY) || + !c->font->get_glyph_contour_point_for_origin (c->buffer->cur ().codepoint, + currControlPoint, + HB_DIRECTION_LTR /*XXX*/, + &currX, &currY)) + return; + + o.x_offset = markX - currX; + o.y_offset = markY - currY; + } + break; + + case 1: /* Anchor Point Actions. */ + { + /* Indexed into 'ankr' table. */ + /* Each action (record in ankrData) contains two 16-bit fields, so we must + double the ankrActionIndex to get the correct offset here. */ + const HBUINT16 *data = &ankrData[entry.data.ankrActionIndex * 2]; + if (!c->sanitizer.check_array (data, 2)) return; + unsigned int markAnchorPoint = *data++; + unsigned int currAnchorPoint = *data++; + const Anchor &markAnchor = c->ankr_table->get_anchor (c->buffer->info[mark].codepoint, + markAnchorPoint, + c->sanitizer.get_num_glyphs ()); + const Anchor &currAnchor = c->ankr_table->get_anchor (c->buffer->cur ().codepoint, + currAnchorPoint, + c->sanitizer.get_num_glyphs ()); + + o.x_offset = c->font->em_scale_x (markAnchor.xCoordinate) - c->font->em_scale_x (currAnchor.xCoordinate); + o.y_offset = c->font->em_scale_y (markAnchor.yCoordinate) - c->font->em_scale_y (currAnchor.yCoordinate); + } + break; + + case 2: /* Control Point Coordinate Actions. */ + { + /* Each action contains four 16-bit fields, so we multiply the ankrActionIndex + by 4 to get the correct offset for the given action. */ + const FWORD *data = (const FWORD *) &ankrData[entry.data.ankrActionIndex * 4]; + if (!c->sanitizer.check_array (data, 4)) return; + int markX = *data++; + int markY = *data++; + int currX = *data++; + int currY = *data++; + + o.x_offset = c->font->em_scale_x (markX) - c->font->em_scale_x (currX); + o.y_offset = c->font->em_scale_y (markY) - c->font->em_scale_y (currY); + } + break; + } + o.attach_type() = OT::Layout::GPOS_impl::ATTACH_TYPE_MARK; + o.attach_chain() = (int) mark - (int) buffer->idx; + buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT; + } + + if (entry.flags & Mark) + { + mark_set = true; + mark = buffer->idx; + } + } + + private: + hb_aat_apply_context_t *c; + unsigned int action_type; + const HBUINT16 *ankrData; + bool mark_set; + unsigned int mark; + }; + + bool apply (hb_aat_apply_context_t *c) const + { + TRACE_APPLY (this); + + driver_context_t dc (this, c); + + StateTableDriver driver (machine, c->buffer, c->font->face); + driver.drive (&dc); + + return_trace (true); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + /* The rest of array sanitizations are done at run-time. */ + return_trace (likely (c->check_struct (this) && + machine.sanitize (c))); + } + + protected: + KernSubTableHeader header; + StateTable machine; + HBUINT32 flags; + public: + DEFINE_SIZE_STATIC (KernSubTableHeader::static_size + 20); +}; + +template +struct KerxSubTableFormat6 +{ + enum Flags + { + ValuesAreLong = 0x00000001, + }; + + bool is_long () const { return flags & ValuesAreLong; } + + int get_kerning (hb_codepoint_t left, hb_codepoint_t right, + hb_aat_apply_context_t *c) const + { + unsigned int num_glyphs = c->sanitizer.get_num_glyphs (); + if (is_long ()) + { + const typename U::Long &t = u.l; + unsigned int l = (this+t.rowIndexTable).get_value_or_null (left, num_glyphs); + unsigned int r = (this+t.columnIndexTable).get_value_or_null (right, num_glyphs); + unsigned int offset = l + r; + if (unlikely (offset < l)) return 0; /* Addition overflow. */ + if (unlikely (hb_unsigned_mul_overflows (offset, sizeof (FWORD32)))) return 0; + const FWORD32 *v = &StructAtOffset (&(this+t.array), offset * sizeof (FWORD32)); + if (unlikely (!v->sanitize (&c->sanitizer))) return 0; + return kerxTupleKern (*v, header.tuple_count (), &(this+vector), c); + } + else + { + const typename U::Short &t = u.s; + unsigned int l = (this+t.rowIndexTable).get_value_or_null (left, num_glyphs); + unsigned int r = (this+t.columnIndexTable).get_value_or_null (right, num_glyphs); + unsigned int offset = l + r; + const FWORD *v = &StructAtOffset (&(this+t.array), offset * sizeof (FWORD)); + if (unlikely (!v->sanitize (&c->sanitizer))) return 0; + return kerxTupleKern (*v, header.tuple_count (), &(this+vector), c); + } + } + + bool apply (hb_aat_apply_context_t *c) const + { + TRACE_APPLY (this); + + if (!c->plan->requested_kerning) + return false; + + if (header.coverage & header.Backwards) + return false; + + accelerator_t accel (*this, c); + hb_kern_machine_t machine (accel, header.coverage & header.CrossStream); + machine.kern (c->font, c->buffer, c->plan->kern_mask); + + return_trace (true); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + (is_long () ? + ( + u.l.rowIndexTable.sanitize (c, this) && + u.l.columnIndexTable.sanitize (c, this) && + c->check_range (this, u.l.array) + ) : ( + u.s.rowIndexTable.sanitize (c, this) && + u.s.columnIndexTable.sanitize (c, this) && + c->check_range (this, u.s.array) + )) && + (header.tuple_count () == 0 || + c->check_range (this, vector)))); + } + + struct accelerator_t + { + const KerxSubTableFormat6 &table; + hb_aat_apply_context_t *c; + + accelerator_t (const KerxSubTableFormat6 &table_, + hb_aat_apply_context_t *c_) : + table (table_), c (c_) {} + + int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const + { return table.get_kerning (left, right, c); } + }; + + protected: + KernSubTableHeader header; + HBUINT32 flags; + HBUINT16 rowCount; + HBUINT16 columnCount; + union U + { + struct Long + { + NNOffset32To> rowIndexTable; + NNOffset32To> columnIndexTable; + NNOffset32To> array; + } l; + struct Short + { + NNOffset32To> rowIndexTable; + NNOffset32To> columnIndexTable; + NNOffset32To> array; + } s; + } u; + NNOffset32To> vector; + public: + DEFINE_SIZE_STATIC (KernSubTableHeader::static_size + 24); +}; + + +struct KerxSubTableHeader +{ + typedef ExtendedTypes Types; + + unsigned tuple_count () const { return tupleCount; } + bool is_horizontal () const { return !(coverage & Vertical); } + + enum Coverage + { + Vertical = 0x80000000u, /* Set if table has vertical kerning values. */ + CrossStream = 0x40000000u, /* Set if table has cross-stream kerning values. */ + Variation = 0x20000000u, /* Set if table has variation kerning values. */ + Backwards = 0x10000000u, /* If clear, process the glyphs forwards, that + * is, from first to last in the glyph stream. + * If we, process them from last to first. + * This flag only applies to state-table based + * 'kerx' subtables (types 1 and 4). */ + Reserved = 0x0FFFFF00u, /* Reserved, set to zero. */ + SubtableType= 0x000000FFu, /* Subtable type. */ + }; + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + public: + HBUINT32 length; + HBUINT32 coverage; + HBUINT32 tupleCount; + public: + DEFINE_SIZE_STATIC (12); +}; + +struct KerxSubTable +{ + friend struct kerx; + + unsigned int get_size () const { return u.header.length; } + unsigned int get_type () const { return u.header.coverage & u.header.SubtableType; } + + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const + { + unsigned int subtable_type = get_type (); + TRACE_DISPATCH (this, subtable_type); + switch (subtable_type) { + case 0: return_trace (c->dispatch (u.format0, std::forward (ds)...)); + case 1: return_trace (c->dispatch (u.format1, std::forward (ds)...)); + case 2: return_trace (c->dispatch (u.format2, std::forward (ds)...)); + case 4: return_trace (c->dispatch (u.format4, std::forward (ds)...)); + case 6: return_trace (c->dispatch (u.format6, std::forward (ds)...)); + default: return_trace (c->default_return_value ()); + } + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (!u.header.sanitize (c) || + u.header.length <= u.header.static_size || + !c->check_range (this, u.header.length)) + return_trace (false); + + return_trace (dispatch (c)); + } + + public: + union { + KerxSubTableHeader header; + KerxSubTableFormat0 format0; + KerxSubTableFormat1 format1; + KerxSubTableFormat2 format2; + KerxSubTableFormat4 format4; + KerxSubTableFormat6 format6; + } u; + public: + DEFINE_SIZE_MIN (12); +}; + + +/* + * The 'kerx' Table + */ + +template +struct KerxTable +{ + /* https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern */ + const T* thiz () const { return static_cast (this); } + + bool has_state_machine () const + { + typedef typename T::SubTable SubTable; + + const SubTable *st = &thiz()->firstSubTable; + unsigned int count = thiz()->tableCount; + for (unsigned int i = 0; i < count; i++) + { + if (st->get_type () == 1) + return true; + st = &StructAfter (*st); + } + return false; + } + + bool has_cross_stream () const + { + typedef typename T::SubTable SubTable; + + const SubTable *st = &thiz()->firstSubTable; + unsigned int count = thiz()->tableCount; + for (unsigned int i = 0; i < count; i++) + { + if (st->u.header.coverage & st->u.header.CrossStream) + return true; + st = &StructAfter (*st); + } + return false; + } + + int get_h_kerning (hb_codepoint_t left, hb_codepoint_t right) const + { + typedef typename T::SubTable SubTable; + + int v = 0; + const SubTable *st = &thiz()->firstSubTable; + unsigned int count = thiz()->tableCount; + for (unsigned int i = 0; i < count; i++) + { + if ((st->u.header.coverage & (st->u.header.Variation | st->u.header.CrossStream)) || + !st->u.header.is_horizontal ()) + continue; + v += st->get_kerning (left, right); + st = &StructAfter (*st); + } + return v; + } + + bool apply (AAT::hb_aat_apply_context_t *c) const + { + typedef typename T::SubTable SubTable; + + bool ret = false; + bool seenCrossStream = false; + c->set_lookup_index (0); + const SubTable *st = &thiz()->firstSubTable; + unsigned int count = thiz()->tableCount; + for (unsigned int i = 0; i < count; i++) + { + bool reverse; + + if (!T::Types::extended && (st->u.header.coverage & st->u.header.Variation)) + goto skip; + + if (HB_DIRECTION_IS_HORIZONTAL (c->buffer->props.direction) != st->u.header.is_horizontal ()) + goto skip; + + reverse = bool (st->u.header.coverage & st->u.header.Backwards) != + HB_DIRECTION_IS_BACKWARD (c->buffer->props.direction); + + if (!c->buffer->message (c->font, "start subtable %d", c->lookup_index)) + goto skip; + + if (!seenCrossStream && + (st->u.header.coverage & st->u.header.CrossStream)) + { + /* Attach all glyphs into a chain. */ + seenCrossStream = true; + hb_glyph_position_t *pos = c->buffer->pos; + unsigned int count = c->buffer->len; + for (unsigned int i = 0; i < count; i++) + { + pos[i].attach_type() = OT::Layout::GPOS_impl::ATTACH_TYPE_CURSIVE; + pos[i].attach_chain() = HB_DIRECTION_IS_FORWARD (c->buffer->props.direction) ? -1 : +1; + /* We intentionally don't set HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT, + * since there needs to be a non-zero attachment for post-positioning to + * be needed. */ + } + } + + if (reverse) + c->buffer->reverse (); + + { + /* See comment in sanitize() for conditional here. */ + hb_sanitize_with_object_t with (&c->sanitizer, i < count - 1 ? st : (const SubTable *) nullptr); + ret |= st->dispatch (c); + } + + if (reverse) + c->buffer->reverse (); + + (void) c->buffer->message (c->font, "end subtable %d", c->lookup_index); + + skip: + st = &StructAfter (*st); + c->set_lookup_index (c->lookup_index + 1); + } + + return ret; + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (unlikely (!thiz()->version.sanitize (c) || + (unsigned) thiz()->version < (unsigned) T::minVersion || + !thiz()->tableCount.sanitize (c))) + return_trace (false); + + typedef typename T::SubTable SubTable; + + const SubTable *st = &thiz()->firstSubTable; + unsigned int count = thiz()->tableCount; + for (unsigned int i = 0; i < count; i++) + { + if (unlikely (!st->u.header.sanitize (c))) + return_trace (false); + /* OpenType kern table has 2-byte subtable lengths. That's limiting. + * MS implementation also only supports one subtable, of format 0, + * anyway. Certain versions of some fonts, like Calibry, contain + * kern subtable that exceeds 64kb. Looks like, the subtable length + * is simply ignored. Which makes sense. It's only needed if you + * have multiple subtables. To handle such fonts, we just ignore + * the length for the last subtable. */ + hb_sanitize_with_object_t with (c, i < count - 1 ? st : (const SubTable *) nullptr); + + if (unlikely (!st->sanitize (c))) + return_trace (false); + + st = &StructAfter (*st); + } + + return_trace (true); + } +}; + +struct kerx : KerxTable +{ + friend struct KerxTable; + + static constexpr hb_tag_t tableTag = HB_AAT_TAG_kerx; + static constexpr unsigned minVersion = 2u; + + typedef KerxSubTableHeader SubTableHeader; + typedef SubTableHeader::Types Types; + typedef KerxSubTable SubTable; + + bool has_data () const { return version; } + + protected: + HBUINT16 version; /* The version number of the extended kerning table + * (currently 2, 3, or 4). */ + HBUINT16 unused; /* Set to 0. */ + HBUINT32 tableCount; /* The number of subtables included in the extended kerning + * table. */ + SubTable firstSubTable; /* Subtables. */ +/*subtableGlyphCoverageArray*/ /* Only if version >= 3. We don't use. */ + + public: + DEFINE_SIZE_MIN (8); +}; + + +} /* namespace AAT */ + + +#endif /* HB_AAT_LAYOUT_KERX_TABLE_HH */ diff --git a/libs/harfbuzz/src/hb-aat-layout-morx-table.hh b/libs/harfbuzz/src/hb-aat-layout-morx-table.hh new file mode 100644 index 00000000..aa4ad4cf --- /dev/null +++ b/libs/harfbuzz/src/hb-aat-layout-morx-table.hh @@ -0,0 +1,1181 @@ +/* + * Copyright © 2017 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 HB_AAT_LAYOUT_MORX_TABLE_HH +#define HB_AAT_LAYOUT_MORX_TABLE_HH + +#include "hb-open-type.hh" +#include "hb-aat-layout-common.hh" +#include "hb-ot-layout-common.hh" +#include "hb-ot-layout-gdef-table.hh" +#include "hb-aat-map.hh" + +/* + * morx -- Extended Glyph Metamorphosis + * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6morx.html + * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6mort.html + */ +#define HB_AAT_TAG_morx HB_TAG('m','o','r','x') +#define HB_AAT_TAG_mort HB_TAG('m','o','r','t') + + +namespace AAT { + +using namespace OT; + +template +struct RearrangementSubtable +{ + typedef typename Types::HBUINT HBUINT; + + typedef void EntryData; + + struct driver_context_t + { + static constexpr bool in_place = true; + enum Flags + { + MarkFirst = 0x8000, /* If set, make the current glyph the first + * glyph to be rearranged. */ + DontAdvance = 0x4000, /* If set, don't advance to the next glyph + * before going to the new state. This means + * that the glyph index doesn't change, even + * if the glyph at that index has changed. */ + MarkLast = 0x2000, /* If set, make the current glyph the last + * glyph to be rearranged. */ + Reserved = 0x1FF0, /* These bits are reserved and should be set to 0. */ + Verb = 0x000F, /* The type of rearrangement specified. */ + }; + + driver_context_t (const RearrangementSubtable *table HB_UNUSED) : + ret (false), + start (0), end (0) {} + + bool is_actionable (StateTableDriver *driver HB_UNUSED, + const Entry &entry) + { + return (entry.flags & Verb) && start < end; + } + void transition (StateTableDriver *driver, + const Entry &entry) + { + hb_buffer_t *buffer = driver->buffer; + unsigned int flags = entry.flags; + + if (flags & MarkFirst) + start = buffer->idx; + + if (flags & MarkLast) + end = hb_min (buffer->idx + 1, buffer->len); + + if ((flags & Verb) && start < end) + { + /* The following map has two nibbles, for start-side + * and end-side. Values of 0,1,2 mean move that many + * to the other side. Value of 3 means move 2 and + * flip them. */ + const unsigned char map[16] = + { + 0x00, /* 0 no change */ + 0x10, /* 1 Ax => xA */ + 0x01, /* 2 xD => Dx */ + 0x11, /* 3 AxD => DxA */ + 0x20, /* 4 ABx => xAB */ + 0x30, /* 5 ABx => xBA */ + 0x02, /* 6 xCD => CDx */ + 0x03, /* 7 xCD => DCx */ + 0x12, /* 8 AxCD => CDxA */ + 0x13, /* 9 AxCD => DCxA */ + 0x21, /* 10 ABxD => DxAB */ + 0x31, /* 11 ABxD => DxBA */ + 0x22, /* 12 ABxCD => CDxAB */ + 0x32, /* 13 ABxCD => CDxBA */ + 0x23, /* 14 ABxCD => DCxAB */ + 0x33, /* 15 ABxCD => DCxBA */ + }; + + unsigned int m = map[flags & Verb]; + unsigned int l = hb_min (2u, m >> 4); + unsigned int r = hb_min (2u, m & 0x0F); + bool reverse_l = 3 == (m >> 4); + bool reverse_r = 3 == (m & 0x0F); + + if (end - start >= l + r && end-start <= HB_MAX_CONTEXT_LENGTH) + { + buffer->merge_clusters (start, hb_min (buffer->idx + 1, buffer->len)); + buffer->merge_clusters (start, end); + + hb_glyph_info_t *info = buffer->info; + hb_glyph_info_t buf[4]; + + memcpy (buf, info + start, l * sizeof (buf[0])); + memcpy (buf + 2, info + end - r, r * sizeof (buf[0])); + + if (l != r) + memmove (info + start + r, info + start + l, (end - start - l - r) * sizeof (buf[0])); + + memcpy (info + start, buf + 2, r * sizeof (buf[0])); + memcpy (info + end - l, buf, l * sizeof (buf[0])); + if (reverse_l) + { + buf[0] = info[end - 1]; + info[end - 1] = info[end - 2]; + info[end - 2] = buf[0]; + } + if (reverse_r) + { + buf[0] = info[start]; + info[start] = info[start + 1]; + info[start + 1] = buf[0]; + } + } + } + } + + public: + bool ret; + private: + unsigned int start; + unsigned int end; + }; + + bool apply (hb_aat_apply_context_t *c) const + { + TRACE_APPLY (this); + + driver_context_t dc (this); + + StateTableDriver driver (machine, c->buffer, c->face); + driver.drive (&dc); + + return_trace (dc.ret); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (machine.sanitize (c)); + } + + protected: + StateTable machine; + public: + DEFINE_SIZE_STATIC (16); +}; + +template +struct ContextualSubtable +{ + typedef typename Types::HBUINT HBUINT; + + struct EntryData + { + HBUINT16 markIndex; /* Index of the substitution table for the + * marked glyph (use 0xFFFF for none). */ + HBUINT16 currentIndex; /* Index of the substitution table for the + * current glyph (use 0xFFFF for none). */ + public: + DEFINE_SIZE_STATIC (4); + }; + + struct driver_context_t + { + static constexpr bool in_place = true; + enum Flags + { + SetMark = 0x8000, /* If set, make the current glyph the marked glyph. */ + DontAdvance = 0x4000, /* If set, don't advance to the next glyph before + * going to the new state. */ + Reserved = 0x3FFF, /* These bits are reserved and should be set to 0. */ + }; + + driver_context_t (const ContextualSubtable *table_, + hb_aat_apply_context_t *c_) : + ret (false), + c (c_), + gdef (*c->gdef_table), + mark_set (false), + has_glyph_classes (gdef.has_glyph_classes ()), + mark (0), + table (table_), + subs (table+table->substitutionTables) {} + + bool is_actionable (StateTableDriver *driver, + const Entry &entry) + { + hb_buffer_t *buffer = driver->buffer; + + if (buffer->idx == buffer->len && !mark_set) + return false; + + return entry.data.markIndex != 0xFFFF || entry.data.currentIndex != 0xFFFF; + } + void transition (StateTableDriver *driver, + const Entry &entry) + { + hb_buffer_t *buffer = driver->buffer; + + /* Looks like CoreText applies neither mark nor current substitution for + * end-of-text if mark was not explicitly set. */ + if (buffer->idx == buffer->len && !mark_set) + return; + + const HBGlyphID16 *replacement; + + replacement = nullptr; + if (Types::extended) + { + if (entry.data.markIndex != 0xFFFF) + { + const Lookup &lookup = subs[entry.data.markIndex]; + replacement = lookup.get_value (buffer->info[mark].codepoint, driver->num_glyphs); + } + } + else + { + unsigned int offset = entry.data.markIndex + buffer->info[mark].codepoint; + const UnsizedArrayOf &subs_old = (const UnsizedArrayOf &) subs; + replacement = &subs_old[Types::wordOffsetToIndex (offset, table, subs_old.arrayZ)]; + if (!replacement->sanitize (&c->sanitizer) || !*replacement) + replacement = nullptr; + } + if (replacement) + { + buffer->unsafe_to_break (mark, hb_min (buffer->idx + 1, buffer->len)); + buffer->info[mark].codepoint = *replacement; + if (has_glyph_classes) + _hb_glyph_info_set_glyph_props (&buffer->info[mark], + gdef.get_glyph_props (*replacement)); + ret = true; + } + + replacement = nullptr; + unsigned int idx = hb_min (buffer->idx, buffer->len - 1); + if (Types::extended) + { + if (entry.data.currentIndex != 0xFFFF) + { + const Lookup &lookup = subs[entry.data.currentIndex]; + replacement = lookup.get_value (buffer->info[idx].codepoint, driver->num_glyphs); + } + } + else + { + unsigned int offset = entry.data.currentIndex + buffer->info[idx].codepoint; + const UnsizedArrayOf &subs_old = (const UnsizedArrayOf &) subs; + replacement = &subs_old[Types::wordOffsetToIndex (offset, table, subs_old.arrayZ)]; + if (!replacement->sanitize (&c->sanitizer) || !*replacement) + replacement = nullptr; + } + if (replacement) + { + buffer->info[idx].codepoint = *replacement; + if (has_glyph_classes) + _hb_glyph_info_set_glyph_props (&buffer->info[idx], + gdef.get_glyph_props (*replacement)); + ret = true; + } + + if (entry.flags & SetMark) + { + mark_set = true; + mark = buffer->idx; + } + } + + public: + bool ret; + private: + hb_aat_apply_context_t *c; + const OT::GDEF &gdef; + bool mark_set; + bool has_glyph_classes; + unsigned int mark; + const ContextualSubtable *table; + const UnsizedListOfOffset16To, HBUINT, false> &subs; + }; + + bool apply (hb_aat_apply_context_t *c) const + { + TRACE_APPLY (this); + + driver_context_t dc (this, c); + + StateTableDriver driver (machine, c->buffer, c->face); + driver.drive (&dc); + + return_trace (dc.ret); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + + unsigned int num_entries = 0; + if (unlikely (!machine.sanitize (c, &num_entries))) return_trace (false); + + if (!Types::extended) + return_trace (substitutionTables.sanitize (c, this, 0)); + + unsigned int num_lookups = 0; + + const Entry *entries = machine.get_entries (); + for (unsigned int i = 0; i < num_entries; i++) + { + const EntryData &data = entries[i].data; + + if (data.markIndex != 0xFFFF) + num_lookups = hb_max (num_lookups, 1u + data.markIndex); + if (data.currentIndex != 0xFFFF) + num_lookups = hb_max (num_lookups, 1u + data.currentIndex); + } + + return_trace (substitutionTables.sanitize (c, this, num_lookups)); + } + + protected: + StateTable + machine; + NNOffsetTo, HBUINT, false>, HBUINT> + substitutionTables; + public: + DEFINE_SIZE_STATIC (20); +}; + + +template +struct LigatureEntry; + +template <> +struct LigatureEntry +{ + enum Flags + { + SetComponent = 0x8000, /* Push this glyph onto the component stack for + * eventual processing. */ + DontAdvance = 0x4000, /* Leave the glyph pointer at this glyph for the + next iteration. */ + PerformAction = 0x2000, /* Use the ligActionIndex to process a ligature + * group. */ + Reserved = 0x1FFF, /* These bits are reserved and should be set to 0. */ + }; + + struct EntryData + { + HBUINT16 ligActionIndex; /* Index to the first ligActionTable entry + * for processing this group, if indicated + * by the flags. */ + public: + DEFINE_SIZE_STATIC (2); + }; + + static bool performAction (const Entry &entry) + { return entry.flags & PerformAction; } + + static unsigned int ligActionIndex (const Entry &entry) + { return entry.data.ligActionIndex; } +}; +template <> +struct LigatureEntry +{ + enum Flags + { + SetComponent = 0x8000, /* Push this glyph onto the component stack for + * eventual processing. */ + DontAdvance = 0x4000, /* Leave the glyph pointer at this glyph for the + next iteration. */ + Offset = 0x3FFF, /* Byte offset from beginning of subtable to the + * ligature action list. This value must be a + * multiple of 4. */ + }; + + typedef void EntryData; + + static bool performAction (const Entry &entry) + { return entry.flags & Offset; } + + static unsigned int ligActionIndex (const Entry &entry) + { return entry.flags & Offset; } +}; + + +template +struct LigatureSubtable +{ + typedef typename Types::HBUINT HBUINT; + + typedef LigatureEntry LigatureEntryT; + typedef typename LigatureEntryT::EntryData EntryData; + + struct driver_context_t + { + static constexpr bool in_place = false; + enum + { + DontAdvance = LigatureEntryT::DontAdvance, + }; + enum LigActionFlags + { + LigActionLast = 0x80000000, /* This is the last action in the list. This also + * implies storage. */ + LigActionStore = 0x40000000, /* Store the ligature at the current cumulated index + * in the ligature table in place of the marked + * (i.e. currently-popped) glyph. */ + LigActionOffset = 0x3FFFFFFF, /* A 30-bit value which is sign-extended to 32-bits + * and added to the glyph ID, resulting in an index + * into the component table. */ + }; + + driver_context_t (const LigatureSubtable *table_, + hb_aat_apply_context_t *c_) : + ret (false), + c (c_), + table (table_), + ligAction (table+table->ligAction), + component (table+table->component), + ligature (table+table->ligature), + match_length (0) {} + + bool is_actionable (StateTableDriver *driver HB_UNUSED, + const Entry &entry) + { + return LigatureEntryT::performAction (entry); + } + void transition (StateTableDriver *driver, + const Entry &entry) + { + hb_buffer_t *buffer = driver->buffer; + + DEBUG_MSG (APPLY, nullptr, "Ligature transition at %u", buffer->idx); + if (entry.flags & LigatureEntryT::SetComponent) + { + /* Never mark same index twice, in case DontAdvance was used... */ + if (match_length && match_positions[(match_length - 1u) % ARRAY_LENGTH (match_positions)] == buffer->out_len) + match_length--; + + match_positions[match_length++ % ARRAY_LENGTH (match_positions)] = buffer->out_len; + DEBUG_MSG (APPLY, nullptr, "Set component at %u", buffer->out_len); + } + + if (LigatureEntryT::performAction (entry)) + { + DEBUG_MSG (APPLY, nullptr, "Perform action with %u", match_length); + unsigned int end = buffer->out_len; + + if (unlikely (!match_length)) + return; + + if (buffer->idx >= buffer->len) + return; /* TODO Work on previous instead? */ + + unsigned int cursor = match_length; + + unsigned int action_idx = LigatureEntryT::ligActionIndex (entry); + action_idx = Types::offsetToIndex (action_idx, table, ligAction.arrayZ); + const HBUINT32 *actionData = &ligAction[action_idx]; + + unsigned int ligature_idx = 0; + unsigned int action; + do + { + if (unlikely (!cursor)) + { + /* Stack underflow. Clear the stack. */ + DEBUG_MSG (APPLY, nullptr, "Stack underflow"); + match_length = 0; + break; + } + + DEBUG_MSG (APPLY, nullptr, "Moving to stack position %u", cursor - 1); + if (unlikely (!buffer->move_to (match_positions[--cursor % ARRAY_LENGTH (match_positions)]))) return; + + if (unlikely (!actionData->sanitize (&c->sanitizer))) break; + action = *actionData; + + uint32_t uoffset = action & LigActionOffset; + if (uoffset & 0x20000000) + uoffset |= 0xC0000000; /* Sign-extend. */ + int32_t offset = (int32_t) uoffset; + unsigned int component_idx = buffer->cur().codepoint + offset; + component_idx = Types::wordOffsetToIndex (component_idx, table, component.arrayZ); + const HBUINT16 &componentData = component[component_idx]; + if (unlikely (!componentData.sanitize (&c->sanitizer))) break; + ligature_idx += componentData; + + DEBUG_MSG (APPLY, nullptr, "Action store %u last %u", + bool (action & LigActionStore), + bool (action & LigActionLast)); + if (action & (LigActionStore | LigActionLast)) + { + ligature_idx = Types::offsetToIndex (ligature_idx, table, ligature.arrayZ); + const HBGlyphID16 &ligatureData = ligature[ligature_idx]; + if (unlikely (!ligatureData.sanitize (&c->sanitizer))) break; + hb_codepoint_t lig = ligatureData; + + DEBUG_MSG (APPLY, nullptr, "Produced ligature %u", lig); + if (unlikely (!buffer->replace_glyph (lig))) return; + + unsigned int lig_end = match_positions[(match_length - 1u) % ARRAY_LENGTH (match_positions)] + 1u; + /* Now go and delete all subsequent components. */ + while (match_length - 1u > cursor) + { + DEBUG_MSG (APPLY, nullptr, "Skipping ligature component"); + if (unlikely (!buffer->move_to (match_positions[--match_length % ARRAY_LENGTH (match_positions)]))) return; + if (unlikely (!buffer->replace_glyph (DELETED_GLYPH))) return; + } + + if (unlikely (!buffer->move_to (lig_end))) return; + buffer->merge_out_clusters (match_positions[cursor % ARRAY_LENGTH (match_positions)], buffer->out_len); + } + + actionData++; + } + while (!(action & LigActionLast)); + if (unlikely (!buffer->move_to (end))) return; + } + } + + public: + bool ret; + private: + hb_aat_apply_context_t *c; + const LigatureSubtable *table; + const UnsizedArrayOf &ligAction; + const UnsizedArrayOf &component; + const UnsizedArrayOf &ligature; + unsigned int match_length; + unsigned int match_positions[HB_MAX_CONTEXT_LENGTH]; + }; + + bool apply (hb_aat_apply_context_t *c) const + { + TRACE_APPLY (this); + + driver_context_t dc (this, c); + + StateTableDriver driver (machine, c->buffer, c->face); + driver.drive (&dc); + + return_trace (dc.ret); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + /* The rest of array sanitizations are done at run-time. */ + return_trace (c->check_struct (this) && machine.sanitize (c) && + ligAction && component && ligature); + } + + protected: + StateTable + machine; + NNOffsetTo, HBUINT> + ligAction; /* Offset to the ligature action table. */ + NNOffsetTo, HBUINT> + component; /* Offset to the component table. */ + NNOffsetTo, HBUINT> + ligature; /* Offset to the actual ligature lists. */ + public: + DEFINE_SIZE_STATIC (28); +}; + +template +struct NoncontextualSubtable +{ + bool apply (hb_aat_apply_context_t *c) const + { + TRACE_APPLY (this); + + const OT::GDEF &gdef (*c->gdef_table); + bool has_glyph_classes = gdef.has_glyph_classes (); + + bool ret = false; + unsigned int num_glyphs = c->face->get_num_glyphs (); + + hb_glyph_info_t *info = c->buffer->info; + unsigned int count = c->buffer->len; + for (unsigned int i = 0; i < count; i++) + { + const HBGlyphID16 *replacement = substitute.get_value (info[i].codepoint, num_glyphs); + if (replacement) + { + info[i].codepoint = *replacement; + if (has_glyph_classes) + _hb_glyph_info_set_glyph_props (&info[i], + gdef.get_glyph_props (*replacement)); + ret = true; + } + } + + return_trace (ret); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (substitute.sanitize (c)); + } + + protected: + Lookup substitute; + public: + DEFINE_SIZE_MIN (2); +}; + +template +struct InsertionSubtable +{ + typedef typename Types::HBUINT HBUINT; + + struct EntryData + { + HBUINT16 currentInsertIndex; /* Zero-based index into the insertion glyph table. + * The number of glyphs to be inserted is contained + * in the currentInsertCount field in the flags. + * A value of 0xFFFF indicates no insertion is to + * be done. */ + HBUINT16 markedInsertIndex; /* Zero-based index into the insertion glyph table. + * The number of glyphs to be inserted is contained + * in the markedInsertCount field in the flags. + * A value of 0xFFFF indicates no insertion is to + * be done. */ + public: + DEFINE_SIZE_STATIC (4); + }; + + struct driver_context_t + { + static constexpr bool in_place = false; + enum Flags + { + SetMark = 0x8000, /* If set, mark the current glyph. */ + DontAdvance = 0x4000, /* If set, don't advance to the next glyph before + * going to the new state. This does not mean + * that the glyph pointed to is the same one as + * before. If you've made insertions immediately + * downstream of the current glyph, the next glyph + * processed would in fact be the first one + * inserted. */ + CurrentIsKashidaLike= 0x2000, /* If set, and the currentInsertList is nonzero, + * then the specified glyph list will be inserted + * as a kashida-like insertion, either before or + * after the current glyph (depending on the state + * of the currentInsertBefore flag). If clear, and + * the currentInsertList is nonzero, then the + * specified glyph list will be inserted as a + * split-vowel-like insertion, either before or + * after the current glyph (depending on the state + * of the currentInsertBefore flag). */ + MarkedIsKashidaLike= 0x1000, /* If set, and the markedInsertList is nonzero, + * then the specified glyph list will be inserted + * as a kashida-like insertion, either before or + * after the marked glyph (depending on the state + * of the markedInsertBefore flag). If clear, and + * the markedInsertList is nonzero, then the + * specified glyph list will be inserted as a + * split-vowel-like insertion, either before or + * after the marked glyph (depending on the state + * of the markedInsertBefore flag). */ + CurrentInsertBefore= 0x0800, /* If set, specifies that insertions are to be made + * to the left of the current glyph. If clear, + * they're made to the right of the current glyph. */ + MarkedInsertBefore= 0x0400, /* If set, specifies that insertions are to be + * made to the left of the marked glyph. If clear, + * they're made to the right of the marked glyph. */ + CurrentInsertCount= 0x3E0, /* This 5-bit field is treated as a count of the + * number of glyphs to insert at the current + * position. Since zero means no insertions, the + * largest number of insertions at any given + * current location is 31 glyphs. */ + MarkedInsertCount= 0x001F, /* This 5-bit field is treated as a count of the + * number of glyphs to insert at the marked + * position. Since zero means no insertions, the + * largest number of insertions at any given + * marked location is 31 glyphs. */ + }; + + driver_context_t (const InsertionSubtable *table, + hb_aat_apply_context_t *c_) : + ret (false), + c (c_), + mark (0), + insertionAction (table+table->insertionAction) {} + + bool is_actionable (StateTableDriver *driver HB_UNUSED, + const Entry &entry) + { + return (entry.flags & (CurrentInsertCount | MarkedInsertCount)) && + (entry.data.currentInsertIndex != 0xFFFF ||entry.data.markedInsertIndex != 0xFFFF); + } + void transition (StateTableDriver *driver, + const Entry &entry) + { + hb_buffer_t *buffer = driver->buffer; + unsigned int flags = entry.flags; + + unsigned mark_loc = buffer->out_len; + + if (entry.data.markedInsertIndex != 0xFFFF) + { + unsigned int count = (flags & MarkedInsertCount); + if (unlikely ((buffer->max_ops -= count) <= 0)) return; + unsigned int start = entry.data.markedInsertIndex; + const HBGlyphID16 *glyphs = &insertionAction[start]; + if (unlikely (!c->sanitizer.check_array (glyphs, count))) count = 0; + + bool before = flags & MarkedInsertBefore; + + unsigned int end = buffer->out_len; + if (unlikely (!buffer->move_to (mark))) return; + + if (buffer->idx < buffer->len && !before) + if (unlikely (!buffer->copy_glyph ())) return; + /* TODO We ignore KashidaLike setting. */ + if (unlikely (!buffer->replace_glyphs (0, count, glyphs))) return; + if (buffer->idx < buffer->len && !before) + buffer->skip_glyph (); + + if (unlikely (!buffer->move_to (end + count))) return; + + buffer->unsafe_to_break_from_outbuffer (mark, hb_min (buffer->idx + 1, buffer->len)); + } + + if (flags & SetMark) + mark = mark_loc; + + if (entry.data.currentInsertIndex != 0xFFFF) + { + unsigned int count = (flags & CurrentInsertCount) >> 5; + if (unlikely ((buffer->max_ops -= count) <= 0)) return; + unsigned int start = entry.data.currentInsertIndex; + const HBGlyphID16 *glyphs = &insertionAction[start]; + if (unlikely (!c->sanitizer.check_array (glyphs, count))) count = 0; + + bool before = flags & CurrentInsertBefore; + + unsigned int end = buffer->out_len; + + if (buffer->idx < buffer->len && !before) + if (unlikely (!buffer->copy_glyph ())) return; + /* TODO We ignore KashidaLike setting. */ + if (unlikely (!buffer->replace_glyphs (0, count, glyphs))) return; + if (buffer->idx < buffer->len && !before) + buffer->skip_glyph (); + + /* Humm. Not sure where to move to. There's this wording under + * DontAdvance flag: + * + * "If set, don't update the glyph index before going to the new state. + * This does not mean that the glyph pointed to is the same one as + * before. If you've made insertions immediately downstream of the + * current glyph, the next glyph processed would in fact be the first + * one inserted." + * + * This suggests that if DontAdvance is NOT set, we should move to + * end+count. If it *was*, then move to end, such that newly inserted + * glyphs are now visible. + * + * https://github.com/harfbuzz/harfbuzz/issues/1224#issuecomment-427691417 + */ + if (unlikely (!buffer->move_to ((flags & DontAdvance) ? end : end + count))) return; + } + } + + public: + bool ret; + private: + hb_aat_apply_context_t *c; + unsigned int mark; + const UnsizedArrayOf &insertionAction; + }; + + bool apply (hb_aat_apply_context_t *c) const + { + TRACE_APPLY (this); + + driver_context_t dc (this, c); + + StateTableDriver driver (machine, c->buffer, c->face); + driver.drive (&dc); + + return_trace (dc.ret); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + /* The rest of array sanitizations are done at run-time. */ + return_trace (c->check_struct (this) && machine.sanitize (c) && + insertionAction); + } + + protected: + StateTable + machine; + NNOffsetTo, HBUINT> + insertionAction; /* Byte offset from stateHeader to the start of + * the insertion glyph table. */ + public: + DEFINE_SIZE_STATIC (20); +}; + + +struct Feature +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + public: + HBUINT16 featureType; /* The type of feature. */ + HBUINT16 featureSetting; /* The feature's setting (aka selector). */ + HBUINT32 enableFlags; /* Flags for the settings that this feature + * and setting enables. */ + HBUINT32 disableFlags; /* Complement of flags for the settings that this + * feature and setting disable. */ + + public: + DEFINE_SIZE_STATIC (12); +}; + +template +struct ChainSubtable +{ + typedef typename Types::HBUINT HBUINT; + + template + friend struct Chain; + + unsigned int get_size () const { return length; } + unsigned int get_type () const { return coverage & 0xFF; } + unsigned int get_coverage () const { return coverage >> (sizeof (HBUINT) * 8 - 8); } + + enum Coverage + { + Vertical = 0x80, /* If set, this subtable will only be applied + * to vertical text. If clear, this subtable + * will only be applied to horizontal text. */ + Backwards = 0x40, /* If set, this subtable will process glyphs + * in descending order. If clear, it will + * process the glyphs in ascending order. */ + AllDirections = 0x20, /* If set, this subtable will be applied to + * both horizontal and vertical text (i.e. + * the state of bit 0x80000000 is ignored). */ + Logical = 0x10, /* If set, this subtable will process glyphs + * in logical order (or reverse logical order, + * depending on the value of bit 0x80000000). */ + }; + enum Type + { + Rearrangement = 0, + Contextual = 1, + Ligature = 2, + Noncontextual = 4, + Insertion = 5 + }; + + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const + { + unsigned int subtable_type = get_type (); + TRACE_DISPATCH (this, subtable_type); + switch (subtable_type) { + case Rearrangement: return_trace (c->dispatch (u.rearrangement, std::forward (ds)...)); + case Contextual: return_trace (c->dispatch (u.contextual, std::forward (ds)...)); + case Ligature: return_trace (c->dispatch (u.ligature, std::forward (ds)...)); + case Noncontextual: return_trace (c->dispatch (u.noncontextual, std::forward (ds)...)); + case Insertion: return_trace (c->dispatch (u.insertion, std::forward (ds)...)); + default: return_trace (c->default_return_value ()); + } + } + + bool apply (hb_aat_apply_context_t *c) const + { + TRACE_APPLY (this); + hb_sanitize_with_object_t with (&c->sanitizer, this); + return_trace (dispatch (c)); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (!length.sanitize (c) || + length <= min_size || + !c->check_range (this, length)) + return_trace (false); + + hb_sanitize_with_object_t with (c, this); + return_trace (dispatch (c)); + } + + protected: + HBUINT length; /* Total subtable length, including this header. */ + HBUINT coverage; /* Coverage flags and subtable type. */ + HBUINT32 subFeatureFlags;/* The 32-bit mask identifying which subtable this is. */ + union { + RearrangementSubtable rearrangement; + ContextualSubtable contextual; + LigatureSubtable ligature; + NoncontextualSubtable noncontextual; + InsertionSubtable insertion; + } u; + public: + DEFINE_SIZE_MIN (2 * sizeof (HBUINT) + 4); +}; + +template +struct Chain +{ + typedef typename Types::HBUINT HBUINT; + + hb_mask_t compile_flags (const hb_aat_map_builder_t *map) const + { + hb_mask_t flags = defaultFlags; + { + unsigned int count = featureCount; + for (unsigned i = 0; i < count; i++) + { + const Feature &feature = featureZ[i]; + hb_aat_layout_feature_type_t type = (hb_aat_layout_feature_type_t) (unsigned int) feature.featureType; + hb_aat_layout_feature_selector_t setting = (hb_aat_layout_feature_selector_t) (unsigned int) feature.featureSetting; + retry: + // Check whether this type/setting pair was requested in the map, and if so, apply its flags. + // (The search here only looks at the type and setting fields of feature_info_t.) + hb_aat_map_builder_t::feature_info_t info = { type, setting, false, 0 }; + if (map->features.bsearch (info)) + { + flags &= feature.disableFlags; + flags |= feature.enableFlags; + } + else if (type == HB_AAT_LAYOUT_FEATURE_TYPE_LETTER_CASE && setting == HB_AAT_LAYOUT_FEATURE_SELECTOR_SMALL_CAPS) + { + /* Deprecated. https://github.com/harfbuzz/harfbuzz/issues/1342 */ + type = HB_AAT_LAYOUT_FEATURE_TYPE_LOWER_CASE; + setting = HB_AAT_LAYOUT_FEATURE_SELECTOR_LOWER_CASE_SMALL_CAPS; + goto retry; + } +#ifndef HB_NO_AAT + else if (type == HB_AAT_LAYOUT_FEATURE_TYPE_LANGUAGE_TAG_TYPE && setting && + /* TODO: Rudimentary language matching. */ + hb_language_matches (map->face->table.ltag->get_language (setting - 1), map->props.language)) + { + flags &= feature.disableFlags; + flags |= feature.enableFlags; + } +#endif + } + } + return flags; + } + + void apply (hb_aat_apply_context_t *c, + hb_mask_t flags) const + { + const ChainSubtable *subtable = &StructAfter> (featureZ.as_array (featureCount)); + unsigned int count = subtableCount; + for (unsigned int i = 0; i < count; i++) + { + bool reverse; + + if (!(subtable->subFeatureFlags & flags)) + goto skip; + + if (!(subtable->get_coverage() & ChainSubtable::AllDirections) && + HB_DIRECTION_IS_VERTICAL (c->buffer->props.direction) != + bool (subtable->get_coverage() & ChainSubtable::Vertical)) + goto skip; + + /* Buffer contents is always in logical direction. Determine if + * we need to reverse before applying this subtable. We reverse + * back after if we did reverse indeed. + * + * Quoting the spac: + * """ + * Bits 28 and 30 of the coverage field control the order in which + * glyphs are processed when the subtable is run by the layout engine. + * Bit 28 is used to indicate if the glyph processing direction is + * the same as logical order or layout order. Bit 30 is used to + * indicate whether glyphs are processed forwards or backwards within + * that order. + + Bit 30 Bit 28 Interpretation for Horizontal Text + 0 0 The subtable is processed in layout order + (the same order as the glyphs, which is + always left-to-right). + 1 0 The subtable is processed in reverse layout order + (the order opposite that of the glyphs, which is + always right-to-left). + 0 1 The subtable is processed in logical order + (the same order as the characters, which may be + left-to-right or right-to-left). + 1 1 The subtable is processed in reverse logical order + (the order opposite that of the characters, which + may be right-to-left or left-to-right). + */ + reverse = subtable->get_coverage () & ChainSubtable::Logical ? + bool (subtable->get_coverage () & ChainSubtable::Backwards) : + bool (subtable->get_coverage () & ChainSubtable::Backwards) != + HB_DIRECTION_IS_BACKWARD (c->buffer->props.direction); + + if (!c->buffer->message (c->font, "start chainsubtable %d", c->lookup_index)) + goto skip; + + if (reverse) + c->buffer->reverse (); + + subtable->apply (c); + + if (reverse) + c->buffer->reverse (); + + (void) c->buffer->message (c->font, "end chainsubtable %d", c->lookup_index); + + if (unlikely (!c->buffer->successful)) return; + + skip: + subtable = &StructAfter> (*subtable); + c->set_lookup_index (c->lookup_index + 1); + } + } + + unsigned int get_size () const { return length; } + + bool sanitize (hb_sanitize_context_t *c, unsigned int version HB_UNUSED) const + { + TRACE_SANITIZE (this); + if (!length.sanitize (c) || + length < min_size || + !c->check_range (this, length)) + return_trace (false); + + if (!c->check_array (featureZ.arrayZ, featureCount)) + return_trace (false); + + const ChainSubtable *subtable = &StructAfter> (featureZ.as_array (featureCount)); + unsigned int count = subtableCount; + for (unsigned int i = 0; i < count; i++) + { + if (!subtable->sanitize (c)) + return_trace (false); + subtable = &StructAfter> (*subtable); + } + + return_trace (true); + } + + protected: + HBUINT32 defaultFlags; /* The default specification for subtables. */ + HBUINT32 length; /* Total byte count, including this header. */ + HBUINT featureCount; /* Number of feature subtable entries. */ + HBUINT subtableCount; /* The number of subtables in the chain. */ + + UnsizedArrayOf featureZ; /* Features. */ +/*ChainSubtable firstSubtable;*//* Subtables. */ +/*subtableGlyphCoverageArray*/ /* Only if version >= 3. We don't use. */ + + public: + DEFINE_SIZE_MIN (8 + 2 * sizeof (HBUINT)); +}; + + +/* + * The 'mort'/'morx' Table + */ + +template +struct mortmorx +{ + static constexpr hb_tag_t tableTag = TAG; + + bool has_data () const { return version != 0; } + + void compile_flags (const hb_aat_map_builder_t *mapper, + hb_aat_map_t *map) const + { + const Chain *chain = &firstChain; + unsigned int count = chainCount; + for (unsigned int i = 0; i < count; i++) + { + map->chain_flags.push (chain->compile_flags (mapper)); + chain = &StructAfter> (*chain); + } + } + + void apply (hb_aat_apply_context_t *c) const + { + if (unlikely (!c->buffer->successful)) return; + c->set_lookup_index (0); + const Chain *chain = &firstChain; + unsigned int count = chainCount; + for (unsigned int i = 0; i < count; i++) + { + chain->apply (c, c->plan->aat_map.chain_flags[i]); + if (unlikely (!c->buffer->successful)) return; + chain = &StructAfter> (*chain); + } + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (!version.sanitize (c) || !version || !chainCount.sanitize (c)) + return_trace (false); + + const Chain *chain = &firstChain; + unsigned int count = chainCount; + for (unsigned int i = 0; i < count; i++) + { + if (!chain->sanitize (c, version)) + return_trace (false); + chain = &StructAfter> (*chain); + } + + return_trace (true); + } + + protected: + HBUINT16 version; /* Version number of the glyph metamorphosis table. + * 1, 2, or 3. */ + HBUINT16 unused; /* Set to 0. */ + HBUINT32 chainCount; /* Number of metamorphosis chains contained in this + * table. */ + Chain firstChain; /* Chains. */ + + public: + DEFINE_SIZE_MIN (8); +}; + +struct morx : mortmorx {}; +struct mort : mortmorx {}; + + +} /* namespace AAT */ + + +#endif /* HB_AAT_LAYOUT_MORX_TABLE_HH */ diff --git a/libs/harfbuzz/src/hb-aat-layout-opbd-table.hh b/libs/harfbuzz/src/hb-aat-layout-opbd-table.hh new file mode 100644 index 00000000..51b650fc --- /dev/null +++ b/libs/harfbuzz/src/hb-aat-layout-opbd-table.hh @@ -0,0 +1,173 @@ +/* + * Copyright © 2019 Ebrahim Byagowi + * + * 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. + */ + +#ifndef HB_AAT_LAYOUT_OPBD_TABLE_HH +#define HB_AAT_LAYOUT_OPBD_TABLE_HH + +#include "hb-aat-layout-common.hh" +#include "hb-open-type.hh" + +/* + * opbd -- Optical Bounds + * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6opbd.html + */ +#define HB_AAT_TAG_opbd HB_TAG('o','p','b','d') + + +namespace AAT { + +struct OpticalBounds +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + FWORD leftSide; + FWORD topSide; + FWORD rightSide; + FWORD bottomSide; + public: + DEFINE_SIZE_STATIC (8); +}; + +struct opbdFormat0 +{ + bool get_bounds (hb_font_t *font, hb_codepoint_t glyph_id, + hb_glyph_extents_t *extents, const void *base) const + { + const Offset16To *bounds_offset = lookupTable.get_value (glyph_id, font->face->get_num_glyphs ()); + if (!bounds_offset) return false; + const OpticalBounds &bounds = base+*bounds_offset; + + if (extents) + *extents = { + font->em_scale_x (bounds.leftSide), + font->em_scale_y (bounds.topSide), + font->em_scale_x (bounds.rightSide), + font->em_scale_y (bounds.bottomSide) + }; + return true; + } + + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && lookupTable.sanitize (c, base))); + } + + protected: + Lookup> + lookupTable; /* Lookup table associating glyphs with the four + * int16 values for the left-side, top-side, + * right-side, and bottom-side optical bounds. */ + public: + DEFINE_SIZE_MIN (2); +}; + +struct opbdFormat1 +{ + bool get_bounds (hb_font_t *font, hb_codepoint_t glyph_id, + hb_glyph_extents_t *extents, const void *base) const + { + const Offset16To *bounds_offset = lookupTable.get_value (glyph_id, font->face->get_num_glyphs ()); + if (!bounds_offset) return false; + const OpticalBounds &bounds = base+*bounds_offset; + + hb_position_t left = 0, top = 0, right = 0, bottom = 0, ignore; + if (font->get_glyph_contour_point (glyph_id, bounds.leftSide, &left, &ignore) || + font->get_glyph_contour_point (glyph_id, bounds.topSide, &ignore, &top) || + font->get_glyph_contour_point (glyph_id, bounds.rightSide, &right, &ignore) || + font->get_glyph_contour_point (glyph_id, bounds.bottomSide, &ignore, &bottom)) + { + if (extents) + *extents = {left, top, right, bottom}; + return true; + } + return false; + } + + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && lookupTable.sanitize (c, base))); + } + + protected: + Lookup> + lookupTable; /* Lookup table associating glyphs with the four + * int16 values for the left-side, top-side, + * right-side, and bottom-side optical bounds. */ + public: + DEFINE_SIZE_MIN (2); +}; + +struct opbd +{ + static constexpr hb_tag_t tableTag = HB_AAT_TAG_opbd; + + bool get_bounds (hb_font_t *font, hb_codepoint_t glyph_id, + hb_glyph_extents_t *extents) const + { + switch (format) + { + case 0: return u.format0.get_bounds (font, glyph_id, extents, this); + case 1: return u.format1.get_bounds (font, glyph_id, extents, this); + default:return false; + } + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (unlikely (!c->check_struct (this) || version.major != 1)) + return_trace (false); + + switch (format) + { + case 0: return_trace (u.format0.sanitize (c, this)); + case 1: return_trace (u.format1.sanitize (c, this)); + default:return_trace (true); + } + } + + protected: + FixedVersion<>version; /* Version number of the optical bounds + * table (0x00010000 for the current version). */ + HBUINT16 format; /* Format of the optical bounds table. + * Format 0 indicates distance and Format 1 indicates + * control point. */ + union { + opbdFormat0 format0; + opbdFormat1 format1; + } u; + public: + DEFINE_SIZE_MIN (8); +}; + +} /* namespace AAT */ + + +#endif /* HB_AAT_LAYOUT_OPBD_TABLE_HH */ diff --git a/libs/harfbuzz/src/hb-aat-layout-trak-table.hh b/libs/harfbuzz/src/hb-aat-layout-trak-table.hh new file mode 100644 index 00000000..2ba9355b --- /dev/null +++ b/libs/harfbuzz/src/hb-aat-layout-trak-table.hh @@ -0,0 +1,230 @@ +/* + * Copyright © 2018 Ebrahim Byagowi + * Copyright © 2018 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 HB_AAT_LAYOUT_TRAK_TABLE_HH +#define HB_AAT_LAYOUT_TRAK_TABLE_HH + +#include "hb-aat-layout-common.hh" +#include "hb-ot-layout.hh" +#include "hb-open-type.hh" + +/* + * trak -- Tracking + * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6trak.html + */ +#define HB_AAT_TAG_trak HB_TAG('t','r','a','k') + + +namespace AAT { + + +struct TrackTableEntry +{ + friend struct TrackData; + + float get_track_value () const { return track.to_float (); } + + int get_value (const void *base, unsigned int index, + unsigned int table_size) const + { return (base+valuesZ).as_array (table_size)[index]; } + + public: + bool sanitize (hb_sanitize_context_t *c, const void *base, + unsigned int table_size) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + (valuesZ.sanitize (c, base, table_size)))); + } + + protected: + F16DOT16 track; /* Track value for this record. */ + NameID trackNameID; /* The 'name' table index for this track. + * (a short word or phrase like "loose" + * or "very tight") */ + NNOffset16To> + valuesZ; /* Offset from start of tracking table to + * per-size tracking values for this track. */ + + public: + DEFINE_SIZE_STATIC (8); +}; + +struct TrackData +{ + float interpolate_at (unsigned int idx, + float target_size, + const TrackTableEntry &trackTableEntry, + const void *base) const + { + unsigned int sizes = nSizes; + hb_array_t size_table ((base+sizeTable).arrayZ, sizes); + + float s0 = size_table[idx].to_float (); + float s1 = size_table[idx + 1].to_float (); + float t = unlikely (s0 == s1) ? 0.f : (target_size - s0) / (s1 - s0); + return t * trackTableEntry.get_value (base, idx + 1, sizes) + + (1.f - t) * trackTableEntry.get_value (base, idx, sizes); + } + + int get_tracking (const void *base, float ptem) const + { + /* + * Choose track. + */ + const TrackTableEntry *trackTableEntry = nullptr; + unsigned int count = nTracks; + for (unsigned int i = 0; i < count; i++) + { + /* Note: Seems like the track entries are sorted by values. But the + * spec doesn't explicitly say that. It just mentions it in the example. */ + + /* For now we only seek for track entries with zero tracking value */ + + if (trackTable[i].get_track_value () == 0.f) + { + trackTableEntry = &trackTable[i]; + break; + } + } + if (!trackTableEntry) return 0.; + + /* + * Choose size. + */ + unsigned int sizes = nSizes; + if (!sizes) return 0.; + if (sizes == 1) return trackTableEntry->get_value (base, 0, sizes); + + hb_array_t size_table ((base+sizeTable).arrayZ, sizes); + unsigned int size_index; + for (size_index = 0; size_index < sizes - 1; size_index++) + if (size_table[size_index].to_float () >= ptem) + break; + + return roundf (interpolate_at (size_index ? size_index - 1 : 0, ptem, + *trackTableEntry, base)); + } + + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + sizeTable.sanitize (c, base, nSizes) && + trackTable.sanitize (c, nTracks, base, nSizes))); + } + + protected: + HBUINT16 nTracks; /* Number of separate tracks included in this table. */ + HBUINT16 nSizes; /* Number of point sizes included in this table. */ + NNOffset32To> + sizeTable; /* Offset from start of the tracking table to + * Array[nSizes] of size values.. */ + UnsizedArrayOf + trackTable; /* Array[nTracks] of TrackTableEntry records. */ + + public: + DEFINE_SIZE_ARRAY (8, trackTable); +}; + +struct trak +{ + static constexpr hb_tag_t tableTag = HB_AAT_TAG_trak; + + bool has_data () const { return version.to_int (); } + + bool apply (hb_aat_apply_context_t *c) const + { + TRACE_APPLY (this); + + hb_mask_t trak_mask = c->plan->trak_mask; + + const float ptem = c->font->ptem; + if (unlikely (ptem <= 0.f)) + return_trace (false); + + hb_buffer_t *buffer = c->buffer; + if (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction)) + { + const TrackData &trackData = this+horizData; + int tracking = trackData.get_tracking (this, ptem); + hb_position_t offset_to_add = c->font->em_scalef_x (tracking / 2); + hb_position_t advance_to_add = c->font->em_scalef_x (tracking); + foreach_grapheme (buffer, start, end) + { + if (!(buffer->info[start].mask & trak_mask)) continue; + buffer->pos[start].x_advance += advance_to_add; + buffer->pos[start].x_offset += offset_to_add; + } + } + else + { + const TrackData &trackData = this+vertData; + int tracking = trackData.get_tracking (this, ptem); + hb_position_t offset_to_add = c->font->em_scalef_y (tracking / 2); + hb_position_t advance_to_add = c->font->em_scalef_y (tracking); + foreach_grapheme (buffer, start, end) + { + if (!(buffer->info[start].mask & trak_mask)) continue; + buffer->pos[start].y_advance += advance_to_add; + buffer->pos[start].y_offset += offset_to_add; + } + } + + return_trace (true); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + + return_trace (likely (c->check_struct (this) && + version.major == 1 && + horizData.sanitize (c, this, this) && + vertData.sanitize (c, this, this))); + } + + protected: + FixedVersion<>version; /* Version of the tracking table + * (0x00010000u for version 1.0). */ + HBUINT16 format; /* Format of the tracking table (set to 0). */ + Offset16To + horizData; /* Offset from start of tracking table to TrackData + * for horizontal text (or 0 if none). */ + Offset16To + vertData; /* Offset from start of tracking table to TrackData + * for vertical text (or 0 if none). */ + HBUINT16 reserved; /* Reserved. Set to 0. */ + + public: + DEFINE_SIZE_STATIC (12); +}; + +} /* namespace AAT */ + + +#endif /* HB_AAT_LAYOUT_TRAK_TABLE_HH */ diff --git a/libs/harfbuzz/src/hb-aat-layout.cc b/libs/harfbuzz/src/hb-aat-layout.cc new file mode 100644 index 00000000..d60126fe --- /dev/null +++ b/libs/harfbuzz/src/hb-aat-layout.cc @@ -0,0 +1,432 @@ +/* + * Copyright © 2017 Google, Inc. + * Copyright © 2018 Ebrahim Byagowi + * + * 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 + */ + +#include "hb.hh" + +#include "hb-aat-layout.hh" +#include "hb-aat-layout-ankr-table.hh" +#include "hb-aat-layout-bsln-table.hh" // Just so we compile it; unused otherwise. +#include "hb-aat-layout-feat-table.hh" +#include "hb-aat-layout-just-table.hh" // Just so we compile it; unused otherwise. +#include "hb-aat-layout-kerx-table.hh" +#include "hb-aat-layout-morx-table.hh" +#include "hb-aat-layout-trak-table.hh" +#include "hb-aat-ltag-table.hh" + + +/* + * hb_aat_apply_context_t + */ + +/* Note: This context is used for kerning, even without AAT, hence the condition. */ +#if !defined(HB_NO_AAT) || !defined(HB_NO_OT_KERN) + +AAT::hb_aat_apply_context_t::hb_aat_apply_context_t (const hb_ot_shape_plan_t *plan_, + hb_font_t *font_, + hb_buffer_t *buffer_, + hb_blob_t *blob) : + plan (plan_), + font (font_), + face (font->face), + buffer (buffer_), + sanitizer (), + ankr_table (&Null (AAT::ankr)), + gdef_table (face->table.GDEF->table), + lookup_index (0) +{ + sanitizer.init (blob); + sanitizer.set_num_glyphs (face->get_num_glyphs ()); + sanitizer.start_processing (); + sanitizer.set_max_ops (HB_SANITIZE_MAX_OPS_MAX); +} + +AAT::hb_aat_apply_context_t::~hb_aat_apply_context_t () +{ sanitizer.end_processing (); } + +void +AAT::hb_aat_apply_context_t::set_ankr_table (const AAT::ankr *ankr_table_) +{ ankr_table = ankr_table_; } + +#endif + + +/** + * SECTION:hb-aat-layout + * @title: hb-aat-layout + * @short_description: Apple Advanced Typography Layout + * @include: hb-aat.h + * + * Functions for querying AAT Layout features in the font face. + * + * HarfBuzz supports all of the AAT tables used to implement shaping. Other + * AAT tables and their associated features are not supported. + **/ + + +#if !defined(HB_NO_AAT) || defined(HAVE_CORETEXT) + +/* Mapping from OpenType feature tags to AAT feature names and selectors. + * + * Table data courtesy of Apple. Converted from mnemonics to integers + * when moving to this file. */ +static const hb_aat_feature_mapping_t feature_mappings[] = +{ + {HB_TAG ('a','f','r','c'), HB_AAT_LAYOUT_FEATURE_TYPE_FRACTIONS, HB_AAT_LAYOUT_FEATURE_SELECTOR_VERTICAL_FRACTIONS, HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_FRACTIONS}, + {HB_TAG ('c','2','p','c'), HB_AAT_LAYOUT_FEATURE_TYPE_UPPER_CASE, HB_AAT_LAYOUT_FEATURE_SELECTOR_UPPER_CASE_PETITE_CAPS, HB_AAT_LAYOUT_FEATURE_SELECTOR_DEFAULT_UPPER_CASE}, + {HB_TAG ('c','2','s','c'), HB_AAT_LAYOUT_FEATURE_TYPE_UPPER_CASE, HB_AAT_LAYOUT_FEATURE_SELECTOR_UPPER_CASE_SMALL_CAPS, HB_AAT_LAYOUT_FEATURE_SELECTOR_DEFAULT_UPPER_CASE}, + {HB_TAG ('c','a','l','t'), HB_AAT_LAYOUT_FEATURE_TYPE_CONTEXTUAL_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_ALTERNATES_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_ALTERNATES_OFF}, + {HB_TAG ('c','a','s','e'), HB_AAT_LAYOUT_FEATURE_TYPE_CASE_SENSITIVE_LAYOUT, HB_AAT_LAYOUT_FEATURE_SELECTOR_CASE_SENSITIVE_LAYOUT_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_CASE_SENSITIVE_LAYOUT_OFF}, + {HB_TAG ('c','l','i','g'), HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES, HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_LIGATURES_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_LIGATURES_OFF}, + {HB_TAG ('c','p','s','p'), HB_AAT_LAYOUT_FEATURE_TYPE_CASE_SENSITIVE_LAYOUT, HB_AAT_LAYOUT_FEATURE_SELECTOR_CASE_SENSITIVE_SPACING_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_CASE_SENSITIVE_SPACING_OFF}, + {HB_TAG ('c','s','w','h'), HB_AAT_LAYOUT_FEATURE_TYPE_CONTEXTUAL_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_SWASH_ALTERNATES_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_SWASH_ALTERNATES_OFF}, + {HB_TAG ('d','l','i','g'), HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES, HB_AAT_LAYOUT_FEATURE_SELECTOR_RARE_LIGATURES_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_RARE_LIGATURES_OFF}, + {HB_TAG ('e','x','p','t'), HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE, HB_AAT_LAYOUT_FEATURE_SELECTOR_EXPERT_CHARACTERS, (hb_aat_layout_feature_selector_t) 16}, + {HB_TAG ('f','r','a','c'), HB_AAT_LAYOUT_FEATURE_TYPE_FRACTIONS, HB_AAT_LAYOUT_FEATURE_SELECTOR_DIAGONAL_FRACTIONS, HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_FRACTIONS}, + {HB_TAG ('f','w','i','d'), HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING, HB_AAT_LAYOUT_FEATURE_SELECTOR_MONOSPACED_TEXT, (hb_aat_layout_feature_selector_t) 7}, + {HB_TAG ('h','a','l','t'), HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING, HB_AAT_LAYOUT_FEATURE_SELECTOR_ALT_HALF_WIDTH_TEXT, (hb_aat_layout_feature_selector_t) 7}, + {HB_TAG ('h','i','s','t'), (hb_aat_layout_feature_type_t) 40, (hb_aat_layout_feature_selector_t) 0, (hb_aat_layout_feature_selector_t) 1}, + {HB_TAG ('h','k','n','a'), HB_AAT_LAYOUT_FEATURE_TYPE_ALTERNATE_KANA, HB_AAT_LAYOUT_FEATURE_SELECTOR_ALTERNATE_HORIZ_KANA_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_ALTERNATE_HORIZ_KANA_OFF}, + {HB_TAG ('h','l','i','g'), HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES, HB_AAT_LAYOUT_FEATURE_SELECTOR_HISTORICAL_LIGATURES_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_HISTORICAL_LIGATURES_OFF}, + {HB_TAG ('h','n','g','l'), HB_AAT_LAYOUT_FEATURE_TYPE_TRANSLITERATION, HB_AAT_LAYOUT_FEATURE_SELECTOR_HANJA_TO_HANGUL, HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_TRANSLITERATION}, + {HB_TAG ('h','o','j','o'), HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE, HB_AAT_LAYOUT_FEATURE_SELECTOR_HOJO_CHARACTERS, (hb_aat_layout_feature_selector_t) 16}, + {HB_TAG ('h','w','i','d'), HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING, HB_AAT_LAYOUT_FEATURE_SELECTOR_HALF_WIDTH_TEXT, (hb_aat_layout_feature_selector_t) 7}, + {HB_TAG ('i','t','a','l'), HB_AAT_LAYOUT_FEATURE_TYPE_ITALIC_CJK_ROMAN, HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_ITALIC_ROMAN_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_ITALIC_ROMAN_OFF}, + {HB_TAG ('j','p','0','4'), HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE, HB_AAT_LAYOUT_FEATURE_SELECTOR_JIS2004_CHARACTERS, (hb_aat_layout_feature_selector_t) 16}, + {HB_TAG ('j','p','7','8'), HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE, HB_AAT_LAYOUT_FEATURE_SELECTOR_JIS1978_CHARACTERS, (hb_aat_layout_feature_selector_t) 16}, + {HB_TAG ('j','p','8','3'), HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE, HB_AAT_LAYOUT_FEATURE_SELECTOR_JIS1983_CHARACTERS, (hb_aat_layout_feature_selector_t) 16}, + {HB_TAG ('j','p','9','0'), HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE, HB_AAT_LAYOUT_FEATURE_SELECTOR_JIS1990_CHARACTERS, (hb_aat_layout_feature_selector_t) 16}, + {HB_TAG ('l','i','g','a'), HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES, HB_AAT_LAYOUT_FEATURE_SELECTOR_COMMON_LIGATURES_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_COMMON_LIGATURES_OFF}, + {HB_TAG ('l','n','u','m'), HB_AAT_LAYOUT_FEATURE_TYPE_NUMBER_CASE, HB_AAT_LAYOUT_FEATURE_SELECTOR_UPPER_CASE_NUMBERS, (hb_aat_layout_feature_selector_t) 2}, + {HB_TAG ('m','g','r','k'), HB_AAT_LAYOUT_FEATURE_TYPE_MATHEMATICAL_EXTRAS, HB_AAT_LAYOUT_FEATURE_SELECTOR_MATHEMATICAL_GREEK_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_MATHEMATICAL_GREEK_OFF}, + {HB_TAG ('n','l','c','k'), HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE, HB_AAT_LAYOUT_FEATURE_SELECTOR_NLCCHARACTERS, (hb_aat_layout_feature_selector_t) 16}, + {HB_TAG ('o','n','u','m'), HB_AAT_LAYOUT_FEATURE_TYPE_NUMBER_CASE, HB_AAT_LAYOUT_FEATURE_SELECTOR_LOWER_CASE_NUMBERS, (hb_aat_layout_feature_selector_t) 2}, + {HB_TAG ('o','r','d','n'), HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_POSITION, HB_AAT_LAYOUT_FEATURE_SELECTOR_ORDINALS, HB_AAT_LAYOUT_FEATURE_SELECTOR_NORMAL_POSITION}, + {HB_TAG ('p','a','l','t'), HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING, HB_AAT_LAYOUT_FEATURE_SELECTOR_ALT_PROPORTIONAL_TEXT, (hb_aat_layout_feature_selector_t) 7}, + {HB_TAG ('p','c','a','p'), HB_AAT_LAYOUT_FEATURE_TYPE_LOWER_CASE, HB_AAT_LAYOUT_FEATURE_SELECTOR_LOWER_CASE_PETITE_CAPS, HB_AAT_LAYOUT_FEATURE_SELECTOR_DEFAULT_LOWER_CASE}, + {HB_TAG ('p','k','n','a'), HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING, HB_AAT_LAYOUT_FEATURE_SELECTOR_PROPORTIONAL_TEXT, (hb_aat_layout_feature_selector_t) 7}, + {HB_TAG ('p','n','u','m'), HB_AAT_LAYOUT_FEATURE_TYPE_NUMBER_SPACING, HB_AAT_LAYOUT_FEATURE_SELECTOR_PROPORTIONAL_NUMBERS, (hb_aat_layout_feature_selector_t) 4}, + {HB_TAG ('p','w','i','d'), HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING, HB_AAT_LAYOUT_FEATURE_SELECTOR_PROPORTIONAL_TEXT, (hb_aat_layout_feature_selector_t) 7}, + {HB_TAG ('q','w','i','d'), HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING, HB_AAT_LAYOUT_FEATURE_SELECTOR_QUARTER_WIDTH_TEXT, (hb_aat_layout_feature_selector_t) 7}, + {HB_TAG ('r','l','i','g'), HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES, HB_AAT_LAYOUT_FEATURE_SELECTOR_REQUIRED_LIGATURES_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_REQUIRED_LIGATURES_OFF}, + {HB_TAG ('r','u','b','y'), HB_AAT_LAYOUT_FEATURE_TYPE_RUBY_KANA, HB_AAT_LAYOUT_FEATURE_SELECTOR_RUBY_KANA_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_RUBY_KANA_OFF}, + {HB_TAG ('s','i','n','f'), HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_POSITION, HB_AAT_LAYOUT_FEATURE_SELECTOR_SCIENTIFIC_INFERIORS, HB_AAT_LAYOUT_FEATURE_SELECTOR_NORMAL_POSITION}, + {HB_TAG ('s','m','c','p'), HB_AAT_LAYOUT_FEATURE_TYPE_LOWER_CASE, HB_AAT_LAYOUT_FEATURE_SELECTOR_LOWER_CASE_SMALL_CAPS, HB_AAT_LAYOUT_FEATURE_SELECTOR_DEFAULT_LOWER_CASE}, + {HB_TAG ('s','m','p','l'), HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE, HB_AAT_LAYOUT_FEATURE_SELECTOR_SIMPLIFIED_CHARACTERS, (hb_aat_layout_feature_selector_t) 16}, + {HB_TAG ('s','s','0','1'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_ONE_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_ONE_OFF}, + {HB_TAG ('s','s','0','2'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWO_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWO_OFF}, + {HB_TAG ('s','s','0','3'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_THREE_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_THREE_OFF}, + {HB_TAG ('s','s','0','4'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FOUR_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FOUR_OFF}, + {HB_TAG ('s','s','0','5'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FIVE_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FIVE_OFF}, + {HB_TAG ('s','s','0','6'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SIX_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SIX_OFF}, + {HB_TAG ('s','s','0','7'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SEVEN_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SEVEN_OFF}, + {HB_TAG ('s','s','0','8'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_EIGHT_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_EIGHT_OFF}, + {HB_TAG ('s','s','0','9'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_NINE_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_NINE_OFF}, + {HB_TAG ('s','s','1','0'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TEN_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TEN_OFF}, + {HB_TAG ('s','s','1','1'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_ELEVEN_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_ELEVEN_OFF}, + {HB_TAG ('s','s','1','2'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWELVE_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWELVE_OFF}, + {HB_TAG ('s','s','1','3'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_THIRTEEN_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_THIRTEEN_OFF}, + {HB_TAG ('s','s','1','4'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FOURTEEN_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FOURTEEN_OFF}, + {HB_TAG ('s','s','1','5'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FIFTEEN_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FIFTEEN_OFF}, + {HB_TAG ('s','s','1','6'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SIXTEEN_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SIXTEEN_OFF}, + {HB_TAG ('s','s','1','7'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SEVENTEEN_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SEVENTEEN_OFF}, + {HB_TAG ('s','s','1','8'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_EIGHTEEN_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_EIGHTEEN_OFF}, + {HB_TAG ('s','s','1','9'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_NINETEEN_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_NINETEEN_OFF}, + {HB_TAG ('s','s','2','0'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWENTY_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWENTY_OFF}, + {HB_TAG ('s','u','b','s'), HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_POSITION, HB_AAT_LAYOUT_FEATURE_SELECTOR_INFERIORS, HB_AAT_LAYOUT_FEATURE_SELECTOR_NORMAL_POSITION}, + {HB_TAG ('s','u','p','s'), HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_POSITION, HB_AAT_LAYOUT_FEATURE_SELECTOR_SUPERIORS, HB_AAT_LAYOUT_FEATURE_SELECTOR_NORMAL_POSITION}, + {HB_TAG ('s','w','s','h'), HB_AAT_LAYOUT_FEATURE_TYPE_CONTEXTUAL_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_SWASH_ALTERNATES_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_SWASH_ALTERNATES_OFF}, + {HB_TAG ('t','i','t','l'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLE_OPTIONS, HB_AAT_LAYOUT_FEATURE_SELECTOR_TITLING_CAPS, HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_STYLE_OPTIONS}, + {HB_TAG ('t','n','a','m'), HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE, HB_AAT_LAYOUT_FEATURE_SELECTOR_TRADITIONAL_NAMES_CHARACTERS, (hb_aat_layout_feature_selector_t) 16}, + {HB_TAG ('t','n','u','m'), HB_AAT_LAYOUT_FEATURE_TYPE_NUMBER_SPACING, HB_AAT_LAYOUT_FEATURE_SELECTOR_MONOSPACED_NUMBERS, (hb_aat_layout_feature_selector_t) 4}, + {HB_TAG ('t','r','a','d'), HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE, HB_AAT_LAYOUT_FEATURE_SELECTOR_TRADITIONAL_CHARACTERS, (hb_aat_layout_feature_selector_t) 16}, + {HB_TAG ('t','w','i','d'), HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING, HB_AAT_LAYOUT_FEATURE_SELECTOR_THIRD_WIDTH_TEXT, (hb_aat_layout_feature_selector_t) 7}, + {HB_TAG ('u','n','i','c'), HB_AAT_LAYOUT_FEATURE_TYPE_LETTER_CASE, (hb_aat_layout_feature_selector_t) 14, (hb_aat_layout_feature_selector_t) 15}, + {HB_TAG ('v','a','l','t'), HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING, HB_AAT_LAYOUT_FEATURE_SELECTOR_ALT_PROPORTIONAL_TEXT, (hb_aat_layout_feature_selector_t) 7}, + {HB_TAG ('v','e','r','t'), HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_SUBSTITUTION, HB_AAT_LAYOUT_FEATURE_SELECTOR_SUBSTITUTE_VERTICAL_FORMS_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_SUBSTITUTE_VERTICAL_FORMS_OFF}, + {HB_TAG ('v','h','a','l'), HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING, HB_AAT_LAYOUT_FEATURE_SELECTOR_ALT_HALF_WIDTH_TEXT, (hb_aat_layout_feature_selector_t) 7}, + {HB_TAG ('v','k','n','a'), HB_AAT_LAYOUT_FEATURE_TYPE_ALTERNATE_KANA, HB_AAT_LAYOUT_FEATURE_SELECTOR_ALTERNATE_VERT_KANA_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_ALTERNATE_VERT_KANA_OFF}, + {HB_TAG ('v','p','a','l'), HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING, HB_AAT_LAYOUT_FEATURE_SELECTOR_ALT_PROPORTIONAL_TEXT, (hb_aat_layout_feature_selector_t) 7}, + {HB_TAG ('v','r','t','2'), HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_SUBSTITUTION, HB_AAT_LAYOUT_FEATURE_SELECTOR_SUBSTITUTE_VERTICAL_FORMS_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_SUBSTITUTE_VERTICAL_FORMS_OFF}, + {HB_TAG ('v','r','t','r'), HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_SUBSTITUTION, (hb_aat_layout_feature_selector_t) 2, (hb_aat_layout_feature_selector_t) 3}, + {HB_TAG ('z','e','r','o'), HB_AAT_LAYOUT_FEATURE_TYPE_TYPOGRAPHIC_EXTRAS, HB_AAT_LAYOUT_FEATURE_SELECTOR_SLASHED_ZERO_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_SLASHED_ZERO_OFF}, +}; + +/** + * hb_aat_layout_find_feature_mapping: + * @tag: The requested #hb_tag_t feature tag + * + * Fetches the AAT feature-and-selector combination that corresponds + * to a given OpenType feature tag. + * + * Return value: the AAT features and selectors corresponding to the + * OpenType feature tag queried + * + **/ +const hb_aat_feature_mapping_t * +hb_aat_layout_find_feature_mapping (hb_tag_t tag) +{ + return hb_sorted_array (feature_mappings).bsearch (tag); +} +#endif + + +#ifndef HB_NO_AAT + +/* + * mort/morx/kerx/trak + */ + + +void +hb_aat_layout_compile_map (const hb_aat_map_builder_t *mapper, + hb_aat_map_t *map) +{ + const AAT::morx& morx = *mapper->face->table.morx; + if (morx.has_data ()) + { + morx.compile_flags (mapper, map); + return; + } + + const AAT::mort& mort = *mapper->face->table.mort; + if (mort.has_data ()) + { + mort.compile_flags (mapper, map); + return; + } +} + + +/** + * hb_aat_layout_has_substitution: + * @face: #hb_face_t to work upon + * + * Tests whether the specified face includes any substitutions in the + * `morx` or `mort` tables. + * + * Note: does not examine the `GSUB` table. + * + * Return value: `true` if data found, `false` otherwise + * + * Since: 2.3.0 + */ +hb_bool_t +hb_aat_layout_has_substitution (hb_face_t *face) +{ + return face->table.morx->has_data () || + face->table.mort->has_data (); +} + +void +hb_aat_layout_substitute (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer) +{ + hb_blob_t *morx_blob = font->face->table.morx.get_blob (); + const AAT::morx& morx = *morx_blob->as (); + if (morx.has_data ()) + { + AAT::hb_aat_apply_context_t c (plan, font, buffer, morx_blob); + if (!buffer->message (font, "start table morx")) return; + morx.apply (&c); + (void) buffer->message (font, "end table morx"); + return; + } + + hb_blob_t *mort_blob = font->face->table.mort.get_blob (); + const AAT::mort& mort = *mort_blob->as (); + if (mort.has_data ()) + { + AAT::hb_aat_apply_context_t c (plan, font, buffer, mort_blob); + if (!buffer->message (font, "start table mort")) return; + mort.apply (&c); + (void) buffer->message (font, "end table mort"); + return; + } +} + +void +hb_aat_layout_zero_width_deleted_glyphs (hb_buffer_t *buffer) +{ + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; + hb_glyph_position_t *pos = buffer->pos; + for (unsigned int i = 0; i < count; i++) + if (unlikely (info[i].codepoint == AAT::DELETED_GLYPH)) + pos[i].x_advance = pos[i].y_advance = pos[i].x_offset = pos[i].y_offset = 0; +} + +static bool +is_deleted_glyph (const hb_glyph_info_t *info) +{ + return info->codepoint == AAT::DELETED_GLYPH; +} + +void +hb_aat_layout_remove_deleted_glyphs (hb_buffer_t *buffer) +{ + hb_ot_layout_delete_glyphs_inplace (buffer, is_deleted_glyph); +} + +/** + * hb_aat_layout_has_positioning: + * @face: #hb_face_t to work upon + * + * Tests whether the specified face includes any positioning information + * in the `kerx` table. + * + * Note: does not examine the `GPOS` table. + * + * Return value: `true` if data found, `false` otherwise + * + * Since: 2.3.0 + */ +hb_bool_t +hb_aat_layout_has_positioning (hb_face_t *face) +{ + return face->table.kerx->has_data (); +} + +void +hb_aat_layout_position (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer) +{ + hb_blob_t *kerx_blob = font->face->table.kerx.get_blob (); + const AAT::kerx& kerx = *kerx_blob->as (); + + AAT::hb_aat_apply_context_t c (plan, font, buffer, kerx_blob); + if (!buffer->message (font, "start table kerx")) return; + c.set_ankr_table (font->face->table.ankr.get ()); + kerx.apply (&c); + (void) buffer->message (font, "end table kerx"); +} + + +/** + * hb_aat_layout_has_tracking: + * @face:: #hb_face_t to work upon + * + * Tests whether the specified face includes any tracking information + * in the `trak` table. + * + * Return value: `true` if data found, `false` otherwise + * + * Since: 2.3.0 + */ +hb_bool_t +hb_aat_layout_has_tracking (hb_face_t *face) +{ + return face->table.trak->has_data (); +} + +void +hb_aat_layout_track (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer) +{ + const AAT::trak& trak = *font->face->table.trak; + + AAT::hb_aat_apply_context_t c (plan, font, buffer); + trak.apply (&c); +} + +/** + * hb_aat_layout_get_feature_types: + * @face: #hb_face_t to work upon + * @start_offset: offset of the first feature type to retrieve + * @feature_count: (inout) (optional): Input = the maximum number of feature types to return; + * Output = the actual number of feature types returned (may be zero) + * @features: (out caller-allocates) (array length=feature_count): Array of feature types found + * + * Fetches a list of the AAT feature types included in the specified face. + * + * Return value: Number of all available feature types. + * + * Since: 2.2.0 + */ +unsigned int +hb_aat_layout_get_feature_types (hb_face_t *face, + unsigned int start_offset, + unsigned int *feature_count, /* IN/OUT. May be NULL. */ + hb_aat_layout_feature_type_t *features /* OUT. May be NULL. */) +{ + return face->table.feat->get_feature_types (start_offset, feature_count, features); +} + +/** + * hb_aat_layout_feature_type_get_name_id: + * @face: #hb_face_t to work upon + * @feature_type: The #hb_aat_layout_feature_type_t of the requested feature type + * + * Fetches the name identifier of the specified feature type in the face's `name` table. + * + * Return value: Name identifier of the requested feature type + * + * Since: 2.2.0 + */ +hb_ot_name_id_t +hb_aat_layout_feature_type_get_name_id (hb_face_t *face, + hb_aat_layout_feature_type_t feature_type) +{ + return face->table.feat->get_feature_name_id (feature_type); +} + +/** + * hb_aat_layout_feature_type_get_selector_infos: + * @face: #hb_face_t to work upon + * @feature_type: The #hb_aat_layout_feature_type_t of the requested feature type + * @start_offset: offset of the first feature type to retrieve + * @selector_count: (inout) (optional): Input = the maximum number of selectors to return; + * Output = the actual number of selectors returned (may be zero) + * @selectors: (out caller-allocates) (array length=selector_count) (optional): + * A buffer pointer. The selectors available for the feature type queries. + * @default_index: (out) (optional): The index of the feature's default selector, if any + * + * Fetches a list of the selectors available for the specified feature in the given face. + * + * If upon return, @default_index is set to #HB_AAT_LAYOUT_NO_SELECTOR_INDEX, then + * the feature type is non-exclusive. Otherwise, @default_index is the index of + * the selector that is selected by default. + * + * Return value: Number of all available feature selectors + * + * Since: 2.2.0 + */ +unsigned int +hb_aat_layout_feature_type_get_selector_infos (hb_face_t *face, + hb_aat_layout_feature_type_t feature_type, + unsigned int start_offset, + unsigned int *selector_count, /* IN/OUT. May be NULL. */ + hb_aat_layout_feature_selector_info_t *selectors, /* OUT. May be NULL. */ + unsigned int *default_index /* OUT. May be NULL. */) +{ + return face->table.feat->get_selector_infos (feature_type, start_offset, selector_count, selectors, default_index); +} + + +#endif diff --git a/libs/harfbuzz/src/hb-aat-layout.h b/libs/harfbuzz/src/hb-aat-layout.h new file mode 100644 index 00000000..9af27400 --- /dev/null +++ b/libs/harfbuzz/src/hb-aat-layout.h @@ -0,0 +1,795 @@ +/* + * Copyright © 2018 Ebrahim Byagowi + * + * 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. + */ + +#if !defined(HB_AAT_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR) +#error "Include instead." +#endif + +#ifndef HB_AAT_LAYOUT_H +#define HB_AAT_LAYOUT_H + +#include "hb.h" + +#include "hb-ot.h" + +HB_BEGIN_DECLS + +/** + * hb_aat_layout_feature_type_t: + * @HB_AAT_LAYOUT_FEATURE_TYPE_INVALID: Initial, unset feature type + * @HB_AAT_LAYOUT_FEATURE_TYPE_ALL_TYPOGRAPHIC: [All Typographic Features](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type0) + * @HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES: [Ligatures](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type1) + * @HB_AAT_LAYOUT_FEATURE_TYPE_CURISVE_CONNECTION: [Cursive Connection](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type2) + * @HB_AAT_LAYOUT_FEATURE_TYPE_LETTER_CASE: [Letter Case](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type3) + * @HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_SUBSTITUTION: [Vertical Substitution](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type4) + * @HB_AAT_LAYOUT_FEATURE_TYPE_LINGUISTIC_REARRANGEMENT: [Linguistic Rearrangement](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type5) + * @HB_AAT_LAYOUT_FEATURE_TYPE_NUMBER_SPACING: [Number Spacing](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type6) + * @HB_AAT_LAYOUT_FEATURE_TYPE_SMART_SWASH_TYPE: [Smart Swash](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type8) + * @HB_AAT_LAYOUT_FEATURE_TYPE_DIACRITICS_TYPE: [Diacritics](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type9) + * @HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_POSITION: [Vertical Position](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type10) + * @HB_AAT_LAYOUT_FEATURE_TYPE_FRACTIONS: [Fractions](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type11) + * @HB_AAT_LAYOUT_FEATURE_TYPE_OVERLAPPING_CHARACTERS_TYPE: [Overlapping Characters](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type13) + * @HB_AAT_LAYOUT_FEATURE_TYPE_TYPOGRAPHIC_EXTRAS: [Typographic Extras](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type14) + * @HB_AAT_LAYOUT_FEATURE_TYPE_MATHEMATICAL_EXTRAS: [Mathematical Extras](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type15) + * @HB_AAT_LAYOUT_FEATURE_TYPE_ORNAMENT_SETS_TYPE: [Ornament Sets](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type16) + * @HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_ALTERNATIVES: [Character Alternatives](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type17) + * @HB_AAT_LAYOUT_FEATURE_TYPE_DESIGN_COMPLEXITY_TYPE: [Design Complexity](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type18) + * @HB_AAT_LAYOUT_FEATURE_TYPE_STYLE_OPTIONS: [Style Options](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type19) + * @HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE: [Character Shape](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type20) + * @HB_AAT_LAYOUT_FEATURE_TYPE_NUMBER_CASE: [Number Case](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type21) + * @HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING: [Text Spacing](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type22) + * @HB_AAT_LAYOUT_FEATURE_TYPE_TRANSLITERATION: [Transliteration](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type23) + * @HB_AAT_LAYOUT_FEATURE_TYPE_ANNOTATION_TYPE: [Annotation](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type24) + * @HB_AAT_LAYOUT_FEATURE_TYPE_KANA_SPACING_TYPE: [Kana Spacing](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type25) + * @HB_AAT_LAYOUT_FEATURE_TYPE_IDEOGRAPHIC_SPACING_TYPE: [Ideographic Spacing](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type26) + * @HB_AAT_LAYOUT_FEATURE_TYPE_UNICODE_DECOMPOSITION_TYPE: [Unicode Decomposition](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type27) + * @HB_AAT_LAYOUT_FEATURE_TYPE_RUBY_KANA: [Ruby Kana](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type28) + * @HB_AAT_LAYOUT_FEATURE_TYPE_CJK_SYMBOL_ALTERNATIVES_TYPE: [CJK Symbol Alternatives](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type29) + * @HB_AAT_LAYOUT_FEATURE_TYPE_IDEOGRAPHIC_ALTERNATIVES_TYPE: [Ideographic Alternatives](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type30) + * @HB_AAT_LAYOUT_FEATURE_TYPE_CJK_VERTICAL_ROMAN_PLACEMENT_TYPE: [CJK Vertical Roman Placement](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type31) + * @HB_AAT_LAYOUT_FEATURE_TYPE_ITALIC_CJK_ROMAN: [Italic CJK Roman](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type32) + * @HB_AAT_LAYOUT_FEATURE_TYPE_CASE_SENSITIVE_LAYOUT: [Case Sensitive Layout](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type33) + * @HB_AAT_LAYOUT_FEATURE_TYPE_ALTERNATE_KANA: [Alternate Kana](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type34) + * @HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES: [Stylistic Alternatives](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type35) + * @HB_AAT_LAYOUT_FEATURE_TYPE_CONTEXTUAL_ALTERNATIVES: [Contextual Alternatives](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type36) + * @HB_AAT_LAYOUT_FEATURE_TYPE_LOWER_CASE: [Lower Case](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type37) + * @HB_AAT_LAYOUT_FEATURE_TYPE_UPPER_CASE: [Upper Case](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type38) + * @HB_AAT_LAYOUT_FEATURE_TYPE_LANGUAGE_TAG_TYPE: [Language Tag](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type39) + * @HB_AAT_LAYOUT_FEATURE_TYPE_CJK_ROMAN_SPACING_TYPE: [CJK Roman Spacing](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type103) + * + * The possible feature types defined for AAT shaping, from Apple [Font Feature Registry](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html). + * + * Since: 2.2.0 + */ +typedef enum +{ + HB_AAT_LAYOUT_FEATURE_TYPE_INVALID = 0xFFFF, + + HB_AAT_LAYOUT_FEATURE_TYPE_ALL_TYPOGRAPHIC = 0, + HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES = 1, + HB_AAT_LAYOUT_FEATURE_TYPE_CURISVE_CONNECTION = 2, + HB_AAT_LAYOUT_FEATURE_TYPE_LETTER_CASE = 3, + HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_SUBSTITUTION = 4, + HB_AAT_LAYOUT_FEATURE_TYPE_LINGUISTIC_REARRANGEMENT = 5, + HB_AAT_LAYOUT_FEATURE_TYPE_NUMBER_SPACING = 6, + HB_AAT_LAYOUT_FEATURE_TYPE_SMART_SWASH_TYPE = 8, + HB_AAT_LAYOUT_FEATURE_TYPE_DIACRITICS_TYPE = 9, + HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_POSITION = 10, + HB_AAT_LAYOUT_FEATURE_TYPE_FRACTIONS = 11, + HB_AAT_LAYOUT_FEATURE_TYPE_OVERLAPPING_CHARACTERS_TYPE = 13, + HB_AAT_LAYOUT_FEATURE_TYPE_TYPOGRAPHIC_EXTRAS = 14, + HB_AAT_LAYOUT_FEATURE_TYPE_MATHEMATICAL_EXTRAS = 15, + HB_AAT_LAYOUT_FEATURE_TYPE_ORNAMENT_SETS_TYPE = 16, + HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_ALTERNATIVES = 17, + HB_AAT_LAYOUT_FEATURE_TYPE_DESIGN_COMPLEXITY_TYPE = 18, + HB_AAT_LAYOUT_FEATURE_TYPE_STYLE_OPTIONS = 19, + HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE = 20, + HB_AAT_LAYOUT_FEATURE_TYPE_NUMBER_CASE = 21, + HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING = 22, + HB_AAT_LAYOUT_FEATURE_TYPE_TRANSLITERATION = 23, + HB_AAT_LAYOUT_FEATURE_TYPE_ANNOTATION_TYPE = 24, + HB_AAT_LAYOUT_FEATURE_TYPE_KANA_SPACING_TYPE = 25, + HB_AAT_LAYOUT_FEATURE_TYPE_IDEOGRAPHIC_SPACING_TYPE = 26, + HB_AAT_LAYOUT_FEATURE_TYPE_UNICODE_DECOMPOSITION_TYPE = 27, + HB_AAT_LAYOUT_FEATURE_TYPE_RUBY_KANA = 28, + HB_AAT_LAYOUT_FEATURE_TYPE_CJK_SYMBOL_ALTERNATIVES_TYPE = 29, + HB_AAT_LAYOUT_FEATURE_TYPE_IDEOGRAPHIC_ALTERNATIVES_TYPE = 30, + HB_AAT_LAYOUT_FEATURE_TYPE_CJK_VERTICAL_ROMAN_PLACEMENT_TYPE = 31, + HB_AAT_LAYOUT_FEATURE_TYPE_ITALIC_CJK_ROMAN = 32, + HB_AAT_LAYOUT_FEATURE_TYPE_CASE_SENSITIVE_LAYOUT = 33, + HB_AAT_LAYOUT_FEATURE_TYPE_ALTERNATE_KANA = 34, + HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES = 35, + HB_AAT_LAYOUT_FEATURE_TYPE_CONTEXTUAL_ALTERNATIVES = 36, + HB_AAT_LAYOUT_FEATURE_TYPE_LOWER_CASE = 37, + HB_AAT_LAYOUT_FEATURE_TYPE_UPPER_CASE = 38, + HB_AAT_LAYOUT_FEATURE_TYPE_LANGUAGE_TAG_TYPE = 39, + HB_AAT_LAYOUT_FEATURE_TYPE_CJK_ROMAN_SPACING_TYPE = 103, + + /*< private >*/ + _HB_AAT_LAYOUT_FEATURE_TYPE_MAX_VALUE = HB_TAG_MAX_SIGNED /*< skip >*/ +} hb_aat_layout_feature_type_t; + +/** + * hb_aat_layout_feature_selector_t: + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_INVALID: Initial, unset feature selector + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_ALL_TYPE_FEATURES_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_ALL_TYPOGRAPHIC + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_ALL_TYPE_FEATURES_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_ALL_TYPOGRAPHIC + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_REQUIRED_LIGATURES_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_REQUIRED_LIGATURES_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_COMMON_LIGATURES_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_COMMON_LIGATURES_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_RARE_LIGATURES_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_RARE_LIGATURES_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_LOGOS_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_LOGOS_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_REBUS_PICTURES_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_REBUS_PICTURES_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_DIPHTHONG_LIGATURES_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_DIPHTHONG_LIGATURES_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_SQUARED_LIGATURES_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_SQUARED_LIGATURES_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_ABBREV_SQUARED_LIGATURES_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_ABBREV_SQUARED_LIGATURES_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_SYMBOL_LIGATURES_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_SYMBOL_LIGATURES_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_LIGATURES_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_LIGATURES_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_HISTORICAL_LIGATURES_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_HISTORICAL_LIGATURES_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_UNCONNECTED: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_PARTIALLY_CONNECTED: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_CURSIVE: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_UPPER_AND_LOWER_CASE: Deprecated + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_ALL_CAPS: Deprecated + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_ALL_LOWER_CASE: Deprecated + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_SMALL_CAPS: Deprecated + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_INITIAL_CAPS: Deprecated + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_INITIAL_CAPS_AND_SMALL_CAPS: Deprecated + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_SUBSTITUTE_VERTICAL_FORMS_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_SUBSTITUTION + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_SUBSTITUTE_VERTICAL_FORMS_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_SUBSTITUTION + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_LINGUISTIC_REARRANGEMENT_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_LINGUISTIC_REARRANGEMENT + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_LINGUISTIC_REARRANGEMENT_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_LINGUISTIC_REARRANGEMENT + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_MONOSPACED_NUMBERS: for #HB_AAT_LAYOUT_FEATURE_TYPE_NUMBER_SPACING + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_PROPORTIONAL_NUMBERS: for #HB_AAT_LAYOUT_FEATURE_TYPE_NUMBER_SPACING + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_THIRD_WIDTH_NUMBERS: for #HB_AAT_LAYOUT_FEATURE_TYPE_NUMBER_SPACING + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_QUARTER_WIDTH_NUMBERS: for #HB_AAT_LAYOUT_FEATURE_TYPE_NUMBER_SPACING + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_WORD_INITIAL_SWASHES_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_SMART_SWASH_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_WORD_INITIAL_SWASHES_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_SMART_SWASH_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_WORD_FINAL_SWASHES_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_SMART_SWASH_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_WORD_FINAL_SWASHES_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_SMART_SWASH_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_LINE_INITIAL_SWASHES_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_SMART_SWASH_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_LINE_INITIAL_SWASHES_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_SMART_SWASH_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_LINE_FINAL_SWASHES_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_SMART_SWASH_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_LINE_FINAL_SWASHES_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_SMART_SWASH_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_NON_FINAL_SWASHES_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_SMART_SWASH_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_NON_FINAL_SWASHES_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_SMART_SWASH_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_SHOW_DIACRITICS: for #HB_AAT_LAYOUT_FEATURE_TYPE_DIACRITICS_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_HIDE_DIACRITICS: for #HB_AAT_LAYOUT_FEATURE_TYPE_DIACRITICS_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_DECOMPOSE_DIACRITICS: for #HB_AAT_LAYOUT_FEATURE_TYPE_DIACRITICS_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_NORMAL_POSITION: for #HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_POSITION + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_SUPERIORS: for #HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_POSITION + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_INFERIORS: for #HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_POSITION + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_ORDINALS: for #HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_POSITION + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_SCIENTIFIC_INFERIORS: for #HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_POSITION + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_FRACTIONS: for #HB_AAT_LAYOUT_FEATURE_TYPE_FRACTIONS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_VERTICAL_FRACTIONS: for #HB_AAT_LAYOUT_FEATURE_TYPE_FRACTIONS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_DIAGONAL_FRACTIONS: for #HB_AAT_LAYOUT_FEATURE_TYPE_FRACTIONS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_PREVENT_OVERLAP_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_OVERLAPPING_CHARACTERS_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_PREVENT_OVERLAP_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_OVERLAPPING_CHARACTERS_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_HYPHENS_TO_EM_DASH_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_TYPOGRAPHIC_EXTRAS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_HYPHENS_TO_EM_DASH_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_TYPOGRAPHIC_EXTRAS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_HYPHEN_TO_EN_DASH_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_TYPOGRAPHIC_EXTRAS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_HYPHEN_TO_EN_DASH_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_TYPOGRAPHIC_EXTRAS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_SLASHED_ZERO_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_TYPOGRAPHIC_EXTRAS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_SLASHED_ZERO_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_TYPOGRAPHIC_EXTRAS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_FORM_INTERROBANG_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_TYPOGRAPHIC_EXTRAS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_FORM_INTERROBANG_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_TYPOGRAPHIC_EXTRAS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_SMART_QUOTES_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_TYPOGRAPHIC_EXTRAS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_SMART_QUOTES_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_TYPOGRAPHIC_EXTRAS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_PERIODS_TO_ELLIPSIS_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_TYPOGRAPHIC_EXTRAS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_PERIODS_TO_ELLIPSIS_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_TYPOGRAPHIC_EXTRAS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_HYPHEN_TO_MINUS_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_MATHEMATICAL_EXTRAS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_HYPHEN_TO_MINUS_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_MATHEMATICAL_EXTRAS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_ASTERISK_TO_MULTIPLY_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_MATHEMATICAL_EXTRAS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_ASTERISK_TO_MULTIPLY_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_MATHEMATICAL_EXTRAS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_SLASH_TO_DIVIDE_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_MATHEMATICAL_EXTRAS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_SLASH_TO_DIVIDE_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_MATHEMATICAL_EXTRAS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_INEQUALITY_LIGATURES_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_MATHEMATICAL_EXTRAS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_INEQUALITY_LIGATURES_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_MATHEMATICAL_EXTRAS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_EXPONENTS_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_MATHEMATICAL_EXTRAS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_EXPONENTS_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_MATHEMATICAL_EXTRAS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_MATHEMATICAL_GREEK_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_MATHEMATICAL_EXTRAS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_MATHEMATICAL_GREEK_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_MATHEMATICAL_EXTRAS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_ORNAMENTS: for #HB_AAT_LAYOUT_FEATURE_TYPE_ORNAMENT_SETS_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_DINGBATS: for #HB_AAT_LAYOUT_FEATURE_TYPE_ORNAMENT_SETS_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_PI_CHARACTERS: for #HB_AAT_LAYOUT_FEATURE_TYPE_ORNAMENT_SETS_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_FLEURONS: for #HB_AAT_LAYOUT_FEATURE_TYPE_ORNAMENT_SETS_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_DECORATIVE_BORDERS: for #HB_AAT_LAYOUT_FEATURE_TYPE_ORNAMENT_SETS_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_INTERNATIONAL_SYMBOLS: for #HB_AAT_LAYOUT_FEATURE_TYPE_ORNAMENT_SETS_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_MATH_SYMBOLS: for #HB_AAT_LAYOUT_FEATURE_TYPE_ORNAMENT_SETS_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_ALTERNATES: for #HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_DESIGN_LEVEL1: for #HB_AAT_LAYOUT_FEATURE_TYPE_DESIGN_COMPLEXITY_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_DESIGN_LEVEL2: for #HB_AAT_LAYOUT_FEATURE_TYPE_DESIGN_COMPLEXITY_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_DESIGN_LEVEL3: for #HB_AAT_LAYOUT_FEATURE_TYPE_DESIGN_COMPLEXITY_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_DESIGN_LEVEL4: for #HB_AAT_LAYOUT_FEATURE_TYPE_DESIGN_COMPLEXITY_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_DESIGN_LEVEL5: for #HB_AAT_LAYOUT_FEATURE_TYPE_DESIGN_COMPLEXITY_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_STYLE_OPTIONS: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLE_OPTIONS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_DISPLAY_TEXT: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLE_OPTIONS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_ENGRAVED_TEXT: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLE_OPTIONS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_ILLUMINATED_CAPS: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLE_OPTIONS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_TITLING_CAPS: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLE_OPTIONS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_TALL_CAPS: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLE_OPTIONS + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_TRADITIONAL_CHARACTERS: for #HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_SIMPLIFIED_CHARACTERS: for #HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_JIS1978_CHARACTERS: for #HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_JIS1983_CHARACTERS: for #HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_JIS1990_CHARACTERS: for #HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_TRADITIONAL_ALT_ONE: for #HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_TRADITIONAL_ALT_TWO: for #HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_TRADITIONAL_ALT_THREE: for #HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_TRADITIONAL_ALT_FOUR: for #HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_TRADITIONAL_ALT_FIVE: for #HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_EXPERT_CHARACTERS: for #HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_JIS2004_CHARACTERS: for #HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_HOJO_CHARACTERS: for #HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_NLCCHARACTERS: for #HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_TRADITIONAL_NAMES_CHARACTERS: for #HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_LOWER_CASE_NUMBERS: for #HB_AAT_LAYOUT_FEATURE_TYPE_NUMBER_CASE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_UPPER_CASE_NUMBERS: for #HB_AAT_LAYOUT_FEATURE_TYPE_NUMBER_CASE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_PROPORTIONAL_TEXT: for #HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_MONOSPACED_TEXT: for #HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_HALF_WIDTH_TEXT: for #HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_THIRD_WIDTH_TEXT: for #HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_QUARTER_WIDTH_TEXT: for #HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_ALT_PROPORTIONAL_TEXT: for #HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_ALT_HALF_WIDTH_TEXT: for #HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_TRANSLITERATION: for #HB_AAT_LAYOUT_FEATURE_TYPE_TRANSLITERATION + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_HANJA_TO_HANGUL: for #HB_AAT_LAYOUT_FEATURE_TYPE_TRANSLITERATION + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_HIRAGANA_TO_KATAKANA: for #HB_AAT_LAYOUT_FEATURE_TYPE_TRANSLITERATION + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_KATAKANA_TO_HIRAGANA: for #HB_AAT_LAYOUT_FEATURE_TYPE_TRANSLITERATION + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_KANA_TO_ROMANIZATION: for #HB_AAT_LAYOUT_FEATURE_TYPE_TRANSLITERATION + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_ROMANIZATION_TO_HIRAGANA: for #HB_AAT_LAYOUT_FEATURE_TYPE_TRANSLITERATION + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_ROMANIZATION_TO_KATAKANA: for #HB_AAT_LAYOUT_FEATURE_TYPE_TRANSLITERATION + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_HANJA_TO_HANGUL_ALT_ONE: for #HB_AAT_LAYOUT_FEATURE_TYPE_TRANSLITERATION + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_HANJA_TO_HANGUL_ALT_TWO: for #HB_AAT_LAYOUT_FEATURE_TYPE_TRANSLITERATION + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_HANJA_TO_HANGUL_ALT_THREE: for #HB_AAT_LAYOUT_FEATURE_TYPE_TRANSLITERATION + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_ANNOTATION: for #HB_AAT_LAYOUT_FEATURE_TYPE_ANNOTATION_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_BOX_ANNOTATION: for #HB_AAT_LAYOUT_FEATURE_TYPE_ANNOTATION_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_ROUNDED_BOX_ANNOTATION: for #HB_AAT_LAYOUT_FEATURE_TYPE_ANNOTATION_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_CIRCLE_ANNOTATION: for #HB_AAT_LAYOUT_FEATURE_TYPE_ANNOTATION_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_INVERTED_CIRCLE_ANNOTATION: for #HB_AAT_LAYOUT_FEATURE_TYPE_ANNOTATION_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_PARENTHESIS_ANNOTATION: for #HB_AAT_LAYOUT_FEATURE_TYPE_ANNOTATION_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_PERIOD_ANNOTATION: for #HB_AAT_LAYOUT_FEATURE_TYPE_ANNOTATION_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_ROMAN_NUMERAL_ANNOTATION: for #HB_AAT_LAYOUT_FEATURE_TYPE_ANNOTATION_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_DIAMOND_ANNOTATION: for #HB_AAT_LAYOUT_FEATURE_TYPE_ANNOTATION_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_INVERTED_BOX_ANNOTATION: for #HB_AAT_LAYOUT_FEATURE_TYPE_ANNOTATION_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_INVERTED_ROUNDED_BOX_ANNOTATION: for #HB_AAT_LAYOUT_FEATURE_TYPE_ANNOTATION_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_FULL_WIDTH_KANA: for #HB_AAT_LAYOUT_FEATURE_TYPE_KANA_SPACING_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_PROPORTIONAL_KANA: for #HB_AAT_LAYOUT_FEATURE_TYPE_KANA_SPACING_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_FULL_WIDTH_IDEOGRAPHS: for #HB_AAT_LAYOUT_FEATURE_TYPE_IDEOGRAPHIC_SPACING_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_PROPORTIONAL_IDEOGRAPHS: for #HB_AAT_LAYOUT_FEATURE_TYPE_IDEOGRAPHIC_SPACING_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_HALF_WIDTH_IDEOGRAPHS: for #HB_AAT_LAYOUT_FEATURE_TYPE_IDEOGRAPHIC_SPACING_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_CANONICAL_COMPOSITION_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_UNICODE_DECOMPOSITION_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_CANONICAL_COMPOSITION_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_UNICODE_DECOMPOSITION_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_COMPATIBILITY_COMPOSITION_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_UNICODE_DECOMPOSITION_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_COMPATIBILITY_COMPOSITION_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_UNICODE_DECOMPOSITION_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_TRANSCODING_COMPOSITION_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_UNICODE_DECOMPOSITION_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_TRANSCODING_COMPOSITION_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_UNICODE_DECOMPOSITION_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_RUBY_KANA: Deprecated; use #HB_AAT_LAYOUT_FEATURE_SELECTOR_RUBY_KANA_OFF instead + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_RUBY_KANA: Deprecated; use #HB_AAT_LAYOUT_FEATURE_SELECTOR_RUBY_KANA_ON instead + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_RUBY_KANA_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_RUBY_KANA + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_RUBY_KANA_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_RUBY_KANA + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_CJK_SYMBOL_ALTERNATIVES: for #HB_AAT_LAYOUT_FEATURE_TYPE_CJK_SYMBOL_ALTERNATIVES_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_SYMBOL_ALT_ONE: for #HB_AAT_LAYOUT_FEATURE_TYPE_CJK_SYMBOL_ALTERNATIVES_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_SYMBOL_ALT_TWO: for #HB_AAT_LAYOUT_FEATURE_TYPE_CJK_SYMBOL_ALTERNATIVES_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_SYMBOL_ALT_THREE: for #HB_AAT_LAYOUT_FEATURE_TYPE_CJK_SYMBOL_ALTERNATIVES_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_SYMBOL_ALT_FOUR: for #HB_AAT_LAYOUT_FEATURE_TYPE_CJK_SYMBOL_ALTERNATIVES_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_SYMBOL_ALT_FIVE: for #HB_AAT_LAYOUT_FEATURE_TYPE_CJK_SYMBOL_ALTERNATIVES_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_IDEOGRAPHIC_ALTERNATIVES: for #HB_AAT_LAYOUT_FEATURE_TYPE_IDEOGRAPHIC_ALTERNATIVES_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_IDEOGRAPHIC_ALT_ONE: for #HB_AAT_LAYOUT_FEATURE_TYPE_IDEOGRAPHIC_ALTERNATIVES_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_IDEOGRAPHIC_ALT_TWO: for #HB_AAT_LAYOUT_FEATURE_TYPE_IDEOGRAPHIC_ALTERNATIVES_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_IDEOGRAPHIC_ALT_THREE: for #HB_AAT_LAYOUT_FEATURE_TYPE_IDEOGRAPHIC_ALTERNATIVES_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_IDEOGRAPHIC_ALT_FOUR: for #HB_AAT_LAYOUT_FEATURE_TYPE_IDEOGRAPHIC_ALTERNATIVES_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_IDEOGRAPHIC_ALT_FIVE: for #HB_AAT_LAYOUT_FEATURE_TYPE_IDEOGRAPHIC_ALTERNATIVES_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_VERTICAL_ROMAN_CENTERED: for #HB_AAT_LAYOUT_FEATURE_TYPE_CJK_VERTICAL_ROMAN_PLACEMENT_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_VERTICAL_ROMAN_HBASELINE: for #HB_AAT_LAYOUT_FEATURE_TYPE_CJK_VERTICAL_ROMAN_PLACEMENT_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_CJK_ITALIC_ROMAN: Deprecated; use #HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_ITALIC_ROMAN_OFF instead + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_ITALIC_ROMAN: Deprecated; use #HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_ITALIC_ROMAN_ON instead + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_ITALIC_ROMAN_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_ITALIC_CJK_ROMAN + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_ITALIC_ROMAN_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_ITALIC_CJK_ROMAN + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_CASE_SENSITIVE_LAYOUT_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_CASE_SENSITIVE_LAYOUT + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_CASE_SENSITIVE_LAYOUT_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_CASE_SENSITIVE_LAYOUT + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_CASE_SENSITIVE_SPACING_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_CASE_SENSITIVE_LAYOUT + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_CASE_SENSITIVE_SPACING_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_CASE_SENSITIVE_LAYOUT + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_ALTERNATE_HORIZ_KANA_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_ALTERNATE_KANA + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_ALTERNATE_HORIZ_KANA_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_ALTERNATE_KANA + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_ALTERNATE_VERT_KANA_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_ALTERNATE_KANA + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_ALTERNATE_VERT_KANA_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_ALTERNATE_KANA + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_STYLISTIC_ALTERNATES: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_ONE_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_ONE_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWO_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWO_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_THREE_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_THREE_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FOUR_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FOUR_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FIVE_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FIVE_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SIX_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SIX_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SEVEN_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SEVEN_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_EIGHT_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_EIGHT_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_NINE_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_NINE_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TEN_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TEN_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_ELEVEN_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_ELEVEN_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWELVE_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWELVE_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_THIRTEEN_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_THIRTEEN_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FOURTEEN_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FOURTEEN_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FIFTEEN_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FIFTEEN_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SIXTEEN_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SIXTEEN_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SEVENTEEN_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SEVENTEEN_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_EIGHTEEN_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_EIGHTEEN_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_NINETEEN_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_NINETEEN_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWENTY_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWENTY_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_ALTERNATES_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_CONTEXTUAL_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_ALTERNATES_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_CONTEXTUAL_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_SWASH_ALTERNATES_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_CONTEXTUAL_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_SWASH_ALTERNATES_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_CONTEXTUAL_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_SWASH_ALTERNATES_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_CONTEXTUAL_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_SWASH_ALTERNATES_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_CONTEXTUAL_ALTERNATIVES + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_DEFAULT_LOWER_CASE: for #HB_AAT_LAYOUT_FEATURE_TYPE_LOWER_CASE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_LOWER_CASE_SMALL_CAPS: for #HB_AAT_LAYOUT_FEATURE_TYPE_LOWER_CASE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_LOWER_CASE_PETITE_CAPS: for #HB_AAT_LAYOUT_FEATURE_TYPE_LOWER_CASE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_DEFAULT_UPPER_CASE: for #HB_AAT_LAYOUT_FEATURE_TYPE_UPPER_CASE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_UPPER_CASE_SMALL_CAPS: for #HB_AAT_LAYOUT_FEATURE_TYPE_UPPER_CASE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_UPPER_CASE_PETITE_CAPS: for #HB_AAT_LAYOUT_FEATURE_TYPE_UPPER_CASE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_HALF_WIDTH_CJK_ROMAN: for #HB_AAT_LAYOUT_FEATURE_TYPE_CJK_ROMAN_SPACING_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_PROPORTIONAL_CJK_ROMAN: for #HB_AAT_LAYOUT_FEATURE_TYPE_CJK_ROMAN_SPACING_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_DEFAULT_CJK_ROMAN: for #HB_AAT_LAYOUT_FEATURE_TYPE_CJK_ROMAN_SPACING_TYPE + * @HB_AAT_LAYOUT_FEATURE_SELECTOR_FULL_WIDTH_CJK_ROMAN: for #HB_AAT_LAYOUT_FEATURE_TYPE_CJK_ROMAN_SPACING_TYPE + * + * The selectors defined for specifying AAT feature settings. + * + * Since: 2.2.0 + */ +typedef enum +{ + HB_AAT_LAYOUT_FEATURE_SELECTOR_INVALID = 0xFFFF, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_ALL_TYPOGRAPHIC */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_ALL_TYPE_FEATURES_ON = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_ALL_TYPE_FEATURES_OFF = 1, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_REQUIRED_LIGATURES_ON = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_REQUIRED_LIGATURES_OFF = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_COMMON_LIGATURES_ON = 2, + HB_AAT_LAYOUT_FEATURE_SELECTOR_COMMON_LIGATURES_OFF = 3, + HB_AAT_LAYOUT_FEATURE_SELECTOR_RARE_LIGATURES_ON = 4, + HB_AAT_LAYOUT_FEATURE_SELECTOR_RARE_LIGATURES_OFF = 5, + HB_AAT_LAYOUT_FEATURE_SELECTOR_LOGOS_ON = 6, + HB_AAT_LAYOUT_FEATURE_SELECTOR_LOGOS_OFF = 7, + HB_AAT_LAYOUT_FEATURE_SELECTOR_REBUS_PICTURES_ON = 8, + HB_AAT_LAYOUT_FEATURE_SELECTOR_REBUS_PICTURES_OFF = 9, + HB_AAT_LAYOUT_FEATURE_SELECTOR_DIPHTHONG_LIGATURES_ON = 10, + HB_AAT_LAYOUT_FEATURE_SELECTOR_DIPHTHONG_LIGATURES_OFF = 11, + HB_AAT_LAYOUT_FEATURE_SELECTOR_SQUARED_LIGATURES_ON = 12, + HB_AAT_LAYOUT_FEATURE_SELECTOR_SQUARED_LIGATURES_OFF = 13, + HB_AAT_LAYOUT_FEATURE_SELECTOR_ABBREV_SQUARED_LIGATURES_ON = 14, + HB_AAT_LAYOUT_FEATURE_SELECTOR_ABBREV_SQUARED_LIGATURES_OFF = 15, + HB_AAT_LAYOUT_FEATURE_SELECTOR_SYMBOL_LIGATURES_ON = 16, + HB_AAT_LAYOUT_FEATURE_SELECTOR_SYMBOL_LIGATURES_OFF = 17, + HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_LIGATURES_ON = 18, + HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_LIGATURES_OFF = 19, + HB_AAT_LAYOUT_FEATURE_SELECTOR_HISTORICAL_LIGATURES_ON = 20, + HB_AAT_LAYOUT_FEATURE_SELECTOR_HISTORICAL_LIGATURES_OFF = 21, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_UNCONNECTED = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_PARTIALLY_CONNECTED = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_CURSIVE = 2, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_LETTER_CASE */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_UPPER_AND_LOWER_CASE = 0, /* deprecated */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_ALL_CAPS = 1, /* deprecated */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_ALL_LOWER_CASE = 2, /* deprecated */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_SMALL_CAPS = 3, /* deprecated */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_INITIAL_CAPS = 4, /* deprecated */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_INITIAL_CAPS_AND_SMALL_CAPS = 5, /* deprecated */ + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_SUBSTITUTION */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_SUBSTITUTE_VERTICAL_FORMS_ON = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_SUBSTITUTE_VERTICAL_FORMS_OFF = 1, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_LINGUISTIC_REARRANGEMENT */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_LINGUISTIC_REARRANGEMENT_ON = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_LINGUISTIC_REARRANGEMENT_OFF = 1, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_NUMBER_SPACING */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_MONOSPACED_NUMBERS = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_PROPORTIONAL_NUMBERS = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_THIRD_WIDTH_NUMBERS = 2, + HB_AAT_LAYOUT_FEATURE_SELECTOR_QUARTER_WIDTH_NUMBERS = 3, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_SMART_SWASH_TYPE */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_WORD_INITIAL_SWASHES_ON = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_WORD_INITIAL_SWASHES_OFF = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_WORD_FINAL_SWASHES_ON = 2, + HB_AAT_LAYOUT_FEATURE_SELECTOR_WORD_FINAL_SWASHES_OFF = 3, + HB_AAT_LAYOUT_FEATURE_SELECTOR_LINE_INITIAL_SWASHES_ON = 4, + HB_AAT_LAYOUT_FEATURE_SELECTOR_LINE_INITIAL_SWASHES_OFF = 5, + HB_AAT_LAYOUT_FEATURE_SELECTOR_LINE_FINAL_SWASHES_ON = 6, + HB_AAT_LAYOUT_FEATURE_SELECTOR_LINE_FINAL_SWASHES_OFF = 7, + HB_AAT_LAYOUT_FEATURE_SELECTOR_NON_FINAL_SWASHES_ON = 8, + HB_AAT_LAYOUT_FEATURE_SELECTOR_NON_FINAL_SWASHES_OFF = 9, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_DIACRITICS_TYPE */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_SHOW_DIACRITICS = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_HIDE_DIACRITICS = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_DECOMPOSE_DIACRITICS = 2, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_POSITION */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_NORMAL_POSITION = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_SUPERIORS = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_INFERIORS = 2, + HB_AAT_LAYOUT_FEATURE_SELECTOR_ORDINALS = 3, + HB_AAT_LAYOUT_FEATURE_SELECTOR_SCIENTIFIC_INFERIORS = 4, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_FRACTIONS */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_FRACTIONS = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_VERTICAL_FRACTIONS = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_DIAGONAL_FRACTIONS = 2, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_OVERLAPPING_CHARACTERS_TYPE */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_PREVENT_OVERLAP_ON = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_PREVENT_OVERLAP_OFF = 1, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_TYPOGRAPHIC_EXTRAS */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_HYPHENS_TO_EM_DASH_ON = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_HYPHENS_TO_EM_DASH_OFF = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_HYPHEN_TO_EN_DASH_ON = 2, + HB_AAT_LAYOUT_FEATURE_SELECTOR_HYPHEN_TO_EN_DASH_OFF = 3, + HB_AAT_LAYOUT_FEATURE_SELECTOR_SLASHED_ZERO_ON = 4, + HB_AAT_LAYOUT_FEATURE_SELECTOR_SLASHED_ZERO_OFF = 5, + HB_AAT_LAYOUT_FEATURE_SELECTOR_FORM_INTERROBANG_ON = 6, + HB_AAT_LAYOUT_FEATURE_SELECTOR_FORM_INTERROBANG_OFF = 7, + HB_AAT_LAYOUT_FEATURE_SELECTOR_SMART_QUOTES_ON = 8, + HB_AAT_LAYOUT_FEATURE_SELECTOR_SMART_QUOTES_OFF = 9, + HB_AAT_LAYOUT_FEATURE_SELECTOR_PERIODS_TO_ELLIPSIS_ON = 10, + HB_AAT_LAYOUT_FEATURE_SELECTOR_PERIODS_TO_ELLIPSIS_OFF = 11, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_MATHEMATICAL_EXTRAS */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_HYPHEN_TO_MINUS_ON = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_HYPHEN_TO_MINUS_OFF = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_ASTERISK_TO_MULTIPLY_ON = 2, + HB_AAT_LAYOUT_FEATURE_SELECTOR_ASTERISK_TO_MULTIPLY_OFF = 3, + HB_AAT_LAYOUT_FEATURE_SELECTOR_SLASH_TO_DIVIDE_ON = 4, + HB_AAT_LAYOUT_FEATURE_SELECTOR_SLASH_TO_DIVIDE_OFF = 5, + HB_AAT_LAYOUT_FEATURE_SELECTOR_INEQUALITY_LIGATURES_ON = 6, + HB_AAT_LAYOUT_FEATURE_SELECTOR_INEQUALITY_LIGATURES_OFF = 7, + HB_AAT_LAYOUT_FEATURE_SELECTOR_EXPONENTS_ON = 8, + HB_AAT_LAYOUT_FEATURE_SELECTOR_EXPONENTS_OFF = 9, + HB_AAT_LAYOUT_FEATURE_SELECTOR_MATHEMATICAL_GREEK_ON = 10, + HB_AAT_LAYOUT_FEATURE_SELECTOR_MATHEMATICAL_GREEK_OFF = 11, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_ORNAMENT_SETS_TYPE */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_ORNAMENTS = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_DINGBATS = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_PI_CHARACTERS = 2, + HB_AAT_LAYOUT_FEATURE_SELECTOR_FLEURONS = 3, + HB_AAT_LAYOUT_FEATURE_SELECTOR_DECORATIVE_BORDERS = 4, + HB_AAT_LAYOUT_FEATURE_SELECTOR_INTERNATIONAL_SYMBOLS = 5, + HB_AAT_LAYOUT_FEATURE_SELECTOR_MATH_SYMBOLS = 6, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_ALTERNATIVES */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_ALTERNATES = 0, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_DESIGN_COMPLEXITY_TYPE */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_DESIGN_LEVEL1 = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_DESIGN_LEVEL2 = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_DESIGN_LEVEL3 = 2, + HB_AAT_LAYOUT_FEATURE_SELECTOR_DESIGN_LEVEL4 = 3, + HB_AAT_LAYOUT_FEATURE_SELECTOR_DESIGN_LEVEL5 = 4, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLE_OPTIONS */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_STYLE_OPTIONS = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_DISPLAY_TEXT = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_ENGRAVED_TEXT = 2, + HB_AAT_LAYOUT_FEATURE_SELECTOR_ILLUMINATED_CAPS = 3, + HB_AAT_LAYOUT_FEATURE_SELECTOR_TITLING_CAPS = 4, + HB_AAT_LAYOUT_FEATURE_SELECTOR_TALL_CAPS = 5, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_TRADITIONAL_CHARACTERS = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_SIMPLIFIED_CHARACTERS = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_JIS1978_CHARACTERS = 2, + HB_AAT_LAYOUT_FEATURE_SELECTOR_JIS1983_CHARACTERS = 3, + HB_AAT_LAYOUT_FEATURE_SELECTOR_JIS1990_CHARACTERS = 4, + HB_AAT_LAYOUT_FEATURE_SELECTOR_TRADITIONAL_ALT_ONE = 5, + HB_AAT_LAYOUT_FEATURE_SELECTOR_TRADITIONAL_ALT_TWO = 6, + HB_AAT_LAYOUT_FEATURE_SELECTOR_TRADITIONAL_ALT_THREE = 7, + HB_AAT_LAYOUT_FEATURE_SELECTOR_TRADITIONAL_ALT_FOUR = 8, + HB_AAT_LAYOUT_FEATURE_SELECTOR_TRADITIONAL_ALT_FIVE = 9, + HB_AAT_LAYOUT_FEATURE_SELECTOR_EXPERT_CHARACTERS = 10, + HB_AAT_LAYOUT_FEATURE_SELECTOR_JIS2004_CHARACTERS = 11, + HB_AAT_LAYOUT_FEATURE_SELECTOR_HOJO_CHARACTERS = 12, + HB_AAT_LAYOUT_FEATURE_SELECTOR_NLCCHARACTERS = 13, + HB_AAT_LAYOUT_FEATURE_SELECTOR_TRADITIONAL_NAMES_CHARACTERS = 14, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_NUMBER_CASE */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_LOWER_CASE_NUMBERS = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_UPPER_CASE_NUMBERS = 1, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_PROPORTIONAL_TEXT = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_MONOSPACED_TEXT = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_HALF_WIDTH_TEXT = 2, + HB_AAT_LAYOUT_FEATURE_SELECTOR_THIRD_WIDTH_TEXT = 3, + HB_AAT_LAYOUT_FEATURE_SELECTOR_QUARTER_WIDTH_TEXT = 4, + HB_AAT_LAYOUT_FEATURE_SELECTOR_ALT_PROPORTIONAL_TEXT = 5, + HB_AAT_LAYOUT_FEATURE_SELECTOR_ALT_HALF_WIDTH_TEXT = 6, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_TRANSLITERATION */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_TRANSLITERATION = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_HANJA_TO_HANGUL = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_HIRAGANA_TO_KATAKANA = 2, + HB_AAT_LAYOUT_FEATURE_SELECTOR_KATAKANA_TO_HIRAGANA = 3, + HB_AAT_LAYOUT_FEATURE_SELECTOR_KANA_TO_ROMANIZATION = 4, + HB_AAT_LAYOUT_FEATURE_SELECTOR_ROMANIZATION_TO_HIRAGANA = 5, + HB_AAT_LAYOUT_FEATURE_SELECTOR_ROMANIZATION_TO_KATAKANA = 6, + HB_AAT_LAYOUT_FEATURE_SELECTOR_HANJA_TO_HANGUL_ALT_ONE = 7, + HB_AAT_LAYOUT_FEATURE_SELECTOR_HANJA_TO_HANGUL_ALT_TWO = 8, + HB_AAT_LAYOUT_FEATURE_SELECTOR_HANJA_TO_HANGUL_ALT_THREE = 9, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_ANNOTATION_TYPE */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_ANNOTATION = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_BOX_ANNOTATION = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_ROUNDED_BOX_ANNOTATION = 2, + HB_AAT_LAYOUT_FEATURE_SELECTOR_CIRCLE_ANNOTATION = 3, + HB_AAT_LAYOUT_FEATURE_SELECTOR_INVERTED_CIRCLE_ANNOTATION = 4, + HB_AAT_LAYOUT_FEATURE_SELECTOR_PARENTHESIS_ANNOTATION = 5, + HB_AAT_LAYOUT_FEATURE_SELECTOR_PERIOD_ANNOTATION = 6, + HB_AAT_LAYOUT_FEATURE_SELECTOR_ROMAN_NUMERAL_ANNOTATION = 7, + HB_AAT_LAYOUT_FEATURE_SELECTOR_DIAMOND_ANNOTATION = 8, + HB_AAT_LAYOUT_FEATURE_SELECTOR_INVERTED_BOX_ANNOTATION = 9, + HB_AAT_LAYOUT_FEATURE_SELECTOR_INVERTED_ROUNDED_BOX_ANNOTATION= 10, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_KANA_SPACING_TYPE */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_FULL_WIDTH_KANA = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_PROPORTIONAL_KANA = 1, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_IDEOGRAPHIC_SPACING_TYPE */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_FULL_WIDTH_IDEOGRAPHS = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_PROPORTIONAL_IDEOGRAPHS = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_HALF_WIDTH_IDEOGRAPHS = 2, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_UNICODE_DECOMPOSITION_TYPE */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_CANONICAL_COMPOSITION_ON = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_CANONICAL_COMPOSITION_OFF = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_COMPATIBILITY_COMPOSITION_ON = 2, + HB_AAT_LAYOUT_FEATURE_SELECTOR_COMPATIBILITY_COMPOSITION_OFF = 3, + HB_AAT_LAYOUT_FEATURE_SELECTOR_TRANSCODING_COMPOSITION_ON = 4, + HB_AAT_LAYOUT_FEATURE_SELECTOR_TRANSCODING_COMPOSITION_OFF = 5, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_RUBY_KANA */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_RUBY_KANA = 0, /* deprecated - use HB_AAT_LAYOUT_FEATURE_SELECTOR_RUBY_KANA_OFF instead */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_RUBY_KANA = 1, /* deprecated - use HB_AAT_LAYOUT_FEATURE_SELECTOR_RUBY_KANA_ON instead */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_RUBY_KANA_ON = 2, + HB_AAT_LAYOUT_FEATURE_SELECTOR_RUBY_KANA_OFF = 3, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_CJK_SYMBOL_ALTERNATIVES_TYPE */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_CJK_SYMBOL_ALTERNATIVES = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_SYMBOL_ALT_ONE = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_SYMBOL_ALT_TWO = 2, + HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_SYMBOL_ALT_THREE = 3, + HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_SYMBOL_ALT_FOUR = 4, + HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_SYMBOL_ALT_FIVE = 5, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_IDEOGRAPHIC_ALTERNATIVES_TYPE */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_IDEOGRAPHIC_ALTERNATIVES = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_IDEOGRAPHIC_ALT_ONE = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_IDEOGRAPHIC_ALT_TWO = 2, + HB_AAT_LAYOUT_FEATURE_SELECTOR_IDEOGRAPHIC_ALT_THREE = 3, + HB_AAT_LAYOUT_FEATURE_SELECTOR_IDEOGRAPHIC_ALT_FOUR = 4, + HB_AAT_LAYOUT_FEATURE_SELECTOR_IDEOGRAPHIC_ALT_FIVE = 5, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_CJK_VERTICAL_ROMAN_PLACEMENT_TYPE */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_VERTICAL_ROMAN_CENTERED = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_VERTICAL_ROMAN_HBASELINE = 1, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_ITALIC_CJK_ROMAN */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_CJK_ITALIC_ROMAN = 0, /* deprecated - use HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_ITALIC_ROMAN_OFF instead */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_ITALIC_ROMAN = 1, /* deprecated - use HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_ITALIC_ROMAN_ON instead */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_ITALIC_ROMAN_ON = 2, + HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_ITALIC_ROMAN_OFF = 3, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_CASE_SENSITIVE_LAYOUT */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_CASE_SENSITIVE_LAYOUT_ON = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_CASE_SENSITIVE_LAYOUT_OFF = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_CASE_SENSITIVE_SPACING_ON = 2, + HB_AAT_LAYOUT_FEATURE_SELECTOR_CASE_SENSITIVE_SPACING_OFF = 3, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_ALTERNATE_KANA */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_ALTERNATE_HORIZ_KANA_ON = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_ALTERNATE_HORIZ_KANA_OFF = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_ALTERNATE_VERT_KANA_ON = 2, + HB_AAT_LAYOUT_FEATURE_SELECTOR_ALTERNATE_VERT_KANA_OFF = 3, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_STYLISTIC_ALTERNATES = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_ONE_ON = 2, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_ONE_OFF = 3, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWO_ON = 4, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWO_OFF = 5, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_THREE_ON = 6, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_THREE_OFF = 7, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FOUR_ON = 8, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FOUR_OFF = 9, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FIVE_ON = 10, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FIVE_OFF = 11, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SIX_ON = 12, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SIX_OFF = 13, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SEVEN_ON = 14, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SEVEN_OFF = 15, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_EIGHT_ON = 16, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_EIGHT_OFF = 17, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_NINE_ON = 18, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_NINE_OFF = 19, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TEN_ON = 20, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TEN_OFF = 21, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_ELEVEN_ON = 22, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_ELEVEN_OFF = 23, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWELVE_ON = 24, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWELVE_OFF = 25, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_THIRTEEN_ON = 26, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_THIRTEEN_OFF = 27, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FOURTEEN_ON = 28, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FOURTEEN_OFF = 29, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FIFTEEN_ON = 30, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FIFTEEN_OFF = 31, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SIXTEEN_ON = 32, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SIXTEEN_OFF = 33, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SEVENTEEN_ON = 34, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SEVENTEEN_OFF = 35, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_EIGHTEEN_ON = 36, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_EIGHTEEN_OFF = 37, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_NINETEEN_ON = 38, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_NINETEEN_OFF = 39, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWENTY_ON = 40, + HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWENTY_OFF = 41, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_CONTEXTUAL_ALTERNATIVES */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_ALTERNATES_ON = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_ALTERNATES_OFF = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_SWASH_ALTERNATES_ON = 2, + HB_AAT_LAYOUT_FEATURE_SELECTOR_SWASH_ALTERNATES_OFF = 3, + HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_SWASH_ALTERNATES_ON = 4, + HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_SWASH_ALTERNATES_OFF= 5, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_LOWER_CASE */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_DEFAULT_LOWER_CASE = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_LOWER_CASE_SMALL_CAPS = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_LOWER_CASE_PETITE_CAPS = 2, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_UPPER_CASE */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_DEFAULT_UPPER_CASE = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_UPPER_CASE_SMALL_CAPS = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_UPPER_CASE_PETITE_CAPS = 2, + + /* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_CJK_ROMAN_SPACING_TYPE */ + HB_AAT_LAYOUT_FEATURE_SELECTOR_HALF_WIDTH_CJK_ROMAN = 0, + HB_AAT_LAYOUT_FEATURE_SELECTOR_PROPORTIONAL_CJK_ROMAN = 1, + HB_AAT_LAYOUT_FEATURE_SELECTOR_DEFAULT_CJK_ROMAN = 2, + HB_AAT_LAYOUT_FEATURE_SELECTOR_FULL_WIDTH_CJK_ROMAN = 3, + + /*< private >*/ + _HB_AAT_LAYOUT_FEATURE_SELECTOR_MAX_VALUE = HB_TAG_MAX_SIGNED /*< skip >*/ +} hb_aat_layout_feature_selector_t; + +HB_EXTERN unsigned int +hb_aat_layout_get_feature_types (hb_face_t *face, + unsigned int start_offset, + unsigned int *feature_count, /* IN/OUT. May be NULL. */ + hb_aat_layout_feature_type_t *features /* OUT. May be NULL. */); + +HB_EXTERN hb_ot_name_id_t +hb_aat_layout_feature_type_get_name_id (hb_face_t *face, + hb_aat_layout_feature_type_t feature_type); + +/** + * hb_aat_layout_feature_selector_info_t: + * @name_id: The selector's name identifier + * @enable: The value to turn the selector on + * @disable: The value to turn the selector off + * + * Structure representing a setting for an #hb_aat_layout_feature_type_t. + */ +typedef struct hb_aat_layout_feature_selector_info_t { + hb_ot_name_id_t name_id; + hb_aat_layout_feature_selector_t enable; + hb_aat_layout_feature_selector_t disable; + /*< private >*/ + unsigned int reserved; +} hb_aat_layout_feature_selector_info_t; + +/** + * HB_AAT_LAYOUT_NO_SELECTOR_INDEX + * + * Used when getting or setting AAT feature selectors. Indicates that + * there is no selector index corresponding to the selector of interest. + * + */ +#define HB_AAT_LAYOUT_NO_SELECTOR_INDEX 0xFFFFu + +HB_EXTERN unsigned int +hb_aat_layout_feature_type_get_selector_infos (hb_face_t *face, + hb_aat_layout_feature_type_t feature_type, + unsigned int start_offset, + unsigned int *selector_count, /* IN/OUT. May be NULL. */ + hb_aat_layout_feature_selector_info_t *selectors, /* OUT. May be NULL. */ + unsigned int *default_index /* OUT. May be NULL. */); + + +/* + * morx/mort + */ + +HB_EXTERN hb_bool_t +hb_aat_layout_has_substitution (hb_face_t *face); + + +/* + * kerx + */ + +HB_EXTERN hb_bool_t +hb_aat_layout_has_positioning (hb_face_t *face); + + +/* + * trak + */ + +HB_EXTERN hb_bool_t +hb_aat_layout_has_tracking (hb_face_t *face); + + +HB_END_DECLS + +#endif /* HB_AAT_LAYOUT_H */ diff --git a/libs/harfbuzz/src/hb-aat-layout.hh b/libs/harfbuzz/src/hb-aat-layout.hh new file mode 100644 index 00000000..5e4e3bda --- /dev/null +++ b/libs/harfbuzz/src/hb-aat-layout.hh @@ -0,0 +1,75 @@ +/* + * Copyright © 2017 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 HB_AAT_LAYOUT_HH +#define HB_AAT_LAYOUT_HH + +#include "hb.hh" + +#include "hb-ot-shape.hh" +#include "hb-aat-ltag-table.hh" + +struct hb_aat_feature_mapping_t +{ + hb_tag_t otFeatureTag; + hb_aat_layout_feature_type_t aatFeatureType; + hb_aat_layout_feature_selector_t selectorToEnable; + hb_aat_layout_feature_selector_t selectorToDisable; + + int cmp (hb_tag_t key) const + { return key < otFeatureTag ? -1 : key > otFeatureTag ? 1 : 0; } +}; + +HB_INTERNAL const hb_aat_feature_mapping_t * +hb_aat_layout_find_feature_mapping (hb_tag_t tag); + +HB_INTERNAL void +hb_aat_layout_compile_map (const hb_aat_map_builder_t *mapper, + hb_aat_map_t *map); + +HB_INTERNAL void +hb_aat_layout_substitute (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer); + +HB_INTERNAL void +hb_aat_layout_zero_width_deleted_glyphs (hb_buffer_t *buffer); + +HB_INTERNAL void +hb_aat_layout_remove_deleted_glyphs (hb_buffer_t *buffer); + +HB_INTERNAL void +hb_aat_layout_position (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer); + +HB_INTERNAL void +hb_aat_layout_track (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer); + + +#endif /* HB_AAT_LAYOUT_HH */ diff --git a/libs/harfbuzz/src/hb-aat-ltag-table.hh b/libs/harfbuzz/src/hb-aat-ltag-table.hh new file mode 100644 index 00000000..6d771e15 --- /dev/null +++ b/libs/harfbuzz/src/hb-aat-ltag-table.hh @@ -0,0 +1,92 @@ +/* + * Copyright © 2018 Ebrahim Byagowi + * + * 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. + */ + +#ifndef HB_AAT_LTAG_TABLE_HH +#define HB_AAT_LTAG_TABLE_HH + +#include "hb-open-type.hh" + +/* + * ltag -- Language Tag + * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6ltag.html + */ +#define HB_AAT_TAG_ltag HB_TAG('l','t','a','g') + + +namespace AAT { + +using namespace OT; + + +struct FTStringRange +{ + friend struct ltag; + + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && (base+tag).sanitize (c, length)); + } + + protected: + NNOffset16To> + tag; /* Offset from the start of the table to + * the beginning of the string */ + HBUINT16 length; /* String length (in bytes) */ + public: + DEFINE_SIZE_STATIC (4); +}; + +struct ltag +{ + static constexpr hb_tag_t tableTag = HB_AAT_TAG_ltag; + + hb_language_t get_language (unsigned int i) const + { + const FTStringRange &range = tagRanges[i]; + return hb_language_from_string ((const char *) (this+range.tag).arrayZ, + range.length); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + version >= 1 && + tagRanges.sanitize (c, this))); + } + + protected: + HBUINT32 version; /* Table version; currently 1 */ + HBUINT32 flags; /* Table flags; currently none defined */ + Array32Of + tagRanges; /* Range for each tag's string */ + public: + DEFINE_SIZE_ARRAY (12, tagRanges); +}; + +} /* namespace AAT */ + + +#endif /* HB_AAT_LTAG_TABLE_HH */ diff --git a/libs/harfbuzz/src/hb-aat-map.cc b/libs/harfbuzz/src/hb-aat-map.cc new file mode 100644 index 00000000..2c38c350 --- /dev/null +++ b/libs/harfbuzz/src/hb-aat-map.cc @@ -0,0 +1,102 @@ +/* + * Copyright © 2009,2010 Red Hat, Inc. + * Copyright © 2010,2011,2013 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. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#include "hb.hh" + +#ifndef HB_NO_AAT_SHAPE + +#include "hb-aat-map.hh" + +#include "hb-aat-layout.hh" +#include "hb-aat-layout-feat-table.hh" + + +void hb_aat_map_builder_t::add_feature (hb_tag_t tag, unsigned value) +{ + if (!face->table.feat->has_data ()) return; + + if (tag == HB_TAG ('a','a','l','t')) + { + if (!face->table.feat->exposes_feature (HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_ALTERNATIVES)) + return; + feature_info_t *info = features.push(); + info->type = HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_ALTERNATIVES; + info->setting = (hb_aat_layout_feature_selector_t) value; + info->seq = features.length; + info->is_exclusive = true; + return; + } + + const hb_aat_feature_mapping_t *mapping = hb_aat_layout_find_feature_mapping (tag); + if (!mapping) return; + + const AAT::FeatureName* feature = &face->table.feat->get_feature (mapping->aatFeatureType); + if (!feature->has_data ()) + { + /* Special case: Chain::compile_flags will fall back to the deprecated version of + * small-caps if necessary, so we need to check for that possibility. + * https://github.com/harfbuzz/harfbuzz/issues/2307 */ + if (mapping->aatFeatureType == HB_AAT_LAYOUT_FEATURE_TYPE_LOWER_CASE && + mapping->selectorToEnable == HB_AAT_LAYOUT_FEATURE_SELECTOR_LOWER_CASE_SMALL_CAPS) + { + feature = &face->table.feat->get_feature (HB_AAT_LAYOUT_FEATURE_TYPE_LETTER_CASE); + if (!feature->has_data ()) return; + } + else return; + } + + feature_info_t *info = features.push(); + info->type = mapping->aatFeatureType; + info->setting = value ? mapping->selectorToEnable : mapping->selectorToDisable; + info->seq = features.length; + info->is_exclusive = feature->is_exclusive (); +} + +void +hb_aat_map_builder_t::compile (hb_aat_map_t &m) +{ + /* Sort features and merge duplicates */ + if (features.length) + { + features.qsort (); + unsigned int j = 0; + for (unsigned int i = 1; i < features.length; i++) + if (features[i].type != features[j].type || + /* Nonexclusive feature selectors come in even/odd pairs to turn a setting on/off + * respectively, so we mask out the low-order bit when checking for "duplicates" + * (selectors referring to the same feature setting) here. */ + (!features[i].is_exclusive && ((features[i].setting & ~1) != (features[j].setting & ~1)))) + features[++j] = features[i]; + features.shrink (j + 1); + } + + hb_aat_layout_compile_map (this, &m); +} + + +#endif diff --git a/libs/harfbuzz/src/hb-aat-map.hh b/libs/harfbuzz/src/hb-aat-map.hh new file mode 100644 index 00000000..c914f58d --- /dev/null +++ b/libs/harfbuzz/src/hb-aat-map.hh @@ -0,0 +1,98 @@ +/* + * Copyright © 2018 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 HB_AAT_MAP_HH +#define HB_AAT_MAP_HH + +#include "hb.hh" + + +struct hb_aat_map_t +{ + friend struct hb_aat_map_builder_t; + + public: + + void init () + { + memset (this, 0, sizeof (*this)); + chain_flags.init (); + } + void fini () { chain_flags.fini (); } + + public: + hb_vector_t chain_flags; +}; + +struct hb_aat_map_builder_t +{ + public: + + HB_INTERNAL hb_aat_map_builder_t (hb_face_t *face_, + const hb_segment_properties_t props_) : + face (face_), + props (props_) {} + + HB_INTERNAL void add_feature (hb_tag_t tag, unsigned int value=1); + + HB_INTERNAL void compile (hb_aat_map_t &m); + + public: + struct feature_info_t + { + hb_aat_layout_feature_type_t type; + hb_aat_layout_feature_selector_t setting; + bool is_exclusive; + unsigned seq; /* For stable sorting only. */ + + HB_INTERNAL static int cmp (const void *pa, const void *pb) + { + const feature_info_t *a = (const feature_info_t *) pa; + const feature_info_t *b = (const feature_info_t *) pb; + if (a->type != b->type) return (a->type < b->type ? -1 : 1); + if (!a->is_exclusive && + (a->setting & ~1) != (b->setting & ~1)) return (a->setting < b->setting ? -1 : 1); + return (a->seq < b->seq ? -1 : a->seq > b->seq ? 1 : 0); + } + + /* compares type & setting only, not is_exclusive flag or seq number */ + int cmp (const feature_info_t& f) const + { + return (f.type != type) ? (f.type < type ? -1 : 1) : + (f.setting != setting) ? (f.setting < setting ? -1 : 1) : 0; + } + }; + + public: + hb_face_t *face; + hb_segment_properties_t props; + + public: + hb_sorted_vector_t features; +}; + + +#endif /* HB_AAT_MAP_HH */ diff --git a/libs/harfbuzz/src/hb-aat.h b/libs/harfbuzz/src/hb-aat.h new file mode 100644 index 00000000..c14313d1 --- /dev/null +++ b/libs/harfbuzz/src/hb-aat.h @@ -0,0 +1,38 @@ +/* + * Copyright © 2018 Ebrahim Byagowi + * + * 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. + */ + +#ifndef HB_AAT_H +#define HB_AAT_H +#define HB_AAT_H_IN + +#include "hb.h" + +#include "hb-aat-layout.h" + +HB_BEGIN_DECLS + +HB_END_DECLS + +#undef HB_AAT_H_IN +#endif /* HB_AAT_H */ diff --git a/libs/harfbuzz/src/hb-algs.hh b/libs/harfbuzz/src/hb-algs.hh new file mode 100644 index 00000000..cc37c073 --- /dev/null +++ b/libs/harfbuzz/src/hb-algs.hh @@ -0,0 +1,1368 @@ +/* + * Copyright © 2017 Google, Inc. + * Copyright © 2019 Facebook, 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 + * Facebook Author(s): Behdad Esfahbod + */ + +#ifndef HB_ALGS_HH +#define HB_ALGS_HH + +#include "hb.hh" +#include "hb-meta.hh" +#include "hb-null.hh" +#include "hb-number.hh" + +#include +#include +#include +#include + +/* + * Flags + */ + +/* Enable bitwise ops on enums marked as flags_t */ +/* To my surprise, looks like the function resolver is happy to silently cast + * one enum to another... So this doesn't provide the type-checking that I + * originally had in mind... :(. + * + * For MSVC warnings, see: https://github.com/harfbuzz/harfbuzz/pull/163 + */ +#ifdef _MSC_VER +# pragma warning(disable:4200) +# pragma warning(disable:4800) +#endif +#define HB_MARK_AS_FLAG_T(T) \ + extern "C++" { \ + static inline constexpr T operator | (T l, T r) { return T ((unsigned) l | (unsigned) r); } \ + static inline constexpr T operator & (T l, T r) { return T ((unsigned) l & (unsigned) r); } \ + static inline constexpr T operator ^ (T l, T r) { return T ((unsigned) l ^ (unsigned) r); } \ + static inline constexpr unsigned operator ~ (T r) { return (~(unsigned) r); } \ + static inline T& operator |= (T &l, T r) { l = l | r; return l; } \ + static inline T& operator &= (T& l, T r) { l = l & r; return l; } \ + static inline T& operator ^= (T& l, T r) { l = l ^ r; return l; } \ + } \ + static_assert (true, "") + +/* Useful for set-operations on small enums. + * For example, for testing "x ∈ {x1, x2, x3}" use: + * (FLAG_UNSAFE(x) & (FLAG(x1) | FLAG(x2) | FLAG(x3))) + */ +#define FLAG(x) (static_assert_expr ((unsigned)(x) < 32) + (((uint32_t) 1U) << (unsigned)(x))) +#define FLAG_UNSAFE(x) ((unsigned)(x) < 32 ? (((uint32_t) 1U) << (unsigned)(x)) : 0) +#define FLAG_RANGE(x,y) (static_assert_expr ((x) < (y)) + FLAG(y+1) - FLAG(x)) +#define FLAG64(x) (static_assert_expr ((unsigned)(x) < 64) + (((uint64_t) 1ULL) << (unsigned)(x))) +#define FLAG64_UNSAFE(x) ((unsigned)(x) < 64 ? (((uint64_t) 1ULL) << (unsigned)(x)) : 0) + + +/* + * Big-endian integers. + */ + +/* Endian swap, used in Windows related backends */ +static inline constexpr uint16_t hb_uint16_swap (uint16_t v) +{ return (v >> 8) | (v << 8); } +static inline constexpr uint32_t hb_uint32_swap (uint32_t v) +{ return (hb_uint16_swap (v) << 16) | hb_uint16_swap (v >> 16); } + +template +struct BEInt; +template +struct BEInt +{ + public: + BEInt () = default; + constexpr BEInt (Type V) : v {uint8_t (V)} {} + constexpr operator Type () const { return v; } + private: uint8_t v; +}; +template +struct BEInt +{ + public: + BEInt () = default; + constexpr BEInt (Type V) : v {uint8_t ((V >> 8) & 0xFF), + uint8_t ((V ) & 0xFF)} {} + + struct __attribute__((packed)) packed_uint16_t { uint16_t v; }; + constexpr operator Type () const + { +#if defined(__OPTIMIZE__) && !defined(HB_NO_PACKED) && \ + ((defined(__GNUC__) && __GNUC__ >= 5) || defined(__clang__)) && \ + defined(__BYTE_ORDER) && \ + (__BYTE_ORDER == __LITTLE_ENDIAN || __BYTE_ORDER == __BIG_ENDIAN) + /* Spoon-feed the compiler a big-endian integer with alignment 1. + * https://github.com/harfbuzz/harfbuzz/pull/1398 */ +#if __BYTE_ORDER == __LITTLE_ENDIAN + return __builtin_bswap16 (((packed_uint16_t *) v)->v); +#else /* __BYTE_ORDER == __BIG_ENDIAN */ + return ((packed_uint16_t *) v)->v; +#endif +#else + return (v[0] << 8) + + (v[1] ); +#endif + } + private: uint8_t v[2]; +}; +template +struct BEInt +{ + static_assert (!std::is_signed::value, ""); + public: + BEInt () = default; + constexpr BEInt (Type V) : v {uint8_t ((V >> 16) & 0xFF), + uint8_t ((V >> 8) & 0xFF), + uint8_t ((V ) & 0xFF)} {} + + constexpr operator Type () const { return (v[0] << 16) + + (v[1] << 8) + + (v[2] ); } + private: uint8_t v[3]; +}; +template +struct BEInt +{ + public: + BEInt () = default; + constexpr BEInt (Type V) : v {uint8_t ((V >> 24) & 0xFF), + uint8_t ((V >> 16) & 0xFF), + uint8_t ((V >> 8) & 0xFF), + uint8_t ((V ) & 0xFF)} {} + + struct __attribute__((packed)) packed_uint32_t { uint32_t v; }; + constexpr operator Type () const { +#if defined(__OPTIMIZE__) && !defined(HB_NO_PACKED) && \ + ((defined(__GNUC__) && __GNUC__ >= 5) || defined(__clang__)) && \ + defined(__BYTE_ORDER) && \ + (__BYTE_ORDER == __LITTLE_ENDIAN || __BYTE_ORDER == __BIG_ENDIAN) + /* Spoon-feed the compiler a big-endian integer with alignment 1. + * https://github.com/harfbuzz/harfbuzz/pull/1398 */ +#if __BYTE_ORDER == __LITTLE_ENDIAN + return __builtin_bswap32 (((packed_uint32_t *) v)->v); +#else /* __BYTE_ORDER == __BIG_ENDIAN */ + return ((packed_uint32_t *) v)->v; +#endif +#else + return (v[0] << 24) + + (v[1] << 16) + + (v[2] << 8) + + (v[3] ); +#endif + } + private: uint8_t v[4]; +}; + +/* Floats. */ + +/* We want our rounding towards +infinity. */ +static inline float +_hb_roundf (float x) { return floorf (x + .5f); } +#define roundf(x) _hb_roundf(x) + + +/* Encodes three unsigned integers in one 64-bit number. If the inputs have more than 21 bits, + * values will be truncated / overlap, and might not decode exactly. */ +#define HB_CODEPOINT_ENCODE3(x,y,z) (((uint64_t) (x) << 42) | ((uint64_t) (y) << 21) | (uint64_t) (z)) +#define HB_CODEPOINT_DECODE3_1(v) ((hb_codepoint_t) ((v) >> 42)) +#define HB_CODEPOINT_DECODE3_2(v) ((hb_codepoint_t) ((v) >> 21) & 0x1FFFFFu) +#define HB_CODEPOINT_DECODE3_3(v) ((hb_codepoint_t) (v) & 0x1FFFFFu) + +/* Custom encoding used by hb-ucd. */ +#define HB_CODEPOINT_ENCODE3_11_7_14(x,y,z) (((uint32_t) ((x) & 0x07FFu) << 21) | (((uint32_t) (y) & 0x007Fu) << 14) | (uint32_t) ((z) & 0x3FFFu)) +#define HB_CODEPOINT_DECODE3_11_7_14_1(v) ((hb_codepoint_t) ((v) >> 21)) +#define HB_CODEPOINT_DECODE3_11_7_14_2(v) ((hb_codepoint_t) (((v) >> 14) & 0x007Fu) | 0x0300) +#define HB_CODEPOINT_DECODE3_11_7_14_3(v) ((hb_codepoint_t) (v) & 0x3FFFu) + + +struct +{ + /* Note. This is dangerous in that if it's passed an rvalue, it returns rvalue-reference. */ + template constexpr auto + operator () (T&& v) const HB_AUTO_RETURN ( std::forward (v) ) +} +HB_FUNCOBJ (hb_identity); +struct +{ + /* Like identity(), but only retains lvalue-references. Rvalues are returned as rvalues. */ + template constexpr T& + operator () (T& v) const { return v; } + + template constexpr hb_remove_reference + operator () (T&& v) const { return v; } +} +HB_FUNCOBJ (hb_lidentity); +struct +{ + /* Like identity(), but always returns rvalue. */ + template constexpr hb_remove_reference + operator () (T&& v) const { return v; } +} +HB_FUNCOBJ (hb_ridentity); + +struct +{ + template constexpr bool + operator () (T&& v) const { return bool (std::forward (v)); } +} +HB_FUNCOBJ (hb_bool); + +struct +{ + private: + + template constexpr auto + impl (const T& v, hb_priority<1>) const HB_RETURN (uint32_t, hb_deref (v).hash ()) + + template constexpr uint32_t + impl (const hb::shared_ptr& v, hb_priority<1>) const + { + return v.get () ? v.get ()->hash () : 0; + } + template constexpr uint32_t + impl (const hb::unique_ptr& v, hb_priority<1>) const + { + return v.get () ? v.get ()->hash () : 0; + } + + template constexpr auto + impl (const T& v, hb_priority<0>) const HB_RETURN (uint32_t, std::hash>{} (hb_deref (v))) + + public: + + template constexpr auto + operator () (const T& v) const HB_RETURN (uint32_t, impl (v, hb_prioritize)) +} +HB_FUNCOBJ (hb_hash); + + +struct +{ + private: + + /* Pointer-to-member-function. */ + template auto + impl (Appl&& a, hb_priority<2>, T &&v, Ts&&... ds) const HB_AUTO_RETURN + ((hb_deref (std::forward (v)).*std::forward (a)) (std::forward (ds)...)) + + /* Pointer-to-member. */ + template auto + impl (Appl&& a, hb_priority<1>, T &&v) const HB_AUTO_RETURN + ((hb_deref (std::forward (v))).*std::forward (a)) + + /* Operator(). */ + template auto + impl (Appl&& a, hb_priority<0>, Ts&&... ds) const HB_AUTO_RETURN + (hb_deref (std::forward (a)) (std::forward (ds)...)) + + public: + + template auto + operator () (Appl&& a, Ts&&... ds) const HB_AUTO_RETURN + ( + impl (std::forward (a), + hb_prioritize, + std::forward (ds)...) + ) +} +HB_FUNCOBJ (hb_invoke); + +template +struct hb_partial_t +{ + hb_partial_t (Appl a, V v) : a (a), v (v) {} + + static_assert (Pos > 0, ""); + + template auto + operator () (Ts&& ...ds) -> decltype (hb_invoke (hb_declval (Appl), + hb_declval (V), + hb_declval (Ts)...)) + { + return hb_invoke (std::forward (a), + std::forward (v), + std::forward (ds)...); + } + template auto + operator () (T0&& d0, Ts&& ...ds) -> decltype (hb_invoke (hb_declval (Appl), + hb_declval (T0), + hb_declval (V), + hb_declval (Ts)...)) + { + return hb_invoke (std::forward (a), + std::forward (d0), + std::forward (v), + std::forward (ds)...); + } + + private: + hb_reference_wrapper a; + V v; +}; +template +auto hb_partial (Appl&& a, V&& v) HB_AUTO_RETURN +(( hb_partial_t (a, v) )) + +/* The following, HB_PARTIALIZE, macro uses a particular corner-case + * of C++11 that is not particularly well-supported by all compilers. + * What's happening is that it's using "this" in a trailing return-type + * via decltype(). Broken compilers deduce the type of "this" pointer + * in that context differently from what it resolves to in the body + * of the function. + * + * One probable cause of this is that at the time of trailing return + * type declaration, "this" points to an incomplete type, whereas in + * the function body the type is complete. That doesn't justify the + * error in any way, but is probably what's happening. + * + * In the case of MSVC, we get around this by using C++14 "decltype(auto)" + * which deduces the type from the actual return statement. For gcc 4.8 + * we use "+this" instead of "this" which produces an rvalue that seems + * to be deduced as the same type with this particular compiler, and seem + * to be fine as default code path as well. + */ +#ifdef _MSC_VER +/* https://github.com/harfbuzz/harfbuzz/issues/1730 */ \ +#define HB_PARTIALIZE(Pos) \ + template \ + decltype(auto) operator () (_T&& _v) const \ + { return hb_partial (this, std::forward<_T> (_v)); } \ + static_assert (true, "") +#else +/* https://github.com/harfbuzz/harfbuzz/issues/1724 */ +#define HB_PARTIALIZE(Pos) \ + template \ + auto operator () (_T&& _v) const HB_AUTO_RETURN \ + (hb_partial (+this, std::forward<_T> (_v))) \ + static_assert (true, "") +#endif + + +struct +{ + private: + + template auto + impl (Pred&& p, Val &&v, hb_priority<1>) const HB_AUTO_RETURN + ( + hb_deref (std::forward (p)).has (std::forward (v)) + ) + + template auto + impl (Pred&& p, Val &&v, hb_priority<0>) const HB_AUTO_RETURN + ( + hb_invoke (std::forward (p), + std::forward (v)) + ) + + public: + + template auto + operator () (Pred&& p, Val &&v) const HB_RETURN (bool, + impl (std::forward (p), + std::forward (v), + hb_prioritize) + ) +} +HB_FUNCOBJ (hb_has); + +struct +{ + private: + + template auto + impl (Pred&& p, Val &&v, hb_priority<1>) const HB_AUTO_RETURN + ( + hb_has (std::forward (p), + std::forward (v)) + ) + + template auto + impl (Pred&& p, Val &&v, hb_priority<0>) const HB_AUTO_RETURN + ( + std::forward (p) == std::forward (v) + ) + + public: + + template auto + operator () (Pred&& p, Val &&v) const HB_RETURN (bool, + impl (std::forward (p), + std::forward (v), + hb_prioritize) + ) +} +HB_FUNCOBJ (hb_match); + +struct +{ + private: + + template auto + impl (Proj&& f, Val &&v, hb_priority<2>) const HB_AUTO_RETURN + ( + hb_deref (std::forward (f)).get (std::forward (v)) + ) + + template auto + impl (Proj&& f, Val &&v, hb_priority<1>) const HB_AUTO_RETURN + ( + hb_invoke (std::forward (f), + std::forward (v)) + ) + + template auto + impl (Proj&& f, Val &&v, hb_priority<0>) const HB_AUTO_RETURN + ( + std::forward (f)[std::forward (v)] + ) + + public: + + template auto + operator () (Proj&& f, Val &&v) const HB_AUTO_RETURN + ( + impl (std::forward (f), + std::forward (v), + hb_prioritize) + ) +} +HB_FUNCOBJ (hb_get); + +struct +{ + private: + + template auto + impl (T1&& v1, T2 &&v2, hb_priority<3>) const HB_AUTO_RETURN + ( + std::forward (v2).cmp (std::forward (v1)) == 0 + ) + + template auto + impl (T1&& v1, T2 &&v2, hb_priority<2>) const HB_AUTO_RETURN + ( + std::forward (v1).cmp (std::forward (v2)) == 0 + ) + + template auto + impl (T1&& v1, T2 &&v2, hb_priority<1>) const HB_AUTO_RETURN + ( + std::forward (v1) == std::forward (v2) + ) + + template auto + impl (T1&& v1, T2 &&v2, hb_priority<0>) const HB_AUTO_RETURN + ( + std::forward (v2) == std::forward (v1) + ) + + public: + + template auto + operator () (T1&& v1, T2 &&v2) const HB_AUTO_RETURN + ( + impl (std::forward (v1), + std::forward (v2), + hb_prioritize) + ) +} +HB_FUNCOBJ (hb_equal); + + +template +struct hb_pair_t +{ + typedef T1 first_t; + typedef T2 second_t; + typedef hb_pair_t pair_t; + + template ::value && + std::is_default_constructible::value)> + hb_pair_t () : first (), second () {} + hb_pair_t (T1 a, T2 b) : first (a), second (b) {} + + template + operator hb_pair_t () { return hb_pair_t (first, second); } + + hb_pair_t reverse () const + { return hb_pair_t (second, first); } + + bool operator == (const pair_t& o) const { return first == o.first && second == o.second; } + bool operator != (const pair_t& o) const { return !(*this == o); } + bool operator < (const pair_t& o) const { return first < o.first || (first == o.first && second < o.second); } + bool operator >= (const pair_t& o) const { return !(*this < o); } + bool operator > (const pair_t& o) const { return first > o.first || (first == o.first && second > o.second); } + bool operator <= (const pair_t& o) const { return !(*this > o); } + + T1 first; + T2 second; +}; +template static inline hb_pair_t +hb_pair (T1&& a, T2&& b) { return hb_pair_t (a, b); } + +struct +{ + template constexpr typename Pair::first_t + operator () (const Pair& pair) const { return pair.first; } +} +HB_FUNCOBJ (hb_first); + +struct +{ + template constexpr typename Pair::second_t + operator () (const Pair& pair) const { return pair.second; } +} +HB_FUNCOBJ (hb_second); + +/* Note. In min/max impl, we can use hb_type_identity for second argument. + * However, that would silently convert between different-signedness integers. + * Instead we accept two different types, such that compiler can err if + * comparing integers of different signedness. */ +struct +{ + template constexpr auto + operator () (T&& a, T2&& b) const HB_AUTO_RETURN + (a <= b ? a : b) +} +HB_FUNCOBJ (hb_min); +struct +{ + template constexpr auto + operator () (T&& a, T2&& b) const HB_AUTO_RETURN + (a >= b ? a : b) +} +HB_FUNCOBJ (hb_max); +struct +{ + template constexpr auto + operator () (T&& x, T2&& min, T3&& max) const HB_AUTO_RETURN + (hb_min (hb_max (std::forward (x), std::forward (min)), std::forward (max))) +} +HB_FUNCOBJ (hb_clamp); + +struct +{ + template void + operator () (T& a, T& b) const + { + using std::swap; // allow ADL + swap (a, b); + } +} +HB_FUNCOBJ (hb_swap); + +/* + * Bithacks. + */ + +/* Return the number of 1 bits in v. */ +template +static inline unsigned int +hb_popcount (T v) +{ +#if (defined(__GNUC__) && (__GNUC__ >= 4)) || defined(__clang__) + if (sizeof (T) <= sizeof (unsigned int)) + return __builtin_popcount (v); + + if (sizeof (T) <= sizeof (unsigned long)) + return __builtin_popcountl (v); + + if (sizeof (T) <= sizeof (unsigned long long)) + return __builtin_popcountll (v); +#endif + + if (sizeof (T) <= 4) + { + /* "HACKMEM 169" */ + uint32_t y; + y = (v >> 1) &033333333333; + y = v - y - ((y >>1) & 033333333333); + return (((y + (y >> 3)) & 030707070707) % 077); + } + + if (sizeof (T) == 8) + { + unsigned int shift = 32; + return hb_popcount ((uint32_t) v) + hb_popcount ((uint32_t) (v >> shift)); + } + + if (sizeof (T) == 16) + { + unsigned int shift = 64; + return hb_popcount ((uint64_t) v) + hb_popcount ((uint64_t) (v >> shift)); + } + + assert (0); + return 0; /* Shut up stupid compiler. */ +} + +/* Returns the number of bits needed to store number */ +template +static inline unsigned int +hb_bit_storage (T v) +{ + if (unlikely (!v)) return 0; + +#if (defined(__GNUC__) && (__GNUC__ >= 4)) || defined(__clang__) + if (sizeof (T) <= sizeof (unsigned int)) + return sizeof (unsigned int) * 8 - __builtin_clz (v); + + if (sizeof (T) <= sizeof (unsigned long)) + return sizeof (unsigned long) * 8 - __builtin_clzl (v); + + if (sizeof (T) <= sizeof (unsigned long long)) + return sizeof (unsigned long long) * 8 - __builtin_clzll (v); +#endif + +#if (defined(_MSC_VER) && _MSC_VER >= 1500) || (defined(__MINGW32__) && (__GNUC__ < 4)) + if (sizeof (T) <= sizeof (unsigned int)) + { + unsigned long where; + _BitScanReverse (&where, v); + return 1 + where; + } +# if defined(_WIN64) + if (sizeof (T) <= 8) + { + unsigned long where; + _BitScanReverse64 (&where, v); + return 1 + where; + } +# endif +#endif + + if (sizeof (T) <= 4) + { + /* "bithacks" */ + const unsigned int b[] = {0x2, 0xC, 0xF0, 0xFF00, 0xFFFF0000}; + const unsigned int S[] = {1, 2, 4, 8, 16}; + unsigned int r = 0; + for (int i = 4; i >= 0; i--) + if (v & b[i]) + { + v >>= S[i]; + r |= S[i]; + } + return r + 1; + } + if (sizeof (T) <= 8) + { + /* "bithacks" */ + const uint64_t b[] = {0x2ULL, 0xCULL, 0xF0ULL, 0xFF00ULL, 0xFFFF0000ULL, 0xFFFFFFFF00000000ULL}; + const unsigned int S[] = {1, 2, 4, 8, 16, 32}; + unsigned int r = 0; + for (int i = 5; i >= 0; i--) + if (v & b[i]) + { + v >>= S[i]; + r |= S[i]; + } + return r + 1; + } + if (sizeof (T) == 16) + { + unsigned int shift = 64; + return (v >> shift) ? hb_bit_storage ((uint64_t) (v >> shift)) + shift : + hb_bit_storage ((uint64_t) v); + } + + assert (0); + return 0; /* Shut up stupid compiler. */ +} + +/* Returns the number of zero bits in the least significant side of v */ +template +static inline unsigned int +hb_ctz (T v) +{ + if (unlikely (!v)) return 8 * sizeof (T); + +#if (defined(__GNUC__) && (__GNUC__ >= 4)) || defined(__clang__) + if (sizeof (T) <= sizeof (unsigned int)) + return __builtin_ctz (v); + + if (sizeof (T) <= sizeof (unsigned long)) + return __builtin_ctzl (v); + + if (sizeof (T) <= sizeof (unsigned long long)) + return __builtin_ctzll (v); +#endif + +#if (defined(_MSC_VER) && _MSC_VER >= 1500) || (defined(__MINGW32__) && (__GNUC__ < 4)) + if (sizeof (T) <= sizeof (unsigned int)) + { + unsigned long where; + _BitScanForward (&where, v); + return where; + } +# if defined(_WIN64) + if (sizeof (T) <= 8) + { + unsigned long where; + _BitScanForward64 (&where, v); + return where; + } +# endif +#endif + + if (sizeof (T) <= 4) + { + /* "bithacks" */ + unsigned int c = 32; + v &= - (int32_t) v; + if (v) c--; + if (v & 0x0000FFFF) c -= 16; + if (v & 0x00FF00FF) c -= 8; + if (v & 0x0F0F0F0F) c -= 4; + if (v & 0x33333333) c -= 2; + if (v & 0x55555555) c -= 1; + return c; + } + if (sizeof (T) <= 8) + { + /* "bithacks" */ + unsigned int c = 64; + v &= - (int64_t) (v); + if (v) c--; + if (v & 0x00000000FFFFFFFFULL) c -= 32; + if (v & 0x0000FFFF0000FFFFULL) c -= 16; + if (v & 0x00FF00FF00FF00FFULL) c -= 8; + if (v & 0x0F0F0F0F0F0F0F0FULL) c -= 4; + if (v & 0x3333333333333333ULL) c -= 2; + if (v & 0x5555555555555555ULL) c -= 1; + return c; + } + if (sizeof (T) == 16) + { + unsigned int shift = 64; + return (uint64_t) v ? hb_bit_storage ((uint64_t) v) : + hb_bit_storage ((uint64_t) (v >> shift)) + shift; + } + + assert (0); + return 0; /* Shut up stupid compiler. */ +} + + +/* + * Tiny stuff. + */ + +/* ASCII tag/character handling */ +static inline bool ISALPHA (unsigned char c) +{ return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); } +static inline bool ISALNUM (unsigned char c) +{ return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9'); } +static inline bool ISSPACE (unsigned char c) +{ return c == ' ' || c =='\f'|| c =='\n'|| c =='\r'|| c =='\t'|| c =='\v'; } +static inline unsigned char TOUPPER (unsigned char c) +{ return (c >= 'a' && c <= 'z') ? c - 'a' + 'A' : c; } +static inline unsigned char TOLOWER (unsigned char c) +{ return (c >= 'A' && c <= 'Z') ? c - 'A' + 'a' : c; } +static inline bool ISHEX (unsigned char c) +{ return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'); } +static inline unsigned char TOHEX (uint8_t c) +{ return (c & 0xF) <= 9 ? (c & 0xF) + '0' : (c & 0xF) + 'a' - 10; } +static inline uint8_t FROMHEX (unsigned char c) +{ return (c >= '0' && c <= '9') ? c - '0' : TOLOWER (c) - 'a' + 10; } + +static inline unsigned int DIV_CEIL (const unsigned int a, unsigned int b) +{ return (a + (b - 1)) / b; } + + +#undef ARRAY_LENGTH +template +static inline unsigned int ARRAY_LENGTH (const Type (&)[n]) { return n; } +/* A const version, but does not detect erratically being called on pointers. */ +#define ARRAY_LENGTH_CONST(__array) ((signed int) (sizeof (__array) / sizeof (__array[0]))) + + +static inline void * +hb_memcpy (void *__restrict dst, const void *__restrict src, size_t len) +{ + /* It's illegal to pass 0 as size to memcpy. */ + if (unlikely (!len)) return dst; + return memcpy (dst, src, len); +} + +static inline int +hb_memcmp (const void *a, const void *b, unsigned int len) +{ + /* It's illegal to pass NULL to memcmp(), even if len is zero. + * So, wrap it. + * https://sourceware.org/bugzilla/show_bug.cgi?id=23878 */ + if (unlikely (!len)) return 0; + return memcmp (a, b, len); +} + +static inline void * +hb_memset (void *s, int c, unsigned int n) +{ + /* It's illegal to pass NULL to memset(), even if n is zero. */ + if (unlikely (!n)) return 0; + return memset (s, c, n); +} + +static inline unsigned int +hb_ceil_to_4 (unsigned int v) +{ + return ((v - 1) | 3) + 1; +} + +template static inline bool +hb_in_range (T u, T lo, T hi) +{ + static_assert (!std::is_signed::value, ""); + + /* The casts below are important as if T is smaller than int, + * the subtract results will become a signed int! */ + return (T)(u - lo) <= (T)(hi - lo); +} +template static inline bool +hb_in_ranges (T u, T lo1, T hi1, T lo2, T hi2) +{ + return hb_in_range (u, lo1, hi1) || hb_in_range (u, lo2, hi2); +} +template static inline bool +hb_in_ranges (T u, T lo1, T hi1, T lo2, T hi2, T lo3, T hi3) +{ + return hb_in_range (u, lo1, hi1) || hb_in_range (u, lo2, hi2) || hb_in_range (u, lo3, hi3); +} +template static inline bool +hb_in_ranges (T u, T lo1, T hi1, T lo2, T hi2, T lo3, T hi3, T lo4, T hi4) +{ + return hb_in_range (u, lo1, hi1) || hb_in_range (u, lo2, hi2) || hb_in_range (u, lo3, hi3) || hb_in_range (u, lo4, hi4); +} + + +/* + * Overflow checking. + */ + +/* Consider __builtin_mul_overflow use here also */ +static inline bool +hb_unsigned_mul_overflows (unsigned int count, unsigned int size) +{ + return (size > 0) && (count >= ((unsigned int) -1) / size); +} + + +/* + * Sort and search. + */ + +template +static int +_hb_cmp_method (const void *pkey, const void *pval, Ts... ds) +{ + const K& key = * (const K*) pkey; + const V& val = * (const V*) pval; + + return val.cmp (key, ds...); +} + +template +static inline bool +hb_bsearch_impl (unsigned *pos, /* Out */ + const K& key, + V* base, size_t nmemb, size_t stride, + int (*compar)(const void *_key, const void *_item, Ts... _ds), + Ts... ds) +{ + /* This is our *only* bsearch implementation. */ + + int min = 0, max = (int) nmemb - 1; + while (min <= max) + { + int mid = ((unsigned int) min + (unsigned int) max) / 2; +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-align" + V* p = (V*) (((const char *) base) + (mid * stride)); +#pragma GCC diagnostic pop + int c = compar ((const void *) std::addressof (key), (const void *) p, ds...); + if (c < 0) + max = mid - 1; + else if (c > 0) + min = mid + 1; + else + { + *pos = mid; + return true; + } + } + *pos = min; + return false; +} + +template +static inline V* +hb_bsearch (const K& key, V* base, + size_t nmemb, size_t stride = sizeof (V), + int (*compar)(const void *_key, const void *_item) = _hb_cmp_method) +{ + unsigned pos; +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-align" + return hb_bsearch_impl (&pos, key, base, nmemb, stride, compar) ? + (V*) (((const char *) base) + (pos * stride)) : nullptr; +#pragma GCC diagnostic pop +} +template +static inline V* +hb_bsearch (const K& key, V* base, + size_t nmemb, size_t stride, + int (*compar)(const void *_key, const void *_item, Ts... _ds), + Ts... ds) +{ + unsigned pos; +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-align" + return hb_bsearch_impl (&pos, key, base, nmemb, stride, compar, ds...) ? + (V*) (((const char *) base) + (pos * stride)) : nullptr; +#pragma GCC diagnostic pop +} + + +/* From https://github.com/noporpoise/sort_r + Feb 5, 2019 (c8c65c1e) + Modified to support optional argument using templates */ + +/* Isaac Turner 29 April 2014 Public Domain */ + +/* +hb_qsort function to be exported. +Parameters: + base is the array to be sorted + nel is the number of elements in the array + width is the size in bytes of each element of the array + compar is the comparison function + arg (optional) is a pointer to be passed to the comparison function + +void hb_qsort(void *base, size_t nel, size_t width, + int (*compar)(const void *_a, const void *_b, [void *_arg]), + [void *arg]); +*/ + +#define SORT_R_SWAP(a,b,tmp) ((void) ((tmp) = (a)), (void) ((a) = (b)), (b) = (tmp)) + +/* swap a and b */ +/* a and b must not be equal! */ +static inline void sort_r_swap(char *__restrict a, char *__restrict b, + size_t w) +{ + char tmp, *end = a+w; + for(; a < end; a++, b++) { SORT_R_SWAP(*a, *b, tmp); } +} + +/* swap a, b iff a>b */ +/* a and b must not be equal! */ +/* __restrict is same as restrict but better support on old machines */ +template +static inline int sort_r_cmpswap(char *__restrict a, + char *__restrict b, size_t w, + int (*compar)(const void *_a, + const void *_b, + Ts... _ds), + Ts... ds) +{ + if(compar(a, b, ds...) > 0) { + sort_r_swap(a, b, w); + return 1; + } + return 0; +} + +/* +Swap consecutive blocks of bytes of size na and nb starting at memory addr ptr, +with the smallest swap so that the blocks are in the opposite order. Blocks may +be internally re-ordered e.g. + 12345ab -> ab34512 + 123abc -> abc123 + 12abcde -> deabc12 +*/ +static inline void sort_r_swap_blocks(char *ptr, size_t na, size_t nb) +{ + if(na > 0 && nb > 0) { + if(na > nb) { sort_r_swap(ptr, ptr+na, nb); } + else { sort_r_swap(ptr, ptr+nb, na); } + } +} + +/* Implement recursive quicksort ourselves */ +/* Note: quicksort is not stable, equivalent values may be swapped */ +template +static inline void sort_r_simple(void *base, size_t nel, size_t w, + int (*compar)(const void *_a, + const void *_b, + Ts... _ds), + Ts... ds) +{ + char *b = (char *)base, *end = b + nel*w; + + /* for(size_t i=0; i b && sort_r_cmpswap(pj-w,pj,w,compar,ds...); pj -= w) {} + } + } + else + { + /* nel > 9; Quicksort */ + + int cmp; + char *pl, *ple, *pr, *pre, *pivot; + char *last = b+w*(nel-1), *tmp; + + /* + Use median of second, middle and second-last items as pivot. + First and last may have been swapped with pivot and therefore be extreme + */ + char *l[3]; + l[0] = b + w; + l[1] = b+w*(nel/2); + l[2] = last - w; + + /* printf("pivots: %i, %i, %i\n", *(int*)l[0], *(int*)l[1], *(int*)l[2]); */ + + if(compar(l[0],l[1],ds...) > 0) { SORT_R_SWAP(l[0], l[1], tmp); } + if(compar(l[1],l[2],ds...) > 0) { + SORT_R_SWAP(l[1], l[2], tmp); + if(compar(l[0],l[1],ds...) > 0) { SORT_R_SWAP(l[0], l[1], tmp); } + } + + /* swap mid value (l[1]), and last element to put pivot as last element */ + if(l[1] != last) { sort_r_swap(l[1], last, w); } + + /* + pl is the next item on the left to be compared to the pivot + pr is the last item on the right that was compared to the pivot + ple is the left position to put the next item that equals the pivot + ple is the last right position where we put an item that equals the pivot + v- end (beyond the array) + EEEEEELLLLLLLLuuuuuuuuGGGGGGGEEEEEEEE. + ^- b ^- ple ^- pl ^- pr ^- pre ^- last (where the pivot is) + Pivot comparison key: + E = equal, L = less than, u = unknown, G = greater than, E = equal + */ + pivot = last; + ple = pl = b; + pre = pr = last; + + /* + Strategy: + Loop into the list from the left and right at the same time to find: + - an item on the left that is greater than the pivot + - an item on the right that is less than the pivot + Once found, they are swapped and the loop continues. + Meanwhile items that are equal to the pivot are moved to the edges of the + array. + */ + while(pl < pr) { + /* Move left hand items which are equal to the pivot to the far left. + break when we find an item that is greater than the pivot */ + for(; pl < pr; pl += w) { + cmp = compar(pl, pivot, ds...); + if(cmp > 0) { break; } + else if(cmp == 0) { + if(ple < pl) { sort_r_swap(ple, pl, w); } + ple += w; + } + } + /* break if last batch of left hand items were equal to pivot */ + if(pl >= pr) { break; } + /* Move right hand items which are equal to the pivot to the far right. + break when we find an item that is less than the pivot */ + for(; pl < pr; ) { + pr -= w; /* Move right pointer onto an unprocessed item */ + cmp = compar(pr, pivot, ds...); + if(cmp == 0) { + pre -= w; + if(pr < pre) { sort_r_swap(pr, pre, w); } + } + else if(cmp < 0) { + if(pl < pr) { sort_r_swap(pl, pr, w); } + pl += w; + break; + } + } + } + + pl = pr; /* pr may have gone below pl */ + + /* + Now we need to go from: EEELLLGGGGEEEE + to: LLLEEEEEEEGGGG + Pivot comparison key: + E = equal, L = less than, u = unknown, G = greater than, E = equal + */ + sort_r_swap_blocks(b, ple-b, pl-ple); + sort_r_swap_blocks(pr, pre-pr, end-pre); + + /*for(size_t i=0; i static inline void +hb_stable_sort (T *array, unsigned int len, int(*compar)(const T2 *, const T2 *), T3 *array2) +{ + for (unsigned int i = 1; i < len; i++) + { + unsigned int j = i; + while (j && compar (&array[j - 1], &array[i]) > 0) + j--; + if (i == j) + continue; + /* Move item i to occupy place for item j, shift what's in between. */ + { + T t = array[i]; + memmove (&array[j + 1], &array[j], (i - j) * sizeof (T)); + array[j] = t; + } + if (array2) + { + T3 t = array2[i]; + memmove (&array2[j + 1], &array2[j], (i - j) * sizeof (T3)); + array2[j] = t; + } + } +} + +template static inline void +hb_stable_sort (T *array, unsigned int len, int(*compar)(const T *, const T *)) +{ + hb_stable_sort (array, len, compar, (int *) nullptr); +} + +static inline hb_bool_t +hb_codepoint_parse (const char *s, unsigned int len, int base, hb_codepoint_t *out) +{ + unsigned int v; + const char *p = s; + const char *end = p + len; + if (unlikely (!hb_parse_uint (&p, end, &v, true/* whole buffer */, base))) + return false; + + *out = v; + return true; +} + + +/* Operators. */ + +struct +{ HB_PARTIALIZE(2); + template constexpr auto + operator () (const T &a, const T &b) const HB_AUTO_RETURN (a & b) +} +HB_FUNCOBJ (hb_bitwise_and); +struct +{ HB_PARTIALIZE(2); + template constexpr auto + operator () (const T &a, const T &b) const HB_AUTO_RETURN (a | b) +} +HB_FUNCOBJ (hb_bitwise_or); +struct +{ HB_PARTIALIZE(2); + template constexpr auto + operator () (const T &a, const T &b) const HB_AUTO_RETURN (a ^ b) +} +HB_FUNCOBJ (hb_bitwise_xor); +struct +{ HB_PARTIALIZE(2); + template constexpr auto + operator () (const T &a, const T &b) const HB_AUTO_RETURN (~a & b) +} +HB_FUNCOBJ (hb_bitwise_lt); +struct +{ HB_PARTIALIZE(2); + template constexpr auto + operator () (const T &a, const T &b) const HB_AUTO_RETURN (a & ~b) +} +HB_FUNCOBJ (hb_bitwise_gt); // aka sub +struct +{ HB_PARTIALIZE(2); + template constexpr auto + operator () (const T &a, const T &b) const HB_AUTO_RETURN (~a | b) +} +HB_FUNCOBJ (hb_bitwise_le); +struct +{ HB_PARTIALIZE(2); + template constexpr auto + operator () (const T &a, const T &b) const HB_AUTO_RETURN (a | ~b) +} +HB_FUNCOBJ (hb_bitwise_ge); +struct +{ + template constexpr auto + operator () (const T &a) const HB_AUTO_RETURN (~a) +} +HB_FUNCOBJ (hb_bitwise_neg); + +struct +{ HB_PARTIALIZE(2); + template constexpr auto + operator () (const T &a, const T2 &b) const HB_AUTO_RETURN (a + b) +} +HB_FUNCOBJ (hb_add); +struct +{ HB_PARTIALIZE(2); + template constexpr auto + operator () (const T &a, const T2 &b) const HB_AUTO_RETURN (a - b) +} +HB_FUNCOBJ (hb_sub); +struct +{ HB_PARTIALIZE(2); + template constexpr auto + operator () (const T &a, const T2 &b) const HB_AUTO_RETURN (b - a) +} +HB_FUNCOBJ (hb_rsub); +struct +{ HB_PARTIALIZE(2); + template constexpr auto + operator () (const T &a, const T2 &b) const HB_AUTO_RETURN (a * b) +} +HB_FUNCOBJ (hb_mul); +struct +{ HB_PARTIALIZE(2); + template constexpr auto + operator () (const T &a, const T2 &b) const HB_AUTO_RETURN (a / b) +} +HB_FUNCOBJ (hb_div); +struct +{ HB_PARTIALIZE(2); + template constexpr auto + operator () (const T &a, const T2 &b) const HB_AUTO_RETURN (a % b) +} +HB_FUNCOBJ (hb_mod); +struct +{ + template constexpr auto + operator () (const T &a) const HB_AUTO_RETURN (+a) +} +HB_FUNCOBJ (hb_pos); +struct +{ + template constexpr auto + operator () (const T &a) const HB_AUTO_RETURN (-a) +} +HB_FUNCOBJ (hb_neg); +struct +{ + template constexpr auto + operator () (T &a) const HB_AUTO_RETURN (++a) +} +HB_FUNCOBJ (hb_inc); +struct +{ + template constexpr auto + operator () (T &a) const HB_AUTO_RETURN (--a) +} +HB_FUNCOBJ (hb_dec); + + +/* Compiler-assisted vectorization. */ + +/* Type behaving similar to vectorized vars defined using __attribute__((vector_size(...))), + * basically a fixed-size bitset. */ +template +struct hb_vector_size_t +{ + elt_t& operator [] (unsigned int i) { return v[i]; } + const elt_t& operator [] (unsigned int i) const { return v[i]; } + + void clear (unsigned char v = 0) { memset (this, v, sizeof (*this)); } + + template + hb_vector_size_t process (const Op& op) const + { + hb_vector_size_t r; + for (unsigned int i = 0; i < ARRAY_LENGTH (v); i++) + r.v[i] = op (v[i]); + return r; + } + template + hb_vector_size_t process (const Op& op, const hb_vector_size_t &o) const + { + hb_vector_size_t r; + for (unsigned int i = 0; i < ARRAY_LENGTH (v); i++) + r.v[i] = op (v[i], o.v[i]); + return r; + } + hb_vector_size_t operator | (const hb_vector_size_t &o) const + { return process (hb_bitwise_or, o); } + hb_vector_size_t operator & (const hb_vector_size_t &o) const + { return process (hb_bitwise_and, o); } + hb_vector_size_t operator ^ (const hb_vector_size_t &o) const + { return process (hb_bitwise_xor, o); } + hb_vector_size_t operator ~ () const + { return process (hb_bitwise_neg); } + + private: + static_assert (0 == byte_size % sizeof (elt_t), ""); + elt_t v[byte_size / sizeof (elt_t)]; +}; + + +#endif /* HB_ALGS_HH */ diff --git a/libs/harfbuzz/src/hb-array.hh b/libs/harfbuzz/src/hb-array.hh new file mode 100644 index 00000000..5884007c --- /dev/null +++ b/libs/harfbuzz/src/hb-array.hh @@ -0,0 +1,445 @@ +/* + * Copyright © 2018 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 HB_ARRAY_HH +#define HB_ARRAY_HH + +#include "hb.hh" +#include "hb-algs.hh" +#include "hb-iter.hh" +#include "hb-null.hh" + + +template +struct hb_sorted_array_t; + +enum hb_not_found_t +{ + HB_NOT_FOUND_DONT_STORE, + HB_NOT_FOUND_STORE, + HB_NOT_FOUND_STORE_CLOSEST, +}; + + +template +struct hb_array_t : hb_iter_with_fallback_t, Type&> +{ + /* + * Constructors. + */ + hb_array_t () = default; + hb_array_t (const hb_array_t&) = default; + ~hb_array_t () = default; + hb_array_t& operator= (const hb_array_t&) = default; + hb_array_t& operator= (hb_array_t&&) = default; + + constexpr hb_array_t (Type *array_, unsigned int length_) : arrayZ (array_), length (length_) {} + template + constexpr hb_array_t (Type (&array_)[length_]) : hb_array_t (array_, length_) {} + + template + constexpr hb_array_t (const hb_array_t &o) : + hb_iter_with_fallback_t (), + arrayZ (o.arrayZ), length (o.length), backwards_length (o.backwards_length) {} + template + hb_array_t& operator = (const hb_array_t &o) + { arrayZ = o.arrayZ; length = o.length; backwards_length = o.backwards_length; return *this; } + + /* + * Iterator implementation. + */ + typedef Type& __item_t__; + static constexpr bool is_random_access_iterator = true; + Type& __item_at__ (unsigned i) const + { + if (unlikely (i >= length)) return CrapOrNull (Type); + return arrayZ[i]; + } + void __forward__ (unsigned n) + { + if (unlikely (n > length)) + n = length; + length -= n; + backwards_length += n; + arrayZ += n; + } + void __rewind__ (unsigned n) + { + if (unlikely (n > backwards_length)) + n = backwards_length; + length += n; + backwards_length -= n; + arrayZ -= n; + } + unsigned __len__ () const { return length; } + /* Ouch. The operator== compares the contents of the array. For range-based for loops, + * it's best if we can just compare arrayZ, though comparing contents is still fast, + * but also would require that Type has operator==. As such, we optimize this operator + * for range-based for loop and just compare arrayZ and length. */ + bool operator != (const hb_array_t& o) const + { return this->arrayZ != o.arrayZ || this->length != o.length; } + + /* Extra operators. + */ + Type * operator & () const { return arrayZ; } + operator hb_array_t () { return hb_array_t (arrayZ, length); } + template operator T * () const { return arrayZ; } + + HB_INTERNAL bool operator == (const hb_array_t &o) const; + + uint32_t hash () const { + uint32_t current = 0; + for (unsigned int i = 0; i < this->length; i++) { + current = current * 31 + hb_hash (this->arrayZ[i]); + } + return current; + } + + /* + * Compare, Sort, and Search. + */ + + /* Note: our compare is NOT lexicographic; it also does NOT call Type::cmp. */ + int cmp (const hb_array_t &a) const + { + if (length != a.length) + return (int) a.length - (int) length; + return hb_memcmp (a.arrayZ, arrayZ, get_size ()); + } + HB_INTERNAL static int cmp (const void *pa, const void *pb) + { + hb_array_t *a = (hb_array_t *) pa; + hb_array_t *b = (hb_array_t *) pb; + return b->cmp (*a); + } + + template + Type *lsearch (const T &x, Type *not_found = nullptr) + { + unsigned i; + return lfind (x, &i) ? &this->arrayZ[i] : not_found; + } + template + const Type *lsearch (const T &x, const Type *not_found = nullptr) const + { + unsigned i; + return lfind (x, &i) ? &this->arrayZ[i] : not_found; + } + template + bool lfind (const T &x, unsigned *pos = nullptr, + hb_not_found_t not_found = HB_NOT_FOUND_DONT_STORE, + unsigned int to_store = (unsigned int) -1) const + { + for (unsigned i = 0; i < length; ++i) + if (hb_equal (x, this->arrayZ[i])) + { + if (pos) + *pos = i; + return true; + } + + if (pos) + { + switch (not_found) + { + case HB_NOT_FOUND_DONT_STORE: + break; + + case HB_NOT_FOUND_STORE: + *pos = to_store; + break; + + case HB_NOT_FOUND_STORE_CLOSEST: + *pos = length; + break; + } + } + return false; + } + + hb_sorted_array_t qsort (int (*cmp_)(const void*, const void*)) + { + if (likely (length)) + hb_qsort (arrayZ, length, this->get_item_size (), cmp_); + return hb_sorted_array_t (*this); + } + hb_sorted_array_t qsort () + { + if (likely (length)) + hb_qsort (arrayZ, length, this->get_item_size (), Type::cmp); + return hb_sorted_array_t (*this); + } + void qsort (unsigned int start, unsigned int end) + { + end = hb_min (end, length); + assert (start <= end); + if (likely (start < end)) + hb_qsort (arrayZ + start, end - start, this->get_item_size (), Type::cmp); + } + + /* + * Other methods. + */ + + unsigned int get_size () const { return length * this->get_item_size (); } + + /* + * Reverse the order of items in this array in the range [start, end). + */ + void reverse (unsigned start = 0, unsigned end = -1) + { + start = hb_min (start, length); + end = hb_min (end, length); + + if (end < start + 2) + return; + + for (unsigned lhs = start, rhs = end - 1; lhs < rhs; lhs++, rhs--) + hb_swap (arrayZ[rhs], arrayZ[lhs]); + } + + hb_array_t sub_array (unsigned int start_offset = 0, unsigned int *seg_count = nullptr /* IN/OUT */) const + { + if (!start_offset && !seg_count) + return *this; + + unsigned int count = length; + if (unlikely (start_offset > count)) + count = 0; + else + count -= start_offset; + if (seg_count) + count = *seg_count = hb_min (count, *seg_count); + return hb_array_t (arrayZ + start_offset, count); + } + hb_array_t sub_array (unsigned int start_offset, unsigned int seg_count) const + { return sub_array (start_offset, &seg_count); } + + hb_array_t truncate (unsigned length) const { return sub_array (0, length); } + + template + const T *as () const + { return length < hb_min_size (T) ? &Null (T) : reinterpret_cast (arrayZ); } + + template + bool check_range (const T *p, unsigned int size = T::static_size) const + { + return arrayZ <= ((const char *) p) + && ((const char *) p) <= arrayZ + length + && (unsigned int) (arrayZ + length - (const char *) p) >= size; + } + + /* Only call if you allocated the underlying array using hb_malloc() or similar. */ + void fini () + { hb_free ((void *) arrayZ); arrayZ = nullptr; length = 0; } + + template + hb_array_t copy (hb_serialize_context_t *c) const + { + TRACE_SERIALIZE (this); + auto* out = c->start_embed (arrayZ); + if (unlikely (!c->extend_size (out, get_size ()))) return_trace (hb_array_t ()); + for (unsigned i = 0; i < length; i++) + out[i] = arrayZ[i]; /* TODO: add version that calls c->copy() */ + return_trace (hb_array_t (out, length)); + } + + template + bool sanitize (hb_sanitize_context_t *c) const + { return c->check_array (arrayZ, length); } + + /* + * Members + */ + + public: + Type *arrayZ = nullptr; + unsigned int length = 0; + unsigned int backwards_length = 0; +}; +template inline hb_array_t +hb_array (T *array, unsigned int length) +{ return hb_array_t (array, length); } +template inline hb_array_t +hb_array (T (&array_)[length_]) +{ return hb_array_t (array_); } + +template +struct hb_sorted_array_t : + hb_iter_t, Type&>, + hb_array_t +{ + typedef hb_iter_t iter_base_t; + HB_ITER_USING (iter_base_t); + static constexpr bool is_random_access_iterator = true; + static constexpr bool is_sorted_iterator = true; + + hb_sorted_array_t () = default; + hb_sorted_array_t (const hb_sorted_array_t&) = default; + ~hb_sorted_array_t () = default; + hb_sorted_array_t& operator= (const hb_sorted_array_t&) = default; + hb_sorted_array_t& operator= (hb_sorted_array_t&&) = default; + + constexpr hb_sorted_array_t (Type *array_, unsigned int length_) : hb_array_t (array_, length_) {} + template + constexpr hb_sorted_array_t (Type (&array_)[length_]) : hb_array_t (array_) {} + + template + constexpr hb_sorted_array_t (const hb_array_t &o) : + hb_iter_t (), + hb_array_t (o) {} + template + hb_sorted_array_t& operator = (const hb_array_t &o) + { hb_array_t (*this) = o; return *this; } + + /* Iterator implementation. */ + + /* See comment in hb_array_of::operator != */ + bool operator != (const hb_sorted_array_t& o) const + { return this->arrayZ != o.arrayZ || this->length != o.length; } + + hb_sorted_array_t sub_array (unsigned int start_offset, unsigned int *seg_count /* IN/OUT */) const + { return hb_sorted_array_t (((const hb_array_t *) (this))->sub_array (start_offset, seg_count)); } + hb_sorted_array_t sub_array (unsigned int start_offset, unsigned int seg_count) const + { return sub_array (start_offset, &seg_count); } + + hb_sorted_array_t truncate (unsigned length) const { return sub_array (0, length); } + + template + Type *bsearch (const T &x, Type *not_found = nullptr) + { + unsigned int i; + return bfind (x, &i) ? &this->arrayZ[i] : not_found; + } + template + const Type *bsearch (const T &x, const Type *not_found = nullptr) const + { + unsigned int i; + return bfind (x, &i) ? &this->arrayZ[i] : not_found; + } + template + bool bfind (const T &x, unsigned int *i = nullptr, + hb_not_found_t not_found = HB_NOT_FOUND_DONT_STORE, + unsigned int to_store = (unsigned int) -1) const + { + unsigned pos; + + if (bsearch_impl (x, &pos)) + { + if (i) + *i = pos; + return true; + } + + if (i) + { + switch (not_found) + { + case HB_NOT_FOUND_DONT_STORE: + break; + + case HB_NOT_FOUND_STORE: + *i = to_store; + break; + + case HB_NOT_FOUND_STORE_CLOSEST: + *i = pos; + break; + } + } + return false; + } + template + bool bsearch_impl (const T &x, unsigned *pos, Ts... ds) const + { + return hb_bsearch_impl (pos, + x, + this->arrayZ, + this->length, + sizeof (Type), + _hb_cmp_method, + std::forward (ds)...); + } +}; +template inline hb_sorted_array_t +hb_sorted_array (T *array, unsigned int length) +{ return hb_sorted_array_t (array, length); } +template inline hb_sorted_array_t +hb_sorted_array (T (&array_)[length_]) +{ return hb_sorted_array_t (array_); } + +template +inline bool hb_array_t::operator == (const hb_array_t &o) const +{ + if (o.length != this->length) return false; + for (unsigned int i = 0; i < this->length; i++) { + if (this->arrayZ[i] != o.arrayZ[i]) return false; + } + return true; +} +template <> +inline bool hb_array_t::operator == (const hb_array_t &o) const +{ + if (o.length != this->length) return false; + return 0 == hb_memcmp (arrayZ, o.arrayZ, length); +} +template <> +inline bool hb_array_t::operator == (const hb_array_t &o) const +{ + if (o.length != this->length) return false; + return 0 == hb_memcmp (arrayZ, o.arrayZ, length); +} + +template <> +inline uint32_t hb_array_t::hash () const { + uint32_t current = 0; + for (unsigned int i = 0; i < this->length; i++) + current = current * 31 + (uint32_t) (this->arrayZ[i] * 2654435761u); + return current; +} +template <> +inline uint32_t hb_array_t::hash () const { + uint32_t current = 0; + for (unsigned int i = 0; i < this->length; i++) + current = current * 31 + (uint32_t) (this->arrayZ[i] * 2654435761u); + return current; +} + + +typedef hb_array_t hb_bytes_t; +typedef hb_array_t hb_ubytes_t; + + + +#endif /* HB_ARRAY_HH */ diff --git a/libs/harfbuzz/src/hb-atomic.hh b/libs/harfbuzz/src/hb-atomic.hh new file mode 100644 index 00000000..14c6fb32 --- /dev/null +++ b/libs/harfbuzz/src/hb-atomic.hh @@ -0,0 +1,196 @@ +/* + * Copyright © 2007 Chris Wilson + * Copyright © 2009,2010 Red Hat, Inc. + * Copyright © 2011,2012 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. + * + * Contributor(s): + * Chris Wilson + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_ATOMIC_HH +#define HB_ATOMIC_HH + +#include "hb.hh" +#include "hb-meta.hh" + + +/* + * Atomic integers and pointers. + */ + + +/* We need external help for these */ + +#if defined(hb_atomic_int_impl_add) \ + && defined(hb_atomic_ptr_impl_get) \ + && defined(hb_atomic_ptr_impl_cmpexch) + +/* Defined externally, i.e. in config.h. */ + + +#elif !defined(HB_NO_MT) && defined(__ATOMIC_ACQUIRE) + +/* C++11-style GCC primitives. We prefer these as they don't require linking to libstdc++ / libc++. */ + +#define _hb_memory_barrier() __sync_synchronize () + +#define hb_atomic_int_impl_add(AI, V) __atomic_fetch_add ((AI), (V), __ATOMIC_ACQ_REL) +#define hb_atomic_int_impl_set_relaxed(AI, V) __atomic_store_n ((AI), (V), __ATOMIC_RELAXED) +#define hb_atomic_int_impl_set(AI, V) __atomic_store_n ((AI), (V), __ATOMIC_RELEASE) +#define hb_atomic_int_impl_get_relaxed(AI) __atomic_load_n ((AI), __ATOMIC_RELAXED) +#define hb_atomic_int_impl_get(AI) __atomic_load_n ((AI), __ATOMIC_ACQUIRE) + +#define hb_atomic_ptr_impl_set_relaxed(P, V) __atomic_store_n ((P), (V), __ATOMIC_RELAXED) +#define hb_atomic_ptr_impl_get_relaxed(P) __atomic_load_n ((P), __ATOMIC_RELAXED) +#define hb_atomic_ptr_impl_get(P) __atomic_load_n ((P), __ATOMIC_ACQUIRE) +static inline bool +_hb_atomic_ptr_impl_cmplexch (const void **P, const void *O_, const void *N) +{ + const void *O = O_; // Need lvalue + return __atomic_compare_exchange_n ((void **) P, (void **) &O, (void *) N, true, __ATOMIC_ACQ_REL, __ATOMIC_RELAXED); +} +#define hb_atomic_ptr_impl_cmpexch(P,O,N) _hb_atomic_ptr_impl_cmplexch ((const void **) (P), (O), (N)) + + +#elif !defined(HB_NO_MT) + +/* C++11 atomics. */ + +#include + +#define _hb_memory_barrier() std::atomic_thread_fence(std::memory_order_ack_rel) +#define _hb_memory_r_barrier() std::atomic_thread_fence(std::memory_order_acquire) +#define _hb_memory_w_barrier() std::atomic_thread_fence(std::memory_order_release) + +#define hb_atomic_int_impl_add(AI, V) (reinterpret_cast *> (AI)->fetch_add ((V), std::memory_order_acq_rel)) +#define hb_atomic_int_impl_set_relaxed(AI, V) (reinterpret_cast *> (AI)->store ((V), std::memory_order_relaxed)) +#define hb_atomic_int_impl_set(AI, V) (reinterpret_cast *> (AI)->store ((V), std::memory_order_release)) +#define hb_atomic_int_impl_get_relaxed(AI) (reinterpret_cast const *> (AI)->load (std::memory_order_relaxed)) +#define hb_atomic_int_impl_get(AI) (reinterpret_cast const *> (AI)->load (std::memory_order_acquire)) + +#define hb_atomic_ptr_impl_set_relaxed(P, V) (reinterpret_cast *> (P)->store ((V), std::memory_order_relaxed)) +#define hb_atomic_ptr_impl_get_relaxed(P) (reinterpret_cast const *> (P)->load (std::memory_order_relaxed)) +#define hb_atomic_ptr_impl_get(P) (reinterpret_cast *> (P)->load (std::memory_order_acquire)) +static inline bool +_hb_atomic_ptr_impl_cmplexch (const void **P, const void *O_, const void *N) +{ + const void *O = O_; // Need lvalue + return reinterpret_cast *> (P)->compare_exchange_weak (O, N, std::memory_order_acq_rel, std::memory_order_relaxed); +} +#define hb_atomic_ptr_impl_cmpexch(P,O,N) _hb_atomic_ptr_impl_cmplexch ((const void **) (P), (O), (N)) + + +#else /* defined(HB_NO_MT) */ + +#define hb_atomic_int_impl_add(AI, V) ((*(AI) += (V)) - (V)) +#define _hb_memory_barrier() do {} while (0) +#define hb_atomic_ptr_impl_cmpexch(P,O,N) (* (void **) (P) == (void *) (O) ? (* (void **) (P) = (void *) (N), true) : false) + +#endif + + +#ifndef _hb_compiler_memory_r_barrier +/* This we always use std::atomic for; and should never be disabled... + * except that MSVC gives me an internal compiler error on it. */ +#if !defined(_MSC_VER) +#include +#define _hb_compiler_memory_r_barrier() std::atomic_signal_fence (std::memory_order_acquire) +#else +#define _hb_compiler_memory_r_barrier() do {} while (0) +#endif +#endif + + + +#ifndef _hb_memory_r_barrier +#define _hb_memory_r_barrier() _hb_memory_barrier () +#endif +#ifndef _hb_memory_w_barrier +#define _hb_memory_w_barrier() _hb_memory_barrier () +#endif +#ifndef hb_atomic_int_impl_set_relaxed +#define hb_atomic_int_impl_set_relaxed(AI, V) (*(AI) = (V)) +#endif +#ifndef hb_atomic_int_impl_get_relaxed +#define hb_atomic_int_impl_get_relaxed(AI) (*(AI)) +#endif + +#ifndef hb_atomic_ptr_impl_set_relaxed +#define hb_atomic_ptr_impl_set_relaxed(P, V) (*(P) = (V)) +#endif +#ifndef hb_atomic_ptr_impl_get_relaxed +#define hb_atomic_ptr_impl_get_relaxed(P) (*(P)) +#endif +#ifndef hb_atomic_int_impl_set +inline void hb_atomic_int_impl_set (int *AI, int v) { _hb_memory_w_barrier (); *AI = v; } +#endif +#ifndef hb_atomic_int_impl_get +inline int hb_atomic_int_impl_get (const int *AI) { int v = *AI; _hb_memory_r_barrier (); return v; } +#endif +#ifndef hb_atomic_ptr_impl_get +inline void *hb_atomic_ptr_impl_get (void ** const P) { void *v = *P; _hb_memory_r_barrier (); return v; } +#endif + + +struct hb_atomic_int_t +{ + hb_atomic_int_t () = default; + constexpr hb_atomic_int_t (int v) : v (v) {} + + hb_atomic_int_t& operator = (int v_) { set_relaxed (v_); return *this; } + operator int () const { return get_relaxed (); } + + void set_relaxed (int v_) { hb_atomic_int_impl_set_relaxed (&v, v_); } + void set_release (int v_) { hb_atomic_int_impl_set (&v, v_); } + int get_relaxed () const { return hb_atomic_int_impl_get_relaxed (&v); } + int get_acquire () const { return hb_atomic_int_impl_get (&v); } + int inc () { return hb_atomic_int_impl_add (&v, 1); } + int dec () { return hb_atomic_int_impl_add (&v, -1); } + + int v = 0; +}; + +template +struct hb_atomic_ptr_t +{ + typedef hb_remove_pointer

T; + + hb_atomic_ptr_t () = default; + constexpr hb_atomic_ptr_t (T* v) : v (v) {} + + void init (T* v_ = nullptr) { set_relaxed (v_); } + void set_relaxed (T* v_) { hb_atomic_ptr_impl_set_relaxed (&v, v_); } + T *get_relaxed () const { return (T *) hb_atomic_ptr_impl_get_relaxed (&v); } + T *get_acquire () const { return (T *) hb_atomic_ptr_impl_get ((void **) &v); } + bool cmpexch (const T *old, T *new_) const { return hb_atomic_ptr_impl_cmpexch ((void **) &v, (void *) old, (void *) new_); } + + T * operator -> () const { return get_acquire (); } + template operator C * () const { return get_acquire (); } + + T *v = nullptr; +}; + + +#endif /* HB_ATOMIC_HH */ diff --git a/libs/harfbuzz/src/hb-bimap.hh b/libs/harfbuzz/src/hb-bimap.hh new file mode 100644 index 00000000..8e8c9887 --- /dev/null +++ b/libs/harfbuzz/src/hb-bimap.hh @@ -0,0 +1,155 @@ +/* + * Copyright © 2019 Adobe 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. + * + * Adobe Author(s): Michiharu Ariza + */ + +#ifndef HB_BIMAP_HH +#define HB_BIMAP_HH + +#include "hb.hh" +#include "hb-map.hh" + +/* Bi-directional map */ +struct hb_bimap_t +{ + void reset () + { + forw_map.reset (); + back_map.reset (); + } + + void resize (unsigned pop) + { + forw_map.resize (pop); + back_map.resize (pop); + } + + bool in_error () const { return forw_map.in_error () || back_map.in_error (); } + + void set (hb_codepoint_t lhs, hb_codepoint_t rhs) + { + if (in_error ()) return; + if (unlikely (lhs == HB_MAP_VALUE_INVALID)) return; + if (unlikely (rhs == HB_MAP_VALUE_INVALID)) { del (lhs); return; } + + forw_map.set (lhs, rhs); + if (unlikely (in_error ())) return; + + back_map.set (rhs, lhs); + if (unlikely (in_error ())) forw_map.del (lhs); + } + + hb_codepoint_t get (hb_codepoint_t lhs) const { return forw_map.get (lhs); } + hb_codepoint_t backward (hb_codepoint_t rhs) const { return back_map.get (rhs); } + + hb_codepoint_t operator [] (hb_codepoint_t lhs) const { return get (lhs); } + bool has (hb_codepoint_t lhs) const { return forw_map.has (lhs); } + + + void del (hb_codepoint_t lhs) + { + back_map.del (get (lhs)); + forw_map.del (lhs); + } + + void clear () + { + forw_map.clear (); + back_map.clear (); + } + + bool is_empty () const { return forw_map.is_empty (); } + + unsigned int get_population () const { return forw_map.get_population (); } + + protected: + hb_map_t forw_map; + hb_map_t back_map; +}; + +/* Inremental bimap: only lhs is given, rhs is incrementally assigned */ +struct hb_inc_bimap_t : hb_bimap_t +{ + /* Add a mapping from lhs to rhs with a unique value if lhs is unknown. + * Return the rhs value as the result. + */ + hb_codepoint_t add (hb_codepoint_t lhs) + { + hb_codepoint_t rhs = forw_map[lhs]; + if (rhs == HB_MAP_VALUE_INVALID) + { + rhs = next_value++; + set (lhs, rhs); + } + return rhs; + } + + hb_codepoint_t skip () + { return next_value++; } + + hb_codepoint_t get_next_value () const + { return next_value; } + + void add_set (const hb_set_t *set) + { + hb_codepoint_t i = HB_SET_VALUE_INVALID; + while (hb_set_next (set, &i)) add (i); + } + + /* Create an identity map. */ + bool identity (unsigned int size) + { + clear (); + for (hb_codepoint_t i = 0; i < size; i++) set (i, i); + return !in_error (); + } + + protected: + static int cmp_id (const void* a, const void* b) + { return (int)*(const hb_codepoint_t *)a - (int)*(const hb_codepoint_t *)b; } + + public: + /* Optional: after finished adding all mappings in a random order, + * reassign rhs to lhs so that they are in the same order. */ + void sort () + { + hb_codepoint_t count = get_population (); + hb_vector_t work; + work.resize (count); + + for (hb_codepoint_t rhs = 0; rhs < count; rhs++) + work[rhs] = back_map[rhs]; + + work.qsort (cmp_id); + + clear (); + for (hb_codepoint_t rhs = 0; rhs < count; rhs++) + set (work[rhs], rhs); + } + + protected: + unsigned int next_value = 0; +}; + +#endif /* HB_BIMAP_HH */ diff --git a/libs/harfbuzz/src/hb-bit-page.hh b/libs/harfbuzz/src/hb-bit-page.hh new file mode 100644 index 00000000..95ae1b7b --- /dev/null +++ b/libs/harfbuzz/src/hb-bit-page.hh @@ -0,0 +1,282 @@ +/* + * Copyright © 2012,2017 Google, Inc. + * Copyright © 2021 Behdad Esfahbod + * + * 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 HB_BIT_PAGE_HH +#define HB_BIT_PAGE_HH + +#include "hb.hh" + +struct hb_bit_page_t +{ + void init0 () { v.clear (); } + void init1 () { v.clear (0xFF); } + + constexpr unsigned len () const + { return ARRAY_LENGTH_CONST (v); } + + bool is_empty () const + { + for (unsigned i = 0; i < len (); i++) + if (v[i]) + return false; + return true; + } + uint32_t hash () const + { + uint32_t h = 0; + for (unsigned i = 0; i < len (); i++) + h = h * 31 + hb_hash (v[i]); + return h; + } + + void add (hb_codepoint_t g) { elt (g) |= mask (g); } + void del (hb_codepoint_t g) { elt (g) &= ~mask (g); } + void set (hb_codepoint_t g, bool value) { if (value) add (g); else del (g); } + bool get (hb_codepoint_t g) const { return elt (g) & mask (g); } + + void add_range (hb_codepoint_t a, hb_codepoint_t b) + { + elt_t *la = &elt (a); + elt_t *lb = &elt (b); + if (la == lb) + *la |= (mask (b) << 1) - mask(a); + else + { + *la |= ~(mask (a) - 1); + la++; + + memset (la, 0xff, (char *) lb - (char *) la); + + *lb |= ((mask (b) << 1) - 1); + } + } + void del_range (hb_codepoint_t a, hb_codepoint_t b) + { + elt_t *la = &elt (a); + elt_t *lb = &elt (b); + if (la == lb) + *la &= ~((mask (b) << 1) - mask(a)); + else + { + *la &= mask (a) - 1; + la++; + + memset (la, 0, (char *) lb - (char *) la); + + *lb &= ~((mask (b) << 1) - 1); + } + } + void set_range (hb_codepoint_t a, hb_codepoint_t b, bool v) + { if (v) add_range (a, b); else del_range (a, b); } + + + // Writes out page values to the array p. Returns the number of values + // written. At most size codepoints will be written. + unsigned int write (uint32_t base, + unsigned int start_value, + hb_codepoint_t *p, + unsigned int size) const + { + unsigned int start_v = start_value >> ELT_BITS_LOG_2; + unsigned int start_bit = start_value & ELT_MASK; + unsigned int count = 0; + for (unsigned i = start_v; i < len () && count < size; i++) + { + elt_t bits = v[i]; + uint32_t v_base = base | (i << ELT_BITS_LOG_2); + for (unsigned int j = start_bit; j < ELT_BITS && count < size; j++) + { + if ((elt_t(1) << j) & bits) { + *p++ = v_base | j; + count++; + } + } + start_bit = 0; + } + return count; + } + + // Writes out the values NOT in this page to the array p. Returns the + // number of values written. At most size codepoints will be written. + // Returns the number of codepoints written. next_value holds the next value + // that should be written (if not present in this page). This is used to fill + // any missing value gaps between this page and the previous page, if any. + // next_value is updated to one more than the last value present in this page. + unsigned int write_inverted (uint32_t base, + unsigned int start_value, + hb_codepoint_t *p, + unsigned int size, + hb_codepoint_t *next_value) const + { + unsigned int start_v = start_value >> ELT_BITS_LOG_2; + unsigned int start_bit = start_value & ELT_MASK; + unsigned int count = 0; + for (unsigned i = start_v; i < len () && count < size; i++) + { + elt_t bits = v[i]; + uint32_t v_offset = i << ELT_BITS_LOG_2; + for (unsigned int j = start_bit; j < ELT_BITS && count < size; j++) + { + if ((elt_t(1) << j) & bits) + { + hb_codepoint_t value = base | v_offset | j; + // Emit all the missing values from next_value up to value - 1. + for (hb_codepoint_t k = *next_value; k < value && count < size; k++) + { + *p++ = k; + count++; + } + // Skip over this value; + *next_value = value + 1; + } + } + start_bit = 0; + } + return count; + } + + bool is_equal (const hb_bit_page_t &other) const + { + return 0 == hb_memcmp (&v, &other.v, sizeof (v)); + } + bool is_subset (const hb_bit_page_t &larger_page) const + { + for (unsigned i = 0; i < len (); i++) + if (~larger_page.v[i] & v[i]) + return false; + return true; + } + + unsigned int get_population () const + { + unsigned int pop = 0; + for (unsigned int i = 0; i < len (); i++) + pop += hb_popcount (v[i]); + return pop; + } + + bool next (hb_codepoint_t *codepoint) const + { + unsigned int m = (*codepoint + 1) & MASK; + if (!m) + { + *codepoint = INVALID; + return false; + } + unsigned int i = m / ELT_BITS; + unsigned int j = m & ELT_MASK; + + const elt_t vv = v[i] & ~((elt_t (1) << j) - 1); + for (const elt_t *p = &vv; i < len (); p = &v[++i]) + if (*p) + { + *codepoint = i * ELT_BITS + elt_get_min (*p); + return true; + } + + *codepoint = INVALID; + return false; + } + bool previous (hb_codepoint_t *codepoint) const + { + unsigned int m = (*codepoint - 1) & MASK; + if (m == MASK) + { + *codepoint = INVALID; + return false; + } + unsigned int i = m / ELT_BITS; + unsigned int j = m & ELT_MASK; + + /* Fancy mask to avoid shifting by elt_t bitsize, which is undefined. */ + const elt_t mask = j < 8 * sizeof (elt_t) - 1 ? + ((elt_t (1) << (j + 1)) - 1) : + (elt_t) -1; + const elt_t vv = v[i] & mask; + const elt_t *p = &vv; + while (true) + { + if (*p) + { + *codepoint = i * ELT_BITS + elt_get_max (*p); + return true; + } + if ((int) i <= 0) break; + p = &v[--i]; + } + + *codepoint = INVALID; + return false; + } + hb_codepoint_t get_min () const + { + for (unsigned int i = 0; i < len (); i++) + if (v[i]) + return i * ELT_BITS + elt_get_min (v[i]); + return INVALID; + } + hb_codepoint_t get_max () const + { + for (int i = len () - 1; i >= 0; i--) + if (v[i]) + return i * ELT_BITS + elt_get_max (v[i]); + return 0; + } + + static constexpr hb_codepoint_t INVALID = HB_SET_VALUE_INVALID; + + typedef unsigned long long elt_t; + static constexpr unsigned PAGE_BITS = 512; + static_assert ((PAGE_BITS & ((PAGE_BITS) - 1)) == 0, ""); + static constexpr unsigned PAGE_BITS_LOG_2 = 9; + static_assert (1 << PAGE_BITS_LOG_2 == PAGE_BITS, ""); + static constexpr unsigned PAGE_BITMASK = PAGE_BITS - 1; + + static unsigned int elt_get_min (const elt_t &elt) { return hb_ctz (elt); } + static unsigned int elt_get_max (const elt_t &elt) { return hb_bit_storage (elt) - 1; } + + typedef hb_vector_size_t vector_t; + + static constexpr unsigned ELT_BITS = sizeof (elt_t) * 8; + static constexpr unsigned ELT_BITS_LOG_2 = 6; + static_assert (1 << ELT_BITS_LOG_2 == ELT_BITS, ""); + static constexpr unsigned ELT_MASK = ELT_BITS - 1; + + static constexpr unsigned BITS = sizeof (vector_t) * 8; + static constexpr unsigned MASK = BITS - 1; + static_assert ((unsigned) PAGE_BITS == (unsigned) BITS, ""); + + elt_t &elt (hb_codepoint_t g) { return v[(g & MASK) / ELT_BITS]; } + const elt_t& elt (hb_codepoint_t g) const { return v[(g & MASK) / ELT_BITS]; } + static constexpr elt_t mask (hb_codepoint_t g) { return elt_t (1) << (g & ELT_MASK); } + + vector_t v; +}; +static_assert (hb_bit_page_t::PAGE_BITS == sizeof (hb_bit_page_t) * 8, ""); + + +#endif /* HB_BIT_PAGE_HH */ diff --git a/libs/harfbuzz/src/hb-bit-set-invertible.hh b/libs/harfbuzz/src/hb-bit-set-invertible.hh new file mode 100644 index 00000000..27fb0732 --- /dev/null +++ b/libs/harfbuzz/src/hb-bit-set-invertible.hh @@ -0,0 +1,375 @@ +/* + * Copyright © 2012,2017 Google, Inc. + * Copyright © 2021 Behdad Esfahbod + * + * 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 HB_BIT_SET_INVERTIBLE_HH +#define HB_BIT_SET_INVERTIBLE_HH + +#include "hb.hh" +#include "hb-bit-set.hh" + + +struct hb_bit_set_invertible_t +{ + hb_bit_set_t s; + bool inverted = false; + + hb_bit_set_invertible_t () = default; + hb_bit_set_invertible_t (const hb_bit_set_invertible_t& o) = default; + hb_bit_set_invertible_t (hb_bit_set_invertible_t&& other) : hb_bit_set_invertible_t () { hb_swap (*this, other); } + hb_bit_set_invertible_t& operator= (const hb_bit_set_invertible_t& o) = default; + hb_bit_set_invertible_t& operator= (hb_bit_set_invertible_t&& other) { hb_swap (*this, other); return *this; } + friend void swap (hb_bit_set_invertible_t &a, hb_bit_set_invertible_t &b) + { + if (likely (!a.s.successful || !b.s.successful)) + return; + hb_swap (a.inverted, b.inverted); + hb_swap (a.s, b.s); + } + + void init () { s.init (); inverted = false; } + void fini () { s.fini (); } + void err () { s.err (); } + bool in_error () const { return s.in_error (); } + explicit operator bool () const { return !is_empty (); } + + void alloc (unsigned sz) { s.alloc (sz); } + void reset () + { + s.reset (); + inverted = false; + } + void clear () + { + s.clear (); + if (likely (s.successful)) + inverted = false; + } + void invert () + { + if (likely (s.successful)) + inverted = !inverted; + } + + bool is_empty () const + { + hb_codepoint_t v = INVALID; + next (&v); + return v == INVALID; + } + uint32_t hash () const { return s.hash () ^ (uint32_t) inverted; } + + hb_codepoint_t get_min () const + { + hb_codepoint_t v = INVALID; + next (&v); + return v; + } + hb_codepoint_t get_max () const + { + hb_codepoint_t v = INVALID; + previous (&v); + return v; + } + unsigned int get_population () const + { return inverted ? INVALID - s.get_population () : s.get_population (); } + + + void add (hb_codepoint_t g) { unlikely (inverted) ? s.del (g) : s.add (g); } + bool add_range (hb_codepoint_t a, hb_codepoint_t b) + { return unlikely (inverted) ? ((void) s.del_range (a, b), true) : s.add_range (a, b); } + + template + void add_array (const T *array, unsigned int count, unsigned int stride=sizeof(T)) + { inverted ? s.del_array (array, count, stride) : s.add_array (array, count, stride); } + template + void add_array (const hb_array_t& arr) { add_array (&arr, arr.len ()); } + + /* Might return false if array looks unsorted. + * Used for faster rejection of corrupt data. */ + template + bool add_sorted_array (const T *array, unsigned int count, unsigned int stride=sizeof(T)) + { return inverted ? s.del_sorted_array (array, count, stride) : s.add_sorted_array (array, count, stride); } + template + bool add_sorted_array (const hb_sorted_array_t& arr) { return add_sorted_array (&arr, arr.len ()); } + + void del (hb_codepoint_t g) { unlikely (inverted) ? s.add (g) : s.del (g); } + void del_range (hb_codepoint_t a, hb_codepoint_t b) + { unlikely (inverted) ? (void) s.add_range (a, b) : s.del_range (a, b); } + + bool get (hb_codepoint_t g) const { return s.get (g) ^ inverted; } + + /* Has interface. */ + static constexpr bool SENTINEL = false; + typedef bool value_t; + value_t operator [] (hb_codepoint_t k) const { return get (k); } + bool has (hb_codepoint_t k) const { return (*this)[k] != SENTINEL; } + /* Predicate. */ + bool operator () (hb_codepoint_t k) const { return has (k); } + + /* Sink interface. */ + hb_bit_set_invertible_t& operator << (hb_codepoint_t v) + { add (v); return *this; } + hb_bit_set_invertible_t& operator << (const hb_pair_t& range) + { add_range (range.first, range.second); return *this; } + + bool intersects (hb_codepoint_t first, hb_codepoint_t last) const + { + hb_codepoint_t c = first - 1; + return next (&c) && c <= last; + } + + void set (const hb_bit_set_invertible_t &other) + { + s.set (other.s); + if (likely (s.successful)) + inverted = other.inverted; + } + + bool is_equal (const hb_bit_set_invertible_t &other) const + { + if (likely (inverted == other.inverted)) + return s.is_equal (other.s); + else + { + /* TODO Add iter_ranges() and use here. */ + auto it1 = iter (); + auto it2 = other.iter (); + return hb_all (+ hb_zip (it1, it2) + | hb_map ([](hb_pair_t _) { return _.first == _.second; })); + } + } + + bool is_subset (const hb_bit_set_invertible_t &larger_set) const + { + if (unlikely (inverted != larger_set.inverted)) + return hb_all (hb_iter (s) | hb_map (larger_set.s)); + else + return unlikely (inverted) ? larger_set.s.is_subset (s) : s.is_subset (larger_set.s); + } + + protected: + template + void process (const Op& op, const hb_bit_set_invertible_t &other) + { s.process (op, other.s); } + public: + void union_ (const hb_bit_set_invertible_t &other) + { + if (likely (inverted == other.inverted)) + { + if (unlikely (inverted)) + process (hb_bitwise_and, other); + else + process (hb_bitwise_or, other); /* Main branch. */ + } + else + { + if (unlikely (inverted)) + process (hb_bitwise_gt, other); + else + process (hb_bitwise_lt, other); + } + if (likely (s.successful)) + inverted = inverted || other.inverted; + } + void intersect (const hb_bit_set_invertible_t &other) + { + if (likely (inverted == other.inverted)) + { + if (unlikely (inverted)) + process (hb_bitwise_or, other); + else + process (hb_bitwise_and, other); /* Main branch. */ + } + else + { + if (unlikely (inverted)) + process (hb_bitwise_lt, other); + else + process (hb_bitwise_gt, other); + } + if (likely (s.successful)) + inverted = inverted && other.inverted; + } + void subtract (const hb_bit_set_invertible_t &other) + { + if (likely (inverted == other.inverted)) + { + if (unlikely (inverted)) + process (hb_bitwise_lt, other); + else + process (hb_bitwise_gt, other); /* Main branch. */ + } + else + { + if (unlikely (inverted)) + process (hb_bitwise_or, other); + else + process (hb_bitwise_and, other); + } + if (likely (s.successful)) + inverted = inverted && !other.inverted; + } + void symmetric_difference (const hb_bit_set_invertible_t &other) + { + process (hb_bitwise_xor, other); + if (likely (s.successful)) + inverted = inverted ^ other.inverted; + } + + bool next (hb_codepoint_t *codepoint) const + { + if (likely (!inverted)) + return s.next (codepoint); + + auto old = *codepoint; + if (unlikely (old + 1 == INVALID)) + { + *codepoint = INVALID; + return false; + } + + auto v = old; + s.next (&v); + if (old + 1 < v) + { + *codepoint = old + 1; + return true; + } + + v = old; + s.next_range (&old, &v); + + *codepoint = v + 1; + return *codepoint != INVALID; + } + bool previous (hb_codepoint_t *codepoint) const + { + if (likely (!inverted)) + return s.previous (codepoint); + + auto old = *codepoint; + if (unlikely (old - 1 == INVALID)) + { + *codepoint = INVALID; + return false; + } + + auto v = old; + s.previous (&v); + + if (old - 1 > v || v == INVALID) + { + *codepoint = old - 1; + return true; + } + + v = old; + s.previous_range (&v, &old); + + *codepoint = v - 1; + return *codepoint != INVALID; + } + bool next_range (hb_codepoint_t *first, hb_codepoint_t *last) const + { + if (likely (!inverted)) + return s.next_range (first, last); + + if (!next (last)) + { + *last = *first = INVALID; + return false; + } + + *first = *last; + s.next (last); + --*last; + return true; + } + bool previous_range (hb_codepoint_t *first, hb_codepoint_t *last) const + { + if (likely (!inverted)) + return s.previous_range (first, last); + + if (!previous (first)) + { + *last = *first = INVALID; + return false; + } + + *last = *first; + s.previous (first); + ++*first; + return true; + } + + unsigned int next_many (hb_codepoint_t codepoint, + hb_codepoint_t *out, + unsigned int size) const + { + return inverted ? s.next_many_inverted (codepoint, out, size) + : s.next_many (codepoint, out, size); + } + + static constexpr hb_codepoint_t INVALID = hb_bit_set_t::INVALID; + + /* + * Iterator implementation. + */ + struct iter_t : hb_iter_with_fallback_t + { + static constexpr bool is_sorted_iterator = true; + iter_t (const hb_bit_set_invertible_t &s_ = Null (hb_bit_set_invertible_t), + bool init = true) : s (&s_), v (INVALID), l(0) + { + if (init) + { + l = s->get_population () + 1; + __next__ (); + } + } + + typedef hb_codepoint_t __item_t__; + hb_codepoint_t __item__ () const { return v; } + bool __more__ () const { return v != INVALID; } + void __next__ () { s->next (&v); if (l) l--; } + void __prev__ () { s->previous (&v); } + unsigned __len__ () const { return l; } + iter_t end () const { return iter_t (*s, false); } + bool operator != (const iter_t& o) const + { return s != o.s || v != o.v; } + + protected: + const hb_bit_set_invertible_t *s; + hb_codepoint_t v; + unsigned l; + }; + iter_t iter () const { return iter_t (*this); } + operator iter_t () const { return iter (); } +}; + + +#endif /* HB_BIT_SET_INVERTIBLE_HH */ diff --git a/libs/harfbuzz/src/hb-bit-set.hh b/libs/harfbuzz/src/hb-bit-set.hh new file mode 100644 index 00000000..a63887ef --- /dev/null +++ b/libs/harfbuzz/src/hb-bit-set.hh @@ -0,0 +1,960 @@ +/* + * Copyright © 2012,2017 Google, Inc. + * Copyright © 2021 Behdad Esfahbod + * + * 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 HB_BIT_SET_HH +#define HB_BIT_SET_HH + +#include "hb.hh" +#include "hb-bit-page.hh" +#include "hb-machinery.hh" + + +struct hb_bit_set_t +{ + hb_bit_set_t () = default; + ~hb_bit_set_t () = default; + + hb_bit_set_t (const hb_bit_set_t& other) : hb_bit_set_t () { set (other); } + hb_bit_set_t ( hb_bit_set_t&& other) : hb_bit_set_t () { hb_swap (*this, other); } + hb_bit_set_t& operator= (const hb_bit_set_t& other) { set (other); return *this; } + hb_bit_set_t& operator= (hb_bit_set_t&& other) { hb_swap (*this, other); return *this; } + friend void swap (hb_bit_set_t &a, hb_bit_set_t &b) + { + if (likely (!a.successful || !b.successful)) + return; + hb_swap (a.population, b.population); + hb_swap (a.last_page_lookup, b.last_page_lookup); + hb_swap (a.page_map, b.page_map); + hb_swap (a.pages, b.pages); + } + + void init () + { + successful = true; + population = 0; + last_page_lookup = 0; + page_map.init (); + pages.init (); + } + void fini () + { + page_map.fini (); + pages.fini (); + } + + using page_t = hb_bit_page_t; + struct page_map_t + { + int cmp (const page_map_t &o) const { return cmp (o.major); } + int cmp (uint32_t o_major) const { return (int) o_major - (int) major; } + + uint32_t major; + uint32_t index; + }; + + bool successful = true; /* Allocations successful */ + mutable unsigned int population = 0; + mutable hb_atomic_int_t last_page_lookup = 0; + hb_sorted_vector_t page_map; + hb_vector_t pages; + + void err () { if (successful) successful = false; } /* TODO Remove */ + bool in_error () const { return !successful; } + + bool resize (unsigned int count) + { + if (unlikely (!successful)) return false; + if (unlikely (!pages.resize (count) || !page_map.resize (count))) + { + pages.resize (page_map.length); + successful = false; + return false; + } + return true; + } + + void alloc (unsigned sz) + { + sz >>= (page_t::PAGE_BITS_LOG_2 - 1); + pages.alloc (sz); + page_map.alloc (sz); + } + + void reset () + { + successful = true; + clear (); + } + + void clear () + { + resize (0); + if (likely (successful)) + population = 0; + } + bool is_empty () const + { + unsigned int count = pages.length; + for (unsigned int i = 0; i < count; i++) + if (!pages[i].is_empty ()) + return false; + return true; + } + explicit operator bool () const { return !is_empty (); } + + uint32_t hash () const + { + uint32_t h = 0; + for (auto &map : page_map) + h = h * 31 + hb_hash (map.major) + hb_hash (pages[map.index]); + return h; + } + + private: + void dirty () { population = UINT_MAX; } + public: + + void add (hb_codepoint_t g) + { + if (unlikely (!successful)) return; + if (unlikely (g == INVALID)) return; + dirty (); + page_t *page = page_for (g, true); if (unlikely (!page)) return; + page->add (g); + } + bool add_range (hb_codepoint_t a, hb_codepoint_t b) + { + if (unlikely (!successful)) return true; /* https://github.com/harfbuzz/harfbuzz/issues/657 */ + if (unlikely (a > b || a == INVALID || b == INVALID)) return false; + dirty (); + unsigned int ma = get_major (a); + unsigned int mb = get_major (b); + if (ma == mb) + { + page_t *page = page_for (a, true); if (unlikely (!page)) return false; + page->add_range (a, b); + } + else + { + page_t *page = page_for (a, true); if (unlikely (!page)) return false; + page->add_range (a, major_start (ma + 1) - 1); + + for (unsigned int m = ma + 1; m < mb; m++) + { + page = page_for (major_start (m), true); if (unlikely (!page)) return false; + page->init1 (); + } + + page = page_for (b, true); if (unlikely (!page)) return false; + page->add_range (major_start (mb), b); + } + return true; + } + + template + void set_array (bool v, const T *array, unsigned int count, unsigned int stride=sizeof(T)) + { + if (unlikely (!successful)) return; + if (!count) return; + dirty (); + hb_codepoint_t g = *array; + while (count) + { + unsigned int m = get_major (g); + page_t *page = page_for (g, v); if (unlikely (v && !page)) return; + unsigned int start = major_start (m); + unsigned int end = major_start (m + 1); + do + { + if (v || page) /* The v check is to optimize out the page check if v is true. */ + page->set (g, v); + + array = &StructAtOffsetUnaligned (array, stride); + count--; + } + while (count && (g = *array, start <= g && g < end)); + } + } + + template + void add_array (const T *array, unsigned int count, unsigned int stride=sizeof(T)) + { set_array (true, array, count, stride); } + template + void add_array (const hb_array_t& arr) { add_array (&arr, arr.len ()); } + + template + void del_array (const T *array, unsigned int count, unsigned int stride=sizeof(T)) + { set_array (false, array, count, stride); } + template + void del_array (const hb_array_t& arr) { del_array (&arr, arr.len ()); } + + /* Might return false if array looks unsorted. + * Used for faster rejection of corrupt data. */ + template + bool set_sorted_array (bool v, const T *array, unsigned int count, unsigned int stride=sizeof(T)) + { + if (unlikely (!successful)) return true; /* https://github.com/harfbuzz/harfbuzz/issues/657 */ + if (unlikely (!count)) return true; + dirty (); + hb_codepoint_t g = *array; + hb_codepoint_t last_g = g; + while (count) + { + unsigned int m = get_major (g); + page_t *page = page_for (g, v); if (unlikely (v && !page)) return false; + unsigned int end = major_start (m + 1); + do + { + /* If we try harder we can change the following comparison to <=; + * Not sure if it's worth it. */ + if (g < last_g) return false; + last_g = g; + + if (v || page) /* The v check is to optimize out the page check if v is true. */ + page->add (g); + + array = &StructAtOffsetUnaligned (array, stride); + count--; + } + while (count && (g = *array, g < end)); + } + return true; + } + + template + bool add_sorted_array (const T *array, unsigned int count, unsigned int stride=sizeof(T)) + { return set_sorted_array (true, array, count, stride); } + template + bool add_sorted_array (const hb_sorted_array_t& arr) { return add_sorted_array (&arr, arr.len ()); } + + template + bool del_sorted_array (const T *array, unsigned int count, unsigned int stride=sizeof(T)) + { return set_sorted_array (false, array, count, stride); } + template + bool del_sorted_array (const hb_sorted_array_t& arr) { return del_sorted_array (&arr, arr.len ()); } + + void del (hb_codepoint_t g) + { + if (unlikely (!successful)) return; + page_t *page = page_for (g); + if (!page) + return; + dirty (); + page->del (g); + } + + private: + void del_pages (int ds, int de) + { + if (ds <= de) + { + // Pre-allocate the workspace that compact() will need so we can bail on allocation failure + // before attempting to rewrite the page map. + hb_vector_t compact_workspace; + if (unlikely (!allocate_compact_workspace (compact_workspace))) return; + + unsigned int write_index = 0; + for (unsigned int i = 0; i < page_map.length; i++) + { + int m = (int) page_map[i].major; + if (m < ds || de < m) + page_map[write_index++] = page_map[i]; + } + compact (compact_workspace, write_index); + resize (write_index); + } + } + + + public: + void del_range (hb_codepoint_t a, hb_codepoint_t b) + { + if (unlikely (!successful)) return; + if (unlikely (a > b || a == INVALID)) return; + dirty (); + unsigned int ma = get_major (a); + unsigned int mb = get_major (b); + /* Delete pages from ds through de if ds <= de. */ + int ds = (a == major_start (ma))? (int) ma: (int) (ma + 1); + int de = (b + 1 == major_start (mb + 1))? (int) mb: ((int) mb - 1); + if (ds > de || (int) ma < ds) + { + page_t *page = page_for (a); + if (page) + { + if (ma == mb) + page->del_range (a, b); + else + page->del_range (a, major_start (ma + 1) - 1); + } + } + if (de < (int) mb && ma != mb) + { + page_t *page = page_for (b); + if (page) + page->del_range (major_start (mb), b); + } + del_pages (ds, de); + } + + bool get (hb_codepoint_t g) const + { + const page_t *page = page_for (g); + if (!page) + return false; + return page->get (g); + } + + /* Has interface. */ + static constexpr bool SENTINEL = false; + typedef bool value_t; + value_t operator [] (hb_codepoint_t k) const { return get (k); } + bool has (hb_codepoint_t k) const { return (*this)[k] != SENTINEL; } + /* Predicate. */ + bool operator () (hb_codepoint_t k) const { return has (k); } + + /* Sink interface. */ + hb_bit_set_t& operator << (hb_codepoint_t v) + { add (v); return *this; } + hb_bit_set_t& operator << (const hb_pair_t& range) + { add_range (range.first, range.second); return *this; } + + bool intersects (hb_codepoint_t first, hb_codepoint_t last) const + { + hb_codepoint_t c = first - 1; + return next (&c) && c <= last; + } + void set (const hb_bit_set_t &other) + { + if (unlikely (!successful)) return; + unsigned int count = other.pages.length; + if (unlikely (!resize (count))) + return; + population = other.population; + + page_map = other.page_map; + pages = other.pages; + } + + bool is_equal (const hb_bit_set_t &other) const + { + if (has_population () && other.has_population () && + population != other.population) + return false; + + unsigned int na = pages.length; + unsigned int nb = other.pages.length; + + unsigned int a = 0, b = 0; + for (; a < na && b < nb; ) + { + if (page_at (a).is_empty ()) { a++; continue; } + if (other.page_at (b).is_empty ()) { b++; continue; } + if (page_map[a].major != other.page_map[b].major || + !page_at (a).is_equal (other.page_at (b))) + return false; + a++; + b++; + } + for (; a < na; a++) + if (!page_at (a).is_empty ()) { return false; } + for (; b < nb; b++) + if (!other.page_at (b).is_empty ()) { return false; } + + return true; + } + + bool is_subset (const hb_bit_set_t &larger_set) const + { + if (has_population () && larger_set.has_population () && + population != larger_set.population) + return false; + + uint32_t spi = 0; + for (uint32_t lpi = 0; spi < page_map.length && lpi < larger_set.page_map.length; lpi++) + { + uint32_t spm = page_map[spi].major; + uint32_t lpm = larger_set.page_map[lpi].major; + auto sp = page_at (spi); + auto lp = larger_set.page_at (lpi); + + if (spm < lpm && !sp.is_empty ()) + return false; + + if (lpm < spm) + continue; + + if (!sp.is_subset (lp)) + return false; + + spi++; + } + + while (spi < page_map.length) + if (!page_at (spi++).is_empty ()) + return false; + + return true; + } + + private: + bool allocate_compact_workspace (hb_vector_t& workspace) + { + if (unlikely (!workspace.resize (pages.length))) + { + successful = false; + return false; + } + + return true; + } + + /* + * workspace should be a pre-sized vector allocated to hold at exactly pages.length + * elements. + */ + void compact (hb_vector_t& workspace, + unsigned int length) + { + assert(workspace.length == pages.length); + hb_vector_t& old_index_to_page_map_index = workspace; + + hb_fill (old_index_to_page_map_index.writer(), 0xFFFFFFFF); + for (unsigned i = 0; i < length; i++) + old_index_to_page_map_index[page_map[i].index] = i; + + compact_pages (old_index_to_page_map_index); + } + void compact_pages (const hb_vector_t& old_index_to_page_map_index) + { + unsigned int write_index = 0; + for (unsigned int i = 0; i < pages.length; i++) + { + if (old_index_to_page_map_index[i] == 0xFFFFFFFF) continue; + + if (write_index < i) + pages[write_index] = pages[i]; + + page_map[old_index_to_page_map_index[i]].index = write_index; + write_index++; + } + } + public: + + void process_ (hb_bit_page_t::vector_t (*op) (const hb_bit_page_t::vector_t &, const hb_bit_page_t::vector_t &), + bool passthru_left, bool passthru_right, + const hb_bit_set_t &other) + { + if (unlikely (!successful)) return; + + dirty (); + + unsigned int na = pages.length; + unsigned int nb = other.pages.length; + unsigned int next_page = na; + + unsigned int count = 0, newCount = 0; + unsigned int a = 0, b = 0; + unsigned int write_index = 0; + + // Pre-allocate the workspace that compact() will need so we can bail on allocation failure + // before attempting to rewrite the page map. + hb_vector_t compact_workspace; + if (!passthru_left && unlikely (!allocate_compact_workspace (compact_workspace))) return; + + for (; a < na && b < nb; ) + { + if (page_map[a].major == other.page_map[b].major) + { + if (!passthru_left) + { + // Move page_map entries that we're keeping from the left side set + // to the front of the page_map vector. This isn't necessary if + // passthru_left is set since no left side pages will be removed + // in that case. + if (write_index < a) + page_map[write_index] = page_map[a]; + write_index++; + } + + count++; + a++; + b++; + } + else if (page_map[a].major < other.page_map[b].major) + { + if (passthru_left) + count++; + a++; + } + else + { + if (passthru_right) + count++; + b++; + } + } + if (passthru_left) + count += na - a; + if (passthru_right) + count += nb - b; + + if (!passthru_left) + { + na = write_index; + next_page = write_index; + compact (compact_workspace, write_index); + } + + if (unlikely (!resize (count))) + return; + + newCount = count; + + /* Process in-place backward. */ + a = na; + b = nb; + for (; a && b; ) + { + if (page_map[a - 1].major == other.page_map[b - 1].major) + { + a--; + b--; + count--; + page_map[count] = page_map[a]; + page_at (count).v = op (page_at (a).v, other.page_at (b).v); + } + else if (page_map[a - 1].major > other.page_map[b - 1].major) + { + a--; + if (passthru_left) + { + count--; + page_map[count] = page_map[a]; + } + } + else + { + b--; + if (passthru_right) + { + count--; + page_map[count].major = other.page_map[b].major; + page_map[count].index = next_page++; + page_at (count).v = other.page_at (b).v; + } + } + } + if (passthru_left) + while (a) + { + a--; + count--; + page_map[count] = page_map [a]; + } + if (passthru_right) + while (b) + { + b--; + count--; + page_map[count].major = other.page_map[b].major; + page_map[count].index = next_page++; + page_at (count).v = other.page_at (b).v; + } + assert (!count); + resize (newCount); + } + template + static hb_bit_page_t::vector_t + op_ (const hb_bit_page_t::vector_t &a, const hb_bit_page_t::vector_t &b) + { return Op{} (a, b); } + template + void process (const Op& op, const hb_bit_set_t &other) + { + process_ (op_, op (1, 0), op (0, 1), other); + } + + void union_ (const hb_bit_set_t &other) { process (hb_bitwise_or, other); } + void intersect (const hb_bit_set_t &other) { process (hb_bitwise_and, other); } + void subtract (const hb_bit_set_t &other) { process (hb_bitwise_gt, other); } + void symmetric_difference (const hb_bit_set_t &other) { process (hb_bitwise_xor, other); } + + bool next (hb_codepoint_t *codepoint) const + { + // TODO: this should be merged with prev() as both implementations + // are very similar. + if (unlikely (*codepoint == INVALID)) { + *codepoint = get_min (); + return *codepoint != INVALID; + } + + const auto* page_map_array = page_map.arrayZ; + unsigned int major = get_major (*codepoint); + unsigned int i = last_page_lookup; + + if (unlikely (i >= page_map.length || page_map_array[i].major != major)) + { + page_map.bfind (major, &i, HB_NOT_FOUND_STORE_CLOSEST); + if (i >= page_map.length) { + *codepoint = INVALID; + return false; + } + } + + const auto* pages_array = pages.arrayZ; + const page_map_t ¤t = page_map_array[i]; + if (likely (current.major == major)) + { + if (pages_array[current.index].next (codepoint)) + { + *codepoint += current.major * page_t::PAGE_BITS; + last_page_lookup = i; + return true; + } + i++; + } + + for (; i < page_map.length; i++) + { + const page_map_t ¤t = page_map.arrayZ[i]; + hb_codepoint_t m = pages_array[current.index].get_min (); + if (m != INVALID) + { + *codepoint = current.major * page_t::PAGE_BITS + m; + last_page_lookup = i; + return true; + } + } + last_page_lookup = 0; + *codepoint = INVALID; + return false; + } + bool previous (hb_codepoint_t *codepoint) const + { + if (unlikely (*codepoint == INVALID)) { + *codepoint = get_max (); + return *codepoint != INVALID; + } + + page_map_t map = {get_major (*codepoint), 0}; + unsigned int i; + page_map.bfind (map, &i, HB_NOT_FOUND_STORE_CLOSEST); + if (i < page_map.length && page_map[i].major == map.major) + { + if (pages[page_map[i].index].previous (codepoint)) + { + *codepoint += page_map[i].major * page_t::PAGE_BITS; + return true; + } + } + i--; + for (; (int) i >= 0; i--) + { + hb_codepoint_t m = pages[page_map[i].index].get_max (); + if (m != INVALID) + { + *codepoint = page_map[i].major * page_t::PAGE_BITS + m; + return true; + } + } + *codepoint = INVALID; + return false; + } + bool next_range (hb_codepoint_t *first, hb_codepoint_t *last) const + { + hb_codepoint_t i; + + i = *last; + if (!next (&i)) + { + *last = *first = INVALID; + return false; + } + + /* TODO Speed up. */ + *last = *first = i; + while (next (&i) && i == *last + 1) + (*last)++; + + return true; + } + bool previous_range (hb_codepoint_t *first, hb_codepoint_t *last) const + { + hb_codepoint_t i; + + i = *first; + if (!previous (&i)) + { + *last = *first = INVALID; + return false; + } + + /* TODO Speed up. */ + *last = *first = i; + while (previous (&i) && i == *first - 1) + (*first)--; + + return true; + } + + unsigned int next_many (hb_codepoint_t codepoint, + hb_codepoint_t *out, + unsigned int size) const + { + // By default, start at the first bit of the first page of values. + unsigned int start_page = 0; + unsigned int start_page_value = 0; + if (unlikely (codepoint != INVALID)) + { + const auto* page_map_array = page_map.arrayZ; + unsigned int major = get_major (codepoint); + unsigned int i = last_page_lookup; + if (unlikely (i >= page_map.length || page_map_array[i].major != major)) + { + page_map.bfind (major, &i, HB_NOT_FOUND_STORE_CLOSEST); + if (i >= page_map.length) + return 0; // codepoint is greater than our max element. + } + start_page = i; + start_page_value = page_remainder (codepoint + 1); + if (unlikely (start_page_value == 0)) + { + // The export-after value was last in the page. Start on next page. + start_page++; + start_page_value = 0; + } + } + + unsigned int initial_size = size; + for (unsigned int i = start_page; i < page_map.length && size; i++) + { + uint32_t base = major_start (page_map[i].major); + unsigned int n = pages[page_map[i].index].write (base, start_page_value, out, size); + out += n; + size -= n; + start_page_value = 0; + } + return initial_size - size; + } + + unsigned int next_many_inverted (hb_codepoint_t codepoint, + hb_codepoint_t *out, + unsigned int size) const + { + unsigned int initial_size = size; + // By default, start at the first bit of the first page of values. + unsigned int start_page = 0; + unsigned int start_page_value = 0; + if (unlikely (codepoint != INVALID)) + { + const auto* page_map_array = page_map.arrayZ; + unsigned int major = get_major (codepoint); + unsigned int i = last_page_lookup; + if (unlikely (i >= page_map.length || page_map_array[i].major != major)) + { + page_map.bfind(major, &i, HB_NOT_FOUND_STORE_CLOSEST); + if (unlikely (i >= page_map.length)) + { + // codepoint is greater than our max element. + while (++codepoint != INVALID && size) + { + *out++ = codepoint; + size--; + } + return initial_size - size; + } + } + start_page = i; + start_page_value = page_remainder (codepoint + 1); + if (unlikely (start_page_value == 0)) + { + // The export-after value was last in the page. Start on next page. + start_page++; + start_page_value = 0; + } + } + + hb_codepoint_t next_value = codepoint + 1; + for (unsigned int i=start_page; i= 0; i--) + { + const auto& map = page_map[(unsigned) i]; + const auto& page = pages[map.index]; + + if (!page.is_empty ()) + return map.major * page_t::PAGE_BITS + page.get_max (); + } + return INVALID; + } + + static constexpr hb_codepoint_t INVALID = page_t::INVALID; + + /* + * Iterator implementation. + */ + struct iter_t : hb_iter_with_fallback_t + { + static constexpr bool is_sorted_iterator = true; + iter_t (const hb_bit_set_t &s_ = Null (hb_bit_set_t), + bool init = true) : s (&s_), v (INVALID), l(0) + { + if (init) + { + l = s->get_population () + 1; + __next__ (); + } + } + + typedef hb_codepoint_t __item_t__; + hb_codepoint_t __item__ () const { return v; } + bool __more__ () const { return v != INVALID; } + void __next__ () { s->next (&v); if (l) l--; } + void __prev__ () { s->previous (&v); } + unsigned __len__ () const { return l; } + iter_t end () const { return iter_t (*s, false); } + bool operator != (const iter_t& o) const + { return s != o.s || v != o.v; } + + protected: + const hb_bit_set_t *s; + hb_codepoint_t v; + unsigned l; + }; + iter_t iter () const { return iter_t (*this); } + operator iter_t () const { return iter (); } + + protected: + + page_t *page_for (hb_codepoint_t g, bool insert = false) + { + unsigned major = get_major (g); + + /* The extra page_map length is necessary; can't just rely on vector here, + * since the next check would be tricked because a null page also has + * major==0, which we can't distinguish from an actualy major==0 page... */ + unsigned i = last_page_lookup; + if (likely (i < page_map.length)) + { + auto &cached_page = page_map.arrayZ[i]; + if (cached_page.major == major) + return &pages[cached_page.index]; + } + + page_map_t map = {major, pages.length}; + if (!page_map.bfind (map, &i, HB_NOT_FOUND_STORE_CLOSEST)) + { + if (!insert) + return nullptr; + + if (unlikely (!resize (pages.length + 1))) + return nullptr; + + pages[map.index].init0 (); + memmove (page_map + i + 1, + page_map + i, + (page_map.length - 1 - i) * page_map.item_size); + page_map[i] = map; + } + + last_page_lookup = i; + return &pages[page_map[i].index]; + } + const page_t *page_for (hb_codepoint_t g) const + { + unsigned major = get_major (g); + + /* The extra page_map length is necessary; can't just rely on vector here, + * since the next check would be tricked because a null page also has + * major==0, which we can't distinguish from an actualy major==0 page... */ + unsigned i = last_page_lookup; + if (likely (i < page_map.length)) + { + auto &cached_page = page_map.arrayZ[i]; + if (cached_page.major == major) + return &pages[cached_page.index]; + } + + page_map_t key = {major}; + if (!page_map.bfind (key, &i)) + return nullptr; + + last_page_lookup = i; + return &pages[page_map[i].index]; + } + page_t &page_at (unsigned int i) { return pages[page_map[i].index]; } + const page_t &page_at (unsigned int i) const { return pages[page_map[i].index]; } + unsigned int get_major (hb_codepoint_t g) const { return g >> page_t::PAGE_BITS_LOG_2; } + unsigned int page_remainder (hb_codepoint_t g) const { return g & page_t::PAGE_BITMASK; } + hb_codepoint_t major_start (unsigned int major) const { return major << page_t::PAGE_BITS_LOG_2; } +}; + + +#endif /* HB_BIT_SET_HH */ diff --git a/libs/harfbuzz/src/hb-blob.cc b/libs/harfbuzz/src/hb-blob.cc new file mode 100644 index 00000000..9bc12ea3 --- /dev/null +++ b/libs/harfbuzz/src/hb-blob.cc @@ -0,0 +1,775 @@ +/* + * Copyright © 2009 Red Hat, Inc. + * Copyright © 2018 Ebrahim Byagowi + * + * 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. + * + * Red Hat Author(s): Behdad Esfahbod + */ + +#include "hb.hh" +#include "hb-blob.hh" + +#ifdef HAVE_SYS_MMAN_H +#ifdef HAVE_UNISTD_H +#include +#endif /* HAVE_UNISTD_H */ +#include +#endif /* HAVE_SYS_MMAN_H */ + + +/** + * SECTION: hb-blob + * @title: hb-blob + * @short_description: Binary data containers + * @include: hb.h + * + * Blobs wrap a chunk of binary data to handle lifecycle management of data + * while it is passed between client and HarfBuzz. Blobs are primarily used + * to create font faces, but also to access font face tables, as well as + * pass around other binary data. + **/ + + +/** + * hb_blob_create: (skip) + * @data: Pointer to blob data. + * @length: Length of @data in bytes. + * @mode: Memory mode for @data. + * @user_data: Data parameter to pass to @destroy. + * @destroy: (nullable): Callback to call when @data is not needed anymore. + * + * Creates a new "blob" object wrapping @data. The @mode parameter is used + * to negotiate ownership and lifecycle of @data. + * + * Return value: New blob, or the empty blob if something failed or if @length is + * zero. Destroy with hb_blob_destroy(). + * + * Since: 0.9.2 + **/ +hb_blob_t * +hb_blob_create (const char *data, + unsigned int length, + hb_memory_mode_t mode, + void *user_data, + hb_destroy_func_t destroy) +{ + if (!length) + { + if (destroy) + destroy (user_data); + return hb_blob_get_empty (); + } + + hb_blob_t *blob = hb_blob_create_or_fail (data, length, mode, + user_data, destroy); + return likely (blob) ? blob : hb_blob_get_empty (); +} + +/** + * hb_blob_create_or_fail: (skip) + * @data: Pointer to blob data. + * @length: Length of @data in bytes. + * @mode: Memory mode for @data. + * @user_data: Data parameter to pass to @destroy. + * @destroy: (nullable): Callback to call when @data is not needed anymore. + * + * Creates a new "blob" object wrapping @data. The @mode parameter is used + * to negotiate ownership and lifecycle of @data. + * + * Note that this function returns a freshly-allocated empty blob even if @length + * is zero. This is in contrast to hb_blob_create(), which returns the singleton + * empty blob (as returned by hb_blob_get_empty()) if @length is zero. + * + * Return value: New blob, or `NULL` if failed. Destroy with hb_blob_destroy(). + * + * Since: 2.8.2 + **/ +hb_blob_t * +hb_blob_create_or_fail (const char *data, + unsigned int length, + hb_memory_mode_t mode, + void *user_data, + hb_destroy_func_t destroy) +{ + hb_blob_t *blob; + + if (length >= 1u << 31 || + !(blob = hb_object_create ())) + { + if (destroy) + destroy (user_data); + return nullptr; + } + + blob->data = data; + blob->length = length; + blob->mode = mode; + + blob->user_data = user_data; + blob->destroy = destroy; + + if (blob->mode == HB_MEMORY_MODE_DUPLICATE) { + blob->mode = HB_MEMORY_MODE_READONLY; + if (!blob->try_make_writable ()) + { + hb_blob_destroy (blob); + return nullptr; + } + } + + return blob; +} + +static void +_hb_blob_destroy (void *data) +{ + hb_blob_destroy ((hb_blob_t *) data); +} + +/** + * hb_blob_create_sub_blob: + * @parent: Parent blob. + * @offset: Start offset of sub-blob within @parent, in bytes. + * @length: Length of sub-blob. + * + * Returns a blob that represents a range of bytes in @parent. The new + * blob is always created with #HB_MEMORY_MODE_READONLY, meaning that it + * will never modify data in the parent blob. The parent data is not + * expected to be modified, and will result in undefined behavior if it + * is. + * + * Makes @parent immutable. + * + * Return value: New blob, or the empty blob if something failed or if + * @length is zero or @offset is beyond the end of @parent's data. Destroy + * with hb_blob_destroy(). + * + * Since: 0.9.2 + **/ +hb_blob_t * +hb_blob_create_sub_blob (hb_blob_t *parent, + unsigned int offset, + unsigned int length) +{ + hb_blob_t *blob; + + if (!length || !parent || offset >= parent->length) + return hb_blob_get_empty (); + + hb_blob_make_immutable (parent); + + blob = hb_blob_create (parent->data + offset, + hb_min (length, parent->length - offset), + HB_MEMORY_MODE_READONLY, + hb_blob_reference (parent), + _hb_blob_destroy); + + return blob; +} + +/** + * hb_blob_copy_writable_or_fail: + * @blob: A blob. + * + * Makes a writable copy of @blob. + * + * Return value: The new blob, or nullptr if allocation failed + * + * Since: 1.8.0 + **/ +hb_blob_t * +hb_blob_copy_writable_or_fail (hb_blob_t *blob) +{ + blob = hb_blob_create (blob->data, + blob->length, + HB_MEMORY_MODE_DUPLICATE, + nullptr, + nullptr); + + if (unlikely (blob == hb_blob_get_empty ())) + blob = nullptr; + + return blob; +} + +/** + * hb_blob_get_empty: + * + * Returns the singleton empty blob. + * + * See TODO:link object types for more information. + * + * Return value: (transfer full): The empty blob. + * + * Since: 0.9.2 + **/ +hb_blob_t * +hb_blob_get_empty () +{ + return const_cast (&Null (hb_blob_t)); +} + +/** + * hb_blob_reference: (skip) + * @blob: a blob. + * + * Increases the reference count on @blob. + * + * See TODO:link object types for more information. + * + * Return value: @blob. + * + * Since: 0.9.2 + **/ +hb_blob_t * +hb_blob_reference (hb_blob_t *blob) +{ + return hb_object_reference (blob); +} + +/** + * hb_blob_destroy: (skip) + * @blob: a blob. + * + * Decreases the reference count on @blob, and if it reaches zero, destroys + * @blob, freeing all memory, possibly calling the destroy-callback the blob + * was created for if it has not been called already. + * + * See TODO:link object types for more information. + * + * Since: 0.9.2 + **/ +void +hb_blob_destroy (hb_blob_t *blob) +{ + if (!hb_object_destroy (blob)) return; + + hb_free (blob); +} + +/** + * hb_blob_set_user_data: (skip) + * @blob: An #hb_blob_t + * @key: The user-data key to set + * @data: A pointer to the user data to set + * @destroy: (nullable): A callback to call when @data is not needed anymore + * @replace: Whether to replace an existing data with the same key + * + * Attaches a user-data key/data pair to the specified blob. + * + * Return value: `true` if success, `false` otherwise + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_blob_set_user_data (hb_blob_t *blob, + hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy, + hb_bool_t replace) +{ + return hb_object_set_user_data (blob, key, data, destroy, replace); +} + +/** + * hb_blob_get_user_data: (skip) + * @blob: a blob + * @key: The user-data key to query + * + * Fetches the user data associated with the specified key, + * attached to the specified font-functions structure. + * + * Return value: (transfer none): A pointer to the user data + * + * Since: 0.9.2 + **/ +void * +hb_blob_get_user_data (const hb_blob_t *blob, + hb_user_data_key_t *key) +{ + return hb_object_get_user_data (blob, key); +} + + +/** + * hb_blob_make_immutable: + * @blob: a blob + * + * Makes a blob immutable. + * + * Since: 0.9.2 + **/ +void +hb_blob_make_immutable (hb_blob_t *blob) +{ + if (hb_object_is_immutable (blob)) + return; + + hb_object_make_immutable (blob); +} + +/** + * hb_blob_is_immutable: + * @blob: a blob. + * + * Tests whether a blob is immutable. + * + * Return value: `true` if @blob is immutable, `false` otherwise + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_blob_is_immutable (hb_blob_t *blob) +{ + return hb_object_is_immutable (blob); +} + + +/** + * hb_blob_get_length: + * @blob: a blob. + * + * Fetches the length of a blob's data. + * + * Return value: the length of @blob data in bytes. + * + * Since: 0.9.2 + **/ +unsigned int +hb_blob_get_length (hb_blob_t *blob) +{ + return blob->length; +} + +/** + * hb_blob_get_data: + * @blob: a blob. + * @length: (out): The length in bytes of the data retrieved + * + * Fetches the data from a blob. + * + * Returns: (nullable) (transfer none) (array length=length): the byte data of @blob. + * + * Since: 0.9.2 + **/ +const char * +hb_blob_get_data (hb_blob_t *blob, unsigned int *length) +{ + if (length) + *length = blob->length; + + return blob->data; +} + +/** + * hb_blob_get_data_writable: + * @blob: a blob. + * @length: (out): output length of the writable data. + * + * Tries to make blob data writable (possibly copying it) and + * return pointer to data. + * + * Fails if blob has been made immutable, or if memory allocation + * fails. + * + * Returns: (transfer none) (array length=length): Writable blob data, + * or `NULL` if failed. + * + * Since: 0.9.2 + **/ +char * +hb_blob_get_data_writable (hb_blob_t *blob, unsigned int *length) +{ + if (hb_object_is_immutable (blob) || + !blob->try_make_writable ()) + { + if (length) *length = 0; + return nullptr; + } + + if (length) *length = blob->length; + return const_cast (blob->data); +} + + +bool +hb_blob_t::try_make_writable_inplace_unix () +{ +#if defined(HAVE_SYS_MMAN_H) && defined(HAVE_MPROTECT) + uintptr_t pagesize = -1, mask, length; + const char *addr; + +#if defined(HAVE_SYSCONF) && defined(_SC_PAGE_SIZE) + pagesize = (uintptr_t) sysconf (_SC_PAGE_SIZE); +#elif defined(HAVE_SYSCONF) && defined(_SC_PAGESIZE) + pagesize = (uintptr_t) sysconf (_SC_PAGESIZE); +#elif defined(HAVE_GETPAGESIZE) + pagesize = (uintptr_t) getpagesize (); +#endif + + if ((uintptr_t) -1L == pagesize) { + DEBUG_MSG_FUNC (BLOB, this, "failed to get pagesize: %s", strerror (errno)); + return false; + } + DEBUG_MSG_FUNC (BLOB, this, "pagesize is %lu", (unsigned long) pagesize); + + mask = ~(pagesize-1); + addr = (const char *) (((uintptr_t) this->data) & mask); + length = (const char *) (((uintptr_t) this->data + this->length + pagesize-1) & mask) - addr; + DEBUG_MSG_FUNC (BLOB, this, + "calling mprotect on [%p..%p] (%lu bytes)", + addr, addr+length, (unsigned long) length); + if (-1 == mprotect ((void *) addr, length, PROT_READ | PROT_WRITE)) { + DEBUG_MSG_FUNC (BLOB, this, "mprotect failed: %s", strerror (errno)); + return false; + } + + this->mode = HB_MEMORY_MODE_WRITABLE; + + DEBUG_MSG_FUNC (BLOB, this, + "successfully made [%p..%p] (%lu bytes) writable\n", + addr, addr+length, (unsigned long) length); + return true; +#else + return false; +#endif +} + +bool +hb_blob_t::try_make_writable_inplace () +{ + DEBUG_MSG_FUNC (BLOB, this, "making writable inplace\n"); + + if (this->try_make_writable_inplace_unix ()) + return true; + + DEBUG_MSG_FUNC (BLOB, this, "making writable -> FAILED\n"); + + /* Failed to make writable inplace, mark that */ + this->mode = HB_MEMORY_MODE_READONLY; + return false; +} + +bool +hb_blob_t::try_make_writable () +{ + if (unlikely (!length)) + mode = HB_MEMORY_MODE_WRITABLE; + + if (this->mode == HB_MEMORY_MODE_WRITABLE) + return true; + + if (this->mode == HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE && this->try_make_writable_inplace ()) + return true; + + if (this->mode == HB_MEMORY_MODE_WRITABLE) + return true; + + + DEBUG_MSG_FUNC (BLOB, this, "current data is -> %p\n", this->data); + + char *new_data; + + new_data = (char *) hb_malloc (this->length); + if (unlikely (!new_data)) + return false; + + DEBUG_MSG_FUNC (BLOB, this, "dupped successfully -> %p\n", this->data); + + memcpy (new_data, this->data, this->length); + this->destroy_user_data (); + this->mode = HB_MEMORY_MODE_WRITABLE; + this->data = new_data; + this->user_data = new_data; + this->destroy = hb_free; + + return true; +} + +/* + * Mmap + */ + +#ifndef HB_NO_OPEN +#ifdef HAVE_MMAP +# if !defined(HB_NO_RESOURCE_FORK) && defined(__APPLE__) +# include +# endif +# include +# include +# include +#endif + +#ifdef _WIN32 +# include +#else +# ifndef O_BINARY +# define O_BINARY 0 +# endif +#endif + +#ifndef MAP_NORESERVE +# define MAP_NORESERVE 0 +#endif + +struct hb_mapped_file_t +{ + char *contents; + unsigned long length; +#ifdef _WIN32 + HANDLE mapping; +#endif +}; + +#if (defined(HAVE_MMAP) || defined(_WIN32)) && !defined(HB_NO_MMAP) +static void +_hb_mapped_file_destroy (void *file_) +{ + hb_mapped_file_t *file = (hb_mapped_file_t *) file_; +#ifdef HAVE_MMAP + munmap (file->contents, file->length); +#elif defined(_WIN32) + UnmapViewOfFile (file->contents); + CloseHandle (file->mapping); +#else + assert (0); // If we don't have mmap we shouldn't reach here +#endif + + hb_free (file); +} +#endif + +#ifdef _PATH_RSRCFORKSPEC +static int +_open_resource_fork (const char *file_name, hb_mapped_file_t *file) +{ + size_t name_len = strlen (file_name); + size_t len = name_len + sizeof (_PATH_RSRCFORKSPEC); + + char *rsrc_name = (char *) hb_malloc (len); + if (unlikely (!rsrc_name)) return -1; + + strncpy (rsrc_name, file_name, name_len); + strncpy (rsrc_name + name_len, _PATH_RSRCFORKSPEC, + sizeof (_PATH_RSRCFORKSPEC)); + + int fd = open (rsrc_name, O_RDONLY | O_BINARY, 0); + hb_free (rsrc_name); + + if (fd != -1) + { + struct stat st; + if (fstat (fd, &st) != -1) + file->length = (unsigned long) st.st_size; + else + { + close (fd); + fd = -1; + } + } + + return fd; +} +#endif + +/** + * hb_blob_create_from_file: + * @file_name: A font filename + * + * Creates a new blob containing the data from the + * specified binary font file. + * + * Returns: An #hb_blob_t pointer with the content of the file, + * or hb_blob_get_empty() if failed. + * + * Since: 1.7.7 + **/ +hb_blob_t * +hb_blob_create_from_file (const char *file_name) +{ + hb_blob_t *blob = hb_blob_create_from_file_or_fail (file_name); + return likely (blob) ? blob : hb_blob_get_empty (); +} + +/** + * hb_blob_create_from_file_or_fail: + * @file_name: A font filename + * + * Creates a new blob containing the data from the + * specified binary font file. + * + * Returns: An #hb_blob_t pointer with the content of the file, + * or `NULL` if failed. + * + * Since: 2.8.2 + **/ +hb_blob_t * +hb_blob_create_from_file_or_fail (const char *file_name) +{ + /* Adopted from glib's gmappedfile.c with Matthias Clasen and + Allison Lortie permission but changed a lot to suit our need. */ +#if defined(HAVE_MMAP) && !defined(HB_NO_MMAP) + hb_mapped_file_t *file = (hb_mapped_file_t *) hb_calloc (1, sizeof (hb_mapped_file_t)); + if (unlikely (!file)) return nullptr; + + int fd = open (file_name, O_RDONLY | O_BINARY, 0); + if (unlikely (fd == -1)) goto fail_without_close; + + struct stat st; + if (unlikely (fstat (fd, &st) == -1)) goto fail; + + file->length = (unsigned long) st.st_size; + +#ifdef _PATH_RSRCFORKSPEC + if (unlikely (file->length == 0)) + { + int rfd = _open_resource_fork (file_name, file); + if (rfd != -1) + { + close (fd); + fd = rfd; + } + } +#endif + + file->contents = (char *) mmap (nullptr, file->length, PROT_READ, + MAP_PRIVATE | MAP_NORESERVE, fd, 0); + + if (unlikely (file->contents == MAP_FAILED)) goto fail; + + close (fd); + + return hb_blob_create_or_fail (file->contents, file->length, + HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE, (void *) file, + (hb_destroy_func_t) _hb_mapped_file_destroy); + +fail: + close (fd); +fail_without_close: + hb_free (file); + +#elif defined(_WIN32) && !defined(HB_NO_MMAP) + hb_mapped_file_t *file = (hb_mapped_file_t *) hb_calloc (1, sizeof (hb_mapped_file_t)); + if (unlikely (!file)) return nullptr; + + HANDLE fd; + unsigned int size = strlen (file_name) + 1; + wchar_t * wchar_file_name = (wchar_t *) hb_malloc (sizeof (wchar_t) * size); + if (unlikely (!wchar_file_name)) goto fail_without_close; + mbstowcs (wchar_file_name, file_name, size); +#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + { + CREATEFILE2_EXTENDED_PARAMETERS ceparams = { 0 }; + ceparams.dwSize = sizeof(CREATEFILE2_EXTENDED_PARAMETERS); + ceparams.dwFileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED & 0xFFFF; + ceparams.dwFileFlags = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED & 0xFFF00000; + ceparams.dwSecurityQosFlags = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED & 0x000F0000; + ceparams.lpSecurityAttributes = nullptr; + ceparams.hTemplateFile = nullptr; + fd = CreateFile2 (wchar_file_name, GENERIC_READ, FILE_SHARE_READ, + OPEN_EXISTING, &ceparams); + } +#else + fd = CreateFileW (wchar_file_name, GENERIC_READ, FILE_SHARE_READ, nullptr, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED, + nullptr); +#endif + hb_free (wchar_file_name); + + if (unlikely (fd == INVALID_HANDLE_VALUE)) goto fail_without_close; + +#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + { + LARGE_INTEGER length; + GetFileSizeEx (fd, &length); + file->length = length.LowPart; + file->mapping = CreateFileMappingFromApp (fd, nullptr, PAGE_READONLY, length.QuadPart, nullptr); + } +#else + file->length = (unsigned long) GetFileSize (fd, nullptr); + file->mapping = CreateFileMapping (fd, nullptr, PAGE_READONLY, 0, 0, nullptr); +#endif + if (unlikely (!file->mapping)) goto fail; + +#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + file->contents = (char *) MapViewOfFileFromApp (file->mapping, FILE_MAP_READ, 0, 0); +#else + file->contents = (char *) MapViewOfFile (file->mapping, FILE_MAP_READ, 0, 0, 0); +#endif + if (unlikely (!file->contents)) goto fail; + + CloseHandle (fd); + return hb_blob_create_or_fail (file->contents, file->length, + HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE, (void *) file, + (hb_destroy_func_t) _hb_mapped_file_destroy); + +fail: + CloseHandle (fd); +fail_without_close: + hb_free (file); + +#endif + + /* The following tries to read a file without knowing its size beforehand + It's used as a fallback for systems without mmap or to read from pipes */ + unsigned long len = 0, allocated = BUFSIZ * 16; + char *data = (char *) hb_malloc (allocated); + if (unlikely (!data)) return nullptr; + + FILE *fp = fopen (file_name, "rb"); + if (unlikely (!fp)) goto fread_fail_without_close; + + while (!feof (fp)) + { + if (allocated - len < BUFSIZ) + { + allocated *= 2; + /* Don't allocate and go more than ~536MB, our mmap reader still + can cover files like that but lets limit our fallback reader */ + if (unlikely (allocated > (2 << 28))) goto fread_fail; + char *new_data = (char *) hb_realloc (data, allocated); + if (unlikely (!new_data)) goto fread_fail; + data = new_data; + } + + unsigned long addition = fread (data + len, 1, allocated - len, fp); + + int err = ferror (fp); +#ifdef EINTR // armcc doesn't have it + if (unlikely (err == EINTR)) continue; +#endif + if (unlikely (err)) goto fread_fail; + + len += addition; + } + fclose (fp); + + return hb_blob_create_or_fail (data, len, HB_MEMORY_MODE_WRITABLE, data, + (hb_destroy_func_t) hb_free); + +fread_fail: + fclose (fp); +fread_fail_without_close: + hb_free (data); + return nullptr; +} +#endif /* !HB_NO_OPEN */ diff --git a/libs/harfbuzz/src/hb-blob.h b/libs/harfbuzz/src/hb-blob.h new file mode 100644 index 00000000..4eb42314 --- /dev/null +++ b/libs/harfbuzz/src/hb-blob.h @@ -0,0 +1,160 @@ +/* + * Copyright © 2009 Red Hat, 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. + * + * Red Hat Author(s): Behdad Esfahbod + */ + +#if !defined(HB_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR) +#error "Include instead." +#endif + +#ifndef HB_BLOB_H +#define HB_BLOB_H + +#include "hb-common.h" + +HB_BEGIN_DECLS + + +/** + * hb_memory_mode_t: + * @HB_MEMORY_MODE_DUPLICATE: HarfBuzz immediately makes a copy of the data. + * @HB_MEMORY_MODE_READONLY: HarfBuzz client will never modify the data, + * and HarfBuzz will never modify the data. + * @HB_MEMORY_MODE_WRITABLE: HarfBuzz client made a copy of the data solely + * for HarfBuzz, so HarfBuzz may modify the data. + * @HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE: See above + * + * Data type holding the memory modes available to + * client programs. + * + * Regarding these various memory-modes: + * + * - In no case shall the HarfBuzz client modify memory + * that is passed to HarfBuzz in a blob. If there is + * any such possibility, @HB_MEMORY_MODE_DUPLICATE should be used + * such that HarfBuzz makes a copy immediately, + * + * - Use @HB_MEMORY_MODE_READONLY otherwise, unless you really really + * really know what you are doing, + * + * - @HB_MEMORY_MODE_WRITABLE is appropriate if you really made a + * copy of data solely for the purpose of passing to + * HarfBuzz and doing that just once (no reuse!), + * + * - If the font is mmap()ed, it's okay to use + * @HB_MEMORY_READONLY_MAY_MAKE_WRITABLE, however, using that mode + * correctly is very tricky. Use @HB_MEMORY_MODE_READONLY instead. + **/ +typedef enum { + HB_MEMORY_MODE_DUPLICATE, + HB_MEMORY_MODE_READONLY, + HB_MEMORY_MODE_WRITABLE, + HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE +} hb_memory_mode_t; + +/** + * hb_blob_t: + * + * Data type for blobs. A blob wraps a chunk of binary + * data and facilitates its lifecycle management between + * a client program and HarfBuzz. + * + **/ +typedef struct hb_blob_t hb_blob_t; + +HB_EXTERN hb_blob_t * +hb_blob_create (const char *data, + unsigned int length, + hb_memory_mode_t mode, + void *user_data, + hb_destroy_func_t destroy); + +HB_EXTERN hb_blob_t * +hb_blob_create_or_fail (const char *data, + unsigned int length, + hb_memory_mode_t mode, + void *user_data, + hb_destroy_func_t destroy); + +HB_EXTERN hb_blob_t * +hb_blob_create_from_file (const char *file_name); + +HB_EXTERN hb_blob_t * +hb_blob_create_from_file_or_fail (const char *file_name); + +/* Always creates with MEMORY_MODE_READONLY. + * Even if the parent blob is writable, we don't + * want the user of the sub-blob to be able to + * modify the parent data as that data may be + * shared among multiple sub-blobs. + */ +HB_EXTERN hb_blob_t * +hb_blob_create_sub_blob (hb_blob_t *parent, + unsigned int offset, + unsigned int length); + +HB_EXTERN hb_blob_t * +hb_blob_copy_writable_or_fail (hb_blob_t *blob); + +HB_EXTERN hb_blob_t * +hb_blob_get_empty (void); + +HB_EXTERN hb_blob_t * +hb_blob_reference (hb_blob_t *blob); + +HB_EXTERN void +hb_blob_destroy (hb_blob_t *blob); + +HB_EXTERN hb_bool_t +hb_blob_set_user_data (hb_blob_t *blob, + hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy, + hb_bool_t replace); + + +HB_EXTERN void * +hb_blob_get_user_data (const hb_blob_t *blob, + hb_user_data_key_t *key); + + +HB_EXTERN void +hb_blob_make_immutable (hb_blob_t *blob); + +HB_EXTERN hb_bool_t +hb_blob_is_immutable (hb_blob_t *blob); + + +HB_EXTERN unsigned int +hb_blob_get_length (hb_blob_t *blob); + +HB_EXTERN const char * +hb_blob_get_data (hb_blob_t *blob, unsigned int *length); + +HB_EXTERN char * +hb_blob_get_data_writable (hb_blob_t *blob, unsigned int *length); + +HB_END_DECLS + +#endif /* HB_BLOB_H */ diff --git a/libs/harfbuzz/src/hb-blob.hh b/libs/harfbuzz/src/hb-blob.hh new file mode 100644 index 00000000..b1b3b94d --- /dev/null +++ b/libs/harfbuzz/src/hb-blob.hh @@ -0,0 +1,98 @@ +/* + * Copyright © 2009 Red Hat, Inc. + * Copyright © 2018 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. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_BLOB_HH +#define HB_BLOB_HH + +#include "hb.hh" + + +/* + * hb_blob_t + */ + +struct hb_blob_t +{ + ~hb_blob_t () { destroy_user_data (); } + + void destroy_user_data () + { + if (destroy) + { + destroy (user_data); + user_data = nullptr; + destroy = nullptr; + } + } + + HB_INTERNAL bool try_make_writable (); + HB_INTERNAL bool try_make_writable_inplace (); + HB_INTERNAL bool try_make_writable_inplace_unix (); + + hb_bytes_t as_bytes () const { return hb_bytes_t (data, length); } + template + const Type* as () const { return as_bytes ().as (); } + + public: + hb_object_header_t header; + + const char *data = nullptr; + unsigned int length = 0; + hb_memory_mode_t mode = (hb_memory_mode_t) 0; + + void *user_data = nullptr; + hb_destroy_func_t destroy = nullptr; +}; + + +/* + * hb_blob_ptr_t + */ + +template +struct hb_blob_ptr_t +{ + typedef hb_remove_pointer

T; + + hb_blob_ptr_t (hb_blob_t *b_ = nullptr) : b (b_) {} + hb_blob_t * operator = (hb_blob_t *b_) { return b = b_; } + const T * operator -> () const { return get (); } + const T & operator * () const { return *get (); } + template operator const C * () const { return get (); } + operator const char * () const { return (const char *) get (); } + const T * get () const { return b->as (); } + hb_blob_t * get_blob () const { return b.get_raw (); } + unsigned int get_length () const { return b.get ()->length; } + void destroy () { hb_blob_destroy (b.get_raw ()); b = nullptr; } + + private: + hb_nonnull_ptr_t b; +}; + + +#endif /* HB_BLOB_HH */ diff --git a/libs/harfbuzz/src/hb-buffer-deserialize-json.hh b/libs/harfbuzz/src/hb-buffer-deserialize-json.hh new file mode 100644 index 00000000..87095855 --- /dev/null +++ b/libs/harfbuzz/src/hb-buffer-deserialize-json.hh @@ -0,0 +1,797 @@ + +#line 1 "hb-buffer-deserialize-json.rl" +/* + * Copyright © 2013 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 HB_BUFFER_DESERIALIZE_JSON_HH +#define HB_BUFFER_DESERIALIZE_JSON_HH + +#include "hb.hh" + + +#line 36 "hb-buffer-deserialize-json.hh" +static const unsigned char _deserialize_json_trans_keys[] = { + 0u, 0u, 9u, 123u, 9u, 34u, 97u, 117u, 120u, 121u, 34u, 34u, 9u, 58u, 9u, 57u, + 48u, 57u, 9u, 125u, 9u, 125u, 9u, 125u, 34u, 34u, 9u, 58u, 9u, 57u, 48u, 57u, + 9u, 125u, 9u, 125u, 108u, 108u, 34u, 34u, 9u, 58u, 9u, 57u, 9u, 125u, 9u, 125u, + 120u, 121u, 34u, 34u, 9u, 58u, 9u, 57u, 48u, 57u, 9u, 125u, 9u, 125u, 34u, 34u, + 9u, 58u, 9u, 57u, 48u, 57u, 9u, 125u, 9u, 125u, 108u, 108u, 34u, 34u, 9u, 58u, + 9u, 57u, 9u, 125u, 9u, 125u, 34u, 34u, 9u, 58u, 9u, 57u, 34u, 92u, 9u, 125u, + 34u, 92u, 9u, 125u, 9u, 125u, 34u, 34u, 9u, 58u, 9u, 57u, 9u, 125u, 9u, 93u, + 9u, 123u, 0u, 0u, 0 +}; + +static const char _deserialize_json_key_spans[] = { + 0, 115, 26, 21, 2, 1, 50, 49, + 10, 117, 117, 117, 1, 50, 49, 10, + 117, 117, 1, 1, 50, 49, 117, 117, + 2, 1, 50, 49, 10, 117, 117, 1, + 50, 49, 10, 117, 117, 1, 1, 50, + 49, 117, 117, 1, 50, 49, 59, 117, + 59, 117, 117, 1, 50, 49, 117, 85, + 115, 0 +}; + +static const short _deserialize_json_index_offsets[] = { + 0, 0, 116, 143, 165, 168, 170, 221, + 271, 282, 400, 518, 636, 638, 689, 739, + 750, 868, 986, 988, 990, 1041, 1091, 1209, + 1327, 1330, 1332, 1383, 1433, 1444, 1562, 1680, + 1682, 1733, 1783, 1794, 1912, 2030, 2032, 2034, + 2085, 2135, 2253, 2371, 2373, 2424, 2474, 2534, + 2652, 2712, 2830, 2948, 2950, 3001, 3051, 3169, + 3255, 3371 +}; + +static const char _deserialize_json_indicies[] = { + 0, 0, 0, 0, 0, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 0, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 2, 1, 3, 3, 3, + 3, 3, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 3, 1, 4, 1, + 5, 1, 6, 7, 1, 8, 9, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 10, 1, 11, 12, + 1, 13, 1, 13, 13, 13, 13, 13, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 13, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 14, 1, 14, 14, + 14, 14, 14, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 14, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 15, 1, 1, 16, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 1, + 18, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 1, 20, 20, 20, 20, 20, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 20, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 21, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 22, + 1, 23, 23, 23, 23, 23, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 23, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 3, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 24, 1, 20, + 20, 20, 20, 20, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 20, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 21, 1, 1, 1, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 22, 1, 25, 1, 25, + 25, 25, 25, 25, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 25, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 26, 1, 26, 26, 26, 26, 26, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 26, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 27, 1, + 1, 28, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 1, 30, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 1, 32, + 32, 32, 32, 32, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 32, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 33, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 34, 1, 32, 32, 32, + 32, 32, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 32, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 33, 1, 1, 1, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 34, 1, 35, 1, 36, 1, 36, + 36, 36, 36, 36, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 36, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 37, 1, 37, 37, 37, 37, 37, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 37, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 38, 39, 39, 39, 39, 39, 39, + 39, 39, 39, 1, 40, 40, 40, 40, + 40, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 40, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 41, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 42, 1, 40, 40, 40, 40, 40, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 40, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 41, 1, 1, + 1, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 42, 1, + 44, 45, 1, 46, 1, 46, 46, 46, + 46, 46, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 46, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 47, 1, + 47, 47, 47, 47, 47, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 47, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 48, 1, 1, 49, + 50, 50, 50, 50, 50, 50, 50, 50, + 50, 1, 51, 52, 52, 52, 52, 52, + 52, 52, 52, 52, 1, 53, 53, 53, + 53, 53, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 53, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 54, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 55, 1, 53, 53, 53, 53, 53, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 53, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 54, 1, + 1, 1, 52, 52, 52, 52, 52, 52, + 52, 52, 52, 52, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 55, + 1, 56, 1, 56, 56, 56, 56, 56, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 56, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 57, 1, 57, 57, + 57, 57, 57, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 57, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 58, 1, 1, 59, 60, 60, + 60, 60, 60, 60, 60, 60, 60, 1, + 61, 62, 62, 62, 62, 62, 62, 62, + 62, 62, 1, 63, 63, 63, 63, 63, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 63, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 64, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 65, + 1, 63, 63, 63, 63, 63, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 63, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 64, 1, 1, 1, + 62, 62, 62, 62, 62, 62, 62, 62, + 62, 62, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 65, 1, 66, + 1, 67, 1, 67, 67, 67, 67, 67, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 67, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 68, 1, 68, 68, + 68, 68, 68, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 68, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 69, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 1, + 71, 71, 71, 71, 71, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 71, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 72, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 73, 1, 71, 71, + 71, 71, 71, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 71, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 72, 1, 1, 1, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 73, 1, 75, 1, 75, 75, + 75, 75, 75, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 75, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 76, + 1, 76, 76, 76, 76, 76, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 76, 1, 77, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 78, 79, 79, 79, 79, 79, 79, 79, + 79, 79, 1, 81, 80, 80, 80, 80, + 80, 80, 80, 80, 80, 80, 80, 80, + 80, 80, 80, 80, 80, 80, 80, 80, + 80, 80, 80, 80, 80, 80, 80, 80, + 80, 80, 80, 80, 80, 80, 80, 80, + 80, 80, 80, 80, 80, 80, 80, 80, + 80, 80, 80, 80, 80, 80, 80, 80, + 80, 80, 80, 80, 80, 82, 80, 83, + 83, 83, 83, 83, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 83, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 84, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 85, 1, 80, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 80, + 1, 86, 86, 86, 86, 86, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 86, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 87, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 88, 1, 86, + 86, 86, 86, 86, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 86, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 87, 1, 1, 1, 89, 89, + 89, 89, 89, 89, 89, 89, 89, 89, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 88, 1, 90, 1, 90, + 90, 90, 90, 90, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 90, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 91, 1, 91, 91, 91, 91, 91, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 91, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 92, 93, 93, 93, 93, 93, 93, + 93, 93, 93, 1, 86, 86, 86, 86, + 86, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 86, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 87, + 1, 1, 1, 94, 94, 94, 94, 94, + 94, 94, 94, 94, 94, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 88, 1, 95, 95, 95, 95, 95, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 95, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 96, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 97, 1, + 0, 0, 0, 0, 0, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 0, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 2, 1, 1, 0 +}; + +static const char _deserialize_json_trans_targs[] = { + 1, 0, 2, 2, 3, 4, 18, 24, + 37, 43, 51, 5, 12, 6, 7, 8, + 9, 11, 9, 11, 10, 2, 55, 10, + 55, 13, 14, 15, 16, 17, 16, 17, + 10, 2, 55, 19, 20, 21, 22, 23, + 10, 2, 55, 23, 25, 31, 26, 27, + 28, 29, 30, 29, 30, 10, 2, 55, + 32, 33, 34, 35, 36, 35, 36, 10, + 2, 55, 38, 39, 40, 41, 42, 10, + 2, 55, 42, 44, 45, 46, 49, 50, + 46, 47, 48, 10, 2, 55, 10, 2, + 55, 50, 52, 53, 49, 54, 54, 55, + 56, 57 +}; + +static const char _deserialize_json_trans_actions[] = { + 0, 0, 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 2, + 2, 2, 0, 0, 3, 3, 4, 0, + 5, 0, 0, 2, 2, 2, 0, 0, + 6, 6, 7, 0, 0, 0, 2, 2, + 8, 8, 9, 0, 0, 0, 0, 0, + 2, 2, 2, 0, 0, 10, 10, 11, + 0, 0, 2, 2, 2, 0, 0, 12, + 12, 13, 0, 0, 0, 2, 2, 14, + 14, 15, 0, 0, 0, 2, 16, 16, + 0, 17, 0, 18, 18, 19, 20, 20, + 21, 17, 0, 0, 22, 22, 23, 0, + 0, 0 +}; + +static const int deserialize_json_start = 1; +static const int deserialize_json_first_final = 55; +static const int deserialize_json_error = 0; + +static const int deserialize_json_en_main = 1; + + +#line 111 "hb-buffer-deserialize-json.rl" + + +static hb_bool_t +_hb_buffer_deserialize_json (hb_buffer_t *buffer, + const char *buf, + unsigned int buf_len, + const char **end_ptr, + hb_font_t *font) +{ + const char *p = buf, *pe = buf + buf_len; + + /* Ensure we have positions. */ + (void) hb_buffer_get_glyph_positions (buffer, nullptr); + + while (p < pe && ISSPACE (*p)) + p++; + if (p < pe && *p == (buffer->len ? ',' : '[')) + { + *end_ptr = ++p; + } + + const char *tok = nullptr; + int cs; + hb_glyph_info_t info = {0}; + hb_glyph_position_t pos = {0}; + +#line 561 "hb-buffer-deserialize-json.hh" + { + cs = deserialize_json_start; + } + +#line 566 "hb-buffer-deserialize-json.hh" + { + int _slen; + int _trans; + const unsigned char *_keys; + const char *_inds; + if ( p == pe ) + goto _test_eof; + if ( cs == 0 ) + goto _out; +_resume: + _keys = _deserialize_json_trans_keys + (cs<<1); + _inds = _deserialize_json_indicies + _deserialize_json_index_offsets[cs]; + + _slen = _deserialize_json_key_spans[cs]; + _trans = _inds[ _slen > 0 && _keys[0] <=(*p) && + (*p) <= _keys[1] ? + (*p) - _keys[0] : _slen ]; + + cs = _deserialize_json_trans_targs[_trans]; + + if ( _deserialize_json_trans_actions[_trans] == 0 ) + goto _again; + + switch ( _deserialize_json_trans_actions[_trans] ) { + case 1: +#line 38 "hb-buffer-deserialize-json.rl" + { + memset (&info, 0, sizeof (info)); + memset (&pos , 0, sizeof (pos )); +} + break; + case 5: +#line 43 "hb-buffer-deserialize-json.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 2: +#line 51 "hb-buffer-deserialize-json.rl" + { + tok = p; +} + break; + case 17: +#line 55 "hb-buffer-deserialize-json.rl" + { if (unlikely (!buffer->ensure_glyphs ())) return false; } + break; + case 23: +#line 56 "hb-buffer-deserialize-json.rl" + { if (unlikely (!buffer->ensure_unicode ())) return false; } + break; + case 18: +#line 58 "hb-buffer-deserialize-json.rl" + { + /* TODO Unescape \" and \\ if found. */ + if (!hb_font_glyph_from_string (font, + tok+1, p - tok - 2, /* Skip "" */ + &info.codepoint)) + return false; +} + break; + case 20: +#line 66 "hb-buffer-deserialize-json.rl" + { if (!parse_uint (tok, p, &info.codepoint)) return false; } + break; + case 8: +#line 67 "hb-buffer-deserialize-json.rl" + { if (!parse_uint (tok, p, &info.cluster )) return false; } + break; + case 10: +#line 68 "hb-buffer-deserialize-json.rl" + { if (!parse_int (tok, p, &pos.x_offset )) return false; } + break; + case 12: +#line 69 "hb-buffer-deserialize-json.rl" + { if (!parse_int (tok, p, &pos.y_offset )) return false; } + break; + case 3: +#line 70 "hb-buffer-deserialize-json.rl" + { if (!parse_int (tok, p, &pos.x_advance)) return false; } + break; + case 6: +#line 71 "hb-buffer-deserialize-json.rl" + { if (!parse_int (tok, p, &pos.y_advance)) return false; } + break; + case 14: +#line 72 "hb-buffer-deserialize-json.rl" + { if (!parse_uint (tok, p, &info.mask )) return false; } + break; + case 16: +#line 51 "hb-buffer-deserialize-json.rl" + { + tok = p; +} +#line 55 "hb-buffer-deserialize-json.rl" + { if (unlikely (!buffer->ensure_glyphs ())) return false; } + break; + case 22: +#line 51 "hb-buffer-deserialize-json.rl" + { + tok = p; +} +#line 56 "hb-buffer-deserialize-json.rl" + { if (unlikely (!buffer->ensure_unicode ())) return false; } + break; + case 19: +#line 58 "hb-buffer-deserialize-json.rl" + { + /* TODO Unescape \" and \\ if found. */ + if (!hb_font_glyph_from_string (font, + tok+1, p - tok - 2, /* Skip "" */ + &info.codepoint)) + return false; +} +#line 43 "hb-buffer-deserialize-json.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 21: +#line 66 "hb-buffer-deserialize-json.rl" + { if (!parse_uint (tok, p, &info.codepoint)) return false; } +#line 43 "hb-buffer-deserialize-json.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 9: +#line 67 "hb-buffer-deserialize-json.rl" + { if (!parse_uint (tok, p, &info.cluster )) return false; } +#line 43 "hb-buffer-deserialize-json.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 11: +#line 68 "hb-buffer-deserialize-json.rl" + { if (!parse_int (tok, p, &pos.x_offset )) return false; } +#line 43 "hb-buffer-deserialize-json.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 13: +#line 69 "hb-buffer-deserialize-json.rl" + { if (!parse_int (tok, p, &pos.y_offset )) return false; } +#line 43 "hb-buffer-deserialize-json.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 4: +#line 70 "hb-buffer-deserialize-json.rl" + { if (!parse_int (tok, p, &pos.x_advance)) return false; } +#line 43 "hb-buffer-deserialize-json.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 7: +#line 71 "hb-buffer-deserialize-json.rl" + { if (!parse_int (tok, p, &pos.y_advance)) return false; } +#line 43 "hb-buffer-deserialize-json.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 15: +#line 72 "hb-buffer-deserialize-json.rl" + { if (!parse_uint (tok, p, &info.mask )) return false; } +#line 43 "hb-buffer-deserialize-json.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; +#line 778 "hb-buffer-deserialize-json.hh" + } + +_again: + if ( cs == 0 ) + goto _out; + if ( ++p != pe ) + goto _resume; + _test_eof: {} + _out: {} + } + +#line 139 "hb-buffer-deserialize-json.rl" + + + *end_ptr = p; + + return p == pe && *(p-1) != ']'; +} + +#endif /* HB_BUFFER_DESERIALIZE_JSON_HH */ diff --git a/libs/harfbuzz/src/hb-buffer-deserialize-json.rl b/libs/harfbuzz/src/hb-buffer-deserialize-json.rl new file mode 100644 index 00000000..d5e3c138 --- /dev/null +++ b/libs/harfbuzz/src/hb-buffer-deserialize-json.rl @@ -0,0 +1,146 @@ +/* + * Copyright © 2013 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 HB_BUFFER_DESERIALIZE_JSON_HH +#define HB_BUFFER_DESERIALIZE_JSON_HH + +#include "hb.hh" + +%%{ + +machine deserialize_json; +alphtype unsigned char; +write data; + +action clear_item { + memset (&info, 0, sizeof (info)); + memset (&pos , 0, sizeof (pos )); +} + +action add_item { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + +action tok { + tok = p; +} + +action ensure_glyphs { if (unlikely (!buffer->ensure_glyphs ())) return false; } +action ensure_unicode { if (unlikely (!buffer->ensure_unicode ())) return false; } + +action parse_glyph_name { + /* TODO Unescape \" and \\ if found. */ + if (!hb_font_glyph_from_string (font, + tok+1, p - tok - 2, /* Skip "" */ + &info.codepoint)) + return false; +} + +action parse_codepoint { if (!parse_uint (tok, p, &info.codepoint)) return false; } +action parse_cluster { if (!parse_uint (tok, p, &info.cluster )) return false; } +action parse_x_offset { if (!parse_int (tok, p, &pos.x_offset )) return false; } +action parse_y_offset { if (!parse_int (tok, p, &pos.y_offset )) return false; } +action parse_x_advance { if (!parse_int (tok, p, &pos.x_advance)) return false; } +action parse_y_advance { if (!parse_int (tok, p, &pos.y_advance)) return false; } +action parse_glyph_flags{ if (!parse_uint (tok, p, &info.mask )) return false; } + +unum = '0' | [1-9] digit*; +num = '-'? unum; + +comma = space* ',' space*; +colon = space* ':' space*; + +codepoint = unum; +glyph_name = '"' ([^\\"] | '\\' [\\"])* '"'; + +parse_glyph_name = (glyph_name >tok %parse_glyph_name); +parse_codepoint = (codepoint >tok %parse_codepoint); + +glyph = "\"g\"" colon (parse_glyph_name | parse_codepoint); +unicode = "\"u\"" colon parse_codepoint; +cluster = "\"cl\"" colon (unum >tok %parse_cluster); +xoffset = "\"dx\"" colon (num >tok %parse_x_offset); +yoffset = "\"dy\"" colon (num >tok %parse_y_offset); +xadvance= "\"ax\"" colon (num >tok %parse_x_advance); +yadvance= "\"ay\"" colon (num >tok %parse_y_advance); +glyphflags="\"fl\"" colon (unum >tok %parse_glyph_flags); + +element = glyph @ensure_glyphs + | unicode @ensure_unicode + | cluster + | xoffset + | yoffset + | xadvance + | yadvance + | glyphflags; +item = + ( '{' space* element (comma element)* space* '}') + >clear_item + @add_item + ; + +main := space* item (comma item)* space* (','|']')?; + +}%% + +static hb_bool_t +_hb_buffer_deserialize_json (hb_buffer_t *buffer, + const char *buf, + unsigned int buf_len, + const char **end_ptr, + hb_font_t *font) +{ + const char *p = buf, *pe = buf + buf_len; + + /* Ensure we have positions. */ + (void) hb_buffer_get_glyph_positions (buffer, nullptr); + + while (p < pe && ISSPACE (*p)) + p++; + if (p < pe && *p == (buffer->len ? ',' : '[')) + { + *end_ptr = ++p; + } + + const char *tok = nullptr; + int cs; + hb_glyph_info_t info = {0}; + hb_glyph_position_t pos = {0}; + %%{ + write init; + write exec; + }%% + + *end_ptr = p; + + return p == pe && *(p-1) != ']'; +} + +#endif /* HB_BUFFER_DESERIALIZE_JSON_HH */ diff --git a/libs/harfbuzz/src/hb-buffer-deserialize-text.hh b/libs/harfbuzz/src/hb-buffer-deserialize-text.hh new file mode 100644 index 00000000..9062610d --- /dev/null +++ b/libs/harfbuzz/src/hb-buffer-deserialize-text.hh @@ -0,0 +1,1061 @@ + +#line 1 "hb-buffer-deserialize-text.rl" +/* + * Copyright © 2013 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 HB_BUFFER_DESERIALIZE_TEXT_HH +#define HB_BUFFER_DESERIALIZE_TEXT_HH + +#include "hb.hh" + + +#line 36 "hb-buffer-deserialize-text.hh" +static const unsigned char _deserialize_text_trans_keys[] = { + 0u, 0u, 9u, 91u, 85u, 85u, 43u, 43u, 48u, 102u, 9u, 85u, 48u, 57u, 45u, 57u, + 48u, 57u, 48u, 57u, 48u, 57u, 45u, 57u, 48u, 57u, 44u, 44u, 45u, 57u, 48u, 57u, + 44u, 57u, 43u, 124u, 45u, 57u, 48u, 57u, 9u, 124u, 9u, 124u, 0u, 0u, 9u, 85u, + 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, + 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, + 9u, 124u, 9u, 124u, 9u, 124u, 0 +}; + +static const char _deserialize_text_key_spans[] = { + 0, 83, 1, 1, 55, 77, 10, 13, + 10, 10, 10, 13, 10, 1, 13, 10, + 14, 82, 13, 10, 116, 116, 0, 77, + 116, 116, 116, 116, 116, 116, 116, 116, + 116, 116, 116, 116, 116, 116, 116, 116, + 116, 116, 116 +}; + +static const short _deserialize_text_index_offsets[] = { + 0, 0, 84, 86, 88, 144, 222, 233, + 247, 258, 269, 280, 294, 305, 307, 321, + 332, 347, 430, 444, 455, 572, 689, 690, + 768, 885, 1002, 1119, 1236, 1353, 1470, 1587, + 1704, 1821, 1938, 2055, 2172, 2289, 2406, 2523, + 2640, 2757, 2874 +}; + +static const char _deserialize_text_indicies[] = { + 0, 0, 0, 0, 0, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 0, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 2, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 3, 1, 4, 1, 5, + 1, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 1, 1, 1, 1, 1, + 1, 1, 6, 6, 6, 6, 6, 6, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 6, 6, 6, 6, 6, 6, + 1, 7, 7, 7, 7, 7, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 7, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 4, 1, 8, + 9, 9, 9, 9, 9, 9, 9, 9, + 9, 1, 10, 1, 1, 11, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 1, + 13, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 1, 15, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 1, 17, 18, + 18, 18, 18, 18, 18, 18, 18, 18, + 1, 19, 1, 1, 20, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 1, 22, + 23, 23, 23, 23, 23, 23, 23, 23, + 23, 1, 24, 1, 25, 1, 1, 26, + 27, 27, 27, 27, 27, 27, 27, 27, + 27, 1, 28, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 1, 24, 1, 1, + 1, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 1, 30, 30, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 30, 1, + 1, 30, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 30, 30, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 30, 1, 31, + 1, 1, 32, 33, 33, 33, 33, 33, + 33, 33, 33, 33, 1, 34, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 1, + 36, 36, 36, 36, 36, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 36, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 37, + 37, 37, 37, 37, 37, 37, 37, 37, + 37, 1, 1, 1, 38, 39, 1, 1, + 37, 37, 37, 37, 37, 37, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 37, 37, 37, 37, 37, 37, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 40, 1, 41, 41, 41, + 41, 41, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 41, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 42, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 43, 1, 1, 7, 7, 7, 7, 7, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 7, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 4, + 1, 44, 44, 44, 44, 44, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 44, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 45, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 46, 1, 44, 44, + 44, 44, 44, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 44, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 1, + 1, 1, 1, 45, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 46, 1, 49, 49, 49, 49, 49, + 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 49, 48, 48, 50, 48, 48, + 48, 48, 48, 48, 48, 51, 1, 48, + 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 52, + 48, 48, 53, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 54, 55, + 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 56, 48, + 57, 57, 57, 57, 57, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 57, + 30, 30, 58, 30, 30, 30, 30, 30, + 30, 30, 59, 1, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 60, 30, 30, 61, + 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 62, 63, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 64, 30, 57, 57, 57, + 57, 57, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 57, 30, 30, 58, + 30, 30, 30, 30, 30, 30, 30, 59, + 1, 30, 30, 30, 65, 66, 66, 66, + 66, 66, 66, 66, 66, 66, 30, 30, + 30, 60, 30, 30, 61, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, + 62, 63, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, + 64, 30, 67, 67, 67, 67, 67, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 67, 1, 1, 68, 1, 1, 1, + 1, 1, 1, 1, 1, 69, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 70, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 71, 1, 72, + 72, 72, 72, 72, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 72, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 42, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 73, 1, 74, 74, 74, 74, + 74, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 74, 48, 48, 50, 48, + 48, 48, 48, 48, 48, 48, 51, 1, + 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, + 52, 48, 48, 53, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 54, + 55, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 56, + 48, 75, 75, 75, 75, 75, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 75, 1, 1, 76, 1, 1, 1, 1, + 1, 1, 1, 77, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 78, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 45, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 79, 1, 80, 80, + 80, 80, 80, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 80, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 81, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 82, 1, 80, 80, 80, 80, 80, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 80, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 81, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 82, 1, + 84, 84, 84, 84, 84, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 84, + 1, 1, 85, 1, 1, 1, 1, 1, + 1, 1, 86, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 87, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 88, 1, 84, 84, 84, + 84, 84, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 84, 1, 1, 85, + 1, 1, 1, 1, 1, 1, 1, 86, + 1, 1, 1, 1, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 87, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 88, 1, 75, 75, 75, 75, 75, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 75, 1, 1, 76, 1, 1, 1, + 1, 1, 1, 1, 77, 1, 1, 1, + 1, 89, 89, 89, 89, 89, 89, 89, + 89, 89, 89, 1, 1, 1, 1, 1, + 1, 78, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 45, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 79, 1, 90, + 90, 90, 90, 90, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 90, 1, + 1, 91, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 92, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 93, 1, 90, 90, 90, 90, + 90, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 90, 1, 1, 91, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 92, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 93, + 1, 67, 67, 67, 67, 67, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 67, 1, 1, 68, 1, 1, 1, 1, + 1, 1, 1, 1, 69, 1, 1, 1, + 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 70, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 71, 1, 94, 94, + 94, 94, 94, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 94, 30, 30, + 58, 30, 30, 30, 30, 30, 30, 30, + 59, 1, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 60, 30, 30, 61, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, + 30, 62, 95, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, + 30, 96, 30, 94, 94, 94, 94, 94, + 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 94, 30, 30, 58, 30, 30, + 30, 30, 30, 30, 30, 59, 1, 30, + 30, 30, 97, 97, 97, 97, 97, 97, + 97, 97, 97, 97, 30, 30, 30, 60, + 30, 30, 61, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 62, 95, + 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 96, 30, + 0 +}; + +static const char _deserialize_text_trans_targs[] = { + 1, 0, 2, 26, 3, 4, 20, 5, + 24, 25, 8, 29, 40, 29, 40, 32, + 37, 33, 34, 12, 13, 16, 13, 16, + 14, 15, 35, 36, 35, 36, 27, 19, + 38, 39, 38, 39, 21, 20, 6, 22, + 23, 21, 22, 23, 21, 22, 23, 25, + 27, 27, 28, 7, 9, 11, 17, 22, + 31, 27, 28, 7, 9, 11, 17, 22, + 31, 41, 42, 30, 10, 18, 22, 31, + 30, 31, 31, 30, 10, 7, 11, 31, + 30, 22, 31, 34, 30, 10, 7, 22, + 31, 37, 30, 10, 22, 31, 27, 22, + 31, 42 +}; + +static const char _deserialize_text_trans_actions[] = { + 0, 0, 0, 0, 1, 0, 2, 0, + 2, 2, 3, 4, 4, 5, 5, 4, + 4, 4, 4, 3, 3, 3, 0, 0, + 6, 3, 4, 4, 5, 5, 5, 3, + 4, 4, 5, 5, 7, 8, 9, 7, + 7, 0, 0, 0, 10, 10, 10, 8, + 12, 13, 14, 15, 15, 15, 16, 11, + 11, 18, 19, 20, 20, 20, 0, 17, + 17, 4, 4, 21, 22, 22, 21, 21, + 0, 0, 13, 10, 23, 23, 23, 10, + 24, 24, 24, 5, 25, 26, 26, 25, + 25, 5, 27, 28, 27, 27, 30, 29, + 29, 5 +}; + +static const char _deserialize_text_eof_actions[] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 7, 0, 0, 0, + 10, 10, 11, 17, 17, 21, 0, 11, + 10, 24, 24, 25, 25, 10, 27, 27, + 21, 29, 29 +}; + +static const int deserialize_text_start = 1; +static const int deserialize_text_first_final = 20; +static const int deserialize_text_error = 0; + +static const int deserialize_text_en_main = 1; + + +#line 117 "hb-buffer-deserialize-text.rl" + + +static hb_bool_t +_hb_buffer_deserialize_text (hb_buffer_t *buffer, + const char *buf, + unsigned int buf_len, + const char **end_ptr, + hb_font_t *font) +{ + const char *p = buf, *pe = buf + buf_len; + + /* Ensure we have positions. */ + (void) hb_buffer_get_glyph_positions (buffer, nullptr); + + while (p < pe && ISSPACE (*p)) + p++; + + const char *eof = pe, *tok = nullptr; + int cs; + hb_glyph_info_t info = {0}; + hb_glyph_position_t pos = {0}; + +#line 513 "hb-buffer-deserialize-text.hh" + { + cs = deserialize_text_start; + } + +#line 518 "hb-buffer-deserialize-text.hh" + { + int _slen; + int _trans; + const unsigned char *_keys; + const char *_inds; + if ( p == pe ) + goto _test_eof; + if ( cs == 0 ) + goto _out; +_resume: + _keys = _deserialize_text_trans_keys + (cs<<1); + _inds = _deserialize_text_indicies + _deserialize_text_index_offsets[cs]; + + _slen = _deserialize_text_key_spans[cs]; + _trans = _inds[ _slen > 0 && _keys[0] <=(*p) && + (*p) <= _keys[1] ? + (*p) - _keys[0] : _slen ]; + + cs = _deserialize_text_trans_targs[_trans]; + + if ( _deserialize_text_trans_actions[_trans] == 0 ) + goto _again; + + switch ( _deserialize_text_trans_actions[_trans] ) { + case 1: +#line 38 "hb-buffer-deserialize-text.rl" + { + memset (&info, 0, sizeof (info)); + memset (&pos , 0, sizeof (pos )); +} + break; + case 3: +#line 51 "hb-buffer-deserialize-text.rl" + { + tok = p; +} + break; + case 5: +#line 55 "hb-buffer-deserialize-text.rl" + { if (unlikely (!buffer->ensure_glyphs ())) return false; } + break; + case 8: +#line 56 "hb-buffer-deserialize-text.rl" + { if (unlikely (!buffer->ensure_unicode ())) return false; } + break; + case 20: +#line 58 "hb-buffer-deserialize-text.rl" + { + /* TODO Unescape delimiters. */ + if (!hb_font_glyph_from_string (font, + tok, p - tok, + &info.codepoint)) + return false; +} + break; + case 9: +#line 66 "hb-buffer-deserialize-text.rl" + {if (!parse_hex (tok, p, &info.codepoint )) return false; } + break; + case 23: +#line 68 "hb-buffer-deserialize-text.rl" + { if (!parse_uint (tok, p, &info.cluster )) return false; } + break; + case 6: +#line 69 "hb-buffer-deserialize-text.rl" + { if (!parse_int (tok, p, &pos.x_offset )) return false; } + break; + case 26: +#line 70 "hb-buffer-deserialize-text.rl" + { if (!parse_int (tok, p, &pos.y_offset )) return false; } + break; + case 22: +#line 71 "hb-buffer-deserialize-text.rl" + { if (!parse_int (tok, p, &pos.x_advance)) return false; } + break; + case 28: +#line 72 "hb-buffer-deserialize-text.rl" + { if (!parse_int (tok, p, &pos.y_advance)) return false; } + break; + case 16: +#line 38 "hb-buffer-deserialize-text.rl" + { + memset (&info, 0, sizeof (info)); + memset (&pos , 0, sizeof (pos )); +} +#line 51 "hb-buffer-deserialize-text.rl" + { + tok = p; +} + break; + case 4: +#line 51 "hb-buffer-deserialize-text.rl" + { + tok = p; +} +#line 55 "hb-buffer-deserialize-text.rl" + { if (unlikely (!buffer->ensure_glyphs ())) return false; } + break; + case 2: +#line 51 "hb-buffer-deserialize-text.rl" + { + tok = p; +} +#line 56 "hb-buffer-deserialize-text.rl" + { if (unlikely (!buffer->ensure_unicode ())) return false; } + break; + case 17: +#line 58 "hb-buffer-deserialize-text.rl" + { + /* TODO Unescape delimiters. */ + if (!hb_font_glyph_from_string (font, + tok, p - tok, + &info.codepoint)) + return false; +} +#line 43 "hb-buffer-deserialize-text.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 19: +#line 58 "hb-buffer-deserialize-text.rl" + { + /* TODO Unescape delimiters. */ + if (!hb_font_glyph_from_string (font, + tok, p - tok, + &info.codepoint)) + return false; +} +#line 55 "hb-buffer-deserialize-text.rl" + { if (unlikely (!buffer->ensure_glyphs ())) return false; } + break; + case 7: +#line 66 "hb-buffer-deserialize-text.rl" + {if (!parse_hex (tok, p, &info.codepoint )) return false; } +#line 43 "hb-buffer-deserialize-text.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 10: +#line 68 "hb-buffer-deserialize-text.rl" + { if (!parse_uint (tok, p, &info.cluster )) return false; } +#line 43 "hb-buffer-deserialize-text.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 25: +#line 70 "hb-buffer-deserialize-text.rl" + { if (!parse_int (tok, p, &pos.y_offset )) return false; } +#line 43 "hb-buffer-deserialize-text.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 21: +#line 71 "hb-buffer-deserialize-text.rl" + { if (!parse_int (tok, p, &pos.x_advance)) return false; } +#line 43 "hb-buffer-deserialize-text.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 27: +#line 72 "hb-buffer-deserialize-text.rl" + { if (!parse_int (tok, p, &pos.y_advance)) return false; } +#line 43 "hb-buffer-deserialize-text.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 24: +#line 73 "hb-buffer-deserialize-text.rl" + { if (!parse_uint (tok, p, &info.mask )) return false; } +#line 43 "hb-buffer-deserialize-text.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 12: +#line 38 "hb-buffer-deserialize-text.rl" + { + memset (&info, 0, sizeof (info)); + memset (&pos , 0, sizeof (pos )); +} +#line 51 "hb-buffer-deserialize-text.rl" + { + tok = p; +} +#line 55 "hb-buffer-deserialize-text.rl" + { if (unlikely (!buffer->ensure_glyphs ())) return false; } + break; + case 15: +#line 38 "hb-buffer-deserialize-text.rl" + { + memset (&info, 0, sizeof (info)); + memset (&pos , 0, sizeof (pos )); +} +#line 51 "hb-buffer-deserialize-text.rl" + { + tok = p; +} +#line 58 "hb-buffer-deserialize-text.rl" + { + /* TODO Unescape delimiters. */ + if (!hb_font_glyph_from_string (font, + tok, p - tok, + &info.codepoint)) + return false; +} + break; + case 18: +#line 58 "hb-buffer-deserialize-text.rl" + { + /* TODO Unescape delimiters. */ + if (!hb_font_glyph_from_string (font, + tok, p - tok, + &info.codepoint)) + return false; +} +#line 55 "hb-buffer-deserialize-text.rl" + { if (unlikely (!buffer->ensure_glyphs ())) return false; } +#line 43 "hb-buffer-deserialize-text.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 29: +#line 58 "hb-buffer-deserialize-text.rl" + { + /* TODO Unescape delimiters. */ + if (!hb_font_glyph_from_string (font, + tok, p - tok, + &info.codepoint)) + return false; +} +#line 73 "hb-buffer-deserialize-text.rl" + { if (!parse_uint (tok, p, &info.mask )) return false; } +#line 43 "hb-buffer-deserialize-text.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 11: +#line 38 "hb-buffer-deserialize-text.rl" + { + memset (&info, 0, sizeof (info)); + memset (&pos , 0, sizeof (pos )); +} +#line 51 "hb-buffer-deserialize-text.rl" + { + tok = p; +} +#line 58 "hb-buffer-deserialize-text.rl" + { + /* TODO Unescape delimiters. */ + if (!hb_font_glyph_from_string (font, + tok, p - tok, + &info.codepoint)) + return false; +} +#line 43 "hb-buffer-deserialize-text.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 14: +#line 38 "hb-buffer-deserialize-text.rl" + { + memset (&info, 0, sizeof (info)); + memset (&pos , 0, sizeof (pos )); +} +#line 51 "hb-buffer-deserialize-text.rl" + { + tok = p; +} +#line 58 "hb-buffer-deserialize-text.rl" + { + /* TODO Unescape delimiters. */ + if (!hb_font_glyph_from_string (font, + tok, p - tok, + &info.codepoint)) + return false; +} +#line 55 "hb-buffer-deserialize-text.rl" + { if (unlikely (!buffer->ensure_glyphs ())) return false; } + break; + case 30: +#line 58 "hb-buffer-deserialize-text.rl" + { + /* TODO Unescape delimiters. */ + if (!hb_font_glyph_from_string (font, + tok, p - tok, + &info.codepoint)) + return false; +} +#line 73 "hb-buffer-deserialize-text.rl" + { if (!parse_uint (tok, p, &info.mask )) return false; } +#line 55 "hb-buffer-deserialize-text.rl" + { if (unlikely (!buffer->ensure_glyphs ())) return false; } +#line 43 "hb-buffer-deserialize-text.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 13: +#line 38 "hb-buffer-deserialize-text.rl" + { + memset (&info, 0, sizeof (info)); + memset (&pos , 0, sizeof (pos )); +} +#line 51 "hb-buffer-deserialize-text.rl" + { + tok = p; +} +#line 58 "hb-buffer-deserialize-text.rl" + { + /* TODO Unescape delimiters. */ + if (!hb_font_glyph_from_string (font, + tok, p - tok, + &info.codepoint)) + return false; +} +#line 55 "hb-buffer-deserialize-text.rl" + { if (unlikely (!buffer->ensure_glyphs ())) return false; } +#line 43 "hb-buffer-deserialize-text.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; +#line 898 "hb-buffer-deserialize-text.hh" + } + +_again: + if ( cs == 0 ) + goto _out; + if ( ++p != pe ) + goto _resume; + _test_eof: {} + if ( p == eof ) + { + switch ( _deserialize_text_eof_actions[cs] ) { + case 17: +#line 58 "hb-buffer-deserialize-text.rl" + { + /* TODO Unescape delimiters. */ + if (!hb_font_glyph_from_string (font, + tok, p - tok, + &info.codepoint)) + return false; +} +#line 43 "hb-buffer-deserialize-text.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 7: +#line 66 "hb-buffer-deserialize-text.rl" + {if (!parse_hex (tok, p, &info.codepoint )) return false; } +#line 43 "hb-buffer-deserialize-text.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 10: +#line 68 "hb-buffer-deserialize-text.rl" + { if (!parse_uint (tok, p, &info.cluster )) return false; } +#line 43 "hb-buffer-deserialize-text.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 25: +#line 70 "hb-buffer-deserialize-text.rl" + { if (!parse_int (tok, p, &pos.y_offset )) return false; } +#line 43 "hb-buffer-deserialize-text.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 21: +#line 71 "hb-buffer-deserialize-text.rl" + { if (!parse_int (tok, p, &pos.x_advance)) return false; } +#line 43 "hb-buffer-deserialize-text.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 27: +#line 72 "hb-buffer-deserialize-text.rl" + { if (!parse_int (tok, p, &pos.y_advance)) return false; } +#line 43 "hb-buffer-deserialize-text.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 24: +#line 73 "hb-buffer-deserialize-text.rl" + { if (!parse_uint (tok, p, &info.mask )) return false; } +#line 43 "hb-buffer-deserialize-text.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 29: +#line 58 "hb-buffer-deserialize-text.rl" + { + /* TODO Unescape delimiters. */ + if (!hb_font_glyph_from_string (font, + tok, p - tok, + &info.codepoint)) + return false; +} +#line 73 "hb-buffer-deserialize-text.rl" + { if (!parse_uint (tok, p, &info.mask )) return false; } +#line 43 "hb-buffer-deserialize-text.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 11: +#line 38 "hb-buffer-deserialize-text.rl" + { + memset (&info, 0, sizeof (info)); + memset (&pos , 0, sizeof (pos )); +} +#line 51 "hb-buffer-deserialize-text.rl" + { + tok = p; +} +#line 58 "hb-buffer-deserialize-text.rl" + { + /* TODO Unescape delimiters. */ + if (!hb_font_glyph_from_string (font, + tok, p - tok, + &info.codepoint)) + return false; +} +#line 43 "hb-buffer-deserialize-text.rl" + { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; +#line 1047 "hb-buffer-deserialize-text.hh" + } + } + + _out: {} + } + +#line 141 "hb-buffer-deserialize-text.rl" + + + *end_ptr = p; + + return p == pe && *(p-1) != ']'; +} + +#endif /* HB_BUFFER_DESERIALIZE_TEXT_HH */ diff --git a/libs/harfbuzz/src/hb-buffer-deserialize-text.rl b/libs/harfbuzz/src/hb-buffer-deserialize-text.rl new file mode 100644 index 00000000..0f33eb89 --- /dev/null +++ b/libs/harfbuzz/src/hb-buffer-deserialize-text.rl @@ -0,0 +1,148 @@ +/* + * Copyright © 2013 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 HB_BUFFER_DESERIALIZE_TEXT_HH +#define HB_BUFFER_DESERIALIZE_TEXT_HH + +#include "hb.hh" + +%%{ + +machine deserialize_text; +alphtype unsigned char; +write data; + +action clear_item { + memset (&info, 0, sizeof (info)); + memset (&pos , 0, sizeof (pos )); +} + +action add_item { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + +action tok { + tok = p; +} + +action ensure_glyphs { if (unlikely (!buffer->ensure_glyphs ())) return false; } +action ensure_unicode { if (unlikely (!buffer->ensure_unicode ())) return false; } + +action parse_glyph { + /* TODO Unescape delimiters. */ + if (!hb_font_glyph_from_string (font, + tok, p - tok, + &info.codepoint)) + return false; +} + +action parse_hexdigits {if (!parse_hex (tok, p, &info.codepoint )) return false; } + +action parse_cluster { if (!parse_uint (tok, p, &info.cluster )) return false; } +action parse_x_offset { if (!parse_int (tok, p, &pos.x_offset )) return false; } +action parse_y_offset { if (!parse_int (tok, p, &pos.y_offset )) return false; } +action parse_x_advance { if (!parse_int (tok, p, &pos.x_advance)) return false; } +action parse_y_advance { if (!parse_int (tok, p, &pos.y_advance)) return false; } +action parse_glyph_flags{ if (!parse_uint (tok, p, &info.mask )) return false; } + +unum = '0' | [1-9] digit*; +num = '-'? unum; + +glyph_id = unum; +glyph_name = ([^\\\]=@+,|] | '\\' [\\\]=@+,|]) *; + +glyph = (glyph_id | glyph_name) >tok %parse_glyph; +cluster = '=' (unum >tok %parse_cluster); +offsets = '@' (num >tok %parse_x_offset) ',' (num >tok %parse_y_offset ); +advances= '+' (num >tok %parse_x_advance) (',' (num >tok %parse_y_advance))?; +glyphflags= '#' (unum >tok %parse_glyph_flags); + +glyph_item = + ( + glyph + cluster? + offsets? + advances? + glyphflags? + ) + >clear_item + @ensure_glyphs + %add_item + ; + +unicode = 'U' '+' xdigit+ >tok %parse_hexdigits; + +unicode_item = + ( + unicode + cluster? + ) + >clear_item + @ensure_unicode + %add_item + ; + +glyphs = glyph_item (space* '|' space* glyph_item)* space* ('|'|']')?; +unicodes = unicode_item (space* '|' space* unicode_item)* space* ('|'|'>')?; + +main := space* ( ('[' glyphs) | ('<' unicodes) ); + +}%% + +static hb_bool_t +_hb_buffer_deserialize_text (hb_buffer_t *buffer, + const char *buf, + unsigned int buf_len, + const char **end_ptr, + hb_font_t *font) +{ + const char *p = buf, *pe = buf + buf_len; + + /* Ensure we have positions. */ + (void) hb_buffer_get_glyph_positions (buffer, nullptr); + + while (p < pe && ISSPACE (*p)) + p++; + + const char *eof = pe, *tok = nullptr; + int cs; + hb_glyph_info_t info = {0}; + hb_glyph_position_t pos = {0}; + %%{ + write init; + write exec; + }%% + + *end_ptr = p; + + return p == pe && *(p-1) != ']'; +} + +#endif /* HB_BUFFER_DESERIALIZE_TEXT_HH */ diff --git a/libs/harfbuzz/src/hb-buffer-serialize.cc b/libs/harfbuzz/src/hb-buffer-serialize.cc new file mode 100644 index 00000000..d1e17754 --- /dev/null +++ b/libs/harfbuzz/src/hb-buffer-serialize.cc @@ -0,0 +1,869 @@ +/* + * Copyright © 2012,2013 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 + */ + +#include "hb.hh" + +#ifndef HB_NO_BUFFER_SERIALIZE + +#include "hb-buffer.hh" + + +static const char *_hb_buffer_serialize_formats[] = { + "text", + "json", + nullptr +}; + +/** + * hb_buffer_serialize_list_formats: + * + * Returns a list of supported buffer serialization formats. + * + * Return value: (transfer none): + * A string array of buffer serialization formats. Should not be freed. + * + * Since: 0.9.7 + **/ +const char ** +hb_buffer_serialize_list_formats () +{ + return _hb_buffer_serialize_formats; +} + +/** + * hb_buffer_serialize_format_from_string: + * @str: (array length=len) (element-type uint8_t): a string to parse + * @len: length of @str, or -1 if string is `NULL` terminated + * + * Parses a string into an #hb_buffer_serialize_format_t. Does not check if + * @str is a valid buffer serialization format, use + * hb_buffer_serialize_list_formats() to get the list of supported formats. + * + * Return value: + * The parsed #hb_buffer_serialize_format_t. + * + * Since: 0.9.7 + **/ +hb_buffer_serialize_format_t +hb_buffer_serialize_format_from_string (const char *str, int len) +{ + /* Upper-case it. */ + return (hb_buffer_serialize_format_t) (hb_tag_from_string (str, len) & ~0x20202020u); +} + +/** + * hb_buffer_serialize_format_to_string: + * @format: an #hb_buffer_serialize_format_t to convert. + * + * Converts @format to the string corresponding it, or `NULL` if it is not a valid + * #hb_buffer_serialize_format_t. + * + * Return value: (transfer none): + * A `NULL` terminated string corresponding to @format. Should not be freed. + * + * Since: 0.9.7 + **/ +const char * +hb_buffer_serialize_format_to_string (hb_buffer_serialize_format_t format) +{ + switch ((unsigned) format) + { + case HB_BUFFER_SERIALIZE_FORMAT_TEXT: return _hb_buffer_serialize_formats[0]; + case HB_BUFFER_SERIALIZE_FORMAT_JSON: return _hb_buffer_serialize_formats[1]; + default: + case HB_BUFFER_SERIALIZE_FORMAT_INVALID: return nullptr; + } +} + +static unsigned int +_hb_buffer_serialize_glyphs_json (hb_buffer_t *buffer, + unsigned int start, + unsigned int end, + char *buf, + unsigned int buf_size, + unsigned int *buf_consumed, + hb_font_t *font, + hb_buffer_serialize_flags_t flags) +{ + hb_glyph_info_t *info = hb_buffer_get_glyph_infos (buffer, nullptr); + hb_glyph_position_t *pos = (flags & HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS) ? + nullptr : hb_buffer_get_glyph_positions (buffer, nullptr); + + *buf_consumed = 0; + hb_position_t x = 0, y = 0; + for (unsigned int i = start; i < end; i++) + { + char b[1024]; + char *p = b; + + /* In the following code, we know b is large enough that no overflow can happen. */ + +#define APPEND(s) HB_STMT_START { strcpy (p, s); p += strlen (s); } HB_STMT_END + + if (i) + *p++ = ','; + else + *p++ = '['; + + *p++ = '{'; + + APPEND ("\"g\":"); + if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_GLYPH_NAMES)) + { + char g[128]; + hb_font_glyph_to_string (font, info[i].codepoint, g, sizeof (g)); + *p++ = '"'; + for (char *q = g; *q; q++) + { + if (unlikely (*q == '"' || *q == '\\')) + *p++ = '\\'; + *p++ = *q; + } + *p++ = '"'; + } + else + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "%u", info[i].codepoint)); + + if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS)) { + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"cl\":%u", info[i].cluster)); + } + + if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS)) + { + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"dx\":%d,\"dy\":%d", + x+pos[i].x_offset, y+pos[i].y_offset)); + if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_ADVANCES)) + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"ax\":%d,\"ay\":%d", + pos[i].x_advance, pos[i].y_advance)); + } + + if (flags & HB_BUFFER_SERIALIZE_FLAG_GLYPH_FLAGS) + { + if (info[i].mask & HB_GLYPH_FLAG_DEFINED) + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"fl\":%u", info[i].mask & HB_GLYPH_FLAG_DEFINED)); + } + + if (flags & HB_BUFFER_SERIALIZE_FLAG_GLYPH_EXTENTS) + { + hb_glyph_extents_t extents; + hb_font_get_glyph_extents(font, info[i].codepoint, &extents); + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"xb\":%d,\"yb\":%d", + extents.x_bearing, extents.y_bearing)); + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"w\":%d,\"h\":%d", + extents.width, extents.height)); + } + + *p++ = '}'; + if (i == end-1) + *p++ = ']'; + + unsigned int l = p - b; + if (buf_size > l) + { + memcpy (buf, b, l); + buf += l; + buf_size -= l; + *buf_consumed += l; + *buf = '\0'; + } else + return i - start; + + if (pos && (flags & HB_BUFFER_SERIALIZE_FLAG_NO_ADVANCES)) + { + x += pos[i].x_advance; + y += pos[i].y_advance; + } + } + + return end - start; +} + +static unsigned int +_hb_buffer_serialize_unicode_json (hb_buffer_t *buffer, + unsigned int start, + unsigned int end, + char *buf, + unsigned int buf_size, + unsigned int *buf_consumed, + hb_buffer_serialize_flags_t flags) +{ + hb_glyph_info_t *info = hb_buffer_get_glyph_infos (buffer, nullptr); + + *buf_consumed = 0; + for (unsigned int i = start; i < end; i++) + { + char b[1024]; + char *p = b; + + if (i) + *p++ = ','; + else + *p++ = '['; + + *p++ = '{'; + + APPEND ("\"u\":"); + + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "%u", info[i].codepoint)); + + if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS)) { + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"cl\":%u", info[i].cluster)); + } + + *p++ = '}'; + + if (i == end-1) + *p++ = ']'; + + unsigned int l = p - b; + if (buf_size > l) + { + memcpy (buf, b, l); + buf += l; + buf_size -= l; + *buf_consumed += l; + *buf = '\0'; + } else + return i - start; + + } + + return end - start; +} + +static unsigned int +_hb_buffer_serialize_glyphs_text (hb_buffer_t *buffer, + unsigned int start, + unsigned int end, + char *buf, + unsigned int buf_size, + unsigned int *buf_consumed, + hb_font_t *font, + hb_buffer_serialize_flags_t flags) +{ + hb_glyph_info_t *info = hb_buffer_get_glyph_infos (buffer, nullptr); + hb_glyph_position_t *pos = (flags & HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS) ? + nullptr : hb_buffer_get_glyph_positions (buffer, nullptr); + + *buf_consumed = 0; + hb_position_t x = 0, y = 0; + for (unsigned int i = start; i < end; i++) + { + char b[1024]; + char *p = b; + + /* In the following code, we know b is large enough that no overflow can happen. */ + + if (i) + *p++ = '|'; + else + *p++ = '['; + + if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_GLYPH_NAMES)) + { + /* TODO Escape delimiters we use. */ + hb_font_glyph_to_string (font, info[i].codepoint, p, 128); + p += strlen (p); + } + else + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "%u", info[i].codepoint)); + + if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS)) { + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "=%u", info[i].cluster)); + } + + if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS)) + { + if (x+pos[i].x_offset || y+pos[i].y_offset) + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "@%d,%d", x+pos[i].x_offset, y+pos[i].y_offset)); + + if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_ADVANCES)) + { + *p++ = '+'; + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "%d", pos[i].x_advance)); + if (pos[i].y_advance) + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",%d", pos[i].y_advance)); + } + } + + if (flags & HB_BUFFER_SERIALIZE_FLAG_GLYPH_FLAGS) + { + if (info[i].mask & HB_GLYPH_FLAG_DEFINED) + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "#%X", info[i].mask &HB_GLYPH_FLAG_DEFINED)); + } + + if (flags & HB_BUFFER_SERIALIZE_FLAG_GLYPH_EXTENTS) + { + hb_glyph_extents_t extents; + hb_font_get_glyph_extents(font, info[i].codepoint, &extents); + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "<%d,%d,%d,%d>", extents.x_bearing, extents.y_bearing, extents.width, extents.height)); + } + + if (i == end-1) { + *p++ = ']'; + } + + unsigned int l = p - b; + if (buf_size > l) + { + memcpy (buf, b, l); + buf += l; + buf_size -= l; + *buf_consumed += l; + *buf = '\0'; + } else + return i - start; + + if (pos && (flags & HB_BUFFER_SERIALIZE_FLAG_NO_ADVANCES)) + { + x += pos[i].x_advance; + y += pos[i].y_advance; + } + } + + return end - start; +} + + +static unsigned int +_hb_buffer_serialize_unicode_text (hb_buffer_t *buffer, + unsigned int start, + unsigned int end, + char *buf, + unsigned int buf_size, + unsigned int *buf_consumed, + hb_buffer_serialize_flags_t flags) +{ + hb_glyph_info_t *info = hb_buffer_get_glyph_infos (buffer, nullptr); + *buf_consumed = 0; + for (unsigned int i = start; i < end; i++) + { + char b[1024]; + char *p = b; + + if (i) + *p++ = '|'; + else + *p++ = '<'; + + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "U+%04X", info[i].codepoint)); + + if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS)) { + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "=%u", info[i].cluster)); + } + + if (i == end-1) + *p++ = '>'; + + unsigned int l = p - b; + if (buf_size > l) + { + memcpy (buf, b, l); + buf += l; + buf_size -= l; + *buf_consumed += l; + *buf = '\0'; + } else + return i - start; + } + return end - start; +} + +/** + * hb_buffer_serialize_glyphs: + * @buffer: an #hb_buffer_t buffer. + * @start: the first item in @buffer to serialize. + * @end: the last item in @buffer to serialize. + * @buf: (out) (array length=buf_size) (element-type uint8_t): output string to + * write serialized buffer into. + * @buf_size: the size of @buf. + * @buf_consumed: (out) (optional): if not `NULL`, will be set to the number of bytes written into @buf. + * @font: (nullable): the #hb_font_t used to shape this buffer, needed to + * read glyph names and extents. If `NULL`, an empty font will be used. + * @format: the #hb_buffer_serialize_format_t to use for formatting the output. + * @flags: the #hb_buffer_serialize_flags_t that control what glyph properties + * to serialize. + * + * Serializes @buffer into a textual representation of its glyph content, + * useful for showing the contents of the buffer, for example during debugging. + * There are currently two supported serialization formats: + * + * ## text + * A human-readable, plain text format. + * The serialized glyphs will look something like: + * + * ``` + * [uni0651=0@518,0+0|uni0628=0+1897] + * ``` + * + * - The serialized glyphs are delimited with `[` and `]`. + * - Glyphs are separated with `|` + * - Each glyph starts with glyph name, or glyph index if + * #HB_BUFFER_SERIALIZE_FLAG_NO_GLYPH_NAMES flag is set. Then, + * - If #HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS is not set, `=` then #hb_glyph_info_t.cluster. + * - If #HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS is not set, the #hb_glyph_position_t in the format: + * - If both #hb_glyph_position_t.x_offset and #hb_glyph_position_t.y_offset are not 0, `@x_offset,y_offset`. Then, + * - `+x_advance`, then `,y_advance` if #hb_glyph_position_t.y_advance is not 0. Then, + * - If #HB_BUFFER_SERIALIZE_FLAG_GLYPH_EXTENTS is set, the #hb_glyph_extents_t in the format `` + * + * ## json + * A machine-readable, structured format. + * The serialized glyphs will look something like: + * + * ``` + * [{"g":"uni0651","cl":0,"dx":518,"dy":0,"ax":0,"ay":0}, + * {"g":"uni0628","cl":0,"dx":0,"dy":0,"ax":1897,"ay":0}] + * ``` + * + * Each glyph is a JSON object, with the following properties: + * - `g`: the glyph name or glyph index if + * #HB_BUFFER_SERIALIZE_FLAG_NO_GLYPH_NAMES flag is set. + * - `cl`: #hb_glyph_info_t.cluster if + * #HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS is not set. + * - `dx`,`dy`,`ax`,`ay`: #hb_glyph_position_t.x_offset, #hb_glyph_position_t.y_offset, + * #hb_glyph_position_t.x_advance and #hb_glyph_position_t.y_advance + * respectively, if #HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS is not set. + * - `xb`,`yb`,`w`,`h`: #hb_glyph_extents_t.x_bearing, #hb_glyph_extents_t.y_bearing, + * #hb_glyph_extents_t.width and #hb_glyph_extents_t.height respectively if + * #HB_BUFFER_SERIALIZE_FLAG_GLYPH_EXTENTS is set. + * + * Return value: + * The number of serialized items. + * + * Since: 0.9.7 + **/ +unsigned int +hb_buffer_serialize_glyphs (hb_buffer_t *buffer, + unsigned int start, + unsigned int end, + char *buf, + unsigned int buf_size, + unsigned int *buf_consumed, + hb_font_t *font, + hb_buffer_serialize_format_t format, + hb_buffer_serialize_flags_t flags) +{ + end = hb_clamp (end, start, buffer->len); + start = hb_min (start, end); + + unsigned int sconsumed; + if (!buf_consumed) + buf_consumed = &sconsumed; + *buf_consumed = 0; + if (buf_size) + *buf = '\0'; + + buffer->assert_glyphs (); + + if (!buffer->have_positions) + flags |= HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS; + + if (unlikely (start == end)) + return 0; + + if (!font) + font = hb_font_get_empty (); + + switch (format) + { + case HB_BUFFER_SERIALIZE_FORMAT_TEXT: + return _hb_buffer_serialize_glyphs_text (buffer, start, end, + buf, buf_size, buf_consumed, + font, flags); + + case HB_BUFFER_SERIALIZE_FORMAT_JSON: + return _hb_buffer_serialize_glyphs_json (buffer, start, end, + buf, buf_size, buf_consumed, + font, flags); + + default: + case HB_BUFFER_SERIALIZE_FORMAT_INVALID: + return 0; + + } +} + +/** + * hb_buffer_serialize_unicode: + * @buffer: an #hb_buffer_t buffer. + * @start: the first item in @buffer to serialize. + * @end: the last item in @buffer to serialize. + * @buf: (out) (array length=buf_size) (element-type uint8_t): output string to + * write serialized buffer into. + * @buf_size: the size of @buf. + * @buf_consumed: (out) (optional): if not `NULL`, will be set to the number of bytes written into @buf. + * @format: the #hb_buffer_serialize_format_t to use for formatting the output. + * @flags: the #hb_buffer_serialize_flags_t that control what glyph properties + * to serialize. + * + * Serializes @buffer into a textual representation of its content, + * when the buffer contains Unicode codepoints (i.e., before shaping). This is + * useful for showing the contents of the buffer, for example during debugging. + * There are currently two supported serialization formats: + * + * ## text + * A human-readable, plain text format. + * The serialized codepoints will look something like: + * + * ``` + *   + * ``` + * + * - Glyphs are separated with `|` + * - Unicode codepoints are expressed as zero-padded four (or more) + * digit hexadecimal numbers preceded by `U+` + * - If #HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS is not set, the cluster + * will be indicated with a `=` then #hb_glyph_info_t.cluster. + * + * ## json + * A machine-readable, structured format. + * The serialized codepoints will be a list of objects with the following + * properties: + * - `u`: the Unicode codepoint as a decimal integer + * - `cl`: #hb_glyph_info_t.cluster if + * #HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS is not set. + * + * For example: + * + * ``` + * [{u:1617,cl:0},{u:1576,cl:1}] + * ``` + * + * Return value: + * The number of serialized items. + * + * Since: 2.7.3 + **/ +unsigned int +hb_buffer_serialize_unicode (hb_buffer_t *buffer, + unsigned int start, + unsigned int end, + char *buf, + unsigned int buf_size, + unsigned int *buf_consumed, + hb_buffer_serialize_format_t format, + hb_buffer_serialize_flags_t flags) +{ + end = hb_clamp (end, start, buffer->len); + start = hb_min (start, end); + + unsigned int sconsumed; + if (!buf_consumed) + buf_consumed = &sconsumed; + *buf_consumed = 0; + if (buf_size) + *buf = '\0'; + + buffer->assert_unicode (); + + if (unlikely (start == end)) + return 0; + + switch (format) + { + case HB_BUFFER_SERIALIZE_FORMAT_TEXT: + return _hb_buffer_serialize_unicode_text (buffer, start, end, + buf, buf_size, buf_consumed, flags); + + case HB_BUFFER_SERIALIZE_FORMAT_JSON: + return _hb_buffer_serialize_unicode_json (buffer, start, end, + buf, buf_size, buf_consumed, flags); + + default: + case HB_BUFFER_SERIALIZE_FORMAT_INVALID: + return 0; + + } +} + +static unsigned int +_hb_buffer_serialize_invalid (hb_buffer_t *buffer, + unsigned int start, + unsigned int end, + char *buf, + unsigned int buf_size, + unsigned int *buf_consumed, + hb_buffer_serialize_format_t format, + hb_buffer_serialize_flags_t flags) +{ + assert (!buffer->len); + + unsigned int sconsumed; + if (!buf_consumed) + buf_consumed = &sconsumed; + if (buf_size < 3) + return 0; + if (format == HB_BUFFER_SERIALIZE_FORMAT_JSON) { + *buf++ = '['; + *buf++ = ']'; + *buf = '\0'; + } else if (format == HB_BUFFER_SERIALIZE_FORMAT_TEXT) { + *buf++ = '!'; + *buf++ = '!'; + *buf = '\0'; + } + *buf_consumed = 2; + return 0; +} + +/** + * hb_buffer_serialize: + * @buffer: an #hb_buffer_t buffer. + * @start: the first item in @buffer to serialize. + * @end: the last item in @buffer to serialize. + * @buf: (out) (array length=buf_size) (element-type uint8_t): output string to + * write serialized buffer into. + * @buf_size: the size of @buf. + * @buf_consumed: (out) (optional): if not `NULL`, will be set to the number of bytes written into @buf. + * @font: (nullable): the #hb_font_t used to shape this buffer, needed to + * read glyph names and extents. If `NULL`, an empty font will be used. + * @format: the #hb_buffer_serialize_format_t to use for formatting the output. + * @flags: the #hb_buffer_serialize_flags_t that control what glyph properties + * to serialize. + * + * Serializes @buffer into a textual representation of its content, whether + * Unicode codepoints or glyph identifiers and positioning information. This is + * useful for showing the contents of the buffer, for example during debugging. + * See the documentation of hb_buffer_serialize_unicode() and + * hb_buffer_serialize_glyphs() for a description of the output format. + * + * Return value: + * The number of serialized items. + * + * Since: 2.7.3 + **/ +unsigned int +hb_buffer_serialize (hb_buffer_t *buffer, + unsigned int start, + unsigned int end, + char *buf, + unsigned int buf_size, + unsigned int *buf_consumed, + hb_font_t *font, + hb_buffer_serialize_format_t format, + hb_buffer_serialize_flags_t flags) +{ + switch (buffer->content_type) + { + + case HB_BUFFER_CONTENT_TYPE_GLYPHS: + return hb_buffer_serialize_glyphs (buffer, start, end, buf, buf_size, + buf_consumed, font, format, flags); + + case HB_BUFFER_CONTENT_TYPE_UNICODE: + return hb_buffer_serialize_unicode (buffer, start, end, buf, buf_size, + buf_consumed, format, flags); + + case HB_BUFFER_CONTENT_TYPE_INVALID: + default: + return _hb_buffer_serialize_invalid (buffer, start, end, buf, buf_size, + buf_consumed, format, flags); + } +} + +static bool +parse_int (const char *pp, const char *end, int32_t *pv) +{ + int v; + const char *p = pp; + if (unlikely (!hb_parse_int (&p, end, &v, true/* whole buffer */))) + return false; + + *pv = v; + return true; +} + +static bool +parse_uint (const char *pp, const char *end, uint32_t *pv) +{ + unsigned int v; + const char *p = pp; + if (unlikely (!hb_parse_uint (&p, end, &v, true/* whole buffer */))) + return false; + + *pv = v; + return true; +} + +static bool +parse_hex (const char *pp, const char *end, uint32_t *pv) +{ + unsigned int v; + const char *p = pp; + if (unlikely (!hb_parse_uint (&p, end, &v, true/* whole buffer */, 16))) + return false; + + *pv = v; + return true; +} + +#include "hb-buffer-deserialize-json.hh" +#include "hb-buffer-deserialize-text.hh" + +/** + * hb_buffer_deserialize_glyphs: + * @buffer: an #hb_buffer_t buffer. + * @buf: (array length=buf_len): string to deserialize + * @buf_len: the size of @buf, or -1 if it is `NULL`-terminated + * @end_ptr: (out) (optional): output pointer to the character after last + * consumed one. + * @font: (nullable): font for getting glyph IDs + * @format: the #hb_buffer_serialize_format_t of the input @buf + * + * Deserializes glyphs @buffer from textual representation in the format + * produced by hb_buffer_serialize_glyphs(). + * + * Return value: `true` if @buf is not fully consumed, `false` otherwise. + * + * Since: 0.9.7 + **/ +hb_bool_t +hb_buffer_deserialize_glyphs (hb_buffer_t *buffer, + const char *buf, + int buf_len, /* -1 means nul-terminated */ + const char **end_ptr, /* May be NULL */ + hb_font_t *font, /* May be NULL */ + hb_buffer_serialize_format_t format) +{ + const char *end; + if (!end_ptr) + end_ptr = &end; + *end_ptr = buf; + + buffer->assert_glyphs (); + + if (unlikely (hb_object_is_immutable (buffer))) + { + if (end_ptr) + *end_ptr = buf; + return false; + } + + if (buf_len == -1) + buf_len = strlen (buf); + + if (!buf_len) + { + *end_ptr = buf; + return false; + } + + hb_buffer_set_content_type (buffer, HB_BUFFER_CONTENT_TYPE_GLYPHS); + + if (!font) + font = hb_font_get_empty (); + + switch (format) + { + case HB_BUFFER_SERIALIZE_FORMAT_TEXT: + return _hb_buffer_deserialize_text (buffer, + buf, buf_len, end_ptr, + font); + + case HB_BUFFER_SERIALIZE_FORMAT_JSON: + return _hb_buffer_deserialize_json (buffer, + buf, buf_len, end_ptr, + font); + + default: + case HB_BUFFER_SERIALIZE_FORMAT_INVALID: + return false; + + } +} + + +/** + * hb_buffer_deserialize_unicode: + * @buffer: an #hb_buffer_t buffer. + * @buf: (array length=buf_len): string to deserialize + * @buf_len: the size of @buf, or -1 if it is `NULL`-terminated + * @end_ptr: (out) (optional): output pointer to the character after last + * consumed one. + * @format: the #hb_buffer_serialize_format_t of the input @buf + * + * Deserializes Unicode @buffer from textual representation in the format + * produced by hb_buffer_serialize_unicode(). + * + * Return value: `true` if @buf is not fully consumed, `false` otherwise. + * + * Since: 2.7.3 + **/ +hb_bool_t +hb_buffer_deserialize_unicode (hb_buffer_t *buffer, + const char *buf, + int buf_len, /* -1 means nul-terminated */ + const char **end_ptr, /* May be NULL */ + hb_buffer_serialize_format_t format) +{ + const char *end; + if (!end_ptr) + end_ptr = &end; + *end_ptr = buf; + + buffer->assert_unicode (); + + if (unlikely (hb_object_is_immutable (buffer))) + { + if (end_ptr) + *end_ptr = buf; + return false; + } + + if (buf_len == -1) + buf_len = strlen (buf); + + if (!buf_len) + { + *end_ptr = buf; + return false; + } + + hb_buffer_set_content_type (buffer, HB_BUFFER_CONTENT_TYPE_UNICODE); + + hb_font_t* font = hb_font_get_empty (); + + switch (format) + { + case HB_BUFFER_SERIALIZE_FORMAT_TEXT: + return _hb_buffer_deserialize_text (buffer, + buf, buf_len, end_ptr, + font); + + case HB_BUFFER_SERIALIZE_FORMAT_JSON: + return _hb_buffer_deserialize_json (buffer, + buf, buf_len, end_ptr, + font); + + default: + case HB_BUFFER_SERIALIZE_FORMAT_INVALID: + return false; + + } +} + + +#endif diff --git a/libs/harfbuzz/src/hb-buffer-verify.cc b/libs/harfbuzz/src/hb-buffer-verify.cc new file mode 100644 index 00000000..5453e1ca --- /dev/null +++ b/libs/harfbuzz/src/hb-buffer-verify.cc @@ -0,0 +1,440 @@ +/* + * Copyright © 2022 Behdad Esfahbod + * + * 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 + */ + +#include "hb.hh" + +#ifndef HB_NO_BUFFER_VERIFY + +#include "hb-buffer.hh" + + +#define BUFFER_VERIFY_ERROR "buffer verify error: " +static inline void +buffer_verify_error (hb_buffer_t *buffer, + hb_font_t *font, + const char *fmt, + ...) HB_PRINTF_FUNC(3, 4); + +static inline void +buffer_verify_error (hb_buffer_t *buffer, + hb_font_t *font, + const char *fmt, + ...) +{ + va_list ap; + va_start (ap, fmt); + if (buffer->messaging ()) + { + buffer->message_impl (font, fmt, ap); + } + else + { + fprintf (stderr, "harfbuzz "); + vfprintf (stderr, fmt, ap); + fprintf (stderr, "\n"); + } + va_end (ap); +} + +static bool +buffer_verify_monotone (hb_buffer_t *buffer, + hb_font_t *font) +{ + /* Check that clusters are monotone. */ + if (buffer->cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES || + buffer->cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS) + { + bool is_forward = HB_DIRECTION_IS_FORWARD (hb_buffer_get_direction (buffer)); + + unsigned int num_glyphs; + hb_glyph_info_t *info = hb_buffer_get_glyph_infos (buffer, &num_glyphs); + + for (unsigned int i = 1; i < num_glyphs; i++) + if (info[i-1].cluster != info[i].cluster && + (info[i-1].cluster < info[i].cluster) != is_forward) + { + buffer_verify_error (buffer, font, BUFFER_VERIFY_ERROR "clusters are not monotone."); + return false; + } + } + + return true; +} + +static bool +buffer_verify_unsafe_to_break (hb_buffer_t *buffer, + hb_buffer_t *text_buffer, + hb_font_t *font, + const hb_feature_t *features, + unsigned int num_features, + const char * const *shapers) +{ + if (buffer->cluster_level != HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES && + buffer->cluster_level != HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS) + { + /* Cannot perform this check without monotone clusters. */ + return true; + } + + /* Check that breaking up shaping at safe-to-break is indeed safe. */ + + hb_buffer_t *fragment = hb_buffer_create_similar (buffer); + hb_buffer_set_flags (fragment, (hb_buffer_flags_t (hb_buffer_get_flags (fragment) & ~HB_BUFFER_FLAG_VERIFY))); + hb_buffer_t *reconstruction = hb_buffer_create_similar (buffer); + hb_buffer_set_flags (reconstruction, (hb_buffer_flags_t (hb_buffer_get_flags (reconstruction) & ~HB_BUFFER_FLAG_VERIFY))); + + unsigned int num_glyphs; + hb_glyph_info_t *info = hb_buffer_get_glyph_infos (buffer, &num_glyphs); + + unsigned int num_chars; + hb_glyph_info_t *text = hb_buffer_get_glyph_infos (text_buffer, &num_chars); + + /* Chop text and shape fragments. */ + bool forward = HB_DIRECTION_IS_FORWARD (hb_buffer_get_direction (buffer)); + unsigned int start = 0; + unsigned int text_start = forward ? 0 : num_chars; + unsigned int text_end = text_start; + for (unsigned int end = 1; end < num_glyphs + 1; end++) + { + if (end < num_glyphs && + (info[end].cluster == info[end-1].cluster || + info[end-(forward?0:1)].mask & HB_GLYPH_FLAG_UNSAFE_TO_BREAK)) + continue; + + /* Shape segment corresponding to glyphs start..end. */ + if (end == num_glyphs) + { + if (forward) + text_end = num_chars; + else + text_start = 0; + } + else + { + if (forward) + { + unsigned int cluster = info[end].cluster; + while (text_end < num_chars && text[text_end].cluster < cluster) + text_end++; + } + else + { + unsigned int cluster = info[end - 1].cluster; + while (text_start && text[text_start - 1].cluster >= cluster) + text_start--; + } + } + assert (text_start < text_end); + + if (0) + printf("start %d end %d text start %d end %d\n", start, end, text_start, text_end); + + hb_buffer_clear_contents (fragment); + + hb_buffer_flags_t flags = hb_buffer_get_flags (fragment); + if (0 < text_start) + flags = (hb_buffer_flags_t) (flags & ~HB_BUFFER_FLAG_BOT); + if (text_end < num_chars) + flags = (hb_buffer_flags_t) (flags & ~HB_BUFFER_FLAG_EOT); + hb_buffer_set_flags (fragment, flags); + + hb_buffer_append (fragment, text_buffer, text_start, text_end); + if (!hb_shape_full (font, fragment, features, num_features, shapers)) + { + buffer_verify_error (buffer, font, BUFFER_VERIFY_ERROR "shaping failed while shaping fragment."); + hb_buffer_destroy (reconstruction); + hb_buffer_destroy (fragment); + return false; + } + else if (!fragment->successful || fragment->shaping_failed) + { + hb_buffer_destroy (reconstruction); + hb_buffer_destroy (fragment); + return true; + } + hb_buffer_append (reconstruction, fragment, 0, -1); + + start = end; + if (forward) + text_start = text_end; + else + text_end = text_start; + } + + bool ret = true; + hb_buffer_diff_flags_t diff = hb_buffer_diff (reconstruction, buffer, (hb_codepoint_t) -1, 0); + if (diff) + { + buffer_verify_error (buffer, font, BUFFER_VERIFY_ERROR "unsafe-to-break test failed."); + ret = false; + + /* Return the reconstructed result instead so it can be inspected. */ + hb_buffer_set_length (buffer, 0); + hb_buffer_append (buffer, reconstruction, 0, -1); + } + + hb_buffer_destroy (reconstruction); + hb_buffer_destroy (fragment); + + return ret; +} + +static bool +buffer_verify_unsafe_to_concat (hb_buffer_t *buffer, + hb_buffer_t *text_buffer, + hb_font_t *font, + const hb_feature_t *features, + unsigned int num_features, + const char * const *shapers) +{ + if (buffer->cluster_level != HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES && + buffer->cluster_level != HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS) + { + /* Cannot perform this check without monotone clusters. */ + return true; + } + + /* Check that shuffling up text before shaping at safe-to-concat points + * is indeed safe. */ + + /* This is what we do: + * + * 1. We shape text once. Then segment the text at all the safe-to-concat + * points; + * + * 2. Then we create two buffers, one containing all the even segments and + * one all the odd segments. + * + * 3. Because all these segments were safe-to-concat at both ends, we + * expect that concatenating them and shaping should NOT change the + * shaping results of each segment. As such, we expect that after + * shaping the two buffers, we still get cluster boundaries at the + * segment boundaries, and that those all are safe-to-concat points. + * Moreover, that there are NOT any safe-to-concat points within the + * segments. + * + * 4. Finally, we reconstruct the shaping results of the original text by + * simply interleaving the shaping results of the segments from the two + * buffers, and assert that the total shaping results is the same as + * the one from original buffer in step 1. + */ + + hb_buffer_t *fragments[2] {hb_buffer_create_similar (buffer), + hb_buffer_create_similar (buffer)}; + hb_buffer_set_flags (fragments[0], (hb_buffer_flags_t (hb_buffer_get_flags (fragments[0]) & ~HB_BUFFER_FLAG_VERIFY))); + hb_buffer_set_flags (fragments[1], (hb_buffer_flags_t (hb_buffer_get_flags (fragments[1]) & ~HB_BUFFER_FLAG_VERIFY))); + hb_buffer_t *reconstruction = hb_buffer_create_similar (buffer); + hb_buffer_set_flags (reconstruction, (hb_buffer_flags_t (hb_buffer_get_flags (reconstruction) & ~HB_BUFFER_FLAG_VERIFY))); + hb_segment_properties_t props; + hb_buffer_get_segment_properties (buffer, &props); + hb_buffer_set_segment_properties (fragments[0], &props); + hb_buffer_set_segment_properties (fragments[1], &props); + hb_buffer_set_segment_properties (reconstruction, &props); + + unsigned num_glyphs; + hb_glyph_info_t *info = hb_buffer_get_glyph_infos (buffer, &num_glyphs); + + unsigned num_chars; + hb_glyph_info_t *text = hb_buffer_get_glyph_infos (text_buffer, &num_chars); + + bool forward = HB_DIRECTION_IS_FORWARD (hb_buffer_get_direction (buffer)); + + if (!forward) + hb_buffer_reverse (buffer); + + /* + * Split text into segments and collect into to fragment streams. + */ + { + unsigned fragment_idx = 0; + unsigned start = 0; + unsigned text_start = 0; + unsigned text_end = 0; + for (unsigned end = 1; end < num_glyphs + 1; end++) + { + if (end < num_glyphs && + (info[end].cluster == info[end-1].cluster || + info[end].mask & HB_GLYPH_FLAG_UNSAFE_TO_CONCAT)) + continue; + + /* Accumulate segment corresponding to glyphs start..end. */ + if (end == num_glyphs) + text_end = num_chars; + else + { + unsigned cluster = info[end].cluster; + while (text_end < num_chars && text[text_end].cluster < cluster) + text_end++; + } + assert (text_start < text_end); + + if (0) + printf("start %d end %d text start %d end %d\n", start, end, text_start, text_end); + +#if 0 + hb_buffer_flags_t flags = hb_buffer_get_flags (fragment); + if (0 < text_start) + flags = (hb_buffer_flags_t) (flags & ~HB_BUFFER_FLAG_BOT); + if (text_end < num_chars) + flags = (hb_buffer_flags_t) (flags & ~HB_BUFFER_FLAG_EOT); + hb_buffer_set_flags (fragment, flags); +#endif + + hb_buffer_append (fragments[fragment_idx], text_buffer, text_start, text_end); + + start = end; + text_start = text_end; + fragment_idx = 1 - fragment_idx; + } + } + + bool ret = true; + hb_buffer_diff_flags_t diff; + + /* + * Shape the two fragment streams. + */ + if (!hb_shape_full (font, fragments[0], features, num_features, shapers)) + { + buffer_verify_error (buffer, font, BUFFER_VERIFY_ERROR "shaping failed while shaping fragment."); + ret = false; + goto out; + } + else if (!fragments[0]->successful || fragments[0]->shaping_failed) + { + ret = true; + goto out; + } + if (!hb_shape_full (font, fragments[1], features, num_features, shapers)) + { + buffer_verify_error (buffer, font, BUFFER_VERIFY_ERROR "shaping failed while shaping fragment."); + ret = false; + goto out; + } + else if (!fragments[1]->successful || fragments[1]->shaping_failed) + { + ret = true; + goto out; + } + + if (!forward) + { + hb_buffer_reverse (fragments[0]); + hb_buffer_reverse (fragments[1]); + } + + /* + * Reconstruct results. + */ + { + unsigned fragment_idx = 0; + unsigned fragment_start[2] {0, 0}; + unsigned fragment_num_glyphs[2]; + hb_glyph_info_t *fragment_info[2]; + for (unsigned i = 0; i < 2; i++) + fragment_info[i] = hb_buffer_get_glyph_infos (fragments[i], &fragment_num_glyphs[i]); + while (fragment_start[0] < fragment_num_glyphs[0] || + fragment_start[1] < fragment_num_glyphs[1]) + { + unsigned fragment_end = fragment_start[fragment_idx] + 1; + while (fragment_end < fragment_num_glyphs[fragment_idx] && + (fragment_info[fragment_idx][fragment_end].cluster == fragment_info[fragment_idx][fragment_end - 1].cluster || + fragment_info[fragment_idx][fragment_end].mask & HB_GLYPH_FLAG_UNSAFE_TO_CONCAT)) + fragment_end++; + + hb_buffer_append (reconstruction, fragments[fragment_idx], fragment_start[fragment_idx], fragment_end); + + fragment_start[fragment_idx] = fragment_end; + fragment_idx = 1 - fragment_idx; + } + } + + if (!forward) + { + hb_buffer_reverse (buffer); + hb_buffer_reverse (reconstruction); + } + + /* + * Diff results. + */ + diff = hb_buffer_diff (reconstruction, buffer, (hb_codepoint_t) -1, 0); + if (diff) + { + buffer_verify_error (buffer, font, BUFFER_VERIFY_ERROR "unsafe-to-concat test failed."); + ret = false; + + /* Return the reconstructed result instead so it can be inspected. */ + hb_buffer_set_length (buffer, 0); + hb_buffer_append (buffer, reconstruction, 0, -1); + } + + +out: + hb_buffer_destroy (reconstruction); + hb_buffer_destroy (fragments[0]); + hb_buffer_destroy (fragments[1]); + + return ret; +} + +bool +hb_buffer_t::verify (hb_buffer_t *text_buffer, + hb_font_t *font, + const hb_feature_t *features, + unsigned int num_features, + const char * const *shapers) +{ + bool ret = true; + if (!buffer_verify_monotone (this, font)) + ret = false; + if (!buffer_verify_unsafe_to_break (this, text_buffer, font, features, num_features, shapers)) + ret = false; + if ((flags & HB_BUFFER_FLAG_PRODUCE_UNSAFE_TO_CONCAT) != 0 && + !buffer_verify_unsafe_to_concat (this, text_buffer, font, features, num_features, shapers)) + ret = false; + if (!ret) + { +#ifndef HB_NO_BUFFER_SERIALIZE + unsigned len = text_buffer->len; + hb_vector_t bytes; + if (likely (bytes.resize (len * 10 + 16))) + { + hb_buffer_serialize_unicode (text_buffer, + 0, len, + bytes.arrayZ, bytes.length, + &len, + HB_BUFFER_SERIALIZE_FORMAT_TEXT, + HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS); + buffer_verify_error (this, font, BUFFER_VERIFY_ERROR "text was: %s.", bytes.arrayZ); + } +#endif + } + return ret; +} + + +#endif diff --git a/libs/harfbuzz/src/hb-buffer.cc b/libs/harfbuzz/src/hb-buffer.cc new file mode 100644 index 00000000..57a5ae03 --- /dev/null +++ b/libs/harfbuzz/src/hb-buffer.cc @@ -0,0 +1,2134 @@ +/* + * Copyright © 1998-2004 David Turner and Werner Lemberg + * Copyright © 2004,2007,2009,2010 Red Hat, Inc. + * Copyright © 2011,2012 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. + * + * Red Hat Author(s): Owen Taylor, Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#include "hb-buffer.hh" +#include "hb-utf.hh" + + +/** + * SECTION: hb-buffer + * @title: hb-buffer + * @short_description: Input and output buffers + * @include: hb.h + * + * Buffers serve a dual role in HarfBuzz; before shaping, they hold + * the input characters that are passed to hb_shape(), and after + * shaping they hold the output glyphs. + **/ + + +/** + * hb_segment_properties_equal: + * @a: first #hb_segment_properties_t to compare. + * @b: second #hb_segment_properties_t to compare. + * + * Checks the equality of two #hb_segment_properties_t's. + * + * Return value: + * `true` if all properties of @a equal those of @b, `false` otherwise. + * + * Since: 0.9.7 + **/ +hb_bool_t +hb_segment_properties_equal (const hb_segment_properties_t *a, + const hb_segment_properties_t *b) +{ + return a->direction == b->direction && + a->script == b->script && + a->language == b->language && + a->reserved1 == b->reserved1 && + a->reserved2 == b->reserved2; + +} + +/** + * hb_segment_properties_hash: + * @p: #hb_segment_properties_t to hash. + * + * Creates a hash representing @p. + * + * Return value: + * A hash of @p. + * + * Since: 0.9.7 + **/ +unsigned int +hb_segment_properties_hash (const hb_segment_properties_t *p) +{ + return ((unsigned int) p->direction * 31 + + (unsigned int) p->script) * 31 + + (intptr_t) (p->language); +} + +/** + * hb_segment_properties_overlay: + * @p: #hb_segment_properties_t to fill in. + * @src: #hb_segment_properties_t to fill in from. + * + * Fills in missing fields of @p from @src in a considered manner. + * + * First, if @p does not have direction set, direction is copied from @src. + * + * Next, if @p and @src have the same direction (which can be unset), if @p + * does not have script set, script is copied from @src. + * + * Finally, if @p and @src have the same direction and script (which either + * can be unset), if @p does not have language set, language is copied from + * @src. + * + * Since: 3.3.0 + **/ +void +hb_segment_properties_overlay (hb_segment_properties_t *p, + const hb_segment_properties_t *src) +{ + if (unlikely (!p || !src)) + return; + + if (!p->direction) + p->direction = src->direction; + + if (p->direction != src->direction) + return; + + if (!p->script) + p->script = src->script; + + if (p->script != src->script) + return; + + if (!p->language) + p->language = src->language; +} + +/* Here is how the buffer works internally: + * + * There are two info pointers: info and out_info. They always have + * the same allocated size, but different lengths. + * + * As an optimization, both info and out_info may point to the + * same piece of memory, which is owned by info. This remains the + * case as long as out_len doesn't exceed i at any time. + * In that case, sync() is mostly no-op and the glyph operations + * operate mostly in-place. + * + * As soon as out_info gets longer than info, out_info is moved over + * to an alternate buffer (which we reuse the pos buffer for), and its + * current contents (out_len entries) are copied to the new place. + * + * This should all remain transparent to the user. sync() then + * switches info over to out_info and does housekeeping. + */ + + + +/* Internal API */ + +bool +hb_buffer_t::enlarge (unsigned int size) +{ + if (unlikely (!successful)) + return false; + if (unlikely (size > max_len)) + { + successful = false; + return false; + } + + unsigned int new_allocated = allocated; + hb_glyph_position_t *new_pos = nullptr; + hb_glyph_info_t *new_info = nullptr; + bool separate_out = out_info != info; + + if (unlikely (hb_unsigned_mul_overflows (size, sizeof (info[0])))) + goto done; + + while (size >= new_allocated) + new_allocated += (new_allocated >> 1) + 32; + + static_assert ((sizeof (info[0]) == sizeof (pos[0])), ""); + if (unlikely (hb_unsigned_mul_overflows (new_allocated, sizeof (info[0])))) + goto done; + + new_pos = (hb_glyph_position_t *) hb_realloc (pos, new_allocated * sizeof (pos[0])); + new_info = (hb_glyph_info_t *) hb_realloc (info, new_allocated * sizeof (info[0])); + +done: + if (unlikely (!new_pos || !new_info)) + successful = false; + + if (likely (new_pos)) + pos = new_pos; + + if (likely (new_info)) + info = new_info; + + out_info = separate_out ? (hb_glyph_info_t *) pos : info; + if (likely (successful)) + allocated = new_allocated; + + return likely (successful); +} + +bool +hb_buffer_t::make_room_for (unsigned int num_in, + unsigned int num_out) +{ + if (unlikely (!ensure (out_len + num_out))) return false; + + if (out_info == info && + out_len + num_out > idx + num_in) + { + assert (have_output); + + out_info = (hb_glyph_info_t *) pos; + memcpy (out_info, info, out_len * sizeof (out_info[0])); + } + + return true; +} + +bool +hb_buffer_t::shift_forward (unsigned int count) +{ + assert (have_output); + if (unlikely (!ensure (len + count))) return false; + + memmove (info + idx + count, info + idx, (len - idx) * sizeof (info[0])); + if (idx + count > len) + { + /* Under memory failure we might expose this area. At least + * clean it up. Oh well... + * + * Ideally, we should at least set Default_Ignorable bits on + * these, as well as consistent cluster values. But the former + * is layering violation... */ + memset (info + len, 0, (idx + count - len) * sizeof (info[0])); + } + len += count; + idx += count; + + return true; +} + +hb_buffer_t::scratch_buffer_t * +hb_buffer_t::get_scratch_buffer (unsigned int *size) +{ + have_output = false; + have_positions = false; + + out_len = 0; + out_info = info; + + assert ((uintptr_t) pos % sizeof (scratch_buffer_t) == 0); + *size = allocated * sizeof (pos[0]) / sizeof (scratch_buffer_t); + return (scratch_buffer_t *) (void *) pos; +} + + + +/* HarfBuzz-Internal API */ + +void +hb_buffer_t::similar (const hb_buffer_t &src) +{ + hb_unicode_funcs_destroy (unicode); + unicode = hb_unicode_funcs_reference (src.unicode); + flags = src.flags; + cluster_level = src.cluster_level; + replacement = src.invisible; + invisible = src.invisible; + not_found = src.not_found; +} + +void +hb_buffer_t::reset () +{ + hb_unicode_funcs_destroy (unicode); + unicode = hb_unicode_funcs_reference (hb_unicode_funcs_get_default ()); + flags = HB_BUFFER_FLAG_DEFAULT; + cluster_level = HB_BUFFER_CLUSTER_LEVEL_DEFAULT; + replacement = HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT; + invisible = 0; + not_found = 0; + + clear (); +} + +void +hb_buffer_t::clear () +{ + content_type = HB_BUFFER_CONTENT_TYPE_INVALID; + hb_segment_properties_t default_props = HB_SEGMENT_PROPERTIES_DEFAULT; + props = default_props; + + successful = true; + shaping_failed = false; + have_output = false; + have_positions = false; + + idx = 0; + len = 0; + out_len = 0; + out_info = info; + + memset (context, 0, sizeof context); + memset (context_len, 0, sizeof context_len); + + deallocate_var_all (); + serial = 0; + scratch_flags = HB_BUFFER_SCRATCH_FLAG_DEFAULT; +} + +void +hb_buffer_t::enter () +{ + deallocate_var_all (); + serial = 0; + shaping_failed = false; + scratch_flags = HB_BUFFER_SCRATCH_FLAG_DEFAULT; + if (likely (!hb_unsigned_mul_overflows (len, HB_BUFFER_MAX_LEN_FACTOR))) + { + max_len = hb_max (len * HB_BUFFER_MAX_LEN_FACTOR, + (unsigned) HB_BUFFER_MAX_LEN_MIN); + } + if (likely (!hb_unsigned_mul_overflows (len, HB_BUFFER_MAX_OPS_FACTOR))) + { + max_ops = hb_max (len * HB_BUFFER_MAX_OPS_FACTOR, + (unsigned) HB_BUFFER_MAX_OPS_MIN); + } +} +void +hb_buffer_t::leave () +{ + max_len = HB_BUFFER_MAX_LEN_DEFAULT; + max_ops = HB_BUFFER_MAX_OPS_DEFAULT; + deallocate_var_all (); + serial = 0; + // Intentionally not reseting shaping_failed, such that it can be inspected. +} + + +void +hb_buffer_t::add (hb_codepoint_t codepoint, + unsigned int cluster) +{ + hb_glyph_info_t *glyph; + + if (unlikely (!ensure (len + 1))) return; + + glyph = &info[len]; + + memset (glyph, 0, sizeof (*glyph)); + glyph->codepoint = codepoint; + glyph->mask = 0; + glyph->cluster = cluster; + + len++; +} + +void +hb_buffer_t::add_info (const hb_glyph_info_t &glyph_info) +{ + if (unlikely (!ensure (len + 1))) return; + + info[len] = glyph_info; + + len++; +} + + +void +hb_buffer_t::clear_output () +{ + have_output = true; + have_positions = false; + + idx = 0; + out_len = 0; + out_info = info; +} + +void +hb_buffer_t::clear_positions () +{ + have_output = false; + have_positions = true; + + out_len = 0; + out_info = info; + + hb_memset (pos, 0, sizeof (pos[0]) * len); +} + +bool +hb_buffer_t::sync () +{ + bool ret = false; + + assert (have_output); + + assert (idx <= len); + + if (unlikely (!successful || !next_glyphs (len - idx))) + goto reset; + + if (out_info != info) + { + pos = (hb_glyph_position_t *) info; + info = out_info; + } + len = out_len; + ret = true; + +reset: + have_output = false; + out_len = 0; + out_info = info; + idx = 0; + + return ret; +} + +int +hb_buffer_t::sync_so_far () +{ + bool had_output = have_output; + unsigned out_i = out_len; + unsigned i = idx; + unsigned old_idx = idx; + + if (sync ()) + idx = out_i; + else + idx = i; + + if (had_output) + { + have_output = true; + out_len = idx; + } + + assert (idx <= len); + + return idx - old_idx; +} + +bool +hb_buffer_t::move_to (unsigned int i) +{ + if (!have_output) + { + assert (i <= len); + idx = i; + return true; + } + if (unlikely (!successful)) + return false; + + assert (i <= out_len + (len - idx)); + + if (out_len < i) + { + unsigned int count = i - out_len; + if (unlikely (!make_room_for (count, count))) return false; + + memmove (out_info + out_len, info + idx, count * sizeof (out_info[0])); + idx += count; + out_len += count; + } + else if (out_len > i) + { + /* Tricky part: rewinding... */ + unsigned int count = out_len - i; + + /* This will blow in our face if memory allocation fails later + * in this same lookup... + * + * We used to shift with extra 32 items. + * But that would leave empty slots in the buffer in case of allocation + * failures. See comments in shift_forward(). This can cause O(N^2) + * behavior more severely than adding 32 empty slots can... */ + if (unlikely (idx < count && !shift_forward (count - idx))) return false; + + assert (idx >= count); + + idx -= count; + out_len -= count; + memmove (info + idx, out_info + out_len, count * sizeof (out_info[0])); + } + + return true; +} + + +void +hb_buffer_t::set_masks (hb_mask_t value, + hb_mask_t mask, + unsigned int cluster_start, + unsigned int cluster_end) +{ + hb_mask_t not_mask = ~mask; + value &= mask; + + if (!mask) + return; + + unsigned int count = len; + for (unsigned int i = 0; i < count; i++) + if (cluster_start <= info[i].cluster && info[i].cluster < cluster_end) + info[i].mask = (info[i].mask & not_mask) | value; +} + +void +hb_buffer_t::merge_clusters_impl (unsigned int start, + unsigned int end) +{ + if (cluster_level == HB_BUFFER_CLUSTER_LEVEL_CHARACTERS) + { + unsafe_to_break (start, end); + return; + } + + unsigned int cluster = info[start].cluster; + + for (unsigned int i = start + 1; i < end; i++) + cluster = hb_min (cluster, info[i].cluster); + + /* Extend end */ + while (end < len && info[end - 1].cluster == info[end].cluster) + end++; + + /* Extend start */ + while (idx < start && info[start - 1].cluster == info[start].cluster) + start--; + + /* If we hit the start of buffer, continue in out-buffer. */ + if (idx == start) + for (unsigned int i = out_len; i && out_info[i - 1].cluster == info[start].cluster; i--) + set_cluster (out_info[i - 1], cluster); + + for (unsigned int i = start; i < end; i++) + set_cluster (info[i], cluster); +} +void +hb_buffer_t::merge_out_clusters (unsigned int start, + unsigned int end) +{ + if (cluster_level == HB_BUFFER_CLUSTER_LEVEL_CHARACTERS) + return; + + if (unlikely (end - start < 2)) + return; + + unsigned int cluster = out_info[start].cluster; + + for (unsigned int i = start + 1; i < end; i++) + cluster = hb_min (cluster, out_info[i].cluster); + + /* Extend start */ + while (start && out_info[start - 1].cluster == out_info[start].cluster) + start--; + + /* Extend end */ + while (end < out_len && out_info[end - 1].cluster == out_info[end].cluster) + end++; + + /* If we hit the end of out-buffer, continue in buffer. */ + if (end == out_len) + for (unsigned int i = idx; i < len && info[i].cluster == out_info[end - 1].cluster; i++) + set_cluster (info[i], cluster); + + for (unsigned int i = start; i < end; i++) + set_cluster (out_info[i], cluster); +} +void +hb_buffer_t::delete_glyph () +{ + /* The logic here is duplicated in hb_ot_hide_default_ignorables(). */ + + unsigned int cluster = info[idx].cluster; + if ((idx + 1 < len && cluster == info[idx + 1].cluster) || + (out_len && cluster == out_info[out_len - 1].cluster)) + { + /* Cluster survives; do nothing. */ + goto done; + } + + if (out_len) + { + /* Merge cluster backward. */ + if (cluster < out_info[out_len - 1].cluster) + { + unsigned int mask = info[idx].mask; + unsigned int old_cluster = out_info[out_len - 1].cluster; + for (unsigned i = out_len; i && out_info[i - 1].cluster == old_cluster; i--) + set_cluster (out_info[i - 1], cluster, mask); + } + goto done; + } + + if (idx + 1 < len) + { + /* Merge cluster forward. */ + merge_clusters (idx, idx + 2); + goto done; + } + +done: + skip_glyph (); +} + +void +hb_buffer_t::guess_segment_properties () +{ + assert_unicode (); + + /* If script is set to INVALID, guess from buffer contents */ + if (props.script == HB_SCRIPT_INVALID) { + for (unsigned int i = 0; i < len; i++) { + hb_script_t script = unicode->script (info[i].codepoint); + if (likely (script != HB_SCRIPT_COMMON && + script != HB_SCRIPT_INHERITED && + script != HB_SCRIPT_UNKNOWN)) { + props.script = script; + break; + } + } + } + + /* If direction is set to INVALID, guess from script */ + if (props.direction == HB_DIRECTION_INVALID) { + props.direction = hb_script_get_horizontal_direction (props.script); + if (props.direction == HB_DIRECTION_INVALID) + props.direction = HB_DIRECTION_LTR; + } + + /* If language is not set, use default language from locale */ + if (props.language == HB_LANGUAGE_INVALID) { + /* TODO get_default_for_script? using $LANGUAGE */ + props.language = hb_language_get_default (); + } +} + + +/* Public API */ + +DEFINE_NULL_INSTANCE (hb_buffer_t) = +{ + HB_OBJECT_HEADER_STATIC, + + const_cast (&_hb_Null_hb_unicode_funcs_t), + HB_BUFFER_FLAG_DEFAULT, + HB_BUFFER_CLUSTER_LEVEL_DEFAULT, + HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT, + 0, /* invisible */ + 0, /* not_found */ + + + HB_BUFFER_CONTENT_TYPE_INVALID, + HB_SEGMENT_PROPERTIES_DEFAULT, + + false, /* successful */ + true, /* shaping_failed */ + false, /* have_output */ + true /* have_positions */ + + /* Zero is good enough for everything else. */ +}; + + +/** + * hb_buffer_create: + * + * Creates a new #hb_buffer_t with all properties to defaults. + * + * Return value: (transfer full): + * A newly allocated #hb_buffer_t with a reference count of 1. The initial + * reference count should be released with hb_buffer_destroy() when you are done + * using the #hb_buffer_t. This function never returns `NULL`. If memory cannot + * be allocated, a special #hb_buffer_t object will be returned on which + * hb_buffer_allocation_successful() returns `false`. + * + * Since: 0.9.2 + **/ +hb_buffer_t * +hb_buffer_create () +{ + hb_buffer_t *buffer; + + if (!(buffer = hb_object_create ())) + return hb_buffer_get_empty (); + + buffer->max_len = HB_BUFFER_MAX_LEN_DEFAULT; + buffer->max_ops = HB_BUFFER_MAX_OPS_DEFAULT; + + buffer->reset (); + + return buffer; +} + +/** + * hb_buffer_create_similar: + * @src: An #hb_buffer_t + * + * Creates a new #hb_buffer_t, similar to hb_buffer_create(). The only + * difference is that the buffer is configured similarly to @src. + * + * Return value: (transfer full): + * A newly allocated #hb_buffer_t, similar to hb_buffer_create(). + * + * Since: 3.3.0 + **/ +hb_buffer_t * +hb_buffer_create_similar (const hb_buffer_t *src) +{ + hb_buffer_t *buffer = hb_buffer_create (); + + buffer->similar (*src); + + return buffer; +} + +/** + * hb_buffer_reset: + * @buffer: An #hb_buffer_t + * + * Resets the buffer to its initial status, as if it was just newly created + * with hb_buffer_create(). + * + * Since: 0.9.2 + **/ +void +hb_buffer_reset (hb_buffer_t *buffer) +{ + if (unlikely (hb_object_is_immutable (buffer))) + return; + + buffer->reset (); +} + +/** + * hb_buffer_get_empty: + * + * Fetches an empty #hb_buffer_t. + * + * Return value: (transfer full): The empty buffer + * + * Since: 0.9.2 + **/ +hb_buffer_t * +hb_buffer_get_empty () +{ + return const_cast (&Null (hb_buffer_t)); +} + +/** + * hb_buffer_reference: (skip) + * @buffer: An #hb_buffer_t + * + * Increases the reference count on @buffer by one. This prevents @buffer from + * being destroyed until a matching call to hb_buffer_destroy() is made. + * + * Return value: (transfer full): + * The referenced #hb_buffer_t. + * + * Since: 0.9.2 + **/ +hb_buffer_t * +hb_buffer_reference (hb_buffer_t *buffer) +{ + return hb_object_reference (buffer); +} + +/** + * hb_buffer_destroy: (skip) + * @buffer: An #hb_buffer_t + * + * Deallocate the @buffer. + * Decreases the reference count on @buffer by one. If the result is zero, then + * @buffer and all associated resources are freed. See hb_buffer_reference(). + * + * Since: 0.9.2 + **/ +void +hb_buffer_destroy (hb_buffer_t *buffer) +{ + if (!hb_object_destroy (buffer)) return; + + hb_unicode_funcs_destroy (buffer->unicode); + + hb_free (buffer->info); + hb_free (buffer->pos); +#ifndef HB_NO_BUFFER_MESSAGE + if (buffer->message_destroy) + buffer->message_destroy (buffer->message_data); +#endif + + hb_free (buffer); +} + +/** + * hb_buffer_set_user_data: (skip) + * @buffer: An #hb_buffer_t + * @key: The user-data key + * @data: A pointer to the user data + * @destroy: (nullable): A callback to call when @data is not needed anymore + * @replace: Whether to replace an existing data with the same key + * + * Attaches a user-data key/data pair to the specified buffer. + * + * Return value: `true` if success, `false` otherwise + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_buffer_set_user_data (hb_buffer_t *buffer, + hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy, + hb_bool_t replace) +{ + return hb_object_set_user_data (buffer, key, data, destroy, replace); +} + +/** + * hb_buffer_get_user_data: (skip) + * @buffer: An #hb_buffer_t + * @key: The user-data key to query + * + * Fetches the user data associated with the specified key, + * attached to the specified buffer. + * + * Return value: (transfer none): A pointer to the user data + * + * Since: 0.9.2 + **/ +void * +hb_buffer_get_user_data (const hb_buffer_t *buffer, + hb_user_data_key_t *key) +{ + return hb_object_get_user_data (buffer, key); +} + + +/** + * hb_buffer_set_content_type: + * @buffer: An #hb_buffer_t + * @content_type: The type of buffer contents to set + * + * Sets the type of @buffer contents. Buffers are either empty, contain + * characters (before shaping), or contain glyphs (the result of shaping). + * + * Since: 0.9.5 + **/ +void +hb_buffer_set_content_type (hb_buffer_t *buffer, + hb_buffer_content_type_t content_type) +{ + buffer->content_type = content_type; +} + +/** + * hb_buffer_get_content_type: + * @buffer: An #hb_buffer_t + * + * Fetches the type of @buffer contents. Buffers are either empty, contain + * characters (before shaping), or contain glyphs (the result of shaping). + * + * Return value: + * The type of @buffer contents + * + * Since: 0.9.5 + **/ +hb_buffer_content_type_t +hb_buffer_get_content_type (const hb_buffer_t *buffer) +{ + return buffer->content_type; +} + + +/** + * hb_buffer_set_unicode_funcs: + * @buffer: An #hb_buffer_t + * @unicode_funcs: The Unicode-functions structure + * + * Sets the Unicode-functions structure of a buffer to + * @unicode_funcs. + * + * Since: 0.9.2 + **/ +void +hb_buffer_set_unicode_funcs (hb_buffer_t *buffer, + hb_unicode_funcs_t *unicode_funcs) +{ + if (unlikely (hb_object_is_immutable (buffer))) + return; + + if (!unicode_funcs) + unicode_funcs = hb_unicode_funcs_get_default (); + + hb_unicode_funcs_reference (unicode_funcs); + hb_unicode_funcs_destroy (buffer->unicode); + buffer->unicode = unicode_funcs; +} + +/** + * hb_buffer_get_unicode_funcs: + * @buffer: An #hb_buffer_t + * + * Fetches the Unicode-functions structure of a buffer. + * + * Return value: The Unicode-functions structure + * + * Since: 0.9.2 + **/ +hb_unicode_funcs_t * +hb_buffer_get_unicode_funcs (const hb_buffer_t *buffer) +{ + return buffer->unicode; +} + +/** + * hb_buffer_set_direction: + * @buffer: An #hb_buffer_t + * @direction: the #hb_direction_t of the @buffer + * + * Set the text flow direction of the buffer. No shaping can happen without + * setting @buffer direction, and it controls the visual direction for the + * output glyphs; for RTL direction the glyphs will be reversed. Many layout + * features depend on the proper setting of the direction, for example, + * reversing RTL text before shaping, then shaping with LTR direction is not + * the same as keeping the text in logical order and shaping with RTL + * direction. + * + * Since: 0.9.2 + **/ +void +hb_buffer_set_direction (hb_buffer_t *buffer, + hb_direction_t direction) + +{ + if (unlikely (hb_object_is_immutable (buffer))) + return; + + buffer->props.direction = direction; +} + +/** + * hb_buffer_get_direction: + * @buffer: An #hb_buffer_t + * + * See hb_buffer_set_direction() + * + * Return value: + * The direction of the @buffer. + * + * Since: 0.9.2 + **/ +hb_direction_t +hb_buffer_get_direction (const hb_buffer_t *buffer) +{ + return buffer->props.direction; +} + +/** + * hb_buffer_set_script: + * @buffer: An #hb_buffer_t + * @script: An #hb_script_t to set. + * + * Sets the script of @buffer to @script. + * + * Script is crucial for choosing the proper shaping behaviour for scripts that + * require it (e.g. Arabic) and the which OpenType features defined in the font + * to be applied. + * + * You can pass one of the predefined #hb_script_t values, or use + * hb_script_from_string() or hb_script_from_iso15924_tag() to get the + * corresponding script from an ISO 15924 script tag. + * + * Since: 0.9.2 + **/ +void +hb_buffer_set_script (hb_buffer_t *buffer, + hb_script_t script) +{ + if (unlikely (hb_object_is_immutable (buffer))) + return; + + buffer->props.script = script; +} + +/** + * hb_buffer_get_script: + * @buffer: An #hb_buffer_t + * + * Fetches the script of @buffer. + * + * Return value: + * The #hb_script_t of the @buffer + * + * Since: 0.9.2 + **/ +hb_script_t +hb_buffer_get_script (const hb_buffer_t *buffer) +{ + return buffer->props.script; +} + +/** + * hb_buffer_set_language: + * @buffer: An #hb_buffer_t + * @language: An hb_language_t to set + * + * Sets the language of @buffer to @language. + * + * Languages are crucial for selecting which OpenType feature to apply to the + * buffer which can result in applying language-specific behaviour. Languages + * are orthogonal to the scripts, and though they are related, they are + * different concepts and should not be confused with each other. + * + * Use hb_language_from_string() to convert from BCP 47 language tags to + * #hb_language_t. + * + * Since: 0.9.2 + **/ +void +hb_buffer_set_language (hb_buffer_t *buffer, + hb_language_t language) +{ + if (unlikely (hb_object_is_immutable (buffer))) + return; + + buffer->props.language = language; +} + +/** + * hb_buffer_get_language: + * @buffer: An #hb_buffer_t + * + * See hb_buffer_set_language(). + * + * Return value: (transfer none): + * The #hb_language_t of the buffer. Must not be freed by the caller. + * + * Since: 0.9.2 + **/ +hb_language_t +hb_buffer_get_language (const hb_buffer_t *buffer) +{ + return buffer->props.language; +} + +/** + * hb_buffer_set_segment_properties: + * @buffer: An #hb_buffer_t + * @props: An #hb_segment_properties_t to use + * + * Sets the segment properties of the buffer, a shortcut for calling + * hb_buffer_set_direction(), hb_buffer_set_script() and + * hb_buffer_set_language() individually. + * + * Since: 0.9.7 + **/ +void +hb_buffer_set_segment_properties (hb_buffer_t *buffer, + const hb_segment_properties_t *props) +{ + if (unlikely (hb_object_is_immutable (buffer))) + return; + + buffer->props = *props; +} + +/** + * hb_buffer_get_segment_properties: + * @buffer: An #hb_buffer_t + * @props: (out): The output #hb_segment_properties_t + * + * Sets @props to the #hb_segment_properties_t of @buffer. + * + * Since: 0.9.7 + **/ +void +hb_buffer_get_segment_properties (const hb_buffer_t *buffer, + hb_segment_properties_t *props) +{ + *props = buffer->props; +} + + +/** + * hb_buffer_set_flags: + * @buffer: An #hb_buffer_t + * @flags: The buffer flags to set + * + * Sets @buffer flags to @flags. See #hb_buffer_flags_t. + * + * Since: 0.9.7 + **/ +void +hb_buffer_set_flags (hb_buffer_t *buffer, + hb_buffer_flags_t flags) +{ + if (unlikely (hb_object_is_immutable (buffer))) + return; + + buffer->flags = flags; +} + +/** + * hb_buffer_get_flags: + * @buffer: An #hb_buffer_t + * + * Fetches the #hb_buffer_flags_t of @buffer. + * + * Return value: + * The @buffer flags + * + * Since: 0.9.7 + **/ +hb_buffer_flags_t +hb_buffer_get_flags (const hb_buffer_t *buffer) +{ + return buffer->flags; +} + +/** + * hb_buffer_set_cluster_level: + * @buffer: An #hb_buffer_t + * @cluster_level: The cluster level to set on the buffer + * + * Sets the cluster level of a buffer. The #hb_buffer_cluster_level_t + * dictates one aspect of how HarfBuzz will treat non-base characters + * during shaping. + * + * Since: 0.9.42 + **/ +void +hb_buffer_set_cluster_level (hb_buffer_t *buffer, + hb_buffer_cluster_level_t cluster_level) +{ + if (unlikely (hb_object_is_immutable (buffer))) + return; + + buffer->cluster_level = cluster_level; +} + +/** + * hb_buffer_get_cluster_level: + * @buffer: An #hb_buffer_t + * + * Fetches the cluster level of a buffer. The #hb_buffer_cluster_level_t + * dictates one aspect of how HarfBuzz will treat non-base characters + * during shaping. + * + * Return value: The cluster level of @buffer + * + * Since: 0.9.42 + **/ +hb_buffer_cluster_level_t +hb_buffer_get_cluster_level (const hb_buffer_t *buffer) +{ + return buffer->cluster_level; +} + + +/** + * hb_buffer_set_replacement_codepoint: + * @buffer: An #hb_buffer_t + * @replacement: the replacement #hb_codepoint_t + * + * Sets the #hb_codepoint_t that replaces invalid entries for a given encoding + * when adding text to @buffer. + * + * Default is #HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT. + * + * Since: 0.9.31 + **/ +void +hb_buffer_set_replacement_codepoint (hb_buffer_t *buffer, + hb_codepoint_t replacement) +{ + if (unlikely (hb_object_is_immutable (buffer))) + return; + + buffer->replacement = replacement; +} + +/** + * hb_buffer_get_replacement_codepoint: + * @buffer: An #hb_buffer_t + * + * Fetches the #hb_codepoint_t that replaces invalid entries for a given encoding + * when adding text to @buffer. + * + * Return value: + * The @buffer replacement #hb_codepoint_t + * + * Since: 0.9.31 + **/ +hb_codepoint_t +hb_buffer_get_replacement_codepoint (const hb_buffer_t *buffer) +{ + return buffer->replacement; +} + + +/** + * hb_buffer_set_invisible_glyph: + * @buffer: An #hb_buffer_t + * @invisible: the invisible #hb_codepoint_t + * + * Sets the #hb_codepoint_t that replaces invisible characters in + * the shaping result. If set to zero (default), the glyph for the + * U+0020 SPACE character is used. Otherwise, this value is used + * verbatim. + * + * Since: 2.0.0 + **/ +void +hb_buffer_set_invisible_glyph (hb_buffer_t *buffer, + hb_codepoint_t invisible) +{ + if (unlikely (hb_object_is_immutable (buffer))) + return; + + buffer->invisible = invisible; +} + +/** + * hb_buffer_get_invisible_glyph: + * @buffer: An #hb_buffer_t + * + * See hb_buffer_set_invisible_glyph(). + * + * Return value: + * The @buffer invisible #hb_codepoint_t + * + * Since: 2.0.0 + **/ +hb_codepoint_t +hb_buffer_get_invisible_glyph (const hb_buffer_t *buffer) +{ + return buffer->invisible; +} + +/** + * hb_buffer_set_not_found_glyph: + * @buffer: An #hb_buffer_t + * @not_found: the not-found #hb_codepoint_t + * + * Sets the #hb_codepoint_t that replaces characters not found in + * the font during shaping. + * + * The not-found glyph defaults to zero, sometimes knows as the + * ".notdef" glyph. This API allows for differentiating the two. + * + * Since: 3.1.0 + **/ +void +hb_buffer_set_not_found_glyph (hb_buffer_t *buffer, + hb_codepoint_t not_found) +{ + if (unlikely (hb_object_is_immutable (buffer))) + return; + + buffer->not_found = not_found; +} + +/** + * hb_buffer_get_not_found_glyph: + * @buffer: An #hb_buffer_t + * + * See hb_buffer_set_not_found_glyph(). + * + * Return value: + * The @buffer not-found #hb_codepoint_t + * + * Since: 3.1.0 + **/ +hb_codepoint_t +hb_buffer_get_not_found_glyph (const hb_buffer_t *buffer) +{ + return buffer->not_found; +} + + +/** + * hb_buffer_clear_contents: + * @buffer: An #hb_buffer_t + * + * Similar to hb_buffer_reset(), but does not clear the Unicode functions and + * the replacement code point. + * + * Since: 0.9.11 + **/ +void +hb_buffer_clear_contents (hb_buffer_t *buffer) +{ + if (unlikely (hb_object_is_immutable (buffer))) + return; + + buffer->clear (); +} + +/** + * hb_buffer_pre_allocate: + * @buffer: An #hb_buffer_t + * @size: Number of items to pre allocate. + * + * Pre allocates memory for @buffer to fit at least @size number of items. + * + * Return value: + * `true` if @buffer memory allocation succeeded, `false` otherwise + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_buffer_pre_allocate (hb_buffer_t *buffer, unsigned int size) +{ + return buffer->ensure (size); +} + +/** + * hb_buffer_allocation_successful: + * @buffer: An #hb_buffer_t + * + * Check if allocating memory for the buffer succeeded. + * + * Return value: + * `true` if @buffer memory allocation succeeded, `false` otherwise. + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_buffer_allocation_successful (hb_buffer_t *buffer) +{ + return buffer->successful; +} + +/** + * hb_buffer_add: + * @buffer: An #hb_buffer_t + * @codepoint: A Unicode code point. + * @cluster: The cluster value of @codepoint. + * + * Appends a character with the Unicode value of @codepoint to @buffer, and + * gives it the initial cluster value of @cluster. Clusters can be any thing + * the client wants, they are usually used to refer to the index of the + * character in the input text stream and are output in + * #hb_glyph_info_t.cluster field. + * + * This function does not check the validity of @codepoint, it is up to the + * caller to ensure it is a valid Unicode code point. + * + * Since: 0.9.7 + **/ +void +hb_buffer_add (hb_buffer_t *buffer, + hb_codepoint_t codepoint, + unsigned int cluster) +{ + buffer->add (codepoint, cluster); + buffer->clear_context (1); +} + +/** + * hb_buffer_set_length: + * @buffer: An #hb_buffer_t + * @length: The new length of @buffer + * + * Similar to hb_buffer_pre_allocate(), but clears any new items added at the + * end. + * + * Return value: + * `true` if @buffer memory allocation succeeded, `false` otherwise. + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_buffer_set_length (hb_buffer_t *buffer, + unsigned int length) +{ + if (unlikely (hb_object_is_immutable (buffer))) + return length == 0; + + if (unlikely (!buffer->ensure (length))) + return false; + + /* Wipe the new space */ + if (length > buffer->len) { + memset (buffer->info + buffer->len, 0, sizeof (buffer->info[0]) * (length - buffer->len)); + if (buffer->have_positions) + memset (buffer->pos + buffer->len, 0, sizeof (buffer->pos[0]) * (length - buffer->len)); + } + + buffer->len = length; + + if (!length) + { + buffer->content_type = HB_BUFFER_CONTENT_TYPE_INVALID; + buffer->clear_context (0); + } + buffer->clear_context (1); + + return true; +} + +/** + * hb_buffer_get_length: + * @buffer: An #hb_buffer_t + * + * Returns the number of items in the buffer. + * + * Return value: + * The @buffer length. + * The value valid as long as buffer has not been modified. + * + * Since: 0.9.2 + **/ +unsigned int +hb_buffer_get_length (const hb_buffer_t *buffer) +{ + return buffer->len; +} + +/** + * hb_buffer_get_glyph_infos: + * @buffer: An #hb_buffer_t + * @length: (out): The output-array length. + * + * Returns @buffer glyph information array. Returned pointer + * is valid as long as @buffer contents are not modified. + * + * Return value: (transfer none) (array length=length): + * The @buffer glyph information array. + * The value valid as long as buffer has not been modified. + * + * Since: 0.9.2 + **/ +hb_glyph_info_t * +hb_buffer_get_glyph_infos (hb_buffer_t *buffer, + unsigned int *length) +{ + if (length) + *length = buffer->len; + + return (hb_glyph_info_t *) buffer->info; +} + +/** + * hb_buffer_get_glyph_positions: + * @buffer: An #hb_buffer_t + * @length: (out): The output length + * + * Returns @buffer glyph position array. Returned pointer + * is valid as long as @buffer contents are not modified. + * + * If buffer did not have positions before, the positions will be + * initialized to zeros, unless this function is called from + * within a buffer message callback (see hb_buffer_set_message_func()), + * in which case `NULL` is returned. + * + * Return value: (transfer none) (array length=length): + * The @buffer glyph position array. + * The value valid as long as buffer has not been modified. + * + * Since: 0.9.2 + **/ +hb_glyph_position_t * +hb_buffer_get_glyph_positions (hb_buffer_t *buffer, + unsigned int *length) +{ + if (length) + *length = buffer->len; + + if (!buffer->have_positions) + { + if (unlikely (buffer->message_depth)) + return nullptr; + + buffer->clear_positions (); + } + + return (hb_glyph_position_t *) buffer->pos; +} + +/** + * hb_buffer_has_positions: + * @buffer: an #hb_buffer_t. + * + * Returns whether @buffer has glyph position data. + * A buffer gains position data when hb_buffer_get_glyph_positions() is called on it, + * and cleared of position data when hb_buffer_clear_contents() is called. + * + * Return value: + * `true` if the @buffer has position array, `false` otherwise. + * + * Since: 2.7.3 + **/ +HB_EXTERN hb_bool_t +hb_buffer_has_positions (hb_buffer_t *buffer) +{ + return buffer->have_positions; +} + +/** + * hb_glyph_info_get_glyph_flags: + * @info: a #hb_glyph_info_t + * + * Returns glyph flags encoded within a #hb_glyph_info_t. + * + * Return value: + * The #hb_glyph_flags_t encoded within @info + * + * Since: 1.5.0 + **/ +hb_glyph_flags_t +(hb_glyph_info_get_glyph_flags) (const hb_glyph_info_t *info) +{ + return hb_glyph_info_get_glyph_flags (info); +} + +/** + * hb_buffer_reverse: + * @buffer: An #hb_buffer_t + * + * Reverses buffer contents. + * + * Since: 0.9.2 + **/ +void +hb_buffer_reverse (hb_buffer_t *buffer) +{ + buffer->reverse (); +} + +/** + * hb_buffer_reverse_range: + * @buffer: An #hb_buffer_t + * @start: start index + * @end: end index + * + * Reverses buffer contents between @start and @end. + * + * Since: 0.9.41 + **/ +void +hb_buffer_reverse_range (hb_buffer_t *buffer, + unsigned int start, unsigned int end) +{ + buffer->reverse_range (start, end); +} + +/** + * hb_buffer_reverse_clusters: + * @buffer: An #hb_buffer_t + * + * Reverses buffer clusters. That is, the buffer contents are + * reversed, then each cluster (consecutive items having the + * same cluster number) are reversed again. + * + * Since: 0.9.2 + **/ +void +hb_buffer_reverse_clusters (hb_buffer_t *buffer) +{ + buffer->reverse_clusters (); +} + +/** + * hb_buffer_guess_segment_properties: + * @buffer: An #hb_buffer_t + * + * Sets unset buffer segment properties based on buffer Unicode + * contents. If buffer is not empty, it must have content type + * #HB_BUFFER_CONTENT_TYPE_UNICODE. + * + * If buffer script is not set (ie. is #HB_SCRIPT_INVALID), it + * will be set to the Unicode script of the first character in + * the buffer that has a script other than #HB_SCRIPT_COMMON, + * #HB_SCRIPT_INHERITED, and #HB_SCRIPT_UNKNOWN. + * + * Next, if buffer direction is not set (ie. is #HB_DIRECTION_INVALID), + * it will be set to the natural horizontal direction of the + * buffer script as returned by hb_script_get_horizontal_direction(). + * If hb_script_get_horizontal_direction() returns #HB_DIRECTION_INVALID, + * then #HB_DIRECTION_LTR is used. + * + * Finally, if buffer language is not set (ie. is #HB_LANGUAGE_INVALID), + * it will be set to the process's default language as returned by + * hb_language_get_default(). This may change in the future by + * taking buffer script into consideration when choosing a language. + * Note that hb_language_get_default() is NOT threadsafe the first time + * it is called. See documentation for that function for details. + * + * Since: 0.9.7 + **/ +void +hb_buffer_guess_segment_properties (hb_buffer_t *buffer) +{ + buffer->guess_segment_properties (); +} + +template +static inline void +hb_buffer_add_utf (hb_buffer_t *buffer, + const typename utf_t::codepoint_t *text, + int text_length, + unsigned int item_offset, + int item_length) +{ + typedef typename utf_t::codepoint_t T; + const hb_codepoint_t replacement = buffer->replacement; + + buffer->assert_unicode (); + + if (unlikely (hb_object_is_immutable (buffer))) + return; + + if (text_length == -1) + text_length = utf_t::strlen (text); + + if (item_length == -1) + item_length = text_length - item_offset; + + if (unlikely (item_length < 0 || + item_length > INT_MAX / 8 || + !buffer->ensure (buffer->len + item_length * sizeof (T) / 4))) + return; + + /* If buffer is empty and pre-context provided, install it. + * This check is written this way, to make sure people can + * provide pre-context in one add_utf() call, then provide + * text in a follow-up call. See: + * + * https://bugzilla.mozilla.org/show_bug.cgi?id=801410#c13 + */ + if (!buffer->len && item_offset > 0) + { + /* Add pre-context */ + buffer->clear_context (0); + const T *prev = text + item_offset; + const T *start = text; + while (start < prev && buffer->context_len[0] < buffer->CONTEXT_LENGTH) + { + hb_codepoint_t u; + prev = utf_t::prev (prev, start, &u, replacement); + buffer->context[0][buffer->context_len[0]++] = u; + } + } + + const T *next = text + item_offset; + const T *end = next + item_length; + while (next < end) + { + hb_codepoint_t u; + const T *old_next = next; + next = utf_t::next (next, end, &u, replacement); + buffer->add (u, old_next - (const T *) text); + } + + /* Add post-context */ + buffer->clear_context (1); + end = text + text_length; + while (next < end && buffer->context_len[1] < buffer->CONTEXT_LENGTH) + { + hb_codepoint_t u; + next = utf_t::next (next, end, &u, replacement); + buffer->context[1][buffer->context_len[1]++] = u; + } + + buffer->content_type = HB_BUFFER_CONTENT_TYPE_UNICODE; +} + +/** + * hb_buffer_add_utf8: + * @buffer: An #hb_buffer_t + * @text: (array length=text_length) (element-type uint8_t): An array of UTF-8 + * characters to append. + * @text_length: The length of the @text, or -1 if it is `NULL` terminated. + * @item_offset: The offset of the first character to add to the @buffer. + * @item_length: The number of characters to add to the @buffer, or -1 for the + * end of @text (assuming it is `NULL` terminated). + * + * See hb_buffer_add_codepoints(). + * + * Replaces invalid UTF-8 characters with the @buffer replacement code point, + * see hb_buffer_set_replacement_codepoint(). + * + * Since: 0.9.2 + **/ +void +hb_buffer_add_utf8 (hb_buffer_t *buffer, + const char *text, + int text_length, + unsigned int item_offset, + int item_length) +{ + hb_buffer_add_utf (buffer, (const uint8_t *) text, text_length, item_offset, item_length); +} + +/** + * hb_buffer_add_utf16: + * @buffer: An #hb_buffer_t + * @text: (array length=text_length): An array of UTF-16 characters to append + * @text_length: The length of the @text, or -1 if it is `NULL` terminated + * @item_offset: The offset of the first character to add to the @buffer + * @item_length: The number of characters to add to the @buffer, or -1 for the + * end of @text (assuming it is `NULL` terminated) + * + * See hb_buffer_add_codepoints(). + * + * Replaces invalid UTF-16 characters with the @buffer replacement code point, + * see hb_buffer_set_replacement_codepoint(). + * + * Since: 0.9.2 + **/ +void +hb_buffer_add_utf16 (hb_buffer_t *buffer, + const uint16_t *text, + int text_length, + unsigned int item_offset, + int item_length) +{ + hb_buffer_add_utf (buffer, text, text_length, item_offset, item_length); +} + +/** + * hb_buffer_add_utf32: + * @buffer: An #hb_buffer_t + * @text: (array length=text_length): An array of UTF-32 characters to append + * @text_length: The length of the @text, or -1 if it is `NULL` terminated + * @item_offset: The offset of the first character to add to the @buffer + * @item_length: The number of characters to add to the @buffer, or -1 for the + * end of @text (assuming it is `NULL` terminated) + * + * See hb_buffer_add_codepoints(). + * + * Replaces invalid UTF-32 characters with the @buffer replacement code point, + * see hb_buffer_set_replacement_codepoint(). + * + * Since: 0.9.2 + **/ +void +hb_buffer_add_utf32 (hb_buffer_t *buffer, + const uint32_t *text, + int text_length, + unsigned int item_offset, + int item_length) +{ + hb_buffer_add_utf (buffer, text, text_length, item_offset, item_length); +} + +/** + * hb_buffer_add_latin1: + * @buffer: An #hb_buffer_t + * @text: (array length=text_length) (element-type uint8_t): an array of UTF-8 + * characters to append + * @text_length: the length of the @text, or -1 if it is `NULL` terminated + * @item_offset: the offset of the first character to add to the @buffer + * @item_length: the number of characters to add to the @buffer, or -1 for the + * end of @text (assuming it is `NULL` terminated) + * + * Similar to hb_buffer_add_codepoints(), but allows only access to first 256 + * Unicode code points that can fit in 8-bit strings. + * + * Has nothing to do with non-Unicode Latin-1 encoding. + * + * Since: 0.9.39 + **/ +void +hb_buffer_add_latin1 (hb_buffer_t *buffer, + const uint8_t *text, + int text_length, + unsigned int item_offset, + int item_length) +{ + hb_buffer_add_utf (buffer, text, text_length, item_offset, item_length); +} + +/** + * hb_buffer_add_codepoints: + * @buffer: a #hb_buffer_t to append characters to. + * @text: (array length=text_length): an array of Unicode code points to append. + * @text_length: the length of the @text, or -1 if it is `NULL` terminated. + * @item_offset: the offset of the first code point to add to the @buffer. + * @item_length: the number of code points to add to the @buffer, or -1 for the + * end of @text (assuming it is `NULL` terminated). + * + * Appends characters from @text array to @buffer. The @item_offset is the + * position of the first character from @text that will be appended, and + * @item_length is the number of character. When shaping part of a larger text + * (e.g. a run of text from a paragraph), instead of passing just the substring + * corresponding to the run, it is preferable to pass the whole + * paragraph and specify the run start and length as @item_offset and + * @item_length, respectively, to give HarfBuzz the full context to be able, + * for example, to do cross-run Arabic shaping or properly handle combining + * marks at stat of run. + * + * This function does not check the validity of @text, it is up to the caller + * to ensure it contains a valid Unicode code points. + * + * Since: 0.9.31 + **/ +void +hb_buffer_add_codepoints (hb_buffer_t *buffer, + const hb_codepoint_t *text, + int text_length, + unsigned int item_offset, + int item_length) +{ + hb_buffer_add_utf (buffer, text, text_length, item_offset, item_length); +} + + +/** + * hb_buffer_append: + * @buffer: An #hb_buffer_t + * @source: source #hb_buffer_t + * @start: start index into source buffer to copy. Use 0 to copy from start of buffer. + * @end: end index into source buffer to copy. Use @HB_FEATURE_GLOBAL_END to copy to end of buffer. + * + * Append (part of) contents of another buffer to this buffer. + * + * Since: 1.5.0 + **/ +HB_EXTERN void +hb_buffer_append (hb_buffer_t *buffer, + const hb_buffer_t *source, + unsigned int start, + unsigned int end) +{ + assert (!buffer->have_output && !source->have_output); + assert (buffer->have_positions == source->have_positions || + !buffer->len || !source->len); + assert (buffer->content_type == source->content_type || + !buffer->len || !source->len); + + if (end > source->len) + end = source->len; + if (start > end) + start = end; + if (start == end) + return; + + if (buffer->len + (end - start) < buffer->len) /* Overflows. */ + { + buffer->successful = false; + return; + } + + unsigned int orig_len = buffer->len; + hb_buffer_set_length (buffer, buffer->len + (end - start)); + if (unlikely (!buffer->successful)) + return; + + if (!orig_len) + buffer->content_type = source->content_type; + if (!buffer->have_positions && source->have_positions) + buffer->clear_positions (); + + hb_segment_properties_overlay (&buffer->props, &source->props); + + memcpy (buffer->info + orig_len, source->info + start, (end - start) * sizeof (buffer->info[0])); + if (buffer->have_positions) + memcpy (buffer->pos + orig_len, source->pos + start, (end - start) * sizeof (buffer->pos[0])); + + if (source->content_type == HB_BUFFER_CONTENT_TYPE_UNICODE) + { + /* See similar logic in add_utf. */ + + /* pre-context */ + if (!orig_len && start + source->context_len[0] > 0) + { + buffer->clear_context (0); + while (start > 0 && buffer->context_len[0] < buffer->CONTEXT_LENGTH) + buffer->context[0][buffer->context_len[0]++] = source->info[--start].codepoint; + for (auto i = 0u; i < source->context_len[0] && buffer->context_len[0] < buffer->CONTEXT_LENGTH; i++) + buffer->context[0][buffer->context_len[0]++] = source->context[0][i]; + } + + /* post-context */ + buffer->clear_context (1); + while (end < source->len && buffer->context_len[1] < buffer->CONTEXT_LENGTH) + buffer->context[1][buffer->context_len[1]++] = source->info[end++].codepoint; + for (auto i = 0u; i < source->context_len[1] && buffer->context_len[1] < buffer->CONTEXT_LENGTH; i++) + buffer->context[1][buffer->context_len[1]++] = source->context[1][i]; + } +} + + +static int +compare_info_codepoint (const hb_glyph_info_t *pa, + const hb_glyph_info_t *pb) +{ + return (int) pb->codepoint - (int) pa->codepoint; +} + +static inline void +normalize_glyphs_cluster (hb_buffer_t *buffer, + unsigned int start, + unsigned int end, + bool backward) +{ + hb_glyph_position_t *pos = buffer->pos; + + /* Total cluster advance */ + hb_position_t total_x_advance = 0, total_y_advance = 0; + for (unsigned int i = start; i < end; i++) + { + total_x_advance += pos[i].x_advance; + total_y_advance += pos[i].y_advance; + } + + hb_position_t x_advance = 0, y_advance = 0; + for (unsigned int i = start; i < end; i++) + { + pos[i].x_offset += x_advance; + pos[i].y_offset += y_advance; + + x_advance += pos[i].x_advance; + y_advance += pos[i].y_advance; + + pos[i].x_advance = 0; + pos[i].y_advance = 0; + } + + if (backward) + { + /* Transfer all cluster advance to the last glyph. */ + pos[end - 1].x_advance = total_x_advance; + pos[end - 1].y_advance = total_y_advance; + + hb_stable_sort (buffer->info + start, end - start - 1, compare_info_codepoint, buffer->pos + start); + } else { + /* Transfer all cluster advance to the first glyph. */ + pos[start].x_advance += total_x_advance; + pos[start].y_advance += total_y_advance; + for (unsigned int i = start + 1; i < end; i++) { + pos[i].x_offset -= total_x_advance; + pos[i].y_offset -= total_y_advance; + } + hb_stable_sort (buffer->info + start + 1, end - start - 1, compare_info_codepoint, buffer->pos + start + 1); + } +} + +/** + * hb_buffer_normalize_glyphs: + * @buffer: An #hb_buffer_t + * + * Reorders a glyph buffer to have canonical in-cluster glyph order / position. + * The resulting clusters should behave identical to pre-reordering clusters. + * + * This has nothing to do with Unicode normalization. + * + * Since: 0.9.2 + **/ +void +hb_buffer_normalize_glyphs (hb_buffer_t *buffer) +{ + assert (buffer->have_positions); + + buffer->assert_glyphs (); + + bool backward = HB_DIRECTION_IS_BACKWARD (buffer->props.direction); + + foreach_cluster (buffer, start, end) + normalize_glyphs_cluster (buffer, start, end, backward); +} + +void +hb_buffer_t::sort (unsigned int start, unsigned int end, int(*compar)(const hb_glyph_info_t *, const hb_glyph_info_t *)) +{ + assert (!have_positions); + for (unsigned int i = start + 1; i < end; i++) + { + unsigned int j = i; + while (j > start && compar (&info[j - 1], &info[i]) > 0) + j--; + if (i == j) + continue; + /* Move item i to occupy place for item j, shift what's in between. */ + merge_clusters (j, i + 1); + { + hb_glyph_info_t t = info[i]; + memmove (&info[j + 1], &info[j], (i - j) * sizeof (hb_glyph_info_t)); + info[j] = t; + } + } +} + + +/* + * Comparing buffers. + */ + +/** + * hb_buffer_diff: + * @buffer: a buffer. + * @reference: other buffer to compare to. + * @dottedcircle_glyph: glyph id of U+25CC DOTTED CIRCLE, or (hb_codepont_t) -1. + * @position_fuzz: allowed absolute difference in position values. + * + * If dottedcircle_glyph is (hb_codepoint_t) -1 then #HB_BUFFER_DIFF_FLAG_DOTTED_CIRCLE_PRESENT + * and #HB_BUFFER_DIFF_FLAG_NOTDEF_PRESENT are never returned. This should be used by most + * callers if just comparing two buffers is needed. + * + * Since: 1.5.0 + **/ +hb_buffer_diff_flags_t +hb_buffer_diff (hb_buffer_t *buffer, + hb_buffer_t *reference, + hb_codepoint_t dottedcircle_glyph, + unsigned int position_fuzz) +{ + if (buffer->content_type != reference->content_type && buffer->len && reference->len) + return HB_BUFFER_DIFF_FLAG_CONTENT_TYPE_MISMATCH; + + hb_buffer_diff_flags_t result = HB_BUFFER_DIFF_FLAG_EQUAL; + bool contains = dottedcircle_glyph != (hb_codepoint_t) -1; + + unsigned int count = reference->len; + + if (buffer->len != count) + { + /* + * we can't compare glyph-by-glyph, but we do want to know if there + * are .notdef or dottedcircle glyphs present in the reference buffer + */ + const hb_glyph_info_t *info = reference->info; + unsigned int i; + for (i = 0; i < count; i++) + { + if (contains && info[i].codepoint == dottedcircle_glyph) + result |= HB_BUFFER_DIFF_FLAG_DOTTED_CIRCLE_PRESENT; + if (contains && info[i].codepoint == 0) + result |= HB_BUFFER_DIFF_FLAG_NOTDEF_PRESENT; + } + result |= HB_BUFFER_DIFF_FLAG_LENGTH_MISMATCH; + return hb_buffer_diff_flags_t (result); + } + + if (!count) + return hb_buffer_diff_flags_t (result); + + const hb_glyph_info_t *buf_info = buffer->info; + const hb_glyph_info_t *ref_info = reference->info; + for (unsigned int i = 0; i < count; i++) + { + if (buf_info->codepoint != ref_info->codepoint) + result |= HB_BUFFER_DIFF_FLAG_CODEPOINT_MISMATCH; + if (buf_info->cluster != ref_info->cluster) + result |= HB_BUFFER_DIFF_FLAG_CLUSTER_MISMATCH; + if ((buf_info->mask & ~ref_info->mask & HB_GLYPH_FLAG_DEFINED)) + result |= HB_BUFFER_DIFF_FLAG_GLYPH_FLAGS_MISMATCH; + if (contains && ref_info->codepoint == dottedcircle_glyph) + result |= HB_BUFFER_DIFF_FLAG_DOTTED_CIRCLE_PRESENT; + if (contains && ref_info->codepoint == 0) + result |= HB_BUFFER_DIFF_FLAG_NOTDEF_PRESENT; + buf_info++; + ref_info++; + } + + if (buffer->content_type == HB_BUFFER_CONTENT_TYPE_GLYPHS) + { + assert (buffer->have_positions); + const hb_glyph_position_t *buf_pos = buffer->pos; + const hb_glyph_position_t *ref_pos = reference->pos; + for (unsigned int i = 0; i < count; i++) + { + if ((unsigned int) abs (buf_pos->x_advance - ref_pos->x_advance) > position_fuzz || + (unsigned int) abs (buf_pos->y_advance - ref_pos->y_advance) > position_fuzz || + (unsigned int) abs (buf_pos->x_offset - ref_pos->x_offset) > position_fuzz || + (unsigned int) abs (buf_pos->y_offset - ref_pos->y_offset) > position_fuzz) + { + result |= HB_BUFFER_DIFF_FLAG_POSITION_MISMATCH; + break; + } + buf_pos++; + ref_pos++; + } + } + + return result; +} + + +/* + * Debugging. + */ + +#ifndef HB_NO_BUFFER_MESSAGE +/** + * hb_buffer_set_message_func: + * @buffer: An #hb_buffer_t + * @func: (closure user_data) (destroy destroy) (scope notified): Callback function + * @user_data: (nullable): Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is not needed anymore + * + * Sets the implementation function for #hb_buffer_message_func_t. + * + * Since: 1.1.3 + **/ +void +hb_buffer_set_message_func (hb_buffer_t *buffer, + hb_buffer_message_func_t func, + void *user_data, hb_destroy_func_t destroy) +{ + if (buffer->message_destroy) + buffer->message_destroy (buffer->message_data); + + if (func) { + buffer->message_func = func; + buffer->message_data = user_data; + buffer->message_destroy = destroy; + } else { + buffer->message_func = nullptr; + buffer->message_data = nullptr; + buffer->message_destroy = nullptr; + } +} +bool +hb_buffer_t::message_impl (hb_font_t *font, const char *fmt, va_list ap) +{ + assert (!have_output || (out_info == info && out_len == idx)); + + message_depth++; + + char buf[100]; + vsnprintf (buf, sizeof (buf), fmt, ap); + bool ret = (bool) this->message_func (this, font, buf, this->message_data); + + message_depth--; + + return ret; +} +#endif diff --git a/libs/harfbuzz/src/hb-buffer.h b/libs/harfbuzz/src/hb-buffer.h new file mode 100644 index 00000000..8c174898 --- /dev/null +++ b/libs/harfbuzz/src/hb-buffer.h @@ -0,0 +1,799 @@ +/* + * Copyright © 1998-2004 David Turner and Werner Lemberg + * Copyright © 2004,2007,2009 Red Hat, Inc. + * Copyright © 2011,2012 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. + * + * Red Hat Author(s): Owen Taylor, Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#if !defined(HB_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR) +#error "Include instead." +#endif + +#ifndef HB_BUFFER_H +#define HB_BUFFER_H + +#include "hb-common.h" +#include "hb-unicode.h" +#include "hb-font.h" + +HB_BEGIN_DECLS + +/** + * hb_glyph_info_t: + * @codepoint: either a Unicode code point (before shaping) or a glyph index + * (after shaping). + * @cluster: the index of the character in the original text that corresponds + * to this #hb_glyph_info_t, or whatever the client passes to + * hb_buffer_add(). More than one #hb_glyph_info_t can have the same + * @cluster value, if they resulted from the same character (e.g. one + * to many glyph substitution), and when more than one character gets + * merged in the same glyph (e.g. many to one glyph substitution) the + * #hb_glyph_info_t will have the smallest cluster value of them. + * By default some characters are merged into the same cluster + * (e.g. combining marks have the same cluster as their bases) + * even if they are separate glyphs, hb_buffer_set_cluster_level() + * allow selecting more fine-grained cluster handling. + * + * The #hb_glyph_info_t is the structure that holds information about the + * glyphs and their relation to input text. + */ +typedef struct hb_glyph_info_t { + hb_codepoint_t codepoint; + /*< private >*/ + hb_mask_t mask; + /*< public >*/ + uint32_t cluster; + + /*< private >*/ + hb_var_int_t var1; + hb_var_int_t var2; +} hb_glyph_info_t; + +/** + * hb_glyph_flags_t: + * @HB_GLYPH_FLAG_UNSAFE_TO_BREAK: Indicates that if input text is broken at the + * beginning of the cluster this glyph is part of, + * then both sides need to be re-shaped, as the + * result might be different. + * On the flip side, it means that when this + * flag is not present, then it is safe to break + * the glyph-run at the beginning of this + * cluster, and the two sides will represent the + * exact same result one would get if breaking + * input text at the beginning of this cluster + * and shaping the two sides separately. + * This can be used to optimize paragraph + * layout, by avoiding re-shaping of each line + * after line-breaking. + * @HB_GLYPH_FLAG_UNSAFE_TO_CONCAT: Indicates that if input text is changed on one + * side of the beginning of the cluster this glyph + * is part of, then the shaping results for the + * other side might change. + * Note that the absence of this flag will NOT by + * itself mean that it IS safe to concat text. + * Only two pieces of text both of which clear of + * this flag can be concatenated safely. + * This can be used to optimize paragraph + * layout, by avoiding re-shaping of each line + * after line-breaking, by limiting the + * reshaping to a small piece around the + * breaking positin only, even if the breaking + * position carries the + * #HB_GLYPH_FLAG_UNSAFE_TO_BREAK or when + * hyphenation or other text transformation + * happens at line-break position, in the following + * way: + * 1. Iterate back from the line-break position + * until the first cluster start position that is + * NOT unsafe-to-concat, 2. shape the segment from + * there till the end of line, 3. check whether the + * resulting glyph-run also is clear of the + * unsafe-to-concat at its start-of-text position; + * if it is, just splice it into place and the line + * is shaped; If not, move on to a position further + * back that is clear of unsafe-to-concat and retry + * from there, and repeat. + * At the start of next line a similar algorithm can + * be implemented. That is: 1. Iterate forward from + * the line-break position until the first cluster + * start position that is NOT unsafe-to-concat, 2. + * shape the segment from beginning of the line to + * that position, 3. check whether the resulting + * glyph-run also is clear of the unsafe-to-concat + * at its end-of-text position; if it is, just splice + * it into place and the beginning is shaped; If not, + * move on to a position further forward that is clear + * of unsafe-to-concat and retry up to there, and repeat. + * A slight complication will arise in the + * implementation of the algorithm above, + * because while our buffer API has a way to + * return flags for position corresponding to + * start-of-text, there is currently no position + * corresponding to end-of-text. This limitation + * can be alleviated by shaping more text than needed + * and looking for unsafe-to-concat flag within text + * clusters. + * The #HB_GLYPH_FLAG_UNSAFE_TO_BREAK flag will + * always imply this flag. + * To use this flag, you must enable the buffer flag + * @HB_BUFFER_FLAG_PRODUCE_UNSAFE_TO_CONCAT during + * shaping, otherwise the buffer flag will not be + * reliably produced. + * Since: 4.0.0 + * @HB_GLYPH_FLAG_SAFE_TO_INSERT_TATWEEL: In scripts that use elongation (Arabic, + Mongolian, Syriac, etc.), this flag signifies + that it is safe to insert a U+0640 TATWEEL + character before this cluster for elongation. + This flag does not determine the + script-specific elongation places, but only + when it is safe to do the elongation without + interrupting text shaping. + Since: 5.1.0 + * @HB_GLYPH_FLAG_DEFINED: All the currently defined flags. + * + * Flags for #hb_glyph_info_t. + * + * Since: 1.5.0 + */ +typedef enum { /*< flags >*/ + HB_GLYPH_FLAG_UNSAFE_TO_BREAK = 0x00000001, + HB_GLYPH_FLAG_UNSAFE_TO_CONCAT = 0x00000002, + HB_GLYPH_FLAG_SAFE_TO_INSERT_TATWEEL = 0x00000004, + + HB_GLYPH_FLAG_DEFINED = 0x00000007 /* OR of all defined flags */ +} hb_glyph_flags_t; + +HB_EXTERN hb_glyph_flags_t +hb_glyph_info_get_glyph_flags (const hb_glyph_info_t *info); + +#define hb_glyph_info_get_glyph_flags(info) \ + ((hb_glyph_flags_t) ((unsigned int) (info)->mask & HB_GLYPH_FLAG_DEFINED)) + + +/** + * hb_glyph_position_t: + * @x_advance: how much the line advances after drawing this glyph when setting + * text in horizontal direction. + * @y_advance: how much the line advances after drawing this glyph when setting + * text in vertical direction. + * @x_offset: how much the glyph moves on the X-axis before drawing it, this + * should not affect how much the line advances. + * @y_offset: how much the glyph moves on the Y-axis before drawing it, this + * should not affect how much the line advances. + * + * The #hb_glyph_position_t is the structure that holds the positions of the + * glyph in both horizontal and vertical directions. All positions in + * #hb_glyph_position_t are relative to the current point. + * + */ +typedef struct hb_glyph_position_t { + hb_position_t x_advance; + hb_position_t y_advance; + hb_position_t x_offset; + hb_position_t y_offset; + + /*< private >*/ + hb_var_int_t var; +} hb_glyph_position_t; + +/** + * hb_segment_properties_t: + * @direction: the #hb_direction_t of the buffer, see hb_buffer_set_direction(). + * @script: the #hb_script_t of the buffer, see hb_buffer_set_script(). + * @language: the #hb_language_t of the buffer, see hb_buffer_set_language(). + * + * The structure that holds various text properties of an #hb_buffer_t. Can be + * set and retrieved using hb_buffer_set_segment_properties() and + * hb_buffer_get_segment_properties(), respectively. + */ +typedef struct hb_segment_properties_t { + hb_direction_t direction; + hb_script_t script; + hb_language_t language; + /*< private >*/ + void *reserved1; + void *reserved2; +} hb_segment_properties_t; + +/** + * HB_SEGMENT_PROPERTIES_DEFAULT: + * + * The default #hb_segment_properties_t of of freshly created #hb_buffer_t. + */ +#define HB_SEGMENT_PROPERTIES_DEFAULT {HB_DIRECTION_INVALID, \ + HB_SCRIPT_INVALID, \ + HB_LANGUAGE_INVALID, \ + (void *) 0, \ + (void *) 0} + +HB_EXTERN hb_bool_t +hb_segment_properties_equal (const hb_segment_properties_t *a, + const hb_segment_properties_t *b); + +HB_EXTERN unsigned int +hb_segment_properties_hash (const hb_segment_properties_t *p); + +HB_EXTERN void +hb_segment_properties_overlay (hb_segment_properties_t *p, + const hb_segment_properties_t *src); + + +/** + * hb_buffer_t: + * + * The main structure holding the input text and its properties before shaping, + * and output glyphs and their information after shaping. + */ + +typedef struct hb_buffer_t hb_buffer_t; + +HB_EXTERN hb_buffer_t * +hb_buffer_create (void); + +HB_EXTERN hb_buffer_t * +hb_buffer_create_similar (const hb_buffer_t *src); + +HB_EXTERN void +hb_buffer_reset (hb_buffer_t *buffer); + + +HB_EXTERN hb_buffer_t * +hb_buffer_get_empty (void); + +HB_EXTERN hb_buffer_t * +hb_buffer_reference (hb_buffer_t *buffer); + +HB_EXTERN void +hb_buffer_destroy (hb_buffer_t *buffer); + +HB_EXTERN hb_bool_t +hb_buffer_set_user_data (hb_buffer_t *buffer, + hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy, + hb_bool_t replace); + +HB_EXTERN void * +hb_buffer_get_user_data (const hb_buffer_t *buffer, + hb_user_data_key_t *key); + + +/** + * hb_buffer_content_type_t: + * @HB_BUFFER_CONTENT_TYPE_INVALID: Initial value for new buffer. + * @HB_BUFFER_CONTENT_TYPE_UNICODE: The buffer contains input characters (before shaping). + * @HB_BUFFER_CONTENT_TYPE_GLYPHS: The buffer contains output glyphs (after shaping). + * + * The type of #hb_buffer_t contents. + */ +typedef enum { + HB_BUFFER_CONTENT_TYPE_INVALID = 0, + HB_BUFFER_CONTENT_TYPE_UNICODE, + HB_BUFFER_CONTENT_TYPE_GLYPHS +} hb_buffer_content_type_t; + +HB_EXTERN void +hb_buffer_set_content_type (hb_buffer_t *buffer, + hb_buffer_content_type_t content_type); + +HB_EXTERN hb_buffer_content_type_t +hb_buffer_get_content_type (const hb_buffer_t *buffer); + + +HB_EXTERN void +hb_buffer_set_unicode_funcs (hb_buffer_t *buffer, + hb_unicode_funcs_t *unicode_funcs); + +HB_EXTERN hb_unicode_funcs_t * +hb_buffer_get_unicode_funcs (const hb_buffer_t *buffer); + +HB_EXTERN void +hb_buffer_set_direction (hb_buffer_t *buffer, + hb_direction_t direction); + +HB_EXTERN hb_direction_t +hb_buffer_get_direction (const hb_buffer_t *buffer); + +HB_EXTERN void +hb_buffer_set_script (hb_buffer_t *buffer, + hb_script_t script); + +HB_EXTERN hb_script_t +hb_buffer_get_script (const hb_buffer_t *buffer); + +HB_EXTERN void +hb_buffer_set_language (hb_buffer_t *buffer, + hb_language_t language); + + +HB_EXTERN hb_language_t +hb_buffer_get_language (const hb_buffer_t *buffer); + +HB_EXTERN void +hb_buffer_set_segment_properties (hb_buffer_t *buffer, + const hb_segment_properties_t *props); + +HB_EXTERN void +hb_buffer_get_segment_properties (const hb_buffer_t *buffer, + hb_segment_properties_t *props); + +HB_EXTERN void +hb_buffer_guess_segment_properties (hb_buffer_t *buffer); + + +/** + * hb_buffer_flags_t: + * @HB_BUFFER_FLAG_DEFAULT: the default buffer flag. + * @HB_BUFFER_FLAG_BOT: flag indicating that special handling of the beginning + * of text paragraph can be applied to this buffer. Should usually + * be set, unless you are passing to the buffer only part + * of the text without the full context. + * @HB_BUFFER_FLAG_EOT: flag indicating that special handling of the end of text + * paragraph can be applied to this buffer, similar to + * @HB_BUFFER_FLAG_BOT. + * @HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES: + * flag indication that character with Default_Ignorable + * Unicode property should use the corresponding glyph + * from the font, instead of hiding them (done by + * replacing them with the space glyph and zeroing the + * advance width.) This flag takes precedence over + * @HB_BUFFER_FLAG_REMOVE_DEFAULT_IGNORABLES. + * @HB_BUFFER_FLAG_REMOVE_DEFAULT_IGNORABLES: + * flag indication that character with Default_Ignorable + * Unicode property should be removed from glyph string + * instead of hiding them (done by replacing them with the + * space glyph and zeroing the advance width.) + * @HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES takes + * precedence over this flag. Since: 1.8.0 + * @HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE: + * flag indicating that a dotted circle should + * not be inserted in the rendering of incorrect + * character sequences (such at <0905 093E>). Since: 2.4.0 + * @HB_BUFFER_FLAG_VERIFY: + * flag indicating that the hb_shape() call and its variants + * should perform various verification processes on the results + * of the shaping operation on the buffer. If the verification + * fails, then either a buffer message is sent, if a message + * handler is installed on the buffer, or a message is written + * to standard error. In either case, the shaping result might + * be modified to show the failed output. Since: 3.4.0 + * @HB_BUFFER_FLAG_PRODUCE_UNSAFE_TO_CONCAT: + * flag indicating that the @HB_GLYPH_FLAG_UNSAFE_TO_CONCAT + * glyph-flag should be produced by the shaper. By default + * it will not be produced since it incurs a cost. Since: 4.0.0 + * @HB_BUFFER_FLAG_PRODUCE_SAFE_TO_INSERT_TATWEEL: + * flag indicating that the @HB_GLYPH_FLAG_SAFE_TO_INSERT_TATWEEL + * glyph-flag should be produced by the shaper. By default + * it will not be produced. Since: 5.1.0 + * @HB_BUFFER_FLAG_DEFINED: All currently defined flags: Since: 4.4.0 + * + * Flags for #hb_buffer_t. + * + * Since: 0.9.20 + */ +typedef enum { /*< flags >*/ + HB_BUFFER_FLAG_DEFAULT = 0x00000000u, + HB_BUFFER_FLAG_BOT = 0x00000001u, /* Beginning-of-text */ + HB_BUFFER_FLAG_EOT = 0x00000002u, /* End-of-text */ + HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES = 0x00000004u, + HB_BUFFER_FLAG_REMOVE_DEFAULT_IGNORABLES = 0x00000008u, + HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE = 0x00000010u, + HB_BUFFER_FLAG_VERIFY = 0x00000020u, + HB_BUFFER_FLAG_PRODUCE_UNSAFE_TO_CONCAT = 0x00000040u, + HB_BUFFER_FLAG_PRODUCE_SAFE_TO_INSERT_TATWEEL = 0x00000080u, + + HB_BUFFER_FLAG_DEFINED = 0x000000FFu +} hb_buffer_flags_t; + +HB_EXTERN void +hb_buffer_set_flags (hb_buffer_t *buffer, + hb_buffer_flags_t flags); + +HB_EXTERN hb_buffer_flags_t +hb_buffer_get_flags (const hb_buffer_t *buffer); + +/** + * hb_buffer_cluster_level_t: + * @HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES: Return cluster values grouped by graphemes into + * monotone order. + * @HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS: Return cluster values grouped into monotone order. + * @HB_BUFFER_CLUSTER_LEVEL_CHARACTERS: Don't group cluster values. + * @HB_BUFFER_CLUSTER_LEVEL_DEFAULT: Default cluster level, + * equal to @HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES. + * + * Data type for holding HarfBuzz's clustering behavior options. The cluster level + * dictates one aspect of how HarfBuzz will treat non-base characters + * during shaping. + * + * In @HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES, non-base + * characters are merged into the cluster of the base character that precedes them. + * + * In @HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS, non-base characters are initially + * assigned their own cluster values, which are not merged into preceding base + * clusters. This allows HarfBuzz to perform additional operations like reorder + * sequences of adjacent marks. + * + * @HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES is the default, because it maintains + * backward compatibility with older versions of HarfBuzz. New client programs that + * do not need to maintain such backward compatibility are recommended to use + * @HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS instead of the default. + * + * Since: 0.9.42 + */ +typedef enum { + HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES = 0, + HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS = 1, + HB_BUFFER_CLUSTER_LEVEL_CHARACTERS = 2, + HB_BUFFER_CLUSTER_LEVEL_DEFAULT = HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES +} hb_buffer_cluster_level_t; + +HB_EXTERN void +hb_buffer_set_cluster_level (hb_buffer_t *buffer, + hb_buffer_cluster_level_t cluster_level); + +HB_EXTERN hb_buffer_cluster_level_t +hb_buffer_get_cluster_level (const hb_buffer_t *buffer); + +/** + * HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT: + * + * The default code point for replacing invalid characters in a given encoding. + * Set to U+FFFD REPLACEMENT CHARACTER. + * + * Since: 0.9.31 + */ +#define HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT 0xFFFDu + +HB_EXTERN void +hb_buffer_set_replacement_codepoint (hb_buffer_t *buffer, + hb_codepoint_t replacement); + +HB_EXTERN hb_codepoint_t +hb_buffer_get_replacement_codepoint (const hb_buffer_t *buffer); + +HB_EXTERN void +hb_buffer_set_invisible_glyph (hb_buffer_t *buffer, + hb_codepoint_t invisible); + +HB_EXTERN hb_codepoint_t +hb_buffer_get_invisible_glyph (const hb_buffer_t *buffer); + +HB_EXTERN void +hb_buffer_set_not_found_glyph (hb_buffer_t *buffer, + hb_codepoint_t not_found); + +HB_EXTERN hb_codepoint_t +hb_buffer_get_not_found_glyph (const hb_buffer_t *buffer); + + +/* + * Content API. + */ + +HB_EXTERN void +hb_buffer_clear_contents (hb_buffer_t *buffer); + +HB_EXTERN hb_bool_t +hb_buffer_pre_allocate (hb_buffer_t *buffer, + unsigned int size); + + +HB_EXTERN hb_bool_t +hb_buffer_allocation_successful (hb_buffer_t *buffer); + +HB_EXTERN void +hb_buffer_reverse (hb_buffer_t *buffer); + +HB_EXTERN void +hb_buffer_reverse_range (hb_buffer_t *buffer, + unsigned int start, unsigned int end); + +HB_EXTERN void +hb_buffer_reverse_clusters (hb_buffer_t *buffer); + + +/* Filling the buffer in */ + +HB_EXTERN void +hb_buffer_add (hb_buffer_t *buffer, + hb_codepoint_t codepoint, + unsigned int cluster); + +HB_EXTERN void +hb_buffer_add_utf8 (hb_buffer_t *buffer, + const char *text, + int text_length, + unsigned int item_offset, + int item_length); + +HB_EXTERN void +hb_buffer_add_utf16 (hb_buffer_t *buffer, + const uint16_t *text, + int text_length, + unsigned int item_offset, + int item_length); + +HB_EXTERN void +hb_buffer_add_utf32 (hb_buffer_t *buffer, + const uint32_t *text, + int text_length, + unsigned int item_offset, + int item_length); + +HB_EXTERN void +hb_buffer_add_latin1 (hb_buffer_t *buffer, + const uint8_t *text, + int text_length, + unsigned int item_offset, + int item_length); + +HB_EXTERN void +hb_buffer_add_codepoints (hb_buffer_t *buffer, + const hb_codepoint_t *text, + int text_length, + unsigned int item_offset, + int item_length); + +HB_EXTERN void +hb_buffer_append (hb_buffer_t *buffer, + const hb_buffer_t *source, + unsigned int start, + unsigned int end); + +HB_EXTERN hb_bool_t +hb_buffer_set_length (hb_buffer_t *buffer, + unsigned int length); + +HB_EXTERN unsigned int +hb_buffer_get_length (const hb_buffer_t *buffer); + +/* Getting glyphs out of the buffer */ + +HB_EXTERN hb_glyph_info_t * +hb_buffer_get_glyph_infos (hb_buffer_t *buffer, + unsigned int *length); + +HB_EXTERN hb_glyph_position_t * +hb_buffer_get_glyph_positions (hb_buffer_t *buffer, + unsigned int *length); + +HB_EXTERN hb_bool_t +hb_buffer_has_positions (hb_buffer_t *buffer); + + +HB_EXTERN void +hb_buffer_normalize_glyphs (hb_buffer_t *buffer); + + +/* + * Serialize + */ + +/** + * hb_buffer_serialize_flags_t: + * @HB_BUFFER_SERIALIZE_FLAG_DEFAULT: serialize glyph names, clusters and positions. + * @HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS: do not serialize glyph cluster. + * @HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS: do not serialize glyph position information. + * @HB_BUFFER_SERIALIZE_FLAG_NO_GLYPH_NAMES: do no serialize glyph name. + * @HB_BUFFER_SERIALIZE_FLAG_GLYPH_EXTENTS: serialize glyph extents. + * @HB_BUFFER_SERIALIZE_FLAG_GLYPH_FLAGS: serialize glyph flags. Since: 1.5.0 + * @HB_BUFFER_SERIALIZE_FLAG_NO_ADVANCES: do not serialize glyph advances, + * glyph offsets will reflect absolute glyph positions. Since: 1.8.0 + * @HB_BUFFER_SERIALIZE_FLAG_DEFINED: All currently defined flags. Since: 4.4.0 + * + * Flags that control what glyph information are serialized in hb_buffer_serialize_glyphs(). + * + * Since: 0.9.20 + */ +typedef enum { /*< flags >*/ + HB_BUFFER_SERIALIZE_FLAG_DEFAULT = 0x00000000u, + HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS = 0x00000001u, + HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS = 0x00000002u, + HB_BUFFER_SERIALIZE_FLAG_NO_GLYPH_NAMES = 0x00000004u, + HB_BUFFER_SERIALIZE_FLAG_GLYPH_EXTENTS = 0x00000008u, + HB_BUFFER_SERIALIZE_FLAG_GLYPH_FLAGS = 0x00000010u, + HB_BUFFER_SERIALIZE_FLAG_NO_ADVANCES = 0x00000020u, + + HB_BUFFER_SERIALIZE_FLAG_DEFINED = 0x0000003Fu +} hb_buffer_serialize_flags_t; + +/** + * hb_buffer_serialize_format_t: + * @HB_BUFFER_SERIALIZE_FORMAT_TEXT: a human-readable, plain text format. + * @HB_BUFFER_SERIALIZE_FORMAT_JSON: a machine-readable JSON format. + * @HB_BUFFER_SERIALIZE_FORMAT_INVALID: invalid format. + * + * The buffer serialization and de-serialization format used in + * hb_buffer_serialize_glyphs() and hb_buffer_deserialize_glyphs(). + * + * Since: 0.9.2 + */ +typedef enum { + HB_BUFFER_SERIALIZE_FORMAT_TEXT = HB_TAG('T','E','X','T'), + HB_BUFFER_SERIALIZE_FORMAT_JSON = HB_TAG('J','S','O','N'), + HB_BUFFER_SERIALIZE_FORMAT_INVALID = HB_TAG_NONE +} hb_buffer_serialize_format_t; + +HB_EXTERN hb_buffer_serialize_format_t +hb_buffer_serialize_format_from_string (const char *str, int len); + +HB_EXTERN const char * +hb_buffer_serialize_format_to_string (hb_buffer_serialize_format_t format); + +HB_EXTERN const char ** +hb_buffer_serialize_list_formats (void); + +HB_EXTERN unsigned int +hb_buffer_serialize_glyphs (hb_buffer_t *buffer, + unsigned int start, + unsigned int end, + char *buf, + unsigned int buf_size, + unsigned int *buf_consumed, + hb_font_t *font, + hb_buffer_serialize_format_t format, + hb_buffer_serialize_flags_t flags); + +HB_EXTERN unsigned int +hb_buffer_serialize_unicode (hb_buffer_t *buffer, + unsigned int start, + unsigned int end, + char *buf, + unsigned int buf_size, + unsigned int *buf_consumed, + hb_buffer_serialize_format_t format, + hb_buffer_serialize_flags_t flags); + +HB_EXTERN unsigned int +hb_buffer_serialize (hb_buffer_t *buffer, + unsigned int start, + unsigned int end, + char *buf, + unsigned int buf_size, + unsigned int *buf_consumed, + hb_font_t *font, + hb_buffer_serialize_format_t format, + hb_buffer_serialize_flags_t flags); + +HB_EXTERN hb_bool_t +hb_buffer_deserialize_glyphs (hb_buffer_t *buffer, + const char *buf, + int buf_len, + const char **end_ptr, + hb_font_t *font, + hb_buffer_serialize_format_t format); + +HB_EXTERN hb_bool_t +hb_buffer_deserialize_unicode (hb_buffer_t *buffer, + const char *buf, + int buf_len, + const char **end_ptr, + hb_buffer_serialize_format_t format); + + + +/* + * Compare buffers + */ + +/** + * hb_buffer_diff_flags_t: + * @HB_BUFFER_DIFF_FLAG_EQUAL: equal buffers. + * @HB_BUFFER_DIFF_FLAG_CONTENT_TYPE_MISMATCH: buffers with different + * #hb_buffer_content_type_t. + * @HB_BUFFER_DIFF_FLAG_LENGTH_MISMATCH: buffers with differing length. + * @HB_BUFFER_DIFF_FLAG_NOTDEF_PRESENT: `.notdef` glyph is present in the + * reference buffer. + * @HB_BUFFER_DIFF_FLAG_DOTTED_CIRCLE_PRESENT: dotted circle glyph is present + * in the reference buffer. + * @HB_BUFFER_DIFF_FLAG_CODEPOINT_MISMATCH: difference in #hb_glyph_info_t.codepoint + * @HB_BUFFER_DIFF_FLAG_CLUSTER_MISMATCH: difference in #hb_glyph_info_t.cluster + * @HB_BUFFER_DIFF_FLAG_GLYPH_FLAGS_MISMATCH: difference in #hb_glyph_flags_t. + * @HB_BUFFER_DIFF_FLAG_POSITION_MISMATCH: difference in #hb_glyph_position_t. + * + * Flags from comparing two #hb_buffer_t's. + * + * Buffer with different #hb_buffer_content_type_t cannot be meaningfully + * compared in any further detail. + * + * For buffers with differing length, the per-glyph comparison is not + * attempted, though we do still scan reference buffer for dotted circle and + * `.notdef` glyphs. + * + * If the buffers have the same length, we compare them glyph-by-glyph and + * report which aspect(s) of the glyph info/position are different. + * + * Since: 1.5.0 + */ +typedef enum { /*< flags >*/ + HB_BUFFER_DIFF_FLAG_EQUAL = 0x0000, + + /* Buffers with different content_type cannot be meaningfully compared + * in any further detail. */ + HB_BUFFER_DIFF_FLAG_CONTENT_TYPE_MISMATCH = 0x0001, + + /* For buffers with differing length, the per-glyph comparison is not + * attempted, though we do still scan reference for dottedcircle / .notdef + * glyphs. */ + HB_BUFFER_DIFF_FLAG_LENGTH_MISMATCH = 0x0002, + + /* We want to know if dottedcircle / .notdef glyphs are present in the + * reference, as we may not care so much about other differences in this + * case. */ + HB_BUFFER_DIFF_FLAG_NOTDEF_PRESENT = 0x0004, + HB_BUFFER_DIFF_FLAG_DOTTED_CIRCLE_PRESENT = 0x0008, + + /* If the buffers have the same length, we compare them glyph-by-glyph + * and report which aspect(s) of the glyph info/position are different. */ + HB_BUFFER_DIFF_FLAG_CODEPOINT_MISMATCH = 0x0010, + HB_BUFFER_DIFF_FLAG_CLUSTER_MISMATCH = 0x0020, + HB_BUFFER_DIFF_FLAG_GLYPH_FLAGS_MISMATCH = 0x0040, + HB_BUFFER_DIFF_FLAG_POSITION_MISMATCH = 0x0080 + +} hb_buffer_diff_flags_t; + +/* Compare the contents of two buffers, report types of differences. */ +HB_EXTERN hb_buffer_diff_flags_t +hb_buffer_diff (hb_buffer_t *buffer, + hb_buffer_t *reference, + hb_codepoint_t dottedcircle_glyph, + unsigned int position_fuzz); + + +/* + * Debugging. + */ + +/** + * hb_buffer_message_func_t: + * @buffer: An #hb_buffer_t to work upon + * @font: The #hb_font_t the @buffer is shaped with + * @message: `NULL`-terminated message passed to the function + * @user_data: User data pointer passed by the caller + * + * A callback method for #hb_buffer_t. The method gets called with the + * #hb_buffer_t it was set on, the #hb_font_t the buffer is shaped with and a + * message describing what step of the shaping process will be performed. + * Returning `false` from this method will skip this shaping step and move to + * the next one. + * + * Return value: `true` to perform the shaping step, `false` to skip it. + * + * Since: 1.1.3 + */ +typedef hb_bool_t (*hb_buffer_message_func_t) (hb_buffer_t *buffer, + hb_font_t *font, + const char *message, + void *user_data); + +HB_EXTERN void +hb_buffer_set_message_func (hb_buffer_t *buffer, + hb_buffer_message_func_t func, + void *user_data, hb_destroy_func_t destroy); + + +HB_END_DECLS + +#endif /* HB_BUFFER_H */ diff --git a/libs/harfbuzz/src/hb-buffer.hh b/libs/harfbuzz/src/hb-buffer.hh new file mode 100644 index 00000000..26c3f0fa --- /dev/null +++ b/libs/harfbuzz/src/hb-buffer.hh @@ -0,0 +1,639 @@ +/* + * Copyright © 1998-2004 David Turner and Werner Lemberg + * Copyright © 2004,2007,2009,2010 Red Hat, Inc. + * Copyright © 2011,2012 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. + * + * Red Hat Author(s): Owen Taylor, Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_BUFFER_HH +#define HB_BUFFER_HH + +#include "hb.hh" +#include "hb-unicode.hh" + + +#ifndef HB_BUFFER_MAX_LEN_FACTOR +#define HB_BUFFER_MAX_LEN_FACTOR 64 +#endif +#ifndef HB_BUFFER_MAX_LEN_MIN +#define HB_BUFFER_MAX_LEN_MIN 16384 +#endif +#ifndef HB_BUFFER_MAX_LEN_DEFAULT +#define HB_BUFFER_MAX_LEN_DEFAULT 0x3FFFFFFF /* Shaping more than a billion chars? Let us know! */ +#endif + +#ifndef HB_BUFFER_MAX_OPS_FACTOR +#define HB_BUFFER_MAX_OPS_FACTOR 1024 +#endif +#ifndef HB_BUFFER_MAX_OPS_MIN +#define HB_BUFFER_MAX_OPS_MIN 16384 +#endif +#ifndef HB_BUFFER_MAX_OPS_DEFAULT +#define HB_BUFFER_MAX_OPS_DEFAULT 0x1FFFFFFF /* Shaping more than a billion operations? Let us know! */ +#endif + +static_assert ((sizeof (hb_glyph_info_t) == 20), ""); +static_assert ((sizeof (hb_glyph_info_t) == sizeof (hb_glyph_position_t)), ""); + +HB_MARK_AS_FLAG_T (hb_glyph_flags_t); +HB_MARK_AS_FLAG_T (hb_buffer_flags_t); +HB_MARK_AS_FLAG_T (hb_buffer_serialize_flags_t); +HB_MARK_AS_FLAG_T (hb_buffer_diff_flags_t); + +enum hb_buffer_scratch_flags_t { + HB_BUFFER_SCRATCH_FLAG_DEFAULT = 0x00000000u, + HB_BUFFER_SCRATCH_FLAG_HAS_NON_ASCII = 0x00000001u, + HB_BUFFER_SCRATCH_FLAG_HAS_DEFAULT_IGNORABLES = 0x00000002u, + HB_BUFFER_SCRATCH_FLAG_HAS_SPACE_FALLBACK = 0x00000004u, + HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT = 0x00000008u, + HB_BUFFER_SCRATCH_FLAG_HAS_CGJ = 0x00000010u, + HB_BUFFER_SCRATCH_FLAG_HAS_GLYPH_FLAGS = 0x00000020u, + HB_BUFFER_SCRATCH_FLAG_HAS_BROKEN_SYLLABLE = 0x00000040u, + + /* Reserved for shapers' internal use. */ + HB_BUFFER_SCRATCH_FLAG_SHAPER0 = 0x01000000u, + HB_BUFFER_SCRATCH_FLAG_SHAPER1 = 0x02000000u, + HB_BUFFER_SCRATCH_FLAG_SHAPER2 = 0x04000000u, + HB_BUFFER_SCRATCH_FLAG_SHAPER3 = 0x08000000u, +}; +HB_MARK_AS_FLAG_T (hb_buffer_scratch_flags_t); + + +/* + * hb_buffer_t + */ + +struct hb_buffer_t +{ + hb_object_header_t header; + + /* + * Information about how the text in the buffer should be treated. + */ + + hb_unicode_funcs_t *unicode; /* Unicode functions */ + hb_buffer_flags_t flags; /* BOT / EOT / etc. */ + hb_buffer_cluster_level_t cluster_level; + hb_codepoint_t replacement; /* U+FFFD or something else. */ + hb_codepoint_t invisible; /* 0 or something else. */ + hb_codepoint_t not_found; /* 0 or something else. */ + + /* + * Buffer contents + */ + + hb_buffer_content_type_t content_type; + hb_segment_properties_t props; /* Script, language, direction */ + + bool successful; /* Allocations successful */ + bool shaping_failed; /* Shaping failure */ + bool have_output; /* Whether we have an output buffer going on */ + bool have_positions; /* Whether we have positions */ + + unsigned int idx; /* Cursor into ->info and ->pos arrays */ + unsigned int len; /* Length of ->info and ->pos arrays */ + unsigned int out_len; /* Length of ->out_info array if have_output */ + + unsigned int allocated; /* Length of allocated arrays */ + hb_glyph_info_t *info; + hb_glyph_info_t *out_info; + hb_glyph_position_t *pos; + + /* Text before / after the main buffer contents. + * Always in Unicode, and ordered outward. + * Index 0 is for "pre-context", 1 for "post-context". */ + static constexpr unsigned CONTEXT_LENGTH = 5u; + hb_codepoint_t context[2][CONTEXT_LENGTH]; + unsigned int context_len[2]; + + + /* + * Managed by enter / leave + */ + + uint8_t allocated_var_bits; + uint8_t serial; + hb_buffer_scratch_flags_t scratch_flags; /* Have space-fallback, etc. */ + unsigned int max_len; /* Maximum allowed len. */ + int max_ops; /* Maximum allowed operations. */ + /* The bits here reflect current allocations of the bytes in glyph_info_t's var1 and var2. */ + + + /* + * Messaging callback + */ + +#ifndef HB_NO_BUFFER_MESSAGE + hb_buffer_message_func_t message_func; + void *message_data; + hb_destroy_func_t message_destroy; + unsigned message_depth; /* How deeply are we inside a message callback? */ +#else + static constexpr unsigned message_depth = 0u; +#endif + + + + /* Methods */ + + HB_NODISCARD bool in_error () const { return !successful; } + + void allocate_var (unsigned int start, unsigned int count) + { + unsigned int end = start + count; + assert (end <= 8); + unsigned int bits = (1u< (info, len).reverse (start, end); + if (have_positions) + hb_array_t (pos, len).reverse (start, end); + } + void reverse () { reverse_range (0, len); } + + template + void reverse_groups (const FuncType& group, + bool merge_clusters = false) + { + if (unlikely (!len)) + return; + + unsigned start = 0; + unsigned i; + for (i = 1; i < len; i++) + { + if (!group (info[i - 1], info[i])) + { + if (merge_clusters) + this->merge_clusters (start, i); + reverse_range (start, i); + start = i; + } + } + if (merge_clusters) + this->merge_clusters (start, i); + reverse_range (start, i); + + reverse (); + } + + template + unsigned group_end (unsigned start, const FuncType& group) const + { + while (++start < len && group (info[start - 1], info[start])) + ; + + return start; + } + + static bool _cluster_group_func (const hb_glyph_info_t& a, + const hb_glyph_info_t& b) + { return a.cluster == b.cluster; } + + void reverse_clusters () { reverse_groups (_cluster_group_func); } + + HB_INTERNAL void guess_segment_properties (); + + HB_INTERNAL bool sync (); + HB_INTERNAL int sync_so_far (); + HB_INTERNAL void clear_output (); + HB_INTERNAL void clear_positions (); + + template + HB_NODISCARD bool replace_glyphs (unsigned int num_in, + unsigned int num_out, + const T *glyph_data) + { + if (unlikely (!make_room_for (num_in, num_out))) return false; + + assert (idx + num_in <= len); + + merge_clusters (idx, idx + num_in); + + hb_glyph_info_t &orig_info = idx < len ? cur() : prev(); + + hb_glyph_info_t *pinfo = &out_info[out_len]; + for (unsigned int i = 0; i < num_out; i++) + { + *pinfo = orig_info; + pinfo->codepoint = glyph_data[i]; + pinfo++; + } + + idx += num_in; + out_len += num_out; + return true; + } + + HB_NODISCARD bool replace_glyph (hb_codepoint_t glyph_index) + { return replace_glyphs (1, 1, &glyph_index); } + + /* Makes a copy of the glyph at idx to output and replace glyph_index */ + HB_NODISCARD bool output_glyph (hb_codepoint_t glyph_index) + { return replace_glyphs (0, 1, &glyph_index); } + + HB_NODISCARD bool output_info (const hb_glyph_info_t &glyph_info) + { + if (unlikely (!make_room_for (0, 1))) return false; + + out_info[out_len] = glyph_info; + + out_len++; + return true; + } + /* Copies glyph at idx to output but doesn't advance idx */ + HB_NODISCARD bool copy_glyph () + { + /* Extra copy because cur()'s return can be freed within + * output_info() call if buffer reallocates. */ + return output_info (hb_glyph_info_t (cur())); + } + + /* Copies glyph at idx to output and advance idx. + * If there's no output, just advance idx. */ + HB_NODISCARD bool next_glyph () + { + if (have_output) + { + if (out_info != info || out_len != idx) + { + if (unlikely (!make_room_for (1, 1))) return false; + out_info[out_len] = info[idx]; + } + out_len++; + } + + idx++; + return true; + } + /* Copies n glyphs at idx to output and advance idx. + * If there's no output, just advance idx. */ + HB_NODISCARD bool next_glyphs (unsigned int n) + { + if (have_output) + { + if (out_info != info || out_len != idx) + { + if (unlikely (!make_room_for (n, n))) return false; + memmove (out_info + out_len, info + idx, n * sizeof (out_info[0])); + } + out_len += n; + } + + idx += n; + return true; + } + /* Advance idx without copying to output. */ + void skip_glyph () { idx++; } + void reset_masks (hb_mask_t mask) + { + for (unsigned int j = 0; j < len; j++) + info[j].mask = mask; + } + void add_masks (hb_mask_t mask) + { + for (unsigned int j = 0; j < len; j++) + info[j].mask |= mask; + } + HB_INTERNAL void set_masks (hb_mask_t value, hb_mask_t mask, + unsigned int cluster_start, unsigned int cluster_end); + + void merge_clusters (unsigned int start, unsigned int end) + { + if (end - start < 2) + return; + merge_clusters_impl (start, end); + } + HB_INTERNAL void merge_clusters_impl (unsigned int start, unsigned int end); + HB_INTERNAL void merge_out_clusters (unsigned int start, unsigned int end); + /* Merge clusters for deleting current glyph, and skip it. */ + HB_INTERNAL void delete_glyph (); + + + /* Adds glyph flags in mask to infos with clusters between start and end. + * The start index will be from out-buffer if from_out_buffer is true. + * If interior is true, then the cluster having the minimum value is skipped. */ + void _set_glyph_flags (hb_mask_t mask, + unsigned start = 0, + unsigned end = (unsigned) -1, + bool interior = false, + bool from_out_buffer = false) + { + end = hb_min (end, len); + + if (interior && !from_out_buffer && end - start < 2) + return; + + scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GLYPH_FLAGS; + + if (!from_out_buffer || !have_output) + { + if (!interior) + { + for (unsigned i = start; i < end; i++) + info[i].mask |= mask; + } + else + { + unsigned cluster = _infos_find_min_cluster (info, start, end); + _infos_set_glyph_flags (info, start, end, cluster, mask); + } + } + else + { + assert (start <= out_len); + assert (idx <= end); + + if (!interior) + { + for (unsigned i = start; i < out_len; i++) + out_info[i].mask |= mask; + for (unsigned i = idx; i < end; i++) + info[i].mask |= mask; + } + else + { + unsigned cluster = _infos_find_min_cluster (info, idx, end); + cluster = _infos_find_min_cluster (out_info, start, out_len, cluster); + + _infos_set_glyph_flags (out_info, start, out_len, cluster, mask); + _infos_set_glyph_flags (info, idx, end, cluster, mask); + } + } + } + + void unsafe_to_break (unsigned int start = 0, unsigned int end = -1) + { + _set_glyph_flags (HB_GLYPH_FLAG_UNSAFE_TO_BREAK | HB_GLYPH_FLAG_UNSAFE_TO_CONCAT, + start, end, + true); + } + void safe_to_insert_tatweel (unsigned int start = 0, unsigned int end = -1) + { + if ((flags & HB_BUFFER_FLAG_PRODUCE_SAFE_TO_INSERT_TATWEEL) == 0) + { + unsafe_to_break (start, end); + return; + } + _set_glyph_flags (HB_GLYPH_FLAG_SAFE_TO_INSERT_TATWEEL, + start, end, + true); + } + void unsafe_to_concat (unsigned int start = 0, unsigned int end = -1) + { + if (likely ((flags & HB_BUFFER_FLAG_PRODUCE_UNSAFE_TO_CONCAT) == 0)) + return; + _set_glyph_flags (HB_GLYPH_FLAG_UNSAFE_TO_CONCAT, + start, end, + true); + } + void unsafe_to_break_from_outbuffer (unsigned int start = 0, unsigned int end = -1) + { + _set_glyph_flags (HB_GLYPH_FLAG_UNSAFE_TO_BREAK | HB_GLYPH_FLAG_UNSAFE_TO_CONCAT, + start, end, + true, true); + } + void unsafe_to_concat_from_outbuffer (unsigned int start = 0, unsigned int end = -1) + { + if (likely ((flags & HB_BUFFER_FLAG_PRODUCE_UNSAFE_TO_CONCAT) == 0)) + return; + _set_glyph_flags (HB_GLYPH_FLAG_UNSAFE_TO_CONCAT, + start, end, + false, true); + } + + + /* Internal methods */ + HB_NODISCARD HB_INTERNAL bool move_to (unsigned int i); /* i is output-buffer index. */ + + HB_NODISCARD HB_INTERNAL bool enlarge (unsigned int size); + + HB_NODISCARD bool ensure (unsigned int size) + { return likely (!size || size < allocated) ? true : enlarge (size); } + + HB_NODISCARD bool ensure_inplace (unsigned int size) + { return likely (!size || size < allocated); } + + void assert_glyphs () + { + assert ((content_type == HB_BUFFER_CONTENT_TYPE_GLYPHS) || + (!len && (content_type == HB_BUFFER_CONTENT_TYPE_INVALID))); + } + void assert_unicode () + { + assert ((content_type == HB_BUFFER_CONTENT_TYPE_UNICODE) || + (!len && (content_type == HB_BUFFER_CONTENT_TYPE_INVALID))); + } + HB_NODISCARD bool ensure_glyphs () + { + if (unlikely (content_type != HB_BUFFER_CONTENT_TYPE_GLYPHS)) + { + if (content_type != HB_BUFFER_CONTENT_TYPE_INVALID) + return false; + assert (len == 0); + content_type = HB_BUFFER_CONTENT_TYPE_GLYPHS; + } + return true; + } + HB_NODISCARD bool ensure_unicode () + { + if (unlikely (content_type != HB_BUFFER_CONTENT_TYPE_UNICODE)) + { + if (content_type != HB_BUFFER_CONTENT_TYPE_INVALID) + return false; + assert (len == 0); + content_type = HB_BUFFER_CONTENT_TYPE_UNICODE; + } + return true; + } + + HB_NODISCARD HB_INTERNAL bool make_room_for (unsigned int num_in, unsigned int num_out); + HB_NODISCARD HB_INTERNAL bool shift_forward (unsigned int count); + + typedef long scratch_buffer_t; + HB_INTERNAL scratch_buffer_t *get_scratch_buffer (unsigned int *size); + + void clear_context (unsigned int side) { context_len[side] = 0; } + + HB_INTERNAL void sort (unsigned int start, unsigned int end, int(*compar)(const hb_glyph_info_t *, const hb_glyph_info_t *)); + + bool messaging () + { +#ifdef HB_NO_BUFFER_MESSAGE + return false; +#else + return unlikely (message_func); +#endif + } + bool message (hb_font_t *font, const char *fmt, ...) HB_PRINTF_FUNC(3, 4) + { +#ifdef HB_NO_BUFFER_MESSAGE + return true; +#else + if (likely (!messaging ())) + return true; + + va_list ap; + va_start (ap, fmt); + bool ret = message_impl (font, fmt, ap); + va_end (ap); + + return ret; +#endif + } + HB_INTERNAL bool message_impl (hb_font_t *font, const char *fmt, va_list ap) HB_PRINTF_FUNC(3, 0); + + static void + set_cluster (hb_glyph_info_t &inf, unsigned int cluster, unsigned int mask = 0) + { + if (inf.cluster != cluster) + inf.mask = (inf.mask & ~HB_GLYPH_FLAG_DEFINED) | (mask & HB_GLYPH_FLAG_DEFINED); + inf.cluster = cluster; + } + void + _infos_set_glyph_flags (hb_glyph_info_t *infos, + unsigned int start, unsigned int end, + unsigned int cluster, + hb_mask_t mask) + { + for (unsigned int i = start; i < end; i++) + if (cluster != infos[i].cluster) + { + scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GLYPH_FLAGS; + infos[i].mask |= mask; + } + } + static unsigned + _infos_find_min_cluster (const hb_glyph_info_t *infos, + unsigned start, unsigned end, + unsigned cluster = UINT_MAX) + { + for (unsigned int i = start; i < end; i++) + cluster = hb_min (cluster, infos[i].cluster); + return cluster; + } + + void clear_glyph_flags (hb_mask_t mask = 0) + { + for (unsigned int i = 0; i < len; i++) + info[i].mask = (info[i].mask & ~HB_GLYPH_FLAG_DEFINED) | (mask & HB_GLYPH_FLAG_DEFINED); + } +}; +DECLARE_NULL_INSTANCE (hb_buffer_t); + + +#define foreach_group(buffer, start, end, group_func) \ + for (unsigned int \ + _count = buffer->len, \ + start = 0, end = _count ? buffer->group_end (0, group_func) : 0; \ + start < _count; \ + start = end, end = buffer->group_end (start, group_func)) + +#define foreach_cluster(buffer, start, end) \ + foreach_group (buffer, start, end, hb_buffer_t::_cluster_group_func) + + +#define HB_BUFFER_XALLOCATE_VAR(b, func, var) \ + b->func (offsetof (hb_glyph_info_t, var) - offsetof(hb_glyph_info_t, var1), \ + sizeof (b->info[0].var)) +#define HB_BUFFER_ALLOCATE_VAR(b, var) HB_BUFFER_XALLOCATE_VAR (b, allocate_var, var ()) +#define HB_BUFFER_TRY_ALLOCATE_VAR(b, var) HB_BUFFER_XALLOCATE_VAR (b, try_allocate_var, var ()) +#define HB_BUFFER_DEALLOCATE_VAR(b, var) HB_BUFFER_XALLOCATE_VAR (b, deallocate_var, var ()) +#define HB_BUFFER_ASSERT_VAR(b, var) HB_BUFFER_XALLOCATE_VAR (b, assert_var, var ()) + + +#endif /* HB_BUFFER_HH */ diff --git a/libs/harfbuzz/src/hb-cache.hh b/libs/harfbuzz/src/hb-cache.hh new file mode 100644 index 00000000..897f313f --- /dev/null +++ b/libs/harfbuzz/src/hb-cache.hh @@ -0,0 +1,86 @@ +/* + * Copyright © 2012 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 HB_CACHE_HH +#define HB_CACHE_HH + +#include "hb.hh" + + +/* Implements a lockfree cache for int->int functions. */ + +template +struct hb_cache_t +{ + using item_t = typename std::conditional::type + >::type; + + static_assert ((key_bits >= cache_bits), ""); + static_assert ((key_bits + value_bits - cache_bits <= 8 * sizeof (item_t)), ""); + + void init () { clear (); } + void fini () {} + + void clear () + { + for (unsigned i = 0; i < ARRAY_LENGTH (values); i++) + values[i] = -1; + } + + bool get (unsigned int key, unsigned int *value) const + { + unsigned int k = key & ((1u<> value_bits) != (key >> cache_bits)) + return false; + *value = v & ((1u<> key_bits) || (value >> value_bits))) + return false; /* Overflows */ + unsigned int k = key & ((1u<>cache_bits)<= OpCode_ESC_Base; } +inline unsigned int OpCode_Size (op_code_t op) { return Is_OpCode_ESC (op) ? 2: 1; } + +#define OpCode_Copyright Make_OpCode_ESC(0) /* CFF Top */ +#define OpCode_isFixedPitch Make_OpCode_ESC(1) /* CFF Top (false) */ +#define OpCode_ItalicAngle Make_OpCode_ESC(2) /* CFF Top (0) */ +#define OpCode_UnderlinePosition Make_OpCode_ESC(3) /* CFF Top (-100) */ +#define OpCode_UnderlineThickness Make_OpCode_ESC(4) /* CFF Top (50) */ +#define OpCode_PaintType Make_OpCode_ESC(5) /* CFF Top (0) */ +#define OpCode_CharstringType Make_OpCode_ESC(6) /* CFF Top (2) */ +#define OpCode_FontMatrix Make_OpCode_ESC(7) /* CFF Top, CFF2 Top (.001 0 0 .001 0 0)*/ +#define OpCode_StrokeWidth Make_OpCode_ESC(8) /* CFF Top (0) */ +#define OpCode_BlueScale Make_OpCode_ESC(9) /* CFF Private, CFF2 Private (0.039625) */ +#define OpCode_BlueShift Make_OpCode_ESC(10) /* CFF Private, CFF2 Private (7) */ +#define OpCode_BlueFuzz Make_OpCode_ESC(11) /* CFF Private, CFF2 Private (1) */ +#define OpCode_StemSnapH Make_OpCode_ESC(12) /* CFF Private, CFF2 Private */ +#define OpCode_StemSnapV Make_OpCode_ESC(13) /* CFF Private, CFF2 Private */ +#define OpCode_ForceBold Make_OpCode_ESC(14) /* CFF Private (false) */ +#define OpCode_reservedESC15 Make_OpCode_ESC(15) +#define OpCode_reservedESC16 Make_OpCode_ESC(16) +#define OpCode_LanguageGroup Make_OpCode_ESC(17) /* CFF Private, CFF2 Private (0) */ +#define OpCode_ExpansionFactor Make_OpCode_ESC(18) /* CFF Private, CFF2 Private (0.06) */ +#define OpCode_initialRandomSeed Make_OpCode_ESC(19) /* CFF Private (0) */ +#define OpCode_SyntheticBase Make_OpCode_ESC(20) /* CFF Top */ +#define OpCode_PostScript Make_OpCode_ESC(21) /* CFF Top */ +#define OpCode_BaseFontName Make_OpCode_ESC(22) /* CFF Top */ +#define OpCode_BaseFontBlend Make_OpCode_ESC(23) /* CFF Top */ +#define OpCode_reservedESC24 Make_OpCode_ESC(24) +#define OpCode_reservedESC25 Make_OpCode_ESC(25) +#define OpCode_reservedESC26 Make_OpCode_ESC(26) +#define OpCode_reservedESC27 Make_OpCode_ESC(27) +#define OpCode_reservedESC28 Make_OpCode_ESC(28) +#define OpCode_reservedESC29 Make_OpCode_ESC(29) +#define OpCode_ROS Make_OpCode_ESC(30) /* CFF Top_CID */ +#define OpCode_CIDFontVersion Make_OpCode_ESC(31) /* CFF Top_CID (0) */ +#define OpCode_CIDFontRevision Make_OpCode_ESC(32) /* CFF Top_CID (0) */ +#define OpCode_CIDFontType Make_OpCode_ESC(33) /* CFF Top_CID (0) */ +#define OpCode_CIDCount Make_OpCode_ESC(34) /* CFF Top_CID (8720) */ +#define OpCode_UIDBase Make_OpCode_ESC(35) /* CFF Top_CID */ +#define OpCode_FDArray Make_OpCode_ESC(36) /* CFF Top_CID, CFF2 Top */ +#define OpCode_FDSelect Make_OpCode_ESC(37) /* CFF Top_CID, CFF2 Top */ +#define OpCode_FontName Make_OpCode_ESC(38) /* CFF Top_CID */ + + +/* === CharString operators === */ + +#define OpCode_hstem 1 /* CFF, CFF2 */ +#define OpCode_Reserved2 2 +#define OpCode_vstem 3 /* CFF, CFF2 */ +#define OpCode_vmoveto 4 /* CFF, CFF2 */ +#define OpCode_rlineto 5 /* CFF, CFF2 */ +#define OpCode_hlineto 6 /* CFF, CFF2 */ +#define OpCode_vlineto 7 /* CFF, CFF2 */ +#define OpCode_rrcurveto 8 /* CFF, CFF2 */ +#define OpCode_Reserved9 9 +#define OpCode_callsubr 10 /* CFF, CFF2 */ +#define OpCode_return 11 /* CFF */ +//#define OpCode_escape 12 /* CFF, CFF2 */ +#define OpCode_Reserved13 13 +#define OpCode_endchar 14 /* CFF */ +#define OpCode_vsindexcs 15 /* CFF2 */ +#define OpCode_blendcs 16 /* CFF2 */ +#define OpCode_Reserved17 17 +#define OpCode_hstemhm 18 /* CFF, CFF2 */ +#define OpCode_hintmask 19 /* CFF, CFF2 */ +#define OpCode_cntrmask 20 /* CFF, CFF2 */ +#define OpCode_rmoveto 21 /* CFF, CFF2 */ +#define OpCode_hmoveto 22 /* CFF, CFF2 */ +#define OpCode_vstemhm 23 /* CFF, CFF2 */ +#define OpCode_rcurveline 24 /* CFF, CFF2 */ +#define OpCode_rlinecurve 25 /* CFF, CFF2 */ +#define OpCode_vvcurveto 26 /* CFF, CFF2 */ +#define OpCode_hhcurveto 27 /* CFF, CFF2 */ +//#define OpCode_shortint 28 /* CFF, CFF2 */ +#define OpCode_callgsubr 29 /* CFF, CFF2 */ +#define OpCode_vhcurveto 30 /* CFF, CFF2 */ +#define OpCode_hvcurveto 31 /* CFF, CFF2 */ + +#define OpCode_fixedcs 255 /* 32-bit fixed */ + +/* Two byte escape operators 12, (0-41) */ +#define OpCode_dotsection Make_OpCode_ESC(0) /* CFF (obsoleted) */ +#define OpCode_ReservedESC1 Make_OpCode_ESC(1) +#define OpCode_ReservedESC2 Make_OpCode_ESC(2) +#define OpCode_and Make_OpCode_ESC(3) /* CFF */ +#define OpCode_or Make_OpCode_ESC(4) /* CFF */ +#define OpCode_not Make_OpCode_ESC(5) /* CFF */ +#define OpCode_ReservedESC6 Make_OpCode_ESC(6) +#define OpCode_ReservedESC7 Make_OpCode_ESC(7) +#define OpCode_ReservedESC8 Make_OpCode_ESC(8) +#define OpCode_abs Make_OpCode_ESC(9) /* CFF */ +#define OpCode_add Make_OpCode_ESC(10) /* CFF */ +#define OpCode_sub Make_OpCode_ESC(11) /* CFF */ +#define OpCode_div Make_OpCode_ESC(12) /* CFF */ +#define OpCode_ReservedESC13 Make_OpCode_ESC(13) +#define OpCode_neg Make_OpCode_ESC(14) /* CFF */ +#define OpCode_eq Make_OpCode_ESC(15) /* CFF */ +#define OpCode_ReservedESC16 Make_OpCode_ESC(16) +#define OpCode_ReservedESC17 Make_OpCode_ESC(17) +#define OpCode_drop Make_OpCode_ESC(18) /* CFF */ +#define OpCode_ReservedESC19 Make_OpCode_ESC(19) +#define OpCode_put Make_OpCode_ESC(20) /* CFF */ +#define OpCode_get Make_OpCode_ESC(21) /* CFF */ +#define OpCode_ifelse Make_OpCode_ESC(22) /* CFF */ +#define OpCode_random Make_OpCode_ESC(23) /* CFF */ +#define OpCode_mul Make_OpCode_ESC(24) /* CFF */ +//#define OpCode_reservedESC25 Make_OpCode_ESC(25) +#define OpCode_sqrt Make_OpCode_ESC(26) /* CFF */ +#define OpCode_dup Make_OpCode_ESC(27) /* CFF */ +#define OpCode_exch Make_OpCode_ESC(28) /* CFF */ +#define OpCode_index Make_OpCode_ESC(29) /* CFF */ +#define OpCode_roll Make_OpCode_ESC(30) /* CFF */ +#define OpCode_reservedESC31 Make_OpCode_ESC(31) +#define OpCode_reservedESC32 Make_OpCode_ESC(32) +#define OpCode_reservedESC33 Make_OpCode_ESC(33) +#define OpCode_hflex Make_OpCode_ESC(34) /* CFF, CFF2 */ +#define OpCode_flex Make_OpCode_ESC(35) /* CFF, CFF2 */ +#define OpCode_hflex1 Make_OpCode_ESC(36) /* CFF, CFF2 */ +#define OpCode_flex1 Make_OpCode_ESC(37) /* CFF, CFF2 */ + + +#define OpCode_Invalid 0xFFFFu + + +struct number_t +{ + void set_int (int v) { value = v; } + int to_int () const { return value; } + + void set_fixed (int32_t v) { value = v / 65536.0; } + int32_t to_fixed () const { return value * 65536.0; } + + void set_real (double v) { value = v; } + double to_real () const { return value; } + + bool in_int_range () const + { return ((double) (int16_t) to_int () == value); } + + bool operator > (const number_t &n) const { return value > n.to_real (); } + bool operator < (const number_t &n) const { return n > *this; } + bool operator >= (const number_t &n) const { return !(*this < n); } + bool operator <= (const number_t &n) const { return !(*this > n); } + + const number_t &operator += (const number_t &n) + { + set_real (to_real () + n.to_real ()); + + return *this; + } + + protected: + double value = 0.; +}; + +/* byte string */ +struct UnsizedByteStr : UnsizedArrayOf +{ + hb_ubytes_t as_ubytes (unsigned l) const + { return hb_ubytes_t ((const unsigned char *) this, l); } + + // encode 2-byte int (Dict/CharString) or 4-byte int (Dict) + template + static bool serialize_int (hb_serialize_context_t *c, op_code_t intOp, V value) + { + TRACE_SERIALIZE (this); + + HBUINT8 *p = c->allocate_size (1); + if (unlikely (!p)) return_trace (false); + *p = intOp; + + T *ip = c->allocate_size (T::static_size); + if (unlikely (!ip)) return_trace (false); + return_trace (c->check_assign (*ip, value, HB_SERIALIZE_ERROR_INT_OVERFLOW)); + } + + template + static bool serialize_int4 (hb_serialize_context_t *c, V value) + { return serialize_int (c, OpCode_longintdict, value); } + + template + static bool serialize_int2 (hb_serialize_context_t *c, V value) + { return serialize_int (c, OpCode_shortint, value); } + + /* Defining null_size allows a Null object may be created. Should be safe because: + * A descendent struct Dict uses a Null pointer to indicate a missing table, + * checked before access. + */ + DEFINE_SIZE_MIN(0); +}; + +/* A byte string associated with the current offset and an error condition */ +struct byte_str_ref_t +{ + byte_str_ref_t () { init (); } + + void init () + { + str = hb_ubytes_t (); + offset = 0; + error = false; + } + + void fini () {} + + byte_str_ref_t (const hb_ubytes_t &str_, unsigned int offset_ = 0) + : str (str_), offset (offset_), error (false) {} + + void reset (const hb_ubytes_t &str_, unsigned int offset_ = 0) + { + str = str_; + offset = offset_; + error = false; + } + + const unsigned char& operator [] (int i) { + if (unlikely ((unsigned int) (offset + i) >= str.length)) + { + set_error (); + return Null (unsigned char); + } + return str[offset + i]; + } + + /* Conversion to hb_ubytes_t */ + operator hb_ubytes_t () const { return str.sub_array (offset, str.length - offset); } + + hb_ubytes_t sub_array (unsigned int offset_, unsigned int len_) const + { return str.sub_array (offset_, len_); } + + bool avail (unsigned int count=1) const + { return (!in_error () && offset + count <= str.length); } + void inc (unsigned int count=1) + { + if (likely (!in_error () && (offset <= str.length) && (offset + count <= str.length))) + { + offset += count; + } + else + { + offset = str.length; + set_error (); + } + } + + void set_error () { error = true; } + bool in_error () const { return error; } + + hb_ubytes_t str; + unsigned int offset; /* beginning of the sub-string within str */ + + protected: + bool error; +}; + +using byte_str_array_t = hb_vector_t; + +/* stack */ +template +struct cff_stack_t +{ + ELEM& operator [] (unsigned int i) + { + if (unlikely (i >= count)) + { + set_error (); + return Crap (ELEM); + } + return elements[i]; + } + + void push (const ELEM &v) + { + if (likely (count < LIMIT)) + elements[count++] = v; + else + set_error (); + } + ELEM &push () + { + if (likely (count < LIMIT)) + return elements[count++]; + else + { + set_error (); + return Crap (ELEM); + } + } + + ELEM& pop () + { + if (likely (count > 0)) + return elements[--count]; + else + { + set_error (); + return Crap (ELEM); + } + } + void pop (unsigned int n) + { + if (likely (count >= n)) + count -= n; + else + set_error (); + } + + const ELEM& peek () + { + if (unlikely (count == 0)) + { + set_error (); + return Null (ELEM); + } + return elements[count - 1]; + } + + void unpop () + { + if (likely (count < LIMIT)) + count++; + else + set_error (); + } + + void clear () { count = 0; } + + bool in_error () const { return (error); } + void set_error () { error = true; } + + unsigned int get_count () const { return count; } + bool is_empty () const { return !count; } + + hb_array_t sub_array (unsigned start, unsigned length) const + { return hb_array_t (elements).sub_array (start, length); } + + private: + bool error = false; + unsigned int count = 0; + ELEM elements[LIMIT]; +}; + +/* argument stack */ +template +struct arg_stack_t : cff_stack_t +{ + void push_int (int v) + { + ARG &n = S::push (); + n.set_int (v); + } + + void push_fixed (int32_t v) + { + ARG &n = S::push (); + n.set_fixed (v); + } + + void push_real (double v) + { + ARG &n = S::push (); + n.set_real (v); + } + + ARG& pop_num () { return this->pop (); } + + int pop_int () { return this->pop ().to_int (); } + + unsigned int pop_uint () + { + int i = pop_int (); + if (unlikely (i < 0)) + { + i = 0; + S::set_error (); + } + return (unsigned) i; + } + + void push_longint_from_substr (byte_str_ref_t& str_ref) + { + push_int ((str_ref[0] << 24) | (str_ref[1] << 16) | (str_ref[2] << 8) | (str_ref[3])); + str_ref.inc (4); + } + + bool push_fixed_from_substr (byte_str_ref_t& str_ref) + { + if (unlikely (!str_ref.avail (4))) + return false; + push_fixed ((int32_t)*(const HBUINT32*)&str_ref[0]); + str_ref.inc (4); + return true; + } + + private: + typedef cff_stack_t S; +}; + +/* an operator prefixed by its operands in a byte string */ +struct op_str_t +{ + hb_ubytes_t str; + op_code_t op; +}; + +/* base of OP_SERIALIZER */ +struct op_serializer_t +{ + protected: + bool copy_opstr (hb_serialize_context_t *c, const op_str_t& opstr) const + { + TRACE_SERIALIZE (this); + + HBUINT8 *d = c->allocate_size (opstr.str.length); + if (unlikely (!d)) return_trace (false); + memcpy (d, &opstr.str[0], opstr.str.length); + return_trace (true); + } +}; + +template +struct parsed_values_t +{ + void init () + { + opStart = 0; + values.init (); + } + void fini () { values.fini (); } + + void alloc (unsigned n) + { + values.alloc (n); + } + + void add_op (op_code_t op, const byte_str_ref_t& str_ref = byte_str_ref_t ()) + { + VAL *val = values.push (); + val->op = op; + val->str = str_ref.str.sub_array (opStart, str_ref.offset - opStart); + opStart = str_ref.offset; + } + + void add_op (op_code_t op, const byte_str_ref_t& str_ref, const VAL &v) + { + VAL *val = values.push (v); + val->op = op; + val->str = str_ref.sub_array ( opStart, str_ref.offset - opStart); + opStart = str_ref.offset; + } + + bool has_op (op_code_t op) const + { + for (const auto& v : values) + if (v.op == op) return true; + return false; + } + + unsigned get_count () const { return values.length; } + const VAL &get_value (unsigned int i) const { return values[i]; } + const VAL &operator [] (unsigned int i) const { return get_value (i); } + + unsigned int opStart; + hb_vector_t values; +}; + +template +struct interp_env_t +{ + interp_env_t () {} + interp_env_t (const hb_ubytes_t &str_) + { + str_ref.reset (str_); + } + bool in_error () const + { return error || str_ref.in_error () || argStack.in_error (); } + + void set_error () { error = true; } + + op_code_t fetch_op () + { + op_code_t op = OpCode_Invalid; + if (unlikely (!str_ref.avail ())) + return OpCode_Invalid; + op = (op_code_t)(unsigned char)str_ref[0]; + if (op == OpCode_escape) { + if (unlikely (!str_ref.avail ())) + return OpCode_Invalid; + op = Make_OpCode_ESC(str_ref[1]); + str_ref.inc (); + } + str_ref.inc (); + return op; + } + + const ARG& eval_arg (unsigned int i) { return argStack[i]; } + + ARG& pop_arg () { return argStack.pop (); } + void pop_n_args (unsigned int n) { argStack.pop (n); } + + void clear_args () { pop_n_args (argStack.get_count ()); } + + byte_str_ref_t + str_ref; + arg_stack_t + argStack; + protected: + bool error = false; +}; + +using num_interp_env_t = interp_env_t<>; + +template +struct opset_t +{ + static void process_op (op_code_t op, interp_env_t& env) + { + switch (op) { + case OpCode_shortint: + env.argStack.push_int ((int16_t)((env.str_ref[0] << 8) | env.str_ref[1])); + env.str_ref.inc (2); + break; + + case OpCode_TwoBytePosInt0: case OpCode_TwoBytePosInt1: + case OpCode_TwoBytePosInt2: case OpCode_TwoBytePosInt3: + env.argStack.push_int ((int16_t)((op - OpCode_TwoBytePosInt0) * 256 + env.str_ref[0] + 108)); + env.str_ref.inc (); + break; + + case OpCode_TwoByteNegInt0: case OpCode_TwoByteNegInt1: + case OpCode_TwoByteNegInt2: case OpCode_TwoByteNegInt3: + env.argStack.push_int ((-(int16_t)(op - OpCode_TwoByteNegInt0) * 256 - env.str_ref[0] - 108)); + env.str_ref.inc (); + break; + + default: + /* 1-byte integer */ + if (likely ((OpCode_OneByteIntFirst <= op) && (op <= OpCode_OneByteIntLast))) + { + env.argStack.push_int ((int)op - 139); + } else { + /* invalid unknown operator */ + env.clear_args (); + env.set_error (); + } + break; + } + } +}; + +template +struct interpreter_t +{ + interpreter_t (ENV& env_) : env (env_) {} + ENV& env; +}; + +} /* namespace CFF */ + +#endif /* HB_CFF_INTERP_COMMON_HH */ diff --git a/libs/harfbuzz/src/hb-cff-interp-cs-common.hh b/libs/harfbuzz/src/hb-cff-interp-cs-common.hh new file mode 100644 index 00000000..f93c83ab --- /dev/null +++ b/libs/harfbuzz/src/hb-cff-interp-cs-common.hh @@ -0,0 +1,908 @@ +/* + * Copyright © 2018 Adobe 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. + * + * Adobe Author(s): Michiharu Ariza + */ +#ifndef HB_CFF_INTERP_CS_COMMON_HH +#define HB_CFF_INTERP_CS_COMMON_HH + +#include "hb.hh" +#include "hb-cff-interp-common.hh" + +namespace CFF { + +using namespace OT; + +enum cs_type_t { + CSType_CharString, + CSType_GlobalSubr, + CSType_LocalSubr +}; + +struct call_context_t +{ + void init (const byte_str_ref_t substr_=byte_str_ref_t (), cs_type_t type_=CSType_CharString, unsigned int subr_num_=0) + { + str_ref = substr_; + type = type_; + subr_num = subr_num_; + } + + void fini () {} + + byte_str_ref_t str_ref; + cs_type_t type; + unsigned int subr_num; +}; + +/* call stack */ +const unsigned int kMaxCallLimit = 10; +const unsigned int kMaxOps = 10000; +struct call_stack_t : cff_stack_t {}; + +template +struct biased_subrs_t +{ + void init (const SUBRS *subrs_) + { + subrs = subrs_; + unsigned int nSubrs = get_count (); + if (nSubrs < 1240) + bias = 107; + else if (nSubrs < 33900) + bias = 1131; + else + bias = 32768; + } + + void fini () {} + + unsigned int get_count () const { return subrs ? subrs->count : 0; } + unsigned int get_bias () const { return bias; } + + hb_ubytes_t operator [] (unsigned int index) const + { + if (unlikely (!subrs || index >= subrs->count)) + return hb_ubytes_t (); + else + return (*subrs)[index]; + } + + protected: + unsigned int bias; + const SUBRS *subrs; +}; + +struct point_t +{ + void set_int (int _x, int _y) + { + x.set_int (_x); + y.set_int (_y); + } + + void move_x (const number_t &dx) { x += dx; } + void move_y (const number_t &dy) { y += dy; } + void move (const number_t &dx, const number_t &dy) { move_x (dx); move_y (dy); } + void move (const point_t &d) { move_x (d.x); move_y (d.y); } + + number_t x; + number_t y; +}; + +template +struct cs_interp_env_t : interp_env_t +{ + cs_interp_env_t (const hb_ubytes_t &str, const SUBRS *globalSubrs_, const SUBRS *localSubrs_) : + interp_env_t (str) + { + context.init (str, CSType_CharString); + seen_moveto = true; + seen_hintmask = false; + hstem_count = 0; + vstem_count = 0; + hintmask_size = 0; + pt.set_int (0, 0); + globalSubrs.init (globalSubrs_); + localSubrs.init (localSubrs_); + } + ~cs_interp_env_t () + { + globalSubrs.fini (); + localSubrs.fini (); + } + + bool in_error () const + { + return callStack.in_error () || SUPER::in_error (); + } + + bool pop_subr_num (const biased_subrs_t& biasedSubrs, unsigned int &subr_num) + { + subr_num = 0; + int n = SUPER::argStack.pop_int (); + n += biasedSubrs.get_bias (); + if (unlikely ((n < 0) || ((unsigned int)n >= biasedSubrs.get_count ()))) + return false; + + subr_num = (unsigned int)n; + return true; + } + + void call_subr (const biased_subrs_t& biasedSubrs, cs_type_t type) + { + unsigned int subr_num = 0; + + if (unlikely (!pop_subr_num (biasedSubrs, subr_num) + || callStack.get_count () >= kMaxCallLimit)) + { + SUPER::set_error (); + return; + } + context.str_ref = SUPER::str_ref; + callStack.push (context); + + context.init ( biasedSubrs[subr_num], type, subr_num); + SUPER::str_ref = context.str_ref; + } + + void return_from_subr () + { + if (unlikely (SUPER::str_ref.in_error ())) + SUPER::set_error (); + context = callStack.pop (); + SUPER::str_ref = context.str_ref; + } + + void determine_hintmask_size () + { + if (!seen_hintmask) + { + vstem_count += SUPER::argStack.get_count() / 2; + hintmask_size = (hstem_count + vstem_count + 7) >> 3; + seen_hintmask = true; + } + } + + void set_endchar (bool endchar_flag_) { endchar_flag = endchar_flag_; } + bool is_endchar () const { return endchar_flag; } + + const number_t &get_x () const { return pt.x; } + const number_t &get_y () const { return pt.y; } + const point_t &get_pt () const { return pt; } + + void moveto (const point_t &pt_ ) { pt = pt_; } + + public: + call_context_t context; + bool endchar_flag; + bool seen_moveto; + bool seen_hintmask; + + unsigned int hstem_count; + unsigned int vstem_count; + unsigned int hintmask_size; + call_stack_t callStack; + biased_subrs_t globalSubrs; + biased_subrs_t localSubrs; + + private: + point_t pt; + + typedef interp_env_t SUPER; +}; + +template +struct path_procs_null_t +{ + static void rmoveto (ENV &env, PARAM& param) {} + static void hmoveto (ENV &env, PARAM& param) {} + static void vmoveto (ENV &env, PARAM& param) {} + static void rlineto (ENV &env, PARAM& param) {} + static void hlineto (ENV &env, PARAM& param) {} + static void vlineto (ENV &env, PARAM& param) {} + static void rrcurveto (ENV &env, PARAM& param) {} + static void rcurveline (ENV &env, PARAM& param) {} + static void rlinecurve (ENV &env, PARAM& param) {} + static void vvcurveto (ENV &env, PARAM& param) {} + static void hhcurveto (ENV &env, PARAM& param) {} + static void vhcurveto (ENV &env, PARAM& param) {} + static void hvcurveto (ENV &env, PARAM& param) {} + static void moveto (ENV &env, PARAM& param, const point_t &pt) {} + static void line (ENV &env, PARAM& param, const point_t &pt1) {} + static void curve (ENV &env, PARAM& param, const point_t &pt1, const point_t &pt2, const point_t &pt3) {} + static void hflex (ENV &env, PARAM& param) {} + static void flex (ENV &env, PARAM& param) {} + static void hflex1 (ENV &env, PARAM& param) {} + static void flex1 (ENV &env, PARAM& param) {} +}; + +template > +struct cs_opset_t : opset_t +{ + static void process_op (op_code_t op, ENV &env, PARAM& param) + { + switch (op) { + + case OpCode_return: + env.return_from_subr (); + break; + case OpCode_endchar: + OPSET::check_width (op, env, param); + env.set_endchar (true); + OPSET::flush_args_and_op (op, env, param); + break; + + case OpCode_fixedcs: + env.argStack.push_fixed_from_substr (env.str_ref); + break; + + case OpCode_callsubr: + env.call_subr (env.localSubrs, CSType_LocalSubr); + break; + + case OpCode_callgsubr: + env.call_subr (env.globalSubrs, CSType_GlobalSubr); + break; + + case OpCode_hstem: + case OpCode_hstemhm: + OPSET::check_width (op, env, param); + OPSET::process_hstem (op, env, param); + break; + case OpCode_vstem: + case OpCode_vstemhm: + OPSET::check_width (op, env, param); + OPSET::process_vstem (op, env, param); + break; + case OpCode_hintmask: + case OpCode_cntrmask: + OPSET::check_width (op, env, param); + OPSET::process_hintmask (op, env, param); + break; + case OpCode_rmoveto: + OPSET::check_width (op, env, param); + PATH::rmoveto (env, param); + OPSET::process_post_move (op, env, param); + break; + case OpCode_hmoveto: + OPSET::check_width (op, env, param); + PATH::hmoveto (env, param); + OPSET::process_post_move (op, env, param); + break; + case OpCode_vmoveto: + OPSET::check_width (op, env, param); + PATH::vmoveto (env, param); + OPSET::process_post_move (op, env, param); + break; + case OpCode_rlineto: + PATH::rlineto (env, param); + process_post_path (op, env, param); + break; + case OpCode_hlineto: + PATH::hlineto (env, param); + process_post_path (op, env, param); + break; + case OpCode_vlineto: + PATH::vlineto (env, param); + process_post_path (op, env, param); + break; + case OpCode_rrcurveto: + PATH::rrcurveto (env, param); + process_post_path (op, env, param); + break; + case OpCode_rcurveline: + PATH::rcurveline (env, param); + process_post_path (op, env, param); + break; + case OpCode_rlinecurve: + PATH::rlinecurve (env, param); + process_post_path (op, env, param); + break; + case OpCode_vvcurveto: + PATH::vvcurveto (env, param); + process_post_path (op, env, param); + break; + case OpCode_hhcurveto: + PATH::hhcurveto (env, param); + process_post_path (op, env, param); + break; + case OpCode_vhcurveto: + PATH::vhcurveto (env, param); + process_post_path (op, env, param); + break; + case OpCode_hvcurveto: + PATH::hvcurveto (env, param); + process_post_path (op, env, param); + break; + + case OpCode_hflex: + PATH::hflex (env, param); + OPSET::process_post_flex (op, env, param); + break; + + case OpCode_flex: + PATH::flex (env, param); + OPSET::process_post_flex (op, env, param); + break; + + case OpCode_hflex1: + PATH::hflex1 (env, param); + OPSET::process_post_flex (op, env, param); + break; + + case OpCode_flex1: + PATH::flex1 (env, param); + OPSET::process_post_flex (op, env, param); + break; + + default: + SUPER::process_op (op, env); + break; + } + } + + static void process_hstem (op_code_t op, ENV &env, PARAM& param) + { + env.hstem_count += env.argStack.get_count () / 2; + OPSET::flush_args_and_op (op, env, param); + } + + static void process_vstem (op_code_t op, ENV &env, PARAM& param) + { + env.vstem_count += env.argStack.get_count () / 2; + OPSET::flush_args_and_op (op, env, param); + } + + static void process_hintmask (op_code_t op, ENV &env, PARAM& param) + { + env.determine_hintmask_size (); + if (likely (env.str_ref.avail (env.hintmask_size))) + { + OPSET::flush_hintmask (op, env, param); + env.str_ref.inc (env.hintmask_size); + } + } + + static void process_post_flex (op_code_t op, ENV &env, PARAM& param) + { + OPSET::flush_args_and_op (op, env, param); + } + + static void check_width (op_code_t op, ENV &env, PARAM& param) + {} + + static void process_post_move (op_code_t op, ENV &env, PARAM& param) + { + if (!env.seen_moveto) + { + env.determine_hintmask_size (); + env.seen_moveto = true; + } + OPSET::flush_args_and_op (op, env, param); + } + + static void process_post_path (op_code_t op, ENV &env, PARAM& param) + { + OPSET::flush_args_and_op (op, env, param); + } + + static void flush_args_and_op (op_code_t op, ENV &env, PARAM& param) + { + OPSET::flush_args (env, param); + OPSET::flush_op (op, env, param); + } + + static void flush_args (ENV &env, PARAM& param) + { + env.pop_n_args (env.argStack.get_count ()); + } + + static void flush_op (op_code_t op, ENV &env, PARAM& param) + { + } + + static void flush_hintmask (op_code_t op, ENV &env, PARAM& param) + { + OPSET::flush_args_and_op (op, env, param); + } + + static bool is_number_op (op_code_t op) + { + switch (op) + { + case OpCode_shortint: + case OpCode_fixedcs: + case OpCode_TwoBytePosInt0: case OpCode_TwoBytePosInt1: + case OpCode_TwoBytePosInt2: case OpCode_TwoBytePosInt3: + case OpCode_TwoByteNegInt0: case OpCode_TwoByteNegInt1: + case OpCode_TwoByteNegInt2: case OpCode_TwoByteNegInt3: + return true; + + default: + /* 1-byte integer */ + return (OpCode_OneByteIntFirst <= op) && (op <= OpCode_OneByteIntLast); + } + } + + protected: + typedef opset_t SUPER; +}; + +template +struct path_procs_t +{ + static void rmoveto (ENV &env, PARAM& param) + { + point_t pt1 = env.get_pt (); + const number_t &dy = env.pop_arg (); + const number_t &dx = env.pop_arg (); + pt1.move (dx, dy); + PATH::moveto (env, param, pt1); + } + + static void hmoveto (ENV &env, PARAM& param) + { + point_t pt1 = env.get_pt (); + pt1.move_x (env.pop_arg ()); + PATH::moveto (env, param, pt1); + } + + static void vmoveto (ENV &env, PARAM& param) + { + point_t pt1 = env.get_pt (); + pt1.move_y (env.pop_arg ()); + PATH::moveto (env, param, pt1); + } + + static void rlineto (ENV &env, PARAM& param) + { + for (unsigned int i = 0; i + 2 <= env.argStack.get_count (); i += 2) + { + point_t pt1 = env.get_pt (); + pt1.move (env.eval_arg (i), env.eval_arg (i+1)); + PATH::line (env, param, pt1); + } + } + + static void hlineto (ENV &env, PARAM& param) + { + point_t pt1; + unsigned int i = 0; + for (; i + 2 <= env.argStack.get_count (); i += 2) + { + pt1 = env.get_pt (); + pt1.move_x (env.eval_arg (i)); + PATH::line (env, param, pt1); + pt1.move_y (env.eval_arg (i+1)); + PATH::line (env, param, pt1); + } + if (i < env.argStack.get_count ()) + { + pt1 = env.get_pt (); + pt1.move_x (env.eval_arg (i)); + PATH::line (env, param, pt1); + } + } + + static void vlineto (ENV &env, PARAM& param) + { + point_t pt1; + unsigned int i = 0; + for (; i + 2 <= env.argStack.get_count (); i += 2) + { + pt1 = env.get_pt (); + pt1.move_y (env.eval_arg (i)); + PATH::line (env, param, pt1); + pt1.move_x (env.eval_arg (i+1)); + PATH::line (env, param, pt1); + } + if (i < env.argStack.get_count ()) + { + pt1 = env.get_pt (); + pt1.move_y (env.eval_arg (i)); + PATH::line (env, param, pt1); + } + } + + static void rrcurveto (ENV &env, PARAM& param) + { + for (unsigned int i = 0; i + 6 <= env.argStack.get_count (); i += 6) + { + point_t pt1 = env.get_pt (); + pt1.move (env.eval_arg (i), env.eval_arg (i+1)); + point_t pt2 = pt1; + pt2.move (env.eval_arg (i+2), env.eval_arg (i+3)); + point_t pt3 = pt2; + pt3.move (env.eval_arg (i+4), env.eval_arg (i+5)); + PATH::curve (env, param, pt1, pt2, pt3); + } + } + + static void rcurveline (ENV &env, PARAM& param) + { + unsigned int arg_count = env.argStack.get_count (); + if (unlikely (arg_count < 8)) + return; + + unsigned int i = 0; + unsigned int curve_limit = arg_count - 2; + for (; i + 6 <= curve_limit; i += 6) + { + point_t pt1 = env.get_pt (); + pt1.move (env.eval_arg (i), env.eval_arg (i+1)); + point_t pt2 = pt1; + pt2.move (env.eval_arg (i+2), env.eval_arg (i+3)); + point_t pt3 = pt2; + pt3.move (env.eval_arg (i+4), env.eval_arg (i+5)); + PATH::curve (env, param, pt1, pt2, pt3); + } + + point_t pt1 = env.get_pt (); + pt1.move (env.eval_arg (i), env.eval_arg (i+1)); + PATH::line (env, param, pt1); + } + + static void rlinecurve (ENV &env, PARAM& param) + { + unsigned int arg_count = env.argStack.get_count (); + if (unlikely (arg_count < 8)) + return; + + unsigned int i = 0; + unsigned int line_limit = arg_count - 6; + for (; i + 2 <= line_limit; i += 2) + { + point_t pt1 = env.get_pt (); + pt1.move (env.eval_arg (i), env.eval_arg (i+1)); + PATH::line (env, param, pt1); + } + + point_t pt1 = env.get_pt (); + pt1.move (env.eval_arg (i), env.eval_arg (i+1)); + point_t pt2 = pt1; + pt2.move (env.eval_arg (i+2), env.eval_arg (i+3)); + point_t pt3 = pt2; + pt3.move (env.eval_arg (i+4), env.eval_arg (i+5)); + PATH::curve (env, param, pt1, pt2, pt3); + } + + static void vvcurveto (ENV &env, PARAM& param) + { + unsigned int i = 0; + point_t pt1 = env.get_pt (); + if ((env.argStack.get_count () & 1) != 0) + pt1.move_x (env.eval_arg (i++)); + for (; i + 4 <= env.argStack.get_count (); i += 4) + { + pt1.move_y (env.eval_arg (i)); + point_t pt2 = pt1; + pt2.move (env.eval_arg (i+1), env.eval_arg (i+2)); + point_t pt3 = pt2; + pt3.move_y (env.eval_arg (i+3)); + PATH::curve (env, param, pt1, pt2, pt3); + pt1 = env.get_pt (); + } + } + + static void hhcurveto (ENV &env, PARAM& param) + { + unsigned int i = 0; + point_t pt1 = env.get_pt (); + if ((env.argStack.get_count () & 1) != 0) + pt1.move_y (env.eval_arg (i++)); + for (; i + 4 <= env.argStack.get_count (); i += 4) + { + pt1.move_x (env.eval_arg (i)); + point_t pt2 = pt1; + pt2.move (env.eval_arg (i+1), env.eval_arg (i+2)); + point_t pt3 = pt2; + pt3.move_x (env.eval_arg (i+3)); + PATH::curve (env, param, pt1, pt2, pt3); + pt1 = env.get_pt (); + } + } + + static void vhcurveto (ENV &env, PARAM& param) + { + point_t pt1, pt2, pt3; + unsigned int i = 0; + if ((env.argStack.get_count () % 8) >= 4) + { + point_t pt1 = env.get_pt (); + pt1.move_y (env.eval_arg (i)); + point_t pt2 = pt1; + pt2.move (env.eval_arg (i+1), env.eval_arg (i+2)); + point_t pt3 = pt2; + pt3.move_x (env.eval_arg (i+3)); + i += 4; + + for (; i + 8 <= env.argStack.get_count (); i += 8) + { + PATH::curve (env, param, pt1, pt2, pt3); + pt1 = env.get_pt (); + pt1.move_x (env.eval_arg (i)); + pt2 = pt1; + pt2.move (env.eval_arg (i+1), env.eval_arg (i+2)); + pt3 = pt2; + pt3.move_y (env.eval_arg (i+3)); + PATH::curve (env, param, pt1, pt2, pt3); + + pt1 = pt3; + pt1.move_y (env.eval_arg (i+4)); + pt2 = pt1; + pt2.move (env.eval_arg (i+5), env.eval_arg (i+6)); + pt3 = pt2; + pt3.move_x (env.eval_arg (i+7)); + } + if (i < env.argStack.get_count ()) + pt3.move_y (env.eval_arg (i)); + PATH::curve (env, param, pt1, pt2, pt3); + } + else + { + for (; i + 8 <= env.argStack.get_count (); i += 8) + { + pt1 = env.get_pt (); + pt1.move_y (env.eval_arg (i)); + pt2 = pt1; + pt2.move (env.eval_arg (i+1), env.eval_arg (i+2)); + pt3 = pt2; + pt3.move_x (env.eval_arg (i+3)); + PATH::curve (env, param, pt1, pt2, pt3); + + pt1 = pt3; + pt1.move_x (env.eval_arg (i+4)); + pt2 = pt1; + pt2.move (env.eval_arg (i+5), env.eval_arg (i+6)); + pt3 = pt2; + pt3.move_y (env.eval_arg (i+7)); + if ((env.argStack.get_count () - i < 16) && ((env.argStack.get_count () & 1) != 0)) + pt3.move_x (env.eval_arg (i+8)); + PATH::curve (env, param, pt1, pt2, pt3); + } + } + } + + static void hvcurveto (ENV &env, PARAM& param) + { + point_t pt1, pt2, pt3; + unsigned int i = 0; + if ((env.argStack.get_count () % 8) >= 4) + { + point_t pt1 = env.get_pt (); + pt1.move_x (env.eval_arg (i)); + point_t pt2 = pt1; + pt2.move (env.eval_arg (i+1), env.eval_arg (i+2)); + point_t pt3 = pt2; + pt3.move_y (env.eval_arg (i+3)); + i += 4; + + for (; i + 8 <= env.argStack.get_count (); i += 8) + { + PATH::curve (env, param, pt1, pt2, pt3); + pt1 = env.get_pt (); + pt1.move_y (env.eval_arg (i)); + pt2 = pt1; + pt2.move (env.eval_arg (i+1), env.eval_arg (i+2)); + pt3 = pt2; + pt3.move_x (env.eval_arg (i+3)); + PATH::curve (env, param, pt1, pt2, pt3); + + pt1 = pt3; + pt1.move_x (env.eval_arg (i+4)); + pt2 = pt1; + pt2.move (env.eval_arg (i+5), env.eval_arg (i+6)); + pt3 = pt2; + pt3.move_y (env.eval_arg (i+7)); + } + if (i < env.argStack.get_count ()) + pt3.move_x (env.eval_arg (i)); + PATH::curve (env, param, pt1, pt2, pt3); + } + else + { + for (; i + 8 <= env.argStack.get_count (); i += 8) + { + pt1 = env.get_pt (); + pt1.move_x (env.eval_arg (i)); + pt2 = pt1; + pt2.move (env.eval_arg (i+1), env.eval_arg (i+2)); + pt3 = pt2; + pt3.move_y (env.eval_arg (i+3)); + PATH::curve (env, param, pt1, pt2, pt3); + + pt1 = pt3; + pt1.move_y (env.eval_arg (i+4)); + pt2 = pt1; + pt2.move (env.eval_arg (i+5), env.eval_arg (i+6)); + pt3 = pt2; + pt3.move_x (env.eval_arg (i+7)); + if ((env.argStack.get_count () - i < 16) && ((env.argStack.get_count () & 1) != 0)) + pt3.move_y (env.eval_arg (i+8)); + PATH::curve (env, param, pt1, pt2, pt3); + } + } + } + + /* default actions to be overridden */ + static void moveto (ENV &env, PARAM& param, const point_t &pt) + { env.moveto (pt); } + + static void line (ENV &env, PARAM& param, const point_t &pt1) + { PATH::moveto (env, param, pt1); } + + static void curve (ENV &env, PARAM& param, const point_t &pt1, const point_t &pt2, const point_t &pt3) + { PATH::moveto (env, param, pt3); } + + static void hflex (ENV &env, PARAM& param) + { + if (likely (env.argStack.get_count () == 7)) + { + point_t pt1 = env.get_pt (); + pt1.move_x (env.eval_arg (0)); + point_t pt2 = pt1; + pt2.move (env.eval_arg (1), env.eval_arg (2)); + point_t pt3 = pt2; + pt3.move_x (env.eval_arg (3)); + point_t pt4 = pt3; + pt4.move_x (env.eval_arg (4)); + point_t pt5 = pt4; + pt5.move_x (env.eval_arg (5)); + pt5.y = pt1.y; + point_t pt6 = pt5; + pt6.move_x (env.eval_arg (6)); + + curve2 (env, param, pt1, pt2, pt3, pt4, pt5, pt6); + } + else + env.set_error (); + } + + static void flex (ENV &env, PARAM& param) + { + if (likely (env.argStack.get_count () == 13)) + { + point_t pt1 = env.get_pt (); + pt1.move (env.eval_arg (0), env.eval_arg (1)); + point_t pt2 = pt1; + pt2.move (env.eval_arg (2), env.eval_arg (3)); + point_t pt3 = pt2; + pt3.move (env.eval_arg (4), env.eval_arg (5)); + point_t pt4 = pt3; + pt4.move (env.eval_arg (6), env.eval_arg (7)); + point_t pt5 = pt4; + pt5.move (env.eval_arg (8), env.eval_arg (9)); + point_t pt6 = pt5; + pt6.move (env.eval_arg (10), env.eval_arg (11)); + + curve2 (env, param, pt1, pt2, pt3, pt4, pt5, pt6); + } + else + env.set_error (); + } + + static void hflex1 (ENV &env, PARAM& param) + { + if (likely (env.argStack.get_count () == 9)) + { + point_t pt1 = env.get_pt (); + pt1.move (env.eval_arg (0), env.eval_arg (1)); + point_t pt2 = pt1; + pt2.move (env.eval_arg (2), env.eval_arg (3)); + point_t pt3 = pt2; + pt3.move_x (env.eval_arg (4)); + point_t pt4 = pt3; + pt4.move_x (env.eval_arg (5)); + point_t pt5 = pt4; + pt5.move (env.eval_arg (6), env.eval_arg (7)); + point_t pt6 = pt5; + pt6.move_x (env.eval_arg (8)); + pt6.y = env.get_pt ().y; + + curve2 (env, param, pt1, pt2, pt3, pt4, pt5, pt6); + } + else + env.set_error (); + } + + static void flex1 (ENV &env, PARAM& param) + { + if (likely (env.argStack.get_count () == 11)) + { + point_t d; + for (unsigned int i = 0; i < 10; i += 2) + d.move (env.eval_arg (i), env.eval_arg (i+1)); + + point_t pt1 = env.get_pt (); + pt1.move (env.eval_arg (0), env.eval_arg (1)); + point_t pt2 = pt1; + pt2.move (env.eval_arg (2), env.eval_arg (3)); + point_t pt3 = pt2; + pt3.move (env.eval_arg (4), env.eval_arg (5)); + point_t pt4 = pt3; + pt4.move (env.eval_arg (6), env.eval_arg (7)); + point_t pt5 = pt4; + pt5.move (env.eval_arg (8), env.eval_arg (9)); + point_t pt6 = pt5; + + if (fabs (d.x.to_real ()) > fabs (d.y.to_real ())) + { + pt6.move_x (env.eval_arg (10)); + pt6.y = env.get_pt ().y; + } + else + { + pt6.x = env.get_pt ().x; + pt6.move_y (env.eval_arg (10)); + } + + curve2 (env, param, pt1, pt2, pt3, pt4, pt5, pt6); + } + else + env.set_error (); + } + + protected: + static void curve2 (ENV &env, PARAM& param, + const point_t &pt1, const point_t &pt2, const point_t &pt3, + const point_t &pt4, const point_t &pt5, const point_t &pt6) + { + PATH::curve (env, param, pt1, pt2, pt3); + PATH::curve (env, param, pt4, pt5, pt6); + } +}; + +template +struct cs_interpreter_t : interpreter_t +{ + cs_interpreter_t (ENV& env_) : interpreter_t (env_) {} + + bool interpret (PARAM& param) + { + SUPER::env.set_endchar (false); + + unsigned max_ops = kMaxOps; + for (;;) { + if (unlikely (!--max_ops)) + { + SUPER::env.set_error (); + break; + } + OPSET::process_op (SUPER::env.fetch_op (), SUPER::env, param); + if (unlikely (SUPER::env.in_error ())) + return false; + if (SUPER::env.is_endchar ()) + break; + } + + return true; + } + + private: + typedef interpreter_t SUPER; +}; + +} /* namespace CFF */ + +#endif /* HB_CFF_INTERP_CS_COMMON_HH */ diff --git a/libs/harfbuzz/src/hb-cff-interp-dict-common.hh b/libs/harfbuzz/src/hb-cff-interp-dict-common.hh new file mode 100644 index 00000000..79fe9b42 --- /dev/null +++ b/libs/harfbuzz/src/hb-cff-interp-dict-common.hh @@ -0,0 +1,203 @@ +/* + * Copyright © 2018 Adobe 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. + * + * Adobe Author(s): Michiharu Ariza + */ +#ifndef HB_CFF_INTERP_DICT_COMMON_HH +#define HB_CFF_INTERP_DICT_COMMON_HH + +#include "hb-cff-interp-common.hh" + +namespace CFF { + +using namespace OT; + +/* an opstr and the parsed out dict value(s) */ +struct dict_val_t : op_str_t +{ + void init () { single_val.set_int (0); } + void fini () {} + + number_t single_val; +}; + +typedef dict_val_t num_dict_val_t; + +template struct dict_values_t : parsed_values_t {}; + +template +struct top_dict_values_t : dict_values_t +{ + void init () + { + dict_values_t::init (); + charStringsOffset = 0; + FDArrayOffset = 0; + } + void fini () { dict_values_t::fini (); } + + unsigned int charStringsOffset; + unsigned int FDArrayOffset; +}; + +struct dict_opset_t : opset_t +{ + static void process_op (op_code_t op, interp_env_t& env) + { + switch (op) { + case OpCode_longintdict: /* 5-byte integer */ + env.argStack.push_longint_from_substr (env.str_ref); + break; + + case OpCode_BCD: /* real number */ + env.argStack.push_real (parse_bcd (env.str_ref)); + break; + + default: + opset_t::process_op (op, env); + break; + } + } + + /* Turns CFF's BCD format into strtod understandable string */ + static double parse_bcd (byte_str_ref_t& str_ref) + { + if (unlikely (str_ref.in_error ())) return .0; + + enum Nibble { DECIMAL=10, EXP_POS, EXP_NEG, RESERVED, NEG, END }; + + char buf[32]; + unsigned char byte = 0; + for (unsigned i = 0, count = 0; count < ARRAY_LENGTH (buf); ++i, ++count) + { + unsigned nibble; + if (!(i & 1)) + { + if (unlikely (!str_ref.avail ())) break; + + byte = str_ref[0]; + str_ref.inc (); + nibble = byte >> 4; + } + else + nibble = byte & 0x0F; + + if (unlikely (nibble == RESERVED)) break; + else if (nibble == END) + { + const char *p = buf; + double pv; + if (unlikely (!hb_parse_double (&p, p + count, &pv, true/* whole buffer */))) + break; + return pv; + } + else + { + buf[count] = "0123456789.EE?-?"[nibble]; + if (nibble == EXP_NEG) + { + ++count; + if (unlikely (count == ARRAY_LENGTH (buf))) break; + buf[count] = '-'; + } + } + } + + str_ref.set_error (); + return .0; + } + + static bool is_hint_op (op_code_t op) + { + switch (op) + { + case OpCode_BlueValues: + case OpCode_OtherBlues: + case OpCode_FamilyBlues: + case OpCode_FamilyOtherBlues: + case OpCode_StemSnapH: + case OpCode_StemSnapV: + case OpCode_StdHW: + case OpCode_StdVW: + case OpCode_BlueScale: + case OpCode_BlueShift: + case OpCode_BlueFuzz: + case OpCode_ForceBold: + case OpCode_LanguageGroup: + case OpCode_ExpansionFactor: + return true; + default: + return false; + } + } +}; + +template +struct top_dict_opset_t : dict_opset_t +{ + static void process_op (op_code_t op, interp_env_t& env, top_dict_values_t & dictval) + { + switch (op) { + case OpCode_CharStrings: + dictval.charStringsOffset = env.argStack.pop_uint (); + env.clear_args (); + break; + case OpCode_FDArray: + dictval.FDArrayOffset = env.argStack.pop_uint (); + env.clear_args (); + break; + case OpCode_FontMatrix: + env.clear_args (); + break; + default: + dict_opset_t::process_op (op, env); + break; + } + } +}; + +template +struct dict_interpreter_t : interpreter_t +{ + dict_interpreter_t (ENV& env_) : interpreter_t (env_) {} + + bool interpret (PARAM& param) + { + param.init (); + while (SUPER::env.str_ref.avail ()) + { + OPSET::process_op (SUPER::env.fetch_op (), SUPER::env, param); + if (unlikely (SUPER::env.in_error ())) + return false; + } + + return true; + } + + private: + typedef interpreter_t SUPER; +}; + +} /* namespace CFF */ + +#endif /* HB_CFF_INTERP_DICT_COMMON_HH */ diff --git a/libs/harfbuzz/src/hb-cff1-interp-cs.hh b/libs/harfbuzz/src/hb-cff1-interp-cs.hh new file mode 100644 index 00000000..b306c2ec --- /dev/null +++ b/libs/harfbuzz/src/hb-cff1-interp-cs.hh @@ -0,0 +1,159 @@ +/* + * Copyright © 2018 Adobe 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. + * + * Adobe Author(s): Michiharu Ariza + */ +#ifndef HB_CFF1_INTERP_CS_HH +#define HB_CFF1_INTERP_CS_HH + +#include "hb.hh" +#include "hb-cff-interp-cs-common.hh" + +namespace CFF { + +using namespace OT; + +typedef biased_subrs_t cff1_biased_subrs_t; + +struct cff1_cs_interp_env_t : cs_interp_env_t +{ + template + cff1_cs_interp_env_t (const hb_ubytes_t &str, ACC &acc, unsigned int fd) + : SUPER (str, acc.globalSubrs, acc.privateDicts[fd].localSubrs) + { + processed_width = false; + has_width = false; + arg_start = 0; + in_seac = false; + } + + void set_width (bool has_width_) + { + if (likely (!processed_width && (SUPER::argStack.get_count () > 0))) + { + if (has_width_) + { + width = SUPER::argStack[0]; + has_width = true; + arg_start = 1; + } + } + processed_width = true; + } + + void clear_args () + { + arg_start = 0; + SUPER::clear_args (); + } + + void set_in_seac (bool _in_seac) { in_seac = _in_seac; } + + bool processed_width; + bool has_width; + unsigned int arg_start; + number_t width; + bool in_seac; + + private: + typedef cs_interp_env_t SUPER; +}; + +template > +struct cff1_cs_opset_t : cs_opset_t +{ + /* PostScript-originated legacy opcodes (OpCode_add etc) are unsupported */ + /* Type 1-originated deprecated opcodes, seac behavior of endchar and dotsection are supported */ + + static void process_op (op_code_t op, cff1_cs_interp_env_t &env, PARAM& param) + { + switch (op) { + case OpCode_dotsection: + SUPER::flush_args_and_op (op, env, param); + break; + + case OpCode_endchar: + OPSET::check_width (op, env, param); + if (env.argStack.get_count () >= 4) + { + OPSET::process_seac (env, param); + } + OPSET::flush_args_and_op (op, env, param); + env.set_endchar (true); + break; + + default: + SUPER::process_op (op, env, param); + } + } + + static void check_width (op_code_t op, cff1_cs_interp_env_t &env, PARAM& param) + { + if (!env.processed_width) + { + bool has_width = false; + switch (op) + { + case OpCode_endchar: + case OpCode_hstem: + case OpCode_hstemhm: + case OpCode_vstem: + case OpCode_vstemhm: + case OpCode_hintmask: + case OpCode_cntrmask: + has_width = ((env.argStack.get_count () & 1) != 0); + break; + case OpCode_hmoveto: + case OpCode_vmoveto: + has_width = (env.argStack.get_count () > 1); + break; + case OpCode_rmoveto: + has_width = (env.argStack.get_count () > 2); + break; + default: + return; + } + env.set_width (has_width); + } + } + + static void process_seac (cff1_cs_interp_env_t &env, PARAM& param) + { + } + + static void flush_args (cff1_cs_interp_env_t &env, PARAM& param) + { + SUPER::flush_args (env, param); + env.clear_args (); /* pop off width */ + } + + private: + typedef cs_opset_t SUPER; +}; + +template +using cff1_cs_interpreter_t = cs_interpreter_t; + +} /* namespace CFF */ + +#endif /* HB_CFF1_INTERP_CS_HH */ diff --git a/libs/harfbuzz/src/hb-cff2-interp-cs.hh b/libs/harfbuzz/src/hb-cff2-interp-cs.hh new file mode 100644 index 00000000..d0b9e7b0 --- /dev/null +++ b/libs/harfbuzz/src/hb-cff2-interp-cs.hh @@ -0,0 +1,275 @@ +/* + * Copyright © 2018 Adobe 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. + * + * Adobe Author(s): Michiharu Ariza + */ +#ifndef HB_CFF2_INTERP_CS_HH +#define HB_CFF2_INTERP_CS_HH + +#include "hb.hh" +#include "hb-cff-interp-cs-common.hh" + +namespace CFF { + +using namespace OT; + +struct blend_arg_t : number_t +{ + void set_int (int v) { reset_blends (); number_t::set_int (v); } + void set_fixed (int32_t v) { reset_blends (); number_t::set_fixed (v); } + void set_real (double v) { reset_blends (); number_t::set_real (v); } + + void set_blends (unsigned int numValues_, unsigned int valueIndex_, + unsigned int numBlends, hb_array_t blends_) + { + numValues = numValues_; + valueIndex = valueIndex_; + deltas.resize (numBlends); + for (unsigned int i = 0; i < numBlends; i++) + deltas[i] = blends_[i]; + } + + bool blending () const { return deltas.length > 0; } + void reset_blends () + { + numValues = valueIndex = 0; + deltas.resize (0); + } + + unsigned int numValues; + unsigned int valueIndex; + hb_vector_t deltas; +}; + +typedef interp_env_t BlendInterpEnv; +typedef biased_subrs_t cff2_biased_subrs_t; + +template +struct cff2_cs_interp_env_t : cs_interp_env_t +{ + template + cff2_cs_interp_env_t (const hb_ubytes_t &str, ACC &acc, unsigned int fd, + const int *coords_=nullptr, unsigned int num_coords_=0) + : SUPER (str, acc.globalSubrs, acc.privateDicts[fd].localSubrs) + { + coords = coords_; + num_coords = num_coords_; + varStore = acc.varStore; + seen_blend = false; + seen_vsindex_ = false; + scalars.init (); + do_blend = num_coords && coords && varStore->size; + set_ivs (acc.privateDicts[fd].ivs); + } + + void fini () + { + scalars.fini (); + SUPER::fini (); + } + + op_code_t fetch_op () + { + if (this->str_ref.avail ()) + return SUPER::fetch_op (); + + /* make up return or endchar op */ + if (this->callStack.is_empty ()) + return OpCode_endchar; + else + return OpCode_return; + } + + const ELEM& eval_arg (unsigned int i) + { + return SUPER::argStack[i]; + } + + const ELEM& pop_arg () + { + return SUPER::argStack.pop (); + } + + void process_blend () + { + if (!seen_blend) + { + region_count = varStore->varStore.get_region_index_count (get_ivs ()); + if (do_blend) + { + if (unlikely (!scalars.resize (region_count))) + SUPER::set_error (); + else + varStore->varStore.get_region_scalars (get_ivs (), coords, num_coords, + &scalars[0], region_count); + } + seen_blend = true; + } + } + + void process_vsindex () + { + unsigned int index = SUPER::argStack.pop_uint (); + if (unlikely (seen_vsindex () || seen_blend)) + { + SUPER::set_error (); + } + else + { + set_ivs (index); + } + seen_vsindex_ = true; + } + + unsigned int get_region_count () const { return region_count; } + void set_region_count (unsigned int region_count_) { region_count = region_count_; } + unsigned int get_ivs () const { return ivs; } + void set_ivs (unsigned int ivs_) { ivs = ivs_; } + bool seen_vsindex () const { return seen_vsindex_; } + + double blend_deltas (hb_array_t deltas) const + { + double v = 0; + if (do_blend) + { + if (likely (scalars.length == deltas.length)) + { + for (unsigned int i = 0; i < scalars.length; i++) + v += (double) scalars[i] * deltas[i].to_real (); + } + } + return v; + } + + protected: + const int *coords; + unsigned int num_coords; + const CFF2VariationStore *varStore; + unsigned int region_count; + unsigned int ivs; + hb_vector_t scalars; + bool do_blend; + bool seen_vsindex_; + bool seen_blend; + + typedef cs_interp_env_t SUPER; +}; +template , PARAM>> +struct cff2_cs_opset_t : cs_opset_t, PARAM, PATH> +{ + static void process_op (op_code_t op, cff2_cs_interp_env_t &env, PARAM& param) + { + switch (op) { + case OpCode_callsubr: + case OpCode_callgsubr: + /* a subroutine number shouldn't be a blended value */ +#if 0 + if (unlikely (env.argStack.peek ().blending ())) + { + env.set_error (); + break; + } +#endif + SUPER::process_op (op, env, param); + break; + + case OpCode_blendcs: + OPSET::process_blend (env, param); + break; + + case OpCode_vsindexcs: +#if 0 + if (unlikely (env.argStack.peek ().blending ())) + { + env.set_error (); + break; + } +#endif + OPSET::process_vsindex (env, param); + break; + + default: + SUPER::process_op (op, env, param); + } + } + + template + static void process_arg_blend (cff2_cs_interp_env_t &env, + ELEM &arg, + const hb_array_t blends, + unsigned n, unsigned i) + { + arg.set_blends (n, i, blends.length, blends); + } + template + static void process_arg_blend (cff2_cs_interp_env_t &env, + ELEM &arg, + const hb_array_t blends, + unsigned n, unsigned i) + { + arg.set_real (arg.to_real () + env.blend_deltas (blends)); + } + + static void process_blend (cff2_cs_interp_env_t &env, PARAM& param) + { + unsigned int n, k; + + env.process_blend (); + k = env.get_region_count (); + n = env.argStack.pop_uint (); + /* copy the blend values into blend array of the default values */ + unsigned int start = env.argStack.get_count () - ((k+1) * n); + /* let an obvious error case fail, but note CFF2 spec doesn't forbid n==0 */ + if (unlikely (start > env.argStack.get_count ())) + { + env.set_error (); + return; + } + for (unsigned int i = 0; i < n; i++) + { + const hb_array_t blends = env.argStack.sub_array (start + n + (i * k), k); + process_arg_blend (env, env.argStack[start + i], blends, n, i); + } + + /* pop off blend values leaving default values now adorned with blend values */ + env.argStack.pop (k * n); + } + + static void process_vsindex (cff2_cs_interp_env_t &env, PARAM& param) + { + env.process_vsindex (); + env.clear_args (); + } + + private: + typedef cs_opset_t, PARAM, PATH> SUPER; +}; + +template +using cff2_cs_interpreter_t = cs_interpreter_t, OPSET, PARAM>; + +} /* namespace CFF */ + +#endif /* HB_CFF2_INTERP_CS_HH */ diff --git a/libs/harfbuzz/src/hb-common.cc b/libs/harfbuzz/src/hb-common.cc new file mode 100644 index 00000000..bbb6cd55 --- /dev/null +++ b/libs/harfbuzz/src/hb-common.cc @@ -0,0 +1,1245 @@ +/* + * Copyright © 2009,2010 Red Hat, Inc. + * Copyright © 2011,2012 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. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#include "hb.hh" +#include "hb-machinery.hh" + +#if !defined(HB_NO_SETLOCALE) && (!defined(HAVE_NEWLOCALE) || !defined(HAVE_USELOCALE)) +#define HB_NO_SETLOCALE 1 +#endif + +#ifndef HB_NO_SETLOCALE + +#include +#ifdef HAVE_XLOCALE_H +#include // Needed on BSD/OS X for uselocale +#endif + +#ifdef WIN32 +#define hb_locale_t _locale_t +#else +#define hb_locale_t locale_t +#endif +#define hb_setlocale setlocale +#define hb_uselocale uselocale + +#else + +#define hb_locale_t void * +#define hb_setlocale(Category, Locale) "C" +#define hb_uselocale(Locale) ((hb_locale_t) 0) + +#endif + +/** + * SECTION:hb-common + * @title: hb-common + * @short_description: Common data types + * @include: hb.h + * + * Common data types used across HarfBuzz are defined here. + **/ + + +/* hb_options_t */ + +hb_atomic_int_t _hb_options; + +void +_hb_options_init () +{ + hb_options_union_t u; + u.i = 0; + u.opts.initialized = true; + + const char *c = getenv ("HB_OPTIONS"); + if (c) + { + while (*c) + { + const char *p = strchr (c, ':'); + if (!p) + p = c + strlen (c); + +#define OPTION(name, symbol) \ + if (0 == strncmp (c, name, p - c) && strlen (name) == static_cast(p - c)) do { u.opts.symbol = true; } while (0) + + OPTION ("uniscribe-bug-compatible", uniscribe_bug_compatible); + +#undef OPTION + + c = *p ? p + 1 : p; + } + + } + + /* This is idempotent and threadsafe. */ + _hb_options = u.i; +} + + +/* hb_tag_t */ + +/** + * hb_tag_from_string: + * @str: (array length=len) (element-type uint8_t): String to convert + * @len: Length of @str, or -1 if it is `NULL`-terminated + * + * Converts a string into an #hb_tag_t. Valid tags + * are four characters. Shorter input strings will be + * padded with spaces. Longer input strings will be + * truncated. + * + * Return value: The #hb_tag_t corresponding to @str + * + * Since: 0.9.2 + **/ +hb_tag_t +hb_tag_from_string (const char *str, int len) +{ + char tag[4]; + unsigned int i; + + if (!str || !len || !*str) + return HB_TAG_NONE; + + if (len < 0 || len > 4) + len = 4; + for (i = 0; i < (unsigned) len && str[i]; i++) + tag[i] = str[i]; + for (; i < 4; i++) + tag[i] = ' '; + + return HB_TAG (tag[0], tag[1], tag[2], tag[3]); +} + +/** + * hb_tag_to_string: + * @tag: #hb_tag_t to convert + * @buf: (out caller-allocates) (array fixed-size=4) (element-type uint8_t): Converted string + * + * Converts an #hb_tag_t to a string and returns it in @buf. + * Strings will be four characters long. + * + * Since: 0.9.5 + **/ +void +hb_tag_to_string (hb_tag_t tag, char *buf) +{ + buf[0] = (char) (uint8_t) (tag >> 24); + buf[1] = (char) (uint8_t) (tag >> 16); + buf[2] = (char) (uint8_t) (tag >> 8); + buf[3] = (char) (uint8_t) (tag >> 0); +} + + +/* hb_direction_t */ + +static const char direction_strings[][4] = { + "ltr", + "rtl", + "ttb", + "btt" +}; + +/** + * hb_direction_from_string: + * @str: (array length=len) (element-type uint8_t): String to convert + * @len: Length of @str, or -1 if it is `NULL`-terminated + * + * Converts a string to an #hb_direction_t. + * + * Matching is loose and applies only to the first letter. For + * examples, "LTR" and "left-to-right" will both return #HB_DIRECTION_LTR. + * + * Unmatched strings will return #HB_DIRECTION_INVALID. + * + * Return value: The #hb_direction_t matching @str + * + * Since: 0.9.2 + **/ +hb_direction_t +hb_direction_from_string (const char *str, int len) +{ + if (unlikely (!str || !len || !*str)) + return HB_DIRECTION_INVALID; + + /* Lets match loosely: just match the first letter, such that + * all of "ltr", "left-to-right", etc work! + */ + char c = TOLOWER (str[0]); + for (unsigned int i = 0; i < ARRAY_LENGTH (direction_strings); i++) + if (c == direction_strings[i][0]) + return (hb_direction_t) (HB_DIRECTION_LTR + i); + + return HB_DIRECTION_INVALID; +} + +/** + * hb_direction_to_string: + * @direction: The #hb_direction_t to convert + * + * Converts an #hb_direction_t to a string. + * + * Return value: (transfer none): The string corresponding to @direction + * + * Since: 0.9.2 + **/ +const char * +hb_direction_to_string (hb_direction_t direction) +{ + if (likely ((unsigned int) (direction - HB_DIRECTION_LTR) + < ARRAY_LENGTH (direction_strings))) + return direction_strings[direction - HB_DIRECTION_LTR]; + + return "invalid"; +} + + +/* hb_language_t */ + +struct hb_language_impl_t { + const char s[1]; +}; + +static const char canon_map[256] = { + 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', '1', '2', '3', '4', '5', '6', '7', '8', '9', 0, 0, 0, 0, 0, 0, + 0, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', + 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, 0, 0, 0, '-', + 0, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', + 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, 0, 0, 0, 0 +}; + +static bool +lang_equal (hb_language_t v1, + const void *v2) +{ + const unsigned char *p1 = (const unsigned char *) v1; + const unsigned char *p2 = (const unsigned char *) v2; + + while (*p1 && *p1 == canon_map[*p2]) { + p1++; + p2++; + } + + return *p1 == canon_map[*p2]; +} + +#if 0 +static unsigned int +lang_hash (const void *key) +{ + const unsigned char *p = key; + unsigned int h = 0; + while (canon_map[*p]) + { + h = (h << 5) - h + canon_map[*p]; + p++; + } + + return h; +} +#endif + + +struct hb_language_item_t { + + struct hb_language_item_t *next; + hb_language_t lang; + + bool operator == (const char *s) const + { return lang_equal (lang, s); } + + hb_language_item_t & operator = (const char *s) + { + /* We can't call strdup(), because we allow custom allocators. */ + size_t len = strlen(s) + 1; + lang = (hb_language_t) hb_malloc(len); + if (likely (lang)) + { + memcpy((unsigned char *) lang, s, len); + for (unsigned char *p = (unsigned char *) lang; *p; p++) + *p = canon_map[*p]; + } + + return *this; + } + + void fini () { hb_free ((void *) lang); } +}; + + +/* Thread-safe lockfree language list */ + +static hb_atomic_ptr_t langs; + +static inline void +free_langs () +{ +retry: + hb_language_item_t *first_lang = langs; + if (unlikely (!langs.cmpexch (first_lang, nullptr))) + goto retry; + + while (first_lang) { + hb_language_item_t *next = first_lang->next; + first_lang->fini (); + hb_free (first_lang); + first_lang = next; + } +} + +static hb_language_item_t * +lang_find_or_insert (const char *key) +{ +retry: + hb_language_item_t *first_lang = langs; + + for (hb_language_item_t *lang = first_lang; lang; lang = lang->next) + if (*lang == key) + return lang; + + /* Not found; allocate one. */ + hb_language_item_t *lang = (hb_language_item_t *) hb_calloc (1, sizeof (hb_language_item_t)); + if (unlikely (!lang)) + return nullptr; + lang->next = first_lang; + *lang = key; + if (unlikely (!lang->lang)) + { + hb_free (lang); + return nullptr; + } + + if (unlikely (!langs.cmpexch (first_lang, lang))) + { + lang->fini (); + hb_free (lang); + goto retry; + } + + if (!first_lang) + hb_atexit (free_langs); /* First person registers atexit() callback. */ + + return lang; +} + + +/** + * hb_language_from_string: + * @str: (array length=len) (element-type uint8_t): a string representing + * a BCP 47 language tag + * @len: length of the @str, or -1 if it is `NULL`-terminated. + * + * Converts @str representing a BCP 47 language tag to the corresponding + * #hb_language_t. + * + * Return value: (transfer none): + * The #hb_language_t corresponding to the BCP 47 language tag. + * + * Since: 0.9.2 + **/ +hb_language_t +hb_language_from_string (const char *str, int len) +{ + if (!str || !len || !*str) + return HB_LANGUAGE_INVALID; + + hb_language_item_t *item = nullptr; + if (len >= 0) + { + /* NUL-terminate it. */ + char strbuf[64]; + len = hb_min (len, (int) sizeof (strbuf) - 1); + memcpy (strbuf, str, len); + strbuf[len] = '\0'; + item = lang_find_or_insert (strbuf); + } + else + item = lang_find_or_insert (str); + + return likely (item) ? item->lang : HB_LANGUAGE_INVALID; +} + +/** + * hb_language_to_string: + * @language: The #hb_language_t to convert + * + * Converts an #hb_language_t to a string. + * + * Return value: (transfer none): + * A `NULL`-terminated string representing the @language. Must not be freed by + * the caller. + * + * Since: 0.9.2 + **/ +const char * +hb_language_to_string (hb_language_t language) +{ + if (unlikely (!language)) return nullptr; + + return language->s; +} + +/** + * hb_language_get_default: + * + * Fetch the default language from current locale. + * + * Note that the first time this function is called, it calls + * "setlocale (LC_CTYPE, nullptr)" to fetch current locale. The underlying + * setlocale function is, in many implementations, NOT threadsafe. To avoid + * problems, call this function once before multiple threads can call it. + * This function is only used from hb_buffer_guess_segment_properties() by + * HarfBuzz itself. + * + * Return value: (transfer none): The default language of the locale as + * an #hb_language_t + * + * Since: 0.9.2 + **/ +hb_language_t +hb_language_get_default () +{ + static hb_atomic_ptr_t default_language; + + hb_language_t language = default_language; + if (unlikely (language == HB_LANGUAGE_INVALID)) + { + language = hb_language_from_string (hb_setlocale (LC_CTYPE, nullptr), -1); + (void) default_language.cmpexch (HB_LANGUAGE_INVALID, language); + } + + return language; +} + +/** + * hb_language_matches: + * @language: The #hb_language_t to work on + * @specific: Another #hb_language_t + * + * Check whether a second language tag is the same or a more + * specific version of the provided language tag. For example, + * "fa_IR.utf8" is a more specific tag for "fa" or for "fa_IR". + * + * Return value: `true` if languages match, `false` otherwise. + * + * Since: 5.0.0 + **/ +hb_bool_t +hb_language_matches (hb_language_t language, + hb_language_t specific) +{ + if (language == specific) return true; + if (!language || !specific) return false; + + const char *l = language->s; + const char *s = specific->s; + unsigned ll = strlen (l); + unsigned sl = strlen (s); + + if (ll > sl) + return false; + + return strncmp (l, s, ll) == 0 && + (s[ll] == '\0' || s[ll] == '-'); +} + + +/* hb_script_t */ + +/** + * hb_script_from_iso15924_tag: + * @tag: an #hb_tag_t representing an ISO 15924 tag. + * + * Converts an ISO 15924 script tag to a corresponding #hb_script_t. + * + * Return value: + * An #hb_script_t corresponding to the ISO 15924 tag. + * + * Since: 0.9.2 + **/ +hb_script_t +hb_script_from_iso15924_tag (hb_tag_t tag) +{ + if (unlikely (tag == HB_TAG_NONE)) + return HB_SCRIPT_INVALID; + + /* Be lenient, adjust case (one capital letter followed by three small letters) */ + tag = (tag & 0xDFDFDFDFu) | 0x00202020u; + + switch (tag) { + + /* These graduated from the 'Q' private-area codes, but + * the old code is still aliased by Unicode, and the Qaai + * one in use by ICU. */ + case HB_TAG('Q','a','a','i'): return HB_SCRIPT_INHERITED; + case HB_TAG('Q','a','a','c'): return HB_SCRIPT_COPTIC; + + /* Script variants from https://unicode.org/iso15924/ */ + case HB_TAG('A','r','a','n'): return HB_SCRIPT_ARABIC; + case HB_TAG('C','y','r','s'): return HB_SCRIPT_CYRILLIC; + case HB_TAG('G','e','o','k'): return HB_SCRIPT_GEORGIAN; + case HB_TAG('H','a','n','s'): return HB_SCRIPT_HAN; + case HB_TAG('H','a','n','t'): return HB_SCRIPT_HAN; + case HB_TAG('J','a','m','o'): return HB_SCRIPT_HANGUL; + case HB_TAG('L','a','t','f'): return HB_SCRIPT_LATIN; + case HB_TAG('L','a','t','g'): return HB_SCRIPT_LATIN; + case HB_TAG('S','y','r','e'): return HB_SCRIPT_SYRIAC; + case HB_TAG('S','y','r','j'): return HB_SCRIPT_SYRIAC; + case HB_TAG('S','y','r','n'): return HB_SCRIPT_SYRIAC; + } + + /* If it looks right, just use the tag as a script */ + if (((uint32_t) tag & 0xE0E0E0E0u) == 0x40606060u) + return (hb_script_t) tag; + + /* Otherwise, return unknown */ + return HB_SCRIPT_UNKNOWN; +} + +/** + * hb_script_from_string: + * @str: (array length=len) (element-type uint8_t): a string representing an + * ISO 15924 tag. + * @len: length of the @str, or -1 if it is `NULL`-terminated. + * + * Converts a string @str representing an ISO 15924 script tag to a + * corresponding #hb_script_t. Shorthand for hb_tag_from_string() then + * hb_script_from_iso15924_tag(). + * + * Return value: + * An #hb_script_t corresponding to the ISO 15924 tag. + * + * Since: 0.9.2 + **/ +hb_script_t +hb_script_from_string (const char *str, int len) +{ + return hb_script_from_iso15924_tag (hb_tag_from_string (str, len)); +} + +/** + * hb_script_to_iso15924_tag: + * @script: an #hb_script_t to convert. + * + * Converts an #hb_script_t to a corresponding ISO 15924 script tag. + * + * Return value: + * An #hb_tag_t representing an ISO 15924 script tag. + * + * Since: 0.9.2 + **/ +hb_tag_t +hb_script_to_iso15924_tag (hb_script_t script) +{ + return (hb_tag_t) script; +} + +/** + * hb_script_get_horizontal_direction: + * @script: The #hb_script_t to query + * + * Fetches the #hb_direction_t of a script when it is + * set horizontally. All right-to-left scripts will return + * #HB_DIRECTION_RTL. All left-to-right scripts will return + * #HB_DIRECTION_LTR. Scripts that can be written either + * horizontally or vertically will return #HB_DIRECTION_INVALID. + * Unknown scripts will return #HB_DIRECTION_LTR. + * + * Return value: The horizontal #hb_direction_t of @script + * + * Since: 0.9.2 + **/ +hb_direction_t +hb_script_get_horizontal_direction (hb_script_t script) +{ + /* https://docs.google.com/spreadsheets/d/1Y90M0Ie3MUJ6UVCRDOypOtijlMDLNNyyLk36T6iMu0o */ + switch ((hb_tag_t) script) + { + /* Unicode-1.1 additions */ + case HB_SCRIPT_ARABIC: + case HB_SCRIPT_HEBREW: + + /* Unicode-3.0 additions */ + case HB_SCRIPT_SYRIAC: + case HB_SCRIPT_THAANA: + + /* Unicode-4.0 additions */ + case HB_SCRIPT_CYPRIOT: + + /* Unicode-4.1 additions */ + case HB_SCRIPT_KHAROSHTHI: + + /* Unicode-5.0 additions */ + case HB_SCRIPT_PHOENICIAN: + case HB_SCRIPT_NKO: + + /* Unicode-5.1 additions */ + case HB_SCRIPT_LYDIAN: + + /* Unicode-5.2 additions */ + case HB_SCRIPT_AVESTAN: + case HB_SCRIPT_IMPERIAL_ARAMAIC: + case HB_SCRIPT_INSCRIPTIONAL_PAHLAVI: + case HB_SCRIPT_INSCRIPTIONAL_PARTHIAN: + case HB_SCRIPT_OLD_SOUTH_ARABIAN: + case HB_SCRIPT_OLD_TURKIC: + case HB_SCRIPT_SAMARITAN: + + /* Unicode-6.0 additions */ + case HB_SCRIPT_MANDAIC: + + /* Unicode-6.1 additions */ + case HB_SCRIPT_MEROITIC_CURSIVE: + case HB_SCRIPT_MEROITIC_HIEROGLYPHS: + + /* Unicode-7.0 additions */ + case HB_SCRIPT_MANICHAEAN: + case HB_SCRIPT_MENDE_KIKAKUI: + case HB_SCRIPT_NABATAEAN: + case HB_SCRIPT_OLD_NORTH_ARABIAN: + case HB_SCRIPT_PALMYRENE: + case HB_SCRIPT_PSALTER_PAHLAVI: + + /* Unicode-8.0 additions */ + case HB_SCRIPT_HATRAN: + + /* Unicode-9.0 additions */ + case HB_SCRIPT_ADLAM: + + /* Unicode-11.0 additions */ + case HB_SCRIPT_HANIFI_ROHINGYA: + case HB_SCRIPT_OLD_SOGDIAN: + case HB_SCRIPT_SOGDIAN: + + /* Unicode-12.0 additions */ + case HB_SCRIPT_ELYMAIC: + + /* Unicode-13.0 additions */ + case HB_SCRIPT_CHORASMIAN: + case HB_SCRIPT_YEZIDI: + + /* Unicode-14.0 additions */ + case HB_SCRIPT_OLD_UYGHUR: + + return HB_DIRECTION_RTL; + + + /* https://github.com/harfbuzz/harfbuzz/issues/1000 */ + case HB_SCRIPT_OLD_HUNGARIAN: + case HB_SCRIPT_OLD_ITALIC: + case HB_SCRIPT_RUNIC: + + return HB_DIRECTION_INVALID; + } + + return HB_DIRECTION_LTR; +} + + +/* hb_version */ + + +/** + * SECTION:hb-version + * @title: hb-version + * @short_description: Information about the version of HarfBuzz in use + * @include: hb.h + * + * These functions and macros allow accessing version of the HarfBuzz + * library used at compile- as well as run-time, and to direct code + * conditionally based on those versions, again, at compile- or run-time. + **/ + + +/** + * hb_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.9.2 + **/ +void +hb_version (unsigned int *major, + unsigned int *minor, + unsigned int *micro) +{ + *major = HB_VERSION_MAJOR; + *minor = HB_VERSION_MINOR; + *micro = HB_VERSION_MICRO; +} + +/** + * hb_version_string: + * + * Returns library version as a string with three components. + * + * Return value: Library version string + * + * Since: 0.9.2 + **/ +const char * +hb_version_string () +{ + return HB_VERSION_STRING; +} + +/** + * hb_version_atleast: + * @major: Library major version component + * @minor: Library minor version component + * @micro: Library micro version component + * + * Tests the library version against a minimum value, + * as three integer components. + * + * Return value: `true` if the library is equal to or greater than + * the test value, `false` otherwise + * + * Since: 0.9.30 + **/ +hb_bool_t +hb_version_atleast (unsigned int major, + unsigned int minor, + unsigned int micro) +{ + return HB_VERSION_ATLEAST (major, minor, micro); +} + + + +/* hb_feature_t and hb_variation_t */ + +static bool +parse_space (const char **pp, const char *end) +{ + while (*pp < end && ISSPACE (**pp)) + (*pp)++; + return true; +} + +static bool +parse_char (const char **pp, const char *end, char c) +{ + parse_space (pp, end); + + if (*pp == end || **pp != c) + return false; + + (*pp)++; + return true; +} + +static bool +parse_uint (const char **pp, const char *end, unsigned int *pv) +{ + /* Intentionally use hb_parse_int inside instead of hb_parse_uint, + * such that -1 turns into "big number"... */ + int v; + if (unlikely (!hb_parse_int (pp, end, &v))) return false; + + *pv = v; + return true; +} + +static bool +parse_uint32 (const char **pp, const char *end, uint32_t *pv) +{ + /* Intentionally use hb_parse_int inside instead of hb_parse_uint, + * such that -1 turns into "big number"... */ + int v; + if (unlikely (!hb_parse_int (pp, end, &v))) return false; + + *pv = v; + return true; +} + +static bool +parse_bool (const char **pp, const char *end, uint32_t *pv) +{ + parse_space (pp, end); + + const char *p = *pp; + while (*pp < end && ISALPHA(**pp)) + (*pp)++; + + /* CSS allows on/off as aliases 1/0. */ + if (*pp - p == 2 + && TOLOWER (p[0]) == 'o' + && TOLOWER (p[1]) == 'n') + *pv = 1; + else if (*pp - p == 3 + && TOLOWER (p[0]) == 'o' + && TOLOWER (p[1]) == 'f' + && TOLOWER (p[2]) == 'f') + *pv = 0; + else + return false; + + return true; +} + +/* hb_feature_t */ + +static bool +parse_feature_value_prefix (const char **pp, const char *end, hb_feature_t *feature) +{ + if (parse_char (pp, end, '-')) + feature->value = 0; + else { + parse_char (pp, end, '+'); + feature->value = 1; + } + + return true; +} + +static bool +parse_tag (const char **pp, const char *end, hb_tag_t *tag) +{ + parse_space (pp, end); + + char quote = 0; + + if (*pp < end && (**pp == '\'' || **pp == '"')) + { + quote = **pp; + (*pp)++; + } + + const char *p = *pp; + while (*pp < end && (ISALNUM(**pp) || **pp == '_')) + (*pp)++; + + if (p == *pp || *pp - p > 4) + return false; + + *tag = hb_tag_from_string (p, *pp - p); + + if (quote) + { + /* CSS expects exactly four bytes. And we only allow quotations for + * CSS compatibility. So, enforce the length. */ + if (*pp - p != 4) + return false; + if (*pp == end || **pp != quote) + return false; + (*pp)++; + } + + return true; +} + +static bool +parse_feature_indices (const char **pp, const char *end, hb_feature_t *feature) +{ + parse_space (pp, end); + + bool has_start; + + feature->start = HB_FEATURE_GLOBAL_START; + feature->end = HB_FEATURE_GLOBAL_END; + + if (!parse_char (pp, end, '[')) + return true; + + has_start = parse_uint (pp, end, &feature->start); + + if (parse_char (pp, end, ':') || parse_char (pp, end, ';')) { + parse_uint (pp, end, &feature->end); + } else { + if (has_start) + feature->end = feature->start + 1; + } + + return parse_char (pp, end, ']'); +} + +static bool +parse_feature_value_postfix (const char **pp, const char *end, hb_feature_t *feature) +{ + bool had_equal = parse_char (pp, end, '='); + bool had_value = parse_uint32 (pp, end, &feature->value) || + parse_bool (pp, end, &feature->value); + /* CSS doesn't use equal-sign between tag and value. + * If there was an equal-sign, then there *must* be a value. + * A value without an equal-sign is ok, but not required. */ + return !had_equal || had_value; +} + +static bool +parse_one_feature (const char **pp, const char *end, hb_feature_t *feature) +{ + return parse_feature_value_prefix (pp, end, feature) && + parse_tag (pp, end, &feature->tag) && + parse_feature_indices (pp, end, feature) && + parse_feature_value_postfix (pp, end, feature) && + parse_space (pp, end) && + *pp == end; +} + +/** + * hb_feature_from_string: + * @str: (array length=len) (element-type uint8_t): a string to parse + * @len: length of @str, or -1 if string is `NULL` terminated + * @feature: (out): the #hb_feature_t to initialize with the parsed values + * + * Parses a string into a #hb_feature_t. + * + * The format for specifying feature strings follows. All valid CSS + * font-feature-settings values other than 'normal' and the global values are + * also accepted, though not documented below. CSS string escapes are not + * supported. + * + * The range indices refer to the positions between Unicode characters. The + * position before the first character is always 0. + * + * The format is Python-esque. Here is how it all works: + * + * + * + * + * Syntax Value Start End + * + * + * Setting value: + * kern 1 0 Turn feature on + * +kern 1 0 Turn feature on + * -kern 0 0 Turn feature off + * kern=0 0 0 Turn feature off + * kern=1 1 0 Turn feature on + * aalt=2 2 0 Choose 2nd alternate + * Setting index: + * kern[] 1 0 Turn feature on + * kern[:] 1 0 Turn feature on + * kern[5:] 1 5 Turn feature on, partial + * kern[:5] 1 0 5 Turn feature on, partial + * kern[3:5] 1 3 5 Turn feature on, range + * kern[3] 1 3 3+1 Turn feature on, single char + * Mixing it all: + * aalt[3:5]=2 2 3 5 Turn 2nd alternate on for range + * + * + * + * + * Return value: + * `true` if @str is successfully parsed, `false` otherwise + * + * Since: 0.9.5 + **/ +hb_bool_t +hb_feature_from_string (const char *str, int len, + hb_feature_t *feature) +{ + hb_feature_t feat; + + if (len < 0) + len = strlen (str); + + if (likely (parse_one_feature (&str, str + len, &feat))) + { + if (feature) + *feature = feat; + return true; + } + + if (feature) + memset (feature, 0, sizeof (*feature)); + return false; +} + +/** + * hb_feature_to_string: + * @feature: an #hb_feature_t to convert + * @buf: (array length=size) (out): output string + * @size: the allocated size of @buf + * + * Converts a #hb_feature_t into a `NULL`-terminated string in the format + * understood by hb_feature_from_string(). The client in responsible for + * allocating big enough size for @buf, 128 bytes is more than enough. + * + * Since: 0.9.5 + **/ +void +hb_feature_to_string (hb_feature_t *feature, + char *buf, unsigned int size) +{ + if (unlikely (!size)) return; + + char s[128]; + unsigned int len = 0; + if (feature->value == 0) + s[len++] = '-'; + hb_tag_to_string (feature->tag, s + len); + len += 4; + while (len && s[len - 1] == ' ') + len--; + if (feature->start != HB_FEATURE_GLOBAL_START || feature->end != HB_FEATURE_GLOBAL_END) + { + s[len++] = '['; + if (feature->start) + len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->start)); + if (feature->end != feature->start + 1) { + s[len++] = ':'; + if (feature->end != HB_FEATURE_GLOBAL_END) + len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->end)); + } + s[len++] = ']'; + } + if (feature->value > 1) + { + s[len++] = '='; + len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->value)); + } + assert (len < ARRAY_LENGTH (s)); + len = hb_min (len, size - 1); + memcpy (buf, s, len); + buf[len] = '\0'; +} + +/* hb_variation_t */ + +static bool +parse_variation_value (const char **pp, const char *end, hb_variation_t *variation) +{ + parse_char (pp, end, '='); /* Optional. */ + double v; + if (unlikely (!hb_parse_double (pp, end, &v))) return false; + + variation->value = v; + return true; +} + +static bool +parse_one_variation (const char **pp, const char *end, hb_variation_t *variation) +{ + return parse_tag (pp, end, &variation->tag) && + parse_variation_value (pp, end, variation) && + parse_space (pp, end) && + *pp == end; +} + +/** + * hb_variation_from_string: + * @str: (array length=len) (element-type uint8_t): a string to parse + * @len: length of @str, or -1 if string is `NULL` terminated + * @variation: (out): the #hb_variation_t to initialize with the parsed values + * + * Parses a string into a #hb_variation_t. + * + * The format for specifying variation settings follows. All valid CSS + * font-variation-settings values other than 'normal' and 'inherited' are also + * accepted, though, not documented below. + * + * The format is a tag, optionally followed by an equals sign, followed by a + * number. For example `wght=500`, or `slnt=-7.5`. + * + * Return value: + * `true` if @str is successfully parsed, `false` otherwise + * + * Since: 1.4.2 + */ +hb_bool_t +hb_variation_from_string (const char *str, int len, + hb_variation_t *variation) +{ + hb_variation_t var; + + if (len < 0) + len = strlen (str); + + if (likely (parse_one_variation (&str, str + len, &var))) + { + if (variation) + *variation = var; + return true; + } + + if (variation) + memset (variation, 0, sizeof (*variation)); + return false; +} + +#ifndef HB_NO_SETLOCALE + +static inline void free_static_C_locale (); + +static struct hb_C_locale_lazy_loader_t : hb_lazy_loader_t, + hb_C_locale_lazy_loader_t> +{ + static hb_locale_t create () + { + hb_locale_t l = newlocale (LC_ALL_MASK, "C", NULL); + if (!l) + return l; + + hb_atexit (free_static_C_locale); + + return l; + } + static void destroy (hb_locale_t l) + { + freelocale (l); + } + static hb_locale_t get_null () + { + return (hb_locale_t) 0; + } +} static_C_locale; + +static inline +void free_static_C_locale () +{ + static_C_locale.free_instance (); +} + +static hb_locale_t +get_C_locale () +{ + return static_C_locale.get_unconst (); +} + +#endif + +/** + * hb_variation_to_string: + * @variation: an #hb_variation_t to convert + * @buf: (array length=size) (out): output string + * @size: the allocated size of @buf + * + * Converts an #hb_variation_t into a `NULL`-terminated string in the format + * understood by hb_variation_from_string(). The client in responsible for + * allocating big enough size for @buf, 128 bytes is more than enough. + * + * Since: 1.4.2 + */ +void +hb_variation_to_string (hb_variation_t *variation, + char *buf, unsigned int size) +{ + if (unlikely (!size)) return; + + char s[128]; + unsigned int len = 0; + hb_tag_to_string (variation->tag, s + len); + len += 4; + while (len && s[len - 1] == ' ') + len--; + s[len++] = '='; + + hb_locale_t oldlocale HB_UNUSED; + oldlocale = hb_uselocale (get_C_locale ()); + len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%g", (double) variation->value)); + (void) hb_uselocale (oldlocale); + + assert (len < ARRAY_LENGTH (s)); + len = hb_min (len, size - 1); + memcpy (buf, s, len); + buf[len] = '\0'; +} + +/** + * hb_color_get_alpha: + * @color: an #hb_color_t we are interested in its channels. + * + * Fetches the alpha channel of the given @color. + * + * Return value: Alpha channel value + * + * Since: 2.1.0 + */ +uint8_t +(hb_color_get_alpha) (hb_color_t color) +{ + return hb_color_get_alpha (color); +} + +/** + * hb_color_get_red: + * @color: an #hb_color_t we are interested in its channels. + * + * Fetches the red channel of the given @color. + * + * Return value: Red channel value + * + * Since: 2.1.0 + */ +uint8_t +(hb_color_get_red) (hb_color_t color) +{ + return hb_color_get_red (color); +} + +/** + * hb_color_get_green: + * @color: an #hb_color_t we are interested in its channels. + * + * Fetches the green channel of the given @color. + * + * Return value: Green channel value + * + * Since: 2.1.0 + */ +uint8_t +(hb_color_get_green) (hb_color_t color) +{ + return hb_color_get_green (color); +} + +/** + * hb_color_get_blue: + * @color: an #hb_color_t we are interested in its channels. + * + * Fetches the blue channel of the given @color. + * + * Return value: Blue channel value + * + * Since: 2.1.0 + */ +uint8_t +(hb_color_get_blue) (hb_color_t color) +{ + return hb_color_get_blue (color); +} + + +/* If there is no visibility control, then hb-static.cc will NOT + * define anything. Instead, we get it to define one set in here + * only, so only libharfbuzz.so defines them, not other libs. */ +#ifdef HB_NO_VISIBILITY +#undef HB_NO_VISIBILITY +#include "hb-static.cc" +#define HB_NO_VISIBILITY 1 +#endif diff --git a/libs/harfbuzz/src/hb-common.h b/libs/harfbuzz/src/hb-common.h new file mode 100644 index 00000000..e92feb98 --- /dev/null +++ b/libs/harfbuzz/src/hb-common.h @@ -0,0 +1,902 @@ +/* + * Copyright © 2007,2008,2009 Red Hat, Inc. + * Copyright © 2011,2012 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. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#if !defined(HB_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR) +#error "Include instead." +#endif + +#ifndef HB_COMMON_H +#define HB_COMMON_H + +#ifndef HB_EXTERN +#define HB_EXTERN extern +#endif + +#ifndef HB_BEGIN_DECLS +# ifdef __cplusplus +# define HB_BEGIN_DECLS extern "C" { +# define HB_END_DECLS } +# else /* !__cplusplus */ +# define HB_BEGIN_DECLS +# define HB_END_DECLS +# endif /* !__cplusplus */ +#endif + +#if defined (_SVR4) || defined (SVR4) || defined (__OpenBSD__) || \ + defined (_sgi) || defined (__sun) || defined (sun) || \ + defined (__digital__) || defined (__HP_cc) +# include +#elif defined (_AIX) +# include +#elif defined (_MSC_VER) && _MSC_VER < 1600 +/* VS 2010 (_MSC_VER 1600) has stdint.h */ +typedef __int8 int8_t; +typedef unsigned __int8 uint8_t; +typedef __int16 int16_t; +typedef unsigned __int16 uint16_t; +typedef __int32 int32_t; +typedef unsigned __int32 uint32_t; +typedef __int64 int64_t; +typedef unsigned __int64 uint64_t; +#elif defined (__KERNEL__) +# include +#else +# include +#endif + +#if defined(__GNUC__) && ((__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)) +#define HB_DEPRECATED __attribute__((__deprecated__)) +#elif defined(_MSC_VER) && (_MSC_VER >= 1300) +#define HB_DEPRECATED __declspec(deprecated) +#else +#define HB_DEPRECATED +#endif + +#if defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)) +#define HB_DEPRECATED_FOR(f) __attribute__((__deprecated__("Use '" #f "' instead"))) +#elif defined(_MSC_FULL_VER) && (_MSC_FULL_VER > 140050320) +#define HB_DEPRECATED_FOR(f) __declspec(deprecated("is deprecated. Use '" #f "' instead")) +#else +#define HB_DEPRECATED_FOR(f) HB_DEPRECATED +#endif + + +HB_BEGIN_DECLS + +/** + * hb_bool_t: + * + * Data type for booleans. + * + **/ +typedef int hb_bool_t; + +/** + * hb_codepoint_t: + * + * Data type for holding Unicode codepoints. Also + * used to hold glyph IDs. + * + **/ +typedef uint32_t hb_codepoint_t; +/** + * hb_position_t: + * + * Data type for holding a single coordinate value. + * Contour points and other multi-dimensional data are + * stored as tuples of #hb_position_t's. + * + **/ +typedef int32_t hb_position_t; +/** + * hb_mask_t: + * + * Data type for bitmasks. + * + **/ +typedef uint32_t hb_mask_t; + +typedef union _hb_var_int_t { + uint32_t u32; + int32_t i32; + uint16_t u16[2]; + int16_t i16[2]; + uint8_t u8[4]; + int8_t i8[4]; +} hb_var_int_t; + +typedef union _hb_var_num_t { + float f; + uint32_t u32; + int32_t i32; + uint16_t u16[2]; + int16_t i16[2]; + uint8_t u8[4]; + int8_t i8[4]; +} hb_var_num_t; + + +/* hb_tag_t */ + +/** + * hb_tag_t: + * + * Data type for tag identifiers. Tags are four + * byte integers, each byte representing a character. + * + * Tags are used to identify tables, design-variation axes, + * scripts, languages, font features, and baselines with + * human-readable names. + * + **/ +typedef uint32_t hb_tag_t; + +/** + * HB_TAG: + * @c1: 1st character of the tag + * @c2: 2nd character of the tag + * @c3: 3rd character of the tag + * @c4: 4th character of the tag + * + * Constructs an #hb_tag_t from four character literals. + * + **/ +#define HB_TAG(c1,c2,c3,c4) ((hb_tag_t)((((uint32_t)(c1)&0xFF)<<24)|(((uint32_t)(c2)&0xFF)<<16)|(((uint32_t)(c3)&0xFF)<<8)|((uint32_t)(c4)&0xFF))) + +/** + * HB_UNTAG: + * @tag: an #hb_tag_t + * + * Extracts four character literals from an #hb_tag_t. + * + * Since: 0.6.0 + * + **/ +#define HB_UNTAG(tag) (uint8_t)(((tag)>>24)&0xFF), (uint8_t)(((tag)>>16)&0xFF), (uint8_t)(((tag)>>8)&0xFF), (uint8_t)((tag)&0xFF) + +/** + * HB_TAG_NONE: + * + * Unset #hb_tag_t. + */ +#define HB_TAG_NONE HB_TAG(0,0,0,0) +/** + * HB_TAG_MAX: + * + * Maximum possible unsigned #hb_tag_t. + * + * Since: 0.9.26 + */ +#define HB_TAG_MAX HB_TAG(0xff,0xff,0xff,0xff) +/** + * HB_TAG_MAX_SIGNED: + * + * Maximum possible signed #hb_tag_t. + * + * Since: 0.9.33 + */ +#define HB_TAG_MAX_SIGNED HB_TAG(0x7f,0xff,0xff,0xff) + +/* len=-1 means str is NUL-terminated. */ +HB_EXTERN hb_tag_t +hb_tag_from_string (const char *str, int len); + +/* buf should have 4 bytes. */ +HB_EXTERN void +hb_tag_to_string (hb_tag_t tag, char *buf); + + +/** + * hb_direction_t: + * @HB_DIRECTION_INVALID: Initial, unset direction. + * @HB_DIRECTION_LTR: Text is set horizontally from left to right. + * @HB_DIRECTION_RTL: Text is set horizontally from right to left. + * @HB_DIRECTION_TTB: Text is set vertically from top to bottom. + * @HB_DIRECTION_BTT: Text is set vertically from bottom to top. + * + * The direction of a text segment or buffer. + * + * A segment can also be tested for horizontal or vertical + * orientation (irrespective of specific direction) with + * HB_DIRECTION_IS_HORIZONTAL() or HB_DIRECTION_IS_VERTICAL(). + * + */ +typedef enum { + HB_DIRECTION_INVALID = 0, + HB_DIRECTION_LTR = 4, + HB_DIRECTION_RTL, + HB_DIRECTION_TTB, + HB_DIRECTION_BTT +} hb_direction_t; + +/* len=-1 means str is NUL-terminated */ +HB_EXTERN hb_direction_t +hb_direction_from_string (const char *str, int len); + +HB_EXTERN const char * +hb_direction_to_string (hb_direction_t direction); + +/** + * HB_DIRECTION_IS_VALID: + * @dir: #hb_direction_t to test + * + * Tests whether a text direction is valid. + * + **/ +#define HB_DIRECTION_IS_VALID(dir) ((((unsigned int) (dir)) & ~3U) == 4) +/* Direction must be valid for the following */ +/** + * HB_DIRECTION_IS_HORIZONTAL: + * @dir: #hb_direction_t to test + * + * Tests whether a text direction is horizontal. Requires + * that the direction be valid. + * + **/ +#define HB_DIRECTION_IS_HORIZONTAL(dir) ((((unsigned int) (dir)) & ~1U) == 4) +/** + * HB_DIRECTION_IS_VERTICAL: + * @dir: #hb_direction_t to test + * + * Tests whether a text direction is vertical. Requires + * that the direction be valid. + * + **/ +#define HB_DIRECTION_IS_VERTICAL(dir) ((((unsigned int) (dir)) & ~1U) == 6) +/** + * HB_DIRECTION_IS_FORWARD: + * @dir: #hb_direction_t to test + * + * Tests whether a text direction moves forward (from left to right, or from + * top to bottom). Requires that the direction be valid. + * + **/ +#define HB_DIRECTION_IS_FORWARD(dir) ((((unsigned int) (dir)) & ~2U) == 4) +/** + * HB_DIRECTION_IS_BACKWARD: + * @dir: #hb_direction_t to test + * + * Tests whether a text direction moves backward (from right to left, or from + * bottom to top). Requires that the direction be valid. + * + **/ +#define HB_DIRECTION_IS_BACKWARD(dir) ((((unsigned int) (dir)) & ~2U) == 5) +/** + * HB_DIRECTION_REVERSE: + * @dir: #hb_direction_t to reverse + * + * Reverses a text direction. Requires that the direction + * be valid. + * + **/ +#define HB_DIRECTION_REVERSE(dir) ((hb_direction_t) (((unsigned int) (dir)) ^ 1)) + + +/* hb_language_t */ + +/** + * hb_language_t: + * + * Data type for languages. Each #hb_language_t corresponds to a BCP 47 + * language tag. + * + */ +typedef const struct hb_language_impl_t *hb_language_t; + +HB_EXTERN hb_language_t +hb_language_from_string (const char *str, int len); + +HB_EXTERN const char * +hb_language_to_string (hb_language_t language); + +/** + * HB_LANGUAGE_INVALID: + * + * An unset #hb_language_t. + * + * Since: 0.6.0 + */ +#define HB_LANGUAGE_INVALID ((hb_language_t) 0) + +HB_EXTERN hb_language_t +hb_language_get_default (void); + +HB_EXTERN hb_bool_t +hb_language_matches (hb_language_t language, + hb_language_t specific); + +/** + * hb_script_t: + * @HB_SCRIPT_COMMON: `Zyyy` + * @HB_SCRIPT_INHERITED: `Zinh` + * @HB_SCRIPT_UNKNOWN: `Zzzz` + * @HB_SCRIPT_ARABIC: `Arab` + * @HB_SCRIPT_ARMENIAN: `Armn` + * @HB_SCRIPT_BENGALI: `Beng` + * @HB_SCRIPT_CYRILLIC: `Cyrl` + * @HB_SCRIPT_DEVANAGARI: `Deva` + * @HB_SCRIPT_GEORGIAN: `Geor` + * @HB_SCRIPT_GREEK: `Grek` + * @HB_SCRIPT_GUJARATI: `Gujr` + * @HB_SCRIPT_GURMUKHI: `Guru` + * @HB_SCRIPT_HANGUL: `Hang` + * @HB_SCRIPT_HAN: `Hani` + * @HB_SCRIPT_HEBREW: `Hebr` + * @HB_SCRIPT_HIRAGANA: `Hira` + * @HB_SCRIPT_KANNADA: `Knda` + * @HB_SCRIPT_KATAKANA: `Kana` + * @HB_SCRIPT_LAO: `Laoo` + * @HB_SCRIPT_LATIN: `Latn` + * @HB_SCRIPT_MALAYALAM: `Mlym` + * @HB_SCRIPT_ORIYA: `Orya` + * @HB_SCRIPT_TAMIL: `Taml` + * @HB_SCRIPT_TELUGU: `Telu` + * @HB_SCRIPT_THAI: `Thai` + * @HB_SCRIPT_TIBETAN: `Tibt` + * @HB_SCRIPT_BOPOMOFO: `Bopo` + * @HB_SCRIPT_BRAILLE: `Brai` + * @HB_SCRIPT_CANADIAN_SYLLABICS: `Cans` + * @HB_SCRIPT_CHEROKEE: `Cher` + * @HB_SCRIPT_ETHIOPIC: `Ethi` + * @HB_SCRIPT_KHMER: `Khmr` + * @HB_SCRIPT_MONGOLIAN: `Mong` + * @HB_SCRIPT_MYANMAR: `Mymr` + * @HB_SCRIPT_OGHAM: `Ogam` + * @HB_SCRIPT_RUNIC: `Runr` + * @HB_SCRIPT_SINHALA: `Sinh` + * @HB_SCRIPT_SYRIAC: `Syrc` + * @HB_SCRIPT_THAANA: `Thaa` + * @HB_SCRIPT_YI: `Yiii` + * @HB_SCRIPT_DESERET: `Dsrt` + * @HB_SCRIPT_GOTHIC: `Goth` + * @HB_SCRIPT_OLD_ITALIC: `Ital` + * @HB_SCRIPT_BUHID: `Buhd` + * @HB_SCRIPT_HANUNOO: `Hano` + * @HB_SCRIPT_TAGALOG: `Tglg` + * @HB_SCRIPT_TAGBANWA: `Tagb` + * @HB_SCRIPT_CYPRIOT: `Cprt` + * @HB_SCRIPT_LIMBU: `Limb` + * @HB_SCRIPT_LINEAR_B: `Linb` + * @HB_SCRIPT_OSMANYA: `Osma` + * @HB_SCRIPT_SHAVIAN: `Shaw` + * @HB_SCRIPT_TAI_LE: `Tale` + * @HB_SCRIPT_UGARITIC: `Ugar` + * @HB_SCRIPT_BUGINESE: `Bugi` + * @HB_SCRIPT_COPTIC: `Copt` + * @HB_SCRIPT_GLAGOLITIC: `Glag` + * @HB_SCRIPT_KHAROSHTHI: `Khar` + * @HB_SCRIPT_NEW_TAI_LUE: `Talu` + * @HB_SCRIPT_OLD_PERSIAN: `Xpeo` + * @HB_SCRIPT_SYLOTI_NAGRI: `Sylo` + * @HB_SCRIPT_TIFINAGH: `Tfng` + * @HB_SCRIPT_BALINESE: `Bali` + * @HB_SCRIPT_CUNEIFORM: `Xsux` + * @HB_SCRIPT_NKO: `Nkoo` + * @HB_SCRIPT_PHAGS_PA: `Phag` + * @HB_SCRIPT_PHOENICIAN: `Phnx` + * @HB_SCRIPT_CARIAN: `Cari` + * @HB_SCRIPT_CHAM: `Cham` + * @HB_SCRIPT_KAYAH_LI: `Kali` + * @HB_SCRIPT_LEPCHA: `Lepc` + * @HB_SCRIPT_LYCIAN: `Lyci` + * @HB_SCRIPT_LYDIAN: `Lydi` + * @HB_SCRIPT_OL_CHIKI: `Olck` + * @HB_SCRIPT_REJANG: `Rjng` + * @HB_SCRIPT_SAURASHTRA: `Saur` + * @HB_SCRIPT_SUNDANESE: `Sund` + * @HB_SCRIPT_VAI: `Vaii` + * @HB_SCRIPT_AVESTAN: `Avst` + * @HB_SCRIPT_BAMUM: `Bamu` + * @HB_SCRIPT_EGYPTIAN_HIEROGLYPHS: `Egyp` + * @HB_SCRIPT_IMPERIAL_ARAMAIC: `Armi` + * @HB_SCRIPT_INSCRIPTIONAL_PAHLAVI: `Phli` + * @HB_SCRIPT_INSCRIPTIONAL_PARTHIAN: `Prti` + * @HB_SCRIPT_JAVANESE: `Java` + * @HB_SCRIPT_KAITHI: `Kthi` + * @HB_SCRIPT_LISU: `Lisu` + * @HB_SCRIPT_MEETEI_MAYEK: `Mtei` + * @HB_SCRIPT_OLD_SOUTH_ARABIAN: `Sarb` + * @HB_SCRIPT_OLD_TURKIC: `Orkh` + * @HB_SCRIPT_SAMARITAN: `Samr` + * @HB_SCRIPT_TAI_THAM: `Lana` + * @HB_SCRIPT_TAI_VIET: `Tavt` + * @HB_SCRIPT_BATAK: `Batk` + * @HB_SCRIPT_BRAHMI: `Brah` + * @HB_SCRIPT_MANDAIC: `Mand` + * @HB_SCRIPT_CHAKMA: `Cakm` + * @HB_SCRIPT_MEROITIC_CURSIVE: `Merc` + * @HB_SCRIPT_MEROITIC_HIEROGLYPHS: `Mero` + * @HB_SCRIPT_MIAO: `Plrd` + * @HB_SCRIPT_SHARADA: `Shrd` + * @HB_SCRIPT_SORA_SOMPENG: `Sora` + * @HB_SCRIPT_TAKRI: `Takr` + * @HB_SCRIPT_BASSA_VAH: `Bass`, Since: 0.9.30 + * @HB_SCRIPT_CAUCASIAN_ALBANIAN: `Aghb`, Since: 0.9.30 + * @HB_SCRIPT_DUPLOYAN: `Dupl`, Since: 0.9.30 + * @HB_SCRIPT_ELBASAN: `Elba`, Since: 0.9.30 + * @HB_SCRIPT_GRANTHA: `Gran`, Since: 0.9.30 + * @HB_SCRIPT_KHOJKI: `Khoj`, Since: 0.9.30 + * @HB_SCRIPT_KHUDAWADI: `Sind`, Since: 0.9.30 + * @HB_SCRIPT_LINEAR_A: `Lina`, Since: 0.9.30 + * @HB_SCRIPT_MAHAJANI: `Mahj`, Since: 0.9.30 + * @HB_SCRIPT_MANICHAEAN: `Mani`, Since: 0.9.30 + * @HB_SCRIPT_MENDE_KIKAKUI: `Mend`, Since: 0.9.30 + * @HB_SCRIPT_MODI: `Modi`, Since: 0.9.30 + * @HB_SCRIPT_MRO: `Mroo`, Since: 0.9.30 + * @HB_SCRIPT_NABATAEAN: `Nbat`, Since: 0.9.30 + * @HB_SCRIPT_OLD_NORTH_ARABIAN: `Narb`, Since: 0.9.30 + * @HB_SCRIPT_OLD_PERMIC: `Perm`, Since: 0.9.30 + * @HB_SCRIPT_PAHAWH_HMONG: `Hmng`, Since: 0.9.30 + * @HB_SCRIPT_PALMYRENE: `Palm`, Since: 0.9.30 + * @HB_SCRIPT_PAU_CIN_HAU: `Pauc`, Since: 0.9.30 + * @HB_SCRIPT_PSALTER_PAHLAVI: `Phlp`, Since: 0.9.30 + * @HB_SCRIPT_SIDDHAM: `Sidd`, Since: 0.9.30 + * @HB_SCRIPT_TIRHUTA: `Tirh`, Since: 0.9.30 + * @HB_SCRIPT_WARANG_CITI: `Wara`, Since: 0.9.30 + * @HB_SCRIPT_AHOM: `Ahom`, Since: 0.9.30 + * @HB_SCRIPT_ANATOLIAN_HIEROGLYPHS: `Hluw`, Since: 0.9.30 + * @HB_SCRIPT_HATRAN: `Hatr`, Since: 0.9.30 + * @HB_SCRIPT_MULTANI: `Mult`, Since: 0.9.30 + * @HB_SCRIPT_OLD_HUNGARIAN: `Hung`, Since: 0.9.30 + * @HB_SCRIPT_SIGNWRITING: `Sgnw`, Since: 0.9.30 + * @HB_SCRIPT_ADLAM: `Adlm`, Since: 1.3.0 + * @HB_SCRIPT_BHAIKSUKI: `Bhks`, Since: 1.3.0 + * @HB_SCRIPT_MARCHEN: `Marc`, Since: 1.3.0 + * @HB_SCRIPT_OSAGE: `Osge`, Since: 1.3.0 + * @HB_SCRIPT_TANGUT: `Tang`, Since: 1.3.0 + * @HB_SCRIPT_NEWA: `Newa`, Since: 1.3.0 + * @HB_SCRIPT_MASARAM_GONDI: `Gonm`, Since: 1.6.0 + * @HB_SCRIPT_NUSHU: `Nshu`, Since: 1.6.0 + * @HB_SCRIPT_SOYOMBO: `Soyo`, Since: 1.6.0 + * @HB_SCRIPT_ZANABAZAR_SQUARE: `Zanb`, Since: 1.6.0 + * @HB_SCRIPT_DOGRA: `Dogr`, Since: 1.8.0 + * @HB_SCRIPT_GUNJALA_GONDI: `Gong`, Since: 1.8.0 + * @HB_SCRIPT_HANIFI_ROHINGYA: `Rohg`, Since: 1.8.0 + * @HB_SCRIPT_MAKASAR: `Maka`, Since: 1.8.0 + * @HB_SCRIPT_MEDEFAIDRIN: `Medf`, Since: 1.8.0 + * @HB_SCRIPT_OLD_SOGDIAN: `Sogo`, Since: 1.8.0 + * @HB_SCRIPT_SOGDIAN: `Sogd`, Since: 1.8.0 + * @HB_SCRIPT_ELYMAIC: `Elym`, Since: 2.4.0 + * @HB_SCRIPT_NANDINAGARI: `Nand`, Since: 2.4.0 + * @HB_SCRIPT_NYIAKENG_PUACHUE_HMONG: `Hmnp`, Since: 2.4.0 + * @HB_SCRIPT_WANCHO: `Wcho`, Since: 2.4.0 + * @HB_SCRIPT_CHORASMIAN: `Chrs`, Since: 2.6.7 + * @HB_SCRIPT_DIVES_AKURU: `Diak`, Since: 2.6.7 + * @HB_SCRIPT_KHITAN_SMALL_SCRIPT: `Kits`, Since: 2.6.7 + * @HB_SCRIPT_YEZIDI: `Yezi`, Since: 2.6.7 + * @HB_SCRIPT_CYPRO_MINOAN: `Cpmn`, Since: 3.0.0 + * @HB_SCRIPT_OLD_UYGHUR: `Ougr`, Since: 3.0.0 + * @HB_SCRIPT_TANGSA: `Tnsa`, Since: 3.0.0 + * @HB_SCRIPT_TOTO: `Toto`, Since: 3.0.0 + * @HB_SCRIPT_VITHKUQI: `Vith`, Since: 3.0.0 + * @HB_SCRIPT_MATH: `Zmth`, Since: 3.4.0 + * @HB_SCRIPT_KAWI: `Kawi`, Since: 5.2.0 + * @HB_SCRIPT_NAG_MUNDARI: `Nagm`, Since: 5.2.0 + * @HB_SCRIPT_INVALID: No script set + * + * Data type for scripts. Each #hb_script_t's value is an #hb_tag_t corresponding + * to the four-letter values defined by [ISO 15924](https://unicode.org/iso15924/). + * + * See also the Script (sc) property of the Unicode Character Database. + * + **/ + +/* https://docs.google.com/spreadsheets/d/1Y90M0Ie3MUJ6UVCRDOypOtijlMDLNNyyLk36T6iMu0o */ +typedef enum +{ + HB_SCRIPT_COMMON = HB_TAG ('Z','y','y','y'), /*1.1*/ + HB_SCRIPT_INHERITED = HB_TAG ('Z','i','n','h'), /*1.1*/ + HB_SCRIPT_UNKNOWN = HB_TAG ('Z','z','z','z'), /*5.0*/ + + HB_SCRIPT_ARABIC = HB_TAG ('A','r','a','b'), /*1.1*/ + HB_SCRIPT_ARMENIAN = HB_TAG ('A','r','m','n'), /*1.1*/ + HB_SCRIPT_BENGALI = HB_TAG ('B','e','n','g'), /*1.1*/ + HB_SCRIPT_CYRILLIC = HB_TAG ('C','y','r','l'), /*1.1*/ + HB_SCRIPT_DEVANAGARI = HB_TAG ('D','e','v','a'), /*1.1*/ + HB_SCRIPT_GEORGIAN = HB_TAG ('G','e','o','r'), /*1.1*/ + HB_SCRIPT_GREEK = HB_TAG ('G','r','e','k'), /*1.1*/ + HB_SCRIPT_GUJARATI = HB_TAG ('G','u','j','r'), /*1.1*/ + HB_SCRIPT_GURMUKHI = HB_TAG ('G','u','r','u'), /*1.1*/ + HB_SCRIPT_HANGUL = HB_TAG ('H','a','n','g'), /*1.1*/ + HB_SCRIPT_HAN = HB_TAG ('H','a','n','i'), /*1.1*/ + HB_SCRIPT_HEBREW = HB_TAG ('H','e','b','r'), /*1.1*/ + HB_SCRIPT_HIRAGANA = HB_TAG ('H','i','r','a'), /*1.1*/ + HB_SCRIPT_KANNADA = HB_TAG ('K','n','d','a'), /*1.1*/ + HB_SCRIPT_KATAKANA = HB_TAG ('K','a','n','a'), /*1.1*/ + HB_SCRIPT_LAO = HB_TAG ('L','a','o','o'), /*1.1*/ + HB_SCRIPT_LATIN = HB_TAG ('L','a','t','n'), /*1.1*/ + HB_SCRIPT_MALAYALAM = HB_TAG ('M','l','y','m'), /*1.1*/ + HB_SCRIPT_ORIYA = HB_TAG ('O','r','y','a'), /*1.1*/ + HB_SCRIPT_TAMIL = HB_TAG ('T','a','m','l'), /*1.1*/ + HB_SCRIPT_TELUGU = HB_TAG ('T','e','l','u'), /*1.1*/ + HB_SCRIPT_THAI = HB_TAG ('T','h','a','i'), /*1.1*/ + + HB_SCRIPT_TIBETAN = HB_TAG ('T','i','b','t'), /*2.0*/ + + HB_SCRIPT_BOPOMOFO = HB_TAG ('B','o','p','o'), /*3.0*/ + HB_SCRIPT_BRAILLE = HB_TAG ('B','r','a','i'), /*3.0*/ + HB_SCRIPT_CANADIAN_SYLLABICS = HB_TAG ('C','a','n','s'), /*3.0*/ + HB_SCRIPT_CHEROKEE = HB_TAG ('C','h','e','r'), /*3.0*/ + HB_SCRIPT_ETHIOPIC = HB_TAG ('E','t','h','i'), /*3.0*/ + HB_SCRIPT_KHMER = HB_TAG ('K','h','m','r'), /*3.0*/ + HB_SCRIPT_MONGOLIAN = HB_TAG ('M','o','n','g'), /*3.0*/ + HB_SCRIPT_MYANMAR = HB_TAG ('M','y','m','r'), /*3.0*/ + HB_SCRIPT_OGHAM = HB_TAG ('O','g','a','m'), /*3.0*/ + HB_SCRIPT_RUNIC = HB_TAG ('R','u','n','r'), /*3.0*/ + HB_SCRIPT_SINHALA = HB_TAG ('S','i','n','h'), /*3.0*/ + HB_SCRIPT_SYRIAC = HB_TAG ('S','y','r','c'), /*3.0*/ + HB_SCRIPT_THAANA = HB_TAG ('T','h','a','a'), /*3.0*/ + HB_SCRIPT_YI = HB_TAG ('Y','i','i','i'), /*3.0*/ + + HB_SCRIPT_DESERET = HB_TAG ('D','s','r','t'), /*3.1*/ + HB_SCRIPT_GOTHIC = HB_TAG ('G','o','t','h'), /*3.1*/ + HB_SCRIPT_OLD_ITALIC = HB_TAG ('I','t','a','l'), /*3.1*/ + + HB_SCRIPT_BUHID = HB_TAG ('B','u','h','d'), /*3.2*/ + HB_SCRIPT_HANUNOO = HB_TAG ('H','a','n','o'), /*3.2*/ + HB_SCRIPT_TAGALOG = HB_TAG ('T','g','l','g'), /*3.2*/ + HB_SCRIPT_TAGBANWA = HB_TAG ('T','a','g','b'), /*3.2*/ + + HB_SCRIPT_CYPRIOT = HB_TAG ('C','p','r','t'), /*4.0*/ + HB_SCRIPT_LIMBU = HB_TAG ('L','i','m','b'), /*4.0*/ + HB_SCRIPT_LINEAR_B = HB_TAG ('L','i','n','b'), /*4.0*/ + HB_SCRIPT_OSMANYA = HB_TAG ('O','s','m','a'), /*4.0*/ + HB_SCRIPT_SHAVIAN = HB_TAG ('S','h','a','w'), /*4.0*/ + HB_SCRIPT_TAI_LE = HB_TAG ('T','a','l','e'), /*4.0*/ + HB_SCRIPT_UGARITIC = HB_TAG ('U','g','a','r'), /*4.0*/ + + HB_SCRIPT_BUGINESE = HB_TAG ('B','u','g','i'), /*4.1*/ + HB_SCRIPT_COPTIC = HB_TAG ('C','o','p','t'), /*4.1*/ + HB_SCRIPT_GLAGOLITIC = HB_TAG ('G','l','a','g'), /*4.1*/ + HB_SCRIPT_KHAROSHTHI = HB_TAG ('K','h','a','r'), /*4.1*/ + HB_SCRIPT_NEW_TAI_LUE = HB_TAG ('T','a','l','u'), /*4.1*/ + HB_SCRIPT_OLD_PERSIAN = HB_TAG ('X','p','e','o'), /*4.1*/ + HB_SCRIPT_SYLOTI_NAGRI = HB_TAG ('S','y','l','o'), /*4.1*/ + HB_SCRIPT_TIFINAGH = HB_TAG ('T','f','n','g'), /*4.1*/ + + HB_SCRIPT_BALINESE = HB_TAG ('B','a','l','i'), /*5.0*/ + HB_SCRIPT_CUNEIFORM = HB_TAG ('X','s','u','x'), /*5.0*/ + HB_SCRIPT_NKO = HB_TAG ('N','k','o','o'), /*5.0*/ + HB_SCRIPT_PHAGS_PA = HB_TAG ('P','h','a','g'), /*5.0*/ + HB_SCRIPT_PHOENICIAN = HB_TAG ('P','h','n','x'), /*5.0*/ + + HB_SCRIPT_CARIAN = HB_TAG ('C','a','r','i'), /*5.1*/ + HB_SCRIPT_CHAM = HB_TAG ('C','h','a','m'), /*5.1*/ + HB_SCRIPT_KAYAH_LI = HB_TAG ('K','a','l','i'), /*5.1*/ + HB_SCRIPT_LEPCHA = HB_TAG ('L','e','p','c'), /*5.1*/ + HB_SCRIPT_LYCIAN = HB_TAG ('L','y','c','i'), /*5.1*/ + HB_SCRIPT_LYDIAN = HB_TAG ('L','y','d','i'), /*5.1*/ + HB_SCRIPT_OL_CHIKI = HB_TAG ('O','l','c','k'), /*5.1*/ + HB_SCRIPT_REJANG = HB_TAG ('R','j','n','g'), /*5.1*/ + HB_SCRIPT_SAURASHTRA = HB_TAG ('S','a','u','r'), /*5.1*/ + HB_SCRIPT_SUNDANESE = HB_TAG ('S','u','n','d'), /*5.1*/ + HB_SCRIPT_VAI = HB_TAG ('V','a','i','i'), /*5.1*/ + + HB_SCRIPT_AVESTAN = HB_TAG ('A','v','s','t'), /*5.2*/ + HB_SCRIPT_BAMUM = HB_TAG ('B','a','m','u'), /*5.2*/ + HB_SCRIPT_EGYPTIAN_HIEROGLYPHS = HB_TAG ('E','g','y','p'), /*5.2*/ + HB_SCRIPT_IMPERIAL_ARAMAIC = HB_TAG ('A','r','m','i'), /*5.2*/ + HB_SCRIPT_INSCRIPTIONAL_PAHLAVI = HB_TAG ('P','h','l','i'), /*5.2*/ + HB_SCRIPT_INSCRIPTIONAL_PARTHIAN = HB_TAG ('P','r','t','i'), /*5.2*/ + HB_SCRIPT_JAVANESE = HB_TAG ('J','a','v','a'), /*5.2*/ + HB_SCRIPT_KAITHI = HB_TAG ('K','t','h','i'), /*5.2*/ + HB_SCRIPT_LISU = HB_TAG ('L','i','s','u'), /*5.2*/ + HB_SCRIPT_MEETEI_MAYEK = HB_TAG ('M','t','e','i'), /*5.2*/ + HB_SCRIPT_OLD_SOUTH_ARABIAN = HB_TAG ('S','a','r','b'), /*5.2*/ + HB_SCRIPT_OLD_TURKIC = HB_TAG ('O','r','k','h'), /*5.2*/ + HB_SCRIPT_SAMARITAN = HB_TAG ('S','a','m','r'), /*5.2*/ + HB_SCRIPT_TAI_THAM = HB_TAG ('L','a','n','a'), /*5.2*/ + HB_SCRIPT_TAI_VIET = HB_TAG ('T','a','v','t'), /*5.2*/ + + HB_SCRIPT_BATAK = HB_TAG ('B','a','t','k'), /*6.0*/ + HB_SCRIPT_BRAHMI = HB_TAG ('B','r','a','h'), /*6.0*/ + HB_SCRIPT_MANDAIC = HB_TAG ('M','a','n','d'), /*6.0*/ + + HB_SCRIPT_CHAKMA = HB_TAG ('C','a','k','m'), /*6.1*/ + HB_SCRIPT_MEROITIC_CURSIVE = HB_TAG ('M','e','r','c'), /*6.1*/ + HB_SCRIPT_MEROITIC_HIEROGLYPHS = HB_TAG ('M','e','r','o'), /*6.1*/ + HB_SCRIPT_MIAO = HB_TAG ('P','l','r','d'), /*6.1*/ + HB_SCRIPT_SHARADA = HB_TAG ('S','h','r','d'), /*6.1*/ + HB_SCRIPT_SORA_SOMPENG = HB_TAG ('S','o','r','a'), /*6.1*/ + HB_SCRIPT_TAKRI = HB_TAG ('T','a','k','r'), /*6.1*/ + + /* + * Since: 0.9.30 + */ + HB_SCRIPT_BASSA_VAH = HB_TAG ('B','a','s','s'), /*7.0*/ + HB_SCRIPT_CAUCASIAN_ALBANIAN = HB_TAG ('A','g','h','b'), /*7.0*/ + HB_SCRIPT_DUPLOYAN = HB_TAG ('D','u','p','l'), /*7.0*/ + HB_SCRIPT_ELBASAN = HB_TAG ('E','l','b','a'), /*7.0*/ + HB_SCRIPT_GRANTHA = HB_TAG ('G','r','a','n'), /*7.0*/ + HB_SCRIPT_KHOJKI = HB_TAG ('K','h','o','j'), /*7.0*/ + HB_SCRIPT_KHUDAWADI = HB_TAG ('S','i','n','d'), /*7.0*/ + HB_SCRIPT_LINEAR_A = HB_TAG ('L','i','n','a'), /*7.0*/ + HB_SCRIPT_MAHAJANI = HB_TAG ('M','a','h','j'), /*7.0*/ + HB_SCRIPT_MANICHAEAN = HB_TAG ('M','a','n','i'), /*7.0*/ + HB_SCRIPT_MENDE_KIKAKUI = HB_TAG ('M','e','n','d'), /*7.0*/ + HB_SCRIPT_MODI = HB_TAG ('M','o','d','i'), /*7.0*/ + HB_SCRIPT_MRO = HB_TAG ('M','r','o','o'), /*7.0*/ + HB_SCRIPT_NABATAEAN = HB_TAG ('N','b','a','t'), /*7.0*/ + HB_SCRIPT_OLD_NORTH_ARABIAN = HB_TAG ('N','a','r','b'), /*7.0*/ + HB_SCRIPT_OLD_PERMIC = HB_TAG ('P','e','r','m'), /*7.0*/ + HB_SCRIPT_PAHAWH_HMONG = HB_TAG ('H','m','n','g'), /*7.0*/ + HB_SCRIPT_PALMYRENE = HB_TAG ('P','a','l','m'), /*7.0*/ + HB_SCRIPT_PAU_CIN_HAU = HB_TAG ('P','a','u','c'), /*7.0*/ + HB_SCRIPT_PSALTER_PAHLAVI = HB_TAG ('P','h','l','p'), /*7.0*/ + HB_SCRIPT_SIDDHAM = HB_TAG ('S','i','d','d'), /*7.0*/ + HB_SCRIPT_TIRHUTA = HB_TAG ('T','i','r','h'), /*7.0*/ + HB_SCRIPT_WARANG_CITI = HB_TAG ('W','a','r','a'), /*7.0*/ + + HB_SCRIPT_AHOM = HB_TAG ('A','h','o','m'), /*8.0*/ + HB_SCRIPT_ANATOLIAN_HIEROGLYPHS = HB_TAG ('H','l','u','w'), /*8.0*/ + HB_SCRIPT_HATRAN = HB_TAG ('H','a','t','r'), /*8.0*/ + HB_SCRIPT_MULTANI = HB_TAG ('M','u','l','t'), /*8.0*/ + HB_SCRIPT_OLD_HUNGARIAN = HB_TAG ('H','u','n','g'), /*8.0*/ + HB_SCRIPT_SIGNWRITING = HB_TAG ('S','g','n','w'), /*8.0*/ + + /* + * Since 1.3.0 + */ + HB_SCRIPT_ADLAM = HB_TAG ('A','d','l','m'), /*9.0*/ + HB_SCRIPT_BHAIKSUKI = HB_TAG ('B','h','k','s'), /*9.0*/ + HB_SCRIPT_MARCHEN = HB_TAG ('M','a','r','c'), /*9.0*/ + HB_SCRIPT_OSAGE = HB_TAG ('O','s','g','e'), /*9.0*/ + HB_SCRIPT_TANGUT = HB_TAG ('T','a','n','g'), /*9.0*/ + HB_SCRIPT_NEWA = HB_TAG ('N','e','w','a'), /*9.0*/ + + /* + * Since 1.6.0 + */ + HB_SCRIPT_MASARAM_GONDI = HB_TAG ('G','o','n','m'), /*10.0*/ + HB_SCRIPT_NUSHU = HB_TAG ('N','s','h','u'), /*10.0*/ + HB_SCRIPT_SOYOMBO = HB_TAG ('S','o','y','o'), /*10.0*/ + HB_SCRIPT_ZANABAZAR_SQUARE = HB_TAG ('Z','a','n','b'), /*10.0*/ + + /* + * Since 1.8.0 + */ + HB_SCRIPT_DOGRA = HB_TAG ('D','o','g','r'), /*11.0*/ + HB_SCRIPT_GUNJALA_GONDI = HB_TAG ('G','o','n','g'), /*11.0*/ + HB_SCRIPT_HANIFI_ROHINGYA = HB_TAG ('R','o','h','g'), /*11.0*/ + HB_SCRIPT_MAKASAR = HB_TAG ('M','a','k','a'), /*11.0*/ + HB_SCRIPT_MEDEFAIDRIN = HB_TAG ('M','e','d','f'), /*11.0*/ + HB_SCRIPT_OLD_SOGDIAN = HB_TAG ('S','o','g','o'), /*11.0*/ + HB_SCRIPT_SOGDIAN = HB_TAG ('S','o','g','d'), /*11.0*/ + + /* + * Since 2.4.0 + */ + HB_SCRIPT_ELYMAIC = HB_TAG ('E','l','y','m'), /*12.0*/ + HB_SCRIPT_NANDINAGARI = HB_TAG ('N','a','n','d'), /*12.0*/ + HB_SCRIPT_NYIAKENG_PUACHUE_HMONG = HB_TAG ('H','m','n','p'), /*12.0*/ + HB_SCRIPT_WANCHO = HB_TAG ('W','c','h','o'), /*12.0*/ + + /* + * Since 2.6.7 + */ + HB_SCRIPT_CHORASMIAN = HB_TAG ('C','h','r','s'), /*13.0*/ + HB_SCRIPT_DIVES_AKURU = HB_TAG ('D','i','a','k'), /*13.0*/ + HB_SCRIPT_KHITAN_SMALL_SCRIPT = HB_TAG ('K','i','t','s'), /*13.0*/ + HB_SCRIPT_YEZIDI = HB_TAG ('Y','e','z','i'), /*13.0*/ + + /* + * Since 3.0.0 + */ + HB_SCRIPT_CYPRO_MINOAN = HB_TAG ('C','p','m','n'), /*14.0*/ + HB_SCRIPT_OLD_UYGHUR = HB_TAG ('O','u','g','r'), /*14.0*/ + HB_SCRIPT_TANGSA = HB_TAG ('T','n','s','a'), /*14.0*/ + HB_SCRIPT_TOTO = HB_TAG ('T','o','t','o'), /*14.0*/ + HB_SCRIPT_VITHKUQI = HB_TAG ('V','i','t','h'), /*14.0*/ + + /* + * Since 3.4.0 + */ + HB_SCRIPT_MATH = HB_TAG ('Z','m','t','h'), + + /* + * Since 5.2.0 + */ + HB_SCRIPT_KAWI = HB_TAG ('K','a','w','i'), /*15.0*/ + HB_SCRIPT_NAG_MUNDARI = HB_TAG ('N','a','g','m'), /*15.0*/ + + /* No script set. */ + HB_SCRIPT_INVALID = HB_TAG_NONE, + + /*< private >*/ + + /* Dummy values to ensure any hb_tag_t value can be passed/stored as hb_script_t + * without risking undefined behavior. We have two, for historical reasons. + * HB_TAG_MAX used to be unsigned, but that was invalid Ansi C, so was changed + * to _HB_SCRIPT_MAX_VALUE to be equal to HB_TAG_MAX_SIGNED as well. + * + * See this thread for technicalities: + * + * https://lists.freedesktop.org/archives/harfbuzz/2014-March/004150.html + */ + _HB_SCRIPT_MAX_VALUE = HB_TAG_MAX_SIGNED, /*< skip >*/ + _HB_SCRIPT_MAX_VALUE_SIGNED = HB_TAG_MAX_SIGNED /*< skip >*/ + +} hb_script_t; + + +/* Script functions */ + +HB_EXTERN hb_script_t +hb_script_from_iso15924_tag (hb_tag_t tag); + +HB_EXTERN hb_script_t +hb_script_from_string (const char *str, int len); + +HB_EXTERN hb_tag_t +hb_script_to_iso15924_tag (hb_script_t script); + +HB_EXTERN hb_direction_t +hb_script_get_horizontal_direction (hb_script_t script); + + +/* User data */ + +/** + * hb_user_data_key_t: + * + * Data structure for holding user-data keys. + * + **/ +typedef struct hb_user_data_key_t { + /*< private >*/ + char unused; +} hb_user_data_key_t; + +/** + * hb_destroy_func_t: + * @user_data: the data to be destroyed + * + * A virtual method for destroy user-data callbacks. + * + */ +typedef void (*hb_destroy_func_t) (void *user_data); + + +/* Font features and variations. */ + +/** + * HB_FEATURE_GLOBAL_START: + * + * Special setting for #hb_feature_t.start to apply the feature from the start + * of the buffer. + * + * Since: 2.0.0 + */ +#define HB_FEATURE_GLOBAL_START 0 + +/** + * HB_FEATURE_GLOBAL_END: + * + * Special setting for #hb_feature_t.end to apply the feature from to the end + * of the buffer. + * + * Since: 2.0.0 + */ +#define HB_FEATURE_GLOBAL_END ((unsigned int) -1) + +/** + * hb_feature_t: + * @tag: The #hb_tag_t tag of the feature + * @value: The value of the feature. 0 disables the feature, non-zero (usually + * 1) enables the feature. For features implemented as lookup type 3 (like + * 'salt') the @value is a one based index into the alternates. + * @start: the cluster to start applying this feature setting (inclusive). + * @end: the cluster to end applying this feature setting (exclusive). + * + * The #hb_feature_t is the structure that holds information about requested + * feature application. The feature will be applied with the given value to all + * glyphs which are in clusters between @start (inclusive) and @end (exclusive). + * Setting start to #HB_FEATURE_GLOBAL_START and end to #HB_FEATURE_GLOBAL_END + * specifies that the feature always applies to the entire buffer. + */ +typedef struct hb_feature_t { + hb_tag_t tag; + uint32_t value; + unsigned int start; + unsigned int end; +} hb_feature_t; + +HB_EXTERN hb_bool_t +hb_feature_from_string (const char *str, int len, + hb_feature_t *feature); + +HB_EXTERN void +hb_feature_to_string (hb_feature_t *feature, + char *buf, unsigned int size); + +/** + * hb_variation_t: + * @tag: The #hb_tag_t tag of the variation-axis name + * @value: The value of the variation axis + * + * Data type for holding variation data. Registered OpenType + * variation-axis tags are listed in + * [OpenType Axis Tag Registry](https://docs.microsoft.com/en-us/typography/opentype/spec/dvaraxisreg). + * + * Since: 1.4.2 + */ +typedef struct hb_variation_t { + hb_tag_t tag; + float value; +} hb_variation_t; + +HB_EXTERN hb_bool_t +hb_variation_from_string (const char *str, int len, + hb_variation_t *variation); + +HB_EXTERN void +hb_variation_to_string (hb_variation_t *variation, + char *buf, unsigned int size); + +/** + * hb_color_t: + * + * Data type for holding color values. Colors are eight bits per + * channel RGB plus alpha transparency. + * + * Since: 2.1.0 + */ +typedef uint32_t hb_color_t; + +/** + * HB_COLOR: + * @b: blue channel value + * @g: green channel value + * @r: red channel value + * @a: alpha channel value + * + * Constructs an #hb_color_t from four integers. + * + * Since: 2.1.0 + */ +#define HB_COLOR(b,g,r,a) ((hb_color_t) HB_TAG ((b),(g),(r),(a))) + +HB_EXTERN uint8_t +hb_color_get_alpha (hb_color_t color); +#define hb_color_get_alpha(color) ((color) & 0xFF) + +HB_EXTERN uint8_t +hb_color_get_red (hb_color_t color); +#define hb_color_get_red(color) (((color) >> 8) & 0xFF) + +HB_EXTERN uint8_t +hb_color_get_green (hb_color_t color); +#define hb_color_get_green(color) (((color) >> 16) & 0xFF) + +HB_EXTERN uint8_t +hb_color_get_blue (hb_color_t color); +#define hb_color_get_blue(color) (((color) >> 24) & 0xFF) + +HB_END_DECLS + +#endif /* HB_COMMON_H */ diff --git a/libs/harfbuzz/src/hb-config.hh b/libs/harfbuzz/src/hb-config.hh new file mode 100644 index 00000000..d56617f6 --- /dev/null +++ b/libs/harfbuzz/src/hb-config.hh @@ -0,0 +1,181 @@ +/* + * Copyright © 2019 Facebook, 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. + * + * Facebook Author(s): Behdad Esfahbod + */ + +#ifndef HB_CONFIG_HH +#define HB_CONFIG_HH + +#if 0 /* Make test happy. */ +#include "hb.hh" +#endif + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifndef HB_BORING_EXPANSION +#define HB_NO_BORING_EXPANSION +#endif + +#ifdef HB_TINY +#define HB_LEAN +#define HB_MINI +#define HB_NO_MT +#define HB_NO_UCD_UNASSIGNED +#ifndef NDEBUG +#define NDEBUG +#endif +#ifndef __OPTIMIZE_SIZE__ +#define __OPTIMIZE_SIZE__ +#endif +#endif + +#ifdef HB_LEAN +#define HB_DISABLE_DEPRECATED +#define HB_NDEBUG +#define HB_NO_ATEXIT +#define HB_NO_BUFFER_MESSAGE +#define HB_NO_BUFFER_SERIALIZE +#define HB_NO_BUFFER_VERIFY +#define HB_NO_BITMAP +#define HB_NO_CFF +#define HB_NO_COLOR +#define HB_NO_DRAW +#define HB_NO_ERRNO +#define HB_NO_FACE_COLLECT_UNICODES +#define HB_NO_GETENV +#define HB_NO_HINTING +#define HB_NO_LANGUAGE_LONG +#define HB_NO_LANGUAGE_PRIVATE_SUBTAG +#define HB_NO_LAYOUT_FEATURE_PARAMS +#define HB_NO_LAYOUT_COLLECT_GLYPHS +#define HB_NO_LAYOUT_RARELY_USED +#define HB_NO_LAYOUT_UNUSED +#define HB_NO_MATH +#define HB_NO_META +#define HB_NO_METRICS +#define HB_NO_MMAP +#define HB_NO_NAME +#define HB_NO_OPEN +#define HB_NO_SETLOCALE +#define HB_NO_OT_FONT_GLYPH_NAMES +#define HB_NO_OT_SHAPE_FRACTIONS +#define HB_NO_STYLE +#define HB_NO_SUBSET_LAYOUT +#define HB_NO_VAR +#endif + +#ifdef HB_MINI +#define HB_NO_AAT +#define HB_NO_LEGACY +#define HB_NO_BORING_EXPANSION +#endif + +#if defined(HAVE_CONFIG_OVERRIDE_H) || defined(HB_CONFIG_OVERRIDE_H) +#ifndef HB_CONFIG_OVERRIDE_H +#define HB_CONFIG_OVERRIDE_H "config-override.h" +#endif +#include HB_CONFIG_OVERRIDE_H +#endif + +/* Closure of options. */ + +#ifdef HB_NO_BORING_EXPANSION +#define HB_NO_BEYOND_64K +#define HB_NO_VARIATIONS2 +#endif + +#ifdef HB_DISABLE_DEPRECATED +#define HB_IF_NOT_DEPRECATED(x) +#else +#define HB_IF_NOT_DEPRECATED(x) x +#endif + +#ifdef HB_NO_AAT +#define HB_NO_OT_NAME_LANGUAGE_AAT +#define HB_NO_AAT_SHAPE +#endif + +#ifdef HB_NO_BITMAP +#define HB_NO_OT_FONT_BITMAP +#endif + +#ifdef HB_NO_CFF +#define HB_NO_OT_FONT_CFF +#define HB_NO_SUBSET_CFF +#endif + +#ifdef HB_NO_GETENV +#define HB_NO_UNISCRIBE_BUG_COMPATIBLE +#endif + +#ifdef HB_NO_LEGACY +#define HB_NO_CMAP_LEGACY_SUBTABLES +#define HB_NO_FALLBACK_SHAPE +#define HB_NO_OT_KERN +#define HB_NO_OT_LAYOUT_BLOCKLIST +#define HB_NO_OT_SHAPE_FALLBACK +#endif + +#ifdef HB_NO_NAME +#define HB_NO_OT_NAME_LANGUAGE +#endif + +#ifdef HB_NO_OT +#define HB_NO_OT_FONT +#define HB_NO_OT_LAYOUT +#define HB_NO_OT_TAG +#define HB_NO_OT_SHAPE +#endif + +#ifdef HB_NO_OT_SHAPE +#define HB_NO_AAT_SHAPE +#endif + +#ifdef HB_NO_OT_SHAPE_FALLBACK +#define HB_NO_OT_SHAPER_ARABIC_FALLBACK +#define HB_NO_OT_SHAPER_HEBREW_FALLBACK +#define HB_NO_OT_SHAPER_THAI_FALLBACK +#define HB_NO_OT_SHAPER_VOWEL_CONSTRAINTS +#endif + +#ifdef NDEBUG +#ifndef HB_NDEBUG +#define HB_NDEBUG +#endif +#endif + +#ifdef __OPTIMIZE_SIZE__ +#ifndef HB_OPTIMIZE_SIZE +#define HB_OPTIMIZE_SIZE +#endif +#endif + +#ifdef HB_OPTIMIZE_SIZE +#define HB_NO_OT_LAYOUT_LOOKUP_CACHE +#endif + + +#endif /* HB_CONFIG_HH */ diff --git a/libs/harfbuzz/src/hb-coretext.cc b/libs/harfbuzz/src/hb-coretext.cc new file mode 100644 index 00000000..99b33c00 --- /dev/null +++ b/libs/harfbuzz/src/hb-coretext.cc @@ -0,0 +1,1195 @@ +/* + * Copyright © 2012,2013 Mozilla Foundation. + * Copyright © 2012,2013 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. + * + * Mozilla Author(s): Jonathan Kew + * Google Author(s): Behdad Esfahbod + */ + +#include "hb.hh" + +#ifdef HAVE_CORETEXT + +#include "hb-shaper-impl.hh" + +#include "hb-coretext.h" +#include "hb-aat-layout.hh" + + +/** + * SECTION:hb-coretext + * @title: hb-coretext + * @short_description: CoreText integration + * @include: hb-coretext.h + * + * Functions for using HarfBuzz with the CoreText fonts. + **/ + +/* https://developer.apple.com/documentation/coretext/1508745-ctfontcreatewithgraphicsfont */ +#define HB_CORETEXT_DEFAULT_FONT_SIZE 12.f + +static void +release_table_data (void *user_data) +{ + CFDataRef cf_data = reinterpret_cast (user_data); + CFRelease(cf_data); +} + +static hb_blob_t * +_hb_cg_reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data) +{ + CGFontRef cg_font = reinterpret_cast (user_data); + CFDataRef cf_data = CGFontCopyTableForTag (cg_font, tag); + if (unlikely (!cf_data)) + return nullptr; + + const char *data = reinterpret_cast (CFDataGetBytePtr (cf_data)); + const size_t length = CFDataGetLength (cf_data); + if (!data || !length) + { + CFRelease (cf_data); + return nullptr; + } + + return hb_blob_create (data, length, HB_MEMORY_MODE_READONLY, + reinterpret_cast (const_cast<__CFData *> (cf_data)), + release_table_data); +} + +static void +_hb_cg_font_release (void *data) +{ + CGFontRelease ((CGFontRef) data); +} + + +static CTFontDescriptorRef +get_last_resort_font_desc () +{ + // TODO Handle allocation failures? + CTFontDescriptorRef last_resort = CTFontDescriptorCreateWithNameAndSize (CFSTR("LastResort"), 0); + CFArrayRef cascade_list = CFArrayCreate (kCFAllocatorDefault, + (const void **) &last_resort, + 1, + &kCFTypeArrayCallBacks); + CFRelease (last_resort); + CFDictionaryRef attributes = CFDictionaryCreate (kCFAllocatorDefault, + (const void **) &kCTFontCascadeListAttribute, + (const void **) &cascade_list, + 1, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + CFRelease (cascade_list); + + CTFontDescriptorRef font_desc = CTFontDescriptorCreateWithAttributes (attributes); + CFRelease (attributes); + return font_desc; +} + +static void +release_data (void *info, const void *data, size_t size) +{ + assert (hb_blob_get_length ((hb_blob_t *) info) == size && + hb_blob_get_data ((hb_blob_t *) info, nullptr) == data); + + hb_blob_destroy ((hb_blob_t *) info); +} + +static CGFontRef +create_cg_font (hb_face_t *face) +{ + CGFontRef cg_font = nullptr; + if (face->destroy == _hb_cg_font_release) + { + cg_font = CGFontRetain ((CGFontRef) face->user_data); + } + else + { + hb_blob_t *blob = hb_face_reference_blob (face); + unsigned int blob_length; + const char *blob_data = hb_blob_get_data (blob, &blob_length); + if (unlikely (!blob_length)) + DEBUG_MSG (CORETEXT, face, "Face has empty blob"); + + CGDataProviderRef provider = CGDataProviderCreateWithData (blob, blob_data, blob_length, &release_data); + if (likely (provider)) + { + cg_font = CGFontCreateWithDataProvider (provider); + if (unlikely (!cg_font)) + DEBUG_MSG (CORETEXT, face, "Face CGFontCreateWithDataProvider() failed"); + CGDataProviderRelease (provider); + } + } + return cg_font; +} + +static CTFontRef +create_ct_font (CGFontRef cg_font, CGFloat font_size) +{ + CTFontRef ct_font = nullptr; + + /* CoreText does not enable trak table usage / tracking when creating a CTFont + * using CTFontCreateWithGraphicsFont. The only way of enabling tracking seems + * to be through the CTFontCreateUIFontForLanguage call. */ + CFStringRef cg_postscript_name = CGFontCopyPostScriptName (cg_font); + if (CFStringHasPrefix (cg_postscript_name, CFSTR (".SFNSText")) || + CFStringHasPrefix (cg_postscript_name, CFSTR (".SFNSDisplay"))) + { +#if !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) && MAC_OS_X_VERSION_MIN_REQUIRED < 1080 +# define kCTFontUIFontSystem kCTFontSystemFontType +# define kCTFontUIFontEmphasizedSystem kCTFontEmphasizedSystemFontType +#endif + CTFontUIFontType font_type = kCTFontUIFontSystem; + if (CFStringHasSuffix (cg_postscript_name, CFSTR ("-Bold"))) + font_type = kCTFontUIFontEmphasizedSystem; + + ct_font = CTFontCreateUIFontForLanguage (font_type, font_size, nullptr); + CFStringRef ct_result_name = CTFontCopyPostScriptName(ct_font); + if (CFStringCompare (ct_result_name, cg_postscript_name, 0) != kCFCompareEqualTo) + { + CFRelease(ct_font); + ct_font = nullptr; + } + CFRelease (ct_result_name); + } + CFRelease (cg_postscript_name); + + if (!ct_font) + ct_font = CTFontCreateWithGraphicsFont (cg_font, font_size, nullptr, nullptr); + + if (unlikely (!ct_font)) { + DEBUG_MSG (CORETEXT, cg_font, "Font CTFontCreateWithGraphicsFont() failed"); + return nullptr; + } + + /* crbug.com/576941 and crbug.com/625902 and the investigation in the latter + * bug indicate that the cascade list reconfiguration occasionally causes + * crashes in CoreText on OS X 10.9, thus let's skip this step on older + * operating system versions. Except for the emoji font, where _not_ + * reconfiguring the cascade list causes CoreText crashes. For details, see + * crbug.com/549610 */ + // 0x00070000 stands for "kCTVersionNumber10_10", see CoreText.h +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + if (&CTGetCoreTextVersion != nullptr && CTGetCoreTextVersion() < 0x00070000) { +#pragma GCC diagnostic pop + CFStringRef fontName = CTFontCopyPostScriptName (ct_font); + bool isEmojiFont = CFStringCompare (fontName, CFSTR("AppleColorEmoji"), 0) == kCFCompareEqualTo; + CFRelease (fontName); + if (!isEmojiFont) + return ct_font; + } + + CFURLRef original_url = nullptr; +#if !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) && MAC_OS_X_VERSION_MIN_REQUIRED < 1060 + ATSFontRef atsFont; + FSRef fsref; + OSStatus status; + atsFont = CTFontGetPlatformFont (ct_font, NULL); + status = ATSFontGetFileReference (atsFont, &fsref); + if (status == noErr) + original_url = CFURLCreateFromFSRef (NULL, &fsref); +#else + original_url = (CFURLRef) CTFontCopyAttribute (ct_font, kCTFontURLAttribute); +#endif + + /* Create font copy with cascade list that has LastResort first; this speeds up CoreText + * font fallback which we don't need anyway. */ + { + CTFontDescriptorRef last_resort_font_desc = get_last_resort_font_desc (); + CTFontRef new_ct_font = CTFontCreateCopyWithAttributes (ct_font, 0.0, nullptr, last_resort_font_desc); + CFRelease (last_resort_font_desc); + if (new_ct_font) + { + /* The CTFontCreateCopyWithAttributes call fails to stay on the same font + * when reconfiguring the cascade list and may switch to a different font + * when there are fonts that go by the same name, since the descriptor is + * just name and size. + * + * Avoid reconfiguring the cascade lists if the new font is outside the + * system locations that we cannot access from the sandboxed renderer + * process in Blink. This can be detected by the new file URL location + * that the newly found font points to. */ + CFURLRef new_url = nullptr; +#if !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) && MAC_OS_X_VERSION_MIN_REQUIRED < 1060 + atsFont = CTFontGetPlatformFont (new_ct_font, NULL); + status = ATSFontGetFileReference (atsFont, &fsref); + if (status == noErr) + new_url = CFURLCreateFromFSRef (NULL, &fsref); +#else + new_url = (CFURLRef) CTFontCopyAttribute (new_ct_font, kCTFontURLAttribute); +#endif + // Keep reconfigured font if URL cannot be retrieved (seems to be the case + // on Mac OS 10.12 Sierra), speculative fix for crbug.com/625606 + if (!original_url || !new_url || CFEqual (original_url, new_url)) { + CFRelease (ct_font); + ct_font = new_ct_font; + } else { + CFRelease (new_ct_font); + DEBUG_MSG (CORETEXT, ct_font, "Discarding reconfigured CTFont, location changed."); + } + if (new_url) + CFRelease (new_url); + } + else + DEBUG_MSG (CORETEXT, ct_font, "Font copy with empty cascade list failed"); + } + + if (original_url) + CFRelease (original_url); + return ct_font; +} + +hb_coretext_face_data_t * +_hb_coretext_shaper_face_data_create (hb_face_t *face) +{ + CGFontRef cg_font = create_cg_font (face); + + if (unlikely (!cg_font)) + { + DEBUG_MSG (CORETEXT, face, "CGFont creation failed.."); + return nullptr; + } + + return (hb_coretext_face_data_t *) cg_font; +} + +void +_hb_coretext_shaper_face_data_destroy (hb_coretext_face_data_t *data) +{ + CFRelease ((CGFontRef) data); +} + +/** + * hb_coretext_face_create: + * @cg_font: The CGFontRef to work upon + * + * Creates an #hb_face_t face object from the specified + * CGFontRef. + * + * Return value: the new #hb_face_t face object + * + * Since: 0.9.10 + */ +hb_face_t * +hb_coretext_face_create (CGFontRef cg_font) +{ + return hb_face_create_for_tables (_hb_cg_reference_table, CGFontRetain (cg_font), _hb_cg_font_release); +} + +/** + * hb_coretext_face_get_cg_font: + * @face: The #hb_face_t to work upon + * + * Fetches the CGFontRef associated with an #hb_face_t + * face object + * + * Return value: the CGFontRef found + * + * Since: 0.9.10 + */ +CGFontRef +hb_coretext_face_get_cg_font (hb_face_t *face) +{ + return (CGFontRef) (const void *) face->data.coretext; +} + + +hb_coretext_font_data_t * +_hb_coretext_shaper_font_data_create (hb_font_t *font) +{ + hb_face_t *face = font->face; + const hb_coretext_face_data_t *face_data = face->data.coretext; + if (unlikely (!face_data)) return nullptr; + CGFontRef cg_font = (CGFontRef) (const void *) face->data.coretext; + + CGFloat font_size = (CGFloat) (font->ptem <= 0.f ? HB_CORETEXT_DEFAULT_FONT_SIZE : font->ptem); + CTFontRef ct_font = create_ct_font (cg_font, font_size); + + if (unlikely (!ct_font)) + { + DEBUG_MSG (CORETEXT, font, "CGFont creation failed.."); + return nullptr; + } + + if (font->num_coords) + { + CFMutableDictionaryRef variations = + CFDictionaryCreateMutable (kCFAllocatorDefault, + font->num_coords, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + + for (unsigned i = 0; i < font->num_coords; i++) + { + if (font->coords[i] == 0.) continue; + + hb_ot_var_axis_info_t info; + unsigned int c = 1; + hb_ot_var_get_axis_infos (font->face, i, &c, &info); + CFDictionarySetValue (variations, + CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &info.tag), + CFNumberCreate (kCFAllocatorDefault, kCFNumberFloatType, &font->design_coords[i]) + ); + } + + CFDictionaryRef attributes = + CFDictionaryCreate (kCFAllocatorDefault, + (const void **) &kCTFontVariationAttribute, + (const void **) &variations, + 1, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + + CTFontDescriptorRef varDesc = CTFontDescriptorCreateWithAttributes (attributes); + CTFontRef new_ct_font = CTFontCreateCopyWithAttributes (ct_font, 0, nullptr, varDesc); + + CFRelease (ct_font); + CFRelease (attributes); + CFRelease (variations); + ct_font = new_ct_font; + } + + return (hb_coretext_font_data_t *) ct_font; +} + +void +_hb_coretext_shaper_font_data_destroy (hb_coretext_font_data_t *data) +{ + CFRelease ((CTFontRef) data); +} + +/** + * hb_coretext_font_create: + * @ct_font: The CTFontRef to work upon + * + * Creates an #hb_font_t font object from the specified + * CTFontRef. + * + * Return value: the new #hb_font_t font object + * + * Since: 1.7.2 + **/ +hb_font_t * +hb_coretext_font_create (CTFontRef ct_font) +{ + CGFontRef cg_font = CTFontCopyGraphicsFont (ct_font, nullptr); + hb_face_t *face = hb_coretext_face_create (cg_font); + CFRelease (cg_font); + hb_font_t *font = hb_font_create (face); + hb_face_destroy (face); + + if (unlikely (hb_object_is_immutable (font))) + return font; + + hb_font_set_ptem (font, CTFontGetSize (ct_font)); + + /* Let there be dragons here... */ + font->data.coretext.cmpexch (nullptr, (hb_coretext_font_data_t *) CFRetain (ct_font)); + + return font; +} + +/** + * hb_coretext_font_get_ct_font: + * @font: #hb_font_t to work upon + * + * Fetches the CTFontRef associated with the specified + * #hb_font_t font object. + * + * Return value: the CTFontRef found + * + * Since: 0.9.10 + */ +CTFontRef +hb_coretext_font_get_ct_font (hb_font_t *font) +{ + CTFontRef ct_font = (CTFontRef) (const void *) font->data.coretext; + return ct_font ? (CTFontRef) ct_font : nullptr; +} + + +/* + * shaper + */ + +struct feature_record_t { + unsigned int feature; + unsigned int setting; +}; + +struct active_feature_t { + feature_record_t rec; + unsigned int order; + + HB_INTERNAL static int cmp (const void *pa, const void *pb) { + const active_feature_t *a = (const active_feature_t *) pa; + const active_feature_t *b = (const active_feature_t *) pb; + return a->rec.feature < b->rec.feature ? -1 : a->rec.feature > b->rec.feature ? 1 : + a->order < b->order ? -1 : a->order > b->order ? 1 : + a->rec.setting < b->rec.setting ? -1 : a->rec.setting > b->rec.setting ? 1 : + 0; + } + bool operator== (const active_feature_t& f) const { + return cmp (this, &f) == 0; + } +}; + +struct feature_event_t { + unsigned int index; + bool start; + active_feature_t feature; + + HB_INTERNAL static int cmp (const void *pa, const void *pb) { + const feature_event_t *a = (const feature_event_t *) pa; + const feature_event_t *b = (const feature_event_t *) pb; + return a->index < b->index ? -1 : a->index > b->index ? 1 : + a->start < b->start ? -1 : a->start > b->start ? 1 : + active_feature_t::cmp (&a->feature, &b->feature); + } +}; + +struct range_record_t { + CTFontRef font; + unsigned int index_first; /* == start */ + unsigned int index_last; /* == end - 1 */ +}; + + +hb_bool_t +_hb_coretext_shape (hb_shape_plan_t *shape_plan, + hb_font_t *font, + hb_buffer_t *buffer, + const hb_feature_t *features, + unsigned int num_features) +{ + hb_face_t *face = font->face; + CGFontRef cg_font = (CGFontRef) (const void *) face->data.coretext; + CTFontRef ct_font = (CTFontRef) (const void *) font->data.coretext; + + CGFloat ct_font_size = CTFontGetSize (ct_font); + CGFloat x_mult = (CGFloat) font->x_scale / ct_font_size; + CGFloat y_mult = (CGFloat) font->y_scale / ct_font_size; + + /* Attach marks to their bases, to match the 'ot' shaper. + * Adapted from a very old version of hb-ot-shape:hb_form_clusters(). + * Note that this only makes us be closer to the 'ot' shaper, + * but by no means the same. For example, if there's + * B1 M1 B2 M2, and B1-B2 form a ligature, M2's cluster will + * continue pointing to B2 even though B2 was merged into B1's + * cluster... */ + if (buffer->cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES) + { + hb_unicode_funcs_t *unicode = buffer->unicode; + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; + for (unsigned int i = 1; i < count; i++) + if (HB_UNICODE_GENERAL_CATEGORY_IS_MARK (unicode->general_category (info[i].codepoint))) + buffer->merge_clusters (i - 1, i + 1); + } + + hb_vector_t feature_records; + hb_vector_t range_records; + + /* + * Set up features. + * (copied + modified from code from hb-uniscribe.cc) + */ + if (num_features) + { + /* Sort features by start/end events. */ + hb_vector_t feature_events; + for (unsigned int i = 0; i < num_features; i++) + { + active_feature_t feature; + +#if MAC_OS_X_VERSION_MIN_REQUIRED < 101000 + const hb_aat_feature_mapping_t * mapping = hb_aat_layout_find_feature_mapping (features[i].tag); + if (!mapping) + continue; + + feature.rec.feature = mapping->aatFeatureType; + feature.rec.setting = features[i].value ? mapping->selectorToEnable : mapping->selectorToDisable; +#else + feature.rec.feature = features[i].tag; + feature.rec.setting = features[i].value; +#endif + feature.order = i; + + feature_event_t *event; + + event = feature_events.push (); + event->index = features[i].start; + event->start = true; + event->feature = feature; + + event = feature_events.push (); + event->index = features[i].end; + event->start = false; + event->feature = feature; + } + feature_events.qsort (); + /* Add a strategic final event. */ + { + active_feature_t feature; + feature.rec.feature = HB_TAG_NONE; + feature.rec.setting = 0; + feature.order = num_features + 1; + + feature_event_t *event = feature_events.push (); + event->index = 0; /* This value does magic. */ + event->start = false; + event->feature = feature; + } + + /* Scan events and save features for each range. */ + hb_vector_t active_features; + unsigned int last_index = 0; + for (unsigned int i = 0; i < feature_events.length; i++) + { + feature_event_t *event = &feature_events[i]; + + if (event->index != last_index) + { + /* Save a snapshot of active features and the range. */ + range_record_t *range = range_records.push (); + + if (active_features.length) + { + CFMutableArrayRef features_array = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); + + /* TODO sort and resolve conflicting features? */ + /* active_features.qsort (); */ + for (unsigned int j = 0; j < active_features.length; j++) + { +#if MAC_OS_X_VERSION_MIN_REQUIRED < 101000 + CFStringRef keys[] = { + kCTFontFeatureTypeIdentifierKey, + kCTFontFeatureSelectorIdentifierKey + }; + CFNumberRef values[] = { + CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &active_features[j].rec.feature), + CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &active_features[j].rec.setting) + }; +#else + char tag[5] = {HB_UNTAG (active_features[j].rec.feature)}; + CFTypeRef keys[] = { + kCTFontOpenTypeFeatureTag, + kCTFontOpenTypeFeatureValue + }; + CFTypeRef values[] = { + CFStringCreateWithCString (kCFAllocatorDefault, tag, kCFStringEncodingASCII), + CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &active_features[j].rec.setting) + }; +#endif + static_assert ((ARRAY_LENGTH_CONST (keys) == ARRAY_LENGTH_CONST (values)), ""); + CFDictionaryRef dict = CFDictionaryCreate (kCFAllocatorDefault, + (const void **) keys, + (const void **) values, + ARRAY_LENGTH (keys), + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + for (unsigned int i = 0; i < ARRAY_LENGTH (values); i++) + CFRelease (values[i]); + + CFArrayAppendValue (features_array, dict); + CFRelease (dict); + + } + + CFDictionaryRef attributes = CFDictionaryCreate (kCFAllocatorDefault, + (const void **) &kCTFontFeatureSettingsAttribute, + (const void **) &features_array, + 1, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + CFRelease (features_array); + + CTFontDescriptorRef font_desc = CTFontDescriptorCreateWithAttributes (attributes); + CFRelease (attributes); + + range->font = CTFontCreateCopyWithAttributes (ct_font, 0.0, nullptr, font_desc); + CFRelease (font_desc); + } + else + { + range->font = nullptr; + } + + range->index_first = last_index; + range->index_last = event->index - 1; + + last_index = event->index; + } + + if (event->start) + { + active_features.push (event->feature); + } else { + active_feature_t *feature = active_features.lsearch (event->feature); + if (feature) + active_features.remove (feature - active_features.arrayZ); + } + } + } + + unsigned int scratch_size; + hb_buffer_t::scratch_buffer_t *scratch = buffer->get_scratch_buffer (&scratch_size); + +#define ALLOCATE_ARRAY(Type, name, len, on_no_room) \ + Type *name = (Type *) scratch; \ + do { \ + unsigned int _consumed = DIV_CEIL ((len) * sizeof (Type), sizeof (*scratch)); \ + if (unlikely (_consumed > scratch_size)) \ + { \ + on_no_room; \ + assert (0); \ + } \ + scratch += _consumed; \ + scratch_size -= _consumed; \ + } while (0) + + ALLOCATE_ARRAY (UniChar, pchars, buffer->len * 2, ((void)nullptr) /*nothing*/); + unsigned int chars_len = 0; + for (unsigned int i = 0; i < buffer->len; i++) { + hb_codepoint_t c = buffer->info[i].codepoint; + if (likely (c <= 0xFFFFu)) + pchars[chars_len++] = c; + else if (unlikely (c > 0x10FFFFu)) + pchars[chars_len++] = 0xFFFDu; + else { + pchars[chars_len++] = 0xD800u + ((c - 0x10000u) >> 10); + pchars[chars_len++] = 0xDC00u + ((c - 0x10000u) & ((1u << 10) - 1)); + } + } + + ALLOCATE_ARRAY (unsigned int, log_clusters, chars_len, ((void)nullptr) /*nothing*/); + chars_len = 0; + for (unsigned int i = 0; i < buffer->len; i++) + { + hb_codepoint_t c = buffer->info[i].codepoint; + unsigned int cluster = buffer->info[i].cluster; + log_clusters[chars_len++] = cluster; + if (hb_in_range (c, 0x10000u, 0x10FFFFu)) + log_clusters[chars_len++] = cluster; /* Surrogates. */ + } + +#define FAIL(...) \ + HB_STMT_START { \ + DEBUG_MSG (CORETEXT, nullptr, __VA_ARGS__); \ + ret = false; \ + goto fail; \ + } HB_STMT_END + + bool ret = true; + CFStringRef string_ref = nullptr; + CTLineRef line = nullptr; + + if (false) + { +resize_and_retry: + DEBUG_MSG (CORETEXT, buffer, "Buffer resize"); + /* string_ref uses the scratch-buffer for backing store, and line references + * string_ref (via attr_string). We must release those before resizing buffer. */ + assert (string_ref); + assert (line); + CFRelease (string_ref); + CFRelease (line); + string_ref = nullptr; + line = nullptr; + + /* Get previous start-of-scratch-area, that we use later for readjusting + * our existing scratch arrays. */ + unsigned int old_scratch_used; + hb_buffer_t::scratch_buffer_t *old_scratch; + old_scratch = buffer->get_scratch_buffer (&old_scratch_used); + old_scratch_used = scratch - old_scratch; + + if (unlikely (!buffer->ensure (buffer->allocated * 2))) + FAIL ("Buffer resize failed"); + + /* Adjust scratch, pchars, and log_cluster arrays. This is ugly, but really the + * cleanest way to do without completely restructuring the rest of this shaper. */ + scratch = buffer->get_scratch_buffer (&scratch_size); + pchars = reinterpret_cast (((char *) scratch + ((char *) pchars - (char *) old_scratch))); + log_clusters = reinterpret_cast (((char *) scratch + ((char *) log_clusters - (char *) old_scratch))); + scratch += old_scratch_used; + scratch_size -= old_scratch_used; + } + { + string_ref = CFStringCreateWithCharactersNoCopy (nullptr, + pchars, chars_len, + kCFAllocatorNull); + if (unlikely (!string_ref)) + FAIL ("CFStringCreateWithCharactersNoCopy failed"); + + /* Create an attributed string, populate it, and create a line from it, then release attributed string. */ + { + CFMutableAttributedStringRef attr_string = CFAttributedStringCreateMutable (kCFAllocatorDefault, + chars_len); + if (unlikely (!attr_string)) + FAIL ("CFAttributedStringCreateMutable failed"); + CFAttributedStringReplaceString (attr_string, CFRangeMake (0, 0), string_ref); + if (HB_DIRECTION_IS_VERTICAL (buffer->props.direction)) + { + CFAttributedStringSetAttribute (attr_string, CFRangeMake (0, chars_len), + kCTVerticalFormsAttributeName, kCFBooleanTrue); + } + + if (buffer->props.language) + { +/* What's the iOS equivalent of this check? + * The symbols was introduced in iOS 7.0. + * At any rate, our fallback is safe and works fine. */ +#if !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) && MAC_OS_X_VERSION_MIN_REQUIRED < 1090 +# define kCTLanguageAttributeName CFSTR ("NSLanguage") +#endif + CFStringRef lang = CFStringCreateWithCStringNoCopy (kCFAllocatorDefault, + hb_language_to_string (buffer->props.language), + kCFStringEncodingUTF8, + kCFAllocatorNull); + if (unlikely (!lang)) + { + CFRelease (attr_string); + FAIL ("CFStringCreateWithCStringNoCopy failed"); + } + CFAttributedStringSetAttribute (attr_string, CFRangeMake (0, chars_len), + kCTLanguageAttributeName, lang); + CFRelease (lang); + } + CFAttributedStringSetAttribute (attr_string, CFRangeMake (0, chars_len), + kCTFontAttributeName, ct_font); + + if (num_features && range_records.length) + { + unsigned int start = 0; + range_record_t *last_range = &range_records[0]; + for (unsigned int k = 0; k < chars_len; k++) + { + range_record_t *range = last_range; + while (log_clusters[k] < range->index_first) + range--; + while (log_clusters[k] > range->index_last) + range++; + if (range != last_range) + { + if (last_range->font) + CFAttributedStringSetAttribute (attr_string, CFRangeMake (start, k - start), + kCTFontAttributeName, last_range->font); + + start = k; + } + + last_range = range; + } + if (start != chars_len && last_range->font) + CFAttributedStringSetAttribute (attr_string, CFRangeMake (start, chars_len - start), + kCTFontAttributeName, last_range->font); + } + /* Enable/disable kern if requested. + * + * Note: once kern is disabled, reenabling it doesn't currently seem to work in CoreText. + */ + if (num_features) + { + unsigned int zeroint = 0; + CFNumberRef zero = CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &zeroint); + for (unsigned int i = 0; i < num_features; i++) + { + const hb_feature_t &feature = features[i]; + if (feature.tag == HB_TAG('k','e','r','n') && + feature.start < chars_len && feature.start < feature.end) + { + CFRange feature_range = CFRangeMake (feature.start, + hb_min (feature.end, chars_len) - feature.start); + if (feature.value) + CFAttributedStringRemoveAttribute (attr_string, feature_range, kCTKernAttributeName); + else + CFAttributedStringSetAttribute (attr_string, feature_range, kCTKernAttributeName, zero); + } + } + CFRelease (zero); + } + + int level = HB_DIRECTION_IS_FORWARD (buffer->props.direction) ? 0 : 1; + CFNumberRef level_number = CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &level); +#if !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) && MAC_OS_X_VERSION_MIN_REQUIRED < 1060 + extern const CFStringRef kCTTypesetterOptionForcedEmbeddingLevel; +#endif + CFDictionaryRef options = CFDictionaryCreate (kCFAllocatorDefault, + (const void **) &kCTTypesetterOptionForcedEmbeddingLevel, + (const void **) &level_number, + 1, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + CFRelease (level_number); + if (unlikely (!options)) + { + CFRelease (attr_string); + FAIL ("CFDictionaryCreate failed"); + } + + CTTypesetterRef typesetter = CTTypesetterCreateWithAttributedStringAndOptions (attr_string, options); + CFRelease (options); + CFRelease (attr_string); + if (unlikely (!typesetter)) + FAIL ("CTTypesetterCreateWithAttributedStringAndOptions failed"); + + line = CTTypesetterCreateLine (typesetter, CFRangeMake(0, 0)); + CFRelease (typesetter); + if (unlikely (!line)) + FAIL ("CTTypesetterCreateLine failed"); + } + + CFArrayRef glyph_runs = CTLineGetGlyphRuns (line); + unsigned int num_runs = CFArrayGetCount (glyph_runs); + DEBUG_MSG (CORETEXT, nullptr, "Num runs: %d", num_runs); + + buffer->len = 0; + uint32_t status_or = 0; + CGFloat advances_so_far = 0; + /* For right-to-left runs, CoreText returns the glyphs positioned such that + * any trailing whitespace is to the left of (0,0). Adjust coordinate system + * to fix for that. Test with any RTL string with trailing spaces. + * https://crbug.com/469028 + */ + if (HB_DIRECTION_IS_BACKWARD (buffer->props.direction)) + { + advances_so_far -= CTLineGetTrailingWhitespaceWidth (line); + if (HB_DIRECTION_IS_VERTICAL (buffer->props.direction)) + advances_so_far = -advances_so_far; + } + + const CFRange range_all = CFRangeMake (0, 0); + + for (unsigned int i = 0; i < num_runs; i++) + { + CTRunRef run = static_cast(CFArrayGetValueAtIndex (glyph_runs, i)); + CTRunStatus run_status = CTRunGetStatus (run); + status_or |= run_status; + DEBUG_MSG (CORETEXT, run, "CTRunStatus: %x", run_status); + CGFloat run_advance = CTRunGetTypographicBounds (run, range_all, nullptr, nullptr, nullptr); + if (HB_DIRECTION_IS_VERTICAL (buffer->props.direction)) + run_advance = -run_advance; + DEBUG_MSG (CORETEXT, run, "Run advance: %g", (double) run_advance); + + /* CoreText does automatic font fallback (AKA "cascading") for characters + * not supported by the requested font, and provides no way to turn it off, + * so we must detect if the returned run uses a font other than the requested + * one and fill in the buffer with .notdef glyphs instead of random glyph + * indices from a different font. + */ + CFDictionaryRef attributes = CTRunGetAttributes (run); + CTFontRef run_ct_font = static_cast(CFDictionaryGetValue (attributes, kCTFontAttributeName)); + if (!CFEqual (run_ct_font, ct_font)) + { + /* The run doesn't use our main font instance. We have to figure out + * whether font fallback happened, or this is just CoreText giving us + * another CTFont using the same underlying CGFont. CoreText seems + * to do that in a variety of situations, one of which being vertical + * text, but also perhaps for caching reasons. + * + * First, see if it uses any of our subfonts created to set font features... + * + * Next, compare the CGFont to the one we used to create our fonts. + * Even this doesn't work all the time. + * + * Finally, we compare PS names, which I don't think are unique... + * + * Looks like if we really want to be sure here we have to modify the + * font to change the name table, similar to what we do in the uniscribe + * backend. + * + * However, even that wouldn't work if we were passed in the CGFont to + * construct a hb_face to begin with. + * + * See: https://github.com/harfbuzz/harfbuzz/pull/36 + * + * Also see: https://bugs.chromium.org/p/chromium/issues/detail?id=597098 + */ + bool matched = false; + for (unsigned int i = 0; i < range_records.length; i++) + if (range_records[i].font && CFEqual (run_ct_font, range_records[i].font)) + { + matched = true; + break; + } + if (!matched) + { + CGFontRef run_cg_font = CTFontCopyGraphicsFont (run_ct_font, nullptr); + if (run_cg_font) + { + matched = CFEqual (run_cg_font, cg_font); + CFRelease (run_cg_font); + } + } + if (!matched) + { + CFStringRef font_ps_name = CTFontCopyName (ct_font, kCTFontPostScriptNameKey); + CFStringRef run_ps_name = CTFontCopyName (run_ct_font, kCTFontPostScriptNameKey); + CFComparisonResult result = CFStringCompare (run_ps_name, font_ps_name, 0); + CFRelease (run_ps_name); + CFRelease (font_ps_name); + if (result == kCFCompareEqualTo) + matched = true; + } + if (!matched) + { + CFRange range = CTRunGetStringRange (run); + DEBUG_MSG (CORETEXT, run, "Run used fallback font: %ld..%ld", + range.location, range.location + range.length); + if (!buffer->ensure_inplace (buffer->len + range.length)) + goto resize_and_retry; + hb_glyph_info_t *info = buffer->info + buffer->len; + + hb_codepoint_t notdef = 0; + hb_direction_t dir = buffer->props.direction; + hb_position_t x_advance, y_advance, x_offset, y_offset; + hb_font_get_glyph_advance_for_direction (font, notdef, dir, &x_advance, &y_advance); + hb_font_get_glyph_origin_for_direction (font, notdef, dir, &x_offset, &y_offset); + hb_position_t advance = x_advance + y_advance; + x_offset = -x_offset; + y_offset = -y_offset; + + unsigned int old_len = buffer->len; + for (CFIndex j = range.location; j < range.location + range.length; j++) + { + UniChar ch = CFStringGetCharacterAtIndex (string_ref, j); + if (hb_in_range (ch, 0xDC00u, 0xDFFFu) && range.location < j) + { + ch = CFStringGetCharacterAtIndex (string_ref, j - 1); + if (hb_in_range (ch, 0xD800u, 0xDBFFu)) + /* This is the second of a surrogate pair. Don't need .notdef + * for this one. */ + continue; + } + if (buffer->unicode->is_default_ignorable (ch)) + continue; + + info->codepoint = notdef; + info->cluster = log_clusters[j]; + + info->mask = advance; + info->var1.i32 = x_offset; + info->var2.i32 = y_offset; + + info++; + buffer->len++; + } + if (HB_DIRECTION_IS_BACKWARD (buffer->props.direction)) + buffer->reverse_range (old_len, buffer->len); + advances_so_far += run_advance; + continue; + } + } + + unsigned int num_glyphs = CTRunGetGlyphCount (run); + if (num_glyphs == 0) + continue; + + if (!buffer->ensure_inplace (buffer->len + num_glyphs)) + goto resize_and_retry; + + hb_glyph_info_t *run_info = buffer->info + buffer->len; + + /* Testing used to indicate that CTRunGetGlyphsPtr, etc (almost?) always + * succeed, and so copying data to our own buffer will be rare. Reports + * have it that this changed in OS X 10.10 Yosemite, and nullptr is returned + * frequently. At any rate, we can test that codepath by setting USE_PTR + * to false. */ + +#define USE_PTR true + +#define SCRATCH_SAVE() \ + unsigned int scratch_size_saved = scratch_size; \ + hb_buffer_t::scratch_buffer_t *scratch_saved = scratch + +#define SCRATCH_RESTORE() \ + scratch_size = scratch_size_saved; \ + scratch = scratch_saved + + { /* Setup glyphs */ + SCRATCH_SAVE(); + const CGGlyph* glyphs = USE_PTR ? CTRunGetGlyphsPtr (run) : nullptr; + if (!glyphs) { + ALLOCATE_ARRAY (CGGlyph, glyph_buf, num_glyphs, goto resize_and_retry); + CTRunGetGlyphs (run, range_all, glyph_buf); + glyphs = glyph_buf; + } + const CFIndex* string_indices = USE_PTR ? CTRunGetStringIndicesPtr (run) : nullptr; + if (!string_indices) { + ALLOCATE_ARRAY (CFIndex, index_buf, num_glyphs, goto resize_and_retry); + CTRunGetStringIndices (run, range_all, index_buf); + string_indices = index_buf; + } + hb_glyph_info_t *info = run_info; + for (unsigned int j = 0; j < num_glyphs; j++) + { + info->codepoint = glyphs[j]; + info->cluster = log_clusters[string_indices[j]]; + info++; + } + SCRATCH_RESTORE(); + } + { + /* Setup positions. + * Note that CoreText does not return advances for glyphs. As such, + * for all but last glyph, we use the delta position to next glyph as + * advance (in the advance direction only), and for last glyph we set + * whatever is needed to make the whole run's advance add up. */ + SCRATCH_SAVE(); + const CGPoint* positions = USE_PTR ? CTRunGetPositionsPtr (run) : nullptr; + if (!positions) { + ALLOCATE_ARRAY (CGPoint, position_buf, num_glyphs, goto resize_and_retry); + CTRunGetPositions (run, range_all, position_buf); + positions = position_buf; + } + hb_glyph_info_t *info = run_info; + if (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction)) + { + hb_position_t x_offset = round ((positions[0].x - advances_so_far) * x_mult); + for (unsigned int j = 0; j < num_glyphs; j++) + { + CGFloat advance; + if (likely (j + 1 < num_glyphs)) + advance = positions[j + 1].x - positions[j].x; + else /* last glyph */ + advance = run_advance - (positions[j].x - positions[0].x); + /* int cast necessary to pass through negative values. */ + info->mask = (int) round (advance * x_mult); + info->var1.i32 = x_offset; + info->var2.i32 = round (positions[j].y * y_mult); + info++; + } + } + else + { + hb_position_t y_offset = round ((positions[0].y - advances_so_far) * y_mult); + for (unsigned int j = 0; j < num_glyphs; j++) + { + CGFloat advance; + if (likely (j + 1 < num_glyphs)) + advance = positions[j + 1].y - positions[j].y; + else /* last glyph */ + advance = run_advance - (positions[j].y - positions[0].y); + /* int cast necessary to pass through negative values. */ + info->mask = (int) round (advance * y_mult); + info->var1.i32 = round (positions[j].x * x_mult); + info->var2.i32 = y_offset; + info++; + } + } + SCRATCH_RESTORE(); + advances_so_far += run_advance; + } +#undef SCRATCH_RESTORE +#undef SCRATCH_SAVE +#undef USE_PTR +#undef ALLOCATE_ARRAY + + buffer->len += num_glyphs; + } + + buffer->clear_positions (); + + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; + hb_glyph_position_t *pos = buffer->pos; + if (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction)) + for (unsigned int i = 0; i < count; i++) + { + pos->x_advance = info->mask; + pos->x_offset = info->var1.i32; + pos->y_offset = info->var2.i32; + + info++; pos++; + } + else + for (unsigned int i = 0; i < count; i++) + { + pos->y_advance = info->mask; + pos->x_offset = info->var1.i32; + pos->y_offset = info->var2.i32; + + info++; pos++; + } + + /* Fix up clusters so that we never return out-of-order indices; + * if core text has reordered glyphs, we'll merge them to the + * beginning of the reordered cluster. CoreText is nice enough + * to tell us whenever it has produced nonmonotonic results... + * Note that we assume the input clusters were nonmonotonic to + * begin with. + * + * This does *not* mean we'll form the same clusters as Uniscribe + * or the native OT backend, only that the cluster indices will be + * monotonic in the output buffer. */ + if (count > 1 && (status_or & kCTRunStatusNonMonotonic) && + buffer->cluster_level != HB_BUFFER_CLUSTER_LEVEL_CHARACTERS) + { + hb_glyph_info_t *info = buffer->info; + if (HB_DIRECTION_IS_FORWARD (buffer->props.direction)) + { + unsigned int cluster = info[count - 1].cluster; + for (unsigned int i = count - 1; i > 0; i--) + { + cluster = hb_min (cluster, info[i - 1].cluster); + info[i - 1].cluster = cluster; + } + } + else + { + unsigned int cluster = info[0].cluster; + for (unsigned int i = 1; i < count; i++) + { + cluster = hb_min (cluster, info[i].cluster); + info[i].cluster = cluster; + } + } + } + } + + /* TODO: Sometimes the above positioning code generates negative + * advance values. Fix them up. Example, with NotoNastaliqUrdu + * font and sequence ابهد. */ + + buffer->clear_glyph_flags (); + buffer->unsafe_to_break (); + +#undef FAIL + +fail: + if (string_ref) + CFRelease (string_ref); + if (line) + CFRelease (line); + + for (unsigned int i = 0; i < range_records.length; i++) + if (range_records[i].font) + CFRelease (range_records[i].font); + + return ret; +} + + +#endif diff --git a/libs/harfbuzz/src/hb-coretext.h b/libs/harfbuzz/src/hb-coretext.h new file mode 100644 index 00000000..e53dbaf2 --- /dev/null +++ b/libs/harfbuzz/src/hb-coretext.h @@ -0,0 +1,96 @@ +/* + * Copyright © 2012 Mozilla Foundation. + * + * 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. + * + * Mozilla Author(s): Jonathan Kew + */ + +#ifndef HB_CORETEXT_H +#define HB_CORETEXT_H + +#include "hb.h" + +#include +#if TARGET_OS_IPHONE +# include +# include +#else +# include +#endif + +HB_BEGIN_DECLS + + +/** + * HB_CORETEXT_TAG_MORT: + * + * The #hb_tag_t tag for the `mort` (glyph metamorphosis) table, + * which holds AAT features. + * + * For more information, see + * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6mort.html + * + **/ +#define HB_CORETEXT_TAG_MORT HB_TAG('m','o','r','t') + +/** + * HB_CORETEXT_TAG_MORX: + * + * The #hb_tag_t tag for the `morx` (extended glyph metamorphosis) + * table, which holds AAT features. + * + * For more information, see + * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6morx.html + * + **/ +#define HB_CORETEXT_TAG_MORX HB_TAG('m','o','r','x') + +/** + * HB_CORETEXT_TAG_KERX: + * + * The #hb_tag_t tag for the `kerx` (extended kerning) table, which + * holds AAT kerning information. + * + * For more information, see + * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6kerx.html + * + **/ +#define HB_CORETEXT_TAG_KERX HB_TAG('k','e','r','x') + + +HB_EXTERN hb_face_t * +hb_coretext_face_create (CGFontRef cg_font); + +HB_EXTERN hb_font_t * +hb_coretext_font_create (CTFontRef ct_font); + + +HB_EXTERN CGFontRef +hb_coretext_face_get_cg_font (hb_face_t *face); + +HB_EXTERN CTFontRef +hb_coretext_font_get_ct_font (hb_font_t *font); + + +HB_END_DECLS + +#endif /* HB_CORETEXT_H */ diff --git a/libs/harfbuzz/src/hb-cplusplus.hh b/libs/harfbuzz/src/hb-cplusplus.hh new file mode 100644 index 00000000..a210ab79 --- /dev/null +++ b/libs/harfbuzz/src/hb-cplusplus.hh @@ -0,0 +1,221 @@ +/* + * Copyright © 2022 Behdad Esfahbod + * + * 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. + */ + +#ifndef HB_CPLUSPLUS_HH +#define HB_CPLUSPLUS_HH + +#include "hb.h" + +HB_BEGIN_DECLS +HB_END_DECLS + +#ifdef __cplusplus + +#include +#include + +#if 0 +#if !(__cplusplus >= 201103L) +#error "HarfBuzz C++ helpers require C++11" +#endif +#endif + +namespace hb { + + +template +struct vtable; + +template +struct shared_ptr +{ + using element_type = T; + + using v = vtable; + + explicit shared_ptr (T *p = nullptr) : p (p) {} + shared_ptr (const shared_ptr &o) : p (v::reference (o.p)) {} + shared_ptr (shared_ptr &&o) : p (o.p) { o.p = nullptr; } + shared_ptr& operator = (const shared_ptr &o) { if (p != o.p) { destroy (); p = o.p; reference (); } return *this; } + shared_ptr& operator = (shared_ptr &&o) { v::destroy (p); p = o.p; o.p = nullptr; return *this; } + ~shared_ptr () { v::destroy (p); p = nullptr; } + + T* get() const { return p; } + + void swap (shared_ptr &o) { std::swap (p, o.p); } + friend void swap (shared_ptr &a, shared_ptr &b) { std::swap (a.p, b.p); } + + operator T * () const { return p; } + T& operator * () const { return *get (); } + T* operator -> () const { return get (); } + operator bool () const { return p; } + bool operator == (const shared_ptr &o) const { return p == o.p; } + bool operator != (const shared_ptr &o) const { return p != o.p; } + + static T* get_empty() { return v::get_empty (); } + T* reference() { return v::reference (p); } + void destroy() { v::destroy (p); } + void set_user_data (hb_user_data_key_t *key, + void *value, + hb_destroy_func_t destroy, + hb_bool_t replace) { v::set_user_data (p, key, value, destroy, replace); } + void * get_user_data (hb_user_data_key_t *key) { return v::get_user_data (p, key); } + + private: + T *p; +}; + +template struct is_shared_ptr : std::false_type {}; +template struct is_shared_ptr> : std::true_type {}; + +template +struct unique_ptr +{ + using element_type = T; + + using v = vtable; + + explicit unique_ptr (T *p = nullptr) : p (p) {} + unique_ptr (const unique_ptr &o) = delete; + unique_ptr (unique_ptr &&o) : p (o.p) { o.p = nullptr; } + unique_ptr& operator = (const unique_ptr &o) = delete; + unique_ptr& operator = (unique_ptr &&o) { v::destroy (p); p = o.p; o.p = nullptr; return *this; } + ~unique_ptr () { v::destroy (p); p = nullptr; } + + T* get() const { return p; } + T* release () { T* v = p; p = nullptr; return v; } + + void swap (unique_ptr &o) { std::swap (p, o.p); } + friend void swap (unique_ptr &a, unique_ptr &b) { std::swap (a.p, b.p); } + + operator T * () const { return p; } + T& operator * () const { return *get (); } + T* operator -> () const { return get (); } + operator bool () { return p; } + + private: + T *p; +}; + +template struct is_unique_ptr : std::false_type {}; +template struct is_unique_ptr> : std::true_type {}; + +template +struct vtable_t +{ + static constexpr auto get_empty = _get_empty; + static constexpr auto reference = _reference; + static constexpr auto destroy = _destroy; + static constexpr auto set_user_data = _set_user_data; + static constexpr auto get_user_data = _get_user_data; +}; + +#define HB_DEFINE_VTABLE(name) \ + template<> \ + struct vtable \ + : vtable_t {} + +HB_DEFINE_VTABLE (buffer); +HB_DEFINE_VTABLE (blob); +HB_DEFINE_VTABLE (face); +HB_DEFINE_VTABLE (font); +HB_DEFINE_VTABLE (font_funcs); +HB_DEFINE_VTABLE (map); +HB_DEFINE_VTABLE (set); +HB_DEFINE_VTABLE (shape_plan); +HB_DEFINE_VTABLE (unicode_funcs); + +#undef HB_DEFINE_VTABLE + + +#ifdef HB_SUBSET_H + +#define HB_DEFINE_VTABLE(name) \ + template<> \ + struct vtable \ + : vtable_t {} + + +HB_DEFINE_VTABLE (subset_input); +HB_DEFINE_VTABLE (subset_plan); + +#undef HB_DEFINE_VTABLE + +#endif + + +} // namespace hb + +/* Workaround for GCC < 7, see: + * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56480 + * https://stackoverflow.com/a/25594741 */ +namespace std { + + +template +struct hash> +{ + std::size_t operator()(const hb::shared_ptr& v) const noexcept + { + std::size_t h = std::hash{}(v.get ()); + return h; + } +}; + +template +struct hash> +{ + std::size_t operator()(const hb::unique_ptr& v) const noexcept + { + std::size_t h = std::hash{}(v.get ()); + return h; + } +}; + + +} // namespace std + +#endif /* __cplusplus */ + +#endif /* HB_CPLUSPLUS_HH */ diff --git a/libs/harfbuzz/src/hb-debug.hh b/libs/harfbuzz/src/hb-debug.hh new file mode 100644 index 00000000..cbe13e52 --- /dev/null +++ b/libs/harfbuzz/src/hb-debug.hh @@ -0,0 +1,468 @@ +/* + * Copyright © 2017 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 HB_DEBUG_HH +#define HB_DEBUG_HH + +#include "hb.hh" +#include "hb-atomic.hh" +#include "hb-algs.hh" + + +#ifndef HB_DEBUG +#define HB_DEBUG 0 +#endif + + +/* + * Global runtime options. + */ + +struct hb_options_t +{ + bool unused : 1; /* In-case sign bit is here. */ + bool initialized : 1; + bool uniscribe_bug_compatible : 1; +}; + +union hb_options_union_t { + int i; + hb_options_t opts; +}; +static_assert ((sizeof (hb_atomic_int_t) >= sizeof (hb_options_union_t)), ""); + +HB_INTERNAL void +_hb_options_init (); + +extern HB_INTERNAL hb_atomic_int_t _hb_options; + +static inline hb_options_t +hb_options () +{ +#ifdef HB_NO_GETENV + return hb_options_t (); +#endif + /* Make a local copy, so we can access bitfield threadsafely. */ + hb_options_union_t u; + u.i = _hb_options; + + if (unlikely (!u.i)) + { + _hb_options_init (); + u.i = _hb_options; + } + + return u.opts; +} + + +/* + * Debug output (needs enabling at compile time.) + */ + +static inline bool +_hb_debug (unsigned int level, + unsigned int max_level) +{ + return level < max_level; +} + +#define DEBUG_LEVEL_ENABLED(WHAT, LEVEL) (_hb_debug ((LEVEL), HB_DEBUG_##WHAT)) +#define DEBUG_ENABLED(WHAT) (DEBUG_LEVEL_ENABLED (WHAT, 0)) + +static inline void +_hb_print_func (const char *func) +{ + if (func) + { + unsigned int func_len = strlen (func); + /* Skip "static" */ + if (0 == strncmp (func, "static ", 7)) + func += 7; + /* Skip "typename" */ + if (0 == strncmp (func, "typename ", 9)) + func += 9; + /* Skip return type */ + const char *space = strchr (func, ' '); + if (space) + func = space + 1; + /* Skip parameter list */ + const char *paren = strchr (func, '('); + if (paren) + func_len = paren - func; + fprintf (stderr, "%.*s", func_len, func); + } +} + +template static inline void +_hb_debug_msg_va (const char *what, + const void *obj, + const char *func, + bool indented, + unsigned int level, + int level_dir, + const char *message, + va_list ap) HB_PRINTF_FUNC(7, 0); +template static inline void +_hb_debug_msg_va (const char *what, + const void *obj, + const char *func, + bool indented, + unsigned int level, + int level_dir, + const char *message, + va_list ap) +{ + if (!_hb_debug (level, max_level)) + return; + + fprintf (stderr, "%-10s", what ? what : ""); + + if (obj) + fprintf (stderr, "(%*p) ", (unsigned int) (2 * sizeof (void *)), obj); + else + fprintf (stderr, " %*s ", (unsigned int) (2 * sizeof (void *)), ""); + + if (indented) { +#define VBAR "\342\224\202" /* U+2502 BOX DRAWINGS LIGHT VERTICAL */ +#define VRBAR "\342\224\234" /* U+251C BOX DRAWINGS LIGHT VERTICAL AND RIGHT */ +#define DLBAR "\342\225\256" /* U+256E BOX DRAWINGS LIGHT ARC DOWN AND LEFT */ +#define ULBAR "\342\225\257" /* U+256F BOX DRAWINGS LIGHT ARC UP AND LEFT */ +#define LBAR "\342\225\264" /* U+2574 BOX DRAWINGS LIGHT LEFT */ + static const char bars[] = + VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR + VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR + VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR + VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR + VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR; + fprintf (stderr, "%2u %s" VRBAR "%s", + level, + bars + sizeof (bars) - 1 - hb_min ((unsigned int) sizeof (bars) - 1, (unsigned int) (sizeof (VBAR) - 1) * level), + level_dir ? (level_dir > 0 ? DLBAR : ULBAR) : LBAR); + } else + fprintf (stderr, " " VRBAR LBAR); + + _hb_print_func (func); + + if (message) + { + fprintf (stderr, ": "); + vfprintf (stderr, message, ap); + } + + fprintf (stderr, "\n"); +} +template <> inline void HB_PRINTF_FUNC(7, 0) +_hb_debug_msg_va<0> (const char *what HB_UNUSED, + const void *obj HB_UNUSED, + const char *func HB_UNUSED, + bool indented HB_UNUSED, + unsigned int level HB_UNUSED, + int level_dir HB_UNUSED, + const char *message HB_UNUSED, + va_list ap HB_UNUSED) {} + +template static inline void +_hb_debug_msg (const char *what, + const void *obj, + const char *func, + bool indented, + unsigned int level, + int level_dir, + const char *message, + ...) HB_PRINTF_FUNC(7, 8); +template static inline void HB_PRINTF_FUNC(7, 8) +_hb_debug_msg (const char *what, + const void *obj, + const char *func, + bool indented, + unsigned int level, + int level_dir, + const char *message, + ...) +{ + va_list ap; + va_start (ap, message); + _hb_debug_msg_va (what, obj, func, indented, level, level_dir, message, ap); + va_end (ap); +} +template <> inline void +_hb_debug_msg<0> (const char *what HB_UNUSED, + const void *obj HB_UNUSED, + const char *func HB_UNUSED, + bool indented HB_UNUSED, + unsigned int level HB_UNUSED, + int level_dir HB_UNUSED, + const char *message HB_UNUSED, + ...) HB_PRINTF_FUNC(7, 8); +template <> inline void HB_PRINTF_FUNC(7, 8) +_hb_debug_msg<0> (const char *what HB_UNUSED, + const void *obj HB_UNUSED, + const char *func HB_UNUSED, + bool indented HB_UNUSED, + unsigned int level HB_UNUSED, + int level_dir HB_UNUSED, + const char *message HB_UNUSED, + ...) {} + +#define DEBUG_MSG_LEVEL(WHAT, OBJ, LEVEL, LEVEL_DIR, ...) _hb_debug_msg (#WHAT, (OBJ), nullptr, true, (LEVEL), (LEVEL_DIR), __VA_ARGS__) +#define DEBUG_MSG(WHAT, OBJ, ...) _hb_debug_msg (#WHAT, (OBJ), nullptr, false, 0, 0, __VA_ARGS__) +#define DEBUG_MSG_FUNC(WHAT, OBJ, ...) _hb_debug_msg (#WHAT, (OBJ), HB_FUNC, false, 0, 0, __VA_ARGS__) + + +/* + * Printer + */ + +template +struct hb_printer_t { + const char *print (const T&) { return "something"; } +}; + +template <> +struct hb_printer_t { + const char *print (bool v) { return v ? "true" : "false"; } +}; + +template <> +struct hb_printer_t { + const char *print (hb_empty_t) { return ""; } +}; + + +/* + * Trace + */ + +template +static inline void _hb_warn_no_return (bool returned) +{ + if (unlikely (!returned)) { + fprintf (stderr, "OUCH, returned with no call to return_trace(). This is a bug, please report.\n"); + } +} +template <> +/*static*/ inline void _hb_warn_no_return (bool returned HB_UNUSED) +{} + +template +struct hb_auto_trace_t +{ + explicit inline hb_auto_trace_t (unsigned int *plevel_, + const char *what_, + const void *obj_, + const char *func, + const char *message, + ...) HB_PRINTF_FUNC(6, 7) + : plevel (plevel_), what (what_), obj (obj_), returned (false) + { + if (plevel) ++*plevel; + + va_list ap; + va_start (ap, message); + _hb_debug_msg_va (what, obj, func, true, plevel ? *plevel : 0, +1, message, ap); + va_end (ap); + } + ~hb_auto_trace_t () + { + _hb_warn_no_return (returned); + if (!returned) { + _hb_debug_msg (what, obj, nullptr, true, plevel ? *plevel : 1, -1, " "); + } + if (plevel) --*plevel; + } + + template + T ret (T&& v, + const char *func = "", + unsigned int line = 0) + { + if (unlikely (returned)) { + fprintf (stderr, "OUCH, double calls to return_trace(). This is a bug, please report.\n"); + return std::forward (v); + } + + _hb_debug_msg (what, obj, func, true, plevel ? *plevel : 1, -1, + "return %s (line %d)", + hb_printer_t>().print (v), line); + if (plevel) --*plevel; + plevel = nullptr; + returned = true; + return std::forward (v); + } + + private: + unsigned int *plevel; + const char *what; + const void *obj; + bool returned; +}; +template /* Make sure we don't use hb_auto_trace_t when not tracing. */ +struct hb_auto_trace_t<0, ret_t> +{ + explicit inline hb_auto_trace_t (unsigned int *plevel_, + const char *what_, + const void *obj_, + const char *func, + const char *message, + ...) HB_PRINTF_FUNC(6, 7) {} + + template + T ret (T&& v, + const char *func HB_UNUSED = nullptr, + unsigned int line HB_UNUSED = 0) { return std::forward (v); } +}; + +/* For disabled tracing; optimize out everything. + * https://github.com/harfbuzz/harfbuzz/pull/605 */ +template +struct hb_no_trace_t { + template + T ret (T&& v, + const char *func HB_UNUSED = nullptr, + unsigned int line HB_UNUSED = 0) { return std::forward (v); } +}; + +#define return_trace(RET) return trace.ret (RET, HB_FUNC, __LINE__) + + +/* + * Instances. + */ + +#ifndef HB_DEBUG_ARABIC +#define HB_DEBUG_ARABIC (HB_DEBUG+0) +#endif + +#ifndef HB_DEBUG_BLOB +#define HB_DEBUG_BLOB (HB_DEBUG+0) +#endif + +#ifndef HB_DEBUG_CORETEXT +#define HB_DEBUG_CORETEXT (HB_DEBUG+0) +#endif + +#ifndef HB_DEBUG_DIRECTWRITE +#define HB_DEBUG_DIRECTWRITE (HB_DEBUG+0) +#endif + +#ifndef HB_DEBUG_FT +#define HB_DEBUG_FT (HB_DEBUG+0) +#endif + +#ifndef HB_DEBUG_OBJECT +#define HB_DEBUG_OBJECT (HB_DEBUG+0) +#endif + +#ifndef HB_DEBUG_SHAPE_PLAN +#define HB_DEBUG_SHAPE_PLAN (HB_DEBUG+0) +#endif + +#ifndef HB_DEBUG_UNISCRIBE +#define HB_DEBUG_UNISCRIBE (HB_DEBUG+0) +#endif + +/* + * With tracing. + */ + +#ifndef HB_DEBUG_APPLY +#define HB_DEBUG_APPLY (HB_DEBUG+0) +#endif +#if HB_DEBUG_APPLY +#define TRACE_APPLY(this) \ + hb_auto_trace_t trace \ + (&c->debug_depth, c->get_name (), this, HB_FUNC, \ + "idx %d gid %u lookup %d", \ + c->buffer->idx, c->buffer->cur().codepoint, (int) c->lookup_index) +#else +#define TRACE_APPLY(this) hb_no_trace_t trace +#endif + +#ifndef HB_DEBUG_SANITIZE +#define HB_DEBUG_SANITIZE (HB_DEBUG+0) +#endif +#if HB_DEBUG_SANITIZE +#define TRACE_SANITIZE(this) \ + hb_auto_trace_t trace \ + (&c->debug_depth, c->get_name (), this, HB_FUNC, \ + " ") +#else +#define TRACE_SANITIZE(this) hb_no_trace_t trace +#endif + +#ifndef HB_DEBUG_SERIALIZE +#define HB_DEBUG_SERIALIZE (HB_DEBUG+0) +#endif +#if HB_DEBUG_SERIALIZE +#define TRACE_SERIALIZE(this) \ + hb_auto_trace_t trace \ + (&c->debug_depth, "SERIALIZE", c, HB_FUNC, \ + " ") +#else +#define TRACE_SERIALIZE(this) hb_no_trace_t trace +#endif + +#ifndef HB_DEBUG_SUBSET +#define HB_DEBUG_SUBSET (HB_DEBUG+0) +#endif +#if HB_DEBUG_SUBSET +#define TRACE_SUBSET(this) \ + hb_auto_trace_t trace \ + (&c->debug_depth, c->get_name (), this, HB_FUNC, \ + " ") +#else +#define TRACE_SUBSET(this) hb_no_trace_t trace +#endif + +#ifndef HB_DEBUG_SUBSET_REPACK +#define HB_DEBUG_SUBSET_REPACK (HB_DEBUG+0) +#endif + +#ifndef HB_DEBUG_DISPATCH +#define HB_DEBUG_DISPATCH ( \ + HB_DEBUG_APPLY + \ + HB_DEBUG_SANITIZE + \ + HB_DEBUG_SERIALIZE + \ + HB_DEBUG_SUBSET + \ + 0) +#endif +#if HB_DEBUG_DISPATCH +#define TRACE_DISPATCH(this, format) \ + hb_auto_trace_t trace \ + (&c->debug_depth, c->get_name (), this, HB_FUNC, \ + "format %d", (int) format) +#else +#define TRACE_DISPATCH(this, format) hb_no_trace_t trace +#endif + + +#ifndef HB_BUFFER_MESSAGE_MORE +#define HB_BUFFER_MESSAGE_MORE (HB_DEBUG+1) +#endif + + +#endif /* HB_DEBUG_HH */ diff --git a/libs/harfbuzz/src/hb-deprecated.h b/libs/harfbuzz/src/hb-deprecated.h new file mode 100644 index 00000000..333dc3cd --- /dev/null +++ b/libs/harfbuzz/src/hb-deprecated.h @@ -0,0 +1,251 @@ +/* + * Copyright © 2013 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 + */ + +#if !defined(HB_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR) +#error "Include instead." +#endif + +#ifndef HB_DEPRECATED_H +#define HB_DEPRECATED_H + +#include "hb-common.h" +#include "hb-unicode.h" +#include "hb-font.h" +#include "hb-set.h" + + +/** + * SECTION:hb-deprecated + * @title: hb-deprecated + * @short_description: Deprecated API + * @include: hb.h + * + * These API have been deprecated in favor of newer API, or because they + * were deemed unnecessary. + **/ + + +HB_BEGIN_DECLS + +#ifndef HB_DISABLE_DEPRECATED + + +/** + * HB_SCRIPT_CANADIAN_ABORIGINAL: + * + * Use #HB_SCRIPT_CANADIAN_SYLLABICS instead: + * + * Deprecated: 0.9.20 + */ +#define HB_SCRIPT_CANADIAN_ABORIGINAL HB_SCRIPT_CANADIAN_SYLLABICS + +/** + * HB_BUFFER_FLAGS_DEFAULT: + * + * Use #HB_BUFFER_FLAG_DEFAULT instead. + * + * Deprecated: 0.9.20 + */ +#define HB_BUFFER_FLAGS_DEFAULT HB_BUFFER_FLAG_DEFAULT +/** + * HB_BUFFER_SERIALIZE_FLAGS_DEFAULT: + * + * Use #HB_BUFFER_SERIALIZE_FLAG_DEFAULT instead. + * + * Deprecated: 0.9.20 + */ +#define HB_BUFFER_SERIALIZE_FLAGS_DEFAULT HB_BUFFER_SERIALIZE_FLAG_DEFAULT + +/** + * hb_font_get_glyph_func_t: + * @font: #hb_font_t to work upon + * @font_data: @font user data pointer + * @unicode: The Unicode code point to query + * @variation_selector: The variation-selector code point to query + * @glyph: (out): The glyph ID retrieved + * @user_data: User data pointer passed by the caller + * + * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. + * + * This method should retrieve the glyph ID for a specified Unicode code point + * font, with an optional variation selector. + * + * Return value: `true` if data found, `false` otherwise + * Deprecated: 1.2.3 + * + **/ +typedef hb_bool_t (*hb_font_get_glyph_func_t) (hb_font_t *font, void *font_data, + hb_codepoint_t unicode, hb_codepoint_t variation_selector, + hb_codepoint_t *glyph, + void *user_data); + +HB_EXTERN HB_DEPRECATED_FOR(hb_font_funcs_set_nominal_glyph_func and hb_font_funcs_set_variation_glyph_func) void +hb_font_funcs_set_glyph_func (hb_font_funcs_t *ffuncs, + hb_font_get_glyph_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_unicode_eastasian_width_func_t: + * @ufuncs: A Unicode-functions structure + * @unicode: The code point to query + * @user_data: User data pointer passed by the caller + * + * A virtual method for the #hb_unicode_funcs_t structure. + * + * Deprecated: 2.0.0 + */ +typedef unsigned int (*hb_unicode_eastasian_width_func_t) (hb_unicode_funcs_t *ufuncs, + hb_codepoint_t unicode, + void *user_data); + +/** + * hb_unicode_funcs_set_eastasian_width_func: + * @ufuncs: a Unicode-function structure + * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign + * @user_data: Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is not needed anymore + * + * Sets the implementation function for #hb_unicode_eastasian_width_func_t. + * + * Since: 0.9.2 + * Deprecated: 2.0.0 + **/ +HB_EXTERN HB_DEPRECATED void +hb_unicode_funcs_set_eastasian_width_func (hb_unicode_funcs_t *ufuncs, + hb_unicode_eastasian_width_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_unicode_eastasian_width: + * @ufuncs: a Unicode-function structure + * @unicode: The code point to query + * + * Don't use. Not used by HarfBuzz. + * + * Since: 0.9.2 + * Deprecated: 2.0.0 + **/ +HB_EXTERN HB_DEPRECATED unsigned int +hb_unicode_eastasian_width (hb_unicode_funcs_t *ufuncs, + hb_codepoint_t unicode); + + +/** + * hb_unicode_decompose_compatibility_func_t: + * @ufuncs: a Unicode function structure + * @u: codepoint to decompose + * @decomposed: address of codepoint array (of length #HB_UNICODE_MAX_DECOMPOSITION_LEN) to write decomposition into + * @user_data: user data pointer as passed to hb_unicode_funcs_set_decompose_compatibility_func() + * + * Fully decompose @u to its Unicode compatibility decomposition. The codepoints of the decomposition will be written to @decomposed. + * The complete length of the decomposition will be returned. + * + * If @u has no compatibility decomposition, zero should be returned. + * + * The Unicode standard guarantees that a buffer of length #HB_UNICODE_MAX_DECOMPOSITION_LEN codepoints will always be sufficient for any + * compatibility decomposition plus an terminating value of 0. Consequently, @decompose must be allocated by the caller to be at least this length. Implementations + * of this function type must ensure that they do not write past the provided array. + * + * Return value: number of codepoints in the full compatibility decomposition of @u, or 0 if no decomposition available. + * + * Deprecated: 2.0.0 + */ +typedef unsigned int (*hb_unicode_decompose_compatibility_func_t) (hb_unicode_funcs_t *ufuncs, + hb_codepoint_t u, + hb_codepoint_t *decomposed, + void *user_data); + +/** + * HB_UNICODE_MAX_DECOMPOSITION_LEN: + * + * See Unicode 6.1 for details on the maximum decomposition length. + * + * Deprecated: 2.0.0 + */ +#define HB_UNICODE_MAX_DECOMPOSITION_LEN (18+1) /* codepoints */ + +/** + * hb_unicode_funcs_set_decompose_compatibility_func: + * @ufuncs: A Unicode-functions structure + * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign + * @user_data: Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is not needed anymore + * + * Sets the implementation function for #hb_unicode_decompose_compatibility_func_t. + * + * + * + * Since: 0.9.2 + * Deprecated: 2.0.0 + **/ +HB_EXTERN HB_DEPRECATED void +hb_unicode_funcs_set_decompose_compatibility_func (hb_unicode_funcs_t *ufuncs, + hb_unicode_decompose_compatibility_func_t func, + void *user_data, hb_destroy_func_t destroy); + +HB_EXTERN HB_DEPRECATED unsigned int +hb_unicode_decompose_compatibility (hb_unicode_funcs_t *ufuncs, + hb_codepoint_t u, + hb_codepoint_t *decomposed); + + +/** + * hb_font_get_glyph_v_kerning_func_t: + * + * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. + * + * This method should retrieve the kerning-adjustment value for a glyph-pair in + * the specified font, for vertical text segments. + * + **/ +typedef hb_font_get_glyph_kerning_func_t hb_font_get_glyph_v_kerning_func_t; + +/** + * hb_font_funcs_set_glyph_v_kerning_func: + * @ffuncs: A font-function structure + * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign + * @user_data: Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is not needed anymore + * + * Sets the implementation function for #hb_font_get_glyph_v_kerning_func_t. + * + * Since: 0.9.2 + * Deprecated: 2.0.0 + **/ +HB_EXTERN void +hb_font_funcs_set_glyph_v_kerning_func (hb_font_funcs_t *ffuncs, + hb_font_get_glyph_v_kerning_func_t func, + void *user_data, hb_destroy_func_t destroy); + +HB_EXTERN hb_position_t +hb_font_get_glyph_v_kerning (hb_font_t *font, + hb_codepoint_t top_glyph, hb_codepoint_t bottom_glyph); + +#endif + +HB_END_DECLS + +#endif /* HB_DEPRECATED_H */ diff --git a/libs/harfbuzz/src/hb-directwrite.cc b/libs/harfbuzz/src/hb-directwrite.cc new file mode 100644 index 00000000..de05b7d8 --- /dev/null +++ b/libs/harfbuzz/src/hb-directwrite.cc @@ -0,0 +1,888 @@ +/* + * Copyright © 2015-2019 Ebrahim Byagowi + * + * 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. + */ + +#include "hb.hh" + +#ifdef HAVE_DIRECTWRITE + +#include "hb-shaper-impl.hh" + +#include + +#include "hb-directwrite.h" + +#include "hb-ms-feature-ranges.hh" + +/** + * SECTION:hb-directwrite + * @title: hb-directwrite + * @short_description: DirectWrite integration + * @include: hb-directwrite.h + * + * Functions for using HarfBuzz with DirectWrite fonts. + **/ + +/* Declare object creator for dynamic support of DWRITE */ +typedef HRESULT (WINAPI *t_DWriteCreateFactory)( + DWRITE_FACTORY_TYPE factoryType, + REFIID iid, + IUnknown **factory +); + + +/* + * DirectWrite font stream helpers + */ + +// This is a font loader which provides only one font (unlike its original design). +// For a better implementation which was also source of this +// and DWriteFontFileStream, have a look at to NativeFontResourceDWrite.cpp in Mozilla +class DWriteFontFileLoader : public IDWriteFontFileLoader +{ +private: + IDWriteFontFileStream *mFontFileStream; +public: + DWriteFontFileLoader (IDWriteFontFileStream *fontFileStream) + { mFontFileStream = fontFileStream; } + + // IUnknown interface + IFACEMETHOD (QueryInterface) (IID const& iid, OUT void** ppObject) + { return S_OK; } + IFACEMETHOD_ (ULONG, AddRef) () { return 1; } + IFACEMETHOD_ (ULONG, Release) () { return 1; } + + // IDWriteFontFileLoader methods + virtual HRESULT STDMETHODCALLTYPE + CreateStreamFromKey (void const* fontFileReferenceKey, + uint32_t fontFileReferenceKeySize, + OUT IDWriteFontFileStream** fontFileStream) + { + *fontFileStream = mFontFileStream; + return S_OK; + } + + virtual ~DWriteFontFileLoader() {} +}; + +class DWriteFontFileStream : public IDWriteFontFileStream +{ +private: + uint8_t *mData; + uint32_t mSize; +public: + DWriteFontFileStream (uint8_t *aData, uint32_t aSize) + { + mData = aData; + mSize = aSize; + } + + // IUnknown interface + IFACEMETHOD (QueryInterface) (IID const& iid, OUT void** ppObject) + { return S_OK; } + IFACEMETHOD_ (ULONG, AddRef) () { return 1; } + IFACEMETHOD_ (ULONG, Release) () { return 1; } + + // IDWriteFontFileStream methods + virtual HRESULT STDMETHODCALLTYPE + ReadFileFragment (void const** fragmentStart, + UINT64 fileOffset, + UINT64 fragmentSize, + OUT void** fragmentContext) + { + // We are required to do bounds checking. + if (fileOffset + fragmentSize > mSize) return E_FAIL; + + // truncate the 64 bit fileOffset to size_t sized index into mData + size_t index = static_cast (fileOffset); + + // We should be alive for the duration of this. + *fragmentStart = &mData[index]; + *fragmentContext = nullptr; + return S_OK; + } + + virtual void STDMETHODCALLTYPE + ReleaseFileFragment (void* fragmentContext) {} + + virtual HRESULT STDMETHODCALLTYPE + GetFileSize (OUT UINT64* fileSize) + { + *fileSize = mSize; + return S_OK; + } + + virtual HRESULT STDMETHODCALLTYPE + GetLastWriteTime (OUT UINT64* lastWriteTime) { return E_NOTIMPL; } + + virtual ~DWriteFontFileStream() {} +}; + + +/* +* shaper face data +*/ + +struct hb_directwrite_face_data_t +{ + HMODULE dwrite_dll; + IDWriteFactory *dwriteFactory; + IDWriteFontFile *fontFile; + DWriteFontFileStream *fontFileStream; + DWriteFontFileLoader *fontFileLoader; + IDWriteFontFace *fontFace; + hb_blob_t *faceBlob; +}; + +hb_directwrite_face_data_t * +_hb_directwrite_shaper_face_data_create (hb_face_t *face) +{ + hb_directwrite_face_data_t *data = new hb_directwrite_face_data_t; + if (unlikely (!data)) + return nullptr; + +#define FAIL(...) \ + HB_STMT_START { \ + DEBUG_MSG (DIRECTWRITE, nullptr, __VA_ARGS__); \ + return nullptr; \ + } HB_STMT_END + + data->dwrite_dll = LoadLibrary (TEXT ("DWRITE")); + if (unlikely (!data->dwrite_dll)) + FAIL ("Cannot find DWrite.DLL"); + + t_DWriteCreateFactory p_DWriteCreateFactory; + +#if defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-function-type" +#endif + + p_DWriteCreateFactory = (t_DWriteCreateFactory) + GetProcAddress (data->dwrite_dll, "DWriteCreateFactory"); + +#if defined(__GNUC__) +#pragma GCC diagnostic pop +#endif + + if (unlikely (!p_DWriteCreateFactory)) + FAIL ("Cannot find DWriteCreateFactory()."); + + HRESULT hr; + + // TODO: factory and fontFileLoader should be cached separately + IDWriteFactory* dwriteFactory; + hr = p_DWriteCreateFactory (DWRITE_FACTORY_TYPE_SHARED, __uuidof (IDWriteFactory), + (IUnknown**) &dwriteFactory); + + if (unlikely (hr != S_OK)) + FAIL ("Failed to run DWriteCreateFactory()."); + + hb_blob_t *blob = hb_face_reference_blob (face); + DWriteFontFileStream *fontFileStream; + fontFileStream = new DWriteFontFileStream ((uint8_t *) hb_blob_get_data (blob, nullptr), + hb_blob_get_length (blob)); + + DWriteFontFileLoader *fontFileLoader = new DWriteFontFileLoader (fontFileStream); + dwriteFactory->RegisterFontFileLoader (fontFileLoader); + + IDWriteFontFile *fontFile; + uint64_t fontFileKey = 0; + hr = dwriteFactory->CreateCustomFontFileReference (&fontFileKey, sizeof (fontFileKey), + fontFileLoader, &fontFile); + + if (FAILED (hr)) + FAIL ("Failed to load font file from data!"); + + BOOL isSupported; + DWRITE_FONT_FILE_TYPE fileType; + DWRITE_FONT_FACE_TYPE faceType; + uint32_t numberOfFaces; + hr = fontFile->Analyze (&isSupported, &fileType, &faceType, &numberOfFaces); + if (FAILED (hr) || !isSupported) + FAIL ("Font file is not supported."); + +#undef FAIL + + IDWriteFontFace *fontFace; + dwriteFactory->CreateFontFace (faceType, 1, &fontFile, 0, + DWRITE_FONT_SIMULATIONS_NONE, &fontFace); + + data->dwriteFactory = dwriteFactory; + data->fontFile = fontFile; + data->fontFileStream = fontFileStream; + data->fontFileLoader = fontFileLoader; + data->fontFace = fontFace; + data->faceBlob = blob; + + return data; +} + +void +_hb_directwrite_shaper_face_data_destroy (hb_directwrite_face_data_t *data) +{ + if (data->fontFace) + data->fontFace->Release (); + if (data->fontFile) + data->fontFile->Release (); + if (data->dwriteFactory) + { + if (data->fontFileLoader) + data->dwriteFactory->UnregisterFontFileLoader (data->fontFileLoader); + data->dwriteFactory->Release (); + } + if (data->fontFileLoader) + delete data->fontFileLoader; + if (data->fontFileStream) + delete data->fontFileStream; + if (data->faceBlob) + hb_blob_destroy (data->faceBlob); + if (data->dwrite_dll) + FreeLibrary (data->dwrite_dll); + if (data) + delete data; +} + + +/* + * shaper font data + */ + +struct hb_directwrite_font_data_t {}; + +hb_directwrite_font_data_t * +_hb_directwrite_shaper_font_data_create (hb_font_t *font) +{ + return (hb_directwrite_font_data_t *) HB_SHAPER_DATA_SUCCEEDED; +} + +void +_hb_directwrite_shaper_font_data_destroy (hb_directwrite_font_data_t *data) +{ +} + + +// Most of TextAnalysis is originally written by Bas Schouten for Mozilla project +// but now is relicensed to MIT for HarfBuzz use +class TextAnalysis : public IDWriteTextAnalysisSource, public IDWriteTextAnalysisSink +{ +public: + + IFACEMETHOD (QueryInterface) (IID const& iid, OUT void** ppObject) + { return S_OK; } + IFACEMETHOD_ (ULONG, AddRef) () { return 1; } + IFACEMETHOD_ (ULONG, Release) () { return 1; } + + // A single contiguous run of characters containing the same analysis + // results. + struct Run + { + uint32_t mTextStart; // starting text position of this run + uint32_t mTextLength; // number of contiguous code units covered + uint32_t mGlyphStart; // starting glyph in the glyphs array + uint32_t mGlyphCount; // number of glyphs associated with this run + // text + DWRITE_SCRIPT_ANALYSIS mScript; + uint8_t mBidiLevel; + bool mIsSideways; + + bool ContainsTextPosition (uint32_t aTextPosition) const + { + return aTextPosition >= mTextStart && + aTextPosition < mTextStart + mTextLength; + } + + Run *nextRun; + }; + +public: + TextAnalysis (const wchar_t* text, uint32_t textLength, + const wchar_t* localeName, DWRITE_READING_DIRECTION readingDirection) + : mTextLength (textLength), mText (text), mLocaleName (localeName), + mReadingDirection (readingDirection), mCurrentRun (nullptr) {} + ~TextAnalysis () + { + // delete runs, except mRunHead which is part of the TextAnalysis object + for (Run *run = mRunHead.nextRun; run;) + { + Run *origRun = run; + run = run->nextRun; + delete origRun; + } + } + + STDMETHODIMP + GenerateResults (IDWriteTextAnalyzer* textAnalyzer, Run **runHead) + { + // Analyzes the text using the script analyzer and returns + // the result as a series of runs. + + HRESULT hr = S_OK; + + // Initially start out with one result that covers the entire range. + // This result will be subdivided by the analysis processes. + mRunHead.mTextStart = 0; + mRunHead.mTextLength = mTextLength; + mRunHead.mBidiLevel = + (mReadingDirection == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT); + mRunHead.nextRun = nullptr; + mCurrentRun = &mRunHead; + + // Call each of the analyzers in sequence, recording their results. + if (SUCCEEDED (hr = textAnalyzer->AnalyzeScript (this, 0, mTextLength, this))) + *runHead = &mRunHead; + + return hr; + } + + // IDWriteTextAnalysisSource implementation + + IFACEMETHODIMP + GetTextAtPosition (uint32_t textPosition, + OUT wchar_t const** textString, + OUT uint32_t* textLength) + { + if (textPosition >= mTextLength) + { + // No text at this position, valid query though. + *textString = nullptr; + *textLength = 0; + } + else + { + *textString = mText + textPosition; + *textLength = mTextLength - textPosition; + } + return S_OK; + } + + IFACEMETHODIMP + GetTextBeforePosition (uint32_t textPosition, + OUT wchar_t const** textString, + OUT uint32_t* textLength) + { + if (textPosition == 0 || textPosition > mTextLength) + { + // Either there is no text before here (== 0), or this + // is an invalid position. The query is considered valid though. + *textString = nullptr; + *textLength = 0; + } + else + { + *textString = mText; + *textLength = textPosition; + } + return S_OK; + } + + IFACEMETHODIMP_ (DWRITE_READING_DIRECTION) + GetParagraphReadingDirection () { return mReadingDirection; } + + IFACEMETHODIMP GetLocaleName (uint32_t textPosition, uint32_t* textLength, + wchar_t const** localeName) + { return S_OK; } + + IFACEMETHODIMP + GetNumberSubstitution (uint32_t textPosition, + OUT uint32_t* textLength, + OUT IDWriteNumberSubstitution** numberSubstitution) + { + // We do not support number substitution. + *numberSubstitution = nullptr; + *textLength = mTextLength - textPosition; + + return S_OK; + } + + // IDWriteTextAnalysisSink implementation + + IFACEMETHODIMP + SetScriptAnalysis (uint32_t textPosition, uint32_t textLength, + DWRITE_SCRIPT_ANALYSIS const* scriptAnalysis) + { + SetCurrentRun (textPosition); + SplitCurrentRun (textPosition); + while (textLength > 0) + { + Run *run = FetchNextRun (&textLength); + run->mScript = *scriptAnalysis; + } + + return S_OK; + } + + IFACEMETHODIMP + SetLineBreakpoints (uint32_t textPosition, + uint32_t textLength, + const DWRITE_LINE_BREAKPOINT* lineBreakpoints) + { return S_OK; } + + IFACEMETHODIMP SetBidiLevel (uint32_t textPosition, uint32_t textLength, + uint8_t explicitLevel, uint8_t resolvedLevel) + { return S_OK; } + + IFACEMETHODIMP + SetNumberSubstitution (uint32_t textPosition, uint32_t textLength, + IDWriteNumberSubstitution* numberSubstitution) + { return S_OK; } + +protected: + Run *FetchNextRun (IN OUT uint32_t* textLength) + { + // Used by the sink setters, this returns a reference to the next run. + // Position and length are adjusted to now point after the current run + // being returned. + + Run *origRun = mCurrentRun; + // Split the tail if needed (the length remaining is less than the + // current run's size). + if (*textLength < mCurrentRun->mTextLength) + SplitCurrentRun (mCurrentRun->mTextStart + *textLength); + else + // Just advance the current run. + mCurrentRun = mCurrentRun->nextRun; + *textLength -= origRun->mTextLength; + + // Return a reference to the run that was just current. + return origRun; + } + + void SetCurrentRun (uint32_t textPosition) + { + // Move the current run to the given position. + // Since the analyzers generally return results in a forward manner, + // this will usually just return early. If not, find the + // corresponding run for the text position. + + if (mCurrentRun && mCurrentRun->ContainsTextPosition (textPosition)) + return; + + for (Run *run = &mRunHead; run; run = run->nextRun) + if (run->ContainsTextPosition (textPosition)) + { + mCurrentRun = run; + return; + } + assert (0); // We should always be able to find the text position in one of our runs + } + + void SplitCurrentRun (uint32_t splitPosition) + { + if (!mCurrentRun) + { + assert (0); // SplitCurrentRun called without current run + // Shouldn't be calling this when no current run is set! + return; + } + // Split the current run. + if (splitPosition <= mCurrentRun->mTextStart) + { + // No need to split, already the start of a run + // or before it. Usually the first. + return; + } + Run *newRun = new Run; + + *newRun = *mCurrentRun; + + // Insert the new run in our linked list. + newRun->nextRun = mCurrentRun->nextRun; + mCurrentRun->nextRun = newRun; + + // Adjust runs' text positions and lengths. + uint32_t splitPoint = splitPosition - mCurrentRun->mTextStart; + newRun->mTextStart += splitPoint; + newRun->mTextLength -= splitPoint; + mCurrentRun->mTextLength = splitPoint; + mCurrentRun = newRun; + } + +protected: + // Input + // (weak references are fine here, since this class is a transient + // stack-based helper that doesn't need to copy data) + uint32_t mTextLength; + const wchar_t* mText; + const wchar_t* mLocaleName; + DWRITE_READING_DIRECTION mReadingDirection; + + // Current processing state. + Run *mCurrentRun; + + // Output is a list of runs starting here + Run mRunHead; +}; + +/* + * shaper + */ + +hb_bool_t +_hb_directwrite_shape (hb_shape_plan_t *shape_plan, + hb_font_t *font, + hb_buffer_t *buffer, + const hb_feature_t *features, + unsigned int num_features) +{ + hb_face_t *face = font->face; + const hb_directwrite_face_data_t *face_data = face->data.directwrite; + IDWriteFactory *dwriteFactory = face_data->dwriteFactory; + IDWriteFontFace *fontFace = face_data->fontFace; + + IDWriteTextAnalyzer* analyzer; + dwriteFactory->CreateTextAnalyzer (&analyzer); + + unsigned int scratch_size; + hb_buffer_t::scratch_buffer_t *scratch = buffer->get_scratch_buffer (&scratch_size); +#define ALLOCATE_ARRAY(Type, name, len) \ + Type *name = (Type *) scratch; \ + do { \ + unsigned int _consumed = DIV_CEIL ((len) * sizeof (Type), sizeof (*scratch)); \ + assert (_consumed <= scratch_size); \ + scratch += _consumed; \ + scratch_size -= _consumed; \ + } while (0) + +#define utf16_index() var1.u32 + + ALLOCATE_ARRAY (wchar_t, textString, buffer->len * 2); + + unsigned int chars_len = 0; + for (unsigned int i = 0; i < buffer->len; i++) + { + hb_codepoint_t c = buffer->info[i].codepoint; + buffer->info[i].utf16_index () = chars_len; + if (likely (c <= 0xFFFFu)) + textString[chars_len++] = c; + else if (unlikely (c > 0x10FFFFu)) + textString[chars_len++] = 0xFFFDu; + else + { + textString[chars_len++] = 0xD800u + ((c - 0x10000u) >> 10); + textString[chars_len++] = 0xDC00u + ((c - 0x10000u) & ((1u << 10) - 1)); + } + } + + ALLOCATE_ARRAY (WORD, log_clusters, chars_len); + /* Need log_clusters to assign features. */ + chars_len = 0; + for (unsigned int i = 0; i < buffer->len; i++) + { + hb_codepoint_t c = buffer->info[i].codepoint; + unsigned int cluster = buffer->info[i].cluster; + log_clusters[chars_len++] = cluster; + if (hb_in_range (c, 0x10000u, 0x10FFFFu)) + log_clusters[chars_len++] = cluster; /* Surrogates. */ + } + + DWRITE_READING_DIRECTION readingDirection; + readingDirection = buffer->props.direction ? + DWRITE_READING_DIRECTION_RIGHT_TO_LEFT : + DWRITE_READING_DIRECTION_LEFT_TO_RIGHT; + + /* + * There's an internal 16-bit limit on some things inside the analyzer, + * but we never attempt to shape a word longer than 64K characters + * in a single gfxShapedWord, so we cannot exceed that limit. + */ + uint32_t textLength = chars_len; + + TextAnalysis analysis (textString, textLength, nullptr, readingDirection); + TextAnalysis::Run *runHead; + HRESULT hr; + hr = analysis.GenerateResults (analyzer, &runHead); + +#define FAIL(...) \ + HB_STMT_START { \ + DEBUG_MSG (DIRECTWRITE, nullptr, __VA_ARGS__); \ + return false; \ + } HB_STMT_END + + if (FAILED (hr)) + FAIL ("Analyzer failed to generate results."); + + uint32_t maxGlyphCount = 3 * textLength / 2 + 16; + uint32_t glyphCount; + bool isRightToLeft = HB_DIRECTION_IS_BACKWARD (buffer->props.direction); + + const wchar_t localeName[20] = {0}; + if (buffer->props.language) + mbstowcs ((wchar_t*) localeName, + hb_language_to_string (buffer->props.language), 20); + + /* + * Set up features. + */ + static_assert ((sizeof (DWRITE_TYPOGRAPHIC_FEATURES) == sizeof (hb_ms_features_t)), ""); + static_assert ((sizeof (DWRITE_FONT_FEATURE) == sizeof (hb_ms_feature_t)), ""); + hb_vector_t range_features; + hb_vector_t range_char_counts; + if (num_features) + { + hb_vector_t feature_records; + hb_vector_t range_records; + if (hb_ms_setup_features (features, num_features, feature_records, range_records)) + hb_ms_make_feature_ranges (feature_records, + range_records, + 0, + chars_len, + log_clusters, + range_features, + range_char_counts); + } + + uint16_t* clusterMap; + clusterMap = new uint16_t[textLength]; + DWRITE_SHAPING_TEXT_PROPERTIES* textProperties; + textProperties = new DWRITE_SHAPING_TEXT_PROPERTIES[textLength]; + +retry_getglyphs: + uint16_t* glyphIndices = new uint16_t[maxGlyphCount]; + DWRITE_SHAPING_GLYPH_PROPERTIES* glyphProperties; + glyphProperties = new DWRITE_SHAPING_GLYPH_PROPERTIES[maxGlyphCount]; + + hr = analyzer->GetGlyphs (textString, + chars_len, + fontFace, + false, + isRightToLeft, + &runHead->mScript, + localeName, + nullptr, + (const DWRITE_TYPOGRAPHIC_FEATURES**) range_features.arrayZ, + range_char_counts.arrayZ, + range_features.length, + maxGlyphCount, + clusterMap, + textProperties, + glyphIndices, + glyphProperties, + &glyphCount); + + if (unlikely (hr == HRESULT_FROM_WIN32 (ERROR_INSUFFICIENT_BUFFER))) + { + delete [] glyphIndices; + delete [] glyphProperties; + + maxGlyphCount *= 2; + + goto retry_getglyphs; + } + if (FAILED (hr)) + FAIL ("Analyzer failed to get glyphs."); + + float* glyphAdvances = new float[maxGlyphCount]; + DWRITE_GLYPH_OFFSET* glyphOffsets = new DWRITE_GLYPH_OFFSET[maxGlyphCount]; + + /* The -2 in the following is to compensate for possible + * alignment needed after the WORD array. sizeof (WORD) == 2. */ + unsigned int glyphs_size = (scratch_size * sizeof (int) - 2) + / (sizeof (WORD) + + sizeof (DWRITE_SHAPING_GLYPH_PROPERTIES) + + sizeof (int) + + sizeof (DWRITE_GLYPH_OFFSET) + + sizeof (uint32_t)); + ALLOCATE_ARRAY (uint32_t, vis_clusters, glyphs_size); + +#undef ALLOCATE_ARRAY + + int fontEmSize = font->face->get_upem (); + if (fontEmSize < 0) fontEmSize = -fontEmSize; + + if (fontEmSize < 0) fontEmSize = -fontEmSize; + double x_mult = (double) font->x_scale / fontEmSize; + double y_mult = (double) font->y_scale / fontEmSize; + + hr = analyzer->GetGlyphPlacements (textString, + clusterMap, + textProperties, + chars_len, + glyphIndices, + glyphProperties, + glyphCount, + fontFace, + fontEmSize, + false, + isRightToLeft, + &runHead->mScript, + localeName, + (const DWRITE_TYPOGRAPHIC_FEATURES**) range_features.arrayZ, + range_char_counts.arrayZ, + range_features.length, + glyphAdvances, + glyphOffsets); + + if (FAILED (hr)) + FAIL ("Analyzer failed to get glyph placements."); + + /* Ok, we've got everything we need, now compose output buffer, + * very, *very*, carefully! */ + + /* Calculate visual-clusters. That's what we ship. */ + for (unsigned int i = 0; i < glyphCount; i++) + vis_clusters[i] = (uint32_t) -1; + for (unsigned int i = 0; i < buffer->len; i++) + { + uint32_t *p = + &vis_clusters[log_clusters[buffer->info[i].utf16_index ()]]; + *p = hb_min (*p, buffer->info[i].cluster); + } + for (unsigned int i = 1; i < glyphCount; i++) + if (vis_clusters[i] == (uint32_t) -1) + vis_clusters[i] = vis_clusters[i - 1]; + +#undef utf16_index + + if (unlikely (!buffer->ensure (glyphCount))) + FAIL ("Buffer in error"); + +#undef FAIL + + /* Set glyph infos */ + buffer->len = 0; + for (unsigned int i = 0; i < glyphCount; i++) + { + hb_glyph_info_t *info = &buffer->info[buffer->len++]; + + info->codepoint = glyphIndices[i]; + info->cluster = vis_clusters[i]; + + /* The rest is crap. Let's store position info there for now. */ + info->mask = glyphAdvances[i]; + info->var1.i32 = glyphOffsets[i].advanceOffset; + info->var2.i32 = glyphOffsets[i].ascenderOffset; + } + + /* Set glyph positions */ + buffer->clear_positions (); + for (unsigned int i = 0; i < glyphCount; i++) + { + hb_glyph_info_t *info = &buffer->info[i]; + hb_glyph_position_t *pos = &buffer->pos[i]; + + /* TODO vertical */ + pos->x_advance = x_mult * (int32_t) info->mask; + pos->x_offset = x_mult * (isRightToLeft ? -info->var1.i32 : info->var1.i32); + pos->y_offset = y_mult * info->var2.i32; + } + + if (isRightToLeft) hb_buffer_reverse (buffer); + + buffer->clear_glyph_flags (); + buffer->unsafe_to_break (); + + delete [] clusterMap; + delete [] glyphIndices; + delete [] textProperties; + delete [] glyphProperties; + delete [] glyphAdvances; + delete [] glyphOffsets; + + /* Wow, done! */ + return true; +} + +struct _hb_directwrite_font_table_context { + IDWriteFontFace *face; + void *table_context; +}; + +static void +_hb_directwrite_table_data_release (void *data) +{ + _hb_directwrite_font_table_context *context = (_hb_directwrite_font_table_context *) data; + context->face->ReleaseFontTable (context->table_context); + hb_free (context); +} + +static hb_blob_t * +_hb_directwrite_reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data) +{ + IDWriteFontFace *dw_face = ((IDWriteFontFace *) user_data); + const void *data; + uint32_t length; + void *table_context; + BOOL exists; + if (!dw_face || FAILED (dw_face->TryGetFontTable (hb_uint32_swap (tag), &data, + &length, &table_context, &exists))) + return nullptr; + + if (!data || !exists || !length) + { + dw_face->ReleaseFontTable (table_context); + return nullptr; + } + + _hb_directwrite_font_table_context *context = (_hb_directwrite_font_table_context *) hb_malloc (sizeof (_hb_directwrite_font_table_context)); + context->face = dw_face; + context->table_context = table_context; + + return hb_blob_create ((const char *) data, length, HB_MEMORY_MODE_READONLY, + context, _hb_directwrite_table_data_release); +} + +static void +_hb_directwrite_font_release (void *data) +{ + if (data) + ((IDWriteFontFace *) data)->Release (); +} + +/** + * hb_directwrite_face_create: + * @font_face: a DirectWrite IDWriteFontFace object. + * + * Constructs a new face object from the specified DirectWrite IDWriteFontFace. + * + * Return value: #hb_face_t object corresponding to the given input + * + * Since: 2.4.0 + **/ +hb_face_t * +hb_directwrite_face_create (IDWriteFontFace *font_face) +{ + if (font_face) + font_face->AddRef (); + return hb_face_create_for_tables (_hb_directwrite_reference_table, font_face, + _hb_directwrite_font_release); +} + +/** +* hb_directwrite_face_get_font_face: +* @face: a #hb_face_t object +* +* Gets the DirectWrite IDWriteFontFace associated with @face. +* +* Return value: DirectWrite IDWriteFontFace object corresponding to the given input +* +* Since: 2.5.0 +**/ +IDWriteFontFace * +hb_directwrite_face_get_font_face (hb_face_t *face) +{ + return face->data.directwrite->fontFace; +} + + +#endif diff --git a/libs/harfbuzz/src/hb-directwrite.h b/libs/harfbuzz/src/hb-directwrite.h new file mode 100644 index 00000000..f837627a --- /dev/null +++ b/libs/harfbuzz/src/hb-directwrite.h @@ -0,0 +1,40 @@ +/* + * Copyright © 2015-2019 Ebrahim Byagowi + * + * 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. + */ + +#ifndef HB_DIRECTWRITE_H +#define HB_DIRECTWRITE_H + +#include "hb.h" + +HB_BEGIN_DECLS + +HB_EXTERN hb_face_t * +hb_directwrite_face_create (IDWriteFontFace *font_face); + +HB_EXTERN IDWriteFontFace * +hb_directwrite_face_get_font_face (hb_face_t *face); + +HB_END_DECLS + +#endif /* HB_DIRECTWRITE_H */ diff --git a/libs/harfbuzz/src/hb-dispatch.hh b/libs/harfbuzz/src/hb-dispatch.hh new file mode 100644 index 00000000..37ca6814 --- /dev/null +++ b/libs/harfbuzz/src/hb-dispatch.hh @@ -0,0 +1,60 @@ +/* + * Copyright © 2007,2008,2009,2010 Red Hat, Inc. + * Copyright © 2012,2018 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. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_DISPATCH_HH +#define HB_DISPATCH_HH + +#include "hb.hh" + +/* + * Dispatch + */ + +template +struct hb_dispatch_context_t +{ + private: + /* https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern */ + const Context* thiz () const { return static_cast (this); } + Context* thiz () { return static_cast< Context *> (this); } + public: + const char *get_name () { return "UNKNOWN"; } + static constexpr unsigned max_debug_depth = MaxDebugDepth; + typedef Return return_t; + template + bool may_dispatch (const T *obj HB_UNUSED, const F *format HB_UNUSED) { return true; } + template + return_t dispatch (const T &obj, Ts&&... ds) + { return obj.dispatch (thiz (), std::forward (ds)...); } + static return_t no_dispatch_return_value () { return Context::default_return_value (); } + static bool stop_sublookup_iteration (const return_t r HB_UNUSED) { return false; } + unsigned debug_depth = 0; +}; + + +#endif /* HB_DISPATCH_HH */ diff --git a/libs/harfbuzz/src/hb-draw.cc b/libs/harfbuzz/src/hb-draw.cc new file mode 100644 index 00000000..46797e64 --- /dev/null +++ b/libs/harfbuzz/src/hb-draw.cc @@ -0,0 +1,366 @@ +/* + * Copyright © 2019-2020 Ebrahim Byagowi + * + * 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. + */ + +#include "hb.hh" + +#ifndef HB_NO_DRAW + +#include "hb-draw.hh" + +/** + * SECTION:hb-draw + * @title: hb-draw + * @short_description: Glyph drawing + * @include: hb.h + * + * Functions for drawing (extracting) glyph shapes. + **/ + +static void +hb_draw_move_to_nil (hb_draw_funcs_t *dfuncs HB_UNUSED, void *draw_data HB_UNUSED, + hb_draw_state_t *st HB_UNUSED, + float to_x HB_UNUSED, float to_y HB_UNUSED, + void *user_data HB_UNUSED) {} + +static void +hb_draw_line_to_nil (hb_draw_funcs_t *dfuncs HB_UNUSED, void *draw_data HB_UNUSED, + hb_draw_state_t *st HB_UNUSED, + float to_x HB_UNUSED, float to_y HB_UNUSED, + void *user_data HB_UNUSED) {} + +static void +hb_draw_quadratic_to_nil (hb_draw_funcs_t *dfuncs, void *draw_data, + hb_draw_state_t *st, + float control_x, float control_y, + float to_x, float to_y, + void *user_data HB_UNUSED) +{ +#define HB_ONE_THIRD 0.33333333f + dfuncs->emit_cubic_to (draw_data, *st, + (st->current_x + 2.f * control_x) * HB_ONE_THIRD, + (st->current_y + 2.f * control_y) * HB_ONE_THIRD, + (to_x + 2.f * control_x) * HB_ONE_THIRD, + (to_y + 2.f * control_y) * HB_ONE_THIRD, + to_x, to_y); +#undef HB_ONE_THIRD +} + +static void +hb_draw_cubic_to_nil (hb_draw_funcs_t *dfuncs HB_UNUSED, void *draw_data HB_UNUSED, + hb_draw_state_t *st HB_UNUSED, + float control1_x HB_UNUSED, float control1_y HB_UNUSED, + float control2_x HB_UNUSED, float control2_y HB_UNUSED, + float to_x HB_UNUSED, float to_y HB_UNUSED, + void *user_data HB_UNUSED) {} + +static void +hb_draw_close_path_nil (hb_draw_funcs_t *dfuncs HB_UNUSED, void *draw_data HB_UNUSED, + hb_draw_state_t *st HB_UNUSED, + void *user_data HB_UNUSED) {} + + +#define HB_DRAW_FUNC_IMPLEMENT(name) \ + \ +void \ +hb_draw_funcs_set_##name##_func (hb_draw_funcs_t *dfuncs, \ + hb_draw_##name##_func_t func, \ + void *user_data, \ + hb_destroy_func_t destroy) \ +{ \ + if (hb_object_is_immutable (dfuncs)) \ + return; \ + \ + if (dfuncs->destroy && dfuncs->destroy->name) \ + dfuncs->destroy->name (!dfuncs->user_data ? nullptr : dfuncs->user_data->name); \ + \ + if (user_data && !dfuncs->user_data) \ + { \ + dfuncs->user_data = (decltype (dfuncs->user_data)) hb_calloc (1, sizeof (*dfuncs->user_data)); \ + if (unlikely (!dfuncs->user_data)) \ + goto fail; \ + } \ + if (destroy && !dfuncs->destroy) \ + { \ + dfuncs->destroy = (decltype (dfuncs->destroy)) hb_calloc (1, sizeof (*dfuncs->destroy)); \ + if (unlikely (!dfuncs->destroy)) \ + goto fail; \ + } \ + \ + if (func) { \ + dfuncs->func.name = func; \ + if (dfuncs->user_data) \ + dfuncs->user_data->name = user_data; \ + if (dfuncs->destroy) \ + dfuncs->destroy->name = destroy; \ + } else { \ + dfuncs->func.name = hb_draw_##name##_nil; \ + if (dfuncs->user_data) \ + dfuncs->user_data->name = nullptr; \ + if (dfuncs->destroy) \ + dfuncs->destroy->name = nullptr; \ + } \ + return; \ + \ +fail: \ + if (destroy) \ + destroy (user_data); \ +} + +HB_DRAW_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_DRAW_FUNC_IMPLEMENT + +/** + * hb_draw_funcs_create: + * + * Creates a new draw callbacks object. + * + * Return value: (transfer full): + * A newly allocated #hb_draw_funcs_t with a reference count of 1. The initial + * reference count should be released with hb_draw_funcs_destroy when you are + * done using the #hb_draw_funcs_t. This function never returns `NULL`. If + * memory cannot be allocated, a special singleton #hb_draw_funcs_t object will + * be returned. + * + * Since: 4.0.0 + **/ +hb_draw_funcs_t * +hb_draw_funcs_create () +{ + hb_draw_funcs_t *dfuncs; + if (unlikely (!(dfuncs = hb_object_create ()))) + return const_cast (&Null (hb_draw_funcs_t)); + + dfuncs->func = Null (hb_draw_funcs_t).func; + + return dfuncs; +} + +DEFINE_NULL_INSTANCE (hb_draw_funcs_t) = +{ + HB_OBJECT_HEADER_STATIC, + + { +#define HB_DRAW_FUNC_IMPLEMENT(name) hb_draw_##name##_nil, + HB_DRAW_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_DRAW_FUNC_IMPLEMENT + } +}; + + +/** + * hb_draw_funcs_reference: (skip) + * @dfuncs: draw functions + * + * Increases the reference count on @dfuncs by one. This prevents @buffer from + * being destroyed until a matching call to hb_draw_funcs_destroy() is made. + * + * Return value: (transfer full): + * The referenced #hb_draw_funcs_t. + * + * Since: 4.0.0 + **/ +hb_draw_funcs_t * +hb_draw_funcs_reference (hb_draw_funcs_t *dfuncs) +{ + return hb_object_reference (dfuncs); +} + +/** + * hb_draw_funcs_destroy: (skip) + * @dfuncs: draw functions + * + * Deallocate the @dfuncs. + * Decreases the reference count on @dfuncs by one. If the result is zero, then + * @dfuncs and all associated resources are freed. See hb_draw_funcs_reference(). + * + * Since: 4.0.0 + **/ +void +hb_draw_funcs_destroy (hb_draw_funcs_t *dfuncs) +{ + if (!hb_object_destroy (dfuncs)) return; + + if (dfuncs->destroy) + { +#define HB_DRAW_FUNC_IMPLEMENT(name) \ + if (dfuncs->destroy->name) dfuncs->destroy->name (!dfuncs->user_data ? nullptr : dfuncs->user_data->name); + HB_DRAW_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_DRAW_FUNC_IMPLEMENT + } + + hb_free (dfuncs->destroy); + hb_free (dfuncs->user_data); + + hb_free (dfuncs); +} + +/** + * hb_draw_funcs_make_immutable: + * @dfuncs: draw functions + * + * Makes @dfuncs object immutable. + * + * Since: 4.0.0 + **/ +void +hb_draw_funcs_make_immutable (hb_draw_funcs_t *dfuncs) +{ + if (hb_object_is_immutable (dfuncs)) + return; + + hb_object_make_immutable (dfuncs); +} + +/** + * hb_draw_funcs_is_immutable: + * @dfuncs: draw functions + * + * Checks whether @dfuncs is immutable. + * + * Return value: `true` if @dfuncs is immutable, `false` otherwise + * + * Since: 4.0.0 + **/ +hb_bool_t +hb_draw_funcs_is_immutable (hb_draw_funcs_t *dfuncs) +{ + return hb_object_is_immutable (dfuncs); +} + + +/** + * hb_draw_move_to: + * @dfuncs: draw functions + * @draw_data: associated draw data passed by the caller + * @st: current draw state + * @to_x: X component of target point + * @to_y: Y component of target point + * + * Perform a "move-to" draw operation. + * + * Since: 4.0.0 + **/ +void +hb_draw_move_to (hb_draw_funcs_t *dfuncs, void *draw_data, + hb_draw_state_t *st, + float to_x, float to_y) +{ + dfuncs->move_to (draw_data, *st, + to_x, to_y); +} + +/** + * hb_draw_line_to: + * @dfuncs: draw functions + * @draw_data: associated draw data passed by the caller + * @st: current draw state + * @to_x: X component of target point + * @to_y: Y component of target point + * + * Perform a "line-to" draw operation. + * + * Since: 4.0.0 + **/ +void +hb_draw_line_to (hb_draw_funcs_t *dfuncs, void *draw_data, + hb_draw_state_t *st, + float to_x, float to_y) +{ + dfuncs->line_to (draw_data, *st, + to_x, to_y); +} + +/** + * hb_draw_quadratic_to: + * @dfuncs: draw functions + * @draw_data: associated draw data passed by the caller + * @st: current draw state + * @control_x: X component of control point + * @control_y: Y component of control point + * @to_x: X component of target point + * @to_y: Y component of target point + * + * Perform a "quadratic-to" draw operation. + * + * Since: 4.0.0 + **/ +void +hb_draw_quadratic_to (hb_draw_funcs_t *dfuncs, void *draw_data, + hb_draw_state_t *st, + float control_x, float control_y, + float to_x, float to_y) +{ + dfuncs->quadratic_to (draw_data, *st, + control_x, control_y, + to_x, to_y); +} + +/** + * hb_draw_cubic_to: + * @dfuncs: draw functions + * @draw_data: associated draw data passed by the caller + * @st: current draw state + * @control1_x: X component of first control point + * @control1_y: Y component of first control point + * @control2_x: X component of second control point + * @control2_y: Y component of second control point + * @to_x: X component of target point + * @to_y: Y component of target point + * + * Perform a "cubic-to" draw operation. + * + * Since: 4.0.0 + **/ +void +hb_draw_cubic_to (hb_draw_funcs_t *dfuncs, void *draw_data, + hb_draw_state_t *st, + float control1_x, float control1_y, + float control2_x, float control2_y, + float to_x, float to_y) +{ + dfuncs->cubic_to (draw_data, *st, + control1_x, control1_y, + control2_x, control2_y, + to_x, to_y); +} + +/** + * hb_draw_close_path: + * @dfuncs: draw functions + * @draw_data: associated draw data passed by the caller + * @st: current draw state + * + * Perform a "close-path" draw operation. + * + * Since: 4.0.0 + **/ +void +hb_draw_close_path (hb_draw_funcs_t *dfuncs, void *draw_data, + hb_draw_state_t *st) +{ + dfuncs->close_path (draw_data, *st); +} + + +#endif diff --git a/libs/harfbuzz/src/hb-draw.h b/libs/harfbuzz/src/hb-draw.h new file mode 100644 index 00000000..c45a5321 --- /dev/null +++ b/libs/harfbuzz/src/hb-draw.h @@ -0,0 +1,325 @@ +/* + * Copyright © 2019-2020 Ebrahim Byagowi + * + * 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. + */ + +#if !defined(HB_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR) +#error "Include instead." +#endif + +#ifndef HB_DRAW_H +#define HB_DRAW_H + +#include "hb.h" + +HB_BEGIN_DECLS + + +/** + * hb_draw_state_t + * @path_open: Whether there is an open path + * @path_start_x: X component of the start of current path + * @path_start_y: Y component of the start of current path + * @current_x: X component of current point + * @current_y: Y component of current point + * + * Current drawing state. + * + * Since: 4.0.0 + **/ +typedef struct hb_draw_state_t { + hb_bool_t path_open; + + float path_start_x; + float path_start_y; + + float current_x; + float current_y; + + /*< private >*/ + hb_var_num_t reserved1; + hb_var_num_t reserved2; + hb_var_num_t reserved3; + hb_var_num_t reserved4; + hb_var_num_t reserved5; + hb_var_num_t reserved6; + hb_var_num_t reserved7; +} hb_draw_state_t; + +/** + * HB_DRAW_STATE_DEFAULT: + * + * The default #hb_draw_state_t at the start of glyph drawing. + */ +#define HB_DRAW_STATE_DEFAULT {0, 0.f, 0.f, 0.f, 0.f, {0.}, {0.}, {0.}} + + +/** + * hb_draw_funcs_t: + * + * Glyph draw callbacks. + * + * #hb_draw_move_to_func_t, #hb_draw_line_to_func_t and + * #hb_draw_cubic_to_func_t calls are necessary to be defined but we translate + * #hb_draw_quadratic_to_func_t calls to #hb_draw_cubic_to_func_t if the + * callback isn't defined. + * + * Since: 4.0.0 + **/ + +typedef struct hb_draw_funcs_t hb_draw_funcs_t; + + +/** + * hb_draw_move_to_func_t: + * @dfuncs: draw functions object + * @draw_data: The data accompanying the draw functions + * @st: current draw state + * @to_x: X component of target point + * @to_y: Y component of target point + * @user_data: User data pointer passed by the caller + * + * A virtual method for the #hb_draw_funcs_t to perform a "move-to" draw + * operation. + * + * Since: 4.0.0 + * + **/ +typedef void (*hb_draw_move_to_func_t) (hb_draw_funcs_t *dfuncs, void *draw_data, + hb_draw_state_t *st, + float to_x, float to_y, + void *user_data); + +/** + * hb_draw_line_to_func_t: + * @dfuncs: draw functions object + * @draw_data: The data accompanying the draw functions + * @st: current draw state + * @to_x: X component of target point + * @to_y: Y component of target point + * @user_data: User data pointer passed by the caller + * + * A virtual method for the #hb_draw_funcs_t to perform a "line-to" draw + * operation. + * + * Since: 4.0.0 + * + **/ +typedef void (*hb_draw_line_to_func_t) (hb_draw_funcs_t *dfuncs, void *draw_data, + hb_draw_state_t *st, + float to_x, float to_y, + void *user_data); + +/** + * hb_draw_quadratic_to_func_t: + * @dfuncs: draw functions object + * @draw_data: The data accompanying the draw functions + * @st: current draw state + * @control_x: X component of control point + * @control_y: Y component of control point + * @to_x: X component of target point + * @to_y: Y component of target point + * @user_data: User data pointer passed by the caller + * + * A virtual method for the #hb_draw_funcs_t to perform a "quadratic-to" draw + * operation. + * + * Since: 4.0.0 + * + **/ +typedef void (*hb_draw_quadratic_to_func_t) (hb_draw_funcs_t *dfuncs, void *draw_data, + hb_draw_state_t *st, + float control_x, float control_y, + float to_x, float to_y, + void *user_data); + +/** + * hb_draw_cubic_to_func_t: + * @dfuncs: draw functions object + * @draw_data: The data accompanying the draw functions + * @st: current draw state + * @control1_x: X component of first control point + * @control1_y: Y component of first control point + * @control2_x: X component of second control point + * @control2_y: Y component of second control point + * @to_x: X component of target point + * @to_y: Y component of target point + * @user_data: User data pointer passed by the caller + * + * A virtual method for the #hb_draw_funcs_t to perform a "cubic-to" draw + * operation. + * + * Since: 4.0.0 + * + **/ +typedef void (*hb_draw_cubic_to_func_t) (hb_draw_funcs_t *dfuncs, void *draw_data, + hb_draw_state_t *st, + float control1_x, float control1_y, + float control2_x, float control2_y, + float to_x, float to_y, + void *user_data); + +/** + * hb_draw_close_path_func_t: + * @dfuncs: draw functions object + * @draw_data: The data accompanying the draw functions + * @st: current draw state + * @user_data: User data pointer passed by the caller + * + * A virtual method for the #hb_draw_funcs_t to perform a "close-path" draw + * operation. + * + * Since: 4.0.0 + * + **/ +typedef void (*hb_draw_close_path_func_t) (hb_draw_funcs_t *dfuncs, void *draw_data, + hb_draw_state_t *st, + void *user_data); + +/** + * hb_draw_funcs_set_move_to_func: + * @dfuncs: draw functions object + * @func: (closure user_data) (destroy destroy) (scope notified): move-to callback + * @user_data: Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is not needed anymore + * + * Sets move-to callback to the draw functions object. + * + * Since: 4.0.0 + **/ +HB_EXTERN void +hb_draw_funcs_set_move_to_func (hb_draw_funcs_t *dfuncs, + hb_draw_move_to_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_draw_funcs_set_line_to_func: + * @dfuncs: draw functions object + * @func: (closure user_data) (destroy destroy) (scope notified): line-to callback + * @user_data: Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is not needed anymore + * + * Sets line-to callback to the draw functions object. + * + * Since: 4.0.0 + **/ +HB_EXTERN void +hb_draw_funcs_set_line_to_func (hb_draw_funcs_t *dfuncs, + hb_draw_line_to_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_draw_funcs_set_quadratic_to_func: + * @dfuncs: draw functions object + * @func: (closure user_data) (destroy destroy) (scope notified): quadratic-to callback + * @user_data: Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is not needed anymore + * + * Sets quadratic-to callback to the draw functions object. + * + * Since: 4.0.0 + **/ +HB_EXTERN void +hb_draw_funcs_set_quadratic_to_func (hb_draw_funcs_t *dfuncs, + hb_draw_quadratic_to_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_draw_funcs_set_cubic_to_func: + * @dfuncs: draw functions + * @func: (closure user_data) (destroy destroy) (scope notified): cubic-to callback + * @user_data: Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is not needed anymore + * + * Sets cubic-to callback to the draw functions object. + * + * Since: 4.0.0 + **/ +HB_EXTERN void +hb_draw_funcs_set_cubic_to_func (hb_draw_funcs_t *dfuncs, + hb_draw_cubic_to_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_draw_funcs_set_close_path_func: + * @dfuncs: draw functions object + * @func: (closure user_data) (destroy destroy) (scope notified): close-path callback + * @user_data: Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is not needed anymore + * + * Sets close-path callback to the draw functions object. + * + * Since: 4.0.0 + **/ +HB_EXTERN void +hb_draw_funcs_set_close_path_func (hb_draw_funcs_t *dfuncs, + hb_draw_close_path_func_t func, + void *user_data, hb_destroy_func_t destroy); + + +HB_EXTERN hb_draw_funcs_t * +hb_draw_funcs_create (void); + +HB_EXTERN hb_draw_funcs_t * +hb_draw_funcs_reference (hb_draw_funcs_t *dfuncs); + +HB_EXTERN void +hb_draw_funcs_destroy (hb_draw_funcs_t *dfuncs); + +HB_EXTERN void +hb_draw_funcs_make_immutable (hb_draw_funcs_t *dfuncs); + +HB_EXTERN hb_bool_t +hb_draw_funcs_is_immutable (hb_draw_funcs_t *dfuncs); + + +HB_EXTERN void +hb_draw_move_to (hb_draw_funcs_t *dfuncs, void *draw_data, + hb_draw_state_t *st, + float to_x, float to_y); + +HB_EXTERN void +hb_draw_line_to (hb_draw_funcs_t *dfuncs, void *draw_data, + hb_draw_state_t *st, + float to_x, float to_y); + +HB_EXTERN void +hb_draw_quadratic_to (hb_draw_funcs_t *dfuncs, void *draw_data, + hb_draw_state_t *st, + float control_x, float control_y, + float to_x, float to_y); + +HB_EXTERN void +hb_draw_cubic_to (hb_draw_funcs_t *dfuncs, void *draw_data, + hb_draw_state_t *st, + float control1_x, float control1_y, + float control2_x, float control2_y, + float to_x, float to_y); + +HB_EXTERN void +hb_draw_close_path (hb_draw_funcs_t *dfuncs, void *draw_data, + hb_draw_state_t *st); + + +HB_END_DECLS + +#endif /* HB_DRAW_H */ diff --git a/libs/harfbuzz/src/hb-draw.hh b/libs/harfbuzz/src/hb-draw.hh new file mode 100644 index 00000000..768f51a8 --- /dev/null +++ b/libs/harfbuzz/src/hb-draw.hh @@ -0,0 +1,231 @@ +/* + * Copyright © 2020 Ebrahim Byagowi + * + * 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. + */ + +#ifndef HB_DRAW_HH +#define HB_DRAW_HH + +#include "hb.hh" + + +/* + * hb_draw_funcs_t + */ + +#define HB_DRAW_FUNCS_IMPLEMENT_CALLBACKS \ + HB_DRAW_FUNC_IMPLEMENT (move_to) \ + HB_DRAW_FUNC_IMPLEMENT (line_to) \ + HB_DRAW_FUNC_IMPLEMENT (quadratic_to) \ + HB_DRAW_FUNC_IMPLEMENT (cubic_to) \ + HB_DRAW_FUNC_IMPLEMENT (close_path) \ + /* ^--- Add new callbacks here */ + +struct hb_draw_funcs_t +{ + hb_object_header_t header; + + struct { +#define HB_DRAW_FUNC_IMPLEMENT(name) hb_draw_##name##_func_t name; + HB_DRAW_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_DRAW_FUNC_IMPLEMENT + } func; + + struct { +#define HB_DRAW_FUNC_IMPLEMENT(name) void *name; + HB_DRAW_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_DRAW_FUNC_IMPLEMENT + } *user_data; + + struct { +#define HB_DRAW_FUNC_IMPLEMENT(name) hb_destroy_func_t name; + HB_DRAW_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_DRAW_FUNC_IMPLEMENT + } *destroy; + + void emit_move_to (void *draw_data, hb_draw_state_t &st, + float to_x, float to_y) + { func.move_to (this, draw_data, &st, + to_x, to_y, + !user_data ? nullptr : user_data->move_to); } + void emit_line_to (void *draw_data, hb_draw_state_t &st, + float to_x, float to_y) + { func.line_to (this, draw_data, &st, + to_x, to_y, + !user_data ? nullptr : user_data->line_to); } + void emit_quadratic_to (void *draw_data, hb_draw_state_t &st, + float control_x, float control_y, + float to_x, float to_y) + { func.quadratic_to (this, draw_data, &st, + control_x, control_y, + to_x, to_y, + !user_data ? nullptr : user_data->quadratic_to); } + void emit_cubic_to (void *draw_data, hb_draw_state_t &st, + float control1_x, float control1_y, + float control2_x, float control2_y, + float to_x, float to_y) + { func.cubic_to (this, draw_data, &st, + control1_x, control1_y, + control2_x, control2_y, + to_x, to_y, + !user_data ? nullptr : user_data->cubic_to); } + void emit_close_path (void *draw_data, hb_draw_state_t &st) + { func.close_path (this, draw_data, &st, + !user_data ? nullptr : user_data->close_path); } + + + void move_to (void *draw_data, hb_draw_state_t &st, + float to_x, float to_y) + { + if (st.path_open) close_path (draw_data, st); + st.current_x = to_x; + st.current_y = to_y; + } + + void line_to (void *draw_data, hb_draw_state_t &st, + float to_x, float to_y) + { + if (!st.path_open) start_path (draw_data, st); + emit_line_to (draw_data, st, to_x, to_y); + st.current_x = to_x; + st.current_y = to_y; + } + + void + quadratic_to (void *draw_data, hb_draw_state_t &st, + float control_x, float control_y, + float to_x, float to_y) + { + if (!st.path_open) start_path (draw_data, st); + emit_quadratic_to (draw_data, st, control_x, control_y, to_x, to_y); + st.current_x = to_x; + st.current_y = to_y; + } + + void + cubic_to (void *draw_data, hb_draw_state_t &st, + float control1_x, float control1_y, + float control2_x, float control2_y, + float to_x, float to_y) + { + if (!st.path_open) start_path (draw_data, st); + emit_cubic_to (draw_data, st, control1_x, control1_y, control2_x, control2_y, to_x, to_y); + st.current_x = to_x; + st.current_y = to_y; + } + + void + close_path (void *draw_data, hb_draw_state_t &st) + { + if (st.path_open) + { + if ((st.path_start_x != st.current_x) || (st.path_start_y != st.current_y)) + emit_line_to (draw_data, st, st.path_start_x, st.path_start_y); + emit_close_path (draw_data, st); + } + st.path_open = false; + st.path_start_x = st.current_x = st.path_start_y = st.current_y = 0; + } + + protected: + + void start_path (void *draw_data, hb_draw_state_t &st) + { + assert (!st.path_open); + emit_move_to (draw_data, st, st.current_x, st.current_y); + st.path_open = true; + st.path_start_x = st.current_x; + st.path_start_y = st.current_y; + } +}; +DECLARE_NULL_INSTANCE (hb_draw_funcs_t); + +struct hb_draw_session_t +{ + hb_draw_session_t (hb_draw_funcs_t *funcs_, void *draw_data_, float slant_ = 0.f) + : slant {slant_}, not_slanted {slant == 0.f}, + funcs {funcs_}, draw_data {draw_data_}, st HB_DRAW_STATE_DEFAULT + {} + + ~hb_draw_session_t () { close_path (); } + + void move_to (float to_x, float to_y) + { + if (likely (not_slanted)) + funcs->move_to (draw_data, st, + to_x, to_y); + else + funcs->move_to (draw_data, st, + to_x + to_y * slant, to_y); + } + void line_to (float to_x, float to_y) + { + if (likely (not_slanted)) + funcs->line_to (draw_data, st, + to_x, to_y); + else + funcs->line_to (draw_data, st, + to_x + to_y * slant, to_y); + } + void + quadratic_to (float control_x, float control_y, + float to_x, float to_y) + { + if (likely (not_slanted)) + funcs->quadratic_to (draw_data, st, + control_x, control_y, + to_x, to_y); + else + funcs->quadratic_to (draw_data, st, + control_x + control_y * slant, control_y, + to_x + to_y * slant, to_y); + } + void + cubic_to (float control1_x, float control1_y, + float control2_x, float control2_y, + float to_x, float to_y) + { + if (likely (not_slanted)) + funcs->cubic_to (draw_data, st, + control1_x, control1_y, + control2_x, control2_y, + to_x, to_y); + else + funcs->cubic_to (draw_data, st, + control1_x + control1_y * slant, control1_y, + control2_x + control2_y * slant, control2_y, + to_x + to_y * slant, to_y); + } + void close_path () + { + funcs->close_path (draw_data, st); + } + + protected: + float slant; + bool not_slanted; + hb_draw_funcs_t *funcs; + void *draw_data; + hb_draw_state_t st; +}; + +#endif /* HB_DRAW_HH */ diff --git a/libs/harfbuzz/src/hb-face.cc b/libs/harfbuzz/src/hb-face.cc new file mode 100644 index 00000000..e7deb31d --- /dev/null +++ b/libs/harfbuzz/src/hb-face.cc @@ -0,0 +1,837 @@ +/* + * Copyright © 2009 Red Hat, Inc. + * Copyright © 2012 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. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#include "hb.hh" + +#include "hb-face.hh" +#include "hb-blob.hh" +#include "hb-open-file.hh" +#include "hb-ot-face.hh" +#include "hb-ot-cmap-table.hh" +#include "hb-map.hh" + + +/** + * SECTION:hb-face + * @title: hb-face + * @short_description: Font face objects + * @include: hb.h + * + * A font face is an object that represents a single face from within a + * font family. + * + * More precisely, a font face represents a single face in a binary font file. + * Font faces are typically built from a binary blob and a face index. + * Font faces are used to create fonts. + **/ + + +/** + * hb_face_count: + * @blob: a blob. + * + * Fetches the number of faces in a blob. + * + * Return value: Number of faces in @blob + * + * Since: 1.7.7 + **/ +unsigned int +hb_face_count (hb_blob_t *blob) +{ + if (unlikely (!blob)) + return 0; + + /* TODO We shouldn't be sanitizing blob. Port to run sanitizer and return if not sane. */ + /* Make API signature const after. */ + hb_blob_t *sanitized = hb_sanitize_context_t ().sanitize_blob (hb_blob_reference (blob)); + const OT::OpenTypeFontFile& ot = *sanitized->as (); + unsigned int ret = ot.get_face_count (); + hb_blob_destroy (sanitized); + + return ret; +} + +/* + * hb_face_t + */ + +DEFINE_NULL_INSTANCE (hb_face_t) = +{ + HB_OBJECT_HEADER_STATIC, + + nullptr, /* reference_table_func */ + nullptr, /* user_data */ + nullptr, /* destroy */ + + 0, /* index */ + 1000, /* upem */ + 0, /* num_glyphs */ + + /* Zero for the rest is fine. */ +}; + + +/** + * hb_face_create_for_tables: + * @reference_table_func: (closure user_data) (destroy destroy) (scope notified): Table-referencing function + * @user_data: A pointer to the user data + * @destroy: (nullable): A callback to call when @data is not needed anymore + * + * Variant of hb_face_create(), built for those cases where it is more + * convenient to provide data for individual tables instead of the whole font + * data. With the caveat that hb_face_get_table_tags() does not currently work + * with faces created this way. + * + * Creates a new face object from the specified @user_data and @reference_table_func, + * with the @destroy callback. + * + * Return value: (transfer full): The new face object + * + * Since: 0.9.2 + **/ +hb_face_t * +hb_face_create_for_tables (hb_reference_table_func_t reference_table_func, + void *user_data, + hb_destroy_func_t destroy) +{ + hb_face_t *face; + + if (!reference_table_func || !(face = hb_object_create ())) { + if (destroy) + destroy (user_data); + return hb_face_get_empty (); + } + + face->reference_table_func = reference_table_func; + face->user_data = user_data; + face->destroy = destroy; + + face->num_glyphs = -1; + + face->data.init0 (face); + face->table.init0 (face); + + return face; +} + + +typedef struct hb_face_for_data_closure_t { + hb_blob_t *blob; + uint16_t index; +} hb_face_for_data_closure_t; + +static hb_face_for_data_closure_t * +_hb_face_for_data_closure_create (hb_blob_t *blob, unsigned int index) +{ + hb_face_for_data_closure_t *closure; + + closure = (hb_face_for_data_closure_t *) hb_calloc (1, sizeof (hb_face_for_data_closure_t)); + if (unlikely (!closure)) + return nullptr; + + closure->blob = blob; + closure->index = (uint16_t) (index & 0xFFFFu); + + return closure; +} + +static void +_hb_face_for_data_closure_destroy (void *data) +{ + hb_face_for_data_closure_t *closure = (hb_face_for_data_closure_t *) data; + + hb_blob_destroy (closure->blob); + hb_free (closure); +} + +static hb_blob_t * +_hb_face_for_data_reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data) +{ + hb_face_for_data_closure_t *data = (hb_face_for_data_closure_t *) user_data; + + if (tag == HB_TAG_NONE) + return hb_blob_reference (data->blob); + + const OT::OpenTypeFontFile &ot_file = *data->blob->as (); + unsigned int base_offset; + const OT::OpenTypeFontFace &ot_face = ot_file.get_face (data->index, &base_offset); + + const OT::OpenTypeTable &table = ot_face.get_table_by_tag (tag); + + hb_blob_t *blob = hb_blob_create_sub_blob (data->blob, base_offset + table.offset, table.length); + + return blob; +} + +/** + * hb_face_create: + * @blob: #hb_blob_t to work upon + * @index: The index of the face within @blob + * + * Constructs a new face object from the specified blob and + * a face index into that blob. + * + * The face index is used for blobs of file formats such as TTC and + * and DFont that can contain more than one face. Face indices within + * such collections are zero-based. + * + * Note: If the blob font format is not a collection, @index + * is ignored. Otherwise, only the lower 16-bits of @index are used. + * The unmodified @index can be accessed via hb_face_get_index(). + * + * Note: The high 16-bits of @index, if non-zero, are used by + * hb_font_create() to load named-instances in variable fonts. See + * hb_font_create() for details. + * + * Return value: (transfer full): The new face object + * + * Since: 0.9.2 + **/ +hb_face_t * +hb_face_create (hb_blob_t *blob, + unsigned int index) +{ + hb_face_t *face; + + if (unlikely (!blob)) + blob = hb_blob_get_empty (); + + blob = hb_sanitize_context_t ().sanitize_blob (hb_blob_reference (blob)); + + hb_face_for_data_closure_t *closure = _hb_face_for_data_closure_create (blob, index); + + if (unlikely (!closure)) + { + hb_blob_destroy (blob); + return hb_face_get_empty (); + } + + face = hb_face_create_for_tables (_hb_face_for_data_reference_table, + closure, + _hb_face_for_data_closure_destroy); + + face->index = index; + + return face; +} + +/** + * hb_face_get_empty: + * + * Fetches the singleton empty face object. + * + * Return value: (transfer full): The empty face object + * + * Since: 0.9.2 + **/ +hb_face_t * +hb_face_get_empty () +{ + return const_cast (&Null (hb_face_t)); +} + + +/** + * hb_face_reference: (skip) + * @face: A face object + * + * Increases the reference count on a face object. + * + * Return value: The @face object + * + * Since: 0.9.2 + **/ +hb_face_t * +hb_face_reference (hb_face_t *face) +{ + return hb_object_reference (face); +} + +/** + * hb_face_destroy: (skip) + * @face: A face object + * + * Decreases the reference count on a face object. When the + * reference count reaches zero, the face is destroyed, + * freeing all memory. + * + * Since: 0.9.2 + **/ +void +hb_face_destroy (hb_face_t *face) +{ + if (!hb_object_destroy (face)) return; + + for (hb_face_t::plan_node_t *node = face->shape_plans; node; ) + { + hb_face_t::plan_node_t *next = node->next; + hb_shape_plan_destroy (node->shape_plan); + hb_free (node); + node = next; + } + + face->data.fini (); + face->table.fini (); + + if (face->destroy) + face->destroy (face->user_data); + + hb_free (face); +} + +/** + * hb_face_set_user_data: (skip) + * @face: A face object + * @key: The user-data key to set + * @data: A pointer to the user data + * @destroy: (nullable): A callback to call when @data is not needed anymore + * @replace: Whether to replace an existing data with the same key + * + * Attaches a user-data key/data pair to the given face object. + * + * Return value: `true` if success, `false` otherwise + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_face_set_user_data (hb_face_t *face, + hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy, + hb_bool_t replace) +{ + return hb_object_set_user_data (face, key, data, destroy, replace); +} + +/** + * hb_face_get_user_data: (skip) + * @face: A face object + * @key: The user-data key to query + * + * Fetches the user data associated with the specified key, + * attached to the specified face object. + * + * Return value: (transfer none): A pointer to the user data + * + * Since: 0.9.2 + **/ +void * +hb_face_get_user_data (const hb_face_t *face, + hb_user_data_key_t *key) +{ + return hb_object_get_user_data (face, key); +} + +/** + * hb_face_make_immutable: + * @face: A face object + * + * Makes the given face object immutable. + * + * Since: 0.9.2 + **/ +void +hb_face_make_immutable (hb_face_t *face) +{ + if (hb_object_is_immutable (face)) + return; + + hb_object_make_immutable (face); +} + +/** + * hb_face_is_immutable: + * @face: A face object + * + * Tests whether the given face object is immutable. + * + * Return value: `true` is @face is immutable, `false` otherwise + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_face_is_immutable (const hb_face_t *face) +{ + return hb_object_is_immutable (face); +} + + +/** + * hb_face_reference_table: + * @face: A face object + * @tag: The #hb_tag_t of the table to query + * + * Fetches a reference to the specified table within + * the specified face. + * + * Return value: (transfer full): A pointer to the @tag table within @face + * + * Since: 0.9.2 + **/ +hb_blob_t * +hb_face_reference_table (const hb_face_t *face, + hb_tag_t tag) +{ + if (unlikely (tag == HB_TAG_NONE)) + return hb_blob_get_empty (); + + return face->reference_table (tag); +} + +/** + * hb_face_reference_blob: + * @face: A face object + * + * Fetches a pointer to the binary blob that contains the + * specified face. Returns an empty blob if referencing face data is not + * possible. + * + * Return value: (transfer full): A pointer to the blob for @face + * + * Since: 0.9.2 + **/ +hb_blob_t * +hb_face_reference_blob (hb_face_t *face) +{ + return face->reference_table (HB_TAG_NONE); +} + +/** + * hb_face_set_index: + * @face: A face object + * @index: The index to assign + * + * Assigns the specified face-index to @face. Fails if the + * face is immutable. + * + * Note: changing the index has no effect on the face itself + * This only changes the value returned by hb_face_get_index(). + * + * Since: 0.9.2 + **/ +void +hb_face_set_index (hb_face_t *face, + unsigned int index) +{ + if (hb_object_is_immutable (face)) + return; + + face->index = index; +} + +/** + * hb_face_get_index: + * @face: A face object + * + * Fetches the face-index corresponding to the given face. + * + * Note: face indices within a collection are zero-based. + * + * Return value: The index of @face. + * + * Since: 0.9.2 + **/ +unsigned int +hb_face_get_index (const hb_face_t *face) +{ + return face->index; +} + +/** + * hb_face_set_upem: + * @face: A face object + * @upem: The units-per-em value to assign + * + * Sets the units-per-em (upem) for a face object to the specified value. + * + * Since: 0.9.2 + **/ +void +hb_face_set_upem (hb_face_t *face, + unsigned int upem) +{ + if (hb_object_is_immutable (face)) + return; + + face->upem = upem; +} + +/** + * hb_face_get_upem: + * @face: A face object + * + * Fetches the units-per-em (upem) value of the specified face object. + * + * Return value: The upem value of @face + * + * Since: 0.9.2 + **/ +unsigned int +hb_face_get_upem (const hb_face_t *face) +{ + return face->get_upem (); +} + +/** + * hb_face_set_glyph_count: + * @face: A face object + * @glyph_count: The glyph-count value to assign + * + * Sets the glyph count for a face object to the specified value. + * + * Since: 0.9.7 + **/ +void +hb_face_set_glyph_count (hb_face_t *face, + unsigned int glyph_count) +{ + if (hb_object_is_immutable (face)) + return; + + face->num_glyphs = glyph_count; +} + +/** + * hb_face_get_glyph_count: + * @face: A face object + * + * Fetches the glyph-count value of the specified face object. + * + * Return value: The glyph-count value of @face + * + * Since: 0.9.7 + **/ +unsigned int +hb_face_get_glyph_count (const hb_face_t *face) +{ + return face->get_num_glyphs (); +} + +/** + * hb_face_get_table_tags: + * @face: A face object + * @start_offset: The index of first table tag to retrieve + * @table_count: (inout): Input = the maximum number of table tags to return; + * Output = the actual number of table tags returned (may be zero) + * @table_tags: (out) (array length=table_count): The array of table tags found + * + * Fetches a list of all table tags for a face, if possible. The list returned will + * begin at the offset provided + * + * Return value: Total number of tables, or zero if it is not possible to list + * + * Since: 1.6.0 + **/ +unsigned int +hb_face_get_table_tags (const hb_face_t *face, + unsigned int start_offset, + unsigned int *table_count, /* IN/OUT */ + hb_tag_t *table_tags /* OUT */) +{ + if (face->destroy != (hb_destroy_func_t) _hb_face_for_data_closure_destroy) + { + if (table_count) + *table_count = 0; + return 0; + } + + hb_face_for_data_closure_t *data = (hb_face_for_data_closure_t *) face->user_data; + + const OT::OpenTypeFontFile &ot_file = *data->blob->as (); + const OT::OpenTypeFontFace &ot_face = ot_file.get_face (data->index); + + return ot_face.get_table_tags (start_offset, table_count, table_tags); +} + + +/* + * Character set. + */ + + +#ifndef HB_NO_FACE_COLLECT_UNICODES +/** + * hb_face_collect_unicodes: + * @face: A face object + * @out: The set to add Unicode characters to + * + * Collects all of the Unicode characters covered by @face and adds + * them to the #hb_set_t set @out. + * + * Since: 1.9.0 + */ +void +hb_face_collect_unicodes (hb_face_t *face, + hb_set_t *out) +{ + face->table.cmap->collect_unicodes (out, face->get_num_glyphs ()); +} +/** + * hb_face_collect_variation_selectors: + * @face: A face object + * @out: The set to add Variation Selector characters to + * + * Collects all Unicode "Variation Selector" characters covered by @face and adds + * them to the #hb_set_t set @out. + * + * Since: 1.9.0 + */ +void +hb_face_collect_variation_selectors (hb_face_t *face, + hb_set_t *out) +{ + face->table.cmap->collect_variation_selectors (out); +} +/** + * hb_face_collect_variation_unicodes: + * @face: A face object + * @variation_selector: The Variation Selector to query + * @out: The set to add Unicode characters to + * + * Collects all Unicode characters for @variation_selector covered by @face and adds + * them to the #hb_set_t set @out. + * + * Since: 1.9.0 + */ +void +hb_face_collect_variation_unicodes (hb_face_t *face, + hb_codepoint_t variation_selector, + hb_set_t *out) +{ + face->table.cmap->collect_variation_unicodes (variation_selector, out); +} +#endif + + +/* + * face-builder: A face that has add_table(). + */ + +struct face_table_info_t +{ + hb_blob_t* data; + unsigned order; +}; + +struct hb_face_builder_data_t +{ + hb_hashmap_t tables; +}; + +static int compare_entries (const void* pa, const void* pb) +{ + const auto& a = * (const hb_pair_t *) pa; + const auto& b = * (const hb_pair_t *) pb; + + /* Order by blob size first (smallest to largest) and then table tag */ + + if (a.second.order != b.second.order) + return a.second.order < b.second.order ? -1 : +1; + + if (a.second.data->length != b.second.data->length) + return a.second.data->length < b.second.data->length ? -1 : +1; + + return a.first < b.first ? -1 : a.first == b.first ? 0 : +1; +} + +static hb_face_builder_data_t * +_hb_face_builder_data_create () +{ + hb_face_builder_data_t *data = (hb_face_builder_data_t *) hb_calloc (1, sizeof (hb_face_builder_data_t)); + if (unlikely (!data)) + return nullptr; + + data->tables.init (); + + return data; +} + +static void +_hb_face_builder_data_destroy (void *user_data) +{ + hb_face_builder_data_t *data = (hb_face_builder_data_t *) user_data; + + for (auto info : data->tables.values()) + hb_blob_destroy (info.data); + + data->tables.fini (); + + hb_free (data); +} + +static hb_blob_t * +_hb_face_builder_data_reference_blob (hb_face_builder_data_t *data) +{ + + unsigned int table_count = data->tables.get_population (); + unsigned int face_length = table_count * 16 + 12; + + for (auto info : data->tables.values()) + face_length += hb_ceil_to_4 (hb_blob_get_length (info.data)); + + char *buf = (char *) hb_malloc (face_length); + if (unlikely (!buf)) + return nullptr; + + hb_serialize_context_t c (buf, face_length); + c.propagate_error (data->tables); + OT::OpenTypeFontFile *f = c.start_serialize (); + + bool is_cff = (data->tables.has (HB_TAG ('C','F','F',' ')) + || data->tables.has (HB_TAG ('C','F','F','2'))); + hb_tag_t sfnt_tag = is_cff ? OT::OpenTypeFontFile::CFFTag : OT::OpenTypeFontFile::TrueTypeTag; + + // Sort the tags so that produced face is deterministic. + hb_vector_t> sorted_entries; + data->tables.iter () | hb_sink (sorted_entries); + if (unlikely (sorted_entries.in_error ())) + { + hb_free (buf); + return nullptr; + } + + sorted_entries.qsort (compare_entries); + + bool ret = f->serialize_single (&c, + sfnt_tag, + + sorted_entries.iter() + | hb_map ([&] (hb_pair_t _) { + return hb_pair_t (_.first, _.second.data); + })); + + c.end_serialize (); + + if (unlikely (!ret)) + { + hb_free (buf); + return nullptr; + } + + return hb_blob_create (buf, face_length, HB_MEMORY_MODE_WRITABLE, buf, hb_free); +} + +static hb_blob_t * +_hb_face_builder_reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data) +{ + hb_face_builder_data_t *data = (hb_face_builder_data_t *) user_data; + + if (!tag) + return _hb_face_builder_data_reference_blob (data); + + return hb_blob_reference (data->tables[tag].data); +} + + +/** + * hb_face_builder_create: + * + * Creates a #hb_face_t that can be used with hb_face_builder_add_table(). + * After tables are added to the face, it can be compiled to a binary + * font file by calling hb_face_reference_blob(). + * + * Return value: (transfer full): New face. + * + * Since: 1.9.0 + **/ +hb_face_t * +hb_face_builder_create () +{ + hb_face_builder_data_t *data = _hb_face_builder_data_create (); + if (unlikely (!data)) return hb_face_get_empty (); + + return hb_face_create_for_tables (_hb_face_builder_reference_table, + data, + _hb_face_builder_data_destroy); +} + +/** + * hb_face_builder_add_table: + * @face: A face object created with hb_face_builder_create() + * @tag: The #hb_tag_t of the table to add + * @blob: The blob containing the table data to add + * + * Add table for @tag with data provided by @blob to the face. @face must + * be created using hb_face_builder_create(). + * + * Since: 1.9.0 + **/ +hb_bool_t +hb_face_builder_add_table (hb_face_t *face, hb_tag_t tag, hb_blob_t *blob) +{ + if (tag == HB_MAP_VALUE_INVALID) + return false; + + if (unlikely (face->destroy != (hb_destroy_func_t) _hb_face_builder_data_destroy)) + return false; + + hb_face_builder_data_t *data = (hb_face_builder_data_t *) face->user_data; + + hb_blob_t* previous = data->tables.get (tag).data; + if (!data->tables.set (tag, face_table_info_t {hb_blob_reference (blob), 0})) + { + hb_blob_destroy (blob); + return false; + } + + hb_blob_destroy (previous); + return true; +} + +/** + * hb_face_builder_sort_tables: + * @face: A face object created with hb_face_builder_create() + * @tags: (array zero-terminated=1): ordered list of table tags terminated by + * %HB_TAG_NONE + * + * Set the ordering of tables for serialization. Any tables not + * specified in the tags list will be ordered after the tables in + * tags, ordered by the default sort ordering. + * + * Since: 5.3.0 + **/ +void +hb_face_builder_sort_tables (hb_face_t *face, + const hb_tag_t *tags) +{ + hb_face_builder_data_t *data = (hb_face_builder_data_t *) face->user_data; + + // Sort all unspecified tables after any specified tables. + for (auto& info : data->tables.values_ref()) + info.order = -1; + + unsigned order = 0; + for (const hb_tag_t* tag = tags; + *tag; + tag++) + { + face_table_info_t* info; + if (!data->tables.has (*tag, &info)) continue; + info->order = order++; + } +} diff --git a/libs/harfbuzz/src/hb-face.h b/libs/harfbuzz/src/hb-face.h new file mode 100644 index 00000000..38e7104a --- /dev/null +++ b/libs/harfbuzz/src/hb-face.h @@ -0,0 +1,181 @@ +/* + * Copyright © 2009 Red Hat, 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. + * + * Red Hat Author(s): Behdad Esfahbod + */ + +#if !defined(HB_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR) +#error "Include instead." +#endif + +#ifndef HB_FACE_H +#define HB_FACE_H + +#include "hb-common.h" +#include "hb-blob.h" +#include "hb-set.h" + +HB_BEGIN_DECLS + + +HB_EXTERN unsigned int +hb_face_count (hb_blob_t *blob); + + +/* + * hb_face_t + */ + +/** + * hb_face_t: + * + * Data type for holding font faces. + * + **/ +typedef struct hb_face_t hb_face_t; + +HB_EXTERN hb_face_t * +hb_face_create (hb_blob_t *blob, + unsigned int index); + +/** + * hb_reference_table_func_t: + * @face: an #hb_face_t to reference table for + * @tag: the tag of the table to reference + * @user_data: User data pointer passed by the caller + * + * Callback function for hb_face_create_for_tables(). + * + * Return value: (transfer full): A pointer to the @tag table within @face + * + * Since: 0.9.2 + */ + +typedef hb_blob_t * (*hb_reference_table_func_t) (hb_face_t *face, hb_tag_t tag, void *user_data); + +/* calls destroy() when not needing user_data anymore */ +HB_EXTERN hb_face_t * +hb_face_create_for_tables (hb_reference_table_func_t reference_table_func, + void *user_data, + hb_destroy_func_t destroy); + +HB_EXTERN hb_face_t * +hb_face_get_empty (void); + +HB_EXTERN hb_face_t * +hb_face_reference (hb_face_t *face); + +HB_EXTERN void +hb_face_destroy (hb_face_t *face); + +HB_EXTERN hb_bool_t +hb_face_set_user_data (hb_face_t *face, + hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy, + hb_bool_t replace); + +HB_EXTERN void * +hb_face_get_user_data (const hb_face_t *face, + hb_user_data_key_t *key); + +HB_EXTERN void +hb_face_make_immutable (hb_face_t *face); + +HB_EXTERN hb_bool_t +hb_face_is_immutable (const hb_face_t *face); + + +HB_EXTERN hb_blob_t * +hb_face_reference_table (const hb_face_t *face, + hb_tag_t tag); + +HB_EXTERN hb_blob_t * +hb_face_reference_blob (hb_face_t *face); + +HB_EXTERN void +hb_face_set_index (hb_face_t *face, + unsigned int index); + +HB_EXTERN unsigned int +hb_face_get_index (const hb_face_t *face); + +HB_EXTERN void +hb_face_set_upem (hb_face_t *face, + unsigned int upem); + +HB_EXTERN unsigned int +hb_face_get_upem (const hb_face_t *face); + +HB_EXTERN void +hb_face_set_glyph_count (hb_face_t *face, + unsigned int glyph_count); + +HB_EXTERN unsigned int +hb_face_get_glyph_count (const hb_face_t *face); + +HB_EXTERN unsigned int +hb_face_get_table_tags (const hb_face_t *face, + unsigned int start_offset, + unsigned int *table_count, /* IN/OUT */ + hb_tag_t *table_tags /* OUT */); + + +/* + * Character set. + */ + +HB_EXTERN void +hb_face_collect_unicodes (hb_face_t *face, + hb_set_t *out); + +HB_EXTERN void +hb_face_collect_variation_selectors (hb_face_t *face, + hb_set_t *out); + +HB_EXTERN void +hb_face_collect_variation_unicodes (hb_face_t *face, + hb_codepoint_t variation_selector, + hb_set_t *out); + + +/* + * Builder face. + */ + +HB_EXTERN hb_face_t * +hb_face_builder_create (void); + +HB_EXTERN hb_bool_t +hb_face_builder_add_table (hb_face_t *face, + hb_tag_t tag, + hb_blob_t *blob); + +HB_EXTERN void +hb_face_builder_sort_tables (hb_face_t *face, + const hb_tag_t *tags); + + +HB_END_DECLS + +#endif /* HB_FACE_H */ diff --git a/libs/harfbuzz/src/hb-face.hh b/libs/harfbuzz/src/hb-face.hh new file mode 100644 index 00000000..12e10d01 --- /dev/null +++ b/libs/harfbuzz/src/hb-face.hh @@ -0,0 +1,109 @@ +/* + * Copyright © 2009 Red Hat, Inc. + * 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. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_FACE_HH +#define HB_FACE_HH + +#include "hb.hh" + +#include "hb-shaper.hh" +#include "hb-shape-plan.hh" +#include "hb-ot-face.hh" + + +/* + * hb_face_t + */ + +#define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_INSTANTIATE_SHAPERS(shaper, face); +#include "hb-shaper-list.hh" +#undef HB_SHAPER_IMPLEMENT + +struct hb_face_t +{ + hb_object_header_t header; + + hb_reference_table_func_t reference_table_func; + void *user_data; + hb_destroy_func_t destroy; + + unsigned int index; /* Face index in a collection, zero-based. */ + mutable hb_atomic_int_t upem; /* Units-per-EM. */ + mutable hb_atomic_int_t num_glyphs; /* Number of glyphs. */ + + hb_shaper_object_dataset_t data;/* Various shaper data. */ + hb_ot_face_t table; /* All the face's tables. */ + + /* Cache */ + struct plan_node_t + { + hb_shape_plan_t *shape_plan; + plan_node_t *next; + }; + hb_atomic_ptr_t shape_plans; + + hb_blob_t *reference_table (hb_tag_t tag) const + { + hb_blob_t *blob; + + if (unlikely (!reference_table_func)) + return hb_blob_get_empty (); + + blob = reference_table_func (/*XXX*/const_cast (this), tag, user_data); + if (unlikely (!blob)) + return hb_blob_get_empty (); + + return blob; + } + + unsigned int get_upem () const + { + unsigned int ret = upem; + if (unlikely (!ret)) + { + return load_upem (); + } + return ret; + } + + unsigned int get_num_glyphs () const + { + unsigned int ret = num_glyphs; + if (unlikely (ret == UINT_MAX)) + return load_num_glyphs (); + return ret; + } + + private: + HB_INTERNAL unsigned int load_upem () const; + HB_INTERNAL unsigned int load_num_glyphs () const; +}; +DECLARE_NULL_INSTANCE (hb_face_t); + + +#endif /* HB_FACE_HH */ diff --git a/libs/harfbuzz/src/hb-fallback-shape.cc b/libs/harfbuzz/src/hb-fallback-shape.cc new file mode 100644 index 00000000..f8524ecc --- /dev/null +++ b/libs/harfbuzz/src/hb-fallback-shape.cc @@ -0,0 +1,125 @@ +/* + * 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 + */ + +#include "hb-shaper-impl.hh" + +#ifndef HB_NO_FALLBACK_SHAPE + +/* + * shaper face data + */ + +struct hb_fallback_face_data_t {}; + +hb_fallback_face_data_t * +_hb_fallback_shaper_face_data_create (hb_face_t *face HB_UNUSED) +{ + return (hb_fallback_face_data_t *) HB_SHAPER_DATA_SUCCEEDED; +} + +void +_hb_fallback_shaper_face_data_destroy (hb_fallback_face_data_t *data HB_UNUSED) +{ +} + + +/* + * shaper font data + */ + +struct hb_fallback_font_data_t {}; + +hb_fallback_font_data_t * +_hb_fallback_shaper_font_data_create (hb_font_t *font HB_UNUSED) +{ + return (hb_fallback_font_data_t *) HB_SHAPER_DATA_SUCCEEDED; +} + +void +_hb_fallback_shaper_font_data_destroy (hb_fallback_font_data_t *data HB_UNUSED) +{ +} + + +/* + * shaper + */ + +hb_bool_t +_hb_fallback_shape (hb_shape_plan_t *shape_plan HB_UNUSED, + hb_font_t *font, + hb_buffer_t *buffer, + const hb_feature_t *features HB_UNUSED, + unsigned int num_features HB_UNUSED) +{ + /* TODO + * + * - Apply fallback kern. + * - Handle Variation Selectors? + * - Apply normalization? + * + * This will make the fallback shaper into a dumb "TrueType" + * shaper which many people unfortunately still request. + */ + + hb_codepoint_t space; + bool has_space = (bool) font->get_nominal_glyph (' ', &space); + + buffer->clear_positions (); + + hb_direction_t direction = buffer->props.direction; + hb_unicode_funcs_t *unicode = buffer->unicode; + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; + hb_glyph_position_t *pos = buffer->pos; + for (unsigned int i = 0; i < count; i++) + { + if (has_space && unicode->is_default_ignorable (info[i].codepoint)) { + info[i].codepoint = space; + pos[i].x_advance = 0; + pos[i].y_advance = 0; + continue; + } + (void) font->get_nominal_glyph (info[i].codepoint, &info[i].codepoint); + font->get_glyph_advance_for_direction (info[i].codepoint, + direction, + &pos[i].x_advance, + &pos[i].y_advance); + font->subtract_glyph_origin_for_direction (info[i].codepoint, + direction, + &pos[i].x_offset, + &pos[i].y_offset); + } + + if (HB_DIRECTION_IS_BACKWARD (direction)) + hb_buffer_reverse (buffer); + + buffer->clear_glyph_flags (); + + return true; +} + +#endif diff --git a/libs/harfbuzz/src/hb-font.cc b/libs/harfbuzz/src/hb-font.cc new file mode 100644 index 00000000..856bbdda --- /dev/null +++ b/libs/harfbuzz/src/hb-font.cc @@ -0,0 +1,2726 @@ +/* + * Copyright © 2009 Red Hat, Inc. + * Copyright © 2012 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. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#include "hb.hh" + +#include "hb-font.hh" +#include "hb-draw.hh" +#include "hb-machinery.hh" + +#include "hb-ot.h" + +#include "hb-ot-var-avar-table.hh" +#include "hb-ot-var-fvar-table.hh" + + +/** + * SECTION:hb-font + * @title: hb-font + * @short_description: Font objects + * @include: hb.h + * + * Functions for working with font objects. + * + * A font object represents a font face at a specific size and with + * certain other parameters (pixels-per-em, points-per-em, variation + * settings) specified. Font objects are created from font face + * objects, and are used as input to hb_shape(), among other things. + * + * Client programs can optionally pass in their own functions that + * implement the basic, lower-level queries of font objects. This set + * of font functions is defined by the virtual methods in + * #hb_font_funcs_t. + * + * HarfBuzz provides a built-in set of lightweight default + * functions for each method in #hb_font_funcs_t. + **/ + + +/* + * hb_font_funcs_t + */ + +static hb_bool_t +hb_font_get_font_h_extents_nil (hb_font_t *font HB_UNUSED, + void *font_data HB_UNUSED, + hb_font_extents_t *extents, + void *user_data HB_UNUSED) +{ + memset (extents, 0, sizeof (*extents)); + return false; +} + +static hb_bool_t +hb_font_get_font_h_extents_default (hb_font_t *font, + void *font_data HB_UNUSED, + hb_font_extents_t *extents, + void *user_data HB_UNUSED) +{ + hb_bool_t ret = font->parent->get_font_h_extents (extents); + if (ret) { + extents->ascender = font->parent_scale_y_distance (extents->ascender); + extents->descender = font->parent_scale_y_distance (extents->descender); + extents->line_gap = font->parent_scale_y_distance (extents->line_gap); + } + return ret; +} + +static hb_bool_t +hb_font_get_font_v_extents_nil (hb_font_t *font HB_UNUSED, + void *font_data HB_UNUSED, + hb_font_extents_t *extents, + void *user_data HB_UNUSED) +{ + memset (extents, 0, sizeof (*extents)); + return false; +} + +static hb_bool_t +hb_font_get_font_v_extents_default (hb_font_t *font, + void *font_data HB_UNUSED, + hb_font_extents_t *extents, + void *user_data HB_UNUSED) +{ + hb_bool_t ret = font->parent->get_font_v_extents (extents); + if (ret) { + extents->ascender = font->parent_scale_x_distance (extents->ascender); + extents->descender = font->parent_scale_x_distance (extents->descender); + extents->line_gap = font->parent_scale_x_distance (extents->line_gap); + } + return ret; +} + +static hb_bool_t +hb_font_get_nominal_glyph_nil (hb_font_t *font HB_UNUSED, + void *font_data HB_UNUSED, + hb_codepoint_t unicode HB_UNUSED, + hb_codepoint_t *glyph, + void *user_data HB_UNUSED) +{ + *glyph = 0; + return false; +} + +static hb_bool_t +hb_font_get_nominal_glyph_default (hb_font_t *font, + void *font_data HB_UNUSED, + hb_codepoint_t unicode, + hb_codepoint_t *glyph, + void *user_data HB_UNUSED) +{ + if (font->has_nominal_glyphs_func_set ()) + { + return font->get_nominal_glyphs (1, &unicode, 0, glyph, 0); + } + return font->parent->get_nominal_glyph (unicode, glyph); +} + +#define hb_font_get_nominal_glyphs_nil hb_font_get_nominal_glyphs_default + +static unsigned int +hb_font_get_nominal_glyphs_default (hb_font_t *font, + void *font_data HB_UNUSED, + unsigned int count, + const hb_codepoint_t *first_unicode, + unsigned int unicode_stride, + hb_codepoint_t *first_glyph, + unsigned int glyph_stride, + void *user_data HB_UNUSED) +{ + if (font->has_nominal_glyph_func_set ()) + { + for (unsigned int i = 0; i < count; i++) + { + if (!font->get_nominal_glyph (*first_unicode, first_glyph)) + return i; + + first_unicode = &StructAtOffsetUnaligned (first_unicode, unicode_stride); + first_glyph = &StructAtOffsetUnaligned (first_glyph, glyph_stride); + } + return count; + } + + return font->parent->get_nominal_glyphs (count, + first_unicode, unicode_stride, + first_glyph, glyph_stride); +} + +static hb_bool_t +hb_font_get_variation_glyph_nil (hb_font_t *font HB_UNUSED, + void *font_data HB_UNUSED, + hb_codepoint_t unicode HB_UNUSED, + hb_codepoint_t variation_selector HB_UNUSED, + hb_codepoint_t *glyph, + void *user_data HB_UNUSED) +{ + *glyph = 0; + return false; +} + +static hb_bool_t +hb_font_get_variation_glyph_default (hb_font_t *font, + void *font_data HB_UNUSED, + hb_codepoint_t unicode, + hb_codepoint_t variation_selector, + hb_codepoint_t *glyph, + void *user_data HB_UNUSED) +{ + return font->parent->get_variation_glyph (unicode, variation_selector, glyph); +} + + +static hb_position_t +hb_font_get_glyph_h_advance_nil (hb_font_t *font, + void *font_data HB_UNUSED, + hb_codepoint_t glyph HB_UNUSED, + void *user_data HB_UNUSED) +{ + return font->x_scale; +} + +static hb_position_t +hb_font_get_glyph_h_advance_default (hb_font_t *font, + void *font_data HB_UNUSED, + hb_codepoint_t glyph, + void *user_data HB_UNUSED) +{ + if (font->has_glyph_h_advances_func_set ()) + { + hb_position_t ret; + font->get_glyph_h_advances (1, &glyph, 0, &ret, 0); + return ret; + } + return font->parent_scale_x_distance (font->parent->get_glyph_h_advance (glyph)); +} + +static hb_position_t +hb_font_get_glyph_v_advance_nil (hb_font_t *font, + void *font_data HB_UNUSED, + hb_codepoint_t glyph HB_UNUSED, + void *user_data HB_UNUSED) +{ + /* TODO use font_extents.ascender+descender */ + return font->y_scale; +} + +static hb_position_t +hb_font_get_glyph_v_advance_default (hb_font_t *font, + void *font_data HB_UNUSED, + hb_codepoint_t glyph, + void *user_data HB_UNUSED) +{ + if (font->has_glyph_v_advances_func_set ()) + { + hb_position_t ret; + font->get_glyph_v_advances (1, &glyph, 0, &ret, 0); + return ret; + } + return font->parent_scale_y_distance (font->parent->get_glyph_v_advance (glyph)); +} + +#define hb_font_get_glyph_h_advances_nil hb_font_get_glyph_h_advances_default + +static void +hb_font_get_glyph_h_advances_default (hb_font_t* font, + void* font_data HB_UNUSED, + unsigned int count, + const hb_codepoint_t *first_glyph, + unsigned int glyph_stride, + hb_position_t *first_advance, + unsigned int advance_stride, + void *user_data HB_UNUSED) +{ + if (font->has_glyph_h_advance_func_set ()) + { + for (unsigned int i = 0; i < count; i++) + { + *first_advance = font->get_glyph_h_advance (*first_glyph); + first_glyph = &StructAtOffsetUnaligned (first_glyph, glyph_stride); + first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride); + } + return; + } + + font->parent->get_glyph_h_advances (count, + first_glyph, glyph_stride, + first_advance, advance_stride); + for (unsigned int i = 0; i < count; i++) + { + *first_advance = font->parent_scale_x_distance (*first_advance); + first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride); + } +} + +#define hb_font_get_glyph_v_advances_nil hb_font_get_glyph_v_advances_default +static void +hb_font_get_glyph_v_advances_default (hb_font_t* font, + void* font_data HB_UNUSED, + unsigned int count, + const hb_codepoint_t *first_glyph, + unsigned int glyph_stride, + hb_position_t *first_advance, + unsigned int advance_stride, + void *user_data HB_UNUSED) +{ + if (font->has_glyph_v_advance_func_set ()) + { + for (unsigned int i = 0; i < count; i++) + { + *first_advance = font->get_glyph_v_advance (*first_glyph); + first_glyph = &StructAtOffsetUnaligned (first_glyph, glyph_stride); + first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride); + } + return; + } + + font->parent->get_glyph_v_advances (count, + first_glyph, glyph_stride, + first_advance, advance_stride); + for (unsigned int i = 0; i < count; i++) + { + *first_advance = font->parent_scale_y_distance (*first_advance); + first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride); + } +} + +static hb_bool_t +hb_font_get_glyph_h_origin_nil (hb_font_t *font HB_UNUSED, + void *font_data HB_UNUSED, + hb_codepoint_t glyph HB_UNUSED, + hb_position_t *x, + hb_position_t *y, + void *user_data HB_UNUSED) +{ + *x = *y = 0; + return true; +} + +static hb_bool_t +hb_font_get_glyph_h_origin_default (hb_font_t *font, + void *font_data HB_UNUSED, + hb_codepoint_t glyph, + hb_position_t *x, + hb_position_t *y, + void *user_data HB_UNUSED) +{ + hb_bool_t ret = font->parent->get_glyph_h_origin (glyph, x, y); + if (ret) + font->parent_scale_position (x, y); + return ret; +} + +static hb_bool_t +hb_font_get_glyph_v_origin_nil (hb_font_t *font HB_UNUSED, + void *font_data HB_UNUSED, + hb_codepoint_t glyph HB_UNUSED, + hb_position_t *x, + hb_position_t *y, + void *user_data HB_UNUSED) +{ + *x = *y = 0; + return false; +} + +static hb_bool_t +hb_font_get_glyph_v_origin_default (hb_font_t *font, + void *font_data HB_UNUSED, + hb_codepoint_t glyph, + hb_position_t *x, + hb_position_t *y, + void *user_data HB_UNUSED) +{ + hb_bool_t ret = font->parent->get_glyph_v_origin (glyph, x, y); + if (ret) + font->parent_scale_position (x, y); + return ret; +} + +static hb_position_t +hb_font_get_glyph_h_kerning_nil (hb_font_t *font HB_UNUSED, + void *font_data HB_UNUSED, + hb_codepoint_t left_glyph HB_UNUSED, + hb_codepoint_t right_glyph HB_UNUSED, + void *user_data HB_UNUSED) +{ + return 0; +} + +static hb_position_t +hb_font_get_glyph_h_kerning_default (hb_font_t *font, + void *font_data HB_UNUSED, + hb_codepoint_t left_glyph, + hb_codepoint_t right_glyph, + void *user_data HB_UNUSED) +{ + return font->parent_scale_x_distance (font->parent->get_glyph_h_kerning (left_glyph, right_glyph)); +} + +#ifndef HB_DISABLE_DEPRECATED +static hb_position_t +hb_font_get_glyph_v_kerning_nil (hb_font_t *font HB_UNUSED, + void *font_data HB_UNUSED, + hb_codepoint_t top_glyph HB_UNUSED, + hb_codepoint_t bottom_glyph HB_UNUSED, + void *user_data HB_UNUSED) +{ + return 0; +} + +static hb_position_t +hb_font_get_glyph_v_kerning_default (hb_font_t *font, + void *font_data HB_UNUSED, + hb_codepoint_t top_glyph, + hb_codepoint_t bottom_glyph, + void *user_data HB_UNUSED) +{ + return font->parent_scale_y_distance (font->parent->get_glyph_v_kerning (top_glyph, bottom_glyph)); +} +#endif + +static hb_bool_t +hb_font_get_glyph_extents_nil (hb_font_t *font HB_UNUSED, + void *font_data HB_UNUSED, + hb_codepoint_t glyph HB_UNUSED, + hb_glyph_extents_t *extents, + void *user_data HB_UNUSED) +{ + memset (extents, 0, sizeof (*extents)); + return false; +} + +static hb_bool_t +hb_font_get_glyph_extents_default (hb_font_t *font, + void *font_data HB_UNUSED, + hb_codepoint_t glyph, + hb_glyph_extents_t *extents, + void *user_data HB_UNUSED) +{ + hb_bool_t ret = font->parent->get_glyph_extents (glyph, extents); + if (ret) { + font->parent_scale_position (&extents->x_bearing, &extents->y_bearing); + font->parent_scale_distance (&extents->width, &extents->height); + } + return ret; +} + +static hb_bool_t +hb_font_get_glyph_contour_point_nil (hb_font_t *font HB_UNUSED, + void *font_data HB_UNUSED, + hb_codepoint_t glyph HB_UNUSED, + unsigned int point_index HB_UNUSED, + hb_position_t *x, + hb_position_t *y, + void *user_data HB_UNUSED) +{ + *x = *y = 0; + return false; +} + +static hb_bool_t +hb_font_get_glyph_contour_point_default (hb_font_t *font, + void *font_data HB_UNUSED, + hb_codepoint_t glyph, + unsigned int point_index, + hb_position_t *x, + hb_position_t *y, + void *user_data HB_UNUSED) +{ + hb_bool_t ret = font->parent->get_glyph_contour_point (glyph, point_index, x, y); + if (ret) + font->parent_scale_position (x, y); + return ret; +} + +static hb_bool_t +hb_font_get_glyph_name_nil (hb_font_t *font HB_UNUSED, + void *font_data HB_UNUSED, + hb_codepoint_t glyph HB_UNUSED, + char *name, + unsigned int size, + void *user_data HB_UNUSED) +{ + if (size) *name = '\0'; + return false; +} + +static hb_bool_t +hb_font_get_glyph_name_default (hb_font_t *font, + void *font_data HB_UNUSED, + hb_codepoint_t glyph, + char *name, + unsigned int size, + void *user_data HB_UNUSED) +{ + return font->parent->get_glyph_name (glyph, name, size); +} + +static hb_bool_t +hb_font_get_glyph_from_name_nil (hb_font_t *font HB_UNUSED, + void *font_data HB_UNUSED, + const char *name HB_UNUSED, + int len HB_UNUSED, /* -1 means nul-terminated */ + hb_codepoint_t *glyph, + void *user_data HB_UNUSED) +{ + *glyph = 0; + return false; +} + +static hb_bool_t +hb_font_get_glyph_from_name_default (hb_font_t *font, + void *font_data HB_UNUSED, + const char *name, + int len, /* -1 means nul-terminated */ + hb_codepoint_t *glyph, + void *user_data HB_UNUSED) +{ + return font->parent->get_glyph_from_name (name, len, glyph); +} + +static void +hb_font_get_glyph_shape_nil (hb_font_t *font HB_UNUSED, + void *font_data HB_UNUSED, + hb_codepoint_t glyph, + hb_draw_funcs_t *draw_funcs, + void *draw_data, + void *user_data HB_UNUSED) +{ +} + + +typedef struct hb_font_get_glyph_shape_default_adaptor_t { + hb_draw_funcs_t *draw_funcs; + void *draw_data; + float x_scale; + float y_scale; +} hb_font_get_glyph_shape_default_adaptor_t; + +static void +hb_draw_move_to_default (hb_draw_funcs_t *dfuncs HB_UNUSED, + void *draw_data, + hb_draw_state_t *st, + float to_x, float to_y, + void *user_data HB_UNUSED) +{ + hb_font_get_glyph_shape_default_adaptor_t *adaptor = (hb_font_get_glyph_shape_default_adaptor_t *) draw_data; + float x_scale = adaptor->x_scale; + float y_scale = adaptor->y_scale; + + adaptor->draw_funcs->emit_move_to (adaptor->draw_data, *st, + x_scale * to_x, y_scale * to_y); +} + +static void +hb_draw_line_to_default (hb_draw_funcs_t *dfuncs HB_UNUSED, void *draw_data, + hb_draw_state_t *st, + float to_x, float to_y, + void *user_data HB_UNUSED) +{ + hb_font_get_glyph_shape_default_adaptor_t *adaptor = (hb_font_get_glyph_shape_default_adaptor_t *) draw_data; + float x_scale = adaptor->x_scale; + float y_scale = adaptor->y_scale; + + st->current_x *= x_scale; + st->current_y *= y_scale; + + adaptor->draw_funcs->emit_line_to (adaptor->draw_data, *st, + x_scale * to_x, y_scale * to_y); +} + +static void +hb_draw_quadratic_to_default (hb_draw_funcs_t *dfuncs HB_UNUSED, void *draw_data, + hb_draw_state_t *st, + float control_x, float control_y, + float to_x, float to_y, + void *user_data HB_UNUSED) +{ + hb_font_get_glyph_shape_default_adaptor_t *adaptor = (hb_font_get_glyph_shape_default_adaptor_t *) draw_data; + float x_scale = adaptor->x_scale; + float y_scale = adaptor->y_scale; + + st->current_x *= x_scale; + st->current_y *= y_scale; + + adaptor->draw_funcs->emit_quadratic_to (adaptor->draw_data, *st, + x_scale * control_x, y_scale * control_y, + x_scale * to_x, y_scale * to_y); +} + +static void +hb_draw_cubic_to_default (hb_draw_funcs_t *dfuncs HB_UNUSED, void *draw_data, + hb_draw_state_t *st, + float control1_x, float control1_y, + float control2_x, float control2_y, + float to_x, float to_y, + void *user_data HB_UNUSED) +{ + hb_font_get_glyph_shape_default_adaptor_t *adaptor = (hb_font_get_glyph_shape_default_adaptor_t *) draw_data; + float x_scale = adaptor->x_scale; + float y_scale = adaptor->y_scale; + + st->current_x *= x_scale; + st->current_y *= y_scale; + + adaptor->draw_funcs->emit_cubic_to (adaptor->draw_data, *st, + x_scale * control1_x, y_scale * control1_y, + x_scale * control2_x, y_scale * control2_y, + x_scale * to_x, y_scale * to_y); +} + +static void +hb_draw_close_path_default (hb_draw_funcs_t *dfuncs HB_UNUSED, void *draw_data, + hb_draw_state_t *st, + void *user_data HB_UNUSED) +{ + hb_font_get_glyph_shape_default_adaptor_t *adaptor = (hb_font_get_glyph_shape_default_adaptor_t *) draw_data; + + adaptor->draw_funcs->emit_close_path (adaptor->draw_data, *st); +} + +static const hb_draw_funcs_t _hb_draw_funcs_default = { + HB_OBJECT_HEADER_STATIC, + + { +#define HB_DRAW_FUNC_IMPLEMENT(name) hb_draw_##name##_default, + HB_DRAW_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_DRAW_FUNC_IMPLEMENT + } +}; + +static void +hb_font_get_glyph_shape_default (hb_font_t *font, + void *font_data HB_UNUSED, + hb_codepoint_t glyph, + hb_draw_funcs_t *draw_funcs, + void *draw_data, + void *user_data HB_UNUSED) +{ + hb_font_get_glyph_shape_default_adaptor_t adaptor = { + draw_funcs, + draw_data, + (float) font->x_scale / (float) font->parent->x_scale, + (float) font->y_scale / (float) font->parent->y_scale + }; + + font->parent->get_glyph_shape (glyph, + const_cast (&_hb_draw_funcs_default), + &adaptor); +} + +DEFINE_NULL_INSTANCE (hb_font_funcs_t) = +{ + HB_OBJECT_HEADER_STATIC, + + nullptr, + nullptr, + { + { +#define HB_FONT_FUNC_IMPLEMENT(name) hb_font_get_##name##_nil, + HB_FONT_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_FONT_FUNC_IMPLEMENT + } + } +}; + +static const hb_font_funcs_t _hb_font_funcs_default = { + HB_OBJECT_HEADER_STATIC, + + nullptr, + nullptr, + { + { +#define HB_FONT_FUNC_IMPLEMENT(name) hb_font_get_##name##_default, + HB_FONT_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_FONT_FUNC_IMPLEMENT + } + } +}; + + +/** + * hb_font_funcs_create: + * + * Creates a new #hb_font_funcs_t structure of font functions. + * + * Return value: (transfer full): The font-functions structure + * + * Since: 0.9.2 + **/ +hb_font_funcs_t * +hb_font_funcs_create () +{ + hb_font_funcs_t *ffuncs; + + if (!(ffuncs = hb_object_create ())) + return hb_font_funcs_get_empty (); + + ffuncs->get = _hb_font_funcs_default.get; + + return ffuncs; +} + +/** + * hb_font_funcs_get_empty: + * + * Fetches an empty font-functions structure. + * + * Return value: (transfer full): The font-functions structure + * + * Since: 0.9.2 + **/ +hb_font_funcs_t * +hb_font_funcs_get_empty () +{ + return const_cast (&_hb_font_funcs_default); +} + +/** + * hb_font_funcs_reference: (skip) + * @ffuncs: The font-functions structure + * + * Increases the reference count on a font-functions structure. + * + * Return value: The font-functions structure + * + * Since: 0.9.2 + **/ +hb_font_funcs_t * +hb_font_funcs_reference (hb_font_funcs_t *ffuncs) +{ + return hb_object_reference (ffuncs); +} + +/** + * hb_font_funcs_destroy: (skip) + * @ffuncs: The font-functions structure + * + * Decreases the reference count on a font-functions structure. When + * the reference count reaches zero, the font-functions structure is + * destroyed, freeing all memory. + * + * Since: 0.9.2 + **/ +void +hb_font_funcs_destroy (hb_font_funcs_t *ffuncs) +{ + if (!hb_object_destroy (ffuncs)) return; + + if (ffuncs->destroy) + { +#define HB_FONT_FUNC_IMPLEMENT(name) if (ffuncs->destroy->name) \ + ffuncs->destroy->name (!ffuncs->user_data ? nullptr : ffuncs->user_data->name); + HB_FONT_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_FONT_FUNC_IMPLEMENT + } + + hb_free (ffuncs->destroy); + hb_free (ffuncs->user_data); + + hb_free (ffuncs); +} + +/** + * hb_font_funcs_set_user_data: (skip) + * @ffuncs: The font-functions structure + * @key: The user-data key to set + * @data: A pointer to the user data set + * @destroy: (nullable): A callback to call when @data is not needed anymore + * @replace: Whether to replace an existing data with the same key + * + * Attaches a user-data key/data pair to the specified font-functions structure. + * + * Return value: `true` if success, `false` otherwise + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_font_funcs_set_user_data (hb_font_funcs_t *ffuncs, + hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy /* May be NULL. */, + hb_bool_t replace) +{ + return hb_object_set_user_data (ffuncs, key, data, destroy, replace); +} + +/** + * hb_font_funcs_get_user_data: (skip) + * @ffuncs: The font-functions structure + * @key: The user-data key to query + * + * Fetches the user data associated with the specified key, + * attached to the specified font-functions structure. + * + * Return value: (transfer none): A pointer to the user data + * + * Since: 0.9.2 + **/ +void * +hb_font_funcs_get_user_data (const hb_font_funcs_t *ffuncs, + hb_user_data_key_t *key) +{ + return hb_object_get_user_data (ffuncs, key); +} + + +/** + * hb_font_funcs_make_immutable: + * @ffuncs: The font-functions structure + * + * Makes a font-functions structure immutable. + * + * Since: 0.9.2 + **/ +void +hb_font_funcs_make_immutable (hb_font_funcs_t *ffuncs) +{ + if (hb_object_is_immutable (ffuncs)) + return; + + hb_object_make_immutable (ffuncs); +} + +/** + * hb_font_funcs_is_immutable: + * @ffuncs: The font-functions structure + * + * Tests whether a font-functions structure is immutable. + * + * Return value: `true` if @ffuncs is immutable, `false` otherwise + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_font_funcs_is_immutable (hb_font_funcs_t *ffuncs) +{ + return hb_object_is_immutable (ffuncs); +} + + +#define HB_FONT_FUNC_IMPLEMENT(name) \ + \ +void \ +hb_font_funcs_set_##name##_func (hb_font_funcs_t *ffuncs, \ + hb_font_get_##name##_func_t func, \ + void *user_data, \ + hb_destroy_func_t destroy) \ +{ \ + if (hb_object_is_immutable (ffuncs)) \ + goto fail; \ + \ + if (!func) \ + { \ + if (destroy) \ + destroy (user_data); \ + destroy = nullptr; \ + user_data = nullptr; \ + } \ + \ + if (ffuncs->destroy && ffuncs->destroy->name) \ + ffuncs->destroy->name (!ffuncs->user_data ? nullptr : ffuncs->user_data->name); \ + \ + if (user_data && !ffuncs->user_data) \ + { \ + ffuncs->user_data = (decltype (ffuncs->user_data)) hb_calloc (1, sizeof (*ffuncs->user_data)); \ + if (unlikely (!ffuncs->user_data)) \ + goto fail; \ + } \ + if (destroy && !ffuncs->destroy) \ + { \ + ffuncs->destroy = (decltype (ffuncs->destroy)) hb_calloc (1, sizeof (*ffuncs->destroy)); \ + if (unlikely (!ffuncs->destroy)) \ + goto fail; \ + } \ + \ + if (func) { \ + ffuncs->get.f.name = func; \ + if (ffuncs->user_data) \ + ffuncs->user_data->name = user_data; \ + if (ffuncs->destroy) \ + ffuncs->destroy->name = destroy; \ + } else { \ + ffuncs->get.f.name = hb_font_get_##name##_default; \ + if (ffuncs->user_data) \ + ffuncs->user_data->name = nullptr; \ + if (ffuncs->destroy) \ + ffuncs->destroy->name = nullptr; \ + } \ + return; \ + \ +fail: \ + if (destroy) \ + destroy (user_data); \ +} + +HB_FONT_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_FONT_FUNC_IMPLEMENT + +bool +hb_font_t::has_func_set (unsigned int i) +{ + return this->klass->get.array[i] != _hb_font_funcs_default.get.array[i]; +} + +bool +hb_font_t::has_func (unsigned int i) +{ + return has_func_set (i) || + (parent && parent != &_hb_Null_hb_font_t && parent->has_func (i)); +} + +/* Public getters */ + +/** + * hb_font_get_h_extents: + * @font: #hb_font_t to work upon + * @extents: (out): The font extents retrieved + * + * Fetches the extents for a specified font, for horizontal + * text segments. + * + * Return value: `true` if data found, `false` otherwise + * + * Since: 1.1.3 + **/ +hb_bool_t +hb_font_get_h_extents (hb_font_t *font, + hb_font_extents_t *extents) +{ + return font->get_font_h_extents (extents); +} + +/** + * hb_font_get_v_extents: + * @font: #hb_font_t to work upon + * @extents: (out): The font extents retrieved + * + * Fetches the extents for a specified font, for vertical + * text segments. + * + * Return value: `true` if data found, `false` otherwise + * + * Since: 1.1.3 + **/ +hb_bool_t +hb_font_get_v_extents (hb_font_t *font, + hb_font_extents_t *extents) +{ + return font->get_font_v_extents (extents); +} + +/** + * hb_font_get_glyph: + * @font: #hb_font_t to work upon + * @unicode: The Unicode code point to query + * @variation_selector: A variation-selector code point + * @glyph: (out): The glyph ID retrieved + * + * Fetches the glyph ID for a Unicode code point in the specified + * font, with an optional variation selector. + * + * If @variation_selector is 0, calls hb_font_get_nominal_glyph(); + * otherwise calls hb_font_get_variation_glyph(). + * + * Return value: `true` if data found, `false` otherwise + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_font_get_glyph (hb_font_t *font, + hb_codepoint_t unicode, + hb_codepoint_t variation_selector, + hb_codepoint_t *glyph) +{ + if (unlikely (variation_selector)) + return font->get_variation_glyph (unicode, variation_selector, glyph); + return font->get_nominal_glyph (unicode, glyph); +} + +/** + * hb_font_get_nominal_glyph: + * @font: #hb_font_t to work upon + * @unicode: The Unicode code point to query + * @glyph: (out): The glyph ID retrieved + * + * Fetches the nominal glyph ID for a Unicode code point in the + * specified font. + * + * This version of the function should not be used to fetch glyph IDs + * for code points modified by variation selectors. For variation-selector + * support, user hb_font_get_variation_glyph() or use hb_font_get_glyph(). + * + * Return value: `true` if data found, `false` otherwise + * + * Since: 1.2.3 + **/ +hb_bool_t +hb_font_get_nominal_glyph (hb_font_t *font, + hb_codepoint_t unicode, + hb_codepoint_t *glyph) +{ + return font->get_nominal_glyph (unicode, glyph); +} + +/** + * hb_font_get_nominal_glyphs: + * @font: #hb_font_t to work upon + * @count: number of code points to query + * @first_unicode: The first Unicode code point to query + * @unicode_stride: The stride between successive code points + * @first_glyph: (out): The first glyph ID retrieved + * @glyph_stride: The stride between successive glyph IDs + * + * Fetches the nominal glyph IDs for a sequence of Unicode code points. Glyph + * IDs must be returned in a #hb_codepoint_t output parameter. + * + * Return value: the number of code points processed + * + * Since: 2.6.3 + **/ +unsigned int +hb_font_get_nominal_glyphs (hb_font_t *font, + unsigned int count, + const hb_codepoint_t *first_unicode, + unsigned int unicode_stride, + hb_codepoint_t *first_glyph, + unsigned int glyph_stride) +{ + return font->get_nominal_glyphs (count, + first_unicode, unicode_stride, + first_glyph, glyph_stride); +} + +/** + * hb_font_get_variation_glyph: + * @font: #hb_font_t to work upon + * @unicode: The Unicode code point to query + * @variation_selector: The variation-selector code point to query + * @glyph: (out): The glyph ID retrieved + * + * Fetches the glyph ID for a Unicode code point when followed by + * by the specified variation-selector code point, in the specified + * font. + * + * Return value: `true` if data found, `false` otherwise + * + * Since: 1.2.3 + **/ +hb_bool_t +hb_font_get_variation_glyph (hb_font_t *font, + hb_codepoint_t unicode, + hb_codepoint_t variation_selector, + hb_codepoint_t *glyph) +{ + return font->get_variation_glyph (unicode, variation_selector, glyph); +} + +/** + * hb_font_get_glyph_h_advance: + * @font: #hb_font_t to work upon + * @glyph: The glyph ID to query + * + * Fetches the advance for a glyph ID in the specified font, + * for horizontal text segments. + * + * Return value: The advance of @glyph within @font + * + * Since: 0.9.2 + **/ +hb_position_t +hb_font_get_glyph_h_advance (hb_font_t *font, + hb_codepoint_t glyph) +{ + return font->get_glyph_h_advance (glyph); +} + +/** + * hb_font_get_glyph_v_advance: + * @font: #hb_font_t to work upon + * @glyph: The glyph ID to query + * + * Fetches the advance for a glyph ID in the specified font, + * for vertical text segments. + * + * Return value: The advance of @glyph within @font + * + * Since: 0.9.2 + **/ +hb_position_t +hb_font_get_glyph_v_advance (hb_font_t *font, + hb_codepoint_t glyph) +{ + return font->get_glyph_v_advance (glyph); +} + +/** + * hb_font_get_glyph_h_advances: + * @font: #hb_font_t to work upon + * @count: The number of glyph IDs in the sequence queried + * @first_glyph: The first glyph ID to query + * @glyph_stride: The stride between successive glyph IDs + * @first_advance: (out): The first advance retrieved + * @advance_stride: The stride between successive advances + * + * Fetches the advances for a sequence of glyph IDs in the specified + * font, for horizontal text segments. + * + * Since: 1.8.6 + **/ +void +hb_font_get_glyph_h_advances (hb_font_t* font, + unsigned int count, + const hb_codepoint_t *first_glyph, + unsigned glyph_stride, + hb_position_t *first_advance, + unsigned advance_stride) +{ + font->get_glyph_h_advances (count, first_glyph, glyph_stride, first_advance, advance_stride); +} +/** + * hb_font_get_glyph_v_advances: + * @font: #hb_font_t to work upon + * @count: The number of glyph IDs in the sequence queried + * @first_glyph: The first glyph ID to query + * @glyph_stride: The stride between successive glyph IDs + * @first_advance: (out): The first advance retrieved + * @advance_stride: (out): The stride between successive advances + * + * Fetches the advances for a sequence of glyph IDs in the specified + * font, for vertical text segments. + * + * Since: 1.8.6 + **/ +void +hb_font_get_glyph_v_advances (hb_font_t* font, + unsigned int count, + const hb_codepoint_t *first_glyph, + unsigned glyph_stride, + hb_position_t *first_advance, + unsigned advance_stride) +{ + font->get_glyph_v_advances (count, first_glyph, glyph_stride, first_advance, advance_stride); +} + +/** + * hb_font_get_glyph_h_origin: + * @font: #hb_font_t to work upon + * @glyph: The glyph ID to query + * @x: (out): The X coordinate of the origin + * @y: (out): The Y coordinate of the origin + * + * Fetches the (X,Y) coordinates of the origin for a glyph ID + * in the specified font, for horizontal text segments. + * + * Return value: `true` if data found, `false` otherwise + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_font_get_glyph_h_origin (hb_font_t *font, + hb_codepoint_t glyph, + hb_position_t *x, + hb_position_t *y) +{ + return font->get_glyph_h_origin (glyph, x, y); +} + +/** + * hb_font_get_glyph_v_origin: + * @font: #hb_font_t to work upon + * @glyph: The glyph ID to query + * @x: (out): The X coordinate of the origin + * @y: (out): The Y coordinate of the origin + * + * Fetches the (X,Y) coordinates of the origin for a glyph ID + * in the specified font, for vertical text segments. + * + * Return value: `true` if data found, `false` otherwise + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_font_get_glyph_v_origin (hb_font_t *font, + hb_codepoint_t glyph, + hb_position_t *x, + hb_position_t *y) +{ + return font->get_glyph_v_origin (glyph, x, y); +} + +/** + * hb_font_get_glyph_h_kerning: + * @font: #hb_font_t to work upon + * @left_glyph: The glyph ID of the left glyph in the glyph pair + * @right_glyph: The glyph ID of the right glyph in the glyph pair + * + * Fetches the kerning-adjustment value for a glyph-pair in + * the specified font, for horizontal text segments. + * + * It handles legacy kerning only (as returned by the corresponding + * #hb_font_funcs_t function). + * + * Return value: The kerning adjustment value + * + * Since: 0.9.2 + **/ +hb_position_t +hb_font_get_glyph_h_kerning (hb_font_t *font, + hb_codepoint_t left_glyph, + hb_codepoint_t right_glyph) +{ + return font->get_glyph_h_kerning (left_glyph, right_glyph); +} + +#ifndef HB_DISABLE_DEPRECATED +/** + * hb_font_get_glyph_v_kerning: + * @font: #hb_font_t to work upon + * @top_glyph: The glyph ID of the top glyph in the glyph pair + * @bottom_glyph: The glyph ID of the bottom glyph in the glyph pair + * + * Fetches the kerning-adjustment value for a glyph-pair in + * the specified font, for vertical text segments. + * + * It handles legacy kerning only (as returned by the corresponding + * #hb_font_funcs_t function). + * + * Return value: The kerning adjustment value + * + * Since: 0.9.2 + * Deprecated: 2.0.0 + **/ +hb_position_t +hb_font_get_glyph_v_kerning (hb_font_t *font, + hb_codepoint_t top_glyph, + hb_codepoint_t bottom_glyph) +{ + return font->get_glyph_v_kerning (top_glyph, bottom_glyph); +} +#endif + +/** + * hb_font_get_glyph_extents: + * @font: #hb_font_t to work upon + * @glyph: The glyph ID to query + * @extents: (out): The #hb_glyph_extents_t retrieved + * + * Fetches the #hb_glyph_extents_t data for a glyph ID + * in the specified font. + * + * Return value: `true` if data found, `false` otherwise + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_font_get_glyph_extents (hb_font_t *font, + hb_codepoint_t glyph, + hb_glyph_extents_t *extents) +{ + return font->get_glyph_extents (glyph, extents); +} + +/** + * hb_font_get_glyph_contour_point: + * @font: #hb_font_t to work upon + * @glyph: The glyph ID to query + * @point_index: The contour-point index to query + * @x: (out): The X value retrieved for the contour point + * @y: (out): The Y value retrieved for the contour point + * + * Fetches the (x,y) coordinates of a specified contour-point index + * in the specified glyph, within the specified font. + * + * Return value: `true` if data found, `false` otherwise + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_font_get_glyph_contour_point (hb_font_t *font, + hb_codepoint_t glyph, + unsigned int point_index, + hb_position_t *x, + hb_position_t *y) +{ + return font->get_glyph_contour_point (glyph, point_index, x, y); +} + +/** + * hb_font_get_glyph_name: + * @font: #hb_font_t to work upon + * @glyph: The glyph ID to query + * @name: (out) (array length=size): Name string retrieved for the glyph ID + * @size: Length of the glyph-name string retrieved + * + * Fetches the glyph-name string for a glyph ID in the specified @font. + * + * Return value: `true` if data found, `false` otherwise + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_font_get_glyph_name (hb_font_t *font, + hb_codepoint_t glyph, + char *name, + unsigned int size) +{ + return font->get_glyph_name (glyph, name, size); +} + +/** + * hb_font_get_glyph_from_name: + * @font: #hb_font_t to work upon + * @name: (array length=len): The name string to query + * @len: The length of the name queried + * @glyph: (out): The glyph ID retrieved + * + * Fetches the glyph ID that corresponds to a name string in the specified @font. + * + * Note: @len == -1 means the name string is null-terminated. + * + * Return value: `true` if data found, `false` otherwise + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_font_get_glyph_from_name (hb_font_t *font, + const char *name, + int len, /* -1 means nul-terminated */ + hb_codepoint_t *glyph) +{ + return font->get_glyph_from_name (name, len, glyph); +} + +/** + * hb_font_get_glyph_shape: + * @font: #hb_font_t to work upon + * @glyph: : The glyph ID + * @dfuncs: #hb_draw_funcs_t to draw to + * @draw_data: User data to pass to draw callbacks + * + * Fetches the glyph shape that corresponds to a glyph in the specified @font. + * The shape is returned by way of calls to the callsbacks of the @dfuncs + * objects, with @draw_data passed to them. + * + * Since: 4.0.0 + **/ +void +hb_font_get_glyph_shape (hb_font_t *font, + hb_codepoint_t glyph, + hb_draw_funcs_t *dfuncs, void *draw_data) +{ + font->get_glyph_shape (glyph, dfuncs, draw_data); +} + +/* A bit higher-level, and with fallback */ + +/** + * hb_font_get_extents_for_direction: + * @font: #hb_font_t to work upon + * @direction: The direction of the text segment + * @extents: (out): The #hb_font_extents_t retrieved + * + * Fetches the extents for a font in a text segment of the + * specified direction. + * + * Calls the appropriate direction-specific variant (horizontal + * or vertical) depending on the value of @direction. + * + * Since: 1.1.3 + **/ +void +hb_font_get_extents_for_direction (hb_font_t *font, + hb_direction_t direction, + hb_font_extents_t *extents) +{ + font->get_extents_for_direction (direction, extents); +} +/** + * hb_font_get_glyph_advance_for_direction: + * @font: #hb_font_t to work upon + * @glyph: The glyph ID to query + * @direction: The direction of the text segment + * @x: (out): The horizontal advance retrieved + * @y: (out): The vertical advance retrieved + * + * Fetches the advance for a glyph ID from the specified font, + * in a text segment of the specified direction. + * + * Calls the appropriate direction-specific variant (horizontal + * or vertical) depending on the value of @direction. + * + * Since: 0.9.2 + **/ +void +hb_font_get_glyph_advance_for_direction (hb_font_t *font, + hb_codepoint_t glyph, + hb_direction_t direction, + hb_position_t *x, + hb_position_t *y) +{ + font->get_glyph_advance_for_direction (glyph, direction, x, y); +} +/** + * hb_font_get_glyph_advances_for_direction: + * @font: #hb_font_t to work upon + * @direction: The direction of the text segment + * @count: The number of glyph IDs in the sequence queried + * @first_glyph: The first glyph ID to query + * @glyph_stride: The stride between successive glyph IDs + * @first_advance: (out): The first advance retrieved + * @advance_stride: (out): The stride between successive advances + * + * Fetches the advances for a sequence of glyph IDs in the specified + * font, in a text segment of the specified direction. + * + * Calls the appropriate direction-specific variant (horizontal + * or vertical) depending on the value of @direction. + * + * Since: 1.8.6 + **/ +HB_EXTERN void +hb_font_get_glyph_advances_for_direction (hb_font_t* font, + hb_direction_t direction, + unsigned int count, + const hb_codepoint_t *first_glyph, + unsigned glyph_stride, + hb_position_t *first_advance, + unsigned advance_stride) +{ + font->get_glyph_advances_for_direction (direction, count, first_glyph, glyph_stride, first_advance, advance_stride); +} + +/** + * hb_font_get_glyph_origin_for_direction: + * @font: #hb_font_t to work upon + * @glyph: The glyph ID to query + * @direction: The direction of the text segment + * @x: (out): The X coordinate retrieved for the origin + * @y: (out): The Y coordinate retrieved for the origin + * + * Fetches the (X,Y) coordinates of the origin for a glyph in + * the specified font. + * + * Calls the appropriate direction-specific variant (horizontal + * or vertical) depending on the value of @direction. + * + * Since: 0.9.2 + **/ +void +hb_font_get_glyph_origin_for_direction (hb_font_t *font, + hb_codepoint_t glyph, + hb_direction_t direction, + hb_position_t *x, + hb_position_t *y) +{ + return font->get_glyph_origin_for_direction (glyph, direction, x, y); +} + +/** + * hb_font_add_glyph_origin_for_direction: + * @font: #hb_font_t to work upon + * @glyph: The glyph ID to query + * @direction: The direction of the text segment + * @x: (inout): Input = The original X coordinate + * Output = The X coordinate plus the X-coordinate of the origin + * @y: (inout): Input = The original Y coordinate + * Output = The Y coordinate plus the Y-coordinate of the origin + * + * Adds the origin coordinates to an (X,Y) point coordinate, in + * the specified glyph ID in the specified font. + * + * Calls the appropriate direction-specific variant (horizontal + * or vertical) depending on the value of @direction. + * + * Since: 0.9.2 + **/ +void +hb_font_add_glyph_origin_for_direction (hb_font_t *font, + hb_codepoint_t glyph, + hb_direction_t direction, + hb_position_t *x, + hb_position_t *y) +{ + return font->add_glyph_origin_for_direction (glyph, direction, x, y); +} + +/** + * hb_font_subtract_glyph_origin_for_direction: + * @font: #hb_font_t to work upon + * @glyph: The glyph ID to query + * @direction: The direction of the text segment + * @x: (inout): Input = The original X coordinate + * Output = The X coordinate minus the X-coordinate of the origin + * @y: (inout): Input = The original Y coordinate + * Output = The Y coordinate minus the Y-coordinate of the origin + * + * Subtracts the origin coordinates from an (X,Y) point coordinate, + * in the specified glyph ID in the specified font. + * + * Calls the appropriate direction-specific variant (horizontal + * or vertical) depending on the value of @direction. + * + * Since: 0.9.2 + **/ +void +hb_font_subtract_glyph_origin_for_direction (hb_font_t *font, + hb_codepoint_t glyph, + hb_direction_t direction, + hb_position_t *x, + hb_position_t *y) +{ + return font->subtract_glyph_origin_for_direction (glyph, direction, x, y); +} + +/** + * hb_font_get_glyph_kerning_for_direction: + * @font: #hb_font_t to work upon + * @first_glyph: The glyph ID of the first glyph in the glyph pair to query + * @second_glyph: The glyph ID of the second glyph in the glyph pair to query + * @direction: The direction of the text segment + * @x: (out): The horizontal kerning-adjustment value retrieved + * @y: (out): The vertical kerning-adjustment value retrieved + * + * Fetches the kerning-adjustment value for a glyph-pair in the specified font. + * + * Calls the appropriate direction-specific variant (horizontal + * or vertical) depending on the value of @direction. + * + * Since: 0.9.2 + **/ +void +hb_font_get_glyph_kerning_for_direction (hb_font_t *font, + hb_codepoint_t first_glyph, + hb_codepoint_t second_glyph, + hb_direction_t direction, + hb_position_t *x, + hb_position_t *y) +{ + return font->get_glyph_kerning_for_direction (first_glyph, second_glyph, direction, x, y); +} + +/** + * hb_font_get_glyph_extents_for_origin: + * @font: #hb_font_t to work upon + * @glyph: The glyph ID to query + * @direction: The direction of the text segment + * @extents: (out): The #hb_glyph_extents_t retrieved + * + * Fetches the #hb_glyph_extents_t data for a glyph ID + * in the specified font, with respect to the origin in + * a text segment in the specified direction. + * + * Calls the appropriate direction-specific variant (horizontal + * or vertical) depending on the value of @direction. + * + * Return value: `true` if data found, `false` otherwise + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_font_get_glyph_extents_for_origin (hb_font_t *font, + hb_codepoint_t glyph, + hb_direction_t direction, + hb_glyph_extents_t *extents) +{ + return font->get_glyph_extents_for_origin (glyph, direction, extents); +} + +/** + * hb_font_get_glyph_contour_point_for_origin: + * @font: #hb_font_t to work upon + * @glyph: The glyph ID to query + * @point_index: The contour-point index to query + * @direction: The direction of the text segment + * @x: (out): The X value retrieved for the contour point + * @y: (out): The Y value retrieved for the contour point + * + * Fetches the (X,Y) coordinates of a specified contour-point index + * in the specified glyph ID in the specified font, with respect + * to the origin in a text segment in the specified direction. + * + * Calls the appropriate direction-specific variant (horizontal + * or vertical) depending on the value of @direction. + * + * Return value: `true` if data found, `false` otherwise + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_font_get_glyph_contour_point_for_origin (hb_font_t *font, + hb_codepoint_t glyph, + unsigned int point_index, + hb_direction_t direction, + hb_position_t *x, + hb_position_t *y) +{ + return font->get_glyph_contour_point_for_origin (glyph, point_index, direction, x, y); +} + +/** + * hb_font_glyph_to_string: + * @font: #hb_font_t to work upon + * @glyph: The glyph ID to query + * @s: (out) (array length=size): The string containing the glyph name + * @size: Length of string @s + * + * Fetches the name of the specified glyph ID in @font and returns + * it in string @s. + * + * If the glyph ID has no name in @font, a string of the form `gidDDD` is + * generated, with `DDD` being the glyph ID. + * + * Since: 0.9.2 + **/ +void +hb_font_glyph_to_string (hb_font_t *font, + hb_codepoint_t glyph, + char *s, + unsigned int size) +{ + font->glyph_to_string (glyph, s, size); +} + +/** + * hb_font_glyph_from_string: + * @font: #hb_font_t to work upon + * @s: (array length=len) (element-type uint8_t): string to query + * @len: The length of the string @s + * @glyph: (out): The glyph ID corresponding to the string requested + * + * Fetches the glyph ID from @font that matches the specified string. + * Strings of the format `gidDDD` or `uniUUUU` are parsed automatically. + * + * Note: @len == -1 means the string is null-terminated. + * + * Return value: `true` if data found, `false` otherwise + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_font_glyph_from_string (hb_font_t *font, + const char *s, + int len, + hb_codepoint_t *glyph) +{ + return font->glyph_from_string (s, len, glyph); +} + + +/* + * hb_font_t + */ + +DEFINE_NULL_INSTANCE (hb_font_t) = +{ + HB_OBJECT_HEADER_STATIC, + + 0, /* serial */ + 0, /* serial_coords */ + + nullptr, /* parent */ + const_cast (&_hb_Null_hb_face_t), + + 1000, /* x_scale */ + 1000, /* y_scale */ + 0., /* slant */ + 0., /* slant_xy; */ + 1.f, /* x_multf */ + 1.f, /* y_multf */ + 1<<16, /* x_mult */ + 1<<16, /* y_mult */ + + 0, /* x_ppem */ + 0, /* y_ppem */ + 0, /* ptem */ + + 0, /* num_coords */ + nullptr, /* coords */ + nullptr, /* design_coords */ + + const_cast (&_hb_Null_hb_font_funcs_t), + + /* Zero for the rest is fine. */ +}; + + +static hb_font_t * +_hb_font_create (hb_face_t *face) +{ + hb_font_t *font; + + if (unlikely (!face)) + face = hb_face_get_empty (); + + if (!(font = hb_object_create ())) + return hb_font_get_empty (); + + hb_face_make_immutable (face); + font->parent = hb_font_get_empty (); + font->face = hb_face_reference (face); + font->klass = hb_font_funcs_get_empty (); + font->data.init0 (font); + font->x_scale = font->y_scale = face->get_upem (); + font->x_multf = font->y_multf = 1.f; + font->x_mult = font->y_mult = 1 << 16; + + return font; +} + +/** + * hb_font_create: + * @face: a face. + * + * Constructs a new font object from the specified face. + * + * Note: If @face's index value (as passed to hb_face_create() + * has non-zero top 16-bits, those bits minus one are passed to + * hb_font_set_var_named_instance(), effectively loading a named-instance + * of a variable font, instead of the default-instance. This allows + * specifying which named-instance to load by default when creating the + * face. + * + * Return value: (transfer full): The new font object + * + * Since: 0.9.2 + **/ +hb_font_t * +hb_font_create (hb_face_t *face) +{ + hb_font_t *font = _hb_font_create (face); + +#ifndef HB_NO_OT_FONT + /* Install our in-house, very lightweight, funcs. */ + hb_ot_font_set_funcs (font); +#endif + +#ifndef HB_NO_VAR + if (face && face->index >> 16) + hb_font_set_var_named_instance (font, (face->index >> 16) - 1); +#endif + + return font; +} + +static void +_hb_font_adopt_var_coords (hb_font_t *font, + int *coords, /* 2.14 normalized */ + float *design_coords, + unsigned int coords_length) +{ + hb_free (font->coords); + hb_free (font->design_coords); + + font->coords = coords; + font->design_coords = design_coords; + font->num_coords = coords_length; + + font->mults_changed (); // Easiest to call this to drop cached data +} + +/** + * hb_font_create_sub_font: + * @parent: The parent font object + * + * Constructs a sub-font font object from the specified @parent font, + * replicating the parent's properties. + * + * Return value: (transfer full): The new sub-font font object + * + * Since: 0.9.2 + **/ +hb_font_t * +hb_font_create_sub_font (hb_font_t *parent) +{ + if (unlikely (!parent)) + parent = hb_font_get_empty (); + + hb_font_t *font = _hb_font_create (parent->face); + + if (unlikely (hb_object_is_immutable (font))) + return font; + + font->parent = hb_font_reference (parent); + + font->x_scale = parent->x_scale; + font->y_scale = parent->y_scale; + font->slant = parent->slant; + font->x_ppem = parent->x_ppem; + font->y_ppem = parent->y_ppem; + font->ptem = parent->ptem; + + unsigned int num_coords = parent->num_coords; + if (num_coords) + { + int *coords = (int *) hb_calloc (num_coords, sizeof (parent->coords[0])); + float *design_coords = (float *) hb_calloc (num_coords, sizeof (parent->design_coords[0])); + if (likely (coords && design_coords)) + { + memcpy (coords, parent->coords, num_coords * sizeof (parent->coords[0])); + memcpy (design_coords, parent->design_coords, num_coords * sizeof (parent->design_coords[0])); + _hb_font_adopt_var_coords (font, coords, design_coords, num_coords); + } + else + { + hb_free (coords); + hb_free (design_coords); + } + } + + font->mults_changed (); + + return font; +} + +/** + * hb_font_get_empty: + * + * Fetches the empty font object. + * + * Return value: (transfer full): The empty font object + * + * Since: 0.9.2 + **/ +hb_font_t * +hb_font_get_empty () +{ + return const_cast (&Null (hb_font_t)); +} + +/** + * hb_font_reference: (skip) + * @font: #hb_font_t to work upon + * + * Increases the reference count on the given font object. + * + * Return value: (transfer full): The @font object + * + * Since: 0.9.2 + **/ +hb_font_t * +hb_font_reference (hb_font_t *font) +{ + return hb_object_reference (font); +} + +/** + * hb_font_destroy: (skip) + * @font: #hb_font_t to work upon + * + * Decreases the reference count on the given font object. When the + * reference count reaches zero, the font is destroyed, + * freeing all memory. + * + * Since: 0.9.2 + **/ +void +hb_font_destroy (hb_font_t *font) +{ + if (!hb_object_destroy (font)) return; + + font->data.fini (); + + if (font->destroy) + font->destroy (font->user_data); + + hb_font_destroy (font->parent); + hb_face_destroy (font->face); + hb_font_funcs_destroy (font->klass); + + hb_free (font->coords); + hb_free (font->design_coords); + + hb_free (font); +} + +/** + * hb_font_set_user_data: (skip) + * @font: #hb_font_t to work upon + * @key: The user-data key + * @data: A pointer to the user data + * @destroy: (nullable): A callback to call when @data is not needed anymore + * @replace: Whether to replace an existing data with the same key + * + * Attaches a user-data key/data pair to the specified font object. + * + * Return value: `true` if success, `false` otherwise + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_font_set_user_data (hb_font_t *font, + hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy /* May be NULL. */, + hb_bool_t replace) +{ + if (!hb_object_is_immutable (font)) + font->serial++; + + return hb_object_set_user_data (font, key, data, destroy, replace); +} + +/** + * hb_font_get_user_data: (skip) + * @font: #hb_font_t to work upon + * @key: The user-data key to query + * + * Fetches the user-data object associated with the specified key, + * attached to the specified font object. + * + * Return value: (transfer none): Pointer to the user data + * + * Since: 0.9.2 + **/ +void * +hb_font_get_user_data (const hb_font_t *font, + hb_user_data_key_t *key) +{ + return hb_object_get_user_data (font, key); +} + +/** + * hb_font_make_immutable: + * @font: #hb_font_t to work upon + * + * Makes @font immutable. + * + * Since: 0.9.2 + **/ +void +hb_font_make_immutable (hb_font_t *font) +{ + if (hb_object_is_immutable (font)) + return; + + if (font->parent) + hb_font_make_immutable (font->parent); + + hb_object_make_immutable (font); +} + +/** + * hb_font_is_immutable: + * @font: #hb_font_t to work upon + * + * Tests whether a font object is immutable. + * + * Return value: `true` if @font is immutable, `false` otherwise + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_font_is_immutable (hb_font_t *font) +{ + return hb_object_is_immutable (font); +} + +/** + * hb_font_get_serial: + * @font: #hb_font_t to work upon + * + * Returns the internal serial number of the font. The serial + * number is increased every time a setting on the font is + * changed, using a setter function. + * + * Return value: serial number + * + * Since: 4.4.0 + **/ +unsigned int +hb_font_get_serial (hb_font_t *font) +{ + return font->serial; +} + +/** + * hb_font_changed: + * @font: #hb_font_t to work upon + * + * Notifies the @font that underlying font data has changed. + * This has the effect of increasing the serial as returned + * by hb_font_get_serial(), which invalidates internal caches. + * + * Since: 4.4.0 + **/ +void +hb_font_changed (hb_font_t *font) +{ + if (hb_object_is_immutable (font)) + return; + + font->serial++; + + font->mults_changed (); +} + +/** + * hb_font_set_parent: + * @font: #hb_font_t to work upon + * @parent: The parent font object to assign + * + * Sets the parent font of @font. + * + * Since: 1.0.5 + **/ +void +hb_font_set_parent (hb_font_t *font, + hb_font_t *parent) +{ + if (hb_object_is_immutable (font)) + return; + + if (parent == font->parent) + return; + + font->serial++; + + if (!parent) + parent = hb_font_get_empty (); + + hb_font_t *old = font->parent; + + font->parent = hb_font_reference (parent); + + hb_font_destroy (old); +} + +/** + * hb_font_get_parent: + * @font: #hb_font_t to work upon + * + * Fetches the parent font of @font. + * + * Return value: (transfer none): The parent font object + * + * Since: 0.9.2 + **/ +hb_font_t * +hb_font_get_parent (hb_font_t *font) +{ + return font->parent; +} + +/** + * hb_font_set_face: + * @font: #hb_font_t to work upon + * @face: The #hb_face_t to assign + * + * Sets @face as the font-face value of @font. + * + * Since: 1.4.3 + **/ +void +hb_font_set_face (hb_font_t *font, + hb_face_t *face) +{ + if (hb_object_is_immutable (font)) + return; + + if (face == font->face) + return; + + font->serial++; + + if (unlikely (!face)) + face = hb_face_get_empty (); + + hb_face_t *old = font->face; + + hb_face_make_immutable (face); + font->face = hb_face_reference (face); + font->mults_changed (); + + hb_face_destroy (old); +} + +/** + * hb_font_get_face: + * @font: #hb_font_t to work upon + * + * Fetches the face associated with the specified font object. + * + * Return value: (transfer none): The #hb_face_t value + * + * Since: 0.9.2 + **/ +hb_face_t * +hb_font_get_face (hb_font_t *font) +{ + return font->face; +} + + +/** + * hb_font_set_funcs: + * @font: #hb_font_t to work upon + * @klass: (closure font_data) (destroy destroy) (scope notified): The font-functions structure. + * @font_data: Data to attach to @font + * @destroy: (nullable): The function to call when @font_data is not needed anymore + * + * Replaces the font-functions structure attached to a font, updating + * the font's user-data with @font-data and the @destroy callback. + * + * Since: 0.9.2 + **/ +void +hb_font_set_funcs (hb_font_t *font, + hb_font_funcs_t *klass, + void *font_data, + hb_destroy_func_t destroy /* May be NULL. */) +{ + if (hb_object_is_immutable (font)) + { + if (destroy) + destroy (font_data); + return; + } + + font->serial++; + + if (font->destroy) + font->destroy (font->user_data); + + if (!klass) + klass = hb_font_funcs_get_empty (); + + hb_font_funcs_reference (klass); + hb_font_funcs_destroy (font->klass); + font->klass = klass; + font->user_data = font_data; + font->destroy = destroy; +} + +/** + * hb_font_set_funcs_data: + * @font: #hb_font_t to work upon + * @font_data: (destroy destroy) (scope notified): Data to attach to @font + * @destroy: (nullable): The function to call when @font_data is not needed anymore + * + * Replaces the user data attached to a font, updating the font's + * @destroy callback. + * + * Since: 0.9.2 + **/ +void +hb_font_set_funcs_data (hb_font_t *font, + void *font_data, + hb_destroy_func_t destroy /* May be NULL. */) +{ + /* Destroy user_data? */ + if (hb_object_is_immutable (font)) + { + if (destroy) + destroy (font_data); + return; + } + + font->serial++; + + if (font->destroy) + font->destroy (font->user_data); + + font->user_data = font_data; + font->destroy = destroy; +} + + +/** + * hb_font_set_scale: + * @font: #hb_font_t to work upon + * @x_scale: Horizontal scale value to assign + * @y_scale: Vertical scale value to assign + * + * Sets the horizontal and vertical scale of a font. + * + * Since: 0.9.2 + **/ +void +hb_font_set_scale (hb_font_t *font, + int x_scale, + int y_scale) +{ + if (hb_object_is_immutable (font)) + return; + + if (font->x_scale == x_scale && font->y_scale == y_scale) + return; + + font->serial++; + + font->x_scale = x_scale; + font->y_scale = y_scale; + font->mults_changed (); +} + +/** + * hb_font_get_scale: + * @font: #hb_font_t to work upon + * @x_scale: (out): Horizontal scale value + * @y_scale: (out): Vertical scale value + * + * Fetches the horizontal and vertical scale of a font. + * + * Since: 0.9.2 + **/ +void +hb_font_get_scale (hb_font_t *font, + int *x_scale, + int *y_scale) +{ + if (x_scale) *x_scale = font->x_scale; + if (y_scale) *y_scale = font->y_scale; +} + +/** + * hb_font_set_ppem: + * @font: #hb_font_t to work upon + * @x_ppem: Horizontal ppem value to assign + * @y_ppem: Vertical ppem value to assign + * + * Sets the horizontal and vertical pixels-per-em (ppem) of a font. + * + * Since: 0.9.2 + **/ +void +hb_font_set_ppem (hb_font_t *font, + unsigned int x_ppem, + unsigned int y_ppem) +{ + if (hb_object_is_immutable (font)) + return; + + if (font->x_ppem == x_ppem && font->y_ppem == y_ppem) + return; + + font->serial++; + + font->x_ppem = x_ppem; + font->y_ppem = y_ppem; +} + +/** + * hb_font_get_ppem: + * @font: #hb_font_t to work upon + * @x_ppem: (out): Horizontal ppem value + * @y_ppem: (out): Vertical ppem value + * + * Fetches the horizontal and vertical points-per-em (ppem) of a font. + * + * Since: 0.9.2 + **/ +void +hb_font_get_ppem (hb_font_t *font, + unsigned int *x_ppem, + unsigned int *y_ppem) +{ + if (x_ppem) *x_ppem = font->x_ppem; + if (y_ppem) *y_ppem = font->y_ppem; +} + +/** + * hb_font_set_ptem: + * @font: #hb_font_t to work upon + * @ptem: font size in points. + * + * Sets the "point size" of a font. Set to zero to unset. + * Used in CoreText to implement optical sizing. + * + * Note: There are 72 points in an inch. + * + * Since: 1.6.0 + **/ +void +hb_font_set_ptem (hb_font_t *font, + float ptem) +{ + if (hb_object_is_immutable (font)) + return; + + if (font->ptem == ptem) + return; + + font->serial++; + + font->ptem = ptem; +} + +/** + * hb_font_get_ptem: + * @font: #hb_font_t to work upon + * + * Fetches the "point size" of a font. Used in CoreText to + * implement optical sizing. + * + * Return value: Point size. A value of zero means "not set." + * + * Since: 1.6.0 + **/ +float +hb_font_get_ptem (hb_font_t *font) +{ + return font->ptem; +} + +/** + * hb_font_set_synthetic_slant: + * @font: #hb_font_t to work upon + * @slant: synthetic slant value. + * + * Sets the "synthetic slant" of a font. By default is zero. + * Synthetic slant is the graphical skew applied to the font + * at rendering time. + * + * HarfBuzz needs to know this value to adjust shaping results, + * metrics, and style values to match the slanted rendering. + * + * Note: The glyph shape fetched via the + * hb_font_get_glyph_shape() is slanted to reflect this value + * as well. + * + * Note: The slant value is a ratio. For example, a + * 20% slant would be represented as a 0.2 value. + * + * Since: 3.3.0 + **/ +HB_EXTERN void +hb_font_set_synthetic_slant (hb_font_t *font, float slant) +{ + if (hb_object_is_immutable (font)) + return; + + if (font->slant == slant) + return; + + font->serial++; + + font->slant = slant; + font->mults_changed (); +} + +/** + * hb_font_get_synthetic_slant: + * @font: #hb_font_t to work upon + * + * Fetches the "synthetic slant" of a font. + * + * Return value: Synthetic slant. By default is zero. + * + * Since: 3.3.0 + **/ +HB_EXTERN float +hb_font_get_synthetic_slant (hb_font_t *font) +{ + return font->slant; +} + +#ifndef HB_NO_VAR +/* + * Variations + */ + +/** + * hb_font_set_variations: + * @font: #hb_font_t to work upon + * @variations: (array length=variations_length): Array of variation settings to apply + * @variations_length: Number of variations to apply + * + * Applies a list of font-variation settings to a font. + * + * Note that this overrides all existing variations set on @font. + * Axes not included in @variations will be effectively set to their + * default values. + * + * Since: 1.4.2 + */ +void +hb_font_set_variations (hb_font_t *font, + const hb_variation_t *variations, + unsigned int variations_length) +{ + if (hb_object_is_immutable (font)) + return; + + font->serial_coords = ++font->serial; + + if (!variations_length) + { + hb_font_set_var_coords_normalized (font, nullptr, 0); + return; + } + + const OT::fvar &fvar = *font->face->table.fvar; + auto axes = fvar.get_axes (); + const unsigned coords_length = axes.length; + + int *normalized = coords_length ? (int *) hb_calloc (coords_length, sizeof (int)) : nullptr; + float *design_coords = coords_length ? (float *) hb_calloc (coords_length, sizeof (float)) : nullptr; + + if (unlikely (coords_length && !(normalized && design_coords))) + { + hb_free (normalized); + hb_free (design_coords); + return; + } + + /* Initialize design coords to default from fvar. */ + for (unsigned int i = 0; i < coords_length; i++) + design_coords[i] = axes[i].get_default (); + + for (unsigned int i = 0; i < variations_length; i++) + { + const auto tag = variations[i].tag; + const auto v = variations[i].value; + for (unsigned axis_index = 0; axis_index < coords_length; axis_index++) + if (axes[axis_index].axisTag == tag) + { + design_coords[axis_index] = v; + normalized[axis_index] = fvar.normalize_axis_value (axis_index, v); + } + } + font->face->table.avar->map_coords (normalized, coords_length); + + _hb_font_adopt_var_coords (font, normalized, design_coords, coords_length); +} + +/** + * hb_font_set_var_coords_design: + * @font: #hb_font_t to work upon + * @coords: (array length=coords_length): Array of variation coordinates to apply + * @coords_length: Number of coordinates to apply + * + * Applies a list of variation coordinates (in design-space units) + * to a font. + * + * Note that this overrides all existing variations set on @font. + * Axes not included in @coords will be effectively set to their + * default values. + * + * Since: 1.4.2 + */ +void +hb_font_set_var_coords_design (hb_font_t *font, + const float *coords, + unsigned int coords_length) +{ + if (hb_object_is_immutable (font)) + return; + + font->serial_coords = ++font->serial; + + int *normalized = coords_length ? (int *) hb_calloc (coords_length, sizeof (int)) : nullptr; + float *design_coords = coords_length ? (float *) hb_calloc (coords_length, sizeof (float)) : nullptr; + + if (unlikely (coords_length && !(normalized && design_coords))) + { + hb_free (normalized); + hb_free (design_coords); + return; + } + + if (coords_length) + memcpy (design_coords, coords, coords_length * sizeof (font->design_coords[0])); + + hb_ot_var_normalize_coords (font->face, coords_length, coords, normalized); + _hb_font_adopt_var_coords (font, normalized, design_coords, coords_length); +} + +/** + * hb_font_set_var_named_instance: + * @font: a font. + * @instance_index: named instance index. + * + * Sets design coords of a font from a named instance index. + * + * Since: 2.6.0 + */ +void +hb_font_set_var_named_instance (hb_font_t *font, + unsigned instance_index) +{ + if (hb_object_is_immutable (font)) + return; + + font->serial_coords = ++font->serial; + + unsigned int coords_length = hb_ot_var_named_instance_get_design_coords (font->face, instance_index, nullptr, nullptr); + + float *coords = coords_length ? (float *) hb_calloc (coords_length, sizeof (float)) : nullptr; + if (unlikely (coords_length && !coords)) + return; + + hb_ot_var_named_instance_get_design_coords (font->face, instance_index, &coords_length, coords); + hb_font_set_var_coords_design (font, coords, coords_length); + hb_free (coords); +} + +/** + * hb_font_set_var_coords_normalized: + * @font: #hb_font_t to work upon + * @coords: (array length=coords_length): Array of variation coordinates to apply + * @coords_length: Number of coordinates to apply + * + * Applies a list of variation coordinates (in normalized units) + * to a font. + * + * Note that this overrides all existing variations set on @font. + * Axes not included in @coords will be effectively set to their + * default values. + * + * Note: Coordinates should be normalized to 2.14. + * + * Since: 1.4.2 + */ +void +hb_font_set_var_coords_normalized (hb_font_t *font, + const int *coords, /* 2.14 normalized */ + unsigned int coords_length) +{ + if (hb_object_is_immutable (font)) + return; + + font->serial_coords = ++font->serial; + + int *copy = coords_length ? (int *) hb_calloc (coords_length, sizeof (coords[0])) : nullptr; + int *unmapped = coords_length ? (int *) hb_calloc (coords_length, sizeof (coords[0])) : nullptr; + float *design_coords = coords_length ? (float *) hb_calloc (coords_length, sizeof (design_coords[0])) : nullptr; + + if (unlikely (coords_length && !(copy && unmapped && design_coords))) + { + hb_free (copy); + hb_free (unmapped); + hb_free (design_coords); + return; + } + + if (coords_length) + { + memcpy (copy, coords, coords_length * sizeof (coords[0])); + memcpy (unmapped, coords, coords_length * sizeof (coords[0])); + } + + /* Best effort design coords simulation */ + font->face->table.avar->unmap_coords (unmapped, coords_length); + for (unsigned int i = 0; i < coords_length; ++i) + design_coords[i] = font->face->table.fvar->unnormalize_axis_value (i, unmapped[i]); + hb_free (unmapped); + + _hb_font_adopt_var_coords (font, copy, design_coords, coords_length); +} + +/** + * hb_font_get_var_coords_normalized: + * @font: #hb_font_t to work upon + * @length: (out): Number of coordinates retrieved + * + * Fetches the list of normalized variation coordinates currently + * set on a font. + * + * Note that this returned array may only contain values for some + * (or none) of the axes; omitted axes effectively have zero values. + * + * Return value is valid as long as variation coordinates of the font + * are not modified. + * + * Return value: coordinates array + * + * Since: 1.4.2 + */ +const int * +hb_font_get_var_coords_normalized (hb_font_t *font, + unsigned int *length) +{ + if (length) + *length = font->num_coords; + + return font->coords; +} + +/** + * hb_font_get_var_coords_design: + * @font: #hb_font_t to work upon + * @length: (out): Number of coordinates retrieved + * + * Fetches the list of variation coordinates (in design-space units) currently + * set on a font. + * + * Note that this returned array may only contain values for some + * (or none) of the axes; omitted axes effectively have their default + * values. + * + * Return value is valid as long as variation coordinates of the font + * are not modified. + * + * Return value: coordinates array + * + * Since: 3.3.0 + */ +const float * +hb_font_get_var_coords_design (hb_font_t *font, + unsigned int *length) +{ + if (length) + *length = font->num_coords; + + return font->design_coords; +} +#endif + +#ifndef HB_DISABLE_DEPRECATED +/* + * Deprecated get_glyph_func(): + */ + +struct hb_trampoline_closure_t +{ + void *user_data; + hb_destroy_func_t destroy; + unsigned int ref_count; +}; + +template +struct hb_trampoline_t +{ + hb_trampoline_closure_t closure; /* Must be first. */ + FuncType func; +}; + +template +static hb_trampoline_t * +trampoline_create (FuncType func, + void *user_data, + hb_destroy_func_t destroy) +{ + typedef hb_trampoline_t trampoline_t; + + trampoline_t *trampoline = (trampoline_t *) hb_calloc (1, sizeof (trampoline_t)); + + if (unlikely (!trampoline)) + return nullptr; + + trampoline->closure.user_data = user_data; + trampoline->closure.destroy = destroy; + trampoline->closure.ref_count = 1; + trampoline->func = func; + + return trampoline; +} + +static void +trampoline_reference (hb_trampoline_closure_t *closure) +{ + closure->ref_count++; +} + +static void +trampoline_destroy (void *user_data) +{ + hb_trampoline_closure_t *closure = (hb_trampoline_closure_t *) user_data; + + if (--closure->ref_count) + return; + + if (closure->destroy) + closure->destroy (closure->user_data); + hb_free (closure); +} + +typedef hb_trampoline_t hb_font_get_glyph_trampoline_t; + +static hb_bool_t +hb_font_get_nominal_glyph_trampoline (hb_font_t *font, + void *font_data, + hb_codepoint_t unicode, + hb_codepoint_t *glyph, + void *user_data) +{ + hb_font_get_glyph_trampoline_t *trampoline = (hb_font_get_glyph_trampoline_t *) user_data; + return trampoline->func (font, font_data, unicode, 0, glyph, trampoline->closure.user_data); +} + +static hb_bool_t +hb_font_get_variation_glyph_trampoline (hb_font_t *font, + void *font_data, + hb_codepoint_t unicode, + hb_codepoint_t variation_selector, + hb_codepoint_t *glyph, + void *user_data) +{ + hb_font_get_glyph_trampoline_t *trampoline = (hb_font_get_glyph_trampoline_t *) user_data; + return trampoline->func (font, font_data, unicode, variation_selector, glyph, trampoline->closure.user_data); +} + +/** + * hb_font_funcs_set_glyph_func: + * @ffuncs: The font-functions structure + * @func: (closure user_data) (destroy destroy) (scope notified): callback function + * @user_data: data to pass to @func + * @destroy: (nullable): function to call when @user_data is not needed anymore + * + * Deprecated. Use hb_font_funcs_set_nominal_glyph_func() and + * hb_font_funcs_set_variation_glyph_func() instead. + * + * Since: 0.9.2 + * Deprecated: 1.2.3 + **/ +void +hb_font_funcs_set_glyph_func (hb_font_funcs_t *ffuncs, + hb_font_get_glyph_func_t func, + void *user_data, + hb_destroy_func_t destroy /* May be NULL. */) +{ + if (hb_object_is_immutable (ffuncs)) + { + if (destroy) + destroy (user_data); + return; + } + + hb_font_get_glyph_trampoline_t *trampoline; + + trampoline = trampoline_create (func, user_data, destroy); + if (unlikely (!trampoline)) + { + if (destroy) + destroy (user_data); + return; + } + + /* Since we pass it to two destroying functions. */ + trampoline_reference (&trampoline->closure); + + hb_font_funcs_set_nominal_glyph_func (ffuncs, + hb_font_get_nominal_glyph_trampoline, + trampoline, + trampoline_destroy); + + hb_font_funcs_set_variation_glyph_func (ffuncs, + hb_font_get_variation_glyph_trampoline, + trampoline, + trampoline_destroy); +} +#endif diff --git a/libs/harfbuzz/src/hb-font.h b/libs/harfbuzz/src/hb-font.h new file mode 100644 index 00000000..e2c3df4a --- /dev/null +++ b/libs/harfbuzz/src/hb-font.h @@ -0,0 +1,1108 @@ +/* + * Copyright © 2009 Red Hat, 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. + * + * Red Hat Author(s): Behdad Esfahbod + */ + +#if !defined(HB_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR) +#error "Include instead." +#endif + +#ifndef HB_FONT_H +#define HB_FONT_H + +#include "hb-common.h" +#include "hb-face.h" +#include "hb-draw.h" + +HB_BEGIN_DECLS + +/** + * hb_font_t: + * + * Data type for holding fonts. + * + */ +typedef struct hb_font_t hb_font_t; + + +/* + * hb_font_funcs_t + */ + +/** + * hb_font_funcs_t: + * + * Data type containing a set of virtual methods used for + * working on #hb_font_t font objects. + * + * HarfBuzz provides a lightweight default function for each of + * the methods in #hb_font_funcs_t. Client programs can implement + * their own replacements for the individual font functions, as + * needed, and replace the default by calling the setter for a + * method. + * + **/ +typedef struct hb_font_funcs_t hb_font_funcs_t; + +HB_EXTERN hb_font_funcs_t * +hb_font_funcs_create (void); + +HB_EXTERN hb_font_funcs_t * +hb_font_funcs_get_empty (void); + +HB_EXTERN hb_font_funcs_t * +hb_font_funcs_reference (hb_font_funcs_t *ffuncs); + +HB_EXTERN void +hb_font_funcs_destroy (hb_font_funcs_t *ffuncs); + +HB_EXTERN hb_bool_t +hb_font_funcs_set_user_data (hb_font_funcs_t *ffuncs, + hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy, + hb_bool_t replace); + + +HB_EXTERN void * +hb_font_funcs_get_user_data (const hb_font_funcs_t *ffuncs, + hb_user_data_key_t *key); + + +HB_EXTERN void +hb_font_funcs_make_immutable (hb_font_funcs_t *ffuncs); + +HB_EXTERN hb_bool_t +hb_font_funcs_is_immutable (hb_font_funcs_t *ffuncs); + + +/* font and glyph extents */ + +/** + * hb_font_extents_t: + * @ascender: The height of typographic ascenders. + * @descender: The depth of typographic descenders. + * @line_gap: The suggested line-spacing gap. + * + * Font-wide extent values, measured in font units. + * + * Note that typically @ascender is positive and @descender + * negative, in coordinate systems that grow up. + **/ +typedef struct hb_font_extents_t { + hb_position_t ascender; + hb_position_t descender; + hb_position_t line_gap; + /*< private >*/ + hb_position_t reserved9; + hb_position_t reserved8; + hb_position_t reserved7; + hb_position_t reserved6; + hb_position_t reserved5; + hb_position_t reserved4; + hb_position_t reserved3; + hb_position_t reserved2; + hb_position_t reserved1; +} hb_font_extents_t; + +/** + * hb_glyph_extents_t: + * @x_bearing: Distance from the x-origin to the left extremum of the glyph. + * @y_bearing: Distance from the top extremum of the glyph to the y-origin. + * @width: Distance from the left extremum of the glyph to the right extremum. + * @height: Distance from the top extremum of the glyph to the bottom extremum. + * + * Glyph extent values, measured in font units. + * + * Note that @height is negative, in coordinate systems that grow up. + **/ +typedef struct hb_glyph_extents_t { + hb_position_t x_bearing; + hb_position_t y_bearing; + hb_position_t width; + hb_position_t height; +} hb_glyph_extents_t; + +/* func types */ + +/** + * hb_font_get_font_extents_func_t: + * @font: #hb_font_t to work upon + * @font_data: @font user data pointer + * @extents: (out): The font extents retrieved + * @user_data: User data pointer passed by the caller + * + * This method should retrieve the extents for a font. + * + **/ +typedef hb_bool_t (*hb_font_get_font_extents_func_t) (hb_font_t *font, void *font_data, + hb_font_extents_t *extents, + void *user_data); + +/** + * hb_font_get_font_h_extents_func_t: + * + * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. + * + * This method should retrieve the extents for a font, for horizontal-direction + * text segments. Extents must be returned in an #hb_glyph_extents output + * parameter. + * + **/ +typedef hb_font_get_font_extents_func_t hb_font_get_font_h_extents_func_t; + +/** + * hb_font_get_font_v_extents_func_t: + * + * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. + * + * This method should retrieve the extents for a font, for vertical-direction + * text segments. Extents must be returned in an #hb_glyph_extents output + * parameter. + * + **/ +typedef hb_font_get_font_extents_func_t hb_font_get_font_v_extents_func_t; + + +/** + * hb_font_get_nominal_glyph_func_t: + * @font: #hb_font_t to work upon + * @font_data: @font user data pointer + * @unicode: The Unicode code point to query + * @glyph: (out): The glyph ID retrieved + * @user_data: User data pointer passed by the caller + * + * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. + * + * This method should retrieve the nominal glyph ID for a specified Unicode code + * point. Glyph IDs must be returned in a #hb_codepoint_t output parameter. + * + * Return value: `true` if data found, `false` otherwise + * + **/ +typedef hb_bool_t (*hb_font_get_nominal_glyph_func_t) (hb_font_t *font, void *font_data, + hb_codepoint_t unicode, + hb_codepoint_t *glyph, + void *user_data); + +/** + * hb_font_get_variation_glyph_func_t: + * @font: #hb_font_t to work upon + * @font_data: @font user data pointer + * @unicode: The Unicode code point to query + * @variation_selector: The variation-selector code point to query + * @glyph: (out): The glyph ID retrieved + * @user_data: User data pointer passed by the caller + * + * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. + * + * This method should retrieve the glyph ID for a specified Unicode code point + * followed by a specified Variation Selector code point. Glyph IDs must be + * returned in a #hb_codepoint_t output parameter. + * + * Return value: `true` if data found, `false` otherwise + * + **/ +typedef hb_bool_t (*hb_font_get_variation_glyph_func_t) (hb_font_t *font, void *font_data, + hb_codepoint_t unicode, hb_codepoint_t variation_selector, + hb_codepoint_t *glyph, + void *user_data); + + +/** + * hb_font_get_nominal_glyphs_func_t: + * @font: #hb_font_t to work upon + * @font_data: @font user data pointer + * @count: number of code points to query + * @first_unicode: The first Unicode code point to query + * @unicode_stride: The stride between successive code points + * @first_glyph: (out): The first glyph ID retrieved + * @glyph_stride: The stride between successive glyph IDs + * @user_data: User data pointer passed by the caller + * + * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. + * + * This method should retrieve the nominal glyph IDs for a sequence of + * Unicode code points. Glyph IDs must be returned in a #hb_codepoint_t + * output parameter. + * + * Return value: the number of code points processed + * + **/ +typedef unsigned int (*hb_font_get_nominal_glyphs_func_t) (hb_font_t *font, void *font_data, + unsigned int count, + const hb_codepoint_t *first_unicode, + unsigned int unicode_stride, + hb_codepoint_t *first_glyph, + unsigned int glyph_stride, + void *user_data); + +/** + * hb_font_get_glyph_advance_func_t: + * @font: #hb_font_t to work upon + * @font_data: @font user data pointer + * @glyph: The glyph ID to query + * @user_data: User data pointer passed by the caller + * + * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. + * + * This method should retrieve the advance for a specified glyph. The + * method must return an #hb_position_t. + * + * Return value: The advance of @glyph within @font + * + **/ +typedef hb_position_t (*hb_font_get_glyph_advance_func_t) (hb_font_t *font, void *font_data, + hb_codepoint_t glyph, + void *user_data); + +/** + * hb_font_get_glyph_h_advance_func_t: + * + * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. + * + * This method should retrieve the advance for a specified glyph, in + * horizontal-direction text segments. Advances must be returned in + * an #hb_position_t output parameter. + * + **/ +typedef hb_font_get_glyph_advance_func_t hb_font_get_glyph_h_advance_func_t; + +/** + * hb_font_get_glyph_v_advance_func_t: + * + * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. + * + * This method should retrieve the advance for a specified glyph, in + * vertical-direction text segments. Advances must be returned in + * an #hb_position_t output parameter. + * + **/ +typedef hb_font_get_glyph_advance_func_t hb_font_get_glyph_v_advance_func_t; + +/** + * hb_font_get_glyph_advances_func_t: + * @font: #hb_font_t to work upon + * @font_data: @font user data pointer + * @count: The number of glyph IDs in the sequence queried + * @first_glyph: The first glyph ID to query + * @glyph_stride: The stride between successive glyph IDs + * @first_advance: (out): The first advance retrieved + * @advance_stride: The stride between successive advances + * @user_data: User data pointer passed by the caller + * + * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. + * + * This method should retrieve the advances for a sequence of glyphs. + * + **/ +typedef void (*hb_font_get_glyph_advances_func_t) (hb_font_t* font, void* font_data, + unsigned int count, + const hb_codepoint_t *first_glyph, + unsigned glyph_stride, + hb_position_t *first_advance, + unsigned advance_stride, + void *user_data); + +/** + * hb_font_get_glyph_h_advances_func_t: + * + * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. + * + * This method should retrieve the advances for a sequence of glyphs, in + * horizontal-direction text segments. + * + **/ +typedef hb_font_get_glyph_advances_func_t hb_font_get_glyph_h_advances_func_t; + +/** + * hb_font_get_glyph_v_advances_func_t: + * + * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. + * + * This method should retrieve the advances for a sequence of glyphs, in + * vertical-direction text segments. + * + **/ +typedef hb_font_get_glyph_advances_func_t hb_font_get_glyph_v_advances_func_t; + +/** + * hb_font_get_glyph_origin_func_t: + * @font: #hb_font_t to work upon + * @font_data: @font user data pointer + * @glyph: The glyph ID to query + * @x: (out): The X coordinate of the origin + * @y: (out): The Y coordinate of the origin + * @user_data: User data pointer passed by the caller + * + * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. + * + * This method should retrieve the (X,Y) coordinates (in font units) of the + * origin for a glyph. Each coordinate must be returned in an #hb_position_t + * output parameter. + * + * Return value: `true` if data found, `false` otherwise + * + **/ +typedef hb_bool_t (*hb_font_get_glyph_origin_func_t) (hb_font_t *font, void *font_data, + hb_codepoint_t glyph, + hb_position_t *x, hb_position_t *y, + void *user_data); + +/** + * hb_font_get_glyph_h_origin_func_t: + * + * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. + * + * This method should retrieve the (X,Y) coordinates (in font units) of the + * origin for a glyph, for horizontal-direction text segments. Each + * coordinate must be returned in an #hb_position_t output parameter. + * + **/ +typedef hb_font_get_glyph_origin_func_t hb_font_get_glyph_h_origin_func_t; + +/** + * hb_font_get_glyph_v_origin_func_t: + * + * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. + * + * This method should retrieve the (X,Y) coordinates (in font units) of the + * origin for a glyph, for vertical-direction text segments. Each coordinate + * must be returned in an #hb_position_t output parameter. + * + **/ +typedef hb_font_get_glyph_origin_func_t hb_font_get_glyph_v_origin_func_t; + +/** + * hb_font_get_glyph_kerning_func_t: + * @font: #hb_font_t to work upon + * @font_data: @font user data pointer + * @first_glyph: The glyph ID of the first glyph in the glyph pair + * @second_glyph: The glyph ID of the second glyph in the glyph pair + * @user_data: User data pointer passed by the caller + * + * This method should retrieve the kerning-adjustment value for a glyph-pair in + * the specified font, for horizontal text segments. + * + **/ +typedef hb_position_t (*hb_font_get_glyph_kerning_func_t) (hb_font_t *font, void *font_data, + hb_codepoint_t first_glyph, hb_codepoint_t second_glyph, + void *user_data); +/** + * hb_font_get_glyph_h_kerning_func_t: + * + * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. + * + * This method should retrieve the kerning-adjustment value for a glyph-pair in + * the specified font, for horizontal text segments. + * + **/ +typedef hb_font_get_glyph_kerning_func_t hb_font_get_glyph_h_kerning_func_t; + + +/** + * hb_font_get_glyph_extents_func_t: + * @font: #hb_font_t to work upon + * @font_data: @font user data pointer + * @glyph: The glyph ID to query + * @extents: (out): The #hb_glyph_extents_t retrieved + * @user_data: User data pointer passed by the caller + * + * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. + * + * This method should retrieve the extents for a specified glyph. Extents must be + * returned in an #hb_glyph_extents output parameter. + * + * Return value: `true` if data found, `false` otherwise + * + **/ +typedef hb_bool_t (*hb_font_get_glyph_extents_func_t) (hb_font_t *font, void *font_data, + hb_codepoint_t glyph, + hb_glyph_extents_t *extents, + void *user_data); + +/** + * hb_font_get_glyph_contour_point_func_t: + * @font: #hb_font_t to work upon + * @font_data: @font user data pointer + * @glyph: The glyph ID to query + * @point_index: The contour-point index to query + * @x: (out): The X value retrieved for the contour point + * @y: (out): The Y value retrieved for the contour point + * @user_data: User data pointer passed by the caller + * + * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. + * + * This method should retrieve the (X,Y) coordinates (in font units) for a + * specified contour point in a glyph. Each coordinate must be returned as + * an #hb_position_t output parameter. + * + * Return value: `true` if data found, `false` otherwise + * + **/ +typedef hb_bool_t (*hb_font_get_glyph_contour_point_func_t) (hb_font_t *font, void *font_data, + hb_codepoint_t glyph, unsigned int point_index, + hb_position_t *x, hb_position_t *y, + void *user_data); + + +/** + * hb_font_get_glyph_name_func_t: + * @font: #hb_font_t to work upon + * @font_data: @font user data pointer + * @glyph: The glyph ID to query + * @name: (out) (array length=size): Name string retrieved for the glyph ID + * @size: Length of the glyph-name string retrieved + * @user_data: User data pointer passed by the caller + * + * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. + * + * This method should retrieve the glyph name that corresponds to a + * glyph ID. The name should be returned in a string output parameter. + * + * Return value: `true` if data found, `false` otherwise + * + **/ +typedef hb_bool_t (*hb_font_get_glyph_name_func_t) (hb_font_t *font, void *font_data, + hb_codepoint_t glyph, + char *name, unsigned int size, + void *user_data); + +/** + * hb_font_get_glyph_from_name_func_t: + * @font: #hb_font_t to work upon + * @font_data: @font user data pointer + * @name: (array length=len): The name string to query + * @len: The length of the name queried + * @glyph: (out): The glyph ID retrieved + * @user_data: User data pointer passed by the caller + * + * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. + * + * This method should retrieve the glyph ID that corresponds to a glyph-name + * string. + * + * Return value: `true` if data found, `false` otherwise + * + **/ +typedef hb_bool_t (*hb_font_get_glyph_from_name_func_t) (hb_font_t *font, void *font_data, + const char *name, int len, /* -1 means nul-terminated */ + hb_codepoint_t *glyph, + void *user_data); + +/** + * hb_font_get_glyph_shape_func_t: + * @font: #hb_font_t to work upon + * @font_data: @font user data pointer + * @glyph: The glyph ID to query + * @draw_funcs: The draw functions to send the shape data to + * @draw_data: The data accompanying the draw functions + * @user_data: User data pointer passed by the caller + * + * A virtual method for the #hb_font_funcs_t of an #hb_font_t object. + * + * Since: 4.0.0 + * + **/ +typedef void (*hb_font_get_glyph_shape_func_t) (hb_font_t *font, void *font_data, + hb_codepoint_t glyph, + hb_draw_funcs_t *draw_funcs, void *draw_data, + void *user_data); + + +/* func setters */ + +/** + * hb_font_funcs_set_font_h_extents_func: + * @ffuncs: A font-function structure + * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign + * @user_data: Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is not needed anymore + * + * Sets the implementation function for #hb_font_get_font_h_extents_func_t. + * + * Since: 1.1.2 + **/ +HB_EXTERN void +hb_font_funcs_set_font_h_extents_func (hb_font_funcs_t *ffuncs, + hb_font_get_font_h_extents_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_font_funcs_set_font_v_extents_func: + * @ffuncs: A font-function structure + * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign + * @user_data: Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is not needed anymore + * + * Sets the implementation function for #hb_font_get_font_v_extents_func_t. + * + * Since: 1.1.2 + **/ +HB_EXTERN void +hb_font_funcs_set_font_v_extents_func (hb_font_funcs_t *ffuncs, + hb_font_get_font_v_extents_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_font_funcs_set_nominal_glyph_func: + * @ffuncs: A font-function structure + * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign + * @user_data: Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is not needed anymore + * + * Sets the implementation function for #hb_font_get_nominal_glyph_func_t. + * + * Since: 1.2.3 + **/ +HB_EXTERN void +hb_font_funcs_set_nominal_glyph_func (hb_font_funcs_t *ffuncs, + hb_font_get_nominal_glyph_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_font_funcs_set_nominal_glyphs_func: + * @ffuncs: A font-function structure + * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign + * @user_data: Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is not needed anymore + * + * Sets the implementation function for #hb_font_get_nominal_glyphs_func_t. + * + * Since: 2.0.0 + **/ +HB_EXTERN void +hb_font_funcs_set_nominal_glyphs_func (hb_font_funcs_t *ffuncs, + hb_font_get_nominal_glyphs_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_font_funcs_set_variation_glyph_func: + * @ffuncs: A font-function structure + * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign + * @user_data: Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is not needed anymore + * + * Sets the implementation function for #hb_font_get_variation_glyph_func_t. + * + * Since: 1.2.3 + **/ +HB_EXTERN void +hb_font_funcs_set_variation_glyph_func (hb_font_funcs_t *ffuncs, + hb_font_get_variation_glyph_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_font_funcs_set_glyph_h_advance_func: + * @ffuncs: A font-function structure + * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign + * @user_data: Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is not needed anymore + * + * Sets the implementation function for #hb_font_get_glyph_h_advance_func_t. + * + * Since: 0.9.2 + **/ +HB_EXTERN void +hb_font_funcs_set_glyph_h_advance_func (hb_font_funcs_t *ffuncs, + hb_font_get_glyph_h_advance_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_font_funcs_set_glyph_v_advance_func: + * @ffuncs: A font-function structure + * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign + * @user_data: Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is not needed anymore + * + * Sets the implementation function for #hb_font_get_glyph_v_advance_func_t. + * + * Since: 0.9.2 + **/ +HB_EXTERN void +hb_font_funcs_set_glyph_v_advance_func (hb_font_funcs_t *ffuncs, + hb_font_get_glyph_v_advance_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_font_funcs_set_glyph_h_advances_func: + * @ffuncs: A font-function structure + * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign + * @user_data: Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is not needed anymore + * + * Sets the implementation function for #hb_font_get_glyph_h_advances_func_t. + * + * Since: 1.8.6 + **/ +HB_EXTERN void +hb_font_funcs_set_glyph_h_advances_func (hb_font_funcs_t *ffuncs, + hb_font_get_glyph_h_advances_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_font_funcs_set_glyph_v_advances_func: + * @ffuncs: A font-function structure + * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign + * @user_data: Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is not needed anymore + * + * Sets the implementation function for #hb_font_get_glyph_v_advances_func_t. + * + * Since: 1.8.6 + **/ +HB_EXTERN void +hb_font_funcs_set_glyph_v_advances_func (hb_font_funcs_t *ffuncs, + hb_font_get_glyph_v_advances_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_font_funcs_set_glyph_h_origin_func: + * @ffuncs: A font-function structure + * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign + * @user_data: Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is not needed anymore + * + * Sets the implementation function for #hb_font_get_glyph_h_origin_func_t. + * + * Since: 0.9.2 + **/ +HB_EXTERN void +hb_font_funcs_set_glyph_h_origin_func (hb_font_funcs_t *ffuncs, + hb_font_get_glyph_h_origin_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_font_funcs_set_glyph_v_origin_func: + * @ffuncs: A font-function structure + * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign + * @user_data: Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is not needed anymore + * + * Sets the implementation function for #hb_font_get_glyph_v_origin_func_t. + * + * Since: 0.9.2 + **/ +HB_EXTERN void +hb_font_funcs_set_glyph_v_origin_func (hb_font_funcs_t *ffuncs, + hb_font_get_glyph_v_origin_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_font_funcs_set_glyph_h_kerning_func: + * @ffuncs: A font-function structure + * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign + * @user_data: Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is not needed anymore + * + * Sets the implementation function for #hb_font_get_glyph_h_kerning_func_t. + * + * Since: 0.9.2 + **/ +HB_EXTERN void +hb_font_funcs_set_glyph_h_kerning_func (hb_font_funcs_t *ffuncs, + hb_font_get_glyph_h_kerning_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_font_funcs_set_glyph_extents_func: + * @ffuncs: A font-function structure + * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign + * @user_data: Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is not needed anymore + * + * Sets the implementation function for #hb_font_get_glyph_extents_func_t. + * + * Since: 0.9.2 + **/ +HB_EXTERN void +hb_font_funcs_set_glyph_extents_func (hb_font_funcs_t *ffuncs, + hb_font_get_glyph_extents_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_font_funcs_set_glyph_contour_point_func: + * @ffuncs: A font-function structure + * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign + * @user_data: Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is not needed anymore + * + * Sets the implementation function for #hb_font_get_glyph_contour_point_func_t. + * + * Since: 0.9.2 + **/ +HB_EXTERN void +hb_font_funcs_set_glyph_contour_point_func (hb_font_funcs_t *ffuncs, + hb_font_get_glyph_contour_point_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_font_funcs_set_glyph_name_func: + * @ffuncs: A font-function structure + * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign + * @user_data: Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is not needed anymore + * + * Sets the implementation function for #hb_font_get_glyph_name_func_t. + * + * Since: 0.9.2 + **/ +HB_EXTERN void +hb_font_funcs_set_glyph_name_func (hb_font_funcs_t *ffuncs, + hb_font_get_glyph_name_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_font_funcs_set_glyph_from_name_func: + * @ffuncs: A font-function structure + * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign + * @user_data: Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is not needed anymore + * + * Sets the implementation function for #hb_font_get_glyph_from_name_func_t. + * + * Since: 0.9.2 + **/ +HB_EXTERN void +hb_font_funcs_set_glyph_from_name_func (hb_font_funcs_t *ffuncs, + hb_font_get_glyph_from_name_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_font_funcs_set_glyph_shape_func: + * @ffuncs: A font-function structure + * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign + * @user_data: Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is not needed anymore + * + * Sets the implementation function for #hb_font_get_glyph_shape_func_t. + * + * Since: 4.0.0 + **/ +HB_EXTERN void +hb_font_funcs_set_glyph_shape_func (hb_font_funcs_t *ffuncs, + hb_font_get_glyph_shape_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/* func dispatch */ + +HB_EXTERN hb_bool_t +hb_font_get_h_extents (hb_font_t *font, + hb_font_extents_t *extents); +HB_EXTERN hb_bool_t +hb_font_get_v_extents (hb_font_t *font, + hb_font_extents_t *extents); + +HB_EXTERN hb_bool_t +hb_font_get_nominal_glyph (hb_font_t *font, + hb_codepoint_t unicode, + hb_codepoint_t *glyph); +HB_EXTERN hb_bool_t +hb_font_get_variation_glyph (hb_font_t *font, + hb_codepoint_t unicode, hb_codepoint_t variation_selector, + hb_codepoint_t *glyph); + +HB_EXTERN unsigned int +hb_font_get_nominal_glyphs (hb_font_t *font, + unsigned int count, + const hb_codepoint_t *first_unicode, + unsigned int unicode_stride, + hb_codepoint_t *first_glyph, + unsigned int glyph_stride); + +HB_EXTERN hb_position_t +hb_font_get_glyph_h_advance (hb_font_t *font, + hb_codepoint_t glyph); +HB_EXTERN hb_position_t +hb_font_get_glyph_v_advance (hb_font_t *font, + hb_codepoint_t glyph); + +HB_EXTERN void +hb_font_get_glyph_h_advances (hb_font_t* font, + unsigned int count, + const hb_codepoint_t *first_glyph, + unsigned glyph_stride, + hb_position_t *first_advance, + unsigned advance_stride); +HB_EXTERN void +hb_font_get_glyph_v_advances (hb_font_t* font, + unsigned int count, + const hb_codepoint_t *first_glyph, + unsigned glyph_stride, + hb_position_t *first_advance, + unsigned advance_stride); + +HB_EXTERN hb_bool_t +hb_font_get_glyph_h_origin (hb_font_t *font, + hb_codepoint_t glyph, + hb_position_t *x, hb_position_t *y); +HB_EXTERN hb_bool_t +hb_font_get_glyph_v_origin (hb_font_t *font, + hb_codepoint_t glyph, + hb_position_t *x, hb_position_t *y); + +HB_EXTERN hb_position_t +hb_font_get_glyph_h_kerning (hb_font_t *font, + hb_codepoint_t left_glyph, hb_codepoint_t right_glyph); + +HB_EXTERN hb_bool_t +hb_font_get_glyph_extents (hb_font_t *font, + hb_codepoint_t glyph, + hb_glyph_extents_t *extents); + +HB_EXTERN hb_bool_t +hb_font_get_glyph_contour_point (hb_font_t *font, + hb_codepoint_t glyph, unsigned int point_index, + hb_position_t *x, hb_position_t *y); + +HB_EXTERN hb_bool_t +hb_font_get_glyph_name (hb_font_t *font, + hb_codepoint_t glyph, + char *name, unsigned int size); +HB_EXTERN hb_bool_t +hb_font_get_glyph_from_name (hb_font_t *font, + const char *name, int len, /* -1 means nul-terminated */ + hb_codepoint_t *glyph); + +HB_EXTERN void +hb_font_get_glyph_shape (hb_font_t *font, + hb_codepoint_t glyph, + hb_draw_funcs_t *dfuncs, void *draw_data); + + +/* high-level funcs, with fallback */ + +/* Calls either hb_font_get_nominal_glyph() if variation_selector is 0, + * otherwise calls hb_font_get_variation_glyph(). */ +HB_EXTERN hb_bool_t +hb_font_get_glyph (hb_font_t *font, + hb_codepoint_t unicode, hb_codepoint_t variation_selector, + hb_codepoint_t *glyph); + +HB_EXTERN void +hb_font_get_extents_for_direction (hb_font_t *font, + hb_direction_t direction, + hb_font_extents_t *extents); +HB_EXTERN void +hb_font_get_glyph_advance_for_direction (hb_font_t *font, + hb_codepoint_t glyph, + hb_direction_t direction, + hb_position_t *x, hb_position_t *y); +HB_EXTERN void +hb_font_get_glyph_advances_for_direction (hb_font_t* font, + hb_direction_t direction, + unsigned int count, + const hb_codepoint_t *first_glyph, + unsigned glyph_stride, + hb_position_t *first_advance, + unsigned advance_stride); +HB_EXTERN void +hb_font_get_glyph_origin_for_direction (hb_font_t *font, + hb_codepoint_t glyph, + hb_direction_t direction, + hb_position_t *x, hb_position_t *y); +HB_EXTERN void +hb_font_add_glyph_origin_for_direction (hb_font_t *font, + hb_codepoint_t glyph, + hb_direction_t direction, + hb_position_t *x, hb_position_t *y); +HB_EXTERN void +hb_font_subtract_glyph_origin_for_direction (hb_font_t *font, + hb_codepoint_t glyph, + hb_direction_t direction, + hb_position_t *x, hb_position_t *y); + +HB_EXTERN void +hb_font_get_glyph_kerning_for_direction (hb_font_t *font, + hb_codepoint_t first_glyph, hb_codepoint_t second_glyph, + hb_direction_t direction, + hb_position_t *x, hb_position_t *y); + +HB_EXTERN hb_bool_t +hb_font_get_glyph_extents_for_origin (hb_font_t *font, + hb_codepoint_t glyph, + hb_direction_t direction, + hb_glyph_extents_t *extents); + +HB_EXTERN hb_bool_t +hb_font_get_glyph_contour_point_for_origin (hb_font_t *font, + hb_codepoint_t glyph, unsigned int point_index, + hb_direction_t direction, + hb_position_t *x, hb_position_t *y); + +/* Generates gidDDD if glyph has no name. */ +HB_EXTERN void +hb_font_glyph_to_string (hb_font_t *font, + hb_codepoint_t glyph, + char *s, unsigned int size); +/* Parses gidDDD and uniUUUU strings automatically. */ +HB_EXTERN hb_bool_t +hb_font_glyph_from_string (hb_font_t *font, + const char *s, int len, /* -1 means nul-terminated */ + hb_codepoint_t *glyph); + + +/* + * hb_font_t + */ + +/* Fonts are very light-weight objects */ + +HB_EXTERN hb_font_t * +hb_font_create (hb_face_t *face); + +HB_EXTERN hb_font_t * +hb_font_create_sub_font (hb_font_t *parent); + +HB_EXTERN hb_font_t * +hb_font_get_empty (void); + +HB_EXTERN hb_font_t * +hb_font_reference (hb_font_t *font); + +HB_EXTERN void +hb_font_destroy (hb_font_t *font); + +HB_EXTERN hb_bool_t +hb_font_set_user_data (hb_font_t *font, + hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy, + hb_bool_t replace); + + +HB_EXTERN void * +hb_font_get_user_data (const hb_font_t *font, + hb_user_data_key_t *key); + +HB_EXTERN void +hb_font_make_immutable (hb_font_t *font); + +HB_EXTERN hb_bool_t +hb_font_is_immutable (hb_font_t *font); + +HB_EXTERN unsigned int +hb_font_get_serial (hb_font_t *font); + +HB_EXTERN void +hb_font_changed (hb_font_t *font); + +HB_EXTERN void +hb_font_set_parent (hb_font_t *font, + hb_font_t *parent); + +HB_EXTERN hb_font_t * +hb_font_get_parent (hb_font_t *font); + +HB_EXTERN void +hb_font_set_face (hb_font_t *font, + hb_face_t *face); + +HB_EXTERN hb_face_t * +hb_font_get_face (hb_font_t *font); + + +HB_EXTERN void +hb_font_set_funcs (hb_font_t *font, + hb_font_funcs_t *klass, + void *font_data, + hb_destroy_func_t destroy); + +/* Be *very* careful with this function! */ +HB_EXTERN void +hb_font_set_funcs_data (hb_font_t *font, + void *font_data, + hb_destroy_func_t destroy); + + +HB_EXTERN void +hb_font_set_scale (hb_font_t *font, + int x_scale, + int y_scale); + +HB_EXTERN void +hb_font_get_scale (hb_font_t *font, + int *x_scale, + int *y_scale); + +/* + * A zero value means "no hinting in that direction" + */ +HB_EXTERN void +hb_font_set_ppem (hb_font_t *font, + unsigned int x_ppem, + unsigned int y_ppem); + +HB_EXTERN void +hb_font_get_ppem (hb_font_t *font, + unsigned int *x_ppem, + unsigned int *y_ppem); + +/* + * Point size per EM. Used for optical-sizing in CoreText. + * A value of zero means "not set". + */ +HB_EXTERN void +hb_font_set_ptem (hb_font_t *font, float ptem); + +HB_EXTERN float +hb_font_get_ptem (hb_font_t *font); + +HB_EXTERN void +hb_font_set_synthetic_slant (hb_font_t *font, float slant); + +HB_EXTERN float +hb_font_get_synthetic_slant (hb_font_t *font); + +HB_EXTERN void +hb_font_set_variations (hb_font_t *font, + const hb_variation_t *variations, + unsigned int variations_length); + +HB_EXTERN void +hb_font_set_var_coords_design (hb_font_t *font, + const float *coords, + unsigned int coords_length); + +HB_EXTERN const float * +hb_font_get_var_coords_design (hb_font_t *font, + unsigned int *length); + +HB_EXTERN void +hb_font_set_var_coords_normalized (hb_font_t *font, + const int *coords, /* 2.14 normalized */ + unsigned int coords_length); + +HB_EXTERN const int * +hb_font_get_var_coords_normalized (hb_font_t *font, + unsigned int *length); + +HB_EXTERN void +hb_font_set_var_named_instance (hb_font_t *font, + unsigned instance_index); + + +HB_END_DECLS + +#endif /* HB_FONT_H */ diff --git a/libs/harfbuzz/src/hb-font.hh b/libs/harfbuzz/src/hb-font.hh new file mode 100644 index 00000000..bb402e23 --- /dev/null +++ b/libs/harfbuzz/src/hb-font.hh @@ -0,0 +1,658 @@ +/* + * Copyright © 2009 Red Hat, Inc. + * 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. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_FONT_HH +#define HB_FONT_HH + +#include "hb.hh" + +#include "hb-face.hh" +#include "hb-shaper.hh" + + +/* + * hb_font_funcs_t + */ + +#define HB_FONT_FUNCS_IMPLEMENT_CALLBACKS \ + HB_FONT_FUNC_IMPLEMENT (font_h_extents) \ + HB_FONT_FUNC_IMPLEMENT (font_v_extents) \ + HB_FONT_FUNC_IMPLEMENT (nominal_glyph) \ + HB_FONT_FUNC_IMPLEMENT (nominal_glyphs) \ + HB_FONT_FUNC_IMPLEMENT (variation_glyph) \ + HB_FONT_FUNC_IMPLEMENT (glyph_h_advance) \ + HB_FONT_FUNC_IMPLEMENT (glyph_v_advance) \ + HB_FONT_FUNC_IMPLEMENT (glyph_h_advances) \ + HB_FONT_FUNC_IMPLEMENT (glyph_v_advances) \ + HB_FONT_FUNC_IMPLEMENT (glyph_h_origin) \ + HB_FONT_FUNC_IMPLEMENT (glyph_v_origin) \ + HB_FONT_FUNC_IMPLEMENT (glyph_h_kerning) \ + HB_IF_NOT_DEPRECATED (HB_FONT_FUNC_IMPLEMENT (glyph_v_kerning)) \ + HB_FONT_FUNC_IMPLEMENT (glyph_extents) \ + HB_FONT_FUNC_IMPLEMENT (glyph_contour_point) \ + HB_FONT_FUNC_IMPLEMENT (glyph_name) \ + HB_FONT_FUNC_IMPLEMENT (glyph_from_name) \ + HB_FONT_FUNC_IMPLEMENT (glyph_shape) \ + /* ^--- Add new callbacks here */ + +struct hb_font_funcs_t +{ + hb_object_header_t header; + + struct { +#define HB_FONT_FUNC_IMPLEMENT(name) void *name; + HB_FONT_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_FONT_FUNC_IMPLEMENT + } *user_data; + + struct { +#define HB_FONT_FUNC_IMPLEMENT(name) hb_destroy_func_t name; + HB_FONT_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_FONT_FUNC_IMPLEMENT + } *destroy; + + /* Don't access these directly. Call font->get_*() instead. */ + union get_t { + struct get_funcs_t { +#define HB_FONT_FUNC_IMPLEMENT(name) hb_font_get_##name##_func_t name; + HB_FONT_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_FONT_FUNC_IMPLEMENT + } f; + void (*array[0 +#define HB_FONT_FUNC_IMPLEMENT(name) +1 + HB_FONT_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_FONT_FUNC_IMPLEMENT + ]) (); + } get; +}; +DECLARE_NULL_INSTANCE (hb_font_funcs_t); + + +/* + * hb_font_t + */ + +#define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_INSTANTIATE_SHAPERS(shaper, font); +#include "hb-shaper-list.hh" +#undef HB_SHAPER_IMPLEMENT + +struct hb_font_t +{ + hb_object_header_t header; + unsigned int serial; + unsigned int serial_coords; + + hb_font_t *parent; + hb_face_t *face; + + int32_t x_scale; + int32_t y_scale; + float slant; + float slant_xy; + float x_multf; + float y_multf; + int64_t x_mult; + int64_t y_mult; + + unsigned int x_ppem; + unsigned int y_ppem; + + float ptem; + + /* Font variation coordinates. */ + unsigned int num_coords; + int *coords; + float *design_coords; + + hb_font_funcs_t *klass; + void *user_data; + hb_destroy_func_t destroy; + + hb_shaper_object_dataset_t data; /* Various shaper data. */ + + + /* Convert from font-space to user-space */ + int64_t dir_mult (hb_direction_t direction) + { return HB_DIRECTION_IS_VERTICAL(direction) ? y_mult : x_mult; } + hb_position_t em_scale_x (int16_t v) { return em_mult (v, x_mult); } + hb_position_t em_scale_y (int16_t v) { return em_mult (v, y_mult); } + hb_position_t em_scalef_x (float v) { return em_multf (v, x_multf); } + hb_position_t em_scalef_y (float v) { return em_multf (v, y_multf); } + float em_fscale_x (int16_t v) { return em_fmult (v, x_multf); } + float em_fscale_y (int16_t v) { return em_fmult (v, y_multf); } + float em_fscalef_x (float v) { return em_fmultf (v, x_multf); } + float em_fscalef_y (float v) { return em_fmultf (v, y_multf); } + hb_position_t em_scale_dir (int16_t v, hb_direction_t direction) + { return em_mult (v, dir_mult (direction)); } + + /* Convert from parent-font user-space to our user-space */ + hb_position_t parent_scale_x_distance (hb_position_t v) + { + if (unlikely (parent && parent->x_scale != x_scale)) + return (hb_position_t) (v * (int64_t) this->x_scale / this->parent->x_scale); + return v; + } + hb_position_t parent_scale_y_distance (hb_position_t v) + { + if (unlikely (parent && parent->y_scale != y_scale)) + return (hb_position_t) (v * (int64_t) this->y_scale / this->parent->y_scale); + return v; + } + hb_position_t parent_scale_x_position (hb_position_t v) + { return parent_scale_x_distance (v); } + hb_position_t parent_scale_y_position (hb_position_t v) + { return parent_scale_y_distance (v); } + + void parent_scale_distance (hb_position_t *x, hb_position_t *y) + { + *x = parent_scale_x_distance (*x); + *y = parent_scale_y_distance (*y); + } + void parent_scale_position (hb_position_t *x, hb_position_t *y) + { + *x = parent_scale_x_position (*x); + *y = parent_scale_y_position (*y); + } + + + /* Public getters */ + + HB_INTERNAL bool has_func (unsigned int i); + HB_INTERNAL bool has_func_set (unsigned int i); + + /* has_* ... */ +#define HB_FONT_FUNC_IMPLEMENT(name) \ + bool \ + has_##name##_func () \ + { \ + hb_font_funcs_t *funcs = this->klass; \ + unsigned int i = offsetof (hb_font_funcs_t::get_t::get_funcs_t, name) / sizeof (funcs->get.array[0]); \ + return has_func (i); \ + } \ + bool \ + has_##name##_func_set () \ + { \ + hb_font_funcs_t *funcs = this->klass; \ + unsigned int i = offsetof (hb_font_funcs_t::get_t::get_funcs_t, name) / sizeof (funcs->get.array[0]); \ + return has_func_set (i); \ + } + HB_FONT_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_FONT_FUNC_IMPLEMENT + + hb_bool_t get_font_h_extents (hb_font_extents_t *extents) + { + memset (extents, 0, sizeof (*extents)); + return klass->get.f.font_h_extents (this, user_data, + extents, + !klass->user_data ? nullptr : klass->user_data->font_h_extents); + } + hb_bool_t get_font_v_extents (hb_font_extents_t *extents) + { + memset (extents, 0, sizeof (*extents)); + return klass->get.f.font_v_extents (this, user_data, + extents, + !klass->user_data ? nullptr : klass->user_data->font_v_extents); + } + + bool has_glyph (hb_codepoint_t unicode) + { + hb_codepoint_t glyph; + return get_nominal_glyph (unicode, &glyph); + } + + hb_bool_t get_nominal_glyph (hb_codepoint_t unicode, + hb_codepoint_t *glyph, + hb_codepoint_t not_found = 0) + { + *glyph = not_found; + return klass->get.f.nominal_glyph (this, user_data, + unicode, glyph, + !klass->user_data ? nullptr : klass->user_data->nominal_glyph); + } + unsigned int get_nominal_glyphs (unsigned int count, + const hb_codepoint_t *first_unicode, + unsigned int unicode_stride, + hb_codepoint_t *first_glyph, + unsigned int glyph_stride) + { + return klass->get.f.nominal_glyphs (this, user_data, + count, + first_unicode, unicode_stride, + first_glyph, glyph_stride, + !klass->user_data ? nullptr : klass->user_data->nominal_glyphs); + } + + hb_bool_t get_variation_glyph (hb_codepoint_t unicode, hb_codepoint_t variation_selector, + hb_codepoint_t *glyph, + hb_codepoint_t not_found = 0) + { + *glyph = not_found; + return klass->get.f.variation_glyph (this, user_data, + unicode, variation_selector, glyph, + !klass->user_data ? nullptr : klass->user_data->variation_glyph); + } + + hb_position_t get_glyph_h_advance (hb_codepoint_t glyph) + { + return klass->get.f.glyph_h_advance (this, user_data, + glyph, + !klass->user_data ? nullptr : klass->user_data->glyph_h_advance); + } + + hb_position_t get_glyph_v_advance (hb_codepoint_t glyph) + { + return klass->get.f.glyph_v_advance (this, user_data, + glyph, + !klass->user_data ? nullptr : klass->user_data->glyph_v_advance); + } + + void get_glyph_h_advances (unsigned int count, + const hb_codepoint_t *first_glyph, + unsigned int glyph_stride, + hb_position_t *first_advance, + unsigned int advance_stride) + { + return klass->get.f.glyph_h_advances (this, user_data, + count, + first_glyph, glyph_stride, + first_advance, advance_stride, + !klass->user_data ? nullptr : klass->user_data->glyph_h_advances); + } + + void get_glyph_v_advances (unsigned int count, + const hb_codepoint_t *first_glyph, + unsigned int glyph_stride, + hb_position_t *first_advance, + unsigned int advance_stride) + { + return klass->get.f.glyph_v_advances (this, user_data, + count, + first_glyph, glyph_stride, + first_advance, advance_stride, + !klass->user_data ? nullptr : klass->user_data->glyph_v_advances); + } + + hb_bool_t get_glyph_h_origin (hb_codepoint_t glyph, + hb_position_t *x, hb_position_t *y) + { + *x = *y = 0; + return klass->get.f.glyph_h_origin (this, user_data, + glyph, x, y, + !klass->user_data ? nullptr : klass->user_data->glyph_h_origin); + } + + hb_bool_t get_glyph_v_origin (hb_codepoint_t glyph, + hb_position_t *x, hb_position_t *y) + { + *x = *y = 0; + return klass->get.f.glyph_v_origin (this, user_data, + glyph, x, y, + !klass->user_data ? nullptr : klass->user_data->glyph_v_origin); + } + + hb_position_t get_glyph_h_kerning (hb_codepoint_t left_glyph, + hb_codepoint_t right_glyph) + { +#ifdef HB_DISABLE_DEPRECATED + return 0; +#else + return klass->get.f.glyph_h_kerning (this, user_data, + left_glyph, right_glyph, + !klass->user_data ? nullptr : klass->user_data->glyph_h_kerning); +#endif + } + + hb_position_t get_glyph_v_kerning (hb_codepoint_t top_glyph, + hb_codepoint_t bottom_glyph) + { +#ifdef HB_DISABLE_DEPRECATED + return 0; +#else + return klass->get.f.glyph_v_kerning (this, user_data, + top_glyph, bottom_glyph, + !klass->user_data ? nullptr : klass->user_data->glyph_v_kerning); +#endif + } + + hb_bool_t get_glyph_extents (hb_codepoint_t glyph, + hb_glyph_extents_t *extents) + { + memset (extents, 0, sizeof (*extents)); + return klass->get.f.glyph_extents (this, user_data, + glyph, + extents, + !klass->user_data ? nullptr : klass->user_data->glyph_extents); + } + + hb_bool_t get_glyph_contour_point (hb_codepoint_t glyph, unsigned int point_index, + hb_position_t *x, hb_position_t *y) + { + *x = *y = 0; + return klass->get.f.glyph_contour_point (this, user_data, + glyph, point_index, + x, y, + !klass->user_data ? nullptr : klass->user_data->glyph_contour_point); + } + + hb_bool_t get_glyph_name (hb_codepoint_t glyph, + char *name, unsigned int size) + { + if (size) *name = '\0'; + return klass->get.f.glyph_name (this, user_data, + glyph, + name, size, + !klass->user_data ? nullptr : klass->user_data->glyph_name); + } + + hb_bool_t get_glyph_from_name (const char *name, int len, /* -1 means nul-terminated */ + hb_codepoint_t *glyph) + { + *glyph = 0; + if (len == -1) len = strlen (name); + return klass->get.f.glyph_from_name (this, user_data, + name, len, + glyph, + !klass->user_data ? nullptr : klass->user_data->glyph_from_name); + } + + void get_glyph_shape (hb_codepoint_t glyph, + hb_draw_funcs_t *draw_funcs, void *draw_data) + { + klass->get.f.glyph_shape (this, user_data, + glyph, + draw_funcs, draw_data, + !klass->user_data ? nullptr : klass->user_data->glyph_shape); + } + + + /* A bit higher-level, and with fallback */ + + void get_h_extents_with_fallback (hb_font_extents_t *extents) + { + if (!get_font_h_extents (extents)) + { + extents->ascender = y_scale * .8; + extents->descender = extents->ascender - y_scale; + extents->line_gap = 0; + } + } + void get_v_extents_with_fallback (hb_font_extents_t *extents) + { + if (!get_font_v_extents (extents)) + { + extents->ascender = x_scale / 2; + extents->descender = extents->ascender - x_scale; + extents->line_gap = 0; + } + } + + void get_extents_for_direction (hb_direction_t direction, + hb_font_extents_t *extents) + { + if (likely (HB_DIRECTION_IS_HORIZONTAL (direction))) + get_h_extents_with_fallback (extents); + else + get_v_extents_with_fallback (extents); + } + + void get_glyph_advance_for_direction (hb_codepoint_t glyph, + hb_direction_t direction, + hb_position_t *x, hb_position_t *y) + { + *x = *y = 0; + if (likely (HB_DIRECTION_IS_HORIZONTAL (direction))) + *x = get_glyph_h_advance (glyph); + else + *y = get_glyph_v_advance (glyph); + } + void get_glyph_advances_for_direction (hb_direction_t direction, + unsigned int count, + const hb_codepoint_t *first_glyph, + unsigned glyph_stride, + hb_position_t *first_advance, + unsigned advance_stride) + { + if (likely (HB_DIRECTION_IS_HORIZONTAL (direction))) + get_glyph_h_advances (count, first_glyph, glyph_stride, first_advance, advance_stride); + else + get_glyph_v_advances (count, first_glyph, glyph_stride, first_advance, advance_stride); + } + + void guess_v_origin_minus_h_origin (hb_codepoint_t glyph, + hb_position_t *x, hb_position_t *y) + { + *x = get_glyph_h_advance (glyph) / 2; + + hb_font_extents_t extents; + get_h_extents_with_fallback (&extents); + *y = extents.ascender; + } + + void get_glyph_h_origin_with_fallback (hb_codepoint_t glyph, + hb_position_t *x, hb_position_t *y) + { + if (!get_glyph_h_origin (glyph, x, y) && + get_glyph_v_origin (glyph, x, y)) + { + hb_position_t dx, dy; + guess_v_origin_minus_h_origin (glyph, &dx, &dy); + *x -= dx; *y -= dy; + } + } + void get_glyph_v_origin_with_fallback (hb_codepoint_t glyph, + hb_position_t *x, hb_position_t *y) + { + if (!get_glyph_v_origin (glyph, x, y) && + get_glyph_h_origin (glyph, x, y)) + { + hb_position_t dx, dy; + guess_v_origin_minus_h_origin (glyph, &dx, &dy); + *x += dx; *y += dy; + } + } + + void get_glyph_origin_for_direction (hb_codepoint_t glyph, + hb_direction_t direction, + hb_position_t *x, hb_position_t *y) + { + if (likely (HB_DIRECTION_IS_HORIZONTAL (direction))) + get_glyph_h_origin_with_fallback (glyph, x, y); + else + get_glyph_v_origin_with_fallback (glyph, x, y); + } + + void add_glyph_h_origin (hb_codepoint_t glyph, + hb_position_t *x, hb_position_t *y) + { + hb_position_t origin_x, origin_y; + + get_glyph_h_origin_with_fallback (glyph, &origin_x, &origin_y); + + *x += origin_x; + *y += origin_y; + } + void add_glyph_v_origin (hb_codepoint_t glyph, + hb_position_t *x, hb_position_t *y) + { + hb_position_t origin_x, origin_y; + + get_glyph_v_origin_with_fallback (glyph, &origin_x, &origin_y); + + *x += origin_x; + *y += origin_y; + } + void add_glyph_origin_for_direction (hb_codepoint_t glyph, + hb_direction_t direction, + hb_position_t *x, hb_position_t *y) + { + hb_position_t origin_x, origin_y; + + get_glyph_origin_for_direction (glyph, direction, &origin_x, &origin_y); + + *x += origin_x; + *y += origin_y; + } + + void subtract_glyph_h_origin (hb_codepoint_t glyph, + hb_position_t *x, hb_position_t *y) + { + hb_position_t origin_x, origin_y; + + get_glyph_h_origin_with_fallback (glyph, &origin_x, &origin_y); + + *x -= origin_x; + *y -= origin_y; + } + void subtract_glyph_v_origin (hb_codepoint_t glyph, + hb_position_t *x, hb_position_t *y) + { + hb_position_t origin_x, origin_y; + + get_glyph_v_origin_with_fallback (glyph, &origin_x, &origin_y); + + *x -= origin_x; + *y -= origin_y; + } + void subtract_glyph_origin_for_direction (hb_codepoint_t glyph, + hb_direction_t direction, + hb_position_t *x, hb_position_t *y) + { + hb_position_t origin_x, origin_y; + + get_glyph_origin_for_direction (glyph, direction, &origin_x, &origin_y); + + *x -= origin_x; + *y -= origin_y; + } + + void get_glyph_kerning_for_direction (hb_codepoint_t first_glyph, hb_codepoint_t second_glyph, + hb_direction_t direction, + hb_position_t *x, hb_position_t *y) + { + if (likely (HB_DIRECTION_IS_HORIZONTAL (direction))) { + *y = 0; + *x = get_glyph_h_kerning (first_glyph, second_glyph); + } else { + *x = 0; + *y = get_glyph_v_kerning (first_glyph, second_glyph); + } + } + + hb_bool_t get_glyph_extents_for_origin (hb_codepoint_t glyph, + hb_direction_t direction, + hb_glyph_extents_t *extents) + { + hb_bool_t ret = get_glyph_extents (glyph, extents); + + if (ret) + subtract_glyph_origin_for_direction (glyph, direction, &extents->x_bearing, &extents->y_bearing); + + return ret; + } + + hb_bool_t get_glyph_contour_point_for_origin (hb_codepoint_t glyph, unsigned int point_index, + hb_direction_t direction, + hb_position_t *x, hb_position_t *y) + { + hb_bool_t ret = get_glyph_contour_point (glyph, point_index, x, y); + + if (ret) + subtract_glyph_origin_for_direction (glyph, direction, x, y); + + return ret; + } + + /* Generates gidDDD if glyph has no name. */ + void + glyph_to_string (hb_codepoint_t glyph, + char *s, unsigned int size) + { + if (get_glyph_name (glyph, s, size)) return; + + if (size && snprintf (s, size, "gid%u", glyph) < 0) + *s = '\0'; + } + + /* Parses gidDDD and uniUUUU strings automatically. */ + hb_bool_t + glyph_from_string (const char *s, int len, /* -1 means nul-terminated */ + hb_codepoint_t *glyph) + { + if (get_glyph_from_name (s, len, glyph)) return true; + + if (len == -1) len = strlen (s); + + /* Straight glyph index. */ + if (hb_codepoint_parse (s, len, 10, glyph)) + return true; + + if (len > 3) + { + /* gidDDD syntax for glyph indices. */ + if (0 == strncmp (s, "gid", 3) && + hb_codepoint_parse (s + 3, len - 3, 10, glyph)) + return true; + + /* uniUUUU and other Unicode character indices. */ + hb_codepoint_t unichar; + if (0 == strncmp (s, "uni", 3) && + hb_codepoint_parse (s + 3, len - 3, 16, &unichar) && + get_nominal_glyph (unichar, glyph)) + return true; + } + + return false; + } + + void mults_changed () + { + float upem = face->get_upem (); + x_multf = x_scale / upem; + y_multf = y_scale / upem; + bool x_neg = x_scale < 0; + x_mult = (x_neg ? -((int64_t) -x_scale << 16) : ((int64_t) x_scale << 16)) / upem; + bool y_neg = y_scale < 0; + y_mult = (y_neg ? -((int64_t) -y_scale << 16) : ((int64_t) y_scale << 16)) / upem; + slant_xy = y_scale ? slant * x_scale / y_scale : 0.f; + + data.fini (); + } + + hb_position_t em_mult (int16_t v, int64_t mult) + { return (hb_position_t) ((v * mult + 32768) >> 16); } + hb_position_t em_multf (float v, float mult) + { return (hb_position_t) roundf (em_fmultf (v, mult)); } + float em_fmultf (float v, float mult) + { return v * mult; } + float em_fmult (int16_t v, float mult) + { return (float) v * mult; } +}; +DECLARE_NULL_INSTANCE (hb_font_t); + + +#endif /* HB_FONT_HH */ diff --git a/libs/harfbuzz/src/hb-ft.cc b/libs/harfbuzz/src/hb-ft.cc new file mode 100644 index 00000000..bcc1dd08 --- /dev/null +++ b/libs/harfbuzz/src/hb-ft.cc @@ -0,0 +1,1283 @@ +/* + * Copyright © 2009 Red Hat, Inc. + * Copyright © 2009 Keith Stribley + * Copyright © 2015 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. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#include "hb.hh" + +#ifdef HAVE_FREETYPE + +#include "hb-ft.h" + +#include "hb-draw.hh" +#include "hb-font.hh" +#include "hb-machinery.hh" +#include "hb-cache.hh" +#include "hb-ot-os2-table.hh" +#include "hb-ot-shaper-arabic-pua.hh" + +#include FT_ADVANCES_H +#include FT_MULTIPLE_MASTERS_H +#include FT_OUTLINE_H +#include FT_TRUETYPE_TABLES_H + + +/** + * SECTION:hb-ft + * @title: hb-ft + * @short_description: FreeType integration + * @include: hb-ft.h + * + * Functions for using HarfBuzz with the FreeType library. + * + * HarfBuzz supports using FreeType to provide face and + * font data. + * + * Note that FreeType is not thread-safe, therefore these + * functions are not thread-safe either. + **/ + + +/* TODO: + * + * In general, this file does a fine job of what it's supposed to do. + * There are, however, things that need more work: + * + * - FreeType works in 26.6 mode. Clients can decide to use that mode, and everything + * would work fine. However, we also abuse this API for performing in font-space, + * but don't pass the correct flags to FreeType. We just abuse the no-hinting mode + * for that, such that no rounding etc happens. As such, we don't set ppem, and + * pass NO_HINTING as load_flags. Would be much better to use NO_SCALE, and scale + * ourselves. + * + * - We don't handle / allow for emboldening / obliqueing. + * + * - In the future, we should add constructors to create fonts in font space? + */ + + +using hb_ft_advance_cache_t = hb_cache_t<16, 24, 8, false>; + +struct hb_ft_font_t +{ + int load_flags; + bool symbol; /* Whether selected cmap is symbol cmap. */ + bool unref; /* Whether to destroy ft_face when done. */ + bool transform; /* Whether to apply FT_Face's transform. */ + + mutable hb_mutex_t lock; + FT_Face ft_face; + mutable unsigned cached_serial; + mutable hb_ft_advance_cache_t advance_cache; +}; + +static hb_ft_font_t * +_hb_ft_font_create (FT_Face ft_face, bool symbol, bool unref) +{ + hb_ft_font_t *ft_font = (hb_ft_font_t *) hb_calloc (1, sizeof (hb_ft_font_t)); + if (unlikely (!ft_font)) return nullptr; + + ft_font->lock.init (); + ft_font->ft_face = ft_face; + ft_font->symbol = symbol; + ft_font->unref = unref; + + ft_font->load_flags = FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING; + + ft_font->cached_serial = (unsigned) -1; + ft_font->advance_cache.init (); + + return ft_font; +} + +static void +_hb_ft_face_destroy (void *data) +{ + FT_Done_Face ((FT_Face) data); +} + +static void +_hb_ft_font_destroy (void *data) +{ + hb_ft_font_t *ft_font = (hb_ft_font_t *) data; + + ft_font->advance_cache.fini (); + + if (ft_font->unref) + _hb_ft_face_destroy (ft_font->ft_face); + + ft_font->lock.fini (); + + hb_free (ft_font); +} + + +/* hb_font changed, update FT_Face. */ +static void _hb_ft_hb_font_changed (hb_font_t *font, FT_Face ft_face) +{ + hb_ft_font_t *ft_font = (hb_ft_font_t *) font->user_data; + + float x_mult = 1.f, y_mult = 1.f; + + if (font->x_scale < 0) x_mult = -x_mult; + if (font->y_scale < 0) y_mult = -y_mult; + + if (FT_Set_Char_Size (ft_face, + abs (font->x_scale), abs (font->y_scale), + 0, 0 +#if 0 + font->x_ppem * 72 * 64 / font->x_scale, + font->y_ppem * 72 * 64 / font->y_scale +#endif + ) && ft_face->num_fixed_sizes) + { +#ifdef HAVE_FT_GET_TRANSFORM + /* Bitmap font, eg. bitmap color emoji. */ + /* TODO Pick largest size? */ + int x_scale = ft_face->available_sizes[0].x_ppem; + int y_scale = ft_face->available_sizes[0].y_ppem; + FT_Set_Char_Size (ft_face, + x_scale, y_scale, + 0, 0); + + /* This contains the sign that was previously in x_mult/y_mult. */ + x_mult = (float) font->x_scale / x_scale; + y_mult = (float) font->y_scale / y_scale; +#endif + } + else + { /* Shrug */ } + + + if (x_mult != 1.f || y_mult != 1.f) + { + FT_Matrix matrix = { (int) roundf (x_mult * (1<<16)), 0, + 0, (int) roundf (y_mult * (1<<16))}; + FT_Set_Transform (ft_face, &matrix, nullptr); + ft_font->transform = true; + } + +#if defined(HAVE_FT_GET_VAR_BLEND_COORDINATES) && !defined(HB_NO_VAR) + unsigned int num_coords; + const float *coords = hb_font_get_var_coords_design (font, &num_coords); + if (num_coords) + { + FT_Fixed *ft_coords = (FT_Fixed *) hb_calloc (num_coords, sizeof (FT_Fixed)); + if (ft_coords) + { + for (unsigned int i = 0; i < num_coords; i++) + ft_coords[i] = coords[i] * 65536.f; + FT_Set_Var_Design_Coordinates (ft_face, num_coords, ft_coords); + hb_free (ft_coords); + } + } +#endif +} + +/* Check if hb_font changed, update FT_Face. */ +static inline bool +_hb_ft_hb_font_check_changed (hb_font_t *font, + const hb_ft_font_t *ft_font) +{ + if (font->serial != ft_font->cached_serial) + { + _hb_ft_hb_font_changed (font, ft_font->ft_face); + ft_font->advance_cache.clear (); + ft_font->cached_serial = font->serial; + return true; + } + return false; +} + + +/** + * hb_ft_font_set_load_flags: + * @font: #hb_font_t to work upon + * @load_flags: The FreeType load flags to set + * + * Sets the FT_Load_Glyph load flags for the specified #hb_font_t. + * + * For more information, see + * https://www.freetype.org/freetype2/docs/reference/ft2-base_interface.html#ft_load_xxx + * + * Since: 1.0.5 + **/ +void +hb_ft_font_set_load_flags (hb_font_t *font, int load_flags) +{ + if (hb_object_is_immutable (font)) + return; + + if (unlikely (font->destroy != (hb_destroy_func_t) _hb_ft_font_destroy)) + return; + + hb_ft_font_t *ft_font = (hb_ft_font_t *) font->user_data; + + ft_font->load_flags = load_flags; +} + +/** + * hb_ft_font_get_load_flags: + * @font: #hb_font_t to work upon + * + * Fetches the FT_Load_Glyph load flags of the specified #hb_font_t. + * + * For more information, see + * https://www.freetype.org/freetype2/docs/reference/ft2-base_interface.html#ft_load_xxx + * + * Return value: FT_Load_Glyph flags found + * + * Since: 1.0.5 + **/ +int +hb_ft_font_get_load_flags (hb_font_t *font) +{ + if (unlikely (font->destroy != (hb_destroy_func_t) _hb_ft_font_destroy)) + return 0; + + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font->user_data; + + return ft_font->load_flags; +} + +/** + * hb_ft_font_get_face: (skip) + * @font: #hb_font_t to work upon + * + * Fetches the FT_Face associated with the specified #hb_font_t + * font object. + * + * Return value: (nullable): the FT_Face found or `NULL` + * + * Since: 0.9.2 + **/ +FT_Face +hb_ft_font_get_face (hb_font_t *font) +{ + if (unlikely (font->destroy != (hb_destroy_func_t) _hb_ft_font_destroy)) + return nullptr; + + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font->user_data; + + return ft_font->ft_face; +} + +/** + * hb_ft_font_lock_face: (skip) + * @font: #hb_font_t to work upon + * + * Gets the FT_Face associated with @font, This face will be kept around until + * you call hb_ft_font_unlock_face(). + * + * Return value: (nullable) (transfer none): the FT_Face associated with @font or `NULL` + * Since: 2.6.5 + **/ +FT_Face +hb_ft_font_lock_face (hb_font_t *font) +{ + if (unlikely (font->destroy != (hb_destroy_func_t) _hb_ft_font_destroy)) + return nullptr; + + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font->user_data; + + ft_font->lock.lock (); + + return ft_font->ft_face; +} + +/** + * hb_ft_font_unlock_face: (skip) + * @font: #hb_font_t to work upon + * + * Releases an FT_Face previously obtained with hb_ft_font_lock_face(). + * + * Since: 2.6.5 + **/ +void +hb_ft_font_unlock_face (hb_font_t *font) +{ + if (unlikely (font->destroy != (hb_destroy_func_t) _hb_ft_font_destroy)) + return; + + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font->user_data; + + ft_font->lock.unlock (); +} + + +static hb_bool_t +hb_ft_get_nominal_glyph (hb_font_t *font, + void *font_data, + hb_codepoint_t unicode, + hb_codepoint_t *glyph, + void *user_data HB_UNUSED) +{ + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + hb_lock_t lock (ft_font->lock); + unsigned int g = FT_Get_Char_Index (ft_font->ft_face, unicode); + + if (unlikely (!g)) + { + if (unlikely (ft_font->symbol)) + { + switch ((unsigned) font->face->table.OS2->get_font_page ()) { + case OT::OS2::font_page_t::FONT_PAGE_NONE: + if (unicode <= 0x00FFu) + /* For symbol-encoded OpenType fonts, we duplicate the + * U+F000..F0FF range at U+0000..U+00FF. That's what + * Windows seems to do, and that's hinted about at: + * https://docs.microsoft.com/en-us/typography/opentype/spec/recom + * under "Non-Standard (Symbol) Fonts". */ + g = FT_Get_Char_Index (ft_font->ft_face, 0xF000u + unicode); + break; +#ifndef HB_NO_OT_SHAPER_ARABIC_FALLBACK + case OT::OS2::font_page_t::FONT_PAGE_SIMP_ARABIC: + g = FT_Get_Char_Index (ft_font->ft_face, _hb_arabic_pua_simp_map (unicode)); + break; + case OT::OS2::font_page_t::FONT_PAGE_TRAD_ARABIC: + g = FT_Get_Char_Index (ft_font->ft_face, _hb_arabic_pua_trad_map (unicode)); + break; +#endif + default: + break; + } + if (!g) + return false; + } + else + return false; + } + + *glyph = g; + return true; +} + +static unsigned int +hb_ft_get_nominal_glyphs (hb_font_t *font HB_UNUSED, + void *font_data, + unsigned int count, + const hb_codepoint_t *first_unicode, + unsigned int unicode_stride, + hb_codepoint_t *first_glyph, + unsigned int glyph_stride, + void *user_data HB_UNUSED) +{ + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + hb_lock_t lock (ft_font->lock); + unsigned int done; + for (done = 0; + done < count && (*first_glyph = FT_Get_Char_Index (ft_font->ft_face, *first_unicode)); + done++) + { + first_unicode = &StructAtOffsetUnaligned (first_unicode, unicode_stride); + first_glyph = &StructAtOffsetUnaligned (first_glyph, glyph_stride); + } + /* We don't need to do ft_font->symbol dance here, since HB calls the singular + * nominal_glyph() for what we don't handle here. */ + return done; +} + + +static hb_bool_t +hb_ft_get_variation_glyph (hb_font_t *font HB_UNUSED, + void *font_data, + hb_codepoint_t unicode, + hb_codepoint_t variation_selector, + hb_codepoint_t *glyph, + void *user_data HB_UNUSED) +{ + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + hb_lock_t lock (ft_font->lock); + unsigned int g = FT_Face_GetCharVariantIndex (ft_font->ft_face, unicode, variation_selector); + + if (unlikely (!g)) + return false; + + *glyph = g; + return true; +} + +static void +hb_ft_get_glyph_h_advances (hb_font_t* font, void* font_data, + unsigned count, + const hb_codepoint_t *first_glyph, + unsigned glyph_stride, + hb_position_t *first_advance, + unsigned advance_stride, + void *user_data HB_UNUSED) +{ + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + hb_lock_t lock (ft_font->lock); + FT_Face ft_face = ft_font->ft_face; + int load_flags = ft_font->load_flags; + float x_mult; +#ifdef HAVE_FT_GET_TRANSFORM + if (ft_font->transform) + { + FT_Matrix matrix; + FT_Get_Transform (ft_face, &matrix, nullptr); + x_mult = sqrtf ((float)matrix.xx * matrix.xx + (float)matrix.xy * matrix.xy) / 65536.f; + } + else +#endif + { + x_mult = font->x_scale < 0 ? -1 : +1; + } + + for (unsigned int i = 0; i < count; i++) + { + FT_Fixed v = 0; + hb_codepoint_t glyph = *first_glyph; + + unsigned int cv; + if (ft_font->advance_cache.get (glyph, &cv)) + v = cv; + else + { + FT_Get_Advance (ft_face, glyph, load_flags, &v); + ft_font->advance_cache.set (glyph, v); + } + + *first_advance = (int) (v * x_mult + (1<<9)) >> 10; + first_glyph = &StructAtOffsetUnaligned (first_glyph, glyph_stride); + first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride); + } +} + +#ifndef HB_NO_VERTICAL +static hb_position_t +hb_ft_get_glyph_v_advance (hb_font_t *font, + void *font_data, + hb_codepoint_t glyph, + void *user_data HB_UNUSED) +{ + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + hb_lock_t lock (ft_font->lock); + FT_Fixed v; + float y_mult; +#ifdef HAVE_FT_GET_TRANSFORM + if (ft_font->transform) + { + FT_Matrix matrix; + FT_Get_Transform (ft_font->ft_face, &matrix, nullptr); + y_mult = sqrtf ((float)matrix.yx * matrix.yx + (float)matrix.yy * matrix.yy) / 65536.f; + } + else +#endif + { + y_mult = font->y_scale < 0 ? -1 : +1; + } + + if (unlikely (FT_Get_Advance (ft_font->ft_face, glyph, ft_font->load_flags | FT_LOAD_VERTICAL_LAYOUT, &v))) + return 0; + + v = (int) (y_mult * v); + + /* Note: FreeType's vertical metrics grows downward while other FreeType coordinates + * have a Y growing upward. Hence the extra negation. */ + + return (-v + (1<<9)) >> 10; +} +#endif + +#ifndef HB_NO_VERTICAL +static hb_bool_t +hb_ft_get_glyph_v_origin (hb_font_t *font, + void *font_data, + hb_codepoint_t glyph, + hb_position_t *x, + hb_position_t *y, + void *user_data HB_UNUSED) +{ + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + hb_lock_t lock (ft_font->lock); + FT_Face ft_face = ft_font->ft_face; + float x_mult, y_mult; +#ifdef HAVE_FT_GET_TRANSFORM + if (ft_font->transform) + { + FT_Matrix matrix; + FT_Get_Transform (ft_face, &matrix, nullptr); + x_mult = sqrtf ((float)matrix.xx * matrix.xx + (float)matrix.xy * matrix.xy) / 65536.f; + y_mult = sqrtf ((float)matrix.yx * matrix.yx + (float)matrix.yy * matrix.yy) / 65536.f; + } + else +#endif + { + x_mult = font->x_scale < 0 ? -1 : +1; + y_mult = font->y_scale < 0 ? -1 : +1; + } + + if (unlikely (FT_Load_Glyph (ft_face, glyph, ft_font->load_flags))) + return false; + + /* Note: FreeType's vertical metrics grows downward while other FreeType coordinates + * have a Y growing upward. Hence the extra negation. */ + *x = ft_face->glyph->metrics.horiBearingX - ft_face->glyph->metrics.vertBearingX; + *y = ft_face->glyph->metrics.horiBearingY - (-ft_face->glyph->metrics.vertBearingY); + + *x = (hb_position_t) (x_mult * *x); + *y = (hb_position_t) (y_mult * *y); + + return true; +} +#endif + +#ifndef HB_NO_OT_SHAPE_FALLBACK +static hb_position_t +hb_ft_get_glyph_h_kerning (hb_font_t *font, + void *font_data, + hb_codepoint_t left_glyph, + hb_codepoint_t right_glyph, + void *user_data HB_UNUSED) +{ + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + hb_lock_t lock (ft_font->lock); + FT_Vector kerningv; + + FT_Kerning_Mode mode = font->x_ppem ? FT_KERNING_DEFAULT : FT_KERNING_UNFITTED; + if (FT_Get_Kerning (ft_font->ft_face, left_glyph, right_glyph, mode, &kerningv)) + return 0; + + return kerningv.x; +} +#endif + +static hb_bool_t +hb_ft_get_glyph_extents (hb_font_t *font, + void *font_data, + hb_codepoint_t glyph, + hb_glyph_extents_t *extents, + void *user_data HB_UNUSED) +{ + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + hb_lock_t lock (ft_font->lock); + FT_Face ft_face = ft_font->ft_face; + float x_mult, y_mult; +#ifdef HAVE_FT_GET_TRANSFORM + if (ft_font->transform) + { + FT_Matrix matrix; + FT_Get_Transform (ft_face, &matrix, nullptr); + x_mult = sqrtf ((float)matrix.xx * matrix.xx + (float)matrix.xy * matrix.xy) / 65536.f; + y_mult = sqrtf ((float)matrix.yx * matrix.yx + (float)matrix.yy * matrix.yy) / 65536.f; + } + else +#endif + { + x_mult = font->x_scale < 0 ? -1 : +1; + y_mult = font->y_scale < 0 ? -1 : +1; + } + + if (unlikely (FT_Load_Glyph (ft_face, glyph, ft_font->load_flags))) + return false; + + extents->x_bearing = (hb_position_t) (x_mult * ft_face->glyph->metrics.horiBearingX); + extents->y_bearing = (hb_position_t) (y_mult * ft_face->glyph->metrics.horiBearingY); + extents->width = (hb_position_t) (x_mult * ft_face->glyph->metrics.width); + extents->height = (hb_position_t) (y_mult * -ft_face->glyph->metrics.height); + + return true; +} + +static hb_bool_t +hb_ft_get_glyph_contour_point (hb_font_t *font HB_UNUSED, + void *font_data, + hb_codepoint_t glyph, + unsigned int point_index, + hb_position_t *x, + hb_position_t *y, + void *user_data HB_UNUSED) +{ + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + hb_lock_t lock (ft_font->lock); + FT_Face ft_face = ft_font->ft_face; + + if (unlikely (FT_Load_Glyph (ft_face, glyph, ft_font->load_flags))) + return false; + + if (unlikely (ft_face->glyph->format != FT_GLYPH_FORMAT_OUTLINE)) + return false; + + if (unlikely (point_index >= (unsigned int) ft_face->glyph->outline.n_points)) + return false; + + *x = ft_face->glyph->outline.points[point_index].x; + *y = ft_face->glyph->outline.points[point_index].y; + + return true; +} + +static hb_bool_t +hb_ft_get_glyph_name (hb_font_t *font HB_UNUSED, + void *font_data, + hb_codepoint_t glyph, + char *name, unsigned int size, + void *user_data HB_UNUSED) +{ + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + hb_lock_t lock (ft_font->lock); + FT_Face ft_face = ft_font->ft_face; + + hb_bool_t ret = !FT_Get_Glyph_Name (ft_face, glyph, name, size); + if (ret && (size && !*name)) + ret = false; + + return ret; +} + +static hb_bool_t +hb_ft_get_glyph_from_name (hb_font_t *font HB_UNUSED, + void *font_data, + const char *name, int len, /* -1 means nul-terminated */ + hb_codepoint_t *glyph, + void *user_data HB_UNUSED) +{ + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + hb_lock_t lock (ft_font->lock); + FT_Face ft_face = ft_font->ft_face; + + if (len < 0) + *glyph = FT_Get_Name_Index (ft_face, (FT_String *) name); + else { + /* Make a nul-terminated version. */ + char buf[128]; + len = hb_min (len, (int) sizeof (buf) - 1); + strncpy (buf, name, len); + buf[len] = '\0'; + *glyph = FT_Get_Name_Index (ft_face, buf); + } + + if (*glyph == 0) + { + /* Check whether the given name was actually the name of glyph 0. */ + char buf[128]; + if (!FT_Get_Glyph_Name(ft_face, 0, buf, sizeof (buf)) && + len < 0 ? !strcmp (buf, name) : !strncmp (buf, name, len)) + return true; + } + + return *glyph != 0; +} + +static hb_bool_t +hb_ft_get_font_h_extents (hb_font_t *font HB_UNUSED, + void *font_data, + hb_font_extents_t *metrics, + void *user_data HB_UNUSED) +{ + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + hb_lock_t lock (ft_font->lock); + FT_Face ft_face = ft_font->ft_face; + float y_mult; +#ifdef HAVE_FT_GET_TRANSFORM + if (ft_font->transform) + { + FT_Matrix matrix; + FT_Get_Transform (ft_face, &matrix, nullptr); + y_mult = sqrtf ((float)matrix.yx * matrix.yx + (float)matrix.yy * matrix.yy) / 65536.f; + } + else +#endif + { + y_mult = font->y_scale < 0 ? -1 : +1; + } + + if (ft_face->units_per_EM != 0) + { + metrics->ascender = FT_MulFix(ft_face->ascender, ft_face->size->metrics.y_scale); + metrics->descender = FT_MulFix(ft_face->descender, ft_face->size->metrics.y_scale); + metrics->line_gap = FT_MulFix( ft_face->height, ft_face->size->metrics.y_scale ) - (metrics->ascender - metrics->descender); + } + else + { + /* Bitmap-only font, eg. color bitmap font. */ + metrics->ascender = ft_face->size->metrics.ascender; + metrics->descender = ft_face->size->metrics.descender; + metrics->line_gap = ft_face->size->metrics.height - (metrics->ascender - metrics->descender); + } + + metrics->ascender = (hb_position_t) (y_mult * metrics->ascender); + metrics->descender = (hb_position_t) (y_mult * metrics->descender); + metrics->line_gap = (hb_position_t) (y_mult * metrics->line_gap); + + return true; +} + +#ifndef HB_NO_DRAW + +static int +_hb_ft_move_to (const FT_Vector *to, + hb_draw_session_t *drawing) +{ + drawing->move_to (to->x, to->y); + return FT_Err_Ok; +} + +static int +_hb_ft_line_to (const FT_Vector *to, + hb_draw_session_t *drawing) +{ + drawing->line_to (to->x, to->y); + return FT_Err_Ok; +} + +static int +_hb_ft_conic_to (const FT_Vector *control, + const FT_Vector *to, + hb_draw_session_t *drawing) +{ + drawing->quadratic_to (control->x, control->y, + to->x, to->y); + return FT_Err_Ok; +} + +static int +_hb_ft_cubic_to (const FT_Vector *control1, + const FT_Vector *control2, + const FT_Vector *to, + hb_draw_session_t *drawing) +{ + drawing->cubic_to (control1->x, control1->y, + control2->x, control2->y, + to->x, to->y); + return FT_Err_Ok; +} + +static void +hb_ft_get_glyph_shape (hb_font_t *font HB_UNUSED, + void *font_data, + hb_codepoint_t glyph, + hb_draw_funcs_t *draw_funcs, void *draw_data, + void *user_data HB_UNUSED) +{ + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + hb_lock_t lock (ft_font->lock); + FT_Face ft_face = ft_font->ft_face; + + if (unlikely (FT_Load_Glyph (ft_face, glyph, + FT_LOAD_NO_BITMAP | ft_font->load_flags))) + return; + + if (ft_face->glyph->format != FT_GLYPH_FORMAT_OUTLINE) + return; + + const FT_Outline_Funcs outline_funcs = { + (FT_Outline_MoveToFunc) _hb_ft_move_to, + (FT_Outline_LineToFunc) _hb_ft_line_to, + (FT_Outline_ConicToFunc) _hb_ft_conic_to, + (FT_Outline_CubicToFunc) _hb_ft_cubic_to, + 0, /* shift */ + 0, /* delta */ + }; + + hb_draw_session_t draw_session (draw_funcs, draw_data, font->slant_xy); + + FT_Outline_Decompose (&ft_face->glyph->outline, + &outline_funcs, + &draw_session); +} +#endif + + +static inline void free_static_ft_funcs (); + +static struct hb_ft_font_funcs_lazy_loader_t : hb_font_funcs_lazy_loader_t +{ + static hb_font_funcs_t *create () + { + hb_font_funcs_t *funcs = hb_font_funcs_create (); + + hb_font_funcs_set_nominal_glyph_func (funcs, hb_ft_get_nominal_glyph, nullptr, nullptr); + hb_font_funcs_set_nominal_glyphs_func (funcs, hb_ft_get_nominal_glyphs, nullptr, nullptr); + hb_font_funcs_set_variation_glyph_func (funcs, hb_ft_get_variation_glyph, nullptr, nullptr); + + hb_font_funcs_set_font_h_extents_func (funcs, hb_ft_get_font_h_extents, nullptr, nullptr); + hb_font_funcs_set_glyph_h_advances_func (funcs, hb_ft_get_glyph_h_advances, nullptr, nullptr); + //hb_font_funcs_set_glyph_h_origin_func (funcs, hb_ft_get_glyph_h_origin, nullptr, nullptr); + +#ifndef HB_NO_VERTICAL + //hb_font_funcs_set_font_v_extents_func (funcs, hb_ft_get_font_v_extents, nullptr, nullptr); + hb_font_funcs_set_glyph_v_advance_func (funcs, hb_ft_get_glyph_v_advance, nullptr, nullptr); + hb_font_funcs_set_glyph_v_origin_func (funcs, hb_ft_get_glyph_v_origin, nullptr, nullptr); +#endif + +#ifndef HB_NO_OT_SHAPE_FALLBACK + hb_font_funcs_set_glyph_h_kerning_func (funcs, hb_ft_get_glyph_h_kerning, nullptr, nullptr); +#endif + //hb_font_funcs_set_glyph_v_kerning_func (funcs, hb_ft_get_glyph_v_kerning, nullptr, nullptr); + hb_font_funcs_set_glyph_extents_func (funcs, hb_ft_get_glyph_extents, nullptr, nullptr); + hb_font_funcs_set_glyph_contour_point_func (funcs, hb_ft_get_glyph_contour_point, nullptr, nullptr); + hb_font_funcs_set_glyph_name_func (funcs, hb_ft_get_glyph_name, nullptr, nullptr); + hb_font_funcs_set_glyph_from_name_func (funcs, hb_ft_get_glyph_from_name, nullptr, nullptr); + +#ifndef HB_NO_DRAW + hb_font_funcs_set_glyph_shape_func (funcs, hb_ft_get_glyph_shape, nullptr, nullptr); +#endif + + hb_font_funcs_make_immutable (funcs); + + hb_atexit (free_static_ft_funcs); + + return funcs; + } +} static_ft_funcs; + +static inline +void free_static_ft_funcs () +{ + static_ft_funcs.free_instance (); +} + +static hb_font_funcs_t * +_hb_ft_get_font_funcs () +{ + return static_ft_funcs.get_unconst (); +} + +static void +_hb_ft_font_set_funcs (hb_font_t *font, FT_Face ft_face, bool unref) +{ + bool symbol = ft_face->charmap && ft_face->charmap->encoding == FT_ENCODING_MS_SYMBOL; + + hb_ft_font_t *ft_font = _hb_ft_font_create (ft_face, symbol, unref); + if (unlikely (!ft_font)) return; + + hb_font_set_funcs (font, + _hb_ft_get_font_funcs (), + ft_font, + _hb_ft_font_destroy); +} + + +static hb_blob_t * +_hb_ft_reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data) +{ + FT_Face ft_face = (FT_Face) user_data; + FT_Byte *buffer; + FT_ULong length = 0; + FT_Error error; + + /* Note: FreeType like HarfBuzz uses the NONE tag for fetching the entire blob */ + + error = FT_Load_Sfnt_Table (ft_face, tag, 0, nullptr, &length); + if (error) + return nullptr; + + buffer = (FT_Byte *) hb_malloc (length); + if (!buffer) + return nullptr; + + error = FT_Load_Sfnt_Table (ft_face, tag, 0, buffer, &length); + if (error) + { + hb_free (buffer); + return nullptr; + } + + return hb_blob_create ((const char *) buffer, length, + HB_MEMORY_MODE_WRITABLE, + buffer, hb_free); +} + +/** + * hb_ft_face_create: + * @ft_face: (destroy destroy) (scope notified): FT_Face to work upon + * @destroy: (nullable): A callback to call when the face object is not needed anymore + * + * Creates an #hb_face_t face object from the specified FT_Face. + * + * This variant of the function does not provide any life-cycle management. + * + * Most client programs should use hb_ft_face_create_referenced() + * (or, perhaps, hb_ft_face_create_cached()) instead. + * + * If you know you have valid reasons not to use hb_ft_face_create_referenced(), + * then it is the client program's responsibility to destroy @ft_face + * after the #hb_face_t face object has been destroyed. + * + * Return value: (transfer full): the new #hb_face_t face object + * + * Since: 0.9.2 + **/ +hb_face_t * +hb_ft_face_create (FT_Face ft_face, + hb_destroy_func_t destroy) +{ + hb_face_t *face; + + if (!ft_face->stream->read) { + hb_blob_t *blob; + + blob = hb_blob_create ((const char *) ft_face->stream->base, + (unsigned int) ft_face->stream->size, + HB_MEMORY_MODE_READONLY, + ft_face, destroy); + face = hb_face_create (blob, ft_face->face_index); + hb_blob_destroy (blob); + } else { + face = hb_face_create_for_tables (_hb_ft_reference_table, ft_face, destroy); + } + + hb_face_set_index (face, ft_face->face_index); + hb_face_set_upem (face, ft_face->units_per_EM); + + return face; +} + +/** + * hb_ft_face_create_referenced: + * @ft_face: FT_Face to work upon + * + * Creates an #hb_face_t face object from the specified FT_Face. + * + * This is the preferred variant of the hb_ft_face_create* + * function family, because it calls FT_Reference_Face() on @ft_face, + * ensuring that @ft_face remains alive as long as the resulting + * #hb_face_t face object remains alive. Also calls FT_Done_Face() + * when the #hb_face_t face object is destroyed. + * + * Use this version unless you know you have good reasons not to. + * + * Return value: (transfer full): the new #hb_face_t face object + * + * Since: 0.9.38 + **/ +hb_face_t * +hb_ft_face_create_referenced (FT_Face ft_face) +{ + FT_Reference_Face (ft_face); + return hb_ft_face_create (ft_face, _hb_ft_face_destroy); +} + +static void +hb_ft_face_finalize (FT_Face ft_face) +{ + hb_face_destroy ((hb_face_t *) ft_face->generic.data); +} + +/** + * hb_ft_face_create_cached: + * @ft_face: FT_Face to work upon + * + * Creates an #hb_face_t face object from the specified FT_Face. + * + * This variant of the function caches the newly created #hb_face_t + * face object, using the @generic pointer of @ft_face. Subsequent function + * calls that are passed the same @ft_face parameter will have the same + * #hb_face_t returned to them, and that #hb_face_t will be correctly + * reference counted. + * + * However, client programs are still responsible for destroying + * @ft_face after the last #hb_face_t face object has been destroyed. + * + * Return value: (transfer full): the new #hb_face_t face object + * + * Since: 0.9.2 + **/ +hb_face_t * +hb_ft_face_create_cached (FT_Face ft_face) +{ + if (unlikely (!ft_face->generic.data || ft_face->generic.finalizer != (FT_Generic_Finalizer) hb_ft_face_finalize)) + { + if (ft_face->generic.finalizer) + ft_face->generic.finalizer (ft_face); + + ft_face->generic.data = hb_ft_face_create (ft_face, nullptr); + ft_face->generic.finalizer = (FT_Generic_Finalizer) hb_ft_face_finalize; + } + + return hb_face_reference ((hb_face_t *) ft_face->generic.data); +} + +/** + * hb_ft_font_create: + * @ft_face: (destroy destroy) (scope notified): FT_Face to work upon + * @destroy: (nullable): A callback to call when the font object is not needed anymore + * + * Creates an #hb_font_t font object from the specified FT_Face. + * + * Note: You must set the face size on @ft_face before calling + * hb_ft_font_create() on it. HarfBuzz assumes size is always set and will + * access `size` member of FT_Face unconditionally. + * + * This variant of the function does not provide any life-cycle management. + * + * Most client programs should use hb_ft_font_create_referenced() + * instead. + * + * If you know you have valid reasons not to use hb_ft_font_create_referenced(), + * then it is the client program's responsibility to destroy @ft_face + * after the #hb_font_t font object has been destroyed. + * + * HarfBuzz will use the @destroy callback on the #hb_font_t font object + * if it is supplied when you use this function. However, even if @destroy + * is provided, it is the client program's responsibility to destroy @ft_face, + * and it is the client program's responsibility to ensure that @ft_face is + * destroyed only after the #hb_font_t font object has been destroyed. + * + * Return value: (transfer full): the new #hb_font_t font object + * + * Since: 0.9.2 + **/ +hb_font_t * +hb_ft_font_create (FT_Face ft_face, + hb_destroy_func_t destroy) +{ + hb_font_t *font; + hb_face_t *face; + + face = hb_ft_face_create (ft_face, destroy); + font = hb_font_create (face); + hb_face_destroy (face); + _hb_ft_font_set_funcs (font, ft_face, false); + hb_ft_font_changed (font); + return font; +} + +/** + * hb_ft_font_changed: + * @font: #hb_font_t to work upon + * + * Refreshes the state of @font when the underlying FT_Face has changed. + * This function should be called after changing the size or + * variation-axis settings on the FT_Face. + * + * Since: 1.0.5 + **/ +void +hb_ft_font_changed (hb_font_t *font) +{ + if (font->destroy != (hb_destroy_func_t) _hb_ft_font_destroy) + return; + + hb_ft_font_t *ft_font = (hb_ft_font_t *) font->user_data; + + FT_Face ft_face = ft_font->ft_face; + + hb_font_set_scale (font, + (int) (((uint64_t) ft_face->size->metrics.x_scale * (uint64_t) ft_face->units_per_EM + (1u<<15)) >> 16), + (int) (((uint64_t) ft_face->size->metrics.y_scale * (uint64_t) ft_face->units_per_EM + (1u<<15)) >> 16)); +#if 0 /* hb-ft works in no-hinting model */ + hb_font_set_ppem (font, + ft_face->size->metrics.x_ppem, + ft_face->size->metrics.y_ppem); +#endif + +#if defined(HAVE_FT_GET_VAR_BLEND_COORDINATES) && !defined(HB_NO_VAR) + FT_MM_Var *mm_var = nullptr; + if (!FT_Get_MM_Var (ft_face, &mm_var)) + { + FT_Fixed *ft_coords = (FT_Fixed *) hb_calloc (mm_var->num_axis, sizeof (FT_Fixed)); + int *coords = (int *) hb_calloc (mm_var->num_axis, sizeof (int)); + if (coords && ft_coords) + { + if (!FT_Get_Var_Blend_Coordinates (ft_face, mm_var->num_axis, ft_coords)) + { + bool nonzero = false; + + for (unsigned int i = 0; i < mm_var->num_axis; ++i) + { + coords[i] = ft_coords[i] >>= 2; + nonzero = nonzero || coords[i]; + } + + if (nonzero) + hb_font_set_var_coords_normalized (font, coords, mm_var->num_axis); + else + hb_font_set_var_coords_normalized (font, nullptr, 0); + } + } + hb_free (coords); + hb_free (ft_coords); +#ifdef HAVE_FT_DONE_MM_VAR + FT_Done_MM_Var (ft_face->glyph->library, mm_var); +#else + hb_free (mm_var); +#endif + } +#endif + + ft_font->advance_cache.clear (); + ft_font->cached_serial = font->serial; +} + +/** + * hb_ft_hb_font_changed: + * @font: #hb_font_t to work upon + * + * Refreshes the state of the underlying FT_Face of @font when the hb_font_t + * @font has changed. + * This function should be called after changing the size or + * variation-axis settings on the @font. + * This call is fast if nothing has changed on @font. + * + * Return value: true if changed, false otherwise + * + * Since: 4.4.0 + **/ +hb_bool_t +hb_ft_hb_font_changed (hb_font_t *font) +{ + if (font->destroy != (hb_destroy_func_t) _hb_ft_font_destroy) + return false; + + hb_ft_font_t *ft_font = (hb_ft_font_t *) font->user_data; + + return _hb_ft_hb_font_check_changed (font, ft_font); +} + +/** + * hb_ft_font_create_referenced: + * @ft_face: FT_Face to work upon + * + * Creates an #hb_font_t font object from the specified FT_Face. + * + * Note: You must set the face size on @ft_face before calling + * hb_ft_font_create_referenced() on it. HarfBuzz assumes size is always set + * and will access `size` member of FT_Face unconditionally. + * + * This is the preferred variant of the hb_ft_font_create* + * function family, because it calls FT_Reference_Face() on @ft_face, + * ensuring that @ft_face remains alive as long as the resulting + * #hb_font_t font object remains alive. + * + * Use this version unless you know you have good reasons not to. + * + * Return value: (transfer full): the new #hb_font_t font object + * + * Since: 0.9.38 + **/ +hb_font_t * +hb_ft_font_create_referenced (FT_Face ft_face) +{ + FT_Reference_Face (ft_face); + return hb_ft_font_create (ft_face, _hb_ft_face_destroy); +} + +static inline void free_static_ft_library (); + +static struct hb_ft_library_lazy_loader_t : hb_lazy_loader_t, + hb_ft_library_lazy_loader_t> +{ + static FT_Library create () + { + FT_Library l; + if (FT_Init_FreeType (&l)) + return nullptr; + + hb_atexit (free_static_ft_library); + + return l; + } + static void destroy (FT_Library l) + { + FT_Done_FreeType (l); + } + static FT_Library get_null () + { + return nullptr; + } +} static_ft_library; + +static inline +void free_static_ft_library () +{ + static_ft_library.free_instance (); +} + +static FT_Library +get_ft_library () +{ + return static_ft_library.get_unconst (); +} + +static void +_release_blob (FT_Face ft_face) +{ + hb_blob_destroy ((hb_blob_t *) ft_face->generic.data); +} + +/** + * hb_ft_font_set_funcs: + * @font: #hb_font_t to work upon + * + * Configures the font-functions structure of the specified + * #hb_font_t font object to use FreeType font functions. + * + * In particular, you can use this function to configure an + * existing #hb_face_t face object for use with FreeType font + * functions even if that #hb_face_t face object was initially + * created with hb_face_create(), and therefore was not + * initially configured to use FreeType font functions. + * + * An #hb_face_t face object created with hb_ft_face_create() + * is preconfigured for FreeType font functions and does not + * require this function to be used. + * + * Note: Internally, this function creates an FT_Face. +* + * + * Since: 1.0.5 + **/ +void +hb_ft_font_set_funcs (hb_font_t *font) +{ + hb_blob_t *blob = hb_face_reference_blob (font->face); + unsigned int blob_length; + const char *blob_data = hb_blob_get_data (blob, &blob_length); + if (unlikely (!blob_length)) + DEBUG_MSG (FT, font, "Font face has empty blob"); + + FT_Face ft_face = nullptr; + FT_Error err = FT_New_Memory_Face (get_ft_library (), + (const FT_Byte *) blob_data, + blob_length, + hb_face_get_index (font->face), + &ft_face); + + if (unlikely (err)) { + hb_blob_destroy (blob); + DEBUG_MSG (FT, font, "Font face FT_New_Memory_Face() failed"); + return; + } + + if (FT_Select_Charmap (ft_face, FT_ENCODING_MS_SYMBOL)) + FT_Select_Charmap (ft_face, FT_ENCODING_UNICODE); + + + ft_face->generic.data = blob; + ft_face->generic.finalizer = (FT_Generic_Finalizer) _release_blob; + + _hb_ft_font_set_funcs (font, ft_face, true); + hb_ft_font_set_load_flags (font, FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING); + + _hb_ft_hb_font_changed (font, ft_face); +} + + +#endif diff --git a/libs/harfbuzz/src/hb-ft.h b/libs/harfbuzz/src/hb-ft.h new file mode 100644 index 00000000..6a8a7abe --- /dev/null +++ b/libs/harfbuzz/src/hb-ft.h @@ -0,0 +1,145 @@ +/* + * Copyright © 2009 Red Hat, Inc. + * Copyright © 2015 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. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_FT_H +#define HB_FT_H + +#include "hb.h" + +#include +#include FT_FREETYPE_H + +HB_BEGIN_DECLS + +/* + * Note: FreeType is not thread-safe. + * Hence, these functions are not either. + */ + +/* + * hb-face from ft-face. + */ + +/* This one creates a new hb-face for given ft-face. + * When the returned hb-face is destroyed, the destroy + * callback is called (if not NULL), with the ft-face passed + * to it. + * + * The client is responsible to make sure that ft-face is + * destroyed after hb-face is destroyed. + * + * Most often you don't want this function. You should use either + * hb_ft_face_create_cached(), or hb_ft_face_create_referenced(). + * In particular, if you are going to pass NULL as destroy, you + * probably should use (the more recent) hb_ft_face_create_referenced() + * instead. + */ +HB_EXTERN hb_face_t * +hb_ft_face_create (FT_Face ft_face, + hb_destroy_func_t destroy); + +/* This version is like hb_ft_face_create(), except that it caches + * the hb-face using the generic pointer of the ft-face. This means + * that subsequent calls to this function with the same ft-face will + * return the same hb-face (correctly referenced). + * + * Client is still responsible for making sure that ft-face is destroyed + * after hb-face is. + */ +HB_EXTERN hb_face_t * +hb_ft_face_create_cached (FT_Face ft_face); + +/* This version is like hb_ft_face_create(), except that it calls + * FT_Reference_Face() on ft-face, as such keeping ft-face alive + * as long as the hb-face is. + * + * This is the most convenient version to use. Use it unless you have + * very good reasons not to. + */ +HB_EXTERN hb_face_t * +hb_ft_face_create_referenced (FT_Face ft_face); + + +/* + * hb-font from ft-face. + */ + +/* + * Note: + * + * Set face size on ft-face before creating hb-font from it. + * Otherwise hb-ft would NOT pick up the font size correctly. + */ + +/* See notes on hb_ft_face_create(). Same issues re lifecycle-management + * apply here. Use hb_ft_font_create_referenced() if you can. */ +HB_EXTERN hb_font_t * +hb_ft_font_create (FT_Face ft_face, + hb_destroy_func_t destroy); + +/* See notes on hb_ft_face_create_referenced() re lifecycle-management + * issues. */ +HB_EXTERN hb_font_t * +hb_ft_font_create_referenced (FT_Face ft_face); + +HB_EXTERN FT_Face +hb_ft_font_get_face (hb_font_t *font); + +HB_EXTERN FT_Face +hb_ft_font_lock_face (hb_font_t *font); + +HB_EXTERN void +hb_ft_font_unlock_face (hb_font_t *font); + +HB_EXTERN void +hb_ft_font_set_load_flags (hb_font_t *font, int load_flags); + +HB_EXTERN int +hb_ft_font_get_load_flags (hb_font_t *font); + +/* Call when size or variations settings on underlying FT_Face changed, + * and you want to update the hb_font_t from it. */ +HB_EXTERN void +hb_ft_font_changed (hb_font_t *font); + +/* Call when size or variations settings on underlying hb_font_t may have + * changed, and you want to update the FT_Face from it. This call is fast + * if nothing changed on hb_font_t. Returns true if changed. */ +HB_EXTERN hb_bool_t +hb_ft_hb_font_changed (hb_font_t *font); + +/* Makes an hb_font_t use FreeType internally to implement font functions. + * Note: this internally creates an FT_Face. Use it when you create your + * hb_face_t using hb_face_create(). */ +HB_EXTERN void +hb_ft_font_set_funcs (hb_font_t *font); + + +HB_END_DECLS + +#endif /* HB_FT_H */ diff --git a/libs/harfbuzz/src/hb-gdi.cc b/libs/harfbuzz/src/hb-gdi.cc new file mode 100644 index 00000000..8e7589be --- /dev/null +++ b/libs/harfbuzz/src/hb-gdi.cc @@ -0,0 +1,85 @@ +/* + * Copyright © 2019 Ebrahim Byagowi + * + * 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. + */ + +#include "hb.hh" + +#ifdef HAVE_GDI + +#include "hb-gdi.h" + + +/** + * SECTION:hb-gdi + * @title: hb-gdi + * @short_description: GDI integration + * @include: hb-gdi.h + * + * Functions for using HarfBuzz with GDI fonts. + **/ + +static hb_blob_t * +_hb_gdi_reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data) +{ + char *buffer = nullptr; + DWORD length = 0; + + HDC hdc = GetDC (nullptr); + if (unlikely (!SelectObject (hdc, (HFONT) user_data))) goto fail; + + length = GetFontData (hdc, hb_uint32_swap (tag), 0, buffer, length); + if (unlikely (length == GDI_ERROR)) goto fail_with_releasedc; + + buffer = (char *) hb_malloc (length); + if (unlikely (!buffer)) goto fail_with_releasedc; + length = GetFontData (hdc, hb_uint32_swap (tag), 0, buffer, length); + if (unlikely (length == GDI_ERROR)) goto fail_with_releasedc_and_free; + ReleaseDC (nullptr, hdc); + + return hb_blob_create ((const char *) buffer, length, HB_MEMORY_MODE_WRITABLE, buffer, hb_free); + +fail_with_releasedc_and_free: + hb_free (buffer); +fail_with_releasedc: + ReleaseDC (nullptr, hdc); +fail: + return hb_blob_get_empty (); +} + +/** + * hb_gdi_face_create: + * @hfont: a HFONT object. + * + * Constructs a new face object from the specified GDI HFONT. + * + * Return value: #hb_face_t object corresponding to the given input + * + * Since: 2.6.0 + **/ +hb_face_t * +hb_gdi_face_create (HFONT hfont) +{ + return hb_face_create_for_tables (_hb_gdi_reference_table, (void *) hfont, nullptr); +} + +#endif diff --git a/libs/harfbuzz/src/hb-gdi.h b/libs/harfbuzz/src/hb-gdi.h new file mode 100644 index 00000000..68cc4391 --- /dev/null +++ b/libs/harfbuzz/src/hb-gdi.h @@ -0,0 +1,39 @@ +/* + * Copyright © 2019 Ebrahim Byagowi + * + * 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. + */ + +#ifndef HB_GDI_H +#define HB_GDI_H + +#include "hb.h" + +#include + +HB_BEGIN_DECLS + +HB_EXTERN hb_face_t * +hb_gdi_face_create (HFONT hfont); + +HB_END_DECLS + +#endif /* HB_GDI_H */ diff --git a/libs/harfbuzz/src/hb-glib.cc b/libs/harfbuzz/src/hb-glib.cc new file mode 100644 index 00000000..8ddc7eba --- /dev/null +++ b/libs/harfbuzz/src/hb-glib.cc @@ -0,0 +1,301 @@ +/* + * Copyright © 2009 Red Hat, Inc. + * 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. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#include "hb.hh" + +#ifdef HAVE_GLIB + +#include "hb-glib.h" + +#include "hb-machinery.hh" + + +/** + * SECTION:hb-glib + * @title: hb-glib + * @short_description: GLib integration + * @include: hb-glib.h + * + * Functions for using HarfBuzz with the GLib library. + * + * HarfBuzz supports using GLib to provide Unicode data, by attaching + * GLib functions to the virtual methods in a #hb_unicode_funcs_t function + * structure. + **/ + + +/** + * hb_glib_script_to_script: + * @script: The GUnicodeScript identifier to query + * + * Fetches the #hb_script_t script that corresponds to the + * specified GUnicodeScript identifier. + * + * Return value: the #hb_script_t script found + * + * Since: 0.9.38 + **/ +hb_script_t +hb_glib_script_to_script (GUnicodeScript script) +{ + return (hb_script_t) g_unicode_script_to_iso15924 (script); +} + +/** + * hb_glib_script_from_script: + * @script: The #hb_script_t to query + * + * Fetches the GUnicodeScript identifier that corresponds to the + * specified #hb_script_t script. + * + * Return value: the GUnicodeScript identifier found + * + * Since: 0.9.38 + **/ +GUnicodeScript +hb_glib_script_from_script (hb_script_t script) +{ + return g_unicode_script_from_iso15924 (script); +} + + +static hb_unicode_combining_class_t +hb_glib_unicode_combining_class (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t unicode, + void *user_data HB_UNUSED) + +{ + return (hb_unicode_combining_class_t) g_unichar_combining_class (unicode); +} + +static hb_unicode_general_category_t +hb_glib_unicode_general_category (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t unicode, + void *user_data HB_UNUSED) + +{ + /* hb_unicode_general_category_t and GUnicodeType are identical */ + return (hb_unicode_general_category_t) g_unichar_type (unicode); +} + +static hb_codepoint_t +hb_glib_unicode_mirroring (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t unicode, + void *user_data HB_UNUSED) +{ + g_unichar_get_mirror_char (unicode, &unicode); + return unicode; +} + +static hb_script_t +hb_glib_unicode_script (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t unicode, + void *user_data HB_UNUSED) +{ + return hb_glib_script_to_script (g_unichar_get_script (unicode)); +} + +static hb_bool_t +hb_glib_unicode_compose (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t a, + hb_codepoint_t b, + hb_codepoint_t *ab, + void *user_data HB_UNUSED) +{ +#if GLIB_CHECK_VERSION(2,29,12) + return g_unichar_compose (a, b, ab); +#endif + + /* We don't ifdef-out the fallback code such that compiler always + * sees it and makes sure it's compilable. */ + + gchar utf8[12]; + gchar *normalized; + int len; + hb_bool_t ret; + + len = g_unichar_to_utf8 (a, utf8); + len += g_unichar_to_utf8 (b, utf8 + len); + normalized = g_utf8_normalize (utf8, len, G_NORMALIZE_NFC); + len = g_utf8_strlen (normalized, -1); + if (unlikely (!len)) + return false; + + if (len == 1) { + *ab = g_utf8_get_char (normalized); + ret = true; + } else { + ret = false; + } + + g_free (normalized); + return ret; +} + +static hb_bool_t +hb_glib_unicode_decompose (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t ab, + hb_codepoint_t *a, + hb_codepoint_t *b, + void *user_data HB_UNUSED) +{ +#if GLIB_CHECK_VERSION(2,29,12) + return g_unichar_decompose (ab, a, b); +#endif + + /* We don't ifdef-out the fallback code such that compiler always + * sees it and makes sure it's compilable. */ + + gchar utf8[6]; + gchar *normalized; + int len; + hb_bool_t ret; + + len = g_unichar_to_utf8 (ab, utf8); + normalized = g_utf8_normalize (utf8, len, G_NORMALIZE_NFD); + len = g_utf8_strlen (normalized, -1); + if (unlikely (!len)) + return false; + + if (len == 1) { + *a = g_utf8_get_char (normalized); + *b = 0; + ret = *a != ab; + } else if (len == 2) { + *a = g_utf8_get_char (normalized); + *b = g_utf8_get_char (g_utf8_next_char (normalized)); + /* Here's the ugly part: if ab decomposes to a single character and + * that character decomposes again, we have to detect that and undo + * the second part :-(. */ + gchar *recomposed = g_utf8_normalize (normalized, -1, G_NORMALIZE_NFC); + hb_codepoint_t c = g_utf8_get_char (recomposed); + if (c != ab && c != *a) { + *a = c; + *b = 0; + } + g_free (recomposed); + ret = true; + } else { + /* If decomposed to more than two characters, take the last one, + * and recompose the rest to get the first component. */ + gchar *end = g_utf8_offset_to_pointer (normalized, len - 1); + gchar *recomposed; + *b = g_utf8_get_char (end); + recomposed = g_utf8_normalize (normalized, end - normalized, G_NORMALIZE_NFC); + /* We expect that recomposed has exactly one character now. */ + *a = g_utf8_get_char (recomposed); + g_free (recomposed); + ret = true; + } + + g_free (normalized); + return ret; +} + + +static inline void free_static_glib_funcs (); + +static struct hb_glib_unicode_funcs_lazy_loader_t : hb_unicode_funcs_lazy_loader_t +{ + static hb_unicode_funcs_t *create () + { + hb_unicode_funcs_t *funcs = hb_unicode_funcs_create (nullptr); + + hb_unicode_funcs_set_combining_class_func (funcs, hb_glib_unicode_combining_class, nullptr, nullptr); + hb_unicode_funcs_set_general_category_func (funcs, hb_glib_unicode_general_category, nullptr, nullptr); + hb_unicode_funcs_set_mirroring_func (funcs, hb_glib_unicode_mirroring, nullptr, nullptr); + hb_unicode_funcs_set_script_func (funcs, hb_glib_unicode_script, nullptr, nullptr); + hb_unicode_funcs_set_compose_func (funcs, hb_glib_unicode_compose, nullptr, nullptr); + hb_unicode_funcs_set_decompose_func (funcs, hb_glib_unicode_decompose, nullptr, nullptr); + + hb_unicode_funcs_make_immutable (funcs); + + hb_atexit (free_static_glib_funcs); + + return funcs; + } +} static_glib_funcs; + +static inline +void free_static_glib_funcs () +{ + static_glib_funcs.free_instance (); +} + +/** + * hb_glib_get_unicode_funcs: + * + * Fetches a Unicode-functions structure that is populated + * with the appropriate GLib function for each method. + * + * Return value: (transfer none): a pointer to the #hb_unicode_funcs_t Unicode-functions structure + * + * Since: 0.9.38 + **/ +hb_unicode_funcs_t * +hb_glib_get_unicode_funcs () +{ + return static_glib_funcs.get_unconst (); +} + + + +#if GLIB_CHECK_VERSION(2,31,10) + +static void +_hb_g_bytes_unref (void *data) +{ + g_bytes_unref ((GBytes *) data); +} + +/** + * hb_glib_blob_create: + * @gbytes: the GBytes structure to work upon + * + * Creates an #hb_blob_t blob from the specified + * GBytes data structure. + * + * Return value: (transfer full): the new #hb_blob_t blob object + * + * Since: 0.9.38 + **/ +hb_blob_t * +hb_glib_blob_create (GBytes *gbytes) +{ + gsize size = 0; + gconstpointer data = g_bytes_get_data (gbytes, &size); + return hb_blob_create ((const char *) data, + size, + HB_MEMORY_MODE_READONLY, + g_bytes_ref (gbytes), + _hb_g_bytes_unref); +} +#endif + + +#endif diff --git a/libs/harfbuzz/src/hb-glib.h b/libs/harfbuzz/src/hb-glib.h new file mode 100644 index 00000000..5f04183b --- /dev/null +++ b/libs/harfbuzz/src/hb-glib.h @@ -0,0 +1,56 @@ +/* + * Copyright © 2009 Red Hat, Inc. + * 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. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_GLIB_H +#define HB_GLIB_H + +#include "hb.h" + +#include + +HB_BEGIN_DECLS + + +HB_EXTERN hb_script_t +hb_glib_script_to_script (GUnicodeScript script); + +HB_EXTERN GUnicodeScript +hb_glib_script_from_script (hb_script_t script); + + +HB_EXTERN hb_unicode_funcs_t * +hb_glib_get_unicode_funcs (void); + +#if GLIB_CHECK_VERSION(2,31,10) +HB_EXTERN hb_blob_t * +hb_glib_blob_create (GBytes *gbytes); +#endif + +HB_END_DECLS + +#endif /* HB_GLIB_H */ diff --git a/libs/harfbuzz/src/hb-gobject-enums.cc.tmpl b/libs/harfbuzz/src/hb-gobject-enums.cc.tmpl new file mode 100644 index 00000000..87a11dd4 --- /dev/null +++ b/libs/harfbuzz/src/hb-gobject-enums.cc.tmpl @@ -0,0 +1,80 @@ +/*** BEGIN file-header ***/ +/* + * Copyright (C) 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 + */ + +#include "hb.hh" + +#ifdef HAVE_GOBJECT + +/* g++ didn't like older gtype.h gcc-only code path. */ +#include +#if !GLIB_CHECK_VERSION(2,29,16) +#undef __GNUC__ +#undef __GNUC_MINOR__ +#define __GNUC__ 2 +#define __GNUC_MINOR__ 6 +#endif + +#include "hb-gobject.h" + +/*** END file-header ***/ + +/*** BEGIN file-production ***/ +/* enumerations from "@basename@" */ +/*** END file-production ***/ + +/*** BEGIN file-tail ***/ + +#endif +/*** END file-tail ***/ + +/*** BEGIN value-header ***/ +GType +@enum_name@_get_type () +{ + static gsize type_id = 0; + + if (g_once_init_enter (&type_id)) + { + static const G@Type@Value values[] = { +/*** END value-header ***/ + +/*** BEGIN value-production ***/ + { @VALUENAME@, "@VALUENAME@", "@valuenick@" }, +/*** END value-production ***/ + +/*** BEGIN value-tail ***/ + { 0, NULL, NULL } + }; + GType id = + g_@type@_register_static (g_intern_static_string ("@EnumName@"), values); + g_once_init_leave (&type_id, id); + } + + return type_id; +} + +/*** END value-tail ***/ diff --git a/libs/harfbuzz/src/hb-gobject-enums.h.tmpl b/libs/harfbuzz/src/hb-gobject-enums.h.tmpl new file mode 100644 index 00000000..6dd98f7f --- /dev/null +++ b/libs/harfbuzz/src/hb-gobject-enums.h.tmpl @@ -0,0 +1,56 @@ +/*** BEGIN file-header ***/ +/* + * Copyright (C) 2013 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 + */ + +#if !defined(HB_GOBJECT_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR) +#error "Include instead." +#endif + +#ifndef HB_GOBJECT_ENUMS_H +#define HB_GOBJECT_ENUMS_H + +#include "hb.h" + +#include + +HB_BEGIN_DECLS + + +/*** END file-header ***/ + +/*** BEGIN value-header ***/ +HB_EXTERN GType +@enum_name@_get_type (void) G_GNUC_CONST; +#define @ENUMPREFIX@_TYPE_@ENUMSHORT@ (@enum_name@_get_type ()) + +/*** END value-header ***/ + +/*** BEGIN file-tail ***/ + +HB_END_DECLS + +#endif /* HB_GOBJECT_ENUMS_H */ +/*** END file-tail ***/ diff --git a/libs/harfbuzz/src/hb-gobject-structs.cc b/libs/harfbuzz/src/hb-gobject-structs.cc new file mode 100644 index 00000000..ef13f1e9 --- /dev/null +++ b/libs/harfbuzz/src/hb-gobject-structs.cc @@ -0,0 +1,111 @@ +/* + * 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 + */ + +#include "hb.hh" + +#ifdef HAVE_GOBJECT + + +/** + * SECTION:hb-gobject + * @title: hb-gobject + * @short_description: GObject integration support + * @include: hb-gobject.h + * + * Support for using HarfBuzz with the GObject library to provide + * type data. + * + * The types and functions listed here are solely a linkage between + * HarfBuzz's public data types and the GTypes used by the GObject framework. + * HarfBuzz uses GObject introspection to generate its Python bindings + * (and potentially other language bindings); client programs should never need + * to access the GObject-integration mechanics. + * + * For client programs using the GNOME and GTK software stack, please see the + * GLib and FreeType integration pages. + **/ + + +/* g++ didn't like older gtype.h gcc-only code path. */ +#include +#if !GLIB_CHECK_VERSION(2,29,16) +#undef __GNUC__ +#undef __GNUC_MINOR__ +#define __GNUC__ 2 +#define __GNUC_MINOR__ 6 +#endif + +#include "hb-gobject.h" + +#define HB_DEFINE_BOXED_TYPE(name,copy_func,free_func) \ +GType \ +hb_gobject_##name##_get_type () \ +{ \ + static gsize type_id = 0; \ + if (g_once_init_enter (&type_id)) { \ + GType id = g_boxed_type_register_static (g_intern_static_string ("hb_" #name "_t"), \ + (GBoxedCopyFunc) copy_func, \ + (GBoxedFreeFunc) free_func); \ + g_once_init_leave (&type_id, id); \ + } \ + return type_id; \ +} + +#define HB_DEFINE_OBJECT_TYPE(name) \ + HB_DEFINE_BOXED_TYPE (name, hb_##name##_reference, hb_##name##_destroy) + +#define HB_DEFINE_VALUE_TYPE(name) \ + static hb_##name##_t *_hb_##name##_reference (const hb_##name##_t *l) \ + { \ + hb_##name##_t *c = (hb_##name##_t *) hb_calloc (1, sizeof (hb_##name##_t)); \ + if (unlikely (!c)) return nullptr; \ + *c = *l; \ + return c; \ + } \ + static void _hb_##name##_destroy (hb_##name##_t *l) { hb_free (l); } \ + HB_DEFINE_BOXED_TYPE (name, _hb_##name##_reference, _hb_##name##_destroy) + +HB_DEFINE_OBJECT_TYPE (buffer) +HB_DEFINE_OBJECT_TYPE (blob) +HB_DEFINE_OBJECT_TYPE (draw_funcs) +HB_DEFINE_OBJECT_TYPE (face) +HB_DEFINE_OBJECT_TYPE (font) +HB_DEFINE_OBJECT_TYPE (font_funcs) +HB_DEFINE_OBJECT_TYPE (set) +HB_DEFINE_OBJECT_TYPE (map) +HB_DEFINE_OBJECT_TYPE (shape_plan) +HB_DEFINE_OBJECT_TYPE (unicode_funcs) +HB_DEFINE_VALUE_TYPE (feature) +HB_DEFINE_VALUE_TYPE (glyph_info) +HB_DEFINE_VALUE_TYPE (glyph_position) +HB_DEFINE_VALUE_TYPE (segment_properties) +HB_DEFINE_VALUE_TYPE (user_data_key) + +HB_DEFINE_VALUE_TYPE (ot_math_glyph_variant) +HB_DEFINE_VALUE_TYPE (ot_math_glyph_part) + + +#endif diff --git a/libs/harfbuzz/src/hb-gobject-structs.h b/libs/harfbuzz/src/hb-gobject-structs.h new file mode 100644 index 00000000..3914a243 --- /dev/null +++ b/libs/harfbuzz/src/hb-gobject-structs.h @@ -0,0 +1,116 @@ +/* + * Copyright (C) 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 + */ + +#if !defined(HB_GOBJECT_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR) +#error "Include instead." +#endif + +#ifndef HB_GOBJECT_STRUCTS_H +#define HB_GOBJECT_STRUCTS_H + +#include "hb.h" + +#include + +HB_BEGIN_DECLS + + +/* Object types */ + +HB_EXTERN GType +hb_gobject_blob_get_type (void); +#define HB_GOBJECT_TYPE_BLOB (hb_gobject_blob_get_type ()) + +HB_EXTERN GType +hb_gobject_buffer_get_type (void); +#define HB_GOBJECT_TYPE_BUFFER (hb_gobject_buffer_get_type ()) + +HB_EXTERN GType +hb_gobject_draw_funcs_get_type (void); +#define HB_GOBJECT_TYPE_DRAW_FUNCS (hb_gobject_draw_funcs_get_type ()) + +HB_EXTERN GType +hb_gobject_face_get_type (void); +#define HB_GOBJECT_TYPE_FACE (hb_gobject_face_get_type ()) + +HB_EXTERN GType +hb_gobject_font_get_type (void); +#define HB_GOBJECT_TYPE_FONT (hb_gobject_font_get_type ()) + +HB_EXTERN GType +hb_gobject_font_funcs_get_type (void); +#define HB_GOBJECT_TYPE_FONT_FUNCS (hb_gobject_font_funcs_get_type ()) + +HB_EXTERN GType +hb_gobject_set_get_type (void); +#define HB_GOBJECT_TYPE_SET (hb_gobject_set_get_type ()) + +HB_EXTERN GType +hb_gobject_map_get_type (void); +#define HB_GOBJECT_TYPE_MAP (hb_gobject_map_get_type ()) + +HB_EXTERN GType +hb_gobject_shape_plan_get_type (void); +#define HB_GOBJECT_TYPE_SHAPE_PLAN (hb_gobject_shape_plan_get_type ()) + +HB_EXTERN GType +hb_gobject_unicode_funcs_get_type (void); +#define HB_GOBJECT_TYPE_UNICODE_FUNCS (hb_gobject_unicode_funcs_get_type ()) + +/* Value types */ + +HB_EXTERN GType +hb_gobject_feature_get_type (void); +#define HB_GOBJECT_TYPE_FEATURE (hb_gobject_feature_get_type ()) + +HB_EXTERN GType +hb_gobject_glyph_info_get_type (void); +#define HB_GOBJECT_TYPE_GLYPH_INFO (hb_gobject_glyph_info_get_type ()) + +HB_EXTERN GType +hb_gobject_glyph_position_get_type (void); +#define HB_GOBJECT_TYPE_GLYPH_POSITION (hb_gobject_glyph_position_get_type ()) + +HB_EXTERN GType +hb_gobject_segment_properties_get_type (void); +#define HB_GOBJECT_TYPE_SEGMENT_PROPERTIES (hb_gobject_segment_properties_get_type ()) + +HB_EXTERN GType +hb_gobject_user_data_key_get_type (void); +#define HB_GOBJECT_TYPE_USER_DATA_KEY (hb_gobject_user_data_key_get_type ()) + +HB_EXTERN GType +hb_gobject_ot_math_glyph_variant_get_type (void); +#define HB_GOBJECT_TYPE_OT_MATH_GLYPH_VARIANT (hb_gobject_ot_math_glyph_variant_get_type ()) + +HB_EXTERN GType +hb_gobject_ot_math_glyph_part_get_type (void); +#define HB_GOBJECT_TYPE_OT_MATH_GLYPH_PART (hb_gobject_ot_math_glyph_part_get_type ()) + + +HB_END_DECLS + +#endif /* HB_GOBJECT_H */ diff --git a/libs/harfbuzz/src/hb-gobject.h b/libs/harfbuzz/src/hb-gobject.h new file mode 100644 index 00000000..8891aa0e --- /dev/null +++ b/libs/harfbuzz/src/hb-gobject.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 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 HB_GOBJECT_H +#define HB_GOBJECT_H +#define HB_GOBJECT_H_IN + +#include "hb.h" + +#include "hb-gobject-enums.h" +#include "hb-gobject-structs.h" + +HB_BEGIN_DECLS +HB_END_DECLS + +#undef HB_GOBJECT_H_IN +#endif /* HB_GOBJECT_H */ diff --git a/libs/harfbuzz/src/hb-graphite2.cc b/libs/harfbuzz/src/hb-graphite2.cc new file mode 100644 index 00000000..4d0e687c --- /dev/null +++ b/libs/harfbuzz/src/hb-graphite2.cc @@ -0,0 +1,449 @@ +/* + * Copyright © 2011 Martin Hosken + * Copyright © 2011 SIL International + * Copyright © 2011,2012 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 + */ + +#include "hb.hh" + +#ifdef HAVE_GRAPHITE2 + +#include "hb-shaper-impl.hh" + +#include "hb-graphite2.h" + +#include + +#include "hb-ot-layout.h" + + +/** + * SECTION:hb-graphite2 + * @title: hb-graphite2 + * @short_description: Graphite2 integration + * @include: hb-graphite2.h + * + * Functions for using HarfBuzz with fonts that include Graphite features. + * + * For Graphite features to work, you must be sure that HarfBuzz was compiled + * with the `graphite2` shaping engine enabled. Currently, the default is to + * not enable `graphite2` shaping. + **/ + + +/* + * shaper face data + */ + +typedef struct hb_graphite2_tablelist_t +{ + struct hb_graphite2_tablelist_t *next; + hb_blob_t *blob; + unsigned int tag; +} hb_graphite2_tablelist_t; + +struct hb_graphite2_face_data_t +{ + hb_face_t *face; + gr_face *grface; + hb_atomic_ptr_t tlist; +}; + +static const void *hb_graphite2_get_table (const void *data, unsigned int tag, size_t *len) +{ + hb_graphite2_face_data_t *face_data = (hb_graphite2_face_data_t *) data; + hb_graphite2_tablelist_t *tlist = face_data->tlist; + + hb_blob_t *blob = nullptr; + + for (hb_graphite2_tablelist_t *p = tlist; p; p = p->next) + if (p->tag == tag) { + blob = p->blob; + break; + } + + if (unlikely (!blob)) + { + blob = face_data->face->reference_table (tag); + + hb_graphite2_tablelist_t *p = (hb_graphite2_tablelist_t *) hb_calloc (1, sizeof (hb_graphite2_tablelist_t)); + if (unlikely (!p)) { + hb_blob_destroy (blob); + return nullptr; + } + p->blob = blob; + p->tag = tag; + +retry: + hb_graphite2_tablelist_t *tlist = face_data->tlist; + p->next = tlist; + + if (unlikely (!face_data->tlist.cmpexch (tlist, p))) + goto retry; + } + + unsigned int tlen; + const char *d = hb_blob_get_data (blob, &tlen); + *len = tlen; + return d; +} + +hb_graphite2_face_data_t * +_hb_graphite2_shaper_face_data_create (hb_face_t *face) +{ + hb_blob_t *silf_blob = face->reference_table (HB_GRAPHITE2_TAG_SILF); + /* Umm, we just reference the table to check whether it exists. + * Maybe add better API for this? */ + if (!hb_blob_get_length (silf_blob)) + { + hb_blob_destroy (silf_blob); + return nullptr; + } + hb_blob_destroy (silf_blob); + + hb_graphite2_face_data_t *data = (hb_graphite2_face_data_t *) hb_calloc (1, sizeof (hb_graphite2_face_data_t)); + if (unlikely (!data)) + return nullptr; + + data->face = face; + const gr_face_ops ops = {sizeof(gr_face_ops), &hb_graphite2_get_table, NULL}; + data->grface = gr_make_face_with_ops (data, &ops, gr_face_preloadAll); + + if (unlikely (!data->grface)) { + hb_free (data); + return nullptr; + } + + return data; +} + +void +_hb_graphite2_shaper_face_data_destroy (hb_graphite2_face_data_t *data) +{ + hb_graphite2_tablelist_t *tlist = data->tlist; + + while (tlist) + { + hb_graphite2_tablelist_t *old = tlist; + hb_blob_destroy (tlist->blob); + tlist = tlist->next; + hb_free (old); + } + + gr_face_destroy (data->grface); + + hb_free (data); +} + +/** + * hb_graphite2_face_get_gr_face: (skip) + * @face: @hb_face_t to query + * + * Fetches the Graphite2 gr_face corresponding to the specified + * #hb_face_t face object. + * + * Return value: the gr_face found + * + * Since: 0.9.10 + */ +gr_face * +hb_graphite2_face_get_gr_face (hb_face_t *face) +{ + const hb_graphite2_face_data_t *data = face->data.graphite2; + return data ? data->grface : nullptr; +} + + +/* + * shaper font data + */ + +struct hb_graphite2_font_data_t {}; + +hb_graphite2_font_data_t * +_hb_graphite2_shaper_font_data_create (hb_font_t *font HB_UNUSED) +{ + return (hb_graphite2_font_data_t *) HB_SHAPER_DATA_SUCCEEDED; +} + +void +_hb_graphite2_shaper_font_data_destroy (hb_graphite2_font_data_t *data HB_UNUSED) +{ +} + +#ifndef HB_DISABLE_DEPRECATED +/** + * hb_graphite2_font_get_gr_font: (skip) + * @font: An #hb_font_t + * + * Always returns `NULL`. Use hb_graphite2_face_get_gr_face() instead. + * + * Return value: (nullable): Graphite2 font associated with @font. + * + * Since: 0.9.10 + * Deprecated: 1.4.2 + */ +gr_font * +hb_graphite2_font_get_gr_font (hb_font_t *font HB_UNUSED) +{ + return nullptr; +} +#endif + + +/* + * shaper + */ + +struct hb_graphite2_cluster_t { + unsigned int base_char; + unsigned int num_chars; + unsigned int base_glyph; + unsigned int num_glyphs; + unsigned int cluster; + int advance; +}; + +hb_bool_t +_hb_graphite2_shape (hb_shape_plan_t *shape_plan HB_UNUSED, + hb_font_t *font, + hb_buffer_t *buffer, + const hb_feature_t *features, + unsigned int num_features) +{ + hb_face_t *face = font->face; + gr_face *grface = face->data.graphite2->grface; + + const char *lang = hb_language_to_string (hb_buffer_get_language (buffer)); + const char *lang_end = lang ? strchr (lang, '-') : nullptr; + int lang_len = lang_end ? lang_end - lang : -1; + gr_feature_val *feats = gr_face_featureval_for_lang (grface, lang ? hb_tag_from_string (lang, lang_len) : 0); + + for (unsigned int i = 0; i < num_features; i++) + { + const gr_feature_ref *fref = gr_face_find_fref (grface, features[i].tag); + if (fref) + gr_fref_set_feature_value (fref, features[i].value, feats); + } + + gr_segment *seg = nullptr; + const gr_slot *is; + unsigned int ci = 0, ic = 0; + unsigned int curradvx = 0, curradvy = 0; + + unsigned int scratch_size; + hb_buffer_t::scratch_buffer_t *scratch = buffer->get_scratch_buffer (&scratch_size); + + uint32_t *chars = (uint32_t *) scratch; + + for (unsigned int i = 0; i < buffer->len; ++i) + chars[i] = buffer->info[i].codepoint; + + /* TODO ensure_native_direction. */ + + hb_tag_t script_tag[HB_OT_MAX_TAGS_PER_SCRIPT]; + unsigned int count = HB_OT_MAX_TAGS_PER_SCRIPT; + hb_ot_tags_from_script_and_language (hb_buffer_get_script (buffer), + HB_LANGUAGE_INVALID, + &count, + script_tag, + nullptr, nullptr); + + seg = gr_make_seg (nullptr, grface, + count ? script_tag[count - 1] : HB_OT_TAG_DEFAULT_SCRIPT, + feats, + gr_utf32, chars, buffer->len, + 2 | (hb_buffer_get_direction (buffer) == HB_DIRECTION_RTL ? 1 : 0)); + + if (unlikely (!seg)) { + if (feats) gr_featureval_destroy (feats); + return false; + } + + unsigned int glyph_count = gr_seg_n_slots (seg); + if (unlikely (!glyph_count)) { + if (feats) gr_featureval_destroy (feats); + gr_seg_destroy (seg); + buffer->len = 0; + return true; + } + + (void) buffer->ensure (glyph_count); + scratch = buffer->get_scratch_buffer (&scratch_size); + while ((DIV_CEIL (sizeof (hb_graphite2_cluster_t) * buffer->len, sizeof (*scratch)) + + DIV_CEIL (sizeof (hb_codepoint_t) * glyph_count, sizeof (*scratch))) > scratch_size) + { + if (unlikely (!buffer->ensure (buffer->allocated * 2))) + { + if (feats) gr_featureval_destroy (feats); + gr_seg_destroy (seg); + return false; + } + scratch = buffer->get_scratch_buffer (&scratch_size); + } + +#define ALLOCATE_ARRAY(Type, name, len) \ + Type *name = (Type *) scratch; \ + do { \ + unsigned int _consumed = DIV_CEIL ((len) * sizeof (Type), sizeof (*scratch)); \ + assert (_consumed <= scratch_size); \ + scratch += _consumed; \ + scratch_size -= _consumed; \ + } while (0) + + ALLOCATE_ARRAY (hb_graphite2_cluster_t, clusters, buffer->len); + ALLOCATE_ARRAY (hb_codepoint_t, gids, glyph_count); + +#undef ALLOCATE_ARRAY + + memset (clusters, 0, sizeof (clusters[0]) * buffer->len); + + hb_codepoint_t *pg = gids; + clusters[0].cluster = buffer->info[0].cluster; + unsigned int upem = hb_face_get_upem (face); + float xscale = (float) font->x_scale / upem; + float yscale = (float) font->y_scale / upem; + yscale *= yscale / xscale; + unsigned int curradv = 0; + if (HB_DIRECTION_IS_BACKWARD(buffer->props.direction)) + { + curradv = gr_slot_origin_X(gr_seg_first_slot(seg)) * xscale; + clusters[0].advance = gr_seg_advance_X(seg) * xscale - curradv; + } + else + clusters[0].advance = 0; + for (is = gr_seg_first_slot (seg), ic = 0; is; is = gr_slot_next_in_segment (is), ic++) + { + unsigned int before = gr_slot_before (is); + unsigned int after = gr_slot_after (is); + *pg = gr_slot_gid (is); + pg++; + while (clusters[ci].base_char > before && ci) + { + clusters[ci-1].num_chars += clusters[ci].num_chars; + clusters[ci-1].num_glyphs += clusters[ci].num_glyphs; + clusters[ci-1].advance += clusters[ci].advance; + ci--; + } + + if (gr_slot_can_insert_before (is) && clusters[ci].num_chars && before >= clusters[ci].base_char + clusters[ci].num_chars) + { + hb_graphite2_cluster_t *c = clusters + ci + 1; + c->base_char = clusters[ci].base_char + clusters[ci].num_chars; + c->cluster = buffer->info[c->base_char].cluster; + c->num_chars = before - c->base_char; + c->base_glyph = ic; + c->num_glyphs = 0; + if (HB_DIRECTION_IS_BACKWARD(buffer->props.direction)) + { + c->advance = curradv - gr_slot_origin_X(is) * xscale; + curradv -= c->advance; + } + else + { + c->advance = 0; + clusters[ci].advance += gr_slot_origin_X(is) * xscale - curradv; + curradv += clusters[ci].advance; + } + ci++; + } + clusters[ci].num_glyphs++; + + if (clusters[ci].base_char + clusters[ci].num_chars < after + 1) + clusters[ci].num_chars = after + 1 - clusters[ci].base_char; + } + + if (HB_DIRECTION_IS_BACKWARD(buffer->props.direction)) + clusters[ci].advance += curradv; + else + clusters[ci].advance += gr_seg_advance_X(seg) * xscale - curradv; + ci++; + + for (unsigned int i = 0; i < ci; ++i) + { + for (unsigned int j = 0; j < clusters[i].num_glyphs; ++j) + { + hb_glyph_info_t *info = &buffer->info[clusters[i].base_glyph + j]; + info->codepoint = gids[clusters[i].base_glyph + j]; + info->cluster = clusters[i].cluster; + info->var1.i32 = clusters[i].advance; // all glyphs in the cluster get the same advance + } + } + buffer->len = glyph_count; + + /* Positioning. */ + unsigned int currclus = UINT_MAX; + const hb_glyph_info_t *info = buffer->info; + hb_glyph_position_t *pPos = hb_buffer_get_glyph_positions (buffer, nullptr); + if (!HB_DIRECTION_IS_BACKWARD(buffer->props.direction)) + { + curradvx = 0; + for (is = gr_seg_first_slot (seg); is; pPos++, ++info, is = gr_slot_next_in_segment (is)) + { + pPos->x_offset = gr_slot_origin_X (is) * xscale - curradvx; + pPos->y_offset = gr_slot_origin_Y (is) * yscale - curradvy; + if (info->cluster != currclus) { + pPos->x_advance = info->var1.i32; + curradvx += pPos->x_advance; + currclus = info->cluster; + } else + pPos->x_advance = 0.; + + pPos->y_advance = gr_slot_advance_Y (is, grface, nullptr) * yscale; + curradvy += pPos->y_advance; + } + } + else + { + curradvx = gr_seg_advance_X(seg) * xscale; + for (is = gr_seg_first_slot (seg); is; pPos++, info++, is = gr_slot_next_in_segment (is)) + { + if (info->cluster != currclus) + { + pPos->x_advance = info->var1.i32; + curradvx -= pPos->x_advance; + currclus = info->cluster; + } else + pPos->x_advance = 0.; + + pPos->y_advance = gr_slot_advance_Y (is, grface, nullptr) * yscale; + curradvy -= pPos->y_advance; + pPos->x_offset = gr_slot_origin_X (is) * xscale - info->var1.i32 - curradvx + pPos->x_advance; + pPos->y_offset = gr_slot_origin_Y (is) * yscale - curradvy; + } + hb_buffer_reverse_clusters (buffer); + } + + if (feats) gr_featureval_destroy (feats); + gr_seg_destroy (seg); + + buffer->clear_glyph_flags (); + buffer->unsafe_to_break (); + + return true; +} + + +#endif diff --git a/libs/harfbuzz/src/hb-graphite2.h b/libs/harfbuzz/src/hb-graphite2.h new file mode 100644 index 00000000..f299da9f --- /dev/null +++ b/libs/harfbuzz/src/hb-graphite2.h @@ -0,0 +1,60 @@ +/* + * Copyright © 2011 Martin Hosken + * Copyright © 2011 SIL International + * + * 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. + */ + +#ifndef HB_GRAPHITE2_H +#define HB_GRAPHITE2_H + +#include "hb.h" + +#include + +HB_BEGIN_DECLS + +/** + * HB_GRAPHITE2_TAG_SILF: + * + * The #hb_tag_t tag for the `Silf` table, which holds Graphite + * features. + * + * For more information, see http://graphite.sil.org/ + * + **/ +#define HB_GRAPHITE2_TAG_SILF HB_TAG('S','i','l','f') + + +HB_EXTERN gr_face * +hb_graphite2_face_get_gr_face (hb_face_t *face); + +#ifndef HB_DISABLE_DEPRECATED + +HB_EXTERN HB_DEPRECATED_FOR (hb_graphite2_face_get_gr_face) gr_font * +hb_graphite2_font_get_gr_font (hb_font_t *font); + +#endif + + +HB_END_DECLS + +#endif /* HB_GRAPHITE2_H */ diff --git a/libs/harfbuzz/src/hb-icu.cc b/libs/harfbuzz/src/hb-icu.cc new file mode 100644 index 00000000..e46401f7 --- /dev/null +++ b/libs/harfbuzz/src/hb-icu.cc @@ -0,0 +1,290 @@ +/* + * Copyright © 2009 Red Hat, Inc. + * Copyright © 2009 Keith Stribley + * 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. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#include "hb.hh" + +#ifdef HAVE_ICU + +#include "hb-icu.h" + +#include "hb-machinery.hh" + +#include +#include +#include +#include +#include + +/* ICU extra semicolon, fixed since 65, https://github.com/unicode-org/icu/commit/480bec3 */ +#if U_ICU_VERSION_MAJOR_NUM < 65 && (defined(__GNUC__) || defined(__clang__)) +#define HB_ICU_EXTRA_SEMI_IGNORED +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wextra-semi-stmt" +#endif + +/** + * SECTION:hb-icu + * @title: hb-icu + * @short_description: ICU integration + * @include: hb-icu.h + * + * Functions for using HarfBuzz with the International Components for Unicode + * (ICU) library. HarfBuzz supports using ICU to provide Unicode data, by attaching + * ICU functions to the virtual methods in a #hb_unicode_funcs_t function + * structure. + **/ + +/** + * hb_icu_script_to_script: + * @script: The UScriptCode identifier to query + * + * Fetches the #hb_script_t script that corresponds to the + * specified UScriptCode identifier. + * + * Return value: the #hb_script_t script found + * + **/ + +hb_script_t +hb_icu_script_to_script (UScriptCode script) +{ + if (unlikely (script == USCRIPT_INVALID_CODE)) + return HB_SCRIPT_INVALID; + + return hb_script_from_string (uscript_getShortName (script), -1); +} + +/** + * hb_icu_script_from_script: + * @script: The #hb_script_t script to query + * + * Fetches the UScriptCode identifier that corresponds to the + * specified #hb_script_t script. + * + * Return value: the UScriptCode identifier found + * + **/ +UScriptCode +hb_icu_script_from_script (hb_script_t script) +{ + if (unlikely (script == HB_SCRIPT_INVALID)) + return USCRIPT_INVALID_CODE; + + unsigned int numScriptCode = 1 + u_getIntPropertyMaxValue (UCHAR_SCRIPT); + for (unsigned int i = 0; i < numScriptCode; i++) + if (unlikely (hb_icu_script_to_script ((UScriptCode) i) == script)) + return (UScriptCode) i; + + return USCRIPT_UNKNOWN; +} + + +static hb_unicode_combining_class_t +hb_icu_unicode_combining_class (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t unicode, + void *user_data HB_UNUSED) + +{ + return (hb_unicode_combining_class_t) u_getCombiningClass (unicode); +} + +static hb_unicode_general_category_t +hb_icu_unicode_general_category (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t unicode, + void *user_data HB_UNUSED) +{ + switch (u_getIntPropertyValue(unicode, UCHAR_GENERAL_CATEGORY)) + { + case U_UNASSIGNED: return HB_UNICODE_GENERAL_CATEGORY_UNASSIGNED; + + case U_UPPERCASE_LETTER: return HB_UNICODE_GENERAL_CATEGORY_UPPERCASE_LETTER; + case U_LOWERCASE_LETTER: return HB_UNICODE_GENERAL_CATEGORY_LOWERCASE_LETTER; + case U_TITLECASE_LETTER: return HB_UNICODE_GENERAL_CATEGORY_TITLECASE_LETTER; + case U_MODIFIER_LETTER: return HB_UNICODE_GENERAL_CATEGORY_MODIFIER_LETTER; + case U_OTHER_LETTER: return HB_UNICODE_GENERAL_CATEGORY_OTHER_LETTER; + + case U_NON_SPACING_MARK: return HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK; + case U_ENCLOSING_MARK: return HB_UNICODE_GENERAL_CATEGORY_ENCLOSING_MARK; + case U_COMBINING_SPACING_MARK: return HB_UNICODE_GENERAL_CATEGORY_SPACING_MARK; + + case U_DECIMAL_DIGIT_NUMBER: return HB_UNICODE_GENERAL_CATEGORY_DECIMAL_NUMBER; + case U_LETTER_NUMBER: return HB_UNICODE_GENERAL_CATEGORY_LETTER_NUMBER; + case U_OTHER_NUMBER: return HB_UNICODE_GENERAL_CATEGORY_OTHER_NUMBER; + + case U_SPACE_SEPARATOR: return HB_UNICODE_GENERAL_CATEGORY_SPACE_SEPARATOR; + case U_LINE_SEPARATOR: return HB_UNICODE_GENERAL_CATEGORY_LINE_SEPARATOR; + case U_PARAGRAPH_SEPARATOR: return HB_UNICODE_GENERAL_CATEGORY_PARAGRAPH_SEPARATOR; + + case U_CONTROL_CHAR: return HB_UNICODE_GENERAL_CATEGORY_CONTROL; + case U_FORMAT_CHAR: return HB_UNICODE_GENERAL_CATEGORY_FORMAT; + case U_PRIVATE_USE_CHAR: return HB_UNICODE_GENERAL_CATEGORY_PRIVATE_USE; + case U_SURROGATE: return HB_UNICODE_GENERAL_CATEGORY_SURROGATE; + + + case U_DASH_PUNCTUATION: return HB_UNICODE_GENERAL_CATEGORY_DASH_PUNCTUATION; + case U_START_PUNCTUATION: return HB_UNICODE_GENERAL_CATEGORY_OPEN_PUNCTUATION; + case U_END_PUNCTUATION: return HB_UNICODE_GENERAL_CATEGORY_CLOSE_PUNCTUATION; + case U_CONNECTOR_PUNCTUATION: return HB_UNICODE_GENERAL_CATEGORY_CONNECT_PUNCTUATION; + case U_OTHER_PUNCTUATION: return HB_UNICODE_GENERAL_CATEGORY_OTHER_PUNCTUATION; + + case U_MATH_SYMBOL: return HB_UNICODE_GENERAL_CATEGORY_MATH_SYMBOL; + case U_CURRENCY_SYMBOL: return HB_UNICODE_GENERAL_CATEGORY_CURRENCY_SYMBOL; + case U_MODIFIER_SYMBOL: return HB_UNICODE_GENERAL_CATEGORY_MODIFIER_SYMBOL; + case U_OTHER_SYMBOL: return HB_UNICODE_GENERAL_CATEGORY_OTHER_SYMBOL; + + case U_INITIAL_PUNCTUATION: return HB_UNICODE_GENERAL_CATEGORY_INITIAL_PUNCTUATION; + case U_FINAL_PUNCTUATION: return HB_UNICODE_GENERAL_CATEGORY_FINAL_PUNCTUATION; + } + + return HB_UNICODE_GENERAL_CATEGORY_UNASSIGNED; +} + +static hb_codepoint_t +hb_icu_unicode_mirroring (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t unicode, + void *user_data HB_UNUSED) +{ + return u_charMirror(unicode); +} + +static hb_script_t +hb_icu_unicode_script (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t unicode, + void *user_data HB_UNUSED) +{ + UErrorCode status = U_ZERO_ERROR; + UScriptCode scriptCode = uscript_getScript(unicode, &status); + + if (unlikely (U_FAILURE (status))) + return HB_SCRIPT_UNKNOWN; + + return hb_icu_script_to_script (scriptCode); +} + +static hb_bool_t +hb_icu_unicode_compose (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t a, + hb_codepoint_t b, + hb_codepoint_t *ab, + void *user_data) +{ + const UNormalizer2 *normalizer = (const UNormalizer2 *) user_data; + UChar32 ret = unorm2_composePair (normalizer, a, b); + if (ret < 0) return false; + *ab = ret; + return true; +} + +static hb_bool_t +hb_icu_unicode_decompose (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t ab, + hb_codepoint_t *a, + hb_codepoint_t *b, + void *user_data) +{ + const UNormalizer2 *normalizer = (const UNormalizer2 *) user_data; + UChar decomposed[4]; + int len; + UErrorCode icu_err = U_ZERO_ERROR; + len = unorm2_getRawDecomposition (normalizer, ab, decomposed, + ARRAY_LENGTH (decomposed), &icu_err); + if (U_FAILURE (icu_err) || len < 0) return false; + + len = u_countChar32 (decomposed, len); + if (len == 1) + { + U16_GET_UNSAFE (decomposed, 0, *a); + *b = 0; + return *a != ab; + } + else if (len == 2) + { + len = 0; + U16_NEXT_UNSAFE (decomposed, len, *a); + U16_NEXT_UNSAFE (decomposed, len, *b); + } + return true; +} + + +static inline void free_static_icu_funcs (); + +static struct hb_icu_unicode_funcs_lazy_loader_t : hb_unicode_funcs_lazy_loader_t +{ + static hb_unicode_funcs_t *create () + { + void *user_data = nullptr; + UErrorCode icu_err = U_ZERO_ERROR; + user_data = (void *) unorm2_getNFCInstance (&icu_err); + assert (user_data); + + hb_unicode_funcs_t *funcs = hb_unicode_funcs_create (nullptr); + + hb_unicode_funcs_set_combining_class_func (funcs, hb_icu_unicode_combining_class, nullptr, nullptr); + hb_unicode_funcs_set_general_category_func (funcs, hb_icu_unicode_general_category, nullptr, nullptr); + hb_unicode_funcs_set_mirroring_func (funcs, hb_icu_unicode_mirroring, nullptr, nullptr); + hb_unicode_funcs_set_script_func (funcs, hb_icu_unicode_script, nullptr, nullptr); + hb_unicode_funcs_set_compose_func (funcs, hb_icu_unicode_compose, user_data, nullptr); + hb_unicode_funcs_set_decompose_func (funcs, hb_icu_unicode_decompose, user_data, nullptr); + + hb_unicode_funcs_make_immutable (funcs); + + hb_atexit (free_static_icu_funcs); + + return funcs; + } +} static_icu_funcs; + +static inline +void free_static_icu_funcs () +{ + static_icu_funcs.free_instance (); +} + +/** + * hb_icu_get_unicode_funcs: + * + * Fetches a Unicode-functions structure that is populated + * with the appropriate ICU function for each method. + * + * Return value: (transfer none): a pointer to the #hb_unicode_funcs_t Unicode-functions structure + * + * Since: 0.9.38 + **/ +hb_unicode_funcs_t * +hb_icu_get_unicode_funcs () +{ + return static_icu_funcs.get_unconst (); +} + +#ifdef HB_ICU_EXTRA_SEMI_IGNORED +#pragma GCC diagnostic pop +#endif + +#endif diff --git a/libs/harfbuzz/src/hb-icu.h b/libs/harfbuzz/src/hb-icu.h new file mode 100644 index 00000000..2db6a7b6 --- /dev/null +++ b/libs/harfbuzz/src/hb-icu.h @@ -0,0 +1,52 @@ +/* + * Copyright © 2009 Red Hat, Inc. + * 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. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_ICU_H +#define HB_ICU_H + +#include "hb.h" + +#include + +HB_BEGIN_DECLS + + +HB_EXTERN hb_script_t +hb_icu_script_to_script (UScriptCode script); + +HB_EXTERN UScriptCode +hb_icu_script_from_script (hb_script_t script); + + +HB_EXTERN hb_unicode_funcs_t * +hb_icu_get_unicode_funcs (void); + + +HB_END_DECLS + +#endif /* HB_ICU_H */ diff --git a/libs/harfbuzz/src/hb-iter.hh b/libs/harfbuzz/src/hb-iter.hh new file mode 100644 index 00000000..1a3ab43d --- /dev/null +++ b/libs/harfbuzz/src/hb-iter.hh @@ -0,0 +1,1016 @@ +/* + * Copyright © 2018 Google, Inc. + * Copyright © 2019 Facebook, 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 + * Facebook Author(s): Behdad Esfahbod + */ + +#ifndef HB_ITER_HH +#define HB_ITER_HH + +#include "hb.hh" +#include "hb-algs.hh" +#include "hb-meta.hh" + + +/* Unified iterator object. + * + * The goal of this template is to make the same iterator interface + * available to all types, and make it very easy and compact to use. + * hb_iter_tator objects are small, light-weight, objects that can be + * copied by value. If the collection / object being iterated on + * is writable, then the iterator returns lvalues, otherwise it + * returns rvalues. + * + * If iterator implementation implements operator!=, then it can be + * used in range-based for loop. That already happens if the iterator + * is random-access. Otherwise, the range-based for loop incurs + * one traversal to find end(), which can be avoided if written + * as a while-style for loop, or if iterator implements a faster + * __end__() method. */ + +/* + * Base classes for iterators. + */ + +/* Base class for all iterators. */ +template +struct hb_iter_t +{ + typedef Item item_t; + constexpr unsigned get_item_size () const { return hb_static_size (Item); } + static constexpr bool is_iterator = true; + static constexpr bool is_random_access_iterator = false; + static constexpr bool is_sorted_iterator = false; + + private: + /* https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern */ + const iter_t* thiz () const { return static_cast (this); } + iter_t* thiz () { return static_cast< iter_t *> (this); } + public: + + /* Operators. */ + iter_t iter () const { return *thiz(); } + iter_t operator + () const { return *thiz(); } + iter_t begin () const { return *thiz(); } + iter_t end () const { return thiz()->__end__ (); } + explicit operator bool () const { return thiz()->__more__ (); } + unsigned len () const { return thiz()->__len__ (); } + /* The following can only be enabled if item_t is reference type. Otherwise + * it will be returning pointer to temporary rvalue. */ + template ::value)> + hb_remove_reference* operator -> () const { return std::addressof (**thiz()); } + item_t operator * () const { return thiz()->__item__ (); } + item_t operator * () { return thiz()->__item__ (); } + item_t operator [] (unsigned i) const { return thiz()->__item_at__ (i); } + item_t operator [] (unsigned i) { return thiz()->__item_at__ (i); } + iter_t& operator += (unsigned count) & { thiz()->__forward__ (count); return *thiz(); } + iter_t operator += (unsigned count) && { thiz()->__forward__ (count); return *thiz(); } + iter_t& operator ++ () & { thiz()->__next__ (); return *thiz(); } + iter_t operator ++ () && { thiz()->__next__ (); return *thiz(); } + iter_t& operator -= (unsigned count) & { thiz()->__rewind__ (count); return *thiz(); } + iter_t operator -= (unsigned count) && { thiz()->__rewind__ (count); return *thiz(); } + iter_t& operator -- () & { thiz()->__prev__ (); return *thiz(); } + iter_t operator -- () && { thiz()->__prev__ (); return *thiz(); } + iter_t operator + (unsigned count) const { auto c = thiz()->iter (); c += count; return c; } + friend iter_t operator + (unsigned count, const iter_t &it) { return it + count; } + iter_t operator ++ (int) { iter_t c (*thiz()); ++*thiz(); return c; } + iter_t operator - (unsigned count) const { auto c = thiz()->iter (); c -= count; return c; } + iter_t operator -- (int) { iter_t c (*thiz()); --*thiz(); return c; } + template + iter_t& operator >> (T &v) & { v = **thiz(); ++*thiz(); return *thiz(); } + template + iter_t operator >> (T &v) && { v = **thiz(); ++*thiz(); return *thiz(); } + template + iter_t& operator << (const T v) & { **thiz() = v; ++*thiz(); return *thiz(); } + template + iter_t operator << (const T v) && { **thiz() = v; ++*thiz(); return *thiz(); } + + protected: + hb_iter_t () = default; + hb_iter_t (const hb_iter_t &o HB_UNUSED) = default; + hb_iter_t (hb_iter_t &&o HB_UNUSED) = default; + hb_iter_t& operator = (const hb_iter_t &o HB_UNUSED) = default; + hb_iter_t& operator = (hb_iter_t &&o HB_UNUSED) = default; +}; + +#define HB_ITER_USING(Name) \ + using item_t = typename Name::item_t; \ + using Name::begin; \ + using Name::end; \ + using Name::get_item_size; \ + using Name::is_iterator; \ + using Name::iter; \ + using Name::operator bool; \ + using Name::len; \ + using Name::operator ->; \ + using Name::operator *; \ + using Name::operator []; \ + using Name::operator +=; \ + using Name::operator ++; \ + using Name::operator -=; \ + using Name::operator --; \ + using Name::operator +; \ + using Name::operator -; \ + using Name::operator >>; \ + using Name::operator <<; \ + static_assert (true, "") + +/* Returns iterator / item type of a type. */ +template +using hb_iter_type = decltype (hb_deref (hb_declval (Iterable)).iter ()); +template +using hb_item_type = decltype (*hb_deref (hb_declval (Iterable)).iter ()); + + +template struct hb_array_t; +template struct hb_sorted_array_t; + +struct +{ + template hb_iter_type + operator () (T&& c) const + { return hb_deref (std::forward (c)).iter (); } + + /* Specialization for C arrays. */ + + template inline hb_array_t + operator () (Type *array, unsigned int length) const + { return hb_array_t (array, length); } + + template hb_array_t + operator () (Type (&array)[length]) const + { return hb_array_t (array, length); } + +} +HB_FUNCOBJ (hb_iter); +struct +{ + template unsigned + operator () (T&& c) const + { return c.len (); } + +} +HB_FUNCOBJ (hb_len); + +/* Mixin to fill in what the subclass doesn't provide. */ +template +struct hb_iter_fallback_mixin_t +{ + private: + /* https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern */ + const iter_t* thiz () const { return static_cast (this); } + iter_t* thiz () { return static_cast< iter_t *> (this); } + public: + + /* Access: Implement __item__(), or __item_at__() if random-access. */ + item_t __item__ () const { return (*thiz())[0]; } + item_t __item_at__ (unsigned i) const { return *(*thiz() + i); } + + /* Termination: Implement __more__(), or __len__() if random-access. */ + bool __more__ () const { return bool (thiz()->len ()); } + unsigned __len__ () const + { iter_t c (*thiz()); unsigned l = 0; while (c) { c++; l++; } return l; } + + /* Advancing: Implement __next__(), or __forward__() if random-access. */ + void __next__ () { *thiz() += 1; } + void __forward__ (unsigned n) { while (*thiz() && n--) ++*thiz(); } + + /* Rewinding: Implement __prev__() or __rewind__() if bidirectional. */ + void __prev__ () { *thiz() -= 1; } + void __rewind__ (unsigned n) { while (*thiz() && n--) --*thiz(); } + + /* Range-based for: Implement __end__() if can be done faster, + * and operator!=. */ + iter_t __end__ () const + { + if (thiz()->is_random_access_iterator) + return *thiz() + thiz()->len (); + /* Above expression loops twice. Following loops once. */ + auto it = *thiz(); + while (it) ++it; + return it; + } + + protected: + hb_iter_fallback_mixin_t () = default; + hb_iter_fallback_mixin_t (const hb_iter_fallback_mixin_t &o HB_UNUSED) = default; + hb_iter_fallback_mixin_t (hb_iter_fallback_mixin_t &&o HB_UNUSED) = default; + hb_iter_fallback_mixin_t& operator = (const hb_iter_fallback_mixin_t &o HB_UNUSED) = default; + hb_iter_fallback_mixin_t& operator = (hb_iter_fallback_mixin_t &&o HB_UNUSED) = default; +}; + +template +struct hb_iter_with_fallback_t : + hb_iter_t, + hb_iter_fallback_mixin_t +{ + protected: + hb_iter_with_fallback_t () = default; + hb_iter_with_fallback_t (const hb_iter_with_fallback_t &o HB_UNUSED) = default; + hb_iter_with_fallback_t (hb_iter_with_fallback_t &&o HB_UNUSED) = default; + hb_iter_with_fallback_t& operator = (const hb_iter_with_fallback_t &o HB_UNUSED) = default; + hb_iter_with_fallback_t& operator = (hb_iter_with_fallback_t &&o HB_UNUSED) = default; +}; + +/* + * Meta-programming predicates. + */ + +/* hb_is_iterator() / hb_is_iterator_of() */ + +template +struct hb_is_iterator_of +{ + template + static hb_true_type impl (hb_priority<2>, hb_iter_t> *); + static hb_false_type impl (hb_priority<0>, const void *); + + public: + static constexpr bool value = decltype (impl (hb_prioritize, hb_declval (Iter*)))::value; +}; +#define hb_is_iterator_of(Iter, Item) hb_is_iterator_of::value +#define hb_is_iterator(Iter) hb_is_iterator_of (Iter, typename Iter::item_t) +#define hb_is_sorted_iterator_of(Iter, Item) (hb_is_iterator_of::value && Iter::is_sorted_iterator) +#define hb_is_sorted_iterator(Iter) hb_is_sorted_iterator_of (Iter, typename Iter::item_t) + +/* hb_is_iterable() */ + +template +struct hb_is_iterable +{ + private: + + template + static auto impl (hb_priority<1>) -> decltype (hb_declval (U).iter (), hb_true_type ()); + + template + static hb_false_type impl (hb_priority<0>); + + public: + static constexpr bool value = decltype (impl (hb_prioritize))::value; +}; +#define hb_is_iterable(Iterable) hb_is_iterable::value + +/* hb_is_source_of() / hb_is_sink_of() */ + +template +struct hb_is_source_of +{ + private: + template ))> + static hb_true_type impl (hb_priority<2>); + template + static auto impl (hb_priority<1>) -> decltype (hb_declval (Iter2) >> hb_declval (Item &), hb_true_type ()); + static hb_false_type impl (hb_priority<0>); + + public: + static constexpr bool value = decltype (impl (hb_prioritize))::value; +}; +#define hb_is_source_of(Iter, Item) hb_is_source_of::value + +template +struct hb_is_sink_of +{ + private: + template ))> + static hb_true_type impl (hb_priority<2>); + template + static auto impl (hb_priority<1>) -> decltype (hb_declval (Iter2) << hb_declval (Item), hb_true_type ()); + static hb_false_type impl (hb_priority<0>); + + public: + static constexpr bool value = decltype (impl (hb_prioritize))::value; +}; +#define hb_is_sink_of(Iter, Item) hb_is_sink_of::value + +/* This is commonly used, so define: */ +#define hb_is_sorted_source_of(Iter, Item) \ + (hb_is_source_of(Iter, Item) && Iter::is_sorted_iterator) + + +/* Range-based 'for' for iterables. */ + +template +static inline auto begin (Iterable&& iterable) HB_AUTO_RETURN (hb_iter (iterable).begin ()) + +template +static inline auto end (Iterable&& iterable) HB_AUTO_RETURN (hb_iter (iterable).end ()) + +/* begin()/end() are NOT looked up non-ADL. So each namespace must declare them. + * Do it for namespace OT. */ +namespace OT { + +template +static inline auto begin (Iterable&& iterable) HB_AUTO_RETURN (hb_iter (iterable).begin ()) + +template +static inline auto end (Iterable&& iterable) HB_AUTO_RETURN (hb_iter (iterable).end ()) + +} + + +/* + * Adaptors, combiners, etc. + */ + +template +static inline auto +operator | (Lhs&& lhs, Rhs&& rhs) HB_AUTO_RETURN (std::forward (rhs) (std::forward (lhs))) + +/* hb_map(), hb_filter(), hb_reduce() */ + +enum class hb_function_sortedness_t { + NOT_SORTED, + RETAINS_SORTING, + SORTED, +}; + +template +struct hb_map_iter_t : + hb_iter_t, + decltype (hb_get (hb_declval (Proj), *hb_declval (Iter)))> +{ + hb_map_iter_t (const Iter& it, Proj f_) : it (it), f (f_) {} + + typedef decltype (hb_get (hb_declval (Proj), *hb_declval (Iter))) __item_t__; + static constexpr bool is_random_access_iterator = Iter::is_random_access_iterator; + static constexpr bool is_sorted_iterator = + Sorted == hb_function_sortedness_t::SORTED ? true : + Sorted == hb_function_sortedness_t::RETAINS_SORTING ? Iter::is_sorted_iterator : + false; + __item_t__ __item__ () const { return hb_get (f.get (), *it); } + __item_t__ __item_at__ (unsigned i) const { return hb_get (f.get (), it[i]); } + bool __more__ () const { return bool (it); } + unsigned __len__ () const { return it.len (); } + void __next__ () { ++it; } + void __forward__ (unsigned n) { it += n; } + void __prev__ () { --it; } + void __rewind__ (unsigned n) { it -= n; } + hb_map_iter_t __end__ () const { return hb_map_iter_t (it.end (), f); } + bool operator != (const hb_map_iter_t& o) const + { return it != o.it; } + + private: + Iter it; + hb_reference_wrapper f; +}; + +template +struct hb_map_iter_factory_t +{ + hb_map_iter_factory_t (Proj f) : f (f) {} + + template + hb_map_iter_t + operator () (Iter it) + { return hb_map_iter_t (it, f); } + + private: + Proj f; +}; +struct +{ + template + hb_map_iter_factory_t + operator () (Proj&& f) const + { return hb_map_iter_factory_t (f); } +} +HB_FUNCOBJ (hb_map); +struct +{ + template + hb_map_iter_factory_t + operator () (Proj&& f) const + { return hb_map_iter_factory_t (f); } +} +HB_FUNCOBJ (hb_map_retains_sorting); +struct +{ + template + hb_map_iter_factory_t + operator () (Proj&& f) const + { return hb_map_iter_factory_t (f); } +} +HB_FUNCOBJ (hb_map_sorted); + +template +struct hb_filter_iter_t : + hb_iter_with_fallback_t, + typename Iter::item_t> +{ + hb_filter_iter_t (const Iter& it_, Pred p_, Proj f_) : it (it_), p (p_), f (f_) + { while (it && !hb_has (p.get (), hb_get (f.get (), *it))) ++it; } + + typedef typename Iter::item_t __item_t__; + static constexpr bool is_sorted_iterator = Iter::is_sorted_iterator; + __item_t__ __item__ () const { return *it; } + bool __more__ () const { return bool (it); } + void __next__ () { do ++it; while (it && !hb_has (p.get (), hb_get (f.get (), *it))); } + void __prev__ () { do --it; while (it && !hb_has (p.get (), hb_get (f.get (), *it))); } + hb_filter_iter_t __end__ () const { return hb_filter_iter_t (it.end (), p, f); } + bool operator != (const hb_filter_iter_t& o) const + { return it != o.it; } + + private: + Iter it; + hb_reference_wrapper p; + hb_reference_wrapper f; +}; +template +struct hb_filter_iter_factory_t +{ + hb_filter_iter_factory_t (Pred p, Proj f) : p (p), f (f) {} + + template + hb_filter_iter_t + operator () (Iter it) + { return hb_filter_iter_t (it, p, f); } + + private: + Pred p; + Proj f; +}; +struct +{ + template + hb_filter_iter_factory_t + operator () (Pred&& p = hb_identity, Proj&& f = hb_identity) const + { return hb_filter_iter_factory_t (p, f); } +} +HB_FUNCOBJ (hb_filter); + +template +struct hb_reduce_t +{ + hb_reduce_t (Redu r, InitT init_value) : r (r), init_value (init_value) {} + + template > + AccuT + operator () (Iter it) + { + AccuT value = init_value; + for (; it; ++it) + value = r (value, *it); + return value; + } + + private: + Redu r; + InitT init_value; +}; +struct +{ + template + hb_reduce_t + operator () (Redu&& r, InitT init_value) const + { return hb_reduce_t (r, init_value); } +} +HB_FUNCOBJ (hb_reduce); + + +/* hb_zip() */ + +template +struct hb_zip_iter_t : + hb_iter_t, + hb_pair_t> +{ + hb_zip_iter_t () {} + hb_zip_iter_t (const A& a, const B& b) : a (a), b (b) {} + + typedef hb_pair_t __item_t__; + static constexpr bool is_random_access_iterator = + A::is_random_access_iterator && + B::is_random_access_iterator; + /* Note. The following categorization is only valid if A is strictly sorted, + * ie. does NOT have duplicates. Previously I tried to categorize sortedness + * more granularly, see commits: + * + * 513762849a683914fc266a17ddf38f133cccf072 + * 4d3cf2adb669c345cc43832d11689271995e160a + * + * However, that was not enough, since hb_sorted_array_t, hb_sorted_vector_t, + * SortedArrayOf, etc all needed to be updated to add more variants. At that + * point I saw it not worth the effort, and instead we now deem all sorted + * collections as essentially strictly-sorted for the purposes of zip. + * + * The above assumption is not as bad as it sounds. Our "sorted" comes with + * no guarantees. It's just a contract, put in place to help you remember, + * and think about, whether an iterator you receive is expected to be + * sorted or not. As such, it's not perfect by definition, and should not + * be treated so. The inaccuracy here just errs in the direction of being + * more permissive, so your code compiles instead of erring on the side of + * marking your zipped iterator unsorted in which case your code won't + * compile. + * + * This semantical limitation does NOT affect logic in any other place I + * know of as of this writing. + */ + static constexpr bool is_sorted_iterator = A::is_sorted_iterator; + + __item_t__ __item__ () const { return __item_t__ (*a, *b); } + __item_t__ __item_at__ (unsigned i) const { return __item_t__ (a[i], b[i]); } + bool __more__ () const { return bool (a) && bool (b); } + unsigned __len__ () const { return hb_min (a.len (), b.len ()); } + void __next__ () { ++a; ++b; } + void __forward__ (unsigned n) { a += n; b += n; } + void __prev__ () { --a; --b; } + void __rewind__ (unsigned n) { a -= n; b -= n; } + hb_zip_iter_t __end__ () const { return hb_zip_iter_t (a.end (), b.end ()); } + /* Note, we should stop if ANY of the iters reaches end. As such two compare + * unequal if both items are unequal, NOT if either is unequal. */ + bool operator != (const hb_zip_iter_t& o) const + { return a != o.a && b != o.b; } + + private: + A a; + B b; +}; +struct +{ HB_PARTIALIZE(2); + template + hb_zip_iter_t, hb_iter_type> + operator () (A&& a, B&& b) const + { return hb_zip_iter_t, hb_iter_type> (hb_iter (a), hb_iter (b)); } +} +HB_FUNCOBJ (hb_zip); + +/* hb_concat() */ + +template +struct hb_concat_iter_t : + hb_iter_t, typename A::item_t> +{ + hb_concat_iter_t () {} + hb_concat_iter_t (A& a, B& b) : a (a), b (b) {} + hb_concat_iter_t (const A& a, const B& b) : a (a), b (b) {} + + + typedef typename A::item_t __item_t__; + static constexpr bool is_random_access_iterator = + A::is_random_access_iterator && + B::is_random_access_iterator; + static constexpr bool is_sorted_iterator = false; + + __item_t__ __item__ () const + { + if (!a) + return *b; + return *a; + } + + __item_t__ __item_at__ (unsigned i) const + { + unsigned a_len = a.len (); + if (i < a_len) + return a[i]; + return b[i - a_len]; + } + + bool __more__ () const { return bool (a) || bool (b); } + + unsigned __len__ () const { return a.len () + b.len (); } + + void __next__ () + { + if (a) + ++a; + else + ++b; + } + + void __forward__ (unsigned n) + { + if (!n) return; + if (!is_random_access_iterator) { + while (n-- && *this) { + (*this)++; + } + return; + } + + unsigned a_len = a.len (); + if (n > a_len) { + n -= a_len; + a.__forward__ (a_len); + b.__forward__ (n); + } else { + a.__forward__ (n); + } + } + + hb_concat_iter_t __end__ () const { return hb_concat_iter_t (a.end (), b.end ()); } + bool operator != (const hb_concat_iter_t& o) const + { + return a != o.a + || b != o.b; + } + + private: + A a; + B b; +}; +struct +{ HB_PARTIALIZE(2); + template + hb_concat_iter_t, hb_iter_type> + operator () (A&& a, B&& b) const + { return hb_concat_iter_t, hb_iter_type> (hb_iter (a), hb_iter (b)); } +} +HB_FUNCOBJ (hb_concat); + +/* hb_apply() */ + +template +struct hb_apply_t +{ + hb_apply_t (Appl a) : a (a) {} + + template + void operator () (Iter it) + { + for (; it; ++it) + (void) hb_invoke (a, *it); + } + + private: + Appl a; +}; +struct +{ + template hb_apply_t + operator () (Appl&& a) const + { return hb_apply_t (a); } + + template hb_apply_t + operator () (Appl *a) const + { return hb_apply_t (*a); } +} +HB_FUNCOBJ (hb_apply); + +/* hb_range()/hb_iota()/hb_repeat() */ + +template +struct hb_range_iter_t : + hb_iter_t, T> +{ + hb_range_iter_t (T start, T end_, S step) : v (start), end_ (end_for (start, end_, step)), step (step) {} + + typedef T __item_t__; + static constexpr bool is_random_access_iterator = true; + static constexpr bool is_sorted_iterator = true; + __item_t__ __item__ () const { return hb_ridentity (v); } + __item_t__ __item_at__ (unsigned j) const { return v + j * step; } + bool __more__ () const { return v != end_; } + unsigned __len__ () const { return !step ? UINT_MAX : (end_ - v) / step; } + void __next__ () { v += step; } + void __forward__ (unsigned n) { v += n * step; } + void __prev__ () { v -= step; } + void __rewind__ (unsigned n) { v -= n * step; } + hb_range_iter_t __end__ () const { return hb_range_iter_t (end_, end_, step); } + bool operator != (const hb_range_iter_t& o) const + { return v != o.v; } + + private: + static inline T end_for (T start, T end_, S step) + { + if (!step) + return end_; + auto res = (end_ - start) % step; + if (!res) + return end_; + end_ += step - res; + return end_; + } + + private: + T v; + T end_; + S step; +}; +struct +{ + template hb_range_iter_t + operator () (T end = (unsigned) -1) const + { return hb_range_iter_t (0, end, 1u); } + + template hb_range_iter_t + operator () (T start, T end, S step = 1u) const + { return hb_range_iter_t (start, end, step); } +} +HB_FUNCOBJ (hb_range); + +template +struct hb_iota_iter_t : + hb_iter_with_fallback_t, T> +{ + hb_iota_iter_t (T start, S step) : v (start), step (step) {} + + private: + + template + auto + inc (hb_type_identity s, hb_priority<1>) + -> hb_void_t (s), hb_declval ()))> + { v = hb_invoke (std::forward (s), v); } + + void + inc (S s, hb_priority<0>) + { v += s; } + + public: + + typedef T __item_t__; + static constexpr bool is_random_access_iterator = true; + static constexpr bool is_sorted_iterator = true; + __item_t__ __item__ () const { return hb_ridentity (v); } + bool __more__ () const { return true; } + unsigned __len__ () const { return UINT_MAX; } + void __next__ () { inc (step, hb_prioritize); } + void __prev__ () { v -= step; } + hb_iota_iter_t __end__ () const { return *this; } + bool operator != (const hb_iota_iter_t& o) const { return true; } + + private: + T v; + S step; +}; +struct +{ + template hb_iota_iter_t + operator () (T start = 0u, S step = 1u) const + { return hb_iota_iter_t (start, step); } +} +HB_FUNCOBJ (hb_iota); + +template +struct hb_repeat_iter_t : + hb_iter_t, T> +{ + hb_repeat_iter_t (T value) : v (value) {} + + typedef T __item_t__; + static constexpr bool is_random_access_iterator = true; + static constexpr bool is_sorted_iterator = true; + __item_t__ __item__ () const { return v; } + __item_t__ __item_at__ (unsigned j) const { return v; } + bool __more__ () const { return true; } + unsigned __len__ () const { return UINT_MAX; } + void __next__ () {} + void __forward__ (unsigned) {} + void __prev__ () {} + void __rewind__ (unsigned) {} + hb_repeat_iter_t __end__ () const { return *this; } + bool operator != (const hb_repeat_iter_t& o) const { return true; } + + private: + T v; +}; +struct +{ + template hb_repeat_iter_t + operator () (T value) const + { return hb_repeat_iter_t (value); } +} +HB_FUNCOBJ (hb_repeat); + +/* hb_enumerate()/hb_take() */ + +struct +{ + template + auto operator () (Iterable&& it, Index start = 0u) const HB_AUTO_RETURN + ( hb_zip (hb_iota (start), it) ) +} +HB_FUNCOBJ (hb_enumerate); + +struct +{ HB_PARTIALIZE(2); + template + auto operator () (Iterable&& it, unsigned count) const HB_AUTO_RETURN + ( hb_zip (hb_range (count), it) | hb_map (hb_second) ) + + /* Specialization arrays. */ + + template inline hb_array_t + operator () (hb_array_t array, unsigned count) const + { return array.sub_array (0, count); } + + template inline hb_sorted_array_t + operator () (hb_sorted_array_t array, unsigned count) const + { return array.sub_array (0, count); } +} +HB_FUNCOBJ (hb_take); + +struct +{ HB_PARTIALIZE(2); + template + auto operator () (Iter it, unsigned count) const HB_AUTO_RETURN + ( + + hb_iota (it, hb_add (count)) + | hb_map (hb_take (count)) + | hb_take ((hb_len (it) + count - 1) / count) + ) +} +HB_FUNCOBJ (hb_chop); + +/* hb_sink() */ + +template +struct hb_sink_t +{ + hb_sink_t (Sink s) : s (s) {} + + template + void operator () (Iter it) + { + for (; it; ++it) + s << *it; + } + + private: + Sink s; +}; +struct +{ + template hb_sink_t + operator () (Sink&& s) const + { return hb_sink_t (s); } + + template hb_sink_t + operator () (Sink *s) const + { return hb_sink_t (*s); } +} +HB_FUNCOBJ (hb_sink); + +/* hb-drain: hb_sink to void / blackhole / /dev/null. */ + +struct +{ + template + void operator () (Iter it) const + { + for (; it; ++it) + (void) *it; + } +} +HB_FUNCOBJ (hb_drain); + +/* hb_unzip(): unzip and sink to two sinks. */ + +template +struct hb_unzip_t +{ + hb_unzip_t (Sink1 s1, Sink2 s2) : s1 (s1), s2 (s2) {} + + template + void operator () (Iter it) + { + for (; it; ++it) + { + const auto &v = *it; + s1 << v.first; + s2 << v.second; + } + } + + private: + Sink1 s1; + Sink2 s2; +}; +struct +{ + template hb_unzip_t + operator () (Sink1&& s1, Sink2&& s2) const + { return hb_unzip_t (s1, s2); } + + template hb_unzip_t + operator () (Sink1 *s1, Sink2 *s2) const + { return hb_unzip_t (*s1, *s2); } +} +HB_FUNCOBJ (hb_unzip); + + +/* hb-all, hb-any, hb-none. */ + +struct +{ + template + bool operator () (Iterable&& c, + Pred&& p = hb_identity, + Proj&& f = hb_identity) const + { + for (auto it = hb_iter (c); it; ++it) + if (!hb_match (std::forward (p), hb_get (std::forward (f), *it))) + return false; + return true; + } +} +HB_FUNCOBJ (hb_all); +struct +{ + template + bool operator () (Iterable&& c, + Pred&& p = hb_identity, + Proj&& f = hb_identity) const + { + for (auto it = hb_iter (c); it; ++it) + if (hb_match (std::forward (p), hb_get (std::forward (f), *it))) + return true; + return false; + } +} +HB_FUNCOBJ (hb_any); +struct +{ + template + bool operator () (Iterable&& c, + Pred&& p = hb_identity, + Proj&& f = hb_identity) const + { + for (auto it = hb_iter (c); it; ++it) + if (hb_match (std::forward (p), hb_get (std::forward (f), *it))) + return false; + return true; + } +} +HB_FUNCOBJ (hb_none); + +/* + * Algorithms operating on iterators. + */ + +template +inline void +hb_fill (C&& c, const V &v) +{ + for (auto i = hb_iter (c); i; i++) + *i = v; +} + +template +inline void +hb_copy (S&& is, D&& id) +{ + hb_iter (is) | hb_sink (id); +} + + +#endif /* HB_ITER_HH */ diff --git a/libs/harfbuzz/src/hb-kern.hh b/libs/harfbuzz/src/hb-kern.hh new file mode 100644 index 00000000..9ea945ca --- /dev/null +++ b/libs/harfbuzz/src/hb-kern.hh @@ -0,0 +1,145 @@ +/* + * Copyright © 2017 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 HB_KERN_HH +#define HB_KERN_HH + +#include "hb-open-type.hh" +#include "hb-aat-layout-common.hh" +#include "hb-ot-layout-gpos-table.hh" + + +namespace OT { + + +template +struct hb_kern_machine_t +{ + hb_kern_machine_t (const Driver &driver_, + bool crossStream_ = false) : + driver (driver_), + crossStream (crossStream_) {} + + HB_NO_SANITIZE_SIGNED_INTEGER_OVERFLOW + void kern (hb_font_t *font, + hb_buffer_t *buffer, + hb_mask_t kern_mask, + bool scale = true) const + { + if (!buffer->message (font, "start kern")) + return; + + buffer->unsafe_to_concat (); + OT::hb_ot_apply_context_t c (1, font, buffer); + c.set_lookup_mask (kern_mask); + c.set_lookup_props (OT::LookupFlag::IgnoreMarks); + auto &skippy_iter = c.iter_input; + + bool horizontal = HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction); + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; + hb_glyph_position_t *pos = buffer->pos; + for (unsigned int idx = 0; idx < count;) + { + if (!(info[idx].mask & kern_mask)) + { + idx++; + continue; + } + + skippy_iter.reset (idx, 1); + unsigned unsafe_to; + if (!skippy_iter.next (&unsafe_to)) + { + idx++; + continue; + } + + unsigned int i = idx; + unsigned int j = skippy_iter.idx; + + hb_position_t kern = driver.get_kerning (info[i].codepoint, + info[j].codepoint); + + + if (likely (!kern)) + goto skip; + + if (horizontal) + { + if (scale) + kern = font->em_scale_x (kern); + if (crossStream) + { + pos[j].y_offset = kern; + buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT; + } + else + { + hb_position_t kern1 = kern >> 1; + hb_position_t kern2 = kern - kern1; + pos[i].x_advance += kern1; + pos[j].x_advance += kern2; + pos[j].x_offset += kern2; + } + } + else + { + if (scale) + kern = font->em_scale_y (kern); + if (crossStream) + { + pos[j].x_offset = kern; + buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT; + } + else + { + hb_position_t kern1 = kern >> 1; + hb_position_t kern2 = kern - kern1; + pos[i].y_advance += kern1; + pos[j].y_advance += kern2; + pos[j].y_offset += kern2; + } + } + + buffer->unsafe_to_break (i, j + 1); + + skip: + idx = skippy_iter.idx; + } + + (void) buffer->message (font, "end kern"); + } + + const Driver &driver; + bool crossStream; +}; + + +} /* namespace OT */ + + +#endif /* HB_KERN_HH */ diff --git a/libs/harfbuzz/src/hb-machinery.hh b/libs/harfbuzz/src/hb-machinery.hh new file mode 100644 index 00000000..2571f22e --- /dev/null +++ b/libs/harfbuzz/src/hb-machinery.hh @@ -0,0 +1,318 @@ +/* + * Copyright © 2007,2008,2009,2010 Red Hat, Inc. + * Copyright © 2012,2018 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. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_MACHINERY_HH +#define HB_MACHINERY_HH + +#include "hb.hh" +#include "hb-blob.hh" + +#include "hb-dispatch.hh" +#include "hb-sanitize.hh" +#include "hb-serialize.hh" + + +/* + * Casts + */ + +/* StructAtOffset(P,Ofs) returns the struct T& that is placed at memory + * location pointed to by P plus Ofs bytes. */ +template +static inline const Type& StructAtOffset(const void *P, unsigned int offset) +{ return * reinterpret_cast ((const char *) P + offset); } +template +static inline Type& StructAtOffset(void *P, unsigned int offset) +{ return * reinterpret_cast ((char *) P + offset); } +template +static inline const Type& StructAtOffsetUnaligned(const void *P, unsigned int offset) +{ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-align" + return * reinterpret_cast ((const char *) P + offset); +#pragma GCC diagnostic pop +} +template +static inline Type& StructAtOffsetUnaligned(void *P, unsigned int offset) +{ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-align" + return * reinterpret_cast ((char *) P + offset); +#pragma GCC diagnostic pop +} + +/* StructAfter(X) returns the struct T& that is placed after X. + * Works with X of variable size also. X must implement get_size() */ +template +static inline const Type& StructAfter(const TObject &X) +{ return StructAtOffset(&X, X.get_size()); } +template +static inline Type& StructAfter(TObject &X) +{ return StructAtOffset(&X, X.get_size()); } + + +/* + * Size checking + */ + +/* Size signifying variable-sized array */ +#ifndef HB_VAR_ARRAY +#define HB_VAR_ARRAY 1 +#endif + +/* Check _assertion in a method environment */ +#define _DEFINE_INSTANCE_ASSERTION1(_line, _assertion) \ + void _instance_assertion_on_line_##_line () const \ + { static_assert ((_assertion), ""); } +# define _DEFINE_INSTANCE_ASSERTION0(_line, _assertion) _DEFINE_INSTANCE_ASSERTION1 (_line, _assertion) +# define DEFINE_INSTANCE_ASSERTION(_assertion) _DEFINE_INSTANCE_ASSERTION0 (__LINE__, _assertion) + +/* Check that _code compiles in a method environment */ +#define _DEFINE_COMPILES_ASSERTION1(_line, _code) \ + void _compiles_assertion_on_line_##_line () const \ + { _code; } +# define _DEFINE_COMPILES_ASSERTION0(_line, _code) _DEFINE_COMPILES_ASSERTION1 (_line, _code) +# define DEFINE_COMPILES_ASSERTION(_code) _DEFINE_COMPILES_ASSERTION0 (__LINE__, _code) + + +#define DEFINE_SIZE_STATIC(size) \ + DEFINE_INSTANCE_ASSERTION (sizeof (*this) == (size)) \ + unsigned int get_size () const { return (size); } \ + static constexpr unsigned null_size = (size); \ + static constexpr unsigned min_size = (size); \ + static constexpr unsigned static_size = (size) + +#define DEFINE_SIZE_UNION(size, _member) \ + DEFINE_COMPILES_ASSERTION ((void) this->u._member.static_size) \ + DEFINE_INSTANCE_ASSERTION (sizeof(this->u._member) == (size)) \ + static constexpr unsigned null_size = (size); \ + static constexpr unsigned min_size = (size) + +#define DEFINE_SIZE_MIN(size) \ + DEFINE_INSTANCE_ASSERTION (sizeof (*this) >= (size)) \ + static constexpr unsigned null_size = (size); \ + static constexpr unsigned min_size = (size) + +#define DEFINE_SIZE_UNBOUNDED(size) \ + DEFINE_INSTANCE_ASSERTION (sizeof (*this) >= (size)) \ + static constexpr unsigned min_size = (size) + +#define DEFINE_SIZE_ARRAY(size, array) \ + DEFINE_COMPILES_ASSERTION ((void) (array)[0].static_size) \ + DEFINE_INSTANCE_ASSERTION (sizeof (*this) == (size) + (HB_VAR_ARRAY+0) * sizeof ((array)[0])) \ + static constexpr unsigned null_size = (size); \ + static constexpr unsigned min_size = (size) + +#define DEFINE_SIZE_ARRAY_SIZED(size, array) \ + unsigned int get_size () const { return (size - (array).min_size + (array).get_size ()); } \ + DEFINE_SIZE_ARRAY(size, array) + + + +/* + * Lazy loaders. + */ + +template +struct hb_data_wrapper_t +{ + static_assert (WheresData > 0, ""); + + Data * get_data () const + { return *(((Data **) (void *) this) - WheresData); } + + bool is_inert () const { return !get_data (); } + + template + Stored * call_create () const { return Subclass::create (get_data ()); } +}; +template <> +struct hb_data_wrapper_t +{ + bool is_inert () const { return false; } + + template + Stored * call_create () const { return Funcs::create (); } +}; + +template struct hb_non_void_t { typedef T1 value; }; +template struct hb_non_void_t { typedef T2 value; }; + +template +struct hb_lazy_loader_t : hb_data_wrapper_t +{ + typedef typename hb_non_void_t + >::value Funcs; + + void init0 () {} /* Init, when memory is already set to 0. No-op for us. */ + void init () { instance.set_relaxed (nullptr); } + void fini () { do_destroy (instance.get_acquire ()); init (); } + + void free_instance () + { + retry: + Stored *p = instance.get_acquire (); + if (unlikely (p && !cmpexch (p, nullptr))) + goto retry; + do_destroy (p); + } + + static void do_destroy (Stored *p) + { + if (p && p != const_cast (Funcs::get_null ())) + Funcs::destroy (p); + } + + const Returned * operator -> () const { return get (); } + template + const U & operator * () const { return *get (); } + explicit operator bool () const + { return get_stored () != Funcs::get_null (); } + template operator const C * () const { return get (); } + + Stored * get_stored () const + { + retry: + Stored *p = this->instance.get_acquire (); + if (unlikely (!p)) + { + if (unlikely (this->is_inert ())) + return const_cast (Funcs::get_null ()); + + p = this->template call_create (); + if (unlikely (!p)) + p = const_cast (Funcs::get_null ()); + + if (unlikely (!cmpexch (nullptr, p))) + { + do_destroy (p); + goto retry; + } + } + return p; + } + Stored * get_stored_relaxed () const + { + return this->instance.get_relaxed (); + } + + bool cmpexch (Stored *current, Stored *value) const + { + /* This *must* be called when there are no other threads accessing. */ + return this->instance.cmpexch (current, value); + } + + const Returned * get () const { return Funcs::convert (get_stored ()); } + const Returned * get_relaxed () const { return Funcs::convert (get_stored_relaxed ()); } + Returned * get_unconst () const { return const_cast (Funcs::convert (get_stored ())); } + + /* To be possibly overloaded by subclasses. */ + static Returned* convert (Stored *p) { return p; } + + /* By default null/init/fini the object. */ + static const Stored* get_null () { return &Null (Stored); } + static Stored *create (Data *data) + { + Stored *p = (Stored *) hb_calloc (1, sizeof (Stored)); + if (likely (p)) + p = new (p) Stored (data); + return p; + } + static Stored *create () + { + Stored *p = (Stored *) hb_calloc (1, sizeof (Stored)); + if (likely (p)) + p = new (p) Stored (); + return p; + } + static void destroy (Stored *p) + { + p->~Stored (); + hb_free (p); + } + +// private: + /* Must only have one pointer. */ + hb_atomic_ptr_t instance; +}; + +/* Specializations. */ + +template +struct hb_face_lazy_loader_t : hb_lazy_loader_t, + hb_face_t, WheresFace> {}; + +template +struct hb_table_lazy_loader_t : hb_lazy_loader_t, + hb_face_t, WheresFace, + hb_blob_t> +{ + static hb_blob_t *create (hb_face_t *face) + { + auto c = hb_sanitize_context_t (); + if (core) + c.set_num_glyphs (0); // So we don't recurse ad infinitum... + return c.reference_table (face); + } + static void destroy (hb_blob_t *p) { hb_blob_destroy (p); } + + static const hb_blob_t *get_null () + { return hb_blob_get_empty (); } + + static const T* convert (const hb_blob_t *blob) + { return blob->as (); } + + hb_blob_t* get_blob () const { return this->get_stored (); } +}; + +template +struct hb_font_funcs_lazy_loader_t : hb_lazy_loader_t +{ + static void destroy (hb_font_funcs_t *p) + { hb_font_funcs_destroy (p); } + static const hb_font_funcs_t *get_null () + { return hb_font_funcs_get_empty (); } +}; +template +struct hb_unicode_funcs_lazy_loader_t : hb_lazy_loader_t +{ + static void destroy (hb_unicode_funcs_t *p) + { hb_unicode_funcs_destroy (p); } + static const hb_unicode_funcs_t *get_null () + { return hb_unicode_funcs_get_empty (); } +}; + + +#endif /* HB_MACHINERY_HH */ diff --git a/libs/harfbuzz/src/hb-map.cc b/libs/harfbuzz/src/hb-map.cc new file mode 100644 index 00000000..5c5f5de5 --- /dev/null +++ b/libs/harfbuzz/src/hb-map.cc @@ -0,0 +1,343 @@ +/* + * Copyright © 2018 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 + */ + +#include "hb-map.hh" + + +/** + * SECTION:hb-map + * @title: hb-map + * @short_description: Object representing integer to integer mapping + * @include: hb.h + * + * Map objects are integer-to-integer hash-maps. Currently they are + * not used in the HarfBuzz public API, but are provided for client's + * use if desired. + **/ + + +/** + * hb_map_create: + * + * Creates a new, initially empty map. + * + * Return value: (transfer full): The new #hb_map_t + * + * Since: 1.7.7 + **/ +hb_map_t * +hb_map_create () +{ + hb_map_t *map; + + if (!(map = hb_object_create ())) + return hb_map_get_empty (); + + return map; +} + +/** + * hb_map_get_empty: + * + * Fetches the singleton empty #hb_map_t. + * + * Return value: (transfer full): The empty #hb_map_t + * + * Since: 1.7.7 + **/ +hb_map_t * +hb_map_get_empty () +{ + return const_cast (&Null (hb_map_t)); +} + +/** + * hb_map_reference: (skip) + * @map: A map + * + * Increases the reference count on a map. + * + * Return value: (transfer full): The map + * + * Since: 1.7.7 + **/ +hb_map_t * +hb_map_reference (hb_map_t *map) +{ + return hb_object_reference (map); +} + +/** + * hb_map_destroy: (skip) + * @map: A map + * + * Decreases the reference count on a map. When + * the reference count reaches zero, the map is + * destroyed, freeing all memory. + * + * Since: 1.7.7 + **/ +void +hb_map_destroy (hb_map_t *map) +{ + if (!hb_object_destroy (map)) return; + + hb_free (map); +} + +/** + * hb_map_set_user_data: (skip) + * @map: A map + * @key: The user-data key to set + * @data: A pointer to the user data to set + * @destroy: (nullable): A callback to call when @data is not needed anymore + * @replace: Whether to replace an existing data with the same key + * + * Attaches a user-data key/data pair to the specified map. + * + * Return value: `true` if success, `false` otherwise + * + * Since: 1.7.7 + **/ +hb_bool_t +hb_map_set_user_data (hb_map_t *map, + hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy, + hb_bool_t replace) +{ + return hb_object_set_user_data (map, key, data, destroy, replace); +} + +/** + * hb_map_get_user_data: (skip) + * @map: A map + * @key: The user-data key to query + * + * Fetches the user data associated with the specified key, + * attached to the specified map. + * + * Return value: (transfer none): A pointer to the user data + * + * Since: 1.7.7 + **/ +void * +hb_map_get_user_data (const hb_map_t *map, + hb_user_data_key_t *key) +{ + return hb_object_get_user_data (map, key); +} + + +/** + * hb_map_allocation_successful: + * @map: A map + * + * Tests whether memory allocation for a set was successful. + * + * Return value: `true` if allocation succeeded, `false` otherwise + * + * Since: 1.7.7 + **/ +hb_bool_t +hb_map_allocation_successful (const hb_map_t *map) +{ + return map->successful; +} + +/** + * hb_map_copy: + * @map: A map + * + * Allocate a copy of @map. + * + * Return value: Newly-allocated map. + * + * Since: 4.4.0 + **/ +hb_map_t * +hb_map_copy (const hb_map_t *map) +{ + hb_map_t *copy = hb_map_create (); + if (unlikely (!copy)) return nullptr; + copy->resize (map->population); + hb_copy (*map, *copy); + return copy; +} + +/** + * hb_map_set: + * @map: A map + * @key: The key to store in the map + * @value: The value to store for @key + * + * Stores @key:@value in the map. + * + * Since: 1.7.7 + **/ +void +hb_map_set (hb_map_t *map, + hb_codepoint_t key, + hb_codepoint_t value) +{ + /* Immutable-safe. */ + map->set (key, value); +} + +/** + * hb_map_get: + * @map: A map + * @key: The key to query + * + * Fetches the value stored for @key in @map. + * + * Since: 1.7.7 + **/ +hb_codepoint_t +hb_map_get (const hb_map_t *map, + hb_codepoint_t key) +{ + return map->get (key); +} + +/** + * hb_map_del: + * @map: A map + * @key: The key to delete + * + * Removes @key and its stored value from @map. + * + * Since: 1.7.7 + **/ +void +hb_map_del (hb_map_t *map, + hb_codepoint_t key) +{ + /* Immutable-safe. */ + map->del (key); +} + +/** + * hb_map_has: + * @map: A map + * @key: The key to query + * + * Tests whether @key is an element of @map. + * + * Return value: `true` if @key is found in @map, `false` otherwise + * + * Since: 1.7.7 + **/ +hb_bool_t +hb_map_has (const hb_map_t *map, + hb_codepoint_t key) +{ + return map->has (key); +} + + +/** + * hb_map_clear: + * @map: A map + * + * Clears out the contents of @map. + * + * Since: 1.7.7 + **/ +void +hb_map_clear (hb_map_t *map) +{ + return map->clear (); +} + +/** + * hb_map_is_empty: + * @map: A map + * + * Tests whether @map is empty (contains no elements). + * + * Return value: `true` if @map is empty + * + * Since: 1.7.7 + **/ +hb_bool_t +hb_map_is_empty (const hb_map_t *map) +{ + return map->is_empty (); +} + +/** + * hb_map_get_population: + * @map: A map + * + * Returns the number of key-value pairs in the map. + * + * Return value: The population of @map + * + * Since: 1.7.7 + **/ +unsigned int +hb_map_get_population (const hb_map_t *map) +{ + return map->get_population (); +} + +/** + * hb_map_is_equal: + * @map: A map + * @other: Another map + * + * Tests whether @map and @other are equal (contain the same + * elements). + * + * Return value: `true` if the two maps are equal, `false` otherwise. + * + * Since: 4.3.0 + **/ +hb_bool_t +hb_map_is_equal (const hb_map_t *map, + const hb_map_t *other) +{ + return map->is_equal (*other); +} + +/** + * hb_map_hash: + * @map: A map + * + * Creates a hash representing @map. + * + * Return value: + * A hash of @map. + * + * Since: 4.4.0 + **/ +HB_EXTERN unsigned int +hb_map_hash (const hb_map_t *map) +{ + return map->hash (); +} + diff --git a/libs/harfbuzz/src/hb-map.h b/libs/harfbuzz/src/hb-map.h new file mode 100644 index 00000000..3a067c5c --- /dev/null +++ b/libs/harfbuzz/src/hb-map.h @@ -0,0 +1,124 @@ +/* + * Copyright © 2018 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 + */ + +#if !defined(HB_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR) +#error "Include instead." +#endif + +#ifndef HB_MAP_H +#define HB_MAP_H + +#include "hb-common.h" + +HB_BEGIN_DECLS + + +/** + * HB_MAP_VALUE_INVALID: + * + * Unset #hb_map_t value. + * + * Since: 1.7.7 + */ +#define HB_MAP_VALUE_INVALID ((hb_codepoint_t) -1) + +/** + * hb_map_t: + * + * Data type for holding integer-to-integer hash maps. + * + **/ +typedef struct hb_map_t hb_map_t; + + +HB_EXTERN hb_map_t * +hb_map_create (void); + +HB_EXTERN hb_map_t * +hb_map_get_empty (void); + +HB_EXTERN hb_map_t * +hb_map_reference (hb_map_t *map); + +HB_EXTERN void +hb_map_destroy (hb_map_t *map); + +HB_EXTERN hb_bool_t +hb_map_set_user_data (hb_map_t *map, + hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy, + hb_bool_t replace); + +HB_EXTERN void * +hb_map_get_user_data (const hb_map_t *map, + hb_user_data_key_t *key); + + +/* Returns false if allocation has failed before */ +HB_EXTERN hb_bool_t +hb_map_allocation_successful (const hb_map_t *map); + +HB_EXTERN hb_map_t * +hb_map_copy (const hb_map_t *map); + +HB_EXTERN void +hb_map_clear (hb_map_t *map); + +HB_EXTERN hb_bool_t +hb_map_is_empty (const hb_map_t *map); + +HB_EXTERN unsigned int +hb_map_get_population (const hb_map_t *map); + +HB_EXTERN hb_bool_t +hb_map_is_equal (const hb_map_t *map, + const hb_map_t *other); + +HB_EXTERN unsigned int +hb_map_hash (const hb_map_t *map); + +HB_EXTERN void +hb_map_set (hb_map_t *map, + hb_codepoint_t key, + hb_codepoint_t value); + +HB_EXTERN hb_codepoint_t +hb_map_get (const hb_map_t *map, + hb_codepoint_t key); + +HB_EXTERN void +hb_map_del (hb_map_t *map, + hb_codepoint_t key); + +HB_EXTERN hb_bool_t +hb_map_has (const hb_map_t *map, + hb_codepoint_t key); + + +HB_END_DECLS + +#endif /* HB_MAP_H */ diff --git a/libs/harfbuzz/src/hb-map.hh b/libs/harfbuzz/src/hb-map.hh new file mode 100644 index 00000000..8302e3f8 --- /dev/null +++ b/libs/harfbuzz/src/hb-map.hh @@ -0,0 +1,480 @@ +/* + * Copyright © 2018 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 HB_MAP_HH +#define HB_MAP_HH + +#include "hb.hh" + + +/* + * hb_hashmap_t + */ + +extern HB_INTERNAL const hb_codepoint_t minus_1; + +template +struct hb_hashmap_t +{ + hb_hashmap_t () { init (); } + ~hb_hashmap_t () { fini (); } + + hb_hashmap_t (const hb_hashmap_t& o) : hb_hashmap_t () { resize (population); hb_copy (o, *this); } + hb_hashmap_t (hb_hashmap_t&& o) : hb_hashmap_t () { hb_swap (*this, o); } + hb_hashmap_t& operator= (const hb_hashmap_t& o) { resize (population); hb_copy (o, *this); return *this; } + hb_hashmap_t& operator= (hb_hashmap_t&& o) { hb_swap (*this, o); return *this; } + + hb_hashmap_t (std::initializer_list> lst) : hb_hashmap_t () + { + for (auto&& item : lst) + set (item.first, item.second); + } + template + hb_hashmap_t (const Iterable &o) : hb_hashmap_t () + { + auto iter = hb_iter (o); + if (iter.is_random_access_iterator) + resize (hb_len (iter)); + hb_copy (iter, *this); + } + + struct item_t + { + K key; + uint32_t hash : 30; + uint32_t is_used_ : 1; + uint32_t is_tombstone_ : 1; + V value; + + bool is_used () const { return is_used_; } + void set_used (bool is_used) { is_used_ = is_used; } + bool is_tombstone () const { return is_tombstone_; } + void set_tombstone (bool is_tombstone) { is_tombstone_ = is_tombstone; } + bool is_real () const { return is_used_ && !is_tombstone_; } + + template + static inline const V& default_value () { return Null(V); }; + template + static inline const V& default_value () + { + static_assert (hb_is_same (V, hb_codepoint_t), ""); + return minus_1; + }; + + void clear () + { + new (std::addressof (key)) K (); + new (std::addressof (value)) V (); + hash = 0; + is_used_ = false; + is_tombstone_ = false; + } + + bool operator == (const K &o) { return hb_deref (key) == hb_deref (o); } + bool operator == (const item_t &o) { return *this == o.key; } + hb_pair_t get_pair() const { return hb_pair_t (key, value); } + hb_pair_t get_pair_ref() const { return hb_pair_t (key, value); } + + uint32_t total_hash () const + { return (hash * 31) + hb_hash (value); } + }; + + hb_object_header_t header; + bool successful; /* Allocations successful */ + unsigned int population; /* Not including tombstones. */ + unsigned int occupancy; /* Including tombstones. */ + unsigned int mask; + unsigned int prime; + item_t *items; + + friend void swap (hb_hashmap_t& a, hb_hashmap_t& b) + { + if (unlikely (!a.successful || !b.successful)) + return; + hb_swap (a.population, b.population); + hb_swap (a.occupancy, b.occupancy); + hb_swap (a.mask, b.mask); + hb_swap (a.prime, b.prime); + hb_swap (a.items, b.items); + } + void init () + { + hb_object_init (this); + + successful = true; + population = occupancy = 0; + mask = 0; + prime = 0; + items = nullptr; + } + void fini () + { + hb_object_fini (this); + + if (likely (items)) { + unsigned size = mask + 1; + for (unsigned i = 0; i < size; i++) + items[i].~item_t (); + hb_free (items); + items = nullptr; + } + population = occupancy = 0; + } + + void reset () + { + successful = true; + clear (); + } + + bool in_error () const { return !successful; } + + bool resize (unsigned new_population = 0) + { + if (unlikely (!successful)) return false; + + unsigned int power = hb_bit_storage (hb_max (population, new_population) * 2 + 8); + unsigned int new_size = 1u << power; + item_t *new_items = (item_t *) hb_malloc ((size_t) new_size * sizeof (item_t)); + if (unlikely (!new_items)) + { + successful = false; + return false; + } + for (auto &_ : hb_iter (new_items, new_size)) + _.clear (); + + unsigned int old_size = mask + 1; + item_t *old_items = items; + + /* Switch to new, empty, array. */ + population = occupancy = 0; + mask = new_size - 1; + prime = prime_for (power); + items = new_items; + + /* Insert back old items. */ + if (old_items) + for (unsigned int i = 0; i < old_size; i++) + { + if (old_items[i].is_real ()) + { + set_with_hash (old_items[i].key, + old_items[i].hash, + std::move (old_items[i].value)); + } + old_items[i].~item_t (); + } + + hb_free (old_items); + + return true; + } + + template + bool set (K key, VV&& value) { return set_with_hash (key, hb_hash (key), std::forward (value)); } + + const V& get (K key) const + { + if (unlikely (!items)) return item_t::default_value (); + unsigned int i = bucket_for (key); + return items[i].is_real () && items[i] == key ? items[i].value : item_t::default_value (); + } + + void del (K key) { set_with_hash (key, hb_hash (key), item_t::default_value (), true); } + + /* Has interface. */ + typedef const V& value_t; + value_t operator [] (K k) const { return get (k); } + template + bool has (K key, VV **vp = nullptr) const + { + if (unlikely (!items)) + return false; + unsigned int i = bucket_for (key); + if (items[i].is_real () && items[i] == key) + { + if (vp) *vp = &items[i].value; + return true; + } + else + return false; + } + /* Projection. */ + V operator () (K k) const { return get (k); } + + void clear () + { + if (unlikely (!successful)) return; + + if (items) + for (auto &_ : hb_iter (items, mask + 1)) + _.clear (); + + population = occupancy = 0; + } + + bool is_empty () const { return population == 0; } + explicit operator bool () const { return !is_empty (); } + + uint32_t hash () const + { + uint32_t h = 0; + for (const auto &item : + hb_array (items, mask ? mask + 1 : 0) + | hb_filter (&item_t::is_real)) + h ^= item.total_hash (); + return h; + } + + bool is_equal (const hb_hashmap_t &other) const + { + if (population != other.population) return false; + + for (auto pair : iter ()) + if (get (pair.first) != pair.second) + return false; + + return true; + } + bool operator == (const hb_hashmap_t &other) const { return is_equal (other); } + bool operator != (const hb_hashmap_t &other) const { return !is_equal (other); } + + unsigned int get_population () const { return population; } + + /* + * Iterator + */ + auto iter () const HB_AUTO_RETURN + ( + + hb_array (items, mask ? mask + 1 : 0) + | hb_filter (&item_t::is_real) + | hb_map (&item_t::get_pair) + ) + auto iter_ref () const HB_AUTO_RETURN + ( + + hb_array (items, mask ? mask + 1 : 0) + | hb_filter (&item_t::is_real) + | hb_map (&item_t::get_pair_ref) + ) + auto keys () const HB_AUTO_RETURN + ( + + hb_array (items, mask ? mask + 1 : 0) + | hb_filter (&item_t::is_real) + | hb_map (&item_t::key) + | hb_map (hb_ridentity) + ) + auto keys_ref () const HB_AUTO_RETURN + ( + + hb_array (items, mask ? mask + 1 : 0) + | hb_filter (&item_t::is_real) + | hb_map (&item_t::key) + ) + auto values () const HB_AUTO_RETURN + ( + + hb_array (items, mask ? mask + 1 : 0) + | hb_filter (&item_t::is_real) + | hb_map (&item_t::value) + | hb_map (hb_ridentity) + ) + auto values_ref () const HB_AUTO_RETURN + ( + + hb_array (items, mask ? mask + 1 : 0) + | hb_filter (&item_t::is_real) + | hb_map (&item_t::value) + ) + + /* Sink interface. */ + hb_hashmap_t& operator << (const hb_pair_t& v) + { set (v.first, v.second); return *this; } + + protected: + + template + bool set_with_hash (K key, uint32_t hash, VV&& value, bool is_delete=false) + { + if (unlikely (!successful)) return false; + if (unlikely ((occupancy + occupancy / 2) >= mask && !resize ())) return false; + unsigned int i = bucket_for_hash (key, hash); + + if (is_delete && items[i].key != key) + return true; /* Trying to delete non-existent key. */ + + if (items[i].is_used ()) + { + occupancy--; + if (!items[i].is_tombstone ()) + population--; + } + + items[i].key = key; + items[i].value = std::forward (value); + items[i].hash = hash; + items[i].set_used (true); + items[i].set_tombstone (is_delete); + + occupancy++; + if (!is_delete) + population++; + + return true; + } + + unsigned int bucket_for (const K &key) const + { + return bucket_for_hash (key, hb_hash (key)); + } + + unsigned int bucket_for_hash (const K &key, uint32_t hash) const + { + hash &= 0x3FFFFFFF; // We only store lower 30bit of hash + unsigned int i = hash % prime; + unsigned int step = 0; + unsigned int tombstone = (unsigned) -1; + while (items[i].is_used ()) + { + if (items[i].hash == hash && items[i] == key) + return i; + if (tombstone == (unsigned) -1 && items[i].is_tombstone ()) + tombstone = i; + i = (i + ++step) & mask; + } + return tombstone == (unsigned) -1 ? i : tombstone; + } + + static unsigned int prime_for (unsigned int shift) + { + /* Following comment and table copied from glib. */ + /* Each table size has an associated prime modulo (the first prime + * lower than the table size) used to find the initial bucket. Probing + * then works modulo 2^n. The prime modulo is necessary to get a + * good distribution with poor hash functions. + */ + /* Not declaring static to make all kinds of compilers happy... */ + /*static*/ const unsigned int prime_mod [32] = + { + 1, /* For 1 << 0 */ + 2, + 3, + 7, + 13, + 31, + 61, + 127, + 251, + 509, + 1021, + 2039, + 4093, + 8191, + 16381, + 32749, + 65521, /* For 1 << 16 */ + 131071, + 262139, + 524287, + 1048573, + 2097143, + 4194301, + 8388593, + 16777213, + 33554393, + 67108859, + 134217689, + 268435399, + 536870909, + 1073741789, + 2147483647 /* For 1 << 31 */ + }; + + if (unlikely (shift >= ARRAY_LENGTH (prime_mod))) + return prime_mod[ARRAY_LENGTH (prime_mod) - 1]; + + return prime_mod[shift]; + } +}; + +/* + * hb_map_t + */ + +struct hb_map_t : hb_hashmap_t +{ + using hashmap = hb_hashmap_t; + + ~hb_map_t () = default; + hb_map_t () : hashmap () {} + hb_map_t (const hb_map_t &o) : hashmap ((hashmap &) o) {} + hb_map_t (hb_map_t &&o) : hashmap (std::move ((hashmap &) o)) {} + hb_map_t& operator= (const hb_map_t&) = default; + hb_map_t& operator= (hb_map_t&&) = default; + hb_map_t (std::initializer_list> lst) : hashmap (lst) {} + template + hb_map_t (const Iterable &o) : hashmap (o) {} +}; + +template +static inline +hb_hashmap_t* hb_hashmap_create () +{ + using hashmap = hb_hashmap_t; + hashmap* map; + if (!(map = hb_object_create ())) + return nullptr; + + return map; +} + +template +static inline +void hb_hashmap_destroy (hb_hashmap_t* map) +{ + if (!hb_object_destroy (map)) + return; + + hb_free (map); +} + +namespace hb { + +template +struct vtable> +{ + static constexpr auto destroy = hb_hashmap_destroy; +}; + +} + + +#endif /* HB_MAP_HH */ diff --git a/libs/harfbuzz/src/hb-meta.hh b/libs/harfbuzz/src/hb-meta.hh new file mode 100644 index 00000000..52a6791e --- /dev/null +++ b/libs/harfbuzz/src/hb-meta.hh @@ -0,0 +1,239 @@ +/* + * Copyright © 2018 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 HB_META_HH +#define HB_META_HH + +#include "hb.hh" + +#include +#include +#include + + +/* + * C++ template meta-programming & fundamentals used with them. + */ + +/* Void! For when we need a expression-type of void. */ +struct hb_empty_t {}; + +/* https://en.cppreference.com/w/cpp/types/void_t */ +template struct _hb_void_t { typedef void type; }; +template using hb_void_t = typename _hb_void_t::type; + +template struct _hb_head_t { typedef Head type; }; +template using hb_head_t = typename _hb_head_t::type; + +template struct hb_integral_constant { static constexpr T value = v; }; +template using hb_bool_constant = hb_integral_constant; +using hb_true_type = hb_bool_constant; +using hb_false_type = hb_bool_constant; + +/* Static-assert as expression. */ +template struct static_assert_expr; +template <> struct static_assert_expr : hb_false_type {}; +#define static_assert_expr(C) static_assert_expr::value + +/* Basic type SFINAE. */ + +template struct hb_enable_if {}; +template struct hb_enable_if { typedef T type; }; +#define hb_enable_if(Cond) typename hb_enable_if<(Cond)>::type* = nullptr +/* Concepts/Requires alias: */ +#define hb_requires(Cond) hb_enable_if((Cond)) + +template struct hb_is_same : hb_false_type {}; +template struct hb_is_same : hb_true_type {}; +#define hb_is_same(T, T2) hb_is_same::value + +/* Function overloading SFINAE and priority. */ + +#define HB_RETURN(Ret, E) -> hb_head_t { return (E); } +#define HB_AUTO_RETURN(E) -> decltype ((E)) { return (E); } +#define HB_VOID_RETURN(E) -> hb_void_t { (E); } + +template struct hb_priority : hb_priority {}; +template <> struct hb_priority<0> {}; +#define hb_prioritize hb_priority<16> () + +#define HB_FUNCOBJ(x) static_const x HB_UNUSED + + +template struct hb_type_identity_t { typedef T type; }; +template using hb_type_identity = typename hb_type_identity_t::type; + +template static inline T hb_declval (); +#define hb_declval(T) (hb_declval ()) + +template struct hb_match_const : hb_type_identity_t, hb_false_type {}; +template struct hb_match_const : hb_type_identity_t, hb_true_type {}; +template using hb_remove_const = typename hb_match_const::type; + +template struct hb_match_reference : hb_type_identity_t, hb_false_type {}; +template struct hb_match_reference : hb_type_identity_t, hb_true_type {}; +template struct hb_match_reference : hb_type_identity_t, hb_true_type {}; +template using hb_remove_reference = typename hb_match_reference::type; +template auto _hb_try_add_lvalue_reference (hb_priority<1>) -> hb_type_identity; +template auto _hb_try_add_lvalue_reference (hb_priority<0>) -> hb_type_identity; +template using hb_add_lvalue_reference = decltype (_hb_try_add_lvalue_reference (hb_prioritize)); +template auto _hb_try_add_rvalue_reference (hb_priority<1>) -> hb_type_identity; +template auto _hb_try_add_rvalue_reference (hb_priority<0>) -> hb_type_identity; +template using hb_add_rvalue_reference = decltype (_hb_try_add_rvalue_reference (hb_prioritize)); + +template struct hb_match_pointer : hb_type_identity_t, hb_false_type {}; +template struct hb_match_pointer : hb_type_identity_t, hb_true_type {}; +template using hb_remove_pointer = typename hb_match_pointer::type; +template auto _hb_try_add_pointer (hb_priority<1>) -> hb_type_identity*>; +template auto _hb_try_add_pointer (hb_priority<1>) -> hb_type_identity; +template using hb_add_pointer = decltype (_hb_try_add_pointer (hb_prioritize)); + + +/* TODO Add feature-parity to std::decay. */ +template using hb_decay = hb_remove_const>; + +#define hb_is_convertible(From,To) std::is_convertible::value + +template +using hb_is_cr_convertible = hb_bool_constant< + hb_is_same (hb_decay, hb_decay) && + (!std::is_const::value || std::is_const::value) && + (!std::is_reference::value || std::is_const::value || std::is_reference::value) +>; +#define hb_is_cr_convertible(From,To) hb_is_cr_convertible::value + + +struct +{ + template constexpr auto + operator () (T&& v) const HB_AUTO_RETURN (std::forward (v)) + + template constexpr auto + operator () (T *v) const HB_AUTO_RETURN (*v) + + template constexpr auto + operator () (const hb::shared_ptr& v) const HB_AUTO_RETURN (*v) + + template constexpr auto + operator () (hb::shared_ptr& v) const HB_AUTO_RETURN (*v) + + template constexpr auto + operator () (const hb::unique_ptr& v) const HB_AUTO_RETURN (*v) + + template constexpr auto + operator () (hb::unique_ptr& v) const HB_AUTO_RETURN (*v) +} +HB_FUNCOBJ (hb_deref); + +template +struct hb_reference_wrapper +{ + hb_reference_wrapper (T v) : v (v) {} + bool operator == (const hb_reference_wrapper& o) const { return v == o.v; } + bool operator != (const hb_reference_wrapper& o) const { return v != o.v; } + operator T () const { return v; } + T get () const { return v; } + T v; +}; +template +struct hb_reference_wrapper +{ + hb_reference_wrapper (T& v) : v (std::addressof (v)) {} + bool operator == (const hb_reference_wrapper& o) const { return v == o.v; } + bool operator != (const hb_reference_wrapper& o) const { return v != o.v; } + operator T& () const { return *v; } + T& get () const { return *v; } + T* v; +}; + + +/* Type traits */ + +template struct hb_int_min; +template <> struct hb_int_min : hb_integral_constant {}; +template <> struct hb_int_min : hb_integral_constant {}; +template <> struct hb_int_min : hb_integral_constant {}; +template <> struct hb_int_min : hb_integral_constant {}; +template <> struct hb_int_min : hb_integral_constant {}; +template <> struct hb_int_min : hb_integral_constant {}; +template <> struct hb_int_min : hb_integral_constant {}; +template <> struct hb_int_min : hb_integral_constant {}; +template <> struct hb_int_min : hb_integral_constant {}; +template <> struct hb_int_min : hb_integral_constant {}; +template <> struct hb_int_min : hb_integral_constant {}; +template struct hb_int_min : hb_integral_constant {}; +#define hb_int_min(T) hb_int_min::value +template struct hb_int_max; +template <> struct hb_int_max : hb_integral_constant {}; +template <> struct hb_int_max : hb_integral_constant {}; +template <> struct hb_int_max : hb_integral_constant {}; +template <> struct hb_int_max : hb_integral_constant {}; +template <> struct hb_int_max : hb_integral_constant {}; +template <> struct hb_int_max : hb_integral_constant {}; +template <> struct hb_int_max : hb_integral_constant {}; +template <> struct hb_int_max : hb_integral_constant {}; +template <> struct hb_int_max : hb_integral_constant {}; +template <> struct hb_int_max : hb_integral_constant {}; +template <> struct hb_int_max : hb_integral_constant {}; +#define hb_int_max(T) hb_int_max::value + +#if defined(__GNUC__) && __GNUC__ < 5 && !defined(__clang__) +#define hb_is_trivially_copyable(T) __has_trivial_copy(T) +#define hb_is_trivially_copy_assignable(T) __has_trivial_assign(T) +#define hb_is_trivially_constructible(T) __has_trivial_constructor(T) +#define hb_is_trivially_copy_constructible(T) __has_trivial_copy_constructor(T) +#define hb_is_trivially_destructible(T) __has_trivial_destructor(T) +#else +#define hb_is_trivially_copyable(T) std::is_trivially_copyable::value +#define hb_is_trivially_copy_assignable(T) std::is_trivially_copy_assignable::value +#define hb_is_trivially_constructible(T) std::is_trivially_constructible::value +#define hb_is_trivially_copy_constructible(T) std::is_trivially_copy_constructible::value +#define hb_is_trivially_destructible(T) std::is_trivially_destructible::value +#endif + +/* Class traits. */ + +#define HB_DELETE_COPY_ASSIGN(TypeName) \ + TypeName(const TypeName&) = delete; \ + void operator=(const TypeName&) = delete +#define HB_DELETE_CREATE_COPY_ASSIGN(TypeName) \ + TypeName() = delete; \ + TypeName(const TypeName&) = delete; \ + void operator=(const TypeName&) = delete + +/* hb_unwrap_type (T) + * If T has no T::type, returns T. Otherwise calls itself on T::type recursively. + */ + +template +struct _hb_unwrap_type : hb_type_identity_t {}; +template +struct _hb_unwrap_type> : _hb_unwrap_type {}; +template +using hb_unwrap_type = _hb_unwrap_type; +#define hb_unwrap_type(T) typename hb_unwrap_type::type + +#endif /* HB_META_HH */ diff --git a/libs/harfbuzz/src/hb-ms-feature-ranges.hh b/libs/harfbuzz/src/hb-ms-feature-ranges.hh new file mode 100644 index 00000000..d40fdeaa --- /dev/null +++ b/libs/harfbuzz/src/hb-ms-feature-ranges.hh @@ -0,0 +1,229 @@ +/* + * Copyright © 2011,2012,2013 Google, Inc. + * Copyright © 2021 Khaled Hosny + * + * 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 HB_MS_FEATURE_RANGES_HH +#define HB_MS_FEATURE_RANGES_HH + +#include "hb.hh" + +typedef struct hb_ms_feature_t { + uint32_t tag_le; + uint32_t value; +} hb_ms_feature_t; + +typedef struct hb_ms_features_t { + hb_ms_feature_t *features; + uint32_t num_features; +} hb_ms_features_t; + +struct hb_ms_active_feature_t { + hb_ms_feature_t fea; + unsigned int order; + + HB_INTERNAL static int cmp (const void *pa, const void *pb) { + const auto *a = (const hb_ms_active_feature_t *) pa; + const auto *b = (const hb_ms_active_feature_t *) pb; + return a->fea.tag_le < b->fea.tag_le ? -1 : a->fea.tag_le > b->fea.tag_le ? 1 : + a->order < b->order ? -1 : a->order > b->order ? 1 : + a->fea.value < b->fea.value ? -1 : a->fea.value > b->fea.value ? 1 : + 0; + } + bool operator== (const hb_ms_active_feature_t& f) const + { return cmp (this, &f) == 0; } +}; + +struct hb_ms_feature_event_t { + unsigned int index; + bool start; + hb_ms_active_feature_t feature; + + HB_INTERNAL static int cmp (const void *pa, const void *pb) + { + const auto *a = (const hb_ms_feature_event_t *) pa; + const auto *b = (const hb_ms_feature_event_t *) pb; + return a->index < b->index ? -1 : a->index > b->index ? 1 : + a->start < b->start ? -1 : a->start > b->start ? 1 : + hb_ms_active_feature_t::cmp (&a->feature, &b->feature); + } +}; + +struct hb_ms_range_record_t { + hb_ms_features_t features; + unsigned int index_first; /* == start */ + unsigned int index_last; /* == end - 1 */ +}; + +static inline bool +hb_ms_setup_features (const hb_feature_t *features, + unsigned int num_features, + hb_vector_t &feature_records, /* OUT */ + hb_vector_t &range_records /* OUT */) +{ + feature_records.shrink(0); + range_records.shrink(0); + + /* Sort features by start/end events. */ + hb_vector_t feature_events; + for (unsigned int i = 0; i < num_features; i++) + { + hb_ms_active_feature_t feature; + feature.fea.tag_le = hb_uint32_swap (features[i].tag); + feature.fea.value = features[i].value; + feature.order = i; + + hb_ms_feature_event_t *event; + + event = feature_events.push (); + event->index = features[i].start; + event->start = true; + event->feature = feature; + + event = feature_events.push (); + event->index = features[i].end; + event->start = false; + event->feature = feature; + } + feature_events.qsort (); + /* Add a strategic final event. */ + { + hb_ms_active_feature_t feature; + feature.fea.tag_le = 0; + feature.fea.value = 0; + feature.order = num_features + 1; + + auto *event = feature_events.push (); + event->index = 0; /* This value does magic. */ + event->start = false; + event->feature = feature; + } + + /* Scan events and save features for each range. */ + hb_vector_t active_features; + unsigned int last_index = 0; + for (unsigned int i = 0; i < feature_events.length; i++) + { + auto *event = &feature_events[i]; + + if (event->index != last_index) + { + /* Save a snapshot of active features and the range. */ + auto *range = range_records.push (); + auto offset = feature_records.length; + + active_features.qsort (); + for (unsigned int j = 0; j < active_features.length; j++) + { + if (!j || active_features[j].fea.tag_le != feature_records[feature_records.length - 1].tag_le) + { + feature_records.push (active_features[j].fea); + } + else + { + /* Overrides value for existing feature. */ + feature_records[feature_records.length - 1].value = active_features[j].fea.value; + } + } + + /* Will convert to pointer after all is ready, since feature_records.array + * may move as we grow it. */ + range->features.features = reinterpret_cast (offset); + range->features.num_features = feature_records.length - offset; + range->index_first = last_index; + range->index_last = event->index - 1; + + last_index = event->index; + } + + if (event->start) + { + active_features.push (event->feature); + } + else + { + auto *feature = active_features.lsearch (event->feature); + if (feature) + active_features.remove (feature - active_features.arrayZ); + } + } + + if (!range_records.length) /* No active feature found. */ + num_features = 0; + + /* Fixup the pointers. */ + for (unsigned int i = 0; i < range_records.length; i++) + { + auto *range = &range_records[i]; + range->features.features = (hb_ms_feature_t *) feature_records + reinterpret_cast (range->features.features); + } + + return !!num_features; +} + +static inline void +hb_ms_make_feature_ranges (hb_vector_t &feature_records, + hb_vector_t &range_records, + unsigned int chars_offset, + unsigned int chars_len, + uint16_t *log_clusters, + hb_vector_t &range_features, /* OUT */ + hb_vector_t &range_counts /* OUT */) +{ + range_features.shrink (0); + range_counts.shrink (0); + + auto *last_range = &range_records[0]; + for (unsigned int i = chars_offset; i < chars_len; i++) + { + auto *range = last_range; + while (log_clusters[i] < range->index_first) + range--; + while (log_clusters[i] > range->index_last) + range++; + if (!range_features.length || + &range->features != range_features[range_features.length - 1]) + { + auto **features = range_features.push (); + auto *c = range_counts.push (); + if (unlikely (!features || !c)) + { + range_features.shrink (0); + range_counts.shrink (0); + break; + } + *features = &range->features; + *c = 1; + } + else + { + range_counts[range_counts.length - 1]++; + } + + last_range = range; + } +} + +#endif /* HB_MS_FEATURE_RANGES_HH */ diff --git a/libs/harfbuzz/src/hb-mutex.hh b/libs/harfbuzz/src/hb-mutex.hh new file mode 100644 index 00000000..6914b224 --- /dev/null +++ b/libs/harfbuzz/src/hb-mutex.hh @@ -0,0 +1,118 @@ +/* + * Copyright © 2007 Chris Wilson + * Copyright © 2009,2010 Red Hat, Inc. + * Copyright © 2011,2012 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. + * + * Contributor(s): + * Chris Wilson + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_MUTEX_HH +#define HB_MUTEX_HH + +#include "hb.hh" + + +/* mutex */ + +/* We need external help for these */ + +#if defined(hb_mutex_impl_init) \ + && defined(hb_mutex_impl_lock) \ + && defined(hb_mutex_impl_unlock) \ + && defined(hb_mutex_impl_finish) + +/* Defined externally, i.e. in config.h; must have typedef'ed hb_mutex_impl_t as well. */ + + +#elif !defined(HB_NO_MT) && !defined(HB_MUTEX_IMPL_STD_MUTEX) && (defined(HAVE_PTHREAD) || defined(__APPLE__)) + +#include +typedef pthread_mutex_t hb_mutex_impl_t; +#define hb_mutex_impl_init(M) pthread_mutex_init (M, nullptr) +#define hb_mutex_impl_lock(M) pthread_mutex_lock (M) +#define hb_mutex_impl_unlock(M) pthread_mutex_unlock (M) +#define hb_mutex_impl_finish(M) pthread_mutex_destroy (M) + + +#elif !defined(HB_NO_MT) && !defined(HB_MUTEX_IMPL_STD_MUTEX) && defined(_WIN32) + +typedef CRITICAL_SECTION hb_mutex_impl_t; +#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +#define hb_mutex_impl_init(M) InitializeCriticalSectionEx (M, 0, 0) +#else +#define hb_mutex_impl_init(M) InitializeCriticalSection (M) +#endif +#define hb_mutex_impl_lock(M) EnterCriticalSection (M) +#define hb_mutex_impl_unlock(M) LeaveCriticalSection (M) +#define hb_mutex_impl_finish(M) DeleteCriticalSection (M) + + +#elif !defined(HB_NO_MT) + +#include +typedef std::mutex hb_mutex_impl_t; +#define hb_mutex_impl_init(M) HB_STMT_START { new (M) hb_mutex_impl_t; } HB_STMT_END +#define hb_mutex_impl_lock(M) (M)->lock () +#define hb_mutex_impl_unlock(M) (M)->unlock () +#define hb_mutex_impl_finish(M) HB_STMT_START { (M)->~hb_mutex_impl_t(); } HB_STMT_END + + +#else /* defined(HB_NO_MT) */ + +typedef int hb_mutex_impl_t; +#define hb_mutex_impl_init(M) HB_STMT_START {} HB_STMT_END +#define hb_mutex_impl_lock(M) HB_STMT_START {} HB_STMT_END +#define hb_mutex_impl_unlock(M) HB_STMT_START {} HB_STMT_END +#define hb_mutex_impl_finish(M) HB_STMT_START {} HB_STMT_END + + +#endif + + +struct hb_mutex_t +{ + /* Create space for, but do not initialize m. */ + alignas(hb_mutex_impl_t) char m[sizeof (hb_mutex_impl_t)]; + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-align" + void init () { hb_mutex_impl_init ((hb_mutex_impl_t *) m); } + void lock () { hb_mutex_impl_lock ((hb_mutex_impl_t *) m); } + void unlock () { hb_mutex_impl_unlock ((hb_mutex_impl_t *) m); } + void fini () { hb_mutex_impl_finish ((hb_mutex_impl_t *) m); } +#pragma GCC diagnostic pop +}; + +struct hb_lock_t +{ + hb_lock_t (hb_mutex_t &mutex_) : mutex (mutex_) { mutex.lock (); } + ~hb_lock_t () { mutex.unlock (); } + private: + hb_mutex_t &mutex; +}; + + +#endif /* HB_MUTEX_HH */ diff --git a/libs/harfbuzz/src/hb-null.hh b/libs/harfbuzz/src/hb-null.hh new file mode 100644 index 00000000..0d7f4da7 --- /dev/null +++ b/libs/harfbuzz/src/hb-null.hh @@ -0,0 +1,226 @@ +/* + * Copyright © 2018 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 HB_NULL_HH +#define HB_NULL_HH + +#include "hb.hh" +#include "hb-meta.hh" + + +/* + * Static pools + */ + +/* Global nul-content Null pool. Enlarge as necessary. */ + +#define HB_NULL_POOL_SIZE 448 + +template +struct _hb_has_min_size : hb_false_type {}; +template +struct _hb_has_min_size> + : hb_true_type {}; +template +using hb_has_min_size = _hb_has_min_size; +#define hb_has_min_size(T) hb_has_min_size::value + +template +struct _hb_has_null_size : hb_false_type {}; +template +struct _hb_has_null_size> + : hb_true_type {}; +template +using hb_has_null_size = _hb_has_null_size; +#define hb_has_null_size(T) hb_has_null_size::value + +/* Use SFINAE to sniff whether T has min_size; in which case return the larger + * of sizeof(T) and T::null_size, otherwise return sizeof(T). + * + * The main purpose of this is to let structs communicate that they are not nullable, + * by defining min_size but *not* null_size. */ + +/* The hard way... + * https://stackoverflow.com/questions/7776448/sfinae-tried-with-bool-gives-compiler-error-template-argument-tvalue-invol + */ + +template +struct _hb_null_size : hb_integral_constant {}; +template +struct _hb_null_size> + : hb_integral_constant T::null_size ? sizeof (T) : T::null_size)> {}; +template +using hb_null_size = _hb_null_size; +#define hb_null_size(T) hb_null_size::value + +/* These doesn't belong here, but since is copy/paste from above, put it here. */ + +/* hb_static_size (T) + * Returns T::static_size if T::min_size is defined, or sizeof (T) otherwise. */ + +template +struct _hb_static_size : hb_integral_constant {}; +template +struct _hb_static_size> : hb_integral_constant {}; +template +using hb_static_size = _hb_static_size; +#define hb_static_size(T) hb_static_size::value + +template +struct _hb_min_size : hb_integral_constant {}; +template +struct _hb_min_size> : hb_integral_constant {}; +template +using hb_min_size = _hb_min_size; +#define hb_min_size(T) hb_min_size::value + + +/* + * Null() + */ + +extern HB_INTERNAL +uint64_t const _hb_NullPool[(HB_NULL_POOL_SIZE + sizeof (uint64_t) - 1) / sizeof (uint64_t)]; + +/* Generic nul-content Null objects. */ +template +struct Null { + static Type const & get_null () + { + static_assert (hb_null_size (Type) <= HB_NULL_POOL_SIZE, "Increase HB_NULL_POOL_SIZE."); + return *reinterpret_cast (_hb_NullPool); + } +}; +template +struct NullHelper +{ + typedef hb_remove_const> Type; + static const Type & get_null () { return Null::get_null (); } +}; +#define Null(Type) NullHelper::get_null () + +/* Specializations for arbitrary-content Null objects expressed in bytes. */ +#define DECLARE_NULL_NAMESPACE_BYTES(Namespace, Type) \ + } /* Close namespace. */ \ + extern HB_INTERNAL const unsigned char _hb_Null_##Namespace##_##Type[hb_null_size (Namespace::Type)]; \ + template <> \ + struct Null { \ + static Namespace::Type const & get_null () { \ + return *reinterpret_cast (_hb_Null_##Namespace##_##Type); \ + } \ + }; \ + namespace Namespace { \ + static_assert (true, "") /* Require semicolon after. */ +#define DECLARE_NULL_NAMESPACE_BYTES_TEMPLATE1(Namespace, Type, Size) \ + } /* Close namespace. */ \ + extern HB_INTERNAL const unsigned char _hb_Null_##Namespace##_##Type[Size]; \ + template \ + struct Null> { \ + static Namespace::Type const & get_null () { \ + return *reinterpret_cast *> (_hb_Null_##Namespace##_##Type); \ + } \ + }; \ + namespace Namespace { \ + static_assert (true, "") /* Require semicolon after. */ +#define DEFINE_NULL_NAMESPACE_BYTES(Namespace, Type) \ + const unsigned char _hb_Null_##Namespace##_##Type[sizeof (_hb_Null_##Namespace##_##Type)] + +/* Specializations for arbitrary-content Null objects expressed as struct initializer. */ +#define DECLARE_NULL_INSTANCE(Type) \ + extern HB_INTERNAL const Type _hb_Null_##Type; \ + template <> \ + struct Null { \ + static Type const & get_null () { \ + return _hb_Null_##Type; \ + } \ + }; \ + static_assert (true, "") /* Require semicolon after. */ +#define DEFINE_NULL_INSTANCE(Type) \ + const Type _hb_Null_##Type + +/* Global writable pool. Enlarge as necessary. */ + +/* To be fully correct, CrapPool must be thread_local. However, we do not rely on CrapPool + * for correct operation. It only exist to catch and divert program logic bugs instead of + * causing bad memory access. So, races there are not actually introducing incorrectness + * in the code. Has ~12kb binary size overhead to have it, also clang build fails with it. */ +extern HB_INTERNAL +/*thread_local*/ uint64_t _hb_CrapPool[(HB_NULL_POOL_SIZE + sizeof (uint64_t) - 1) / sizeof (uint64_t)]; + +/* CRAP pool: Common Region for Access Protection. */ +template +static inline Type& Crap () { + static_assert (hb_null_size (Type) <= HB_NULL_POOL_SIZE, "Increase HB_NULL_POOL_SIZE."); + Type *obj = reinterpret_cast (_hb_CrapPool); + memcpy (obj, &Null (Type), sizeof (*obj)); + return *obj; +} +template +struct CrapHelper +{ + typedef hb_remove_const> Type; + static Type & get_crap () { return Crap (); } +}; +#define Crap(Type) CrapHelper::get_crap () + +template +struct CrapOrNullHelper { + static Type & get () { return Crap (Type); } +}; +template +struct CrapOrNullHelper { + static const Type & get () { return Null (Type); } +}; +#define CrapOrNull(Type) CrapOrNullHelper::get () + + +/* + * hb_nonnull_ptr_t + */ + +template +struct hb_nonnull_ptr_t +{ + typedef hb_remove_pointer

T; + + hb_nonnull_ptr_t (T *v_ = nullptr) : v (v_) {} + T * operator = (T *v_) { return v = v_; } + T * operator -> () const { return get (); } + T & operator * () const { return *get (); } + T ** operator & () const { return &v; } + /* Only auto-cast to const types. */ + template operator const C * () const { return get (); } + operator const char * () const { return (const char *) get (); } + T * get () const { return v ? v : const_cast (&Null (T)); } + T * get_raw () const { return v; } + + private: + T *v; +}; + + +#endif /* HB_NULL_HH */ diff --git a/libs/harfbuzz/src/hb-number-parser.hh b/libs/harfbuzz/src/hb-number-parser.hh new file mode 100644 index 00000000..1a9dbba6 --- /dev/null +++ b/libs/harfbuzz/src/hb-number-parser.hh @@ -0,0 +1,237 @@ + +#line 1 "hb-number-parser.rl" +/* + * Copyright © 2019 Ebrahim Byagowi + * + * 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. + * + */ + +#ifndef HB_NUMBER_PARSER_HH +#define HB_NUMBER_PARSER_HH + +#include "hb.hh" + + +#line 35 "hb-number-parser.hh" +static const unsigned char _double_parser_trans_keys[] = { + 0u, 0u, 43u, 57u, 46u, 57u, 48u, 57u, 43u, 57u, 48u, 57u, 48u, 101u, 48u, 57u, + 46u, 101u, 0 +}; + +static const char _double_parser_key_spans[] = { + 0, 15, 12, 10, 15, 10, 54, 10, + 56 +}; + +static const unsigned char _double_parser_index_offsets[] = { + 0, 0, 16, 29, 40, 56, 67, 122, + 133 +}; + +static const char _double_parser_indicies[] = { + 0, 1, 2, 3, 1, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, + 1, 3, 1, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 1, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, + 1, 6, 1, 7, 1, 1, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 1, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 1, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 9, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 9, 1, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 1, 3, 1, + 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 9, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 9, 1, 0 +}; + +static const char _double_parser_trans_targs[] = { + 2, 0, 2, 3, 8, 6, 5, 5, + 7, 4 +}; + +static const char _double_parser_trans_actions[] = { + 0, 0, 1, 0, 2, 3, 0, 4, + 5, 0 +}; + +static const int double_parser_start = 1; +static const int double_parser_first_final = 6; +static const int double_parser_error = 0; + +static const int double_parser_en_main = 1; + + +#line 68 "hb-number-parser.rl" + + +/* Works only for n < 512 */ +static inline double +_pow10 (unsigned exponent) +{ + static const double _powers_of_10[] = + { + 1.0e+256, + 1.0e+128, + 1.0e+64, + 1.0e+32, + 1.0e+16, + 1.0e+8, + 10000., + 100., + 10. + }; + unsigned mask = 1 << (ARRAY_LENGTH (_powers_of_10) - 1); + double result = 1; + for (const double *power = _powers_of_10; mask; ++power, mask >>= 1) + if (exponent & mask) result *= *power; + return result; +} + +/* a variant of strtod that also gets end of buffer in its second argument */ +static inline double +strtod_rl (const char *p, const char **end_ptr /* IN/OUT */) +{ + double value = 0; + double frac = 0; + double frac_count = 0; + unsigned exp = 0; + bool neg = false, exp_neg = false, exp_overflow = false; + const unsigned long long MAX_FRACT = 0xFFFFFFFFFFFFFull; /* 2^52-1 */ + const unsigned MAX_EXP = 0x7FFu; /* 2^11-1 */ + + const char *pe = *end_ptr; + while (p < pe && ISSPACE (*p)) + p++; + + int cs; + +#line 139 "hb-number-parser.hh" + { + cs = double_parser_start; + } + +#line 144 "hb-number-parser.hh" + { + int _slen; + int _trans; + const unsigned char *_keys; + const char *_inds; + if ( p == pe ) + goto _test_eof; + if ( cs == 0 ) + goto _out; +_resume: + _keys = _double_parser_trans_keys + (cs<<1); + _inds = _double_parser_indicies + _double_parser_index_offsets[cs]; + + _slen = _double_parser_key_spans[cs]; + _trans = _inds[ _slen > 0 && _keys[0] <=(*p) && + (*p) <= _keys[1] ? + (*p) - _keys[0] : _slen ]; + + cs = _double_parser_trans_targs[_trans]; + + if ( _double_parser_trans_actions[_trans] == 0 ) + goto _again; + + switch ( _double_parser_trans_actions[_trans] ) { + case 1: +#line 37 "hb-number-parser.rl" + { neg = true; } + break; + case 4: +#line 38 "hb-number-parser.rl" + { exp_neg = true; } + break; + case 2: +#line 40 "hb-number-parser.rl" + { + value = value * 10. + ((*p) - '0'); +} + break; + case 3: +#line 43 "hb-number-parser.rl" + { + if (likely (frac <= MAX_FRACT / 10)) + { + frac = frac * 10. + ((*p) - '0'); + ++frac_count; + } +} + break; + case 5: +#line 50 "hb-number-parser.rl" + { + if (likely (exp * 10 + ((*p) - '0') <= MAX_EXP)) + exp = exp * 10 + ((*p) - '0'); + else + exp_overflow = true; +} + break; +#line 202 "hb-number-parser.hh" + } + +_again: + if ( cs == 0 ) + goto _out; + if ( ++p != pe ) + goto _resume; + _test_eof: {} + _out: {} + } + +#line 113 "hb-number-parser.rl" + + + *end_ptr = p; + + if (frac_count) value += frac / _pow10 (frac_count); + if (neg) value *= -1.; + + if (unlikely (exp_overflow)) + { + if (value == 0) return value; + if (exp_neg) return neg ? -DBL_MIN : DBL_MIN; + else return neg ? -DBL_MAX : DBL_MAX; + } + + if (exp) + { + if (exp_neg) value /= _pow10 (exp); + else value *= _pow10 (exp); + } + + return value; +} + +#endif /* HB_NUMBER_PARSER_HH */ diff --git a/libs/harfbuzz/src/hb-number-parser.rl b/libs/harfbuzz/src/hb-number-parser.rl new file mode 100644 index 00000000..c6c4a3ba --- /dev/null +++ b/libs/harfbuzz/src/hb-number-parser.rl @@ -0,0 +1,136 @@ +/* + * Copyright © 2019 Ebrahim Byagowi + * + * 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. + * + */ + +#ifndef HB_NUMBER_PARSER_HH +#define HB_NUMBER_PARSER_HH + +#include "hb.hh" + +%%{ + +machine double_parser; +alphtype unsigned char; +write data; + +action see_neg { neg = true; } +action see_exp_neg { exp_neg = true; } + +action add_int { + value = value * 10. + (fc - '0'); +} +action add_frac { + if (likely (frac <= MAX_FRACT / 10)) + { + frac = frac * 10. + (fc - '0'); + ++frac_count; + } +} +action add_exp { + if (likely (exp * 10 + (fc - '0') <= MAX_EXP)) + exp = exp * 10 + (fc - '0'); + else + exp_overflow = true; +} + +num = [0-9]+; + +main := ( + ( + (('+'|'-'@see_neg)? num @add_int) ('.' num @add_frac)? + | + (('+'|'-'@see_neg)? '.' num @add_frac) + ) + (('e'|'E') (('+'|'-'@see_exp_neg)? num @add_exp))? +); + +}%% + +/* Works only for n < 512 */ +static inline double +_pow10 (unsigned exponent) +{ + static const double _powers_of_10[] = + { + 1.0e+256, + 1.0e+128, + 1.0e+64, + 1.0e+32, + 1.0e+16, + 1.0e+8, + 10000., + 100., + 10. + }; + unsigned mask = 1 << (ARRAY_LENGTH (_powers_of_10) - 1); + double result = 1; + for (const double *power = _powers_of_10; mask; ++power, mask >>= 1) + if (exponent & mask) result *= *power; + return result; +} + +/* a variant of strtod that also gets end of buffer in its second argument */ +static inline double +strtod_rl (const char *p, const char **end_ptr /* IN/OUT */) +{ + double value = 0; + double frac = 0; + double frac_count = 0; + unsigned exp = 0; + bool neg = false, exp_neg = false, exp_overflow = false; + const unsigned long long MAX_FRACT = 0xFFFFFFFFFFFFFull; /* 2^52-1 */ + const unsigned MAX_EXP = 0x7FFu; /* 2^11-1 */ + + const char *pe = *end_ptr; + while (p < pe && ISSPACE (*p)) + p++; + + int cs; + %%{ + write init; + write exec; + }%% + + *end_ptr = p; + + if (frac_count) value += frac / _pow10 (frac_count); + if (neg) value *= -1.; + + if (unlikely (exp_overflow)) + { + if (value == 0) return value; + if (exp_neg) return neg ? -DBL_MIN : DBL_MIN; + else return neg ? -DBL_MAX : DBL_MAX; + } + + if (exp) + { + if (exp_neg) value /= _pow10 (exp); + else value *= _pow10 (exp); + } + + return value; +} + +#endif /* HB_NUMBER_PARSER_HH */ diff --git a/libs/harfbuzz/src/hb-number.cc b/libs/harfbuzz/src/hb-number.cc new file mode 100644 index 00000000..6e4f3f7e --- /dev/null +++ b/libs/harfbuzz/src/hb-number.cc @@ -0,0 +1,80 @@ +/* + * Copyright © 2019 Ebrahim Byagowi + * + * 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. + * + */ + +#include "hb.hh" +#include "hb-machinery.hh" +#include "hb-number.hh" +#include "hb-number-parser.hh" + +template +static bool +_parse_number (const char **pp, const char *end, T *pv, + bool whole_buffer, Func f) +{ + char buf[32]; + unsigned len = hb_min (ARRAY_LENGTH (buf) - 1, (unsigned) (end - *pp)); + strncpy (buf, *pp, len); + buf[len] = '\0'; + + char *p = buf; + char *pend = p; + + errno = 0; + *pv = f (p, &pend); + if (unlikely (errno || p == pend || + /* Check if consumed whole buffer if is requested */ + (whole_buffer && pend - p != end - *pp))) + return false; + + *pp += pend - p; + return true; +} + +bool +hb_parse_int (const char **pp, const char *end, int *pv, bool whole_buffer) +{ + return _parse_number (pp, end, pv, whole_buffer, + [] (const char *p, char **end) + { return strtol (p, end, 10); }); +} + +bool +hb_parse_uint (const char **pp, const char *end, unsigned *pv, + bool whole_buffer, int base) +{ + return _parse_number (pp, end, pv, whole_buffer, + [base] (const char *p, char **end) + { return strtoul (p, end, base); }); +} + +bool +hb_parse_double (const char **pp, const char *end, double *pv, bool whole_buffer) +{ + const char *pend = end; + *pv = strtod_rl (*pp, &pend); + if (unlikely (*pp == pend)) return false; + *pp = pend; + return !whole_buffer || end == pend; +} diff --git a/libs/harfbuzz/src/hb-number.hh b/libs/harfbuzz/src/hb-number.hh new file mode 100644 index 00000000..14d1260a --- /dev/null +++ b/libs/harfbuzz/src/hb-number.hh @@ -0,0 +1,41 @@ +/* + * Copyright © 2019 Ebrahim Byagowi + * + * 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. + * + */ + +#ifndef HB_NUMBER_HH +#define HB_NUMBER_HH + +HB_INTERNAL bool +hb_parse_int (const char **pp, const char *end, int *pv, + bool whole_buffer = false); + +HB_INTERNAL bool +hb_parse_uint (const char **pp, const char *end, unsigned int *pv, + bool whole_buffer = false, int base = 10); + +HB_INTERNAL bool +hb_parse_double (const char **pp, const char *end, double *pv, + bool whole_buffer = false); + +#endif /* HB_NUMBER_HH */ diff --git a/libs/harfbuzz/src/hb-object.hh b/libs/harfbuzz/src/hb-object.hh new file mode 100644 index 00000000..9876c292 --- /dev/null +++ b/libs/harfbuzz/src/hb-object.hh @@ -0,0 +1,337 @@ +/* + * Copyright © 2007 Chris Wilson + * Copyright © 2009,2010 Red Hat, Inc. + * Copyright © 2011,2012 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. + * + * Contributor(s): + * Chris Wilson + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_OBJECT_HH +#define HB_OBJECT_HH + +#include "hb.hh" +#include "hb-atomic.hh" +#include "hb-mutex.hh" +#include "hb-vector.hh" + + +/* + * Lockable set + */ + +template +struct hb_lockable_set_t +{ + hb_vector_t items; + + void init () { items.init (); } + + template + item_t *replace_or_insert (T v, lock_t &l, bool replace) + { + l.lock (); + item_t *item = items.lsearch (v); + if (item) { + if (replace) { + item_t old = *item; + *item = v; + l.unlock (); + old.fini (); + } + else { + item = nullptr; + l.unlock (); + } + } else { + item = items.push (v); + l.unlock (); + } + return item; + } + + template + void remove (T v, lock_t &l) + { + l.lock (); + item_t *item = items.lsearch (v); + if (item) + { + item_t old = *item; + *item = items[items.length - 1]; + items.pop (); + l.unlock (); + old.fini (); + } else { + l.unlock (); + } + } + + template + bool find (T v, item_t *i, lock_t &l) + { + l.lock (); + item_t *item = items.lsearch (v); + if (item) + *i = *item; + l.unlock (); + return !!item; + } + + template + item_t *find_or_insert (T v, lock_t &l) + { + l.lock (); + item_t *item = items.find (v); + if (!item) { + item = items.push (v); + } + l.unlock (); + return item; + } + + void fini (lock_t &l) + { + if (!items.length) + { + /* No need to lock. */ + items.fini (); + return; + } + l.lock (); + while (items.length) + { + item_t old = items[items.length - 1]; + items.pop (); + l.unlock (); + old.fini (); + l.lock (); + } + items.fini (); + l.unlock (); + } + +}; + + +/* + * Reference-count. + */ + +struct hb_reference_count_t +{ + mutable hb_atomic_int_t ref_count; + + void init (int v = 1) { ref_count = v; } + int get_relaxed () const { return ref_count; } + int inc () const { return ref_count.inc (); } + int dec () const { return ref_count.dec (); } + void fini () { ref_count = -0x0000DEAD; } + + bool is_inert () const { return !ref_count; } + bool is_valid () const { return ref_count > 0; } +}; + + +/* user_data */ + +struct hb_user_data_array_t +{ + struct hb_user_data_item_t { + hb_user_data_key_t *key; + void *data; + hb_destroy_func_t destroy; + + bool operator == (const hb_user_data_key_t *other_key) const { return key == other_key; } + bool operator == (const hb_user_data_item_t &other) const { return key == other.key; } + + void fini () { if (destroy) destroy (data); } + }; + + hb_mutex_t lock; + hb_lockable_set_t items; + + void init () { lock.init (); items.init (); } + + HB_INTERNAL bool set (hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy, + hb_bool_t replace); + + HB_INTERNAL void *get (hb_user_data_key_t *key); + + void fini () { items.fini (lock); lock.fini (); } +}; + + +/* + * Object header + */ + +struct hb_object_header_t +{ + hb_reference_count_t ref_count; + mutable hb_atomic_int_t writable = 0; + hb_atomic_ptr_t user_data; + + bool is_inert () const { return !ref_count.get_relaxed (); } +}; +#define HB_OBJECT_HEADER_STATIC {} + + +/* + * Object + */ + +template +static inline void hb_object_trace (const Type *obj, const char *function) +{ + DEBUG_MSG (OBJECT, (void *) obj, + "%s refcount=%d", + function, + obj ? obj->header.ref_count.get_relaxed () : 0); +} + +template +static inline Type *hb_object_create (Ts... ds) +{ + Type *obj = (Type *) hb_calloc (1, sizeof (Type)); + + if (unlikely (!obj)) + return obj; + + new (obj) Type (std::forward (ds)...); + + hb_object_init (obj); + hb_object_trace (obj, HB_FUNC); + + return obj; +} +template +static inline void hb_object_init (Type *obj) +{ + obj->header.ref_count.init (); + obj->header.writable = true; + obj->header.user_data.init (); +} +template +static inline bool hb_object_is_valid (const Type *obj) +{ + return likely (obj->header.ref_count.is_valid ()); +} +template +static inline bool hb_object_is_immutable (const Type *obj) +{ + return !obj->header.writable; +} +template +static inline void hb_object_make_immutable (const Type *obj) +{ + obj->header.writable = false; +} +template +static inline Type *hb_object_reference (Type *obj) +{ + hb_object_trace (obj, HB_FUNC); + if (unlikely (!obj || obj->header.is_inert ())) + return obj; + assert (hb_object_is_valid (obj)); + obj->header.ref_count.inc (); + return obj; +} +template +static inline bool hb_object_destroy (Type *obj) +{ + hb_object_trace (obj, HB_FUNC); + if (unlikely (!obj || obj->header.is_inert ())) + return false; + assert (hb_object_is_valid (obj)); + if (obj->header.ref_count.dec () != 1) + return false; + + hb_object_fini (obj); + + if (!std::is_trivially_destructible::value) + obj->~Type (); + + return true; +} +template +static inline void hb_object_fini (Type *obj) +{ + obj->header.ref_count.fini (); /* Do this before user_data */ + hb_user_data_array_t *user_data = obj->header.user_data.get_acquire (); + if (user_data) + { + user_data->fini (); + hb_free (user_data); + obj->header.user_data.set_relaxed (nullptr); + } +} +template +static inline bool hb_object_set_user_data (Type *obj, + hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy, + hb_bool_t replace) +{ + if (unlikely (!obj || obj->header.is_inert ())) + return false; + assert (hb_object_is_valid (obj)); + +retry: + hb_user_data_array_t *user_data = obj->header.user_data.get_acquire (); + if (unlikely (!user_data)) + { + user_data = (hb_user_data_array_t *) hb_calloc (sizeof (hb_user_data_array_t), 1); + if (unlikely (!user_data)) + return false; + user_data->init (); + if (unlikely (!obj->header.user_data.cmpexch (nullptr, user_data))) + { + user_data->fini (); + hb_free (user_data); + goto retry; + } + } + + return user_data->set (key, data, destroy, replace); +} + +template +static inline void *hb_object_get_user_data (Type *obj, + hb_user_data_key_t *key) +{ + if (unlikely (!obj || obj->header.is_inert ())) + return nullptr; + assert (hb_object_is_valid (obj)); + hb_user_data_array_t *user_data = obj->header.user_data.get_acquire (); + if (!user_data) + return nullptr; + return user_data->get (key); +} + + +#endif /* HB_OBJECT_HH */ diff --git a/libs/harfbuzz/src/hb-open-file.hh b/libs/harfbuzz/src/hb-open-file.hh new file mode 100644 index 00000000..6eee5827 --- /dev/null +++ b/libs/harfbuzz/src/hb-open-file.hh @@ -0,0 +1,537 @@ +/* + * Copyright © 2007,2008,2009 Red Hat, Inc. + * Copyright © 2012 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. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_OPEN_FILE_HH +#define HB_OPEN_FILE_HH + +#include "hb-open-type.hh" +#include "hb-ot-head-table.hh" + + +namespace OT { + +/* + * + * The OpenType Font File + * + */ + + +/* + * Organization of an OpenType Font + */ + +struct OpenTypeFontFile; +struct OpenTypeOffsetTable; +struct TTCHeader; + + +typedef struct TableRecord +{ + int cmp (Tag t) const { return -t.cmp (tag); } + + HB_INTERNAL static int cmp (const void *pa, const void *pb) + { + const TableRecord *a = (const TableRecord *) pa; + const TableRecord *b = (const TableRecord *) pb; + return b->cmp (a->tag); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + Tag tag; /* 4-byte identifier. */ + CheckSum checkSum; /* CheckSum for this table. */ + Offset32 offset; /* Offset from beginning of TrueType font + * file. */ + HBUINT32 length; /* Length of this table. */ + public: + DEFINE_SIZE_STATIC (16); +} OpenTypeTable; + +typedef struct OpenTypeOffsetTable +{ + friend struct OpenTypeFontFile; + + unsigned int get_table_count () const { return tables.len; } + const TableRecord& get_table (unsigned int i) const + { return tables[i]; } + unsigned int get_table_tags (unsigned int start_offset, + unsigned int *table_count, /* IN/OUT */ + hb_tag_t *table_tags /* OUT */) const + { + if (table_count) + { + + tables.sub_array (start_offset, table_count) + | hb_map (&TableRecord::tag) + | hb_sink (hb_array (table_tags, *table_count)) + ; + } + return tables.len; + } + bool find_table_index (hb_tag_t tag, unsigned int *table_index) const + { + Tag t; + t = tag; + /* Use lfind for small fonts; there are fonts that have unsorted table entries; + * those tend to work in other tools, so tolerate them. + * https://github.com/harfbuzz/harfbuzz/issues/3065 */ + if (tables.len < 16) + return tables.lfind (t, table_index, HB_NOT_FOUND_STORE, Index::NOT_FOUND_INDEX); + else + return tables.bfind (t, table_index, HB_NOT_FOUND_STORE, Index::NOT_FOUND_INDEX); + } + const TableRecord& get_table_by_tag (hb_tag_t tag) const + { + unsigned int table_index; + find_table_index (tag, &table_index); + return get_table (table_index); + } + + public: + + template >::value))> + bool serialize (hb_serialize_context_t *c, + hb_tag_t sfnt_tag, + Iterator it) + { + TRACE_SERIALIZE (this); + /* Alloc 12 for the OTHeader. */ + if (unlikely (!c->extend_min (this))) return_trace (false); + /* Write sfntVersion (bytes 0..3). */ + sfnt_version = sfnt_tag; + /* Take space for numTables, searchRange, entrySelector, RangeShift + * and the TableRecords themselves. */ + unsigned num_items = it.len (); + if (unlikely (!tables.serialize (c, num_items))) return_trace (false); + + const char *dir_end = (const char *) c->head; + HBUINT32 *checksum_adjustment = nullptr; + + /* Write OffsetTables, alloc for and write actual table blobs. */ + unsigned i = 0; + for (hb_pair_t entry : it) + { + hb_blob_t *blob = entry.second; + unsigned len = blob->length; + + /* Allocate room for the table and copy it. */ + char *start = (char *) c->allocate_size (len); + if (unlikely (!start)) return false; + + TableRecord &rec = tables.arrayZ[i]; + rec.tag = entry.first; + rec.length = len; + rec.offset = 0; + if (unlikely (!c->check_assign (rec.offset, + (unsigned) ((char *) start - (char *) this), + HB_SERIALIZE_ERROR_OFFSET_OVERFLOW))) + return_trace (false); + + if (likely (len)) + memcpy (start, blob->data, len); + + /* 4-byte alignment. */ + c->align (4); + const char *end = (const char *) c->head; + + if (entry.first == HB_OT_TAG_head && + (unsigned) (end - start) >= head::static_size) + { + head *h = (head *) start; + checksum_adjustment = &h->checkSumAdjustment; + *checksum_adjustment = 0; + } + + rec.checkSum.set_for_data (start, end - start); + i++; + } + + tables.qsort (); + + if (checksum_adjustment) + { + CheckSum checksum; + + /* The following line is a slower version of the following block. */ + //checksum.set_for_data (this, (const char *) c->head - (const char *) this); + checksum.set_for_data (this, dir_end - (const char *) this); + for (unsigned int i = 0; i < num_items; i++) + { + TableRecord &rec = tables.arrayZ[i]; + checksum = checksum + rec.checkSum; + } + + *checksum_adjustment = 0xB1B0AFBAu - checksum; + } + + return_trace (true); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && tables.sanitize (c)); + } + + protected: + Tag sfnt_version; /* '\0\001\0\00' if TrueType / 'OTTO' if CFF */ + BinSearchArrayOf + tables; + public: + DEFINE_SIZE_ARRAY (12, tables); +} OpenTypeFontFace; + + +/* + * TrueType Collections + */ + +struct TTCHeaderVersion1 +{ + friend struct TTCHeader; + + unsigned int get_face_count () const { return table.len; } + const OpenTypeFontFace& get_face (unsigned int i) const { return this+table[i]; } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (table.sanitize (c, this)); + } + + protected: + Tag ttcTag; /* TrueType Collection ID string: 'ttcf' */ + FixedVersion<>version; /* Version of the TTC Header (1.0), + * 0x00010000u */ + Array32Of> + table; /* Array of offsets to the OffsetTable for each font + * from the beginning of the file */ + public: + DEFINE_SIZE_ARRAY (12, table); +}; + +struct TTCHeader +{ + friend struct OpenTypeFontFile; + + private: + + unsigned int get_face_count () const + { + switch (u.header.version.major) { + case 2: /* version 2 is compatible with version 1 */ + case 1: return u.version1.get_face_count (); + default:return 0; + } + } + const OpenTypeFontFace& get_face (unsigned int i) const + { + switch (u.header.version.major) { + case 2: /* version 2 is compatible with version 1 */ + case 1: return u.version1.get_face (i); + default:return Null (OpenTypeFontFace); + } + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (unlikely (!u.header.version.sanitize (c))) return_trace (false); + switch (u.header.version.major) { + case 2: /* version 2 is compatible with version 1 */ + case 1: return_trace (u.version1.sanitize (c)); + default:return_trace (true); + } + } + + protected: + union { + struct { + Tag ttcTag; /* TrueType Collection ID string: 'ttcf' */ + FixedVersion<>version; /* Version of the TTC Header (1.0 or 2.0), + * 0x00010000u or 0x00020000u */ + } header; + TTCHeaderVersion1 version1; + } u; +}; + +/* + * Mac Resource Fork + * + * http://mirror.informatimago.com/next/developer.apple.com/documentation/mac/MoreToolbox/MoreToolbox-99.html + */ + +struct ResourceRecord +{ + const OpenTypeFontFace & get_face (const void *data_base) const + { return * reinterpret_cast ((data_base+offset).arrayZ); } + + bool sanitize (hb_sanitize_context_t *c, + const void *data_base) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + offset.sanitize (c, data_base) && + get_face (data_base).sanitize (c)); + } + + protected: + HBUINT16 id; /* Resource ID. */ + HBINT16 nameOffset; /* Offset from beginning of resource name list + * to resource name, -1 means there is none. */ + HBUINT8 attrs; /* Resource attributes */ + NNOffset24To> + offset; /* Offset from beginning of data block to + * data for this resource */ + HBUINT32 reserved; /* Reserved for handle to resource */ + public: + DEFINE_SIZE_STATIC (12); +}; + +#define HB_TAG_sfnt HB_TAG ('s','f','n','t') + +struct ResourceTypeRecord +{ + unsigned int get_resource_count () const + { return tag == HB_TAG_sfnt ? resCountM1 + 1 : 0; } + + bool is_sfnt () const { return tag == HB_TAG_sfnt; } + + const ResourceRecord& get_resource_record (unsigned int i, + const void *type_base) const + { return (type_base+resourcesZ).as_array (get_resource_count ())[i]; } + + bool sanitize (hb_sanitize_context_t *c, + const void *type_base, + const void *data_base) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + resourcesZ.sanitize (c, type_base, + get_resource_count (), + data_base)); + } + + protected: + Tag tag; /* Resource type. */ + HBUINT16 resCountM1; /* Number of resources minus 1. */ + NNOffset16To> + resourcesZ; /* Offset from beginning of resource type list + * to reference item list for this type. */ + public: + DEFINE_SIZE_STATIC (8); +}; + +struct ResourceMap +{ + unsigned int get_face_count () const + { + unsigned int count = get_type_count (); + for (unsigned int i = 0; i < count; i++) + { + const ResourceTypeRecord& type = get_type_record (i); + if (type.is_sfnt ()) + return type.get_resource_count (); + } + return 0; + } + + const OpenTypeFontFace& get_face (unsigned int idx, + const void *data_base) const + { + unsigned int count = get_type_count (); + for (unsigned int i = 0; i < count; i++) + { + const ResourceTypeRecord& type = get_type_record (i); + /* The check for idx < count is here because ResourceRecord is NOT null-safe. + * Because an offset of 0 there does NOT mean null. */ + if (type.is_sfnt () && idx < type.get_resource_count ()) + return type.get_resource_record (idx, &(this+typeList)).get_face (data_base); + } + return Null (OpenTypeFontFace); + } + + bool sanitize (hb_sanitize_context_t *c, const void *data_base) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + typeList.sanitize (c, this, + &(this+typeList), + data_base)); + } + + private: + unsigned int get_type_count () const { return (this+typeList).lenM1 + 1; } + + const ResourceTypeRecord& get_type_record (unsigned int i) const + { return (this+typeList)[i]; } + + protected: + HBUINT8 reserved0[16]; /* Reserved for copy of resource header */ + HBUINT32 reserved1; /* Reserved for handle to next resource map */ + HBUINT16 resreved2; /* Reserved for file reference number */ + HBUINT16 attrs; /* Resource fork attribute */ + NNOffset16To> + typeList; /* Offset from beginning of map to + * resource type list */ + Offset16 nameList; /* Offset from beginning of map to + * resource name list */ + public: + DEFINE_SIZE_STATIC (28); +}; + +struct ResourceForkHeader +{ + unsigned int get_face_count () const + { return (this+map).get_face_count (); } + + const OpenTypeFontFace& get_face (unsigned int idx, + unsigned int *base_offset = nullptr) const + { + const OpenTypeFontFace &face = (this+map).get_face (idx, &(this+data)); + if (base_offset) + *base_offset = (const char *) &face - (const char *) this; + return face; + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + data.sanitize (c, this, dataLen) && + map.sanitize (c, this, &(this+data))); + } + + protected: + NNOffset32To> + data; /* Offset from beginning of resource fork + * to resource data */ + NNOffset32To + map; /* Offset from beginning of resource fork + * to resource map */ + HBUINT32 dataLen; /* Length of resource data */ + HBUINT32 mapLen; /* Length of resource map */ + public: + DEFINE_SIZE_STATIC (16); +}; + +/* + * OpenType Font File + */ + +struct OpenTypeFontFile +{ + enum { + CFFTag = HB_TAG ('O','T','T','O'), /* OpenType with Postscript outlines */ + TrueTypeTag = HB_TAG ( 0 , 1 , 0 , 0 ), /* OpenType with TrueType outlines */ + TTCTag = HB_TAG ('t','t','c','f'), /* TrueType Collection */ + DFontTag = HB_TAG ( 0 , 0 , 1 , 0 ), /* DFont Mac Resource Fork */ + TrueTag = HB_TAG ('t','r','u','e'), /* Obsolete Apple TrueType */ + Typ1Tag = HB_TAG ('t','y','p','1') /* Obsolete Apple Type1 font in SFNT container */ + }; + + hb_tag_t get_tag () const { return u.tag; } + + unsigned int get_face_count () const + { + switch (u.tag) { + case CFFTag: /* All the non-collection tags */ + case TrueTag: + case Typ1Tag: + case TrueTypeTag: return 1; + case TTCTag: return u.ttcHeader.get_face_count (); + case DFontTag: return u.rfHeader.get_face_count (); + default: return 0; + } + } + const OpenTypeFontFace& get_face (unsigned int i, unsigned int *base_offset = nullptr) const + { + if (base_offset) + *base_offset = 0; + switch (u.tag) { + /* Note: for non-collection SFNT data we ignore index. This is because + * Apple dfont container is a container of SFNT's. So each SFNT is a + * non-TTC, but the index is more than zero. */ + case CFFTag: /* All the non-collection tags */ + case TrueTag: + case Typ1Tag: + case TrueTypeTag: return u.fontFace; + case TTCTag: return u.ttcHeader.get_face (i); + case DFontTag: return u.rfHeader.get_face (i, base_offset); + default: return Null (OpenTypeFontFace); + } + } + + template >::value))> + bool serialize_single (hb_serialize_context_t *c, + hb_tag_t sfnt_tag, + Iterator items) + { + TRACE_SERIALIZE (this); + assert (sfnt_tag != TTCTag); + if (unlikely (!c->extend_min (this))) return_trace (false); + return_trace (u.fontFace.serialize (c, sfnt_tag, items)); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (unlikely (!u.tag.sanitize (c))) return_trace (false); + switch (u.tag) { + case CFFTag: /* All the non-collection tags */ + case TrueTag: + case Typ1Tag: + case TrueTypeTag: return_trace (u.fontFace.sanitize (c)); + case TTCTag: return_trace (u.ttcHeader.sanitize (c)); + case DFontTag: return_trace (u.rfHeader.sanitize (c)); + default: return_trace (true); + } + } + + protected: + union { + Tag tag; /* 4-byte identifier. */ + OpenTypeFontFace fontFace; + TTCHeader ttcHeader; + ResourceForkHeader rfHeader; + } u; + public: + DEFINE_SIZE_UNION (4, tag); +}; + + +} /* namespace OT */ + + +#endif /* HB_OPEN_FILE_HH */ diff --git a/libs/harfbuzz/src/hb-open-type.hh b/libs/harfbuzz/src/hb-open-type.hh new file mode 100644 index 00000000..e66f4518 --- /dev/null +++ b/libs/harfbuzz/src/hb-open-type.hh @@ -0,0 +1,1142 @@ +/* + * Copyright © 2007,2008,2009,2010 Red Hat, Inc. + * Copyright © 2012 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. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_OPEN_TYPE_HH +#define HB_OPEN_TYPE_HH + +#include "hb.hh" +#include "hb-blob.hh" +#include "hb-face.hh" +#include "hb-machinery.hh" +#include "hb-meta.hh" +#include "hb-subset.hh" + + +namespace OT { + + +/* + * + * The OpenType Font File: Data Types + */ + + +/* "The following data types are used in the OpenType font file. + * All OpenType fonts use Motorola-style byte ordering (Big Endian):" */ + +/* + * Int types + */ + +/* Integer types in big-endian order and no alignment requirement */ +template +struct IntType +{ + typedef Type type; + + IntType () = default; + explicit constexpr IntType (Type V) : v {V} {} + IntType& operator = (Type i) { v = i; return *this; } + /* For reason we define cast out operator for signed/unsigned, instead of Type, see: + * https://github.com/harfbuzz/harfbuzz/pull/2875/commits/09836013995cab2b9f07577a179ad7b024130467 */ + operator typename std::conditional::value, signed, unsigned>::type () const { return v; } + + bool operator == (const IntType &o) const { return (Type) v == (Type) o.v; } + bool operator != (const IntType &o) const { return !(*this == o); } + + IntType& operator += (unsigned count) { *this = *this + count; return *this; } + IntType& operator -= (unsigned count) { *this = *this - count; return *this; } + IntType& operator ++ () { *this += 1; return *this; } + IntType& operator -- () { *this -= 1; return *this; } + IntType operator ++ (int) { IntType c (*this); ++*this; return c; } + IntType operator -- (int) { IntType c (*this); --*this; return c; } + + HB_INTERNAL static int cmp (const IntType *a, const IntType *b) + { return b->cmp (*a); } + HB_INTERNAL static int cmp (const void *a, const void *b) + { + IntType *pa = (IntType *) a; + IntType *pb = (IntType *) b; + + return pb->cmp (*pa); + } + template ::value && + sizeof (Type2) < sizeof (int) && + sizeof (Type) < sizeof (int))> + int cmp (Type2 a) const + { + Type b = v; + return (int) a - (int) b; + } + template + int cmp (Type2 a) const + { + Type b = v; + return a < b ? -1 : a == b ? 0 : +1; + } + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + protected: + BEInt v; + public: + DEFINE_SIZE_STATIC (Size); +}; + +typedef IntType HBUINT8; /* 8-bit unsigned integer. */ +typedef IntType HBINT8; /* 8-bit signed integer. */ +typedef IntType HBUINT16; /* 16-bit unsigned integer. */ +typedef IntType HBINT16; /* 16-bit signed integer. */ +typedef IntType HBUINT32; /* 32-bit unsigned integer. */ +typedef IntType HBINT32; /* 32-bit signed integer. */ +/* Note: we cannot defined a signed HBINT24 because there's no corresponding C type. + * Works for unsigned, but not signed, since we rely on compiler for sign-extension. */ +typedef IntType HBUINT24; /* 24-bit unsigned integer. */ + +/* 15-bit unsigned number; top bit used for extension. */ +struct HBUINT15 : HBUINT16 +{ + /* TODO Flesh out; actually mask top bit. */ + HBUINT15& operator = (uint16_t i ) { HBUINT16::operator= (i); return *this; } + public: + DEFINE_SIZE_STATIC (2); +}; + +/* 16-bit signed integer (HBINT16) that describes a quantity in FUnits. */ +typedef HBINT16 FWORD; + +/* 32-bit signed integer (HBINT32) that describes a quantity in FUnits. */ +typedef HBINT32 FWORD32; + +/* 16-bit unsigned integer (HBUINT16) that describes a quantity in FUnits. */ +typedef HBUINT16 UFWORD; + +template +struct HBFixed : Type +{ + static constexpr float shift = (float) (1 << fraction_bits); + static_assert (Type::static_size * 8 > fraction_bits, ""); + + HBFixed& operator = (typename Type::type i ) { Type::operator= (i); return *this; } + float to_float () const { return ((int32_t) Type::v) / shift; } + void set_float (float f) { Type::v = roundf (f * shift); } + public: + DEFINE_SIZE_STATIC (Type::static_size); +}; + +/* 16-bit signed fixed number with the low 14 bits of fraction (2.14). */ +using F2DOT14 = HBFixed; + +/* 32-bit signed fixed-point number (16.16). */ +using F16DOT16 = HBFixed; + +/* Date represented in number of seconds since 12:00 midnight, January 1, + * 1904. The value is represented as a signed 64-bit integer. */ +struct LONGDATETIME +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + protected: + HBINT32 major; + HBUINT32 minor; + public: + DEFINE_SIZE_STATIC (8); +}; + +/* Array of four uint8s (length = 32 bits) used to identify a script, language + * system, feature, or baseline */ +struct Tag : HBUINT32 +{ + Tag& operator = (hb_tag_t i) { HBUINT32::operator= (i); return *this; } + /* What the char* converters return is NOT nul-terminated. Print using "%.4s" */ + operator const char* () const { return reinterpret_cast (this); } + operator char* () { return reinterpret_cast (this); } + public: + DEFINE_SIZE_STATIC (4); +}; + +/* Glyph index number, same as uint16 (length = 16 bits) */ +struct HBGlyphID16 : HBUINT16 +{ + HBGlyphID16& operator = (uint16_t i) { HBUINT16::operator= (i); return *this; } +}; +struct HBGlyphID24 : HBUINT24 +{ + HBGlyphID24& operator = (uint32_t i) { HBUINT24::operator= (i); return *this; } +}; + +/* Script/language-system/feature index */ +struct Index : HBUINT16 { + static constexpr unsigned NOT_FOUND_INDEX = 0xFFFFu; + Index& operator = (uint16_t i) { HBUINT16::operator= (i); return *this; } +}; +DECLARE_NULL_NAMESPACE_BYTES (OT, Index); + +typedef Index NameID; + +struct VarIdx : HBUINT32 { + static constexpr unsigned NO_VARIATION = 0xFFFFFFFFu; + VarIdx& operator = (uint32_t i) { HBUINT32::operator= (i); return *this; } +}; +DECLARE_NULL_NAMESPACE_BYTES (OT, VarIdx); + +/* Offset, Null offset = 0 */ +template +struct Offset : Type +{ + Offset& operator = (typename Type::type i) { Type::operator= (i); return *this; } + + typedef Type type; + + bool is_null () const { return has_null && 0 == *this; } + + public: + DEFINE_SIZE_STATIC (sizeof (Type)); +}; + +typedef Offset Offset16; +typedef Offset Offset24; +typedef Offset Offset32; + + +/* CheckSum */ +struct CheckSum : HBUINT32 +{ + CheckSum& operator = (uint32_t i) { HBUINT32::operator= (i); return *this; } + + /* This is reference implementation from the spec. */ + static uint32_t CalcTableChecksum (const HBUINT32 *Table, uint32_t Length) + { + uint32_t Sum = 0L; + assert (0 == (Length & 3)); + const HBUINT32 *EndPtr = Table + Length / HBUINT32::static_size; + + while (Table < EndPtr) + Sum += *Table++; + return Sum; + } + + /* Note: data should be 4byte aligned and have 4byte padding at the end. */ + void set_for_data (const void *data, unsigned int length) + { *this = CalcTableChecksum ((const HBUINT32 *) data, length); } + + public: + DEFINE_SIZE_STATIC (4); +}; + + +/* + * Version Numbers + */ + +template +struct FixedVersion +{ + uint32_t to_int () const { return (major << (sizeof (FixedType) * 8)) + minor; } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + FixedType major; + FixedType minor; + public: + DEFINE_SIZE_STATIC (2 * sizeof (FixedType)); +}; + + +/* + * Template subclasses of Offset that do the dereferencing. + * Use: (base+offset) + */ + +template +struct _hb_has_null +{ + static const Type *get_null () { return nullptr; } + static Type *get_crap () { return nullptr; } +}; +template +struct _hb_has_null +{ + static const Type *get_null () { return &Null (Type); } + static Type *get_crap () { return &Crap (Type); } +}; + +template +struct OffsetTo : Offset +{ + // Make sure Type is not unbounded; works only for types that are fully defined at OffsetTo time. + static_assert (has_null == false || + (hb_has_null_size (Type) || !hb_has_min_size (Type)), ""); + + HB_DELETE_COPY_ASSIGN (OffsetTo); + OffsetTo () = default; + + OffsetTo& operator = (typename OffsetType::type i) { OffsetType::operator= (i); return *this; } + + const Type& operator () (const void *base) const + { + if (unlikely (this->is_null ())) return *_hb_has_null::get_null (); + return StructAtOffset (base, *this); + } + Type& operator () (void *base) const + { + if (unlikely (this->is_null ())) return *_hb_has_null::get_crap (); + return StructAtOffset (base, *this); + } + + template + friend const Type& operator + (const Base &base, const OffsetTo &offset) { return offset ((const void *) base); } + template + friend const Type& operator + (const OffsetTo &offset, const Base &base) { return offset ((const void *) base); } + template + friend Type& operator + (Base &&base, OffsetTo &offset) { return offset ((void *) base); } + template + friend Type& operator + (OffsetTo &offset, Base &&base) { return offset ((void *) base); } + + + template + bool serialize_subset (hb_subset_context_t *c, const OffsetTo& src, + const void *src_base, Ts&&... ds) + { + *this = 0; + if (src.is_null ()) + return false; + + auto *s = c->serializer; + + s->push (); + + bool ret = c->dispatch (src_base+src, std::forward (ds)...); + + if (ret || !has_null) + s->add_link (*this, s->pop_pack ()); + else + s->pop_discard (); + + return ret; + } + + + template + bool serialize_serialize (hb_serialize_context_t *c, Ts&&... ds) + { + *this = 0; + + Type* obj = c->push (); + bool ret = obj->serialize (c, std::forward (ds)...); + + if (ret) + c->add_link (*this, c->pop_pack ()); + else + c->pop_discard (); + + return ret; + } + + /* TODO: Somehow merge this with previous function into a serialize_dispatch(). */ + /* Workaround clang bug: https://bugs.llvm.org/show_bug.cgi?id=23029 + * Can't compile: whence = hb_serialize_context_t::Head followed by Ts&&... + */ + template + bool serialize_copy (hb_serialize_context_t *c, const OffsetTo& src, + const void *src_base, unsigned dst_bias, + hb_serialize_context_t::whence_t whence, + Ts&&... ds) + { + *this = 0; + if (src.is_null ()) + return false; + + c->push (); + + bool ret = c->copy (src_base+src, std::forward (ds)...); + + c->add_link (*this, c->pop_pack (), whence, dst_bias); + + return ret; + } + + bool serialize_copy (hb_serialize_context_t *c, const OffsetTo& src, + const void *src_base, unsigned dst_bias = 0) + { return serialize_copy (c, src, src_base, dst_bias, hb_serialize_context_t::Head); } + + bool sanitize_shallow (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + if (unlikely (!c->check_struct (this))) return_trace (false); + if (unlikely (this->is_null ())) return_trace (true); + if (unlikely ((const char *) base + (unsigned) *this < (const char *) base)) return_trace (false); + return_trace (true); + } + + template + bool sanitize (hb_sanitize_context_t *c, const void *base, Ts&&... ds) const + { + TRACE_SANITIZE (this); + return_trace (sanitize_shallow (c, base) && + (this->is_null () || + c->dispatch (StructAtOffset (base, *this), std::forward (ds)...) || + neuter (c))); + } + + /* Set the offset to Null */ + bool neuter (hb_sanitize_context_t *c) const + { + if (!has_null) return false; + return c->try_set (this, 0); + } + DEFINE_SIZE_STATIC (sizeof (OffsetType)); +}; +/* Partial specializations. */ +template using Offset16To = OffsetTo; +template using Offset24To = OffsetTo; +template using Offset32To = OffsetTo; + +template using NNOffsetTo = OffsetTo; +template using NNOffset16To = Offset16To; +template using NNOffset24To = Offset24To; +template using NNOffset32To = Offset32To; + + +/* + * Array Types + */ + +template +struct UnsizedArrayOf +{ + typedef Type item_t; + static constexpr unsigned item_size = hb_static_size (Type); + + HB_DELETE_CREATE_COPY_ASSIGN (UnsizedArrayOf); + + const Type& operator [] (int i_) const + { + unsigned int i = (unsigned int) i_; + const Type *p = &arrayZ[i]; + if (unlikely ((const void *) p < (const void *) arrayZ)) return Null (Type); /* Overflowed. */ + _hb_compiler_memory_r_barrier (); + return *p; + } + Type& operator [] (int i_) + { + unsigned int i = (unsigned int) i_; + Type *p = &arrayZ[i]; + if (unlikely ((const void *) p < (const void *) arrayZ)) return Crap (Type); /* Overflowed. */ + _hb_compiler_memory_r_barrier (); + return *p; + } + + unsigned int get_size (unsigned int len) const + { return len * Type::static_size; } + + template operator T * () { return arrayZ; } + template operator const T * () const { return arrayZ; } + hb_array_t as_array (unsigned int len) + { return hb_array (arrayZ, len); } + hb_array_t as_array (unsigned int len) const + { return hb_array (arrayZ, len); } + + template + Type &lsearch (unsigned int len, const T &x, Type ¬_found = Crap (Type)) + { return *as_array (len).lsearch (x, ¬_found); } + template + const Type &lsearch (unsigned int len, const T &x, const Type ¬_found = Null (Type)) const + { return *as_array (len).lsearch (x, ¬_found); } + template + bool lfind (unsigned int len, const T &x, unsigned int *i = nullptr, + hb_not_found_t not_found = HB_NOT_FOUND_DONT_STORE, + unsigned int to_store = (unsigned int) -1) const + { return as_array (len).lfind (x, i, not_found, to_store); } + + void qsort (unsigned int len, unsigned int start = 0, unsigned int end = (unsigned int) -1) + { as_array (len).qsort (start, end); } + + bool serialize (hb_serialize_context_t *c, unsigned int items_len) + { + TRACE_SERIALIZE (this); + if (unlikely (!c->extend (this, items_len))) return_trace (false); + return_trace (true); + } + template + bool serialize (hb_serialize_context_t *c, Iterator items) + { + TRACE_SERIALIZE (this); + unsigned count = items.len (); + if (unlikely (!serialize (c, count))) return_trace (false); + /* TODO Umm. Just exhaust the iterator instead? Being extra + * cautious right now.. */ + for (unsigned i = 0; i < count; i++, ++items) + arrayZ[i] = *items; + return_trace (true); + } + + UnsizedArrayOf* copy (hb_serialize_context_t *c, unsigned count) const + { + TRACE_SERIALIZE (this); + auto *out = c->start_embed (this); + if (unlikely (!as_array (count).copy (c))) return_trace (nullptr); + return_trace (out); + } + + template + bool sanitize (hb_sanitize_context_t *c, unsigned int count, Ts&&... ds) const + { + TRACE_SANITIZE (this); + if (unlikely (!sanitize_shallow (c, count))) return_trace (false); + if (!sizeof... (Ts) && hb_is_trivially_copyable(Type)) return_trace (true); + for (unsigned int i = 0; i < count; i++) + if (unlikely (!c->dispatch (arrayZ[i], std::forward (ds)...))) + return_trace (false); + return_trace (true); + } + + bool sanitize_shallow (hb_sanitize_context_t *c, unsigned int count) const + { + TRACE_SANITIZE (this); + return_trace (c->check_array (arrayZ, count)); + } + + public: + Type arrayZ[HB_VAR_ARRAY]; + public: + DEFINE_SIZE_UNBOUNDED (0); +}; + +/* Unsized array of offset's */ +template +using UnsizedArray16OfOffsetTo = UnsizedArrayOf>; + +/* Unsized array of offsets relative to the beginning of the array itself. */ +template +struct UnsizedListOfOffset16To : UnsizedArray16OfOffsetTo +{ + const Type& operator [] (int i_) const + { + unsigned int i = (unsigned int) i_; + const OffsetTo *p = &this->arrayZ[i]; + if (unlikely ((const void *) p < (const void *) this->arrayZ)) return Null (Type); /* Overflowed. */ + _hb_compiler_memory_r_barrier (); + return this+*p; + } + Type& operator [] (int i_) + { + unsigned int i = (unsigned int) i_; + const OffsetTo *p = &this->arrayZ[i]; + if (unlikely ((const void *) p < (const void *) this->arrayZ)) return Crap (Type); /* Overflowed. */ + _hb_compiler_memory_r_barrier (); + return this+*p; + } + + template + bool sanitize (hb_sanitize_context_t *c, unsigned int count, Ts&&... ds) const + { + TRACE_SANITIZE (this); + return_trace ((UnsizedArray16OfOffsetTo + ::sanitize (c, count, this, std::forward (ds)...))); + } +}; + +/* An array with sorted elements. Supports binary searching. */ +template +struct SortedUnsizedArrayOf : UnsizedArrayOf +{ + hb_sorted_array_t as_array (unsigned int len) + { return hb_sorted_array (this->arrayZ, len); } + hb_sorted_array_t as_array (unsigned int len) const + { return hb_sorted_array (this->arrayZ, len); } + operator hb_sorted_array_t () { return as_array (); } + operator hb_sorted_array_t () const { return as_array (); } + + template + Type &bsearch (unsigned int len, const T &x, Type ¬_found = Crap (Type)) + { return *as_array (len).bsearch (x, ¬_found); } + template + const Type &bsearch (unsigned int len, const T &x, const Type ¬_found = Null (Type)) const + { return *as_array (len).bsearch (x, ¬_found); } + template + bool bfind (unsigned int len, const T &x, unsigned int *i = nullptr, + hb_not_found_t not_found = HB_NOT_FOUND_DONT_STORE, + unsigned int to_store = (unsigned int) -1) const + { return as_array (len).bfind (x, i, not_found, to_store); } +}; + + +/* An array with a number of elements. */ +template +struct ArrayOf +{ + typedef Type item_t; + static constexpr unsigned item_size = hb_static_size (Type); + + HB_DELETE_CREATE_COPY_ASSIGN (ArrayOf); + + const Type& operator [] (int i_) const + { + unsigned int i = (unsigned int) i_; + if (unlikely (i >= len)) return Null (Type); + _hb_compiler_memory_r_barrier (); + return arrayZ[i]; + } + Type& operator [] (int i_) + { + unsigned int i = (unsigned int) i_; + if (unlikely (i >= len)) return Crap (Type); + _hb_compiler_memory_r_barrier (); + return arrayZ[i]; + } + + unsigned int get_size () const + { return len.static_size + len * Type::static_size; } + + explicit operator bool () const { return len; } + + void pop () { len--; } + + hb_array_t< Type> as_array () { return hb_array (arrayZ, len); } + hb_array_t as_array () const { return hb_array (arrayZ, len); } + + /* Iterator. */ + typedef hb_array_t iter_t; + typedef hb_array_t< Type> writer_t; + iter_t iter () const { return as_array (); } + writer_t writer () { return as_array (); } + operator iter_t () const { return iter (); } + operator writer_t () { return writer (); } + + hb_array_t sub_array (unsigned int start_offset, unsigned int count) const + { return as_array ().sub_array (start_offset, count); } + hb_array_t sub_array (unsigned int start_offset, unsigned int *count = nullptr /* IN/OUT */) const + { return as_array ().sub_array (start_offset, count); } + hb_array_t sub_array (unsigned int start_offset, unsigned int count) + { return as_array ().sub_array (start_offset, count); } + hb_array_t sub_array (unsigned int start_offset, unsigned int *count = nullptr /* IN/OUT */) + { return as_array ().sub_array (start_offset, count); } + + template + Type &lsearch (const T &x, Type ¬_found = Crap (Type)) + { return *as_array ().lsearch (x, ¬_found); } + template + const Type &lsearch (const T &x, const Type ¬_found = Null (Type)) const + { return *as_array ().lsearch (x, ¬_found); } + template + bool lfind (const T &x, unsigned int *i = nullptr, + hb_not_found_t not_found = HB_NOT_FOUND_DONT_STORE, + unsigned int to_store = (unsigned int) -1) const + { return as_array ().lfind (x, i, not_found, to_store); } + + void qsort (unsigned int start = 0, unsigned int end = (unsigned int) -1) + { as_array ().qsort (start, end); } + + HB_NODISCARD bool serialize (hb_serialize_context_t *c, unsigned items_len) + { + TRACE_SERIALIZE (this); + if (unlikely (!c->extend_min (this))) return_trace (false); + c->check_assign (len, items_len, HB_SERIALIZE_ERROR_ARRAY_OVERFLOW); + if (unlikely (!c->extend (this))) return_trace (false); + return_trace (true); + } + template + HB_NODISCARD bool serialize (hb_serialize_context_t *c, Iterator items) + { + TRACE_SERIALIZE (this); + unsigned count = items.len (); + if (unlikely (!serialize (c, count))) return_trace (false); + /* TODO Umm. Just exhaust the iterator instead? Being extra + * cautious right now.. */ + for (unsigned i = 0; i < count; i++, ++items) + arrayZ[i] = *items; + return_trace (true); + } + + Type* serialize_append (hb_serialize_context_t *c) + { + TRACE_SERIALIZE (this); + len++; + if (unlikely (!len || !c->extend (this))) + { + len--; + return_trace (nullptr); + } + return_trace (&arrayZ[len - 1]); + } + + ArrayOf* copy (hb_serialize_context_t *c) const + { + TRACE_SERIALIZE (this); + auto *out = c->start_embed (this); + if (unlikely (!c->extend_min (out))) return_trace (nullptr); + c->check_assign (out->len, len, HB_SERIALIZE_ERROR_ARRAY_OVERFLOW); + if (unlikely (!as_array ().copy (c))) return_trace (nullptr); + return_trace (out); + } + + template + bool sanitize (hb_sanitize_context_t *c, Ts&&... ds) const + { + TRACE_SANITIZE (this); + if (unlikely (!sanitize_shallow (c))) return_trace (false); + if (!sizeof... (Ts) && hb_is_trivially_copyable(Type)) return_trace (true); + unsigned int count = len; + for (unsigned int i = 0; i < count; i++) + if (unlikely (!c->dispatch (arrayZ[i], std::forward (ds)...))) + return_trace (false); + return_trace (true); + } + + bool sanitize_shallow (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (len.sanitize (c) && c->check_array (arrayZ, len)); + } + + public: + LenType len; + Type arrayZ[HB_VAR_ARRAY]; + public: + DEFINE_SIZE_ARRAY (sizeof (LenType), arrayZ); +}; +template using Array16Of = ArrayOf; +template using Array24Of = ArrayOf; +template using Array32Of = ArrayOf; +using PString = ArrayOf; + +/* Array of Offset's */ +template using Array16OfOffset16To = ArrayOf, HBUINT16>; +template using Array16OfOffset32To = ArrayOf, HBUINT16>; +template using Array32OfOffset32To = ArrayOf, HBUINT32>; + +/* Array of offsets relative to the beginning of the array itself. */ +template +struct List16OfOffsetTo : ArrayOf, HBUINT16> +{ + const Type& operator [] (int i_) const + { + unsigned int i = (unsigned int) i_; + if (unlikely (i >= this->len)) return Null (Type); + _hb_compiler_memory_r_barrier (); + return this+this->arrayZ[i]; + } + const Type& operator [] (int i_) + { + unsigned int i = (unsigned int) i_; + if (unlikely (i >= this->len)) return Crap (Type); + _hb_compiler_memory_r_barrier (); + return this+this->arrayZ[i]; + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + struct List16OfOffsetTo *out = c->serializer->embed (*this); + if (unlikely (!out)) return_trace (false); + unsigned int count = this->len; + for (unsigned int i = 0; i < count; i++) + out->arrayZ[i].serialize_subset (c, this->arrayZ[i], this, out); + return_trace (true); + } + + template + bool sanitize (hb_sanitize_context_t *c, Ts&&... ds) const + { + TRACE_SANITIZE (this); + return_trace ((Array16Of>::sanitize (c, this, std::forward (ds)...))); + } +}; + +template +using List16OfOffset16To = List16OfOffsetTo; + +/* An array starting at second element. */ +template +struct HeadlessArrayOf +{ + static constexpr unsigned item_size = Type::static_size; + + HB_DELETE_CREATE_COPY_ASSIGN (HeadlessArrayOf); + + const Type& operator [] (int i_) const + { + unsigned int i = (unsigned int) i_; + if (unlikely (i >= lenP1 || !i)) return Null (Type); + _hb_compiler_memory_r_barrier (); + return arrayZ[i-1]; + } + Type& operator [] (int i_) + { + unsigned int i = (unsigned int) i_; + if (unlikely (i >= lenP1 || !i)) return Crap (Type); + _hb_compiler_memory_r_barrier (); + return arrayZ[i-1]; + } + unsigned int get_size () const + { return lenP1.static_size + get_length () * Type::static_size; } + + unsigned get_length () const { return lenP1 ? lenP1 - 1 : 0; } + + hb_array_t< Type> as_array () { return hb_array (arrayZ, get_length ()); } + hb_array_t as_array () const { return hb_array (arrayZ, get_length ()); } + + /* Iterator. */ + typedef hb_array_t iter_t; + typedef hb_array_t< Type> writer_t; + iter_t iter () const { return as_array (); } + writer_t writer () { return as_array (); } + operator iter_t () const { return iter (); } + operator writer_t () { return writer (); } + + bool serialize (hb_serialize_context_t *c, unsigned int items_len) + { + TRACE_SERIALIZE (this); + if (unlikely (!c->extend_min (this))) return_trace (false); + c->check_assign (lenP1, items_len + 1, HB_SERIALIZE_ERROR_ARRAY_OVERFLOW); + if (unlikely (!c->extend (this))) return_trace (false); + return_trace (true); + } + template + bool serialize (hb_serialize_context_t *c, Iterator items) + { + TRACE_SERIALIZE (this); + unsigned count = items.len (); + if (unlikely (!serialize (c, count))) return_trace (false); + /* TODO Umm. Just exhaust the iterator instead? Being extra + * cautious right now.. */ + for (unsigned i = 0; i < count; i++, ++items) + arrayZ[i] = *items; + return_trace (true); + } + + template + bool sanitize (hb_sanitize_context_t *c, Ts&&... ds) const + { + TRACE_SANITIZE (this); + if (unlikely (!sanitize_shallow (c))) return_trace (false); + if (!sizeof... (Ts) && hb_is_trivially_copyable(Type)) return_trace (true); + unsigned int count = get_length (); + for (unsigned int i = 0; i < count; i++) + if (unlikely (!c->dispatch (arrayZ[i], std::forward (ds)...))) + return_trace (false); + return_trace (true); + } + + private: + bool sanitize_shallow (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (lenP1.sanitize (c) && + (!lenP1 || c->check_array (arrayZ, lenP1 - 1))); + } + + public: + LenType lenP1; + Type arrayZ[HB_VAR_ARRAY]; + public: + DEFINE_SIZE_ARRAY (sizeof (LenType), arrayZ); +}; + +/* An array storing length-1. */ +template +struct ArrayOfM1 +{ + HB_DELETE_CREATE_COPY_ASSIGN (ArrayOfM1); + + const Type& operator [] (int i_) const + { + unsigned int i = (unsigned int) i_; + if (unlikely (i > lenM1)) return Null (Type); + _hb_compiler_memory_r_barrier (); + return arrayZ[i]; + } + Type& operator [] (int i_) + { + unsigned int i = (unsigned int) i_; + if (unlikely (i > lenM1)) return Crap (Type); + _hb_compiler_memory_r_barrier (); + return arrayZ[i]; + } + unsigned int get_size () const + { return lenM1.static_size + (lenM1 + 1) * Type::static_size; } + + template + bool sanitize (hb_sanitize_context_t *c, Ts&&... ds) const + { + TRACE_SANITIZE (this); + if (unlikely (!sanitize_shallow (c))) return_trace (false); + if (!sizeof... (Ts) && hb_is_trivially_copyable(Type)) return_trace (true); + unsigned int count = lenM1 + 1; + for (unsigned int i = 0; i < count; i++) + if (unlikely (!c->dispatch (arrayZ[i], std::forward (ds)...))) + return_trace (false); + return_trace (true); + } + + private: + bool sanitize_shallow (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (lenM1.sanitize (c) && + (c->check_array (arrayZ, lenM1 + 1))); + } + + public: + LenType lenM1; + Type arrayZ[HB_VAR_ARRAY]; + public: + DEFINE_SIZE_ARRAY (sizeof (LenType), arrayZ); +}; + +/* An array with sorted elements. Supports binary searching. */ +template +struct SortedArrayOf : ArrayOf +{ + hb_sorted_array_t< Type> as_array () { return hb_sorted_array (this->arrayZ, this->len); } + hb_sorted_array_t as_array () const { return hb_sorted_array (this->arrayZ, this->len); } + + /* Iterator. */ + typedef hb_sorted_array_t iter_t; + typedef hb_sorted_array_t< Type> writer_t; + iter_t iter () const { return as_array (); } + writer_t writer () { return as_array (); } + operator iter_t () const { return iter (); } + operator writer_t () { return writer (); } + + hb_sorted_array_t sub_array (unsigned int start_offset, unsigned int count) const + { return as_array ().sub_array (start_offset, count); } + hb_sorted_array_t sub_array (unsigned int start_offset, unsigned int *count = nullptr /* IN/OUT */) const + { return as_array ().sub_array (start_offset, count); } + hb_sorted_array_t sub_array (unsigned int start_offset, unsigned int count) + { return as_array ().sub_array (start_offset, count); } + hb_sorted_array_t sub_array (unsigned int start_offset, unsigned int *count = nullptr /* IN/OUT */) + { return as_array ().sub_array (start_offset, count); } + + bool serialize (hb_serialize_context_t *c, unsigned int items_len) + { + TRACE_SERIALIZE (this); + bool ret = ArrayOf::serialize (c, items_len); + return_trace (ret); + } + template + bool serialize (hb_serialize_context_t *c, Iterator items) + { + TRACE_SERIALIZE (this); + bool ret = ArrayOf::serialize (c, items); + return_trace (ret); + } + + template + Type &bsearch (const T &x, Type ¬_found = Crap (Type)) + { return *as_array ().bsearch (x, ¬_found); } + template + const Type &bsearch (const T &x, const Type ¬_found = Null (Type)) const + { return *as_array ().bsearch (x, ¬_found); } + template + bool bfind (const T &x, unsigned int *i = nullptr, + hb_not_found_t not_found = HB_NOT_FOUND_DONT_STORE, + unsigned int to_store = (unsigned int) -1) const + { return as_array ().bfind (x, i, not_found, to_store); } +}; + +template using SortedArray16Of = SortedArrayOf; +template using SortedArray24Of = SortedArrayOf; +template using SortedArray32Of = SortedArrayOf; + +/* + * Binary-search arrays + */ + +template +struct BinSearchHeader +{ + operator uint32_t () const { return len; } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + BinSearchHeader& operator = (unsigned int v) + { + len = v; + assert (len == v); + entrySelector = hb_max (1u, hb_bit_storage (v)) - 1; + searchRange = 16 * (1u << entrySelector); + rangeShift = v * 16 > searchRange + ? 16 * v - searchRange + : 0; + return *this; + } + + protected: + LenType len; + LenType searchRange; + LenType entrySelector; + LenType rangeShift; + + public: + DEFINE_SIZE_STATIC (8); +}; + +template +using BinSearchArrayOf = SortedArrayOf>; + + +struct VarSizedBinSearchHeader +{ + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + HBUINT16 unitSize; /* Size of a lookup unit for this search in bytes. */ + HBUINT16 nUnits; /* Number of units of the preceding size to be searched. */ + HBUINT16 searchRange; /* The value of unitSize times the largest power of 2 + * that is less than or equal to the value of nUnits. */ + HBUINT16 entrySelector; /* The log base 2 of the largest power of 2 less than + * or equal to the value of nUnits. */ + HBUINT16 rangeShift; /* The value of unitSize times the difference of the + * value of nUnits minus the largest power of 2 less + * than or equal to the value of nUnits. */ + public: + DEFINE_SIZE_STATIC (10); +}; + +template +struct VarSizedBinSearchArrayOf +{ + static constexpr unsigned item_size = Type::static_size; + + HB_DELETE_CREATE_COPY_ASSIGN (VarSizedBinSearchArrayOf); + + bool last_is_terminator () const + { + if (unlikely (!header.nUnits)) return false; + + /* Gah. + * + * "The number of termination values that need to be included is table-specific. + * The value that indicates binary search termination is 0xFFFF." */ + const HBUINT16 *words = &StructAtOffset (&bytesZ, (header.nUnits - 1) * header.unitSize); + unsigned int count = Type::TerminationWordCount; + for (unsigned int i = 0; i < count; i++) + if (words[i] != 0xFFFFu) + return false; + return true; + } + + const Type& operator [] (int i_) const + { + unsigned int i = (unsigned int) i_; + if (unlikely (i >= get_length ())) return Null (Type); + _hb_compiler_memory_r_barrier (); + return StructAtOffset (&bytesZ, i * header.unitSize); + } + Type& operator [] (int i_) + { + unsigned int i = (unsigned int) i_; + if (unlikely (i >= get_length ())) return Crap (Type); + _hb_compiler_memory_r_barrier (); + return StructAtOffset (&bytesZ, i * header.unitSize); + } + unsigned int get_length () const + { return header.nUnits - last_is_terminator (); } + unsigned int get_size () const + { return header.static_size + header.nUnits * header.unitSize; } + + template + bool sanitize (hb_sanitize_context_t *c, Ts&&... ds) const + { + TRACE_SANITIZE (this); + if (unlikely (!sanitize_shallow (c))) return_trace (false); + if (!sizeof... (Ts) && hb_is_trivially_copyable(Type)) return_trace (true); + unsigned int count = get_length (); + for (unsigned int i = 0; i < count; i++) + if (unlikely (!(*this)[i].sanitize (c, std::forward (ds)...))) + return_trace (false); + return_trace (true); + } + + template + const Type *bsearch (const T &key) const + { + unsigned pos; + return hb_bsearch_impl (&pos, + key, + (const void *) bytesZ, + get_length (), + header.unitSize, + _hb_cmp_method) + ? (const Type *) (((const char *) &bytesZ) + (pos * header.unitSize)) + : nullptr; + } + + private: + bool sanitize_shallow (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (header.sanitize (c) && + Type::static_size <= header.unitSize && + c->check_range (bytesZ.arrayZ, + header.nUnits, + header.unitSize)); + } + + protected: + VarSizedBinSearchHeader header; + UnsizedArrayOf bytesZ; + public: + DEFINE_SIZE_ARRAY (10, bytesZ); +}; + + +} /* namespace OT */ + + +#endif /* HB_OPEN_TYPE_HH */ diff --git a/libs/harfbuzz/src/hb-ot-cff-common.hh b/libs/harfbuzz/src/hb-ot-cff-common.hh new file mode 100644 index 00000000..ae3b83a2 --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-cff-common.hh @@ -0,0 +1,564 @@ +/* + * Copyright © 2018 Adobe 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. + * + * Adobe Author(s): Michiharu Ariza + */ +#ifndef HB_OT_CFF_COMMON_HH +#define HB_OT_CFF_COMMON_HH + +#include "hb-open-type.hh" +#include "hb-bimap.hh" +#include "hb-ot-layout-common.hh" +#include "hb-cff-interp-dict-common.hh" +#include "hb-subset-plan.hh" + +namespace CFF { + +using namespace OT; + +#define CFF_UNDEF_CODE 0xFFFFFFFF + +using objidx_t = hb_serialize_context_t::objidx_t; +using whence_t = hb_serialize_context_t::whence_t; + +/* utility macro */ +template +static inline const Type& StructAtOffsetOrNull (const void *P, unsigned int offset) +{ return offset ? StructAtOffset (P, offset) : Null (Type); } + +struct code_pair_t +{ + hb_codepoint_t code; + hb_codepoint_t glyph; +}; + +using str_buff_t = hb_vector_t; +using str_buff_vec_t = hb_vector_t; + +/* CFF INDEX */ +template +struct CFFIndex +{ + unsigned int offset_array_size () const + { return offSize * (count + 1); } + + CFFIndex *copy (hb_serialize_context_t *c) const + { + TRACE_SERIALIZE (this); + unsigned int size = get_size (); + CFFIndex *out = c->allocate_size (size); + if (likely (out)) + memcpy (out, this, size); + return_trace (out); + } + + bool serialize (hb_serialize_context_t *c, + unsigned int offSize_, + const byte_str_array_t &byteArray) + { + TRACE_SERIALIZE (this); + + if (byteArray.length == 0) + { + COUNT *dest = c->allocate_min (); + if (unlikely (!dest)) return_trace (false); + *dest = 0; + return_trace (true); + } + + /* serialize CFFIndex header */ + if (unlikely (!c->extend_min (this))) return_trace (false); + this->count = byteArray.length; + this->offSize = offSize_; + if (unlikely (!c->allocate_size (offSize_ * (byteArray.length + 1)))) + return_trace (false); + + /* serialize indices */ + unsigned int offset = 1; + unsigned int i = 0; + for (; i < byteArray.length; i++) + { + set_offset_at (i, offset); + offset += byteArray[i].get_size (); + } + set_offset_at (i, offset); + + /* serialize data */ + for (unsigned int i = 0; i < byteArray.length; i++) + { + const hb_ubytes_t &bs = byteArray[i]; + unsigned char *dest = c->allocate_size (bs.length); + if (unlikely (!dest)) return_trace (false); + memcpy (dest, &bs[0], bs.length); + } + + return_trace (true); + } + + bool serialize (hb_serialize_context_t *c, + unsigned int offSize_, + const str_buff_vec_t &buffArray) + { + byte_str_array_t byteArray; + byteArray.init (); + byteArray.resize (buffArray.length); + for (unsigned int i = 0; i < byteArray.length; i++) + byteArray[i] = hb_ubytes_t (buffArray[i].arrayZ, buffArray[i].length); + bool result = this->serialize (c, offSize_, byteArray); + byteArray.fini (); + return result; + } + + template + bool serialize (hb_serialize_context_t *c, + Iterator it) + { + TRACE_SERIALIZE (this); + serialize_header(c, + it | hb_map ([] (const hb_ubytes_t &_) { return _.length; })); + for (const auto &_ : +it) + _.copy (c); + return_trace (true); + } + + bool serialize (hb_serialize_context_t *c, + const byte_str_array_t &byteArray) + { return serialize (c, + hb_iter (byteArray)); } + + bool serialize (hb_serialize_context_t *c, + const str_buff_vec_t &buffArray) + { + auto it = + + hb_iter (buffArray) + | hb_map ([] (const str_buff_t &_) { return hb_ubytes_t (_.arrayZ, _.length); }) + ; + return serialize (c, it); + } + + template + bool serialize_header (hb_serialize_context_t *c, + Iterator it) + { + TRACE_SERIALIZE (this); + + unsigned total = + it | hb_reduce (hb_add, 0); + unsigned off_size = (hb_bit_storage (total + 1) + 7) / 8; + + /* serialize CFFIndex header */ + if (unlikely (!c->extend_min (this))) return_trace (false); + this->count = it.len (); + if (!this->count) return_trace (true); + if (unlikely (!c->extend (this->offSize))) return_trace (false); + this->offSize = off_size; + if (unlikely (!c->allocate_size (off_size * (this->count + 1)))) + return_trace (false); + + /* serialize indices */ + unsigned int offset = 1; + unsigned int i = 0; + for (unsigned _ : +it) + { + CFFIndex::set_offset_at (i++, offset); + offset += _; + } + CFFIndex::set_offset_at (i, offset); + + return_trace (true); + } + + void set_offset_at (unsigned int index, unsigned int offset) + { + assert (index <= count); + HBUINT8 *p = offsets + offSize * index + offSize; + unsigned int size = offSize; + for (; size; size--) + { + --p; + *p = offset & 0xFF; + offset >>= 8; + } + } + + private: + unsigned int offset_at (unsigned int index) const + { + assert (index <= count); + + unsigned int size = offSize; + const HBUINT8 *p = offsets + size * index; + unsigned int offset = 0; + for (; size; size--) + offset = (offset << 8) + *p++; + return offset; + } + + unsigned int length_at (unsigned int index) const + { + unsigned offset0 = offset_at (index); + unsigned offset1 = offset_at (index + 1); + if (unlikely (offset1 < offset0 || offset1 > offset_at (count))) + return 0; + return offset1 - offset0; + } + + const unsigned char *data_base () const + { return (const unsigned char *) this + min_size + offSize.static_size + offset_array_size (); } + public: + + hb_ubytes_t operator [] (unsigned int index) const + { + if (unlikely (index >= count)) return hb_ubytes_t (); + unsigned length = length_at (index); + if (unlikely (!length)) return hb_ubytes_t (); + return hb_ubytes_t (data_base () + offset_at (index) - 1, length); + } + + unsigned int get_size () const + { + if (count) + return min_size + offSize.static_size + offset_array_size () + (offset_at (count) - 1); + return min_size; /* empty CFFIndex contains count only */ + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + (count == 0 || /* empty INDEX */ + (count < count + 1u && + c->check_struct (&offSize) && offSize >= 1 && offSize <= 4 && + c->check_array (offsets, offSize, count + 1u) && + c->check_array ((const HBUINT8*) data_base (), 1, offset_at (count) - 1))))); + } + + public: + COUNT count; /* Number of object data. Note there are (count+1) offsets */ + private: + HBUINT8 offSize; /* The byte size of each offset in the offsets array. */ + HBUINT8 offsets[HB_VAR_ARRAY]; + /* The array of (count + 1) offsets into objects array (1-base). */ + /* HBUINT8 data[HB_VAR_ARRAY]; Object data */ + public: + DEFINE_SIZE_MIN (COUNT::static_size); +}; + +template +struct CFFIndexOf : CFFIndex +{ + template + bool serialize (hb_serialize_context_t *c, + unsigned int offSize_, + const DATA *dataArray, + unsigned int dataArrayLen, + const hb_vector_t &dataSizeArray, + const PARAM1 ¶m1, + const PARAM2 ¶m2) + { + TRACE_SERIALIZE (this); + /* serialize CFFIndex header */ + if (unlikely (!c->extend_min (this))) return_trace (false); + this->count = dataArrayLen; + this->offSize = offSize_; + if (unlikely (!c->allocate_size (offSize_ * (dataArrayLen + 1)))) + return_trace (false); + + /* serialize indices */ + unsigned int offset = 1; + unsigned int i = 0; + for (; i < dataArrayLen; i++) + { + CFFIndex::set_offset_at (i, offset); + offset += dataSizeArray[i]; + } + CFFIndex::set_offset_at (i, offset); + + /* serialize data */ + for (unsigned int i = 0; i < dataArrayLen; i++) + { + TYPE *dest = c->start_embed (); + if (unlikely (!dest || !dest->serialize (c, dataArray[i], param1, param2))) + return_trace (false); + } + return_trace (true); + } +}; + +/* Top Dict, Font Dict, Private Dict */ +struct Dict : UnsizedByteStr +{ + template + bool serialize (hb_serialize_context_t *c, + const DICTVAL &dictval, + OP_SERIALIZER& opszr, + Ts&&... ds) + { + TRACE_SERIALIZE (this); + for (unsigned int i = 0; i < dictval.get_count (); i++) + if (unlikely (!opszr.serialize (c, dictval[i], std::forward (ds)...))) + return_trace (false); + + return_trace (true); + } + + template + static bool serialize_int_op (hb_serialize_context_t *c, op_code_t op, V value, op_code_t intOp) + { + // XXX: not sure why but LLVM fails to compile the following 'unlikely' macro invocation + if (/*unlikely*/ (!serialize_int (c, intOp, value))) + return false; + + TRACE_SERIALIZE (this); + /* serialize the opcode */ + HBUINT8 *p = c->allocate_size (OpCode_Size (op)); + if (unlikely (!p)) return_trace (false); + if (Is_OpCode_ESC (op)) + { + *p = OpCode_escape; + op = Unmake_OpCode_ESC (op); + p++; + } + *p = op; + return_trace (true); + } + + template + static bool serialize_int4_op (hb_serialize_context_t *c, op_code_t op, V value) + { return serialize_int_op (c, op, value, OpCode_longintdict); } + + template + static bool serialize_int2_op (hb_serialize_context_t *c, op_code_t op, V value) + { return serialize_int_op (c, op, value, OpCode_shortint); } + + template + static bool serialize_link_op (hb_serialize_context_t *c, op_code_t op, objidx_t link, whence_t whence) + { + T &ofs = *(T *) (c->head + OpCode_Size (int_op)); + if (unlikely (!serialize_int_op (c, op, 0, int_op))) return false; + c->add_link (ofs, link, whence); + return true; + } + + static bool serialize_link4_op (hb_serialize_context_t *c, op_code_t op, objidx_t link, whence_t whence = whence_t::Head) + { return serialize_link_op (c, op, link, whence); } + + static bool serialize_link2_op (hb_serialize_context_t *c, op_code_t op, objidx_t link, whence_t whence = whence_t::Head) + { return serialize_link_op (c, op, link, whence); } +}; + +struct TopDict : Dict {}; +struct FontDict : Dict {}; +struct PrivateDict : Dict {}; + +struct table_info_t +{ + void init () { offset = size = 0; link = 0; } + + unsigned int offset; + unsigned int size; + objidx_t link; +}; + +template +struct FDArray : CFFIndexOf +{ + template + bool serialize (hb_serialize_context_t *c, + Iterator it, + OP_SERIALIZER& opszr) + { + TRACE_SERIALIZE (this); + + /* serialize INDEX data */ + hb_vector_t sizes; + c->push (); + + it + | hb_map ([&] (const hb_pair_t &_) + { + FontDict *dict = c->start_embed (); + dict->serialize (c, _.first, opszr, _.second); + return c->head - (const char*)dict; + }) + | hb_sink (sizes) + ; + c->pop_pack (false); + + /* serialize INDEX header */ + return_trace (CFFIndex::serialize_header (c, hb_iter (sizes))); + } +}; + +/* FDSelect */ +struct FDSelect0 { + bool sanitize (hb_sanitize_context_t *c, unsigned int fdcount) const + { + TRACE_SANITIZE (this); + if (unlikely (!(c->check_struct (this)))) + return_trace (false); + for (unsigned int i = 0; i < c->get_num_glyphs (); i++) + if (unlikely (!fds[i].sanitize (c))) + return_trace (false); + + return_trace (true); + } + + hb_codepoint_t get_fd (hb_codepoint_t glyph) const + { return (hb_codepoint_t) fds[glyph]; } + + unsigned int get_size (unsigned int num_glyphs) const + { return HBUINT8::static_size * num_glyphs; } + + HBUINT8 fds[HB_VAR_ARRAY]; + + DEFINE_SIZE_MIN (0); +}; + +template +struct FDSelect3_4_Range +{ + bool sanitize (hb_sanitize_context_t *c, const void * /*nullptr*/, unsigned int fdcount) const + { + TRACE_SANITIZE (this); + return_trace (first < c->get_num_glyphs () && (fd < fdcount)); + } + + GID_TYPE first; + FD_TYPE fd; + public: + DEFINE_SIZE_STATIC (GID_TYPE::static_size + FD_TYPE::static_size); +}; + +template +struct FDSelect3_4 +{ + unsigned int get_size () const + { return GID_TYPE::static_size * 2 + ranges.get_size (); } + + bool sanitize (hb_sanitize_context_t *c, unsigned int fdcount) const + { + TRACE_SANITIZE (this); + if (unlikely (!c->check_struct (this) || !ranges.sanitize (c, nullptr, fdcount) || + (nRanges () == 0) || ranges[0].first != 0)) + return_trace (false); + + for (unsigned int i = 1; i < nRanges (); i++) + if (unlikely (ranges[i - 1].first >= ranges[i].first)) + return_trace (false); + + if (unlikely (!sentinel().sanitize (c) || (sentinel() != c->get_num_glyphs ()))) + return_trace (false); + + return_trace (true); + } + + hb_codepoint_t get_fd (hb_codepoint_t glyph) const + { + unsigned int i; + for (i = 1; i < nRanges (); i++) + if (glyph < ranges[i].first) + break; + + return (hb_codepoint_t) ranges[i - 1].fd; + } + + GID_TYPE &nRanges () { return ranges.len; } + GID_TYPE nRanges () const { return ranges.len; } + GID_TYPE &sentinel () { return StructAfter (ranges[nRanges () - 1]); } + const GID_TYPE &sentinel () const { return StructAfter (ranges[nRanges () - 1]); } + + ArrayOf, GID_TYPE> ranges; + /* GID_TYPE sentinel */ + + DEFINE_SIZE_ARRAY (GID_TYPE::static_size, ranges); +}; + +typedef FDSelect3_4 FDSelect3; +typedef FDSelect3_4_Range FDSelect3_Range; + +struct FDSelect +{ + bool serialize (hb_serialize_context_t *c, const FDSelect &src, unsigned int num_glyphs) + { + TRACE_SERIALIZE (this); + unsigned int size = src.get_size (num_glyphs); + FDSelect *dest = c->allocate_size (size); + if (unlikely (!dest)) return_trace (false); + memcpy (dest, &src, size); + return_trace (true); + } + + unsigned int get_size (unsigned int num_glyphs) const + { + switch (format) + { + case 0: return format.static_size + u.format0.get_size (num_glyphs); + case 3: return format.static_size + u.format3.get_size (); + default:return 0; + } + } + + hb_codepoint_t get_fd (hb_codepoint_t glyph) const + { + if (this == &Null (FDSelect)) return 0; + + switch (format) + { + case 0: return u.format0.get_fd (glyph); + case 3: return u.format3.get_fd (glyph); + default:return 0; + } + } + + bool sanitize (hb_sanitize_context_t *c, unsigned int fdcount) const + { + TRACE_SANITIZE (this); + if (unlikely (!c->check_struct (this))) + return_trace (false); + + switch (format) + { + case 0: return_trace (u.format0.sanitize (c, fdcount)); + case 3: return_trace (u.format3.sanitize (c, fdcount)); + default:return_trace (false); + } + } + + HBUINT8 format; + union { + FDSelect0 format0; + FDSelect3 format3; + } u; + public: + DEFINE_SIZE_MIN (1); +}; + +template +struct Subrs : CFFIndex +{ + typedef COUNT count_type; + typedef CFFIndex SUPER; +}; + +} /* namespace CFF */ + +#endif /* HB_OT_CFF_COMMON_HH */ diff --git a/libs/harfbuzz/src/hb-ot-cff1-std-str.hh b/libs/harfbuzz/src/hb-ot-cff1-std-str.hh new file mode 100644 index 00000000..65d56ae1 --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-cff1-std-str.hh @@ -0,0 +1,425 @@ +/* + * Copyright © 2019 Adobe, 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. + * + * Adobe Author(s): Michiharu Ariza + */ + +#ifndef HB_OT_CFF1_STD_STR_HH +#if 0 /* Make checks happy. */ +#define HB_OT_CFF1_STD_STR_HH +#include "hb.hh" +#endif + +_S(".notdef") +_S("space") +_S("exclam") +_S("quotedbl") +_S("numbersign") +_S("dollar") +_S("percent") +_S("ampersand") +_S("quoteright") +_S("parenleft") +_S("parenright") +_S("asterisk") +_S("plus") +_S("comma") +_S("hyphen") +_S("period") +_S("slash") +_S("zero") +_S("one") +_S("two") +_S("three") +_S("four") +_S("five") +_S("six") +_S("seven") +_S("eight") +_S("nine") +_S("colon") +_S("semicolon") +_S("less") +_S("equal") +_S("greater") +_S("question") +_S("at") +_S("A") +_S("B") +_S("C") +_S("D") +_S("E") +_S("F") +_S("G") +_S("H") +_S("I") +_S("J") +_S("K") +_S("L") +_S("M") +_S("N") +_S("O") +_S("P") +_S("Q") +_S("R") +_S("S") +_S("T") +_S("U") +_S("V") +_S("W") +_S("X") +_S("Y") +_S("Z") +_S("bracketleft") +_S("backslash") +_S("bracketright") +_S("asciicircum") +_S("underscore") +_S("quoteleft") +_S("a") +_S("b") +_S("c") +_S("d") +_S("e") +_S("f") +_S("g") +_S("h") +_S("i") +_S("j") +_S("k") +_S("l") +_S("m") +_S("n") +_S("o") +_S("p") +_S("q") +_S("r") +_S("s") +_S("t") +_S("u") +_S("v") +_S("w") +_S("x") +_S("y") +_S("z") +_S("braceleft") +_S("bar") +_S("braceright") +_S("asciitilde") +_S("exclamdown") +_S("cent") +_S("sterling") +_S("fraction") +_S("yen") +_S("florin") +_S("section") +_S("currency") +_S("quotesingle") +_S("quotedblleft") +_S("guillemotleft") +_S("guilsinglleft") +_S("guilsinglright") +_S("fi") +_S("fl") +_S("endash") +_S("dagger") +_S("daggerdbl") +_S("periodcentered") +_S("paragraph") +_S("bullet") +_S("quotesinglbase") +_S("quotedblbase") +_S("quotedblright") +_S("guillemotright") +_S("ellipsis") +_S("perthousand") +_S("questiondown") +_S("grave") +_S("acute") +_S("circumflex") +_S("tilde") +_S("macron") +_S("breve") +_S("dotaccent") +_S("dieresis") +_S("ring") +_S("cedilla") +_S("hungarumlaut") +_S("ogonek") +_S("caron") +_S("emdash") +_S("AE") +_S("ordfeminine") +_S("Lslash") +_S("Oslash") +_S("OE") +_S("ordmasculine") +_S("ae") +_S("dotlessi") +_S("lslash") +_S("oslash") +_S("oe") +_S("germandbls") +_S("onesuperior") +_S("logicalnot") +_S("mu") +_S("trademark") +_S("Eth") +_S("onehalf") +_S("plusminus") +_S("Thorn") +_S("onequarter") +_S("divide") +_S("brokenbar") +_S("degree") +_S("thorn") +_S("threequarters") +_S("twosuperior") +_S("registered") +_S("minus") +_S("eth") +_S("multiply") +_S("threesuperior") +_S("copyright") +_S("Aacute") +_S("Acircumflex") +_S("Adieresis") +_S("Agrave") +_S("Aring") +_S("Atilde") +_S("Ccedilla") +_S("Eacute") +_S("Ecircumflex") +_S("Edieresis") +_S("Egrave") +_S("Iacute") +_S("Icircumflex") +_S("Idieresis") +_S("Igrave") +_S("Ntilde") +_S("Oacute") +_S("Ocircumflex") +_S("Odieresis") +_S("Ograve") +_S("Otilde") +_S("Scaron") +_S("Uacute") +_S("Ucircumflex") +_S("Udieresis") +_S("Ugrave") +_S("Yacute") +_S("Ydieresis") +_S("Zcaron") +_S("aacute") +_S("acircumflex") +_S("adieresis") +_S("agrave") +_S("aring") +_S("atilde") +_S("ccedilla") +_S("eacute") +_S("ecircumflex") +_S("edieresis") +_S("egrave") +_S("iacute") +_S("icircumflex") +_S("idieresis") +_S("igrave") +_S("ntilde") +_S("oacute") +_S("ocircumflex") +_S("odieresis") +_S("ograve") +_S("otilde") +_S("scaron") +_S("uacute") +_S("ucircumflex") +_S("udieresis") +_S("ugrave") +_S("yacute") +_S("ydieresis") +_S("zcaron") +_S("exclamsmall") +_S("Hungarumlautsmall") +_S("dollaroldstyle") +_S("dollarsuperior") +_S("ampersandsmall") +_S("Acutesmall") +_S("parenleftsuperior") +_S("parenrightsuperior") +_S("twodotenleader") +_S("onedotenleader") +_S("zerooldstyle") +_S("oneoldstyle") +_S("twooldstyle") +_S("threeoldstyle") +_S("fouroldstyle") +_S("fiveoldstyle") +_S("sixoldstyle") +_S("sevenoldstyle") +_S("eightoldstyle") +_S("nineoldstyle") +_S("commasuperior") +_S("threequartersemdash") +_S("periodsuperior") +_S("questionsmall") +_S("asuperior") +_S("bsuperior") +_S("centsuperior") +_S("dsuperior") +_S("esuperior") +_S("isuperior") +_S("lsuperior") +_S("msuperior") +_S("nsuperior") +_S("osuperior") +_S("rsuperior") +_S("ssuperior") +_S("tsuperior") +_S("ff") +_S("ffi") +_S("ffl") +_S("parenleftinferior") +_S("parenrightinferior") +_S("Circumflexsmall") +_S("hyphensuperior") +_S("Gravesmall") +_S("Asmall") +_S("Bsmall") +_S("Csmall") +_S("Dsmall") +_S("Esmall") +_S("Fsmall") +_S("Gsmall") +_S("Hsmall") +_S("Ismall") +_S("Jsmall") +_S("Ksmall") +_S("Lsmall") +_S("Msmall") +_S("Nsmall") +_S("Osmall") +_S("Psmall") +_S("Qsmall") +_S("Rsmall") +_S("Ssmall") +_S("Tsmall") +_S("Usmall") +_S("Vsmall") +_S("Wsmall") +_S("Xsmall") +_S("Ysmall") +_S("Zsmall") +_S("colonmonetary") +_S("onefitted") +_S("rupiah") +_S("Tildesmall") +_S("exclamdownsmall") +_S("centoldstyle") +_S("Lslashsmall") +_S("Scaronsmall") +_S("Zcaronsmall") +_S("Dieresissmall") +_S("Brevesmall") +_S("Caronsmall") +_S("Dotaccentsmall") +_S("Macronsmall") +_S("figuredash") +_S("hypheninferior") +_S("Ogoneksmall") +_S("Ringsmall") +_S("Cedillasmall") +_S("questiondownsmall") +_S("oneeighth") +_S("threeeighths") +_S("fiveeighths") +_S("seveneighths") +_S("onethird") +_S("twothirds") +_S("zerosuperior") +_S("foursuperior") +_S("fivesuperior") +_S("sixsuperior") +_S("sevensuperior") +_S("eightsuperior") +_S("ninesuperior") +_S("zeroinferior") +_S("oneinferior") +_S("twoinferior") +_S("threeinferior") +_S("fourinferior") +_S("fiveinferior") +_S("sixinferior") +_S("seveninferior") +_S("eightinferior") +_S("nineinferior") +_S("centinferior") +_S("dollarinferior") +_S("periodinferior") +_S("commainferior") +_S("Agravesmall") +_S("Aacutesmall") +_S("Acircumflexsmall") +_S("Atildesmall") +_S("Adieresissmall") +_S("Aringsmall") +_S("AEsmall") +_S("Ccedillasmall") +_S("Egravesmall") +_S("Eacutesmall") +_S("Ecircumflexsmall") +_S("Edieresissmall") +_S("Igravesmall") +_S("Iacutesmall") +_S("Icircumflexsmall") +_S("Idieresissmall") +_S("Ethsmall") +_S("Ntildesmall") +_S("Ogravesmall") +_S("Oacutesmall") +_S("Ocircumflexsmall") +_S("Otildesmall") +_S("Odieresissmall") +_S("OEsmall") +_S("Oslashsmall") +_S("Ugravesmall") +_S("Uacutesmall") +_S("Ucircumflexsmall") +_S("Udieresissmall") +_S("Yacutesmall") +_S("Thornsmall") +_S("Ydieresissmall") +_S("001.000") +_S("001.001") +_S("001.002") +_S("001.003") +_S("Black") +_S("Bold") +_S("Book") +_S("Light") +_S("Medium") +_S("Regular") +_S("Roman") +_S("Semibold") + +#endif /* HB_OT_CFF1_STD_STR_HH */ diff --git a/libs/harfbuzz/src/hb-ot-cff1-table.cc b/libs/harfbuzz/src/hb-ot-cff1-table.cc new file mode 100644 index 00000000..bd9fe5d6 --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-cff1-table.cc @@ -0,0 +1,609 @@ +/* + * Copyright © 2018 Adobe 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. + * + * Adobe Author(s): Michiharu Ariza + */ + +#include "hb.hh" + +#ifndef HB_NO_CFF + +#include "hb-draw.hh" +#include "hb-algs.hh" +#include "hb-ot-cff1-table.hh" +#include "hb-cff1-interp-cs.hh" + +using namespace CFF; + +struct sid_to_gid_t +{ + uint16_t sid; + uint8_t gid; + + int cmp (uint16_t a) const + { + if (a == sid) return 0; + return (a < sid) ? -1 : 1; + } +}; + +/* SID to code */ +static const uint8_t standard_encoding_to_code [] = +{ + 0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, + 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, + 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, + 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, + 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, + 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, + 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 177, + 178, 179, 180, 182, 183, 184, 185, 186, 187, 188, 189, 191, 193, 194, 195, 196, + 197, 198, 199, 200, 202, 203, 205, 206, 207, 208, 225, 227, 232, 233, 234, 235, + 241, 245, 248, 249, 250, 251 +}; + +/* SID to code */ +static const uint8_t expert_encoding_to_code [] = +{ + 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 44, 45, 46, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 58, 59, 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, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 87, 88, 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, 201, 0, 0, 0, 0, 189, 0, 0, 188, 0, + 0, 0, 0, 190, 202, 0, 0, 0, 0, 203, 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, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 33, 34, 36, 37, 38, 39, 40, 41, 42, 43, 48, + 49, 50, 51, 52, 53, 54, 55, 56, 57, 60, 61, 62, 63, 65, 66, 67, + 68, 69, 73, 76, 77, 78, 79, 82, 83, 84, 86, 89, 90, 91, 93, 94, + 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, + 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, + 161, 162, 163, 166, 167, 168, 169, 170, 172, 175, 178, 179, 182, 183, 184, 191, + 192, 193, 194, 195, 196, 197, 200, 204, 205, 206, 207, 208, 209, 210, 211, 212, + 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, + 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, + 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255 +}; + +/* glyph ID to SID */ +static const uint16_t expert_charset_to_sid [] = +{ + 0, 1, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 13, 14, 15, 99, + 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 27, 28, 249, 250, 251, 252, + 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 109, 110, + 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, + 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, + 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, + 315, 316, 317, 318, 158, 155, 163, 319, 320, 321, 322, 323, 324, 325, 326, 150, + 164, 169, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, + 341, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, 355, 356, + 357, 358, 359, 360, 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, + 373, 374, 375, 376, 377, 378 +}; + +/* glyph ID to SID */ +static const uint16_t expert_subset_charset_to_sid [] = +{ + 0, 1, 231, 232, 235, 236, 237, 238, 13, 14, 15, 99, 239, 240, 241, 242, + 243, 244, 245, 246, 247, 248, 27, 28, 249, 250, 251, 253, 254, 255, 256, 257, + 258, 259, 260, 261, 262, 263, 264, 265, 266, 109, 110, 267, 268, 269, 270, 272, + 300, 301, 302, 305, 314, 315, 158, 155, 163, 320, 321, 322, 323, 324, 325, 326, + 150, 164, 169, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, + 340, 341, 342, 343, 344, 345, 346 +}; + +/* SID to glyph ID */ +static const sid_to_gid_t expert_charset_sid_to_gid [] = +{ + { 1, 1 }, { 13, 12 }, { 14, 13 }, { 15, 14 }, + { 27, 26 }, { 28, 27 }, { 99, 15 }, { 109, 46 }, + { 110, 47 }, { 150, 111 }, { 155, 101 }, { 158, 100 }, + { 163, 102 }, { 164, 112 }, { 169, 113 }, { 229, 2 }, + { 230, 3 }, { 231, 4 }, { 232, 5 }, { 233, 6 }, + { 234, 7 }, { 235, 8 }, { 236, 9 }, { 237, 10 }, + { 238, 11 }, { 239, 16 }, { 240, 17 }, { 241, 18 }, + { 242, 19 }, { 243, 20 }, { 244, 21 }, { 245, 22 }, + { 246, 23 }, { 247, 24 }, { 248, 25 }, { 249, 28 }, + { 250, 29 }, { 251, 30 }, { 252, 31 }, { 253, 32 }, + { 254, 33 }, { 255, 34 }, { 256, 35 }, { 257, 36 }, + { 258, 37 }, { 259, 38 }, { 260, 39 }, { 261, 40 }, + { 262, 41 }, { 263, 42 }, { 264, 43 }, { 265, 44 }, + { 266, 45 }, { 267, 48 }, { 268, 49 }, { 269, 50 }, + { 270, 51 }, { 271, 52 }, { 272, 53 }, { 273, 54 }, + { 274, 55 }, { 275, 56 }, { 276, 57 }, { 277, 58 }, + { 278, 59 }, { 279, 60 }, { 280, 61 }, { 281, 62 }, + { 282, 63 }, { 283, 64 }, { 284, 65 }, { 285, 66 }, + { 286, 67 }, { 287, 68 }, { 288, 69 }, { 289, 70 }, + { 290, 71 }, { 291, 72 }, { 292, 73 }, { 293, 74 }, + { 294, 75 }, { 295, 76 }, { 296, 77 }, { 297, 78 }, + { 298, 79 }, { 299, 80 }, { 300, 81 }, { 301, 82 }, + { 302, 83 }, { 303, 84 }, { 304, 85 }, { 305, 86 }, + { 306, 87 }, { 307, 88 }, { 308, 89 }, { 309, 90 }, + { 310, 91 }, { 311, 92 }, { 312, 93 }, { 313, 94 }, + { 314, 95 }, { 315, 96 }, { 316, 97 }, { 317, 98 }, + { 318, 99 }, { 319, 103 }, { 320, 104 }, { 321, 105 }, + { 322, 106 }, { 323, 107 }, { 324, 108 }, { 325, 109 }, + { 326, 110 }, { 327, 114 }, { 328, 115 }, { 329, 116 }, + { 330, 117 }, { 331, 118 }, { 332, 119 }, { 333, 120 }, + { 334, 121 }, { 335, 122 }, { 336, 123 }, { 337, 124 }, + { 338, 125 }, { 339, 126 }, { 340, 127 }, { 341, 128 }, + { 342, 129 }, { 343, 130 }, { 344, 131 }, { 345, 132 }, + { 346, 133 }, { 347, 134 }, { 348, 135 }, { 349, 136 }, + { 350, 137 }, { 351, 138 }, { 352, 139 }, { 353, 140 }, + { 354, 141 }, { 355, 142 }, { 356, 143 }, { 357, 144 }, + { 358, 145 }, { 359, 146 }, { 360, 147 }, { 361, 148 }, + { 362, 149 }, { 363, 150 }, { 364, 151 }, { 365, 152 }, + { 366, 153 }, { 367, 154 }, { 368, 155 }, { 369, 156 }, + { 370, 157 }, { 371, 158 }, { 372, 159 }, { 373, 160 }, + { 374, 161 }, { 375, 162 }, { 376, 163 }, { 377, 164 }, + { 378, 165 } +}; + +/* SID to glyph ID */ +static const sid_to_gid_t expert_subset_charset_sid_to_gid [] = +{ + { 1, 1 }, { 13, 8 }, { 14, 9 }, { 15, 10 }, + { 27, 22 }, { 28, 23 }, { 99, 11 }, { 109, 41 }, + { 110, 42 }, { 150, 64 }, { 155, 55 }, { 158, 54 }, + { 163, 56 }, { 164, 65 }, { 169, 66 }, { 231, 2 }, + { 232, 3 }, { 235, 4 }, { 236, 5 }, { 237, 6 }, + { 238, 7 }, { 239, 12 }, { 240, 13 }, { 241, 14 }, + { 242, 15 }, { 243, 16 }, { 244, 17 }, { 245, 18 }, + { 246, 19 }, { 247, 20 }, { 248, 21 }, { 249, 24 }, + { 250, 25 }, { 251, 26 }, { 253, 27 }, { 254, 28 }, + { 255, 29 }, { 256, 30 }, { 257, 31 }, { 258, 32 }, + { 259, 33 }, { 260, 34 }, { 261, 35 }, { 262, 36 }, + { 263, 37 }, { 264, 38 }, { 265, 39 }, { 266, 40 }, + { 267, 43 }, { 268, 44 }, { 269, 45 }, { 270, 46 }, + { 272, 47 }, { 300, 48 }, { 301, 49 }, { 302, 50 }, + { 305, 51 }, { 314, 52 }, { 315, 53 }, { 320, 57 }, + { 321, 58 }, { 322, 59 }, { 323, 60 }, { 324, 61 }, + { 325, 62 }, { 326, 63 }, { 327, 67 }, { 328, 68 }, + { 329, 69 }, { 330, 70 }, { 331, 71 }, { 332, 72 }, + { 333, 73 }, { 334, 74 }, { 335, 75 }, { 336, 76 }, + { 337, 77 }, { 338, 78 }, { 339, 79 }, { 340, 80 }, + { 341, 81 }, { 342, 82 }, { 343, 83 }, { 344, 84 }, + { 345, 85 }, { 346, 86 } +}; + +/* code to SID */ +static const uint8_t standard_encoding_to_sid [] = +{ + 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, + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 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, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, + 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, + 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 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, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, + 0, 111, 112, 113, 114, 0, 115, 116, 117, 118, 119, 120, 121, 122, 0, 123, + 0, 124, 125, 126, 127, 128, 129, 130, 131, 0, 132, 133, 0, 134, 135, 136, + 137, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 138, 0, 139, 0, 0, 0, 0, 140, 141, 142, 143, 0, 0, 0, 0, + 0, 144, 0, 0, 0, 145, 0, 0, 146, 147, 148, 149, 0, 0, 0, 0 +}; + +hb_codepoint_t OT::cff1::lookup_standard_encoding_for_code (hb_codepoint_t sid) +{ + if (sid < ARRAY_LENGTH (standard_encoding_to_code)) + return (hb_codepoint_t)standard_encoding_to_code[sid]; + else + return 0; +} + +hb_codepoint_t OT::cff1::lookup_expert_encoding_for_code (hb_codepoint_t sid) +{ + if (sid < ARRAY_LENGTH (expert_encoding_to_code)) + return (hb_codepoint_t)expert_encoding_to_code[sid]; + else + return 0; +} + +hb_codepoint_t OT::cff1::lookup_expert_charset_for_sid (hb_codepoint_t glyph) +{ + if (glyph < ARRAY_LENGTH (expert_charset_to_sid)) + return (hb_codepoint_t)expert_charset_to_sid[glyph]; + else + return 0; +} + +hb_codepoint_t OT::cff1::lookup_expert_subset_charset_for_sid (hb_codepoint_t glyph) +{ + if (glyph < ARRAY_LENGTH (expert_subset_charset_to_sid)) + return (hb_codepoint_t)expert_subset_charset_to_sid[glyph]; + else + return 0; +} + +hb_codepoint_t OT::cff1::lookup_expert_charset_for_glyph (hb_codepoint_t sid) +{ + const auto *pair = hb_sorted_array (expert_charset_sid_to_gid).bsearch (sid); + return pair ? pair->gid : 0; +} + +hb_codepoint_t OT::cff1::lookup_expert_subset_charset_for_glyph (hb_codepoint_t sid) +{ + const auto *pair = hb_sorted_array (expert_subset_charset_sid_to_gid).bsearch (sid); + return pair ? pair->gid : 0; +} + +hb_codepoint_t OT::cff1::lookup_standard_encoding_for_sid (hb_codepoint_t code) +{ + if (code < ARRAY_LENGTH (standard_encoding_to_sid)) + return (hb_codepoint_t)standard_encoding_to_sid[code]; + else + return CFF_UNDEF_SID; +} + +struct bounds_t +{ + void init () + { + min.set_int (INT_MAX, INT_MAX); + max.set_int (INT_MIN, INT_MIN); + } + + void update (const point_t &pt) + { + if (pt.x < min.x) min.x = pt.x; + if (pt.x > max.x) max.x = pt.x; + if (pt.y < min.y) min.y = pt.y; + if (pt.y > max.y) max.y = pt.y; + } + + void merge (const bounds_t &b) + { + if (empty ()) + *this = b; + else if (!b.empty ()) + { + if (b.min.x < min.x) min.x = b.min.x; + if (b.max.x > max.x) max.x = b.max.x; + if (b.min.y < min.y) min.y = b.min.y; + if (b.max.y > max.y) max.y = b.max.y; + } + } + + void offset (const point_t &delta) + { + if (!empty ()) + { + min.move (delta); + max.move (delta); + } + } + + bool empty () const { return (min.x >= max.x) || (min.y >= max.y); } + + point_t min; + point_t max; +}; + +struct cff1_extents_param_t +{ + cff1_extents_param_t (const OT::cff1::accelerator_t *_cff) : cff (_cff) + { + bounds.init (); + } + + void start_path () { path_open = true; } + void end_path () { path_open = false; } + bool is_path_open () const { return path_open; } + + bool path_open = false; + bounds_t bounds; + + const OT::cff1::accelerator_t *cff; +}; + +struct cff1_path_procs_extents_t : path_procs_t +{ + static void moveto (cff1_cs_interp_env_t &env, cff1_extents_param_t& param, const point_t &pt) + { + param.end_path (); + env.moveto (pt); + } + + static void line (cff1_cs_interp_env_t &env, cff1_extents_param_t& param, const point_t &pt1) + { + if (!param.is_path_open ()) + { + param.start_path (); + param.bounds.update (env.get_pt ()); + } + env.moveto (pt1); + param.bounds.update (env.get_pt ()); + } + + static void curve (cff1_cs_interp_env_t &env, cff1_extents_param_t& param, const point_t &pt1, const point_t &pt2, const point_t &pt3) + { + if (!param.is_path_open ()) + { + param.start_path (); + param.bounds.update (env.get_pt ()); + } + /* include control points */ + param.bounds.update (pt1); + param.bounds.update (pt2); + env.moveto (pt3); + param.bounds.update (env.get_pt ()); + } +}; + +static bool _get_bounds (const OT::cff1::accelerator_t *cff, hb_codepoint_t glyph, bounds_t &bounds, bool in_seac=false); + +struct cff1_cs_opset_extents_t : cff1_cs_opset_t +{ + static void process_seac (cff1_cs_interp_env_t &env, cff1_extents_param_t& param) + { + unsigned int n = env.argStack.get_count (); + point_t delta; + delta.x = env.argStack[n-4]; + delta.y = env.argStack[n-3]; + hb_codepoint_t base = param.cff->std_code_to_glyph (env.argStack[n-2].to_int ()); + hb_codepoint_t accent = param.cff->std_code_to_glyph (env.argStack[n-1].to_int ()); + + bounds_t base_bounds, accent_bounds; + if (likely (!env.in_seac && base && accent + && _get_bounds (param.cff, base, base_bounds, true) + && _get_bounds (param.cff, accent, accent_bounds, true))) + { + param.bounds.merge (base_bounds); + accent_bounds.offset (delta); + param.bounds.merge (accent_bounds); + } + else + env.set_error (); + } +}; + +bool _get_bounds (const OT::cff1::accelerator_t *cff, hb_codepoint_t glyph, bounds_t &bounds, bool in_seac) +{ + bounds.init (); + if (unlikely (!cff->is_valid () || (glyph >= cff->num_glyphs))) return false; + + unsigned int fd = cff->fdSelect->get_fd (glyph); + const hb_ubytes_t str = (*cff->charStrings)[glyph]; + cff1_cs_interp_env_t env (str, *cff, fd); + env.set_in_seac (in_seac); + cff1_cs_interpreter_t interp (env); + cff1_extents_param_t param (cff); + if (unlikely (!interp.interpret (param))) return false; + bounds = param.bounds; + return true; +} + +bool OT::cff1::accelerator_t::get_extents (hb_font_t *font, hb_codepoint_t glyph, hb_glyph_extents_t *extents) const +{ +#ifdef HB_NO_OT_FONT_CFF + /* XXX Remove check when this code moves to .hh file. */ + return true; +#endif + + bounds_t bounds; + + if (!_get_bounds (this, glyph, bounds)) + return false; + + if (bounds.min.x >= bounds.max.x) + { + extents->width = 0; + extents->x_bearing = 0; + } + else + { + extents->x_bearing = font->em_scalef_x (bounds.min.x.to_real ()); + extents->width = font->em_scalef_x (bounds.max.x.to_real ()) - extents->x_bearing; + } + if (bounds.min.y >= bounds.max.y) + { + extents->height = 0; + extents->y_bearing = 0; + } + else + { + extents->y_bearing = font->em_scalef_y (bounds.max.y.to_real ()); + extents->height = font->em_scalef_y (bounds.min.y.to_real ()) - extents->y_bearing; + } + + return true; +} + +struct cff1_path_param_t +{ + cff1_path_param_t (const OT::cff1::accelerator_t *cff_, hb_font_t *font_, + hb_draw_session_t &draw_session_, point_t *delta_) + { + draw_session = &draw_session_; + cff = cff_; + font = font_; + delta = delta_; + } + + void move_to (const point_t &p) + { + point_t point = p; + if (delta) point.move (*delta); + draw_session->move_to (font->em_fscalef_x (point.x.to_real ()), font->em_fscalef_y (point.y.to_real ())); + } + + void line_to (const point_t &p) + { + point_t point = p; + if (delta) point.move (*delta); + draw_session->line_to (font->em_fscalef_x (point.x.to_real ()), font->em_fscalef_y (point.y.to_real ())); + } + + void cubic_to (const point_t &p1, const point_t &p2, const point_t &p3) + { + point_t point1 = p1, point2 = p2, point3 = p3; + if (delta) + { + point1.move (*delta); + point2.move (*delta); + point3.move (*delta); + } + draw_session->cubic_to (font->em_fscalef_x (point1.x.to_real ()), font->em_fscalef_y (point1.y.to_real ()), + font->em_fscalef_x (point2.x.to_real ()), font->em_fscalef_y (point2.y.to_real ()), + font->em_fscalef_x (point3.x.to_real ()), font->em_fscalef_y (point3.y.to_real ())); + } + + void end_path () { draw_session->close_path (); } + + hb_font_t *font; + hb_draw_session_t *draw_session; + point_t *delta; + + const OT::cff1::accelerator_t *cff; +}; + +struct cff1_path_procs_path_t : path_procs_t +{ + static void moveto (cff1_cs_interp_env_t &env, cff1_path_param_t& param, const point_t &pt) + { + param.move_to (pt); + env.moveto (pt); + } + + static void line (cff1_cs_interp_env_t &env, cff1_path_param_t ¶m, const point_t &pt1) + { + param.line_to (pt1); + env.moveto (pt1); + } + + static void curve (cff1_cs_interp_env_t &env, cff1_path_param_t ¶m, const point_t &pt1, const point_t &pt2, const point_t &pt3) + { + param.cubic_to (pt1, pt2, pt3); + env.moveto (pt3); + } +}; + +static bool _get_path (const OT::cff1::accelerator_t *cff, hb_font_t *font, hb_codepoint_t glyph, + hb_draw_session_t &draw_session, bool in_seac = false, point_t *delta = nullptr); + +struct cff1_cs_opset_path_t : cff1_cs_opset_t +{ + static void process_seac (cff1_cs_interp_env_t &env, cff1_path_param_t& param) + { + /* End previous path */ + param.end_path (); + + unsigned int n = env.argStack.get_count (); + point_t delta; + delta.x = env.argStack[n-4]; + delta.y = env.argStack[n-3]; + hb_codepoint_t base = param.cff->std_code_to_glyph (env.argStack[n-2].to_int ()); + hb_codepoint_t accent = param.cff->std_code_to_glyph (env.argStack[n-1].to_int ()); + + if (unlikely (!(!env.in_seac && base && accent + && _get_path (param.cff, param.font, base, *param.draw_session, true) + && _get_path (param.cff, param.font, accent, *param.draw_session, true, &delta)))) + env.set_error (); + } +}; + +bool _get_path (const OT::cff1::accelerator_t *cff, hb_font_t *font, hb_codepoint_t glyph, + hb_draw_session_t &draw_session, bool in_seac, point_t *delta) +{ + if (unlikely (!cff->is_valid () || (glyph >= cff->num_glyphs))) return false; + + unsigned int fd = cff->fdSelect->get_fd (glyph); + const hb_ubytes_t str = (*cff->charStrings)[glyph]; + cff1_cs_interp_env_t env (str, *cff, fd); + env.set_in_seac (in_seac); + cff1_cs_interpreter_t interp (env); + cff1_path_param_t param (cff, font, draw_session, delta); + if (unlikely (!interp.interpret (param))) return false; + + /* Let's end the path specially since it is called inside seac also */ + param.end_path (); + + return true; +} + +bool OT::cff1::accelerator_t::get_path (hb_font_t *font, hb_codepoint_t glyph, hb_draw_session_t &draw_session) const +{ +#ifdef HB_NO_OT_FONT_CFF + /* XXX Remove check when this code moves to .hh file. */ + return true; +#endif + + return _get_path (this, font, glyph, draw_session); +} + +struct get_seac_param_t +{ + get_seac_param_t (const OT::cff1::accelerator_t *_cff) : cff (_cff) {} + + bool has_seac () const { return base && accent; } + + const OT::cff1::accelerator_t *cff; + hb_codepoint_t base = 0; + hb_codepoint_t accent = 0; +}; + +struct cff1_cs_opset_seac_t : cff1_cs_opset_t +{ + static void process_seac (cff1_cs_interp_env_t &env, get_seac_param_t& param) + { + unsigned int n = env.argStack.get_count (); + hb_codepoint_t base_char = (hb_codepoint_t)env.argStack[n-2].to_int (); + hb_codepoint_t accent_char = (hb_codepoint_t)env.argStack[n-1].to_int (); + + param.base = param.cff->std_code_to_glyph (base_char); + param.accent = param.cff->std_code_to_glyph (accent_char); + } +}; + +bool OT::cff1::accelerator_t::get_seac_components (hb_codepoint_t glyph, hb_codepoint_t *base, hb_codepoint_t *accent) const +{ + if (unlikely (!is_valid () || (glyph >= num_glyphs))) return false; + + unsigned int fd = fdSelect->get_fd (glyph); + const hb_ubytes_t str = (*charStrings)[glyph]; + cff1_cs_interp_env_t env (str, *this, fd); + cff1_cs_interpreter_t interp (env); + get_seac_param_t param (this); + if (unlikely (!interp.interpret (param))) return false; + + if (param.has_seac ()) + { + *base = param.base; + *accent = param.accent; + return true; + } + return false; +} + + +#endif diff --git a/libs/harfbuzz/src/hb-ot-cff1-table.hh b/libs/harfbuzz/src/hb-ot-cff1-table.hh new file mode 100644 index 00000000..17b02966 --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-cff1-table.hh @@ -0,0 +1,1486 @@ +/* + * Copyright © 2018 Adobe 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. + * + * Adobe Author(s): Michiharu Ariza + */ + +#ifndef HB_OT_CFF1_TABLE_HH +#define HB_OT_CFF1_TABLE_HH + +#include "hb-ot-cff-common.hh" +#include "hb-subset-cff1.hh" +#include "hb-draw.hh" + +#define HB_STRING_ARRAY_NAME cff1_std_strings +#define HB_STRING_ARRAY_LIST "hb-ot-cff1-std-str.hh" +#include "hb-string-array.hh" +#undef HB_STRING_ARRAY_LIST +#undef HB_STRING_ARRAY_NAME + +namespace CFF { + +/* + * CFF -- Compact Font Format (CFF) + * https://www.adobe.com/content/dam/acom/en/devnet/font/pdfs/5176.CFF.pdf + */ +#define HB_OT_TAG_cff1 HB_TAG('C','F','F',' ') + +#define CFF_UNDEF_SID CFF_UNDEF_CODE + +enum EncodingID { StandardEncoding = 0, ExpertEncoding = 1 }; +enum CharsetID { ISOAdobeCharset = 0, ExpertCharset = 1, ExpertSubsetCharset = 2 }; + +typedef CFFIndex CFF1Index; +template struct CFF1IndexOf : CFFIndexOf {}; + +typedef CFFIndex CFF1Index; +typedef CFF1Index CFF1CharStrings; +typedef Subrs CFF1Subrs; + +struct CFF1FDSelect : FDSelect {}; + +/* Encoding */ +struct Encoding0 { + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (codes.sanitize (c)); + } + + hb_codepoint_t get_code (hb_codepoint_t glyph) const + { + assert (glyph > 0); + glyph--; + if (glyph < nCodes ()) + { + return (hb_codepoint_t)codes[glyph]; + } + else + return CFF_UNDEF_CODE; + } + + HBUINT8 &nCodes () { return codes.len; } + HBUINT8 nCodes () const { return codes.len; } + + ArrayOf codes; + + DEFINE_SIZE_ARRAY_SIZED (1, codes); +}; + +struct Encoding1_Range { + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + HBUINT8 first; + HBUINT8 nLeft; + + DEFINE_SIZE_STATIC (2); +}; + +struct Encoding1 { + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (ranges.sanitize (c)); + } + + hb_codepoint_t get_code (hb_codepoint_t glyph) const + { + assert (glyph > 0); + glyph--; + for (unsigned int i = 0; i < nRanges (); i++) + { + if (glyph <= ranges[i].nLeft) + { + hb_codepoint_t code = (hb_codepoint_t) ranges[i].first + glyph; + return (likely (code < 0x100) ? code: CFF_UNDEF_CODE); + } + glyph -= (ranges[i].nLeft + 1); + } + return CFF_UNDEF_CODE; + } + + HBUINT8 &nRanges () { return ranges.len; } + HBUINT8 nRanges () const { return ranges.len; } + + ArrayOf ranges; + + DEFINE_SIZE_ARRAY_SIZED (1, ranges); +}; + +struct SuppEncoding { + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + HBUINT8 code; + HBUINT16 glyph; + + DEFINE_SIZE_STATIC (3); +}; + +struct CFF1SuppEncData { + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (supps.sanitize (c)); + } + + void get_codes (hb_codepoint_t sid, hb_vector_t &codes) const + { + for (unsigned int i = 0; i < nSups (); i++) + if (sid == supps[i].glyph) + codes.push (supps[i].code); + } + + HBUINT8 &nSups () { return supps.len; } + HBUINT8 nSups () const { return supps.len; } + + ArrayOf supps; + + DEFINE_SIZE_ARRAY_SIZED (1, supps); +}; + +struct Encoding +{ + /* serialize a fullset Encoding */ + bool serialize (hb_serialize_context_t *c, const Encoding &src) + { + TRACE_SERIALIZE (this); + unsigned int size = src.get_size (); + Encoding *dest = c->allocate_size (size); + if (unlikely (!dest)) return_trace (false); + memcpy (dest, &src, size); + return_trace (true); + } + + /* serialize a subset Encoding */ + bool serialize (hb_serialize_context_t *c, + uint8_t format, + unsigned int enc_count, + const hb_vector_t& code_ranges, + const hb_vector_t& supp_codes) + { + TRACE_SERIALIZE (this); + Encoding *dest = c->extend_min (this); + if (unlikely (!dest)) return_trace (false); + dest->format = format | ((supp_codes.length > 0) ? 0x80 : 0); + switch (format) { + case 0: + { + Encoding0 *fmt0 = c->allocate_size (Encoding0::min_size + HBUINT8::static_size * enc_count); + if (unlikely (!fmt0)) return_trace (false); + fmt0->nCodes () = enc_count; + unsigned int glyph = 0; + for (unsigned int i = 0; i < code_ranges.length; i++) + { + hb_codepoint_t code = code_ranges[i].code; + for (int left = (int)code_ranges[i].glyph; left >= 0; left--) + fmt0->codes[glyph++] = code++; + if (unlikely (!((glyph <= 0x100) && (code <= 0x100)))) + return_trace (false); + } + } + break; + + case 1: + { + Encoding1 *fmt1 = c->allocate_size (Encoding1::min_size + Encoding1_Range::static_size * code_ranges.length); + if (unlikely (!fmt1)) return_trace (false); + fmt1->nRanges () = code_ranges.length; + for (unsigned int i = 0; i < code_ranges.length; i++) + { + if (unlikely (!((code_ranges[i].code <= 0xFF) && (code_ranges[i].glyph <= 0xFF)))) + return_trace (false); + fmt1->ranges[i].first = code_ranges[i].code; + fmt1->ranges[i].nLeft = code_ranges[i].glyph; + } + } + break; + + } + + if (supp_codes.length) + { + CFF1SuppEncData *suppData = c->allocate_size (CFF1SuppEncData::min_size + SuppEncoding::static_size * supp_codes.length); + if (unlikely (!suppData)) return_trace (false); + suppData->nSups () = supp_codes.length; + for (unsigned int i = 0; i < supp_codes.length; i++) + { + suppData->supps[i].code = supp_codes[i].code; + suppData->supps[i].glyph = supp_codes[i].glyph; /* actually SID */ + } + } + + return_trace (true); + } + + unsigned int get_size () const + { + unsigned int size = min_size; + switch (table_format ()) + { + case 0: size += u.format0.get_size (); break; + case 1: size += u.format1.get_size (); break; + } + if (has_supplement ()) + size += suppEncData ().get_size (); + return size; + } + + hb_codepoint_t get_code (hb_codepoint_t glyph) const + { + switch (table_format ()) + { + case 0: return u.format0.get_code (glyph); + case 1: return u.format1.get_code (glyph); + default:return 0; + } + } + + uint8_t table_format () const { return format & 0x7F; } + bool has_supplement () const { return format & 0x80; } + + void get_supplement_codes (hb_codepoint_t sid, hb_vector_t &codes) const + { + codes.resize (0); + if (has_supplement ()) + suppEncData().get_codes (sid, codes); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (unlikely (!c->check_struct (this))) + return_trace (false); + + switch (table_format ()) + { + case 0: if (unlikely (!u.format0.sanitize (c))) { return_trace (false); } break; + case 1: if (unlikely (!u.format1.sanitize (c))) { return_trace (false); } break; + default:return_trace (false); + } + return_trace (likely (!has_supplement () || suppEncData ().sanitize (c))); + } + + protected: + const CFF1SuppEncData &suppEncData () const + { + switch (table_format ()) + { + case 0: return StructAfter (u.format0.codes[u.format0.nCodes ()-1]); + case 1: return StructAfter (u.format1.ranges[u.format1.nRanges ()-1]); + default:return Null (CFF1SuppEncData); + } + } + + public: + HBUINT8 format; + union { + Encoding0 format0; + Encoding1 format1; + } u; + /* CFF1SuppEncData suppEncData; */ + + DEFINE_SIZE_MIN (1); +}; + +/* Charset */ +struct Charset0 { + bool sanitize (hb_sanitize_context_t *c, unsigned int num_glyphs) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && sids[num_glyphs - 1].sanitize (c)); + } + + hb_codepoint_t get_sid (hb_codepoint_t glyph, unsigned num_glyphs) const + { + if (unlikely (glyph >= num_glyphs)) return 0; + if (glyph == 0) + return 0; + else + return sids[glyph - 1]; + } + + void collect_glyph_to_sid_map (hb_map_t *mapping, unsigned int num_glyphs) const + { + for (hb_codepoint_t gid = 1; gid < num_glyphs; gid++) + mapping->set (gid, sids[gid - 1]); + } + + hb_codepoint_t get_glyph (hb_codepoint_t sid, unsigned int num_glyphs) const + { + if (sid == 0) + return 0; + + for (unsigned int glyph = 1; glyph < num_glyphs; glyph++) + { + if (sids[glyph-1] == sid) + return glyph; + } + return 0; + } + + unsigned int get_size (unsigned int num_glyphs) const + { + assert (num_glyphs > 0); + return HBUINT16::static_size * (num_glyphs - 1); + } + + HBUINT16 sids[HB_VAR_ARRAY]; + + DEFINE_SIZE_ARRAY(0, sids); +}; + +template +struct Charset_Range { + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + HBUINT16 first; + TYPE nLeft; + + DEFINE_SIZE_STATIC (HBUINT16::static_size + TYPE::static_size); +}; + +template +struct Charset1_2 { + bool sanitize (hb_sanitize_context_t *c, unsigned int num_glyphs) const + { + TRACE_SANITIZE (this); + if (unlikely (!c->check_struct (this))) + return_trace (false); + num_glyphs--; + for (unsigned int i = 0; num_glyphs > 0; i++) + { + if (unlikely (!ranges[i].sanitize (c) || (num_glyphs < ranges[i].nLeft + 1))) + return_trace (false); + num_glyphs -= (ranges[i].nLeft + 1); + } + return_trace (true); + } + + hb_codepoint_t get_sid (hb_codepoint_t glyph, unsigned num_glyphs) const + { + if (unlikely (glyph >= num_glyphs)) return 0; + if (glyph == 0) return 0; + glyph--; + for (unsigned int i = 0;; i++) + { + if (glyph <= ranges[i].nLeft) + return (hb_codepoint_t) ranges[i].first + glyph; + glyph -= (ranges[i].nLeft + 1); + } + + return 0; + } + + void collect_glyph_to_sid_map (hb_map_t *mapping, unsigned int num_glyphs) const + { + hb_codepoint_t gid = 1; + if (gid >= num_glyphs) + return; + for (unsigned i = 0;; i++) + { + hb_codepoint_t sid = ranges[i].first; + unsigned count = ranges[i].nLeft + 1; + for (unsigned j = 0; j < count; j++) + mapping->set (gid++, sid++); + + if (gid >= num_glyphs) + break; + } + } + + hb_codepoint_t get_glyph (hb_codepoint_t sid, unsigned int num_glyphs) const + { + if (sid == 0) return 0; + hb_codepoint_t glyph = 1; + for (unsigned int i = 0;; i++) + { + if (glyph >= num_glyphs) + return 0; + if ((ranges[i].first <= sid) && (sid <= ranges[i].first + ranges[i].nLeft)) + return glyph + (sid - ranges[i].first); + glyph += (ranges[i].nLeft + 1); + } + + return 0; + } + + unsigned int get_size (unsigned int num_glyphs) const + { + unsigned int size = HBUINT8::static_size; + int glyph = (int)num_glyphs; + + assert (glyph > 0); + glyph--; + for (unsigned int i = 0; glyph > 0; i++) + { + glyph -= (ranges[i].nLeft + 1); + size += Charset_Range::static_size; + } + + return size; + } + + Charset_Range ranges[HB_VAR_ARRAY]; + + DEFINE_SIZE_ARRAY (0, ranges); +}; + +typedef Charset1_2 Charset1; +typedef Charset1_2 Charset2; +typedef Charset_Range Charset1_Range; +typedef Charset_Range Charset2_Range; + +struct Charset +{ + /* serialize a fullset Charset */ + bool serialize (hb_serialize_context_t *c, const Charset &src, unsigned int num_glyphs) + { + TRACE_SERIALIZE (this); + unsigned int size = src.get_size (num_glyphs); + Charset *dest = c->allocate_size (size); + if (unlikely (!dest)) return_trace (false); + memcpy (dest, &src, size); + return_trace (true); + } + + /* serialize a subset Charset */ + bool serialize (hb_serialize_context_t *c, + uint8_t format, + unsigned int num_glyphs, + const hb_vector_t& sid_ranges) + { + TRACE_SERIALIZE (this); + Charset *dest = c->extend_min (this); + if (unlikely (!dest)) return_trace (false); + dest->format = format; + switch (format) + { + case 0: + { + Charset0 *fmt0 = c->allocate_size (Charset0::min_size + HBUINT16::static_size * (num_glyphs - 1)); + if (unlikely (!fmt0)) return_trace (false); + unsigned int glyph = 0; + for (unsigned int i = 0; i < sid_ranges.length; i++) + { + hb_codepoint_t sid = sid_ranges[i].code; + for (int left = (int)sid_ranges[i].glyph; left >= 0; left--) + fmt0->sids[glyph++] = sid++; + } + } + break; + + case 1: + { + Charset1 *fmt1 = c->allocate_size (Charset1::min_size + Charset1_Range::static_size * sid_ranges.length); + if (unlikely (!fmt1)) return_trace (false); + for (unsigned int i = 0; i < sid_ranges.length; i++) + { + if (unlikely (!(sid_ranges[i].glyph <= 0xFF))) + return_trace (false); + fmt1->ranges[i].first = sid_ranges[i].code; + fmt1->ranges[i].nLeft = sid_ranges[i].glyph; + } + } + break; + + case 2: + { + Charset2 *fmt2 = c->allocate_size (Charset2::min_size + Charset2_Range::static_size * sid_ranges.length); + if (unlikely (!fmt2)) return_trace (false); + for (unsigned int i = 0; i < sid_ranges.length; i++) + { + if (unlikely (!(sid_ranges[i].glyph <= 0xFFFF))) + return_trace (false); + fmt2->ranges[i].first = sid_ranges[i].code; + fmt2->ranges[i].nLeft = sid_ranges[i].glyph; + } + } + break; + + } + return_trace (true); + } + + unsigned int get_size (unsigned int num_glyphs) const + { + switch (format) + { + case 0: return min_size + u.format0.get_size (num_glyphs); + case 1: return min_size + u.format1.get_size (num_glyphs); + case 2: return min_size + u.format2.get_size (num_glyphs); + default:return 0; + } + } + + hb_codepoint_t get_sid (hb_codepoint_t glyph, unsigned int num_glyphs) const + { + switch (format) + { + case 0: return u.format0.get_sid (glyph, num_glyphs); + case 1: return u.format1.get_sid (glyph, num_glyphs); + case 2: return u.format2.get_sid (glyph, num_glyphs); + default:return 0; + } + } + + void collect_glyph_to_sid_map (hb_map_t *mapping, unsigned int num_glyphs) const + { + switch (format) + { + case 0: u.format0.collect_glyph_to_sid_map (mapping, num_glyphs); return; + case 1: u.format1.collect_glyph_to_sid_map (mapping, num_glyphs); return; + case 2: u.format2.collect_glyph_to_sid_map (mapping, num_glyphs); return; + default:return; + } + } + + hb_codepoint_t get_glyph (hb_codepoint_t sid, unsigned int num_glyphs) const + { + switch (format) + { + case 0: return u.format0.get_glyph (sid, num_glyphs); + case 1: return u.format1.get_glyph (sid, num_glyphs); + case 2: return u.format2.get_glyph (sid, num_glyphs); + default:return 0; + } + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (unlikely (!c->check_struct (this))) + return_trace (false); + + switch (format) + { + case 0: return_trace (u.format0.sanitize (c, c->get_num_glyphs ())); + case 1: return_trace (u.format1.sanitize (c, c->get_num_glyphs ())); + case 2: return_trace (u.format2.sanitize (c, c->get_num_glyphs ())); + default:return_trace (false); + } + } + + HBUINT8 format; + union { + Charset0 format0; + Charset1 format1; + Charset2 format2; + } u; + + DEFINE_SIZE_MIN (1); +}; + +struct CFF1StringIndex : CFF1Index +{ + bool serialize (hb_serialize_context_t *c, const CFF1StringIndex &strings, + const hb_inc_bimap_t &sidmap) + { + TRACE_SERIALIZE (this); + if (unlikely ((strings.count == 0) || (sidmap.get_population () == 0))) + { + if (unlikely (!c->extend_min (this->count))) + return_trace (false); + count = 0; + return_trace (true); + } + + byte_str_array_t bytesArray; + bytesArray.init (); + if (!bytesArray.resize (sidmap.get_population ())) + return_trace (false); + for (unsigned int i = 0; i < strings.count; i++) + { + hb_codepoint_t j = sidmap[i]; + if (j != HB_MAP_VALUE_INVALID) + bytesArray[j] = strings[i]; + } + + bool result = CFF1Index::serialize (c, bytesArray); + bytesArray.fini (); + return_trace (result); + } +}; + +struct cff1_top_dict_interp_env_t : num_interp_env_t +{ + cff1_top_dict_interp_env_t () + : num_interp_env_t(), prev_offset(0), last_offset(0) {} + cff1_top_dict_interp_env_t (const hb_ubytes_t &bytes) + : num_interp_env_t(bytes), prev_offset(0), last_offset(0) {} + + unsigned int prev_offset; + unsigned int last_offset; +}; + +struct name_dict_values_t +{ + enum name_dict_val_index_t + { + version, + notice, + copyright, + fullName, + familyName, + weight, + postscript, + fontName, + baseFontName, + registry, + ordering, + + ValCount + }; + + void init () + { + for (unsigned int i = 0; i < ValCount; i++) + values[i] = CFF_UNDEF_SID; + } + + unsigned int& operator[] (unsigned int i) + { assert (i < ValCount); return values[i]; } + + unsigned int operator[] (unsigned int i) const + { assert (i < ValCount); return values[i]; } + + static enum name_dict_val_index_t name_op_to_index (op_code_t op) + { + switch (op) { + default: // can't happen - just make some compiler happy + case OpCode_version: + return version; + case OpCode_Notice: + return notice; + case OpCode_Copyright: + return copyright; + case OpCode_FullName: + return fullName; + case OpCode_FamilyName: + return familyName; + case OpCode_Weight: + return weight; + case OpCode_PostScript: + return postscript; + case OpCode_FontName: + return fontName; + case OpCode_BaseFontName: + return baseFontName; + } + } + + unsigned int values[ValCount]; +}; + +struct cff1_top_dict_val_t : op_str_t +{ + unsigned int last_arg_offset; +}; + +struct cff1_top_dict_values_t : top_dict_values_t +{ + void init () + { + top_dict_values_t::init (); + + nameSIDs.init (); + ros_supplement = 0; + cidCount = 8720; + EncodingOffset = 0; + CharsetOffset = 0; + FDSelectOffset = 0; + privateDictInfo.init (); + } + void fini () { top_dict_values_t::fini (); } + + bool is_CID () const + { return nameSIDs[name_dict_values_t::registry] != CFF_UNDEF_SID; } + + name_dict_values_t nameSIDs; + unsigned int ros_supplement_offset; + unsigned int ros_supplement; + unsigned int cidCount; + + unsigned int EncodingOffset; + unsigned int CharsetOffset; + unsigned int FDSelectOffset; + table_info_t privateDictInfo; +}; + +struct cff1_top_dict_opset_t : top_dict_opset_t +{ + static void process_op (op_code_t op, cff1_top_dict_interp_env_t& env, cff1_top_dict_values_t& dictval) + { + cff1_top_dict_val_t val; + val.last_arg_offset = (env.last_offset-1) - dictval.opStart; /* offset to the last argument */ + + switch (op) { + case OpCode_version: + case OpCode_Notice: + case OpCode_Copyright: + case OpCode_FullName: + case OpCode_FontName: + case OpCode_FamilyName: + case OpCode_Weight: + case OpCode_PostScript: + case OpCode_BaseFontName: + dictval.nameSIDs[name_dict_values_t::name_op_to_index (op)] = env.argStack.pop_uint (); + env.clear_args (); + break; + case OpCode_isFixedPitch: + case OpCode_ItalicAngle: + case OpCode_UnderlinePosition: + case OpCode_UnderlineThickness: + case OpCode_PaintType: + case OpCode_CharstringType: + case OpCode_UniqueID: + case OpCode_StrokeWidth: + case OpCode_SyntheticBase: + case OpCode_CIDFontVersion: + case OpCode_CIDFontRevision: + case OpCode_CIDFontType: + case OpCode_UIDBase: + case OpCode_FontBBox: + case OpCode_XUID: + case OpCode_BaseFontBlend: + env.clear_args (); + break; + + case OpCode_CIDCount: + dictval.cidCount = env.argStack.pop_uint (); + env.clear_args (); + break; + + case OpCode_ROS: + dictval.ros_supplement = env.argStack.pop_uint (); + dictval.nameSIDs[name_dict_values_t::ordering] = env.argStack.pop_uint (); + dictval.nameSIDs[name_dict_values_t::registry] = env.argStack.pop_uint (); + env.clear_args (); + break; + + case OpCode_Encoding: + dictval.EncodingOffset = env.argStack.pop_uint (); + env.clear_args (); + if (unlikely (dictval.EncodingOffset == 0)) return; + break; + + case OpCode_charset: + dictval.CharsetOffset = env.argStack.pop_uint (); + env.clear_args (); + if (unlikely (dictval.CharsetOffset == 0)) return; + break; + + case OpCode_FDSelect: + dictval.FDSelectOffset = env.argStack.pop_uint (); + env.clear_args (); + break; + + case OpCode_Private: + dictval.privateDictInfo.offset = env.argStack.pop_uint (); + dictval.privateDictInfo.size = env.argStack.pop_uint (); + env.clear_args (); + break; + + default: + env.last_offset = env.str_ref.offset; + top_dict_opset_t::process_op (op, env, dictval); + /* Record this operand below if stack is empty, otherwise done */ + if (!env.argStack.is_empty ()) return; + break; + } + + if (unlikely (env.in_error ())) return; + + dictval.add_op (op, env.str_ref, val); + } +}; + +struct cff1_font_dict_values_t : dict_values_t +{ + void init () + { + dict_values_t::init (); + privateDictInfo.init (); + fontName = CFF_UNDEF_SID; + } + void fini () { dict_values_t::fini (); } + + table_info_t privateDictInfo; + unsigned int fontName; +}; + +struct cff1_font_dict_opset_t : dict_opset_t +{ + static void process_op (op_code_t op, num_interp_env_t& env, cff1_font_dict_values_t& dictval) + { + switch (op) { + case OpCode_FontName: + dictval.fontName = env.argStack.pop_uint (); + env.clear_args (); + break; + case OpCode_FontMatrix: + case OpCode_PaintType: + env.clear_args (); + break; + case OpCode_Private: + dictval.privateDictInfo.offset = env.argStack.pop_uint (); + dictval.privateDictInfo.size = env.argStack.pop_uint (); + env.clear_args (); + break; + + default: + dict_opset_t::process_op (op, env); + if (!env.argStack.is_empty ()) return; + break; + } + + if (unlikely (env.in_error ())) return; + + dictval.add_op (op, env.str_ref); + } +}; + +template +struct cff1_private_dict_values_base_t : dict_values_t +{ + void init () + { + dict_values_t::init (); + subrsOffset = 0; + localSubrs = &Null (CFF1Subrs); + } + void fini () { dict_values_t::fini (); } + + unsigned int subrsOffset; + const CFF1Subrs *localSubrs; +}; + +typedef cff1_private_dict_values_base_t cff1_private_dict_values_subset_t; +typedef cff1_private_dict_values_base_t cff1_private_dict_values_t; + +struct cff1_private_dict_opset_t : dict_opset_t +{ + static void process_op (op_code_t op, num_interp_env_t& env, cff1_private_dict_values_t& dictval) + { + num_dict_val_t val; + val.init (); + + switch (op) { + case OpCode_BlueValues: + case OpCode_OtherBlues: + case OpCode_FamilyBlues: + case OpCode_FamilyOtherBlues: + case OpCode_StemSnapH: + case OpCode_StemSnapV: + env.clear_args (); + break; + case OpCode_StdHW: + case OpCode_StdVW: + case OpCode_BlueScale: + case OpCode_BlueShift: + case OpCode_BlueFuzz: + case OpCode_ForceBold: + case OpCode_LanguageGroup: + case OpCode_ExpansionFactor: + case OpCode_initialRandomSeed: + case OpCode_defaultWidthX: + case OpCode_nominalWidthX: + val.single_val = env.argStack.pop_num (); + env.clear_args (); + break; + case OpCode_Subrs: + dictval.subrsOffset = env.argStack.pop_uint (); + env.clear_args (); + break; + + default: + dict_opset_t::process_op (op, env); + if (!env.argStack.is_empty ()) return; + break; + } + + if (unlikely (env.in_error ())) return; + + dictval.add_op (op, env.str_ref, val); + } +}; + +struct cff1_private_dict_opset_subset : dict_opset_t +{ + static void process_op (op_code_t op, num_interp_env_t& env, cff1_private_dict_values_subset_t& dictval) + { + switch (op) { + case OpCode_BlueValues: + case OpCode_OtherBlues: + case OpCode_FamilyBlues: + case OpCode_FamilyOtherBlues: + case OpCode_StemSnapH: + case OpCode_StemSnapV: + case OpCode_StdHW: + case OpCode_StdVW: + case OpCode_BlueScale: + case OpCode_BlueShift: + case OpCode_BlueFuzz: + case OpCode_ForceBold: + case OpCode_LanguageGroup: + case OpCode_ExpansionFactor: + case OpCode_initialRandomSeed: + case OpCode_defaultWidthX: + case OpCode_nominalWidthX: + env.clear_args (); + break; + + case OpCode_Subrs: + dictval.subrsOffset = env.argStack.pop_uint (); + env.clear_args (); + break; + + default: + dict_opset_t::process_op (op, env); + if (!env.argStack.is_empty ()) return; + break; + } + + if (unlikely (env.in_error ())) return; + + dictval.add_op (op, env.str_ref); + } +}; + +typedef dict_interpreter_t cff1_top_dict_interpreter_t; +typedef dict_interpreter_t cff1_font_dict_interpreter_t; + +typedef CFF1Index CFF1NameIndex; +typedef CFF1IndexOf CFF1TopDictIndex; + +struct cff1_font_dict_values_mod_t +{ + cff1_font_dict_values_mod_t() { init (); } + + void init () { init ( &Null (cff1_font_dict_values_t), CFF_UNDEF_SID ); } + + void init (const cff1_font_dict_values_t *base_, + unsigned int fontName_) + { + base = base_; + fontName = fontName_; + privateDictInfo.init (); + } + + unsigned get_count () const { return base->get_count (); } + + const op_str_t &operator [] (unsigned int i) const { return (*base)[i]; } + + const cff1_font_dict_values_t *base; + table_info_t privateDictInfo; + unsigned int fontName; +}; + +struct CFF1FDArray : FDArray +{ + /* FDArray::serialize() requires this partial specialization to compile */ + template + bool serialize (hb_serialize_context_t *c, ITER it, OP_SERIALIZER& opszr) + { return FDArray::serialize (c, it, opszr); } +}; + +} /* namespace CFF */ + +namespace OT { + +using namespace CFF; + +struct cff1 +{ + static constexpr hb_tag_t tableTag = HB_OT_TAG_cff1; + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + likely (version.major == 1)); + } + + template + struct accelerator_templ_t + { + void init (hb_face_t *face) + { + topDict.init (); + fontDicts.init (); + privateDicts.init (); + + this->blob = sc.reference_table (face); + + /* setup for run-time santization */ + sc.init (this->blob); + sc.start_processing (); + + const OT::cff1 *cff = this->blob->template as (); + + if (cff == &Null (OT::cff1)) + { fini (); return; } + + nameIndex = &cff->nameIndex (cff); + if ((nameIndex == &Null (CFF1NameIndex)) || !nameIndex->sanitize (&sc)) + { fini (); return; } + + topDictIndex = &StructAtOffset (nameIndex, nameIndex->get_size ()); + if ((topDictIndex == &Null (CFF1TopDictIndex)) || !topDictIndex->sanitize (&sc) || (topDictIndex->count == 0)) + { fini (); return; } + + { /* parse top dict */ + const hb_ubytes_t topDictStr = (*topDictIndex)[0]; + if (unlikely (!topDictStr.sanitize (&sc))) { fini (); return; } + cff1_top_dict_interp_env_t env (topDictStr); + cff1_top_dict_interpreter_t top_interp (env); + if (unlikely (!top_interp.interpret (topDict))) { fini (); return; } + } + + if (is_predef_charset ()) + charset = &Null (Charset); + else + { + charset = &StructAtOffsetOrNull (cff, topDict.CharsetOffset); + if (unlikely ((charset == &Null (Charset)) || !charset->sanitize (&sc))) { fini (); return; } + } + + fdCount = 1; + if (is_CID ()) + { + fdArray = &StructAtOffsetOrNull (cff, topDict.FDArrayOffset); + fdSelect = &StructAtOffsetOrNull (cff, topDict.FDSelectOffset); + if (unlikely ((fdArray == &Null (CFF1FDArray)) || !fdArray->sanitize (&sc) || + (fdSelect == &Null (CFF1FDSelect)) || !fdSelect->sanitize (&sc, fdArray->count))) + { fini (); return; } + + fdCount = fdArray->count; + } + else + { + fdArray = &Null (CFF1FDArray); + fdSelect = &Null (CFF1FDSelect); + } + + encoding = &Null (Encoding); + if (is_CID ()) + { + if (unlikely (charset == &Null (Charset))) { fini (); return; } + } + else + { + if (!is_predef_encoding ()) + { + encoding = &StructAtOffsetOrNull (cff, topDict.EncodingOffset); + if (unlikely ((encoding == &Null (Encoding)) || !encoding->sanitize (&sc))) { fini (); return; } + } + } + + stringIndex = &StructAtOffset (topDictIndex, topDictIndex->get_size ()); + if ((stringIndex == &Null (CFF1StringIndex)) || !stringIndex->sanitize (&sc)) + { fini (); return; } + + globalSubrs = &StructAtOffset (stringIndex, stringIndex->get_size ()); + if ((globalSubrs != &Null (CFF1Subrs)) && !globalSubrs->sanitize (&sc)) + { fini (); return; } + + charStrings = &StructAtOffsetOrNull (cff, topDict.charStringsOffset); + + if ((charStrings == &Null (CFF1CharStrings)) || unlikely (!charStrings->sanitize (&sc))) + { fini (); return; } + + num_glyphs = charStrings->count; + if (num_glyphs != sc.get_num_glyphs ()) + { fini (); return; } + + if (unlikely (!privateDicts.resize (fdCount))) + { fini (); return; } + for (unsigned int i = 0; i < fdCount; i++) + privateDicts[i].init (); + + // parse CID font dicts and gather private dicts + if (is_CID ()) + { + for (unsigned int i = 0; i < fdCount; i++) + { + hb_ubytes_t fontDictStr = (*fdArray)[i]; + if (unlikely (!fontDictStr.sanitize (&sc))) { fini (); return; } + cff1_font_dict_values_t *font; + cff1_top_dict_interp_env_t env (fontDictStr); + cff1_font_dict_interpreter_t font_interp (env); + font = fontDicts.push (); + if (unlikely (fontDicts.in_error ())) { fini (); return; } + + font->init (); + if (unlikely (!font_interp.interpret (*font))) { fini (); return; } + PRIVDICTVAL *priv = &privateDicts[i]; + const hb_ubytes_t privDictStr = StructAtOffset (cff, font->privateDictInfo.offset).as_ubytes (font->privateDictInfo.size); + if (unlikely (!privDictStr.sanitize (&sc))) { fini (); return; } + num_interp_env_t env2 (privDictStr); + dict_interpreter_t priv_interp (env2); + priv->init (); + if (unlikely (!priv_interp.interpret (*priv))) { fini (); return; } + + priv->localSubrs = &StructAtOffsetOrNull (&privDictStr, priv->subrsOffset); + if (priv->localSubrs != &Null (CFF1Subrs) && + unlikely (!priv->localSubrs->sanitize (&sc))) + { fini (); return; } + } + } + else /* non-CID */ + { + cff1_top_dict_values_t *font = &topDict; + PRIVDICTVAL *priv = &privateDicts[0]; + + const hb_ubytes_t privDictStr = StructAtOffset (cff, font->privateDictInfo.offset).as_ubytes (font->privateDictInfo.size); + if (unlikely (!privDictStr.sanitize (&sc))) { fini (); return; } + num_interp_env_t env (privDictStr); + dict_interpreter_t priv_interp (env); + priv->init (); + if (unlikely (!priv_interp.interpret (*priv))) { fini (); return; } + + priv->localSubrs = &StructAtOffsetOrNull (&privDictStr, priv->subrsOffset); + if (priv->localSubrs != &Null (CFF1Subrs) && + unlikely (!priv->localSubrs->sanitize (&sc))) + { fini (); return; } + } + } + + void fini () + { + sc.end_processing (); + topDict.fini (); + fontDicts.fini (); + privateDicts.fini (); + hb_blob_destroy (blob); + blob = nullptr; + } + + bool is_valid () const { return blob; } + bool is_CID () const { return topDict.is_CID (); } + + bool is_predef_charset () const { return topDict.CharsetOffset <= ExpertSubsetCharset; } + + unsigned int std_code_to_glyph (hb_codepoint_t code) const + { + hb_codepoint_t sid = lookup_standard_encoding_for_sid (code); + if (unlikely (sid == CFF_UNDEF_SID)) + return 0; + + if (charset != &Null (Charset)) + return charset->get_glyph (sid, num_glyphs); + else if ((topDict.CharsetOffset == ISOAdobeCharset) + && (code <= 228 /*zcaron*/)) return sid; + return 0; + } + + bool is_predef_encoding () const { return topDict.EncodingOffset <= ExpertEncoding; } + + hb_codepoint_t glyph_to_code (hb_codepoint_t glyph) const + { + if (encoding != &Null (Encoding)) + return encoding->get_code (glyph); + else + { + hb_codepoint_t sid = glyph_to_sid (glyph); + if (sid == 0) return 0; + hb_codepoint_t code = 0; + switch (topDict.EncodingOffset) + { + case StandardEncoding: + code = lookup_standard_encoding_for_code (sid); + break; + case ExpertEncoding: + code = lookup_expert_encoding_for_code (sid); + break; + default: + break; + } + return code; + } + } + + hb_map_t *create_glyph_to_sid_map () const + { + if (charset != &Null (Charset)) + { + hb_map_t *mapping = hb_map_create (); + mapping->set (0, 0); + charset->collect_glyph_to_sid_map (mapping, num_glyphs); + return mapping; + } + else + return nullptr; + } + + hb_codepoint_t glyph_to_sid (hb_codepoint_t glyph) const + { + if (charset != &Null (Charset)) + return charset->get_sid (glyph, num_glyphs); + else + { + hb_codepoint_t sid = 0; + switch (topDict.CharsetOffset) + { + case ISOAdobeCharset: + if (glyph <= 228 /*zcaron*/) sid = glyph; + break; + case ExpertCharset: + sid = lookup_expert_charset_for_sid (glyph); + break; + case ExpertSubsetCharset: + sid = lookup_expert_subset_charset_for_sid (glyph); + break; + default: + break; + } + return sid; + } + } + + hb_codepoint_t sid_to_glyph (hb_codepoint_t sid) const + { + if (charset != &Null (Charset)) + return charset->get_glyph (sid, num_glyphs); + else + { + hb_codepoint_t glyph = 0; + switch (topDict.CharsetOffset) + { + case ISOAdobeCharset: + if (sid <= 228 /*zcaron*/) glyph = sid; + break; + case ExpertCharset: + glyph = lookup_expert_charset_for_glyph (sid); + break; + case ExpertSubsetCharset: + glyph = lookup_expert_subset_charset_for_glyph (sid); + break; + default: + break; + } + return glyph; + } + } + + protected: + hb_blob_t *blob = nullptr; + hb_sanitize_context_t sc; + + public: + const Encoding *encoding = nullptr; + const Charset *charset = nullptr; + const CFF1NameIndex *nameIndex = nullptr; + const CFF1TopDictIndex *topDictIndex = nullptr; + const CFF1StringIndex *stringIndex = nullptr; + const CFF1Subrs *globalSubrs = nullptr; + const CFF1CharStrings *charStrings = nullptr; + const CFF1FDArray *fdArray = nullptr; + const CFF1FDSelect *fdSelect = nullptr; + unsigned int fdCount = 0; + + cff1_top_dict_values_t topDict; + hb_vector_t + fontDicts; + hb_vector_t privateDicts; + + unsigned int num_glyphs = 0; + }; + + struct accelerator_t : accelerator_templ_t + { + accelerator_t (hb_face_t *face) + { + SUPER::init (face); + + glyph_names.set_relaxed (nullptr); + + if (!is_valid ()) return; + if (is_CID ()) return; + + } + ~accelerator_t () + { + hb_sorted_vector_t *names = glyph_names.get_relaxed (); + if (names) + { + names->fini (); + hb_free (names); + } + + SUPER::fini (); + } + + bool get_glyph_name (hb_codepoint_t glyph, + char *buf, unsigned int buf_len) const + { + if (unlikely (!is_valid ())) return false; + if (is_CID()) return false; + if (unlikely (!buf_len)) return true; + hb_codepoint_t sid = glyph_to_sid (glyph); + const char *str; + size_t str_len; + if (sid < cff1_std_strings_length) + { + hb_bytes_t byte_str = cff1_std_strings (sid); + str = byte_str.arrayZ; + str_len = byte_str.length; + } + else + { + hb_ubytes_t ubyte_str = (*stringIndex)[sid - cff1_std_strings_length]; + str = (const char *)ubyte_str.arrayZ; + str_len = ubyte_str.length; + } + if (!str_len) return false; + unsigned int len = hb_min (buf_len - 1, str_len); + strncpy (buf, (const char*)str, len); + buf[len] = '\0'; + return true; + } + + bool get_glyph_from_name (const char *name, int len, + hb_codepoint_t *glyph) const + { + if (unlikely (!is_valid ())) return false; + if (is_CID()) return false; + if (len < 0) len = strlen (name); + if (unlikely (!len)) return false; + + retry: + hb_sorted_vector_t *names = glyph_names.get_acquire (); + if (unlikely (!names)) + { + names = (hb_sorted_vector_t *) hb_calloc (sizeof (hb_sorted_vector_t), 1); + if (likely (names)) + { + names->init (); + /* TODO */ + + /* fill glyph names */ + for (hb_codepoint_t gid = 0; gid < num_glyphs; gid++) + { + hb_codepoint_t sid = glyph_to_sid (gid); + gname_t gname; + gname.sid = sid; + if (sid < cff1_std_strings_length) + gname.name = cff1_std_strings (sid); + else + { + hb_ubytes_t ustr = (*stringIndex)[sid - cff1_std_strings_length]; + gname.name = hb_bytes_t ((const char*) ustr.arrayZ, ustr.length); + } + if (unlikely (!gname.name.arrayZ)) + gname.name = hb_bytes_t ("", 0); /* To avoid nullptr. */ + names->push (gname); + } + names->qsort (); + } + if (unlikely (!glyph_names.cmpexch (nullptr, names))) + { + if (names) + { + names->fini (); + hb_free (names); + } + goto retry; + } + } + + gname_t key = { hb_bytes_t (name, len), 0 }; + const gname_t *gname = names ? names->bsearch (key) : nullptr; + if (!gname) return false; + hb_codepoint_t gid = sid_to_glyph (gname->sid); + if (!gid && gname->sid) return false; + *glyph = gid; + return true; + } + + HB_INTERNAL bool get_extents (hb_font_t *font, hb_codepoint_t glyph, hb_glyph_extents_t *extents) const; + HB_INTERNAL bool get_seac_components (hb_codepoint_t glyph, hb_codepoint_t *base, hb_codepoint_t *accent) const; + HB_INTERNAL bool get_path (hb_font_t *font, hb_codepoint_t glyph, hb_draw_session_t &draw_session) const; + + private: + struct gname_t + { + hb_bytes_t name; + uint16_t sid; + + static int cmp (const void *a_, const void *b_) + { + const gname_t *a = (const gname_t *)a_; + const gname_t *b = (const gname_t *)b_; + unsigned minlen = hb_min (a->name.length, b->name.length); + int ret = strncmp (a->name.arrayZ, b->name.arrayZ, minlen); + if (ret) return ret; + return a->name.length - b->name.length; + } + + int cmp (const gname_t &a) const { return cmp (&a, this); } + }; + + mutable hb_atomic_ptr_t> glyph_names; + + typedef accelerator_templ_t SUPER; + }; + + struct accelerator_subset_t : accelerator_templ_t {}; + + bool subset (hb_subset_context_t *c) const { return hb_subset_cff1 (c); } + + protected: + HB_INTERNAL static hb_codepoint_t lookup_standard_encoding_for_code (hb_codepoint_t sid); + HB_INTERNAL static hb_codepoint_t lookup_expert_encoding_for_code (hb_codepoint_t sid); + HB_INTERNAL static hb_codepoint_t lookup_expert_charset_for_sid (hb_codepoint_t glyph); + HB_INTERNAL static hb_codepoint_t lookup_expert_subset_charset_for_sid (hb_codepoint_t glyph); + HB_INTERNAL static hb_codepoint_t lookup_expert_charset_for_glyph (hb_codepoint_t sid); + HB_INTERNAL static hb_codepoint_t lookup_expert_subset_charset_for_glyph (hb_codepoint_t sid); + HB_INTERNAL static hb_codepoint_t lookup_standard_encoding_for_sid (hb_codepoint_t code); + + public: + FixedVersion version; /* Version of CFF table. set to 0x0100u */ + NNOffsetTo nameIndex; /* headerSize = Offset to Name INDEX. */ + HBUINT8 offSize; /* offset size (unused?) */ + + public: + DEFINE_SIZE_STATIC (4); +}; + +struct cff1_accelerator_t : cff1::accelerator_t { + cff1_accelerator_t (hb_face_t *face) : cff1::accelerator_t (face) {} +}; + +} /* namespace OT */ + +#endif /* HB_OT_CFF1_TABLE_HH */ diff --git a/libs/harfbuzz/src/hb-ot-cff2-table.cc b/libs/harfbuzz/src/hb-ot-cff2-table.cc new file mode 100644 index 00000000..50c76daf --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-cff2-table.cc @@ -0,0 +1,211 @@ +/* + * Copyright © 2018 Adobe 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. + * + * Adobe Author(s): Michiharu Ariza + */ + +#include "hb.hh" + +#ifndef HB_NO_OT_FONT_CFF + +#include "hb-ot-cff2-table.hh" +#include "hb-cff2-interp-cs.hh" +#include "hb-draw.hh" + +using namespace CFF; + +struct cff2_extents_param_t +{ + cff2_extents_param_t () + { + min_x.set_int (INT_MAX); + min_y.set_int (INT_MAX); + max_x.set_int (INT_MIN); + max_y.set_int (INT_MIN); + } + + void start_path () { path_open = true; } + void end_path () { path_open = false; } + bool is_path_open () const { return path_open; } + + void update_bounds (const point_t &pt) + { + if (pt.x < min_x) min_x = pt.x; + if (pt.x > max_x) max_x = pt.x; + if (pt.y < min_y) min_y = pt.y; + if (pt.y > max_y) max_y = pt.y; + } + + bool path_open = false; + number_t min_x; + number_t min_y; + number_t max_x; + number_t max_y; +}; + +struct cff2_path_procs_extents_t : path_procs_t, cff2_extents_param_t> +{ + static void moveto (cff2_cs_interp_env_t &env, cff2_extents_param_t& param, const point_t &pt) + { + param.end_path (); + env.moveto (pt); + } + + static void line (cff2_cs_interp_env_t &env, cff2_extents_param_t& param, const point_t &pt1) + { + if (!param.is_path_open ()) + { + param.start_path (); + param.update_bounds (env.get_pt ()); + } + env.moveto (pt1); + param.update_bounds (env.get_pt ()); + } + + static void curve (cff2_cs_interp_env_t &env, cff2_extents_param_t& param, const point_t &pt1, const point_t &pt2, const point_t &pt3) + { + if (!param.is_path_open ()) + { + param.start_path (); + param.update_bounds (env.get_pt ()); + } + /* include control points */ + param.update_bounds (pt1); + param.update_bounds (pt2); + env.moveto (pt3); + param.update_bounds (env.get_pt ()); + } +}; + +struct cff2_cs_opset_extents_t : cff2_cs_opset_t {}; + +bool OT::cff2::accelerator_t::get_extents (hb_font_t *font, + hb_codepoint_t glyph, + hb_glyph_extents_t *extents) const +{ +#ifdef HB_NO_OT_FONT_CFF + /* XXX Remove check when this code moves to .hh file. */ + return true; +#endif + + if (unlikely (!is_valid () || (glyph >= num_glyphs))) return false; + + unsigned int fd = fdSelect->get_fd (glyph); + const hb_ubytes_t str = (*charStrings)[glyph]; + cff2_cs_interp_env_t env (str, *this, fd, font->coords, font->num_coords); + cff2_cs_interpreter_t interp (env); + cff2_extents_param_t param; + if (unlikely (!interp.interpret (param))) return false; + + if (param.min_x >= param.max_x) + { + extents->width = 0; + extents->x_bearing = 0; + } + else + { + extents->x_bearing = font->em_scalef_x (param.min_x.to_real ()); + extents->width = font->em_scalef_x (param.max_x.to_real ()) - extents->x_bearing; + } + if (param.min_y >= param.max_y) + { + extents->height = 0; + extents->y_bearing = 0; + } + else + { + extents->y_bearing = font->em_scalef_y (param.max_y.to_real ()); + extents->height = font->em_scalef_y (param.min_y.to_real ()) - extents->y_bearing; + } + + return true; +} + +struct cff2_path_param_t +{ + cff2_path_param_t (hb_font_t *font_, hb_draw_session_t &draw_session_) + { + draw_session = &draw_session_; + font = font_; + } + + void move_to (const point_t &p) + { draw_session->move_to (font->em_fscalef_x (p.x.to_real ()), font->em_fscalef_y (p.y.to_real ())); } + + void line_to (const point_t &p) + { draw_session->line_to (font->em_fscalef_x (p.x.to_real ()), font->em_fscalef_y (p.y.to_real ())); } + + void cubic_to (const point_t &p1, const point_t &p2, const point_t &p3) + { + draw_session->cubic_to (font->em_fscalef_x (p1.x.to_real ()), font->em_fscalef_y (p1.y.to_real ()), + font->em_fscalef_x (p2.x.to_real ()), font->em_fscalef_y (p2.y.to_real ()), + font->em_fscalef_x (p3.x.to_real ()), font->em_fscalef_y (p3.y.to_real ())); + } + + protected: + hb_draw_session_t *draw_session; + hb_font_t *font; +}; + +struct cff2_path_procs_path_t : path_procs_t, cff2_path_param_t> +{ + static void moveto (cff2_cs_interp_env_t &env, cff2_path_param_t& param, const point_t &pt) + { + param.move_to (pt); + env.moveto (pt); + } + + static void line (cff2_cs_interp_env_t &env, cff2_path_param_t& param, const point_t &pt1) + { + param.line_to (pt1); + env.moveto (pt1); + } + + static void curve (cff2_cs_interp_env_t &env, cff2_path_param_t& param, const point_t &pt1, const point_t &pt2, const point_t &pt3) + { + param.cubic_to (pt1, pt2, pt3); + env.moveto (pt3); + } +}; + +struct cff2_cs_opset_path_t : cff2_cs_opset_t {}; + +bool OT::cff2::accelerator_t::get_path (hb_font_t *font, hb_codepoint_t glyph, hb_draw_session_t &draw_session) const +{ +#ifdef HB_NO_OT_FONT_CFF + /* XXX Remove check when this code moves to .hh file. */ + return true; +#endif + + if (unlikely (!is_valid () || (glyph >= num_glyphs))) return false; + + unsigned int fd = fdSelect->get_fd (glyph); + const hb_ubytes_t str = (*charStrings)[glyph]; + cff2_cs_interp_env_t env (str, *this, fd, font->coords, font->num_coords); + cff2_cs_interpreter_t interp (env); + cff2_path_param_t param (font, draw_session); + if (unlikely (!interp.interpret (param))) return false; + return true; +} + +#endif diff --git a/libs/harfbuzz/src/hb-ot-cff2-table.hh b/libs/harfbuzz/src/hb-ot-cff2-table.hh new file mode 100644 index 00000000..746160dc --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-cff2-table.hh @@ -0,0 +1,536 @@ +/* + * Copyright © 2018 Adobe 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. + * + * Adobe Author(s): Michiharu Ariza + */ + +#ifndef HB_OT_CFF2_TABLE_HH +#define HB_OT_CFF2_TABLE_HH + +#include "hb-ot-cff-common.hh" +#include "hb-subset-cff2.hh" +#include "hb-draw.hh" + +namespace CFF { + +/* + * CFF2 -- Compact Font Format (CFF) Version 2 + * https://docs.microsoft.com/en-us/typography/opentype/spec/cff2 + */ +#define HB_OT_TAG_cff2 HB_TAG('C','F','F','2') + +typedef CFFIndex CFF2Index; +template struct CFF2IndexOf : CFFIndexOf {}; + +typedef CFF2Index CFF2CharStrings; +typedef Subrs CFF2Subrs; + +typedef FDSelect3_4 FDSelect4; +typedef FDSelect3_4_Range FDSelect4_Range; + +struct CFF2FDSelect +{ + bool serialize (hb_serialize_context_t *c, const CFF2FDSelect &src, unsigned int num_glyphs) + { + TRACE_SERIALIZE (this); + unsigned int size = src.get_size (num_glyphs); + CFF2FDSelect *dest = c->allocate_size (size); + if (unlikely (!dest)) return_trace (false); + memcpy (dest, &src, size); + return_trace (true); + } + + unsigned int get_size (unsigned int num_glyphs) const + { + switch (format) + { + case 0: return format.static_size + u.format0.get_size (num_glyphs); + case 3: return format.static_size + u.format3.get_size (); + case 4: return format.static_size + u.format4.get_size (); + default:return 0; + } + } + + hb_codepoint_t get_fd (hb_codepoint_t glyph) const + { + if (this == &Null (CFF2FDSelect)) + return 0; + + switch (format) + { + case 0: return u.format0.get_fd (glyph); + case 3: return u.format3.get_fd (glyph); + case 4: return u.format4.get_fd (glyph); + default:return 0; + } + } + + bool sanitize (hb_sanitize_context_t *c, unsigned int fdcount) const + { + TRACE_SANITIZE (this); + if (unlikely (!c->check_struct (this))) + return_trace (false); + + switch (format) + { + case 0: return_trace (u.format0.sanitize (c, fdcount)); + case 3: return_trace (u.format3.sanitize (c, fdcount)); + case 4: return_trace (u.format4.sanitize (c, fdcount)); + default:return_trace (false); + } + } + + HBUINT8 format; + union { + FDSelect0 format0; + FDSelect3 format3; + FDSelect4 format4; + } u; + public: + DEFINE_SIZE_MIN (2); +}; + +struct CFF2VariationStore +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this)) && c->check_range (&varStore, size) && varStore.sanitize (c)); + } + + bool serialize (hb_serialize_context_t *c, const CFF2VariationStore *varStore) + { + TRACE_SERIALIZE (this); + unsigned int size_ = varStore->get_size (); + CFF2VariationStore *dest = c->allocate_size (size_); + if (unlikely (!dest)) return_trace (false); + memcpy (dest, varStore, size_); + return_trace (true); + } + + unsigned int get_size () const { return HBUINT16::static_size + size; } + + HBUINT16 size; + VariationStore varStore; + + DEFINE_SIZE_MIN (2 + VariationStore::min_size); +}; + +struct cff2_top_dict_values_t : top_dict_values_t<> +{ + void init () + { + top_dict_values_t<>::init (); + vstoreOffset = 0; + FDSelectOffset = 0; + } + void fini () { top_dict_values_t<>::fini (); } + + unsigned int vstoreOffset; + unsigned int FDSelectOffset; +}; + +struct cff2_top_dict_opset_t : top_dict_opset_t<> +{ + static void process_op (op_code_t op, num_interp_env_t& env, cff2_top_dict_values_t& dictval) + { + switch (op) { + case OpCode_FontMatrix: + { + dict_val_t val; + val.init (); + dictval.add_op (op, env.str_ref); + env.clear_args (); + } + break; + + case OpCode_vstore: + dictval.vstoreOffset = env.argStack.pop_uint (); + env.clear_args (); + break; + case OpCode_FDSelect: + dictval.FDSelectOffset = env.argStack.pop_uint (); + env.clear_args (); + break; + + default: + SUPER::process_op (op, env, dictval); + /* Record this operand below if stack is empty, otherwise done */ + if (!env.argStack.is_empty ()) return; + } + + if (unlikely (env.in_error ())) return; + + dictval.add_op (op, env.str_ref); + } + + typedef top_dict_opset_t<> SUPER; +}; + +struct cff2_font_dict_values_t : dict_values_t +{ + void init () + { + dict_values_t::init (); + privateDictInfo.init (); + } + void fini () { dict_values_t::fini (); } + + table_info_t privateDictInfo; +}; + +struct cff2_font_dict_opset_t : dict_opset_t +{ + static void process_op (op_code_t op, num_interp_env_t& env, cff2_font_dict_values_t& dictval) + { + switch (op) { + case OpCode_Private: + dictval.privateDictInfo.offset = env.argStack.pop_uint (); + dictval.privateDictInfo.size = env.argStack.pop_uint (); + env.clear_args (); + break; + + default: + SUPER::process_op (op, env); + if (!env.argStack.is_empty ()) + return; + } + + if (unlikely (env.in_error ())) return; + + dictval.add_op (op, env.str_ref); + } + + private: + typedef dict_opset_t SUPER; +}; + +template +struct cff2_private_dict_values_base_t : dict_values_t +{ + void init () + { + dict_values_t::init (); + subrsOffset = 0; + localSubrs = &Null (CFF2Subrs); + ivs = 0; + } + void fini () { dict_values_t::fini (); } + + unsigned int subrsOffset; + const CFF2Subrs *localSubrs; + unsigned int ivs; +}; + +typedef cff2_private_dict_values_base_t cff2_private_dict_values_subset_t; +typedef cff2_private_dict_values_base_t cff2_private_dict_values_t; + +struct cff2_priv_dict_interp_env_t : num_interp_env_t +{ + cff2_priv_dict_interp_env_t (const hb_ubytes_t &str) : + num_interp_env_t (str) {} + + void process_vsindex () + { + if (likely (!seen_vsindex)) + { + set_ivs (argStack.pop_uint ()); + } + seen_vsindex = true; + } + + unsigned int get_ivs () const { return ivs; } + void set_ivs (unsigned int ivs_) { ivs = ivs_; } + + protected: + unsigned int ivs = 0; + bool seen_vsindex = false; +}; + +struct cff2_private_dict_opset_t : dict_opset_t +{ + static void process_op (op_code_t op, cff2_priv_dict_interp_env_t& env, cff2_private_dict_values_t& dictval) + { + num_dict_val_t val; + val.init (); + + switch (op) { + case OpCode_StdHW: + case OpCode_StdVW: + case OpCode_BlueScale: + case OpCode_BlueShift: + case OpCode_BlueFuzz: + case OpCode_ExpansionFactor: + case OpCode_LanguageGroup: + val.single_val = env.argStack.pop_num (); + env.clear_args (); + break; + case OpCode_BlueValues: + case OpCode_OtherBlues: + case OpCode_FamilyBlues: + case OpCode_FamilyOtherBlues: + case OpCode_StemSnapH: + case OpCode_StemSnapV: + env.clear_args (); + break; + case OpCode_Subrs: + dictval.subrsOffset = env.argStack.pop_uint (); + env.clear_args (); + break; + case OpCode_vsindexdict: + env.process_vsindex (); + dictval.ivs = env.get_ivs (); + env.clear_args (); + break; + case OpCode_blenddict: + break; + + default: + dict_opset_t::process_op (op, env); + if (!env.argStack.is_empty ()) return; + break; + } + + if (unlikely (env.in_error ())) return; + + dictval.add_op (op, env.str_ref, val); + } +}; + +struct cff2_private_dict_opset_subset_t : dict_opset_t +{ + static void process_op (op_code_t op, cff2_priv_dict_interp_env_t& env, cff2_private_dict_values_subset_t& dictval) + { + switch (op) { + case OpCode_BlueValues: + case OpCode_OtherBlues: + case OpCode_FamilyBlues: + case OpCode_FamilyOtherBlues: + case OpCode_StdHW: + case OpCode_StdVW: + case OpCode_BlueScale: + case OpCode_BlueShift: + case OpCode_BlueFuzz: + case OpCode_StemSnapH: + case OpCode_StemSnapV: + case OpCode_LanguageGroup: + case OpCode_ExpansionFactor: + env.clear_args (); + break; + + case OpCode_blenddict: + env.clear_args (); + return; + + case OpCode_Subrs: + dictval.subrsOffset = env.argStack.pop_uint (); + env.clear_args (); + break; + + default: + SUPER::process_op (op, env); + if (!env.argStack.is_empty ()) return; + break; + } + + if (unlikely (env.in_error ())) return; + + dictval.add_op (op, env.str_ref); + } + + private: + typedef dict_opset_t SUPER; +}; + +typedef dict_interpreter_t cff2_top_dict_interpreter_t; +typedef dict_interpreter_t cff2_font_dict_interpreter_t; + +struct CFF2FDArray : FDArray +{ + /* FDArray::serialize does not compile without this partial specialization */ + template + bool serialize (hb_serialize_context_t *c, ITER it, OP_SERIALIZER& opszr) + { return FDArray::serialize (c, it, opszr); } +}; + +} /* namespace CFF */ + +namespace OT { + +using namespace CFF; + +struct cff2 +{ + static constexpr hb_tag_t tableTag = HB_OT_TAG_cff2; + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + likely (version.major == 2)); + } + + template + struct accelerator_templ_t + { + accelerator_templ_t (hb_face_t *face) + { + topDict.init (); + fontDicts.init (); + privateDicts.init (); + + this->blob = sc.reference_table (face); + + /* setup for run-time santization */ + sc.init (this->blob); + sc.start_processing (); + + const OT::cff2 *cff2 = this->blob->template as (); + + if (cff2 == &Null (OT::cff2)) + goto fail; + + { /* parse top dict */ + hb_ubytes_t topDictStr = (cff2 + cff2->topDict).as_ubytes (cff2->topDictSize); + if (unlikely (!topDictStr.sanitize (&sc))) goto fail; + num_interp_env_t env (topDictStr); + cff2_top_dict_interpreter_t top_interp (env); + topDict.init (); + if (unlikely (!top_interp.interpret (topDict))) goto fail; + } + + globalSubrs = &StructAtOffset (cff2, cff2->topDict + cff2->topDictSize); + varStore = &StructAtOffsetOrNull (cff2, topDict.vstoreOffset); + charStrings = &StructAtOffsetOrNull (cff2, topDict.charStringsOffset); + fdArray = &StructAtOffsetOrNull (cff2, topDict.FDArrayOffset); + fdSelect = &StructAtOffsetOrNull (cff2, topDict.FDSelectOffset); + + if (((varStore != &Null (CFF2VariationStore)) && unlikely (!varStore->sanitize (&sc))) || + (charStrings == &Null (CFF2CharStrings)) || unlikely (!charStrings->sanitize (&sc)) || + (globalSubrs == &Null (CFF2Subrs)) || unlikely (!globalSubrs->sanitize (&sc)) || + (fdArray == &Null (CFF2FDArray)) || unlikely (!fdArray->sanitize (&sc)) || + (((fdSelect != &Null (CFF2FDSelect)) && unlikely (!fdSelect->sanitize (&sc, fdArray->count))))) + goto fail; + + num_glyphs = charStrings->count; + if (num_glyphs != sc.get_num_glyphs ()) + goto fail; + + fdCount = fdArray->count; + if (!privateDicts.resize (fdCount)) + goto fail; + + /* parse font dicts and gather private dicts */ + for (unsigned int i = 0; i < fdCount; i++) + { + const hb_ubytes_t fontDictStr = (*fdArray)[i]; + if (unlikely (!fontDictStr.sanitize (&sc))) goto fail; + cff2_font_dict_values_t *font; + num_interp_env_t env (fontDictStr); + cff2_font_dict_interpreter_t font_interp (env); + font = fontDicts.push (); + if (unlikely (font == &Crap (cff2_font_dict_values_t))) goto fail; + font->init (); + if (unlikely (!font_interp.interpret (*font))) goto fail; + + const hb_ubytes_t privDictStr = StructAtOffsetOrNull (cff2, font->privateDictInfo.offset).as_ubytes (font->privateDictInfo.size); + if (unlikely (!privDictStr.sanitize (&sc))) goto fail; + cff2_priv_dict_interp_env_t env2 (privDictStr); + dict_interpreter_t priv_interp (env2); + privateDicts[i].init (); + if (unlikely (!priv_interp.interpret (privateDicts[i]))) goto fail; + + privateDicts[i].localSubrs = &StructAtOffsetOrNull (&privDictStr[0], privateDicts[i].subrsOffset); + if (privateDicts[i].localSubrs != &Null (CFF2Subrs) && + unlikely (!privateDicts[i].localSubrs->sanitize (&sc))) + goto fail; + } + + + return; + + fail: + _fini (); + } + ~accelerator_templ_t () { _fini (); } + void _fini () + { + sc.end_processing (); + topDict.fini (); + fontDicts.fini (); + privateDicts.fini (); + hb_blob_destroy (blob); + blob = nullptr; + } + + bool is_valid () const { return blob; } + + protected: + hb_blob_t *blob = nullptr; + hb_sanitize_context_t sc; + + public: + cff2_top_dict_values_t topDict; + const CFF2Subrs *globalSubrs = nullptr; + const CFF2VariationStore *varStore = nullptr; + const CFF2CharStrings *charStrings = nullptr; + const CFF2FDArray *fdArray = nullptr; + const CFF2FDSelect *fdSelect = nullptr; + unsigned int fdCount = 0; + + hb_vector_t fontDicts; + hb_vector_t privateDicts; + + unsigned int num_glyphs = 0; + }; + + struct accelerator_t : accelerator_templ_t + { + accelerator_t (hb_face_t *face) : accelerator_templ_t (face) {} + + HB_INTERNAL bool get_extents (hb_font_t *font, + hb_codepoint_t glyph, + hb_glyph_extents_t *extents) const; + HB_INTERNAL bool get_path (hb_font_t *font, hb_codepoint_t glyph, hb_draw_session_t &draw_session) const; + }; + + typedef accelerator_templ_t accelerator_subset_t; + + bool subset (hb_subset_context_t *c) const { return hb_subset_cff2 (c); } + + public: + FixedVersion version; /* Version of CFF2 table. set to 0x0200u */ + NNOffsetTo topDict; /* headerSize = Offset to Top DICT. */ + HBUINT16 topDictSize; /* Top DICT size */ + + public: + DEFINE_SIZE_STATIC (5); +}; + +struct cff2_accelerator_t : cff2::accelerator_t { + cff2_accelerator_t (hb_face_t *face) : cff2::accelerator_t (face) {} +}; + +} /* namespace OT */ + +#endif /* HB_OT_CFF2_TABLE_HH */ diff --git a/libs/harfbuzz/src/hb-ot-cmap-table.hh b/libs/harfbuzz/src/hb-ot-cmap-table.hh new file mode 100644 index 00000000..09c9fe93 --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-cmap-table.hh @@ -0,0 +1,1946 @@ +/* + * Copyright © 2014 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 HB_OT_CMAP_TABLE_HH +#define HB_OT_CMAP_TABLE_HH + +#include "hb-ot-os2-table.hh" +#include "hb-ot-shaper-arabic-pua.hh" +#include "hb-open-type.hh" +#include "hb-set.hh" + +/* + * cmap -- Character to Glyph Index Mapping + * https://docs.microsoft.com/en-us/typography/opentype/spec/cmap + */ +#define HB_OT_TAG_cmap HB_TAG('c','m','a','p') + +namespace OT { + + +struct CmapSubtableFormat0 +{ + bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const + { + hb_codepoint_t gid = codepoint < 256 ? glyphIdArray[codepoint] : 0; + if (unlikely (!gid)) + return false; + *glyph = gid; + return true; + } + + unsigned get_language () const + { + return language; + } + + void collect_unicodes (hb_set_t *out) const + { + for (unsigned int i = 0; i < 256; i++) + if (glyphIdArray[i]) + out->add (i); + } + + void collect_mapping (hb_set_t *unicodes, /* OUT */ + hb_map_t *mapping /* OUT */) const + { + for (unsigned i = 0; i < 256; i++) + if (glyphIdArray[i]) + { + hb_codepoint_t glyph = glyphIdArray[i]; + unicodes->add (i); + mapping->set (i, glyph); + } + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + protected: + HBUINT16 format; /* Format number is set to 0. */ + HBUINT16 length; /* Byte length of this subtable. */ + HBUINT16 language; /* Ignore. */ + HBUINT8 glyphIdArray[256];/* An array that maps character + * code to glyph index values. */ + public: + DEFINE_SIZE_STATIC (6 + 256); +}; + +struct CmapSubtableFormat4 +{ + + + template + void to_ranges (Iterator it, Writer& range_writer) + { + hb_codepoint_t start_cp = 0, prev_run_start_cp = 0, run_start_cp = 0, end_cp = 0, last_gid = 0; + int run_length = 0 , delta = 0, prev_delta = 0; + + enum { + FIRST_SUB_RANGE, + FOLLOWING_SUB_RANGE, + } mode; + + while (it) { + // Start a new range + { + const auto& pair = *it; + start_cp = pair.first; + prev_run_start_cp = start_cp; + run_start_cp = start_cp; + end_cp = start_cp; + last_gid = pair.second; + run_length = 1; + prev_delta = 0; + } + + delta = last_gid - start_cp; + mode = FIRST_SUB_RANGE; + it++; + + while (it) { + // Process range + const auto& pair = *it; + hb_codepoint_t next_cp = pair.first; + hb_codepoint_t next_gid = pair.second; + if (next_cp != end_cp + 1) { + // Current range is over, stop processing. + break; + } + + if (next_gid == last_gid + 1) { + // The current run continues. + end_cp = next_cp; + run_length++; + last_gid = next_gid; + it++; + continue; + } + + // A new run is starting, decide if we want to commit the current run. + int split_cost = (mode == FIRST_SUB_RANGE) ? 8 : 16; + int run_cost = run_length * 2; + if (run_cost >= split_cost) { + commit_current_range(start_cp, + prev_run_start_cp, + run_start_cp, + end_cp, + delta, + prev_delta, + split_cost, + range_writer); + start_cp = next_cp; + } + + // Start the new run + mode = FOLLOWING_SUB_RANGE; + prev_run_start_cp = run_start_cp; + run_start_cp = next_cp; + end_cp = next_cp; + prev_delta = delta; + delta = next_gid - run_start_cp; + run_length = 1; + last_gid = next_gid; + it++; + } + + // Finalize range + commit_current_range (start_cp, + prev_run_start_cp, + run_start_cp, + end_cp, + delta, + prev_delta, + 8, + range_writer); + } + + if (likely (end_cp != 0xFFFF)) { + range_writer (0xFFFF, 0xFFFF, 1); + } + } + + /* + * Writes the current range as either one or two ranges depending on what is most efficient. + */ + template + void commit_current_range (hb_codepoint_t start, + hb_codepoint_t prev_run_start, + hb_codepoint_t run_start, + hb_codepoint_t end, + int run_delta, + int previous_run_delta, + int split_cost, + Writer& range_writer) { + bool should_split = false; + if (start < run_start && run_start < end) { + int run_cost = (end - run_start + 1) * 2; + if (run_cost >= split_cost) { + should_split = true; + } + } + + // TODO(grieger): handle case where delta is legitimately 0, mark range offset array instead? + if (should_split) { + if (start == prev_run_start) + range_writer (start, run_start - 1, previous_run_delta); + else + range_writer (start, run_start - 1, 0); + range_writer (run_start, end, run_delta); + return; + } + + + if (start == run_start) { + // Range is only a run + range_writer (start, end, run_delta); + return; + } + + // Write only a single non-run range. + range_writer (start, end, 0); + } + + template + unsigned serialize_find_segcount (Iterator it) { + struct Counter { + unsigned segcount = 0; + + void operator() (hb_codepoint_t start, + hb_codepoint_t end, + int delta) { + segcount++; + } + } counter; + + to_ranges (+it, counter); + return counter.segcount; + } + + + template + bool serialize_start_end_delta_arrays (hb_serialize_context_t *c, + Iterator it, + int segcount) + { + struct Writer { + hb_serialize_context_t *serializer_; + HBUINT16* end_code_; + HBUINT16* start_code_; + HBINT16* id_delta_; + int index_; + + Writer(hb_serialize_context_t *serializer) + : serializer_(serializer), + end_code_(nullptr), + start_code_(nullptr), + id_delta_(nullptr), + index_ (0) {} + void operator() (hb_codepoint_t start, + hb_codepoint_t end, + int delta) { + start_code_[index_] = start; + end_code_[index_] = end; + id_delta_[index_] = delta; + index_++; + } + } writer(c); + + writer.end_code_ = c->allocate_size (HBUINT16::static_size * segcount); + c->allocate_size (2); // padding + writer.start_code_ = c->allocate_size (HBUINT16::static_size * segcount); + writer.id_delta_ = c->allocate_size (HBINT16::static_size * segcount); + + if (unlikely (!writer.end_code_ || !writer.start_code_ || !writer.id_delta_)) return false; + + to_ranges (+it, writer); + return true; + } + + template + HBUINT16* serialize_rangeoffset_glyid (hb_serialize_context_t *c, + Iterator it, + HBUINT16 *endCode, + HBUINT16 *startCode, + HBINT16 *idDelta, + unsigned segcount) + { + hb_map_t cp_to_gid { it }; + + HBUINT16 *idRangeOffset = c->allocate_size (HBUINT16::static_size * segcount); + if (unlikely (!c->check_success (idRangeOffset))) return nullptr; + if (unlikely ((char *)idRangeOffset - (char *)idDelta != (int) segcount * (int) HBINT16::static_size)) return nullptr; + + for (unsigned i : + hb_range (segcount) + | hb_filter ([&] (const unsigned _) { return idDelta[_] == 0; })) + { + idRangeOffset[i] = 2 * (c->start_embed () - idRangeOffset - i); + for (hb_codepoint_t cp = startCode[i]; cp <= endCode[i]; cp++) + { + HBUINT16 gid; + gid = cp_to_gid[cp]; + c->copy (gid); + } + } + + return idRangeOffset; + } + + template + void serialize (hb_serialize_context_t *c, + Iterator it) + { + auto format4_iter = + + it + | hb_filter ([&] (const hb_pair_t _) + { return _.first <= 0xFFFF; }) + ; + + if (!format4_iter) return; + + unsigned table_initpos = c->length (); + if (unlikely (!c->extend_min (this))) return; + this->format = 4; + + hb_vector_t> cp_to_gid { + format4_iter + }; + + //serialize endCode[], startCode[], idDelta[] + HBUINT16* endCode = c->start_embed (); + unsigned segcount = serialize_find_segcount (cp_to_gid.iter()); + if (unlikely (!serialize_start_end_delta_arrays (c, cp_to_gid.iter(), segcount))) + return; + + HBUINT16 *startCode = endCode + segcount + 1; + HBINT16 *idDelta = ((HBINT16*)startCode) + segcount; + + HBUINT16 *idRangeOffset = serialize_rangeoffset_glyid (c, + cp_to_gid.iter (), + endCode, + startCode, + idDelta, + segcount); + if (unlikely (!c->check_success (idRangeOffset))) return; + + this->length = c->length () - table_initpos; + if ((long long) this->length != (long long) c->length () - table_initpos) + { + // Length overflowed. Discard the current object before setting the error condition, otherwise + // discard is a noop which prevents the higher level code from reverting the serializer to the + // pre-error state in cmap4 overflow handling code. + c->pop_discard (); + c->err (HB_SERIALIZE_ERROR_INT_OVERFLOW); + return; + } + + this->segCountX2 = segcount * 2; + this->entrySelector = hb_max (1u, hb_bit_storage (segcount)) - 1; + this->searchRange = 2 * (1u << this->entrySelector); + this->rangeShift = segcount * 2 > this->searchRange + ? 2 * segcount - this->searchRange + : 0; + } + + unsigned get_language () const + { + return language; + } + + struct accelerator_t + { + accelerator_t () {} + accelerator_t (const CmapSubtableFormat4 *subtable) { init (subtable); } + + void init (const CmapSubtableFormat4 *subtable) + { + segCount = subtable->segCountX2 / 2; + endCount = subtable->values.arrayZ; + startCount = endCount + segCount + 1; + idDelta = startCount + segCount; + idRangeOffset = idDelta + segCount; + glyphIdArray = idRangeOffset + segCount; + glyphIdArrayLength = (subtable->length - 16 - 8 * segCount) / 2; + } + + bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const + { + struct CustomRange + { + int cmp (hb_codepoint_t k, + unsigned distance) const + { + if (k > last) return +1; + if (k < (&last)[distance]) return -1; + return 0; + } + HBUINT16 last; + }; + + const HBUINT16 *found = hb_bsearch (codepoint, + this->endCount, + this->segCount, + 2, + _hb_cmp_method, + this->segCount + 1); + if (unlikely (!found)) + return false; + unsigned int i = found - endCount; + + hb_codepoint_t gid; + unsigned int rangeOffset = this->idRangeOffset[i]; + if (rangeOffset == 0) + gid = codepoint + this->idDelta[i]; + else + { + /* Somebody has been smoking... */ + unsigned int index = rangeOffset / 2 + (codepoint - this->startCount[i]) + i - this->segCount; + if (unlikely (index >= this->glyphIdArrayLength)) + return false; + gid = this->glyphIdArray[index]; + if (unlikely (!gid)) + return false; + gid += this->idDelta[i]; + } + gid &= 0xFFFFu; + if (unlikely (!gid)) + return false; + *glyph = gid; + return true; + } + + HB_INTERNAL static bool get_glyph_func (const void *obj, hb_codepoint_t codepoint, hb_codepoint_t *glyph) + { return ((const accelerator_t *) obj)->get_glyph (codepoint, glyph); } + + void collect_unicodes (hb_set_t *out) const + { + unsigned int count = this->segCount; + if (count && this->startCount[count - 1] == 0xFFFFu) + count--; /* Skip sentinel segment. */ + for (unsigned int i = 0; i < count; i++) + { + hb_codepoint_t start = this->startCount[i]; + hb_codepoint_t end = this->endCount[i]; + unsigned int rangeOffset = this->idRangeOffset[i]; + out->add_range(start, end); + if (rangeOffset == 0) + { + for (hb_codepoint_t codepoint = start; codepoint <= end; codepoint++) + { + hb_codepoint_t gid = (codepoint + this->idDelta[i]) & 0xFFFFu; + if (unlikely (!gid)) + out->del(codepoint); + } + } + else + { + for (hb_codepoint_t codepoint = start; codepoint <= end; codepoint++) + { + unsigned int index = rangeOffset / 2 + (codepoint - this->startCount[i]) + i - this->segCount; + if (unlikely (index >= this->glyphIdArrayLength)) + { + out->del_range (codepoint, end); + break; + } + hb_codepoint_t gid = this->glyphIdArray[index]; + if (unlikely (!gid)) + out->del(codepoint); + } + } + } + } + + void collect_mapping (hb_set_t *unicodes, /* OUT */ + hb_map_t *mapping /* OUT */) const + { + // TODO(grieger): optimize similar to collect_unicodes + // (ie. use add_range()) + unsigned count = this->segCount; + if (count && this->startCount[count - 1] == 0xFFFFu) + count--; /* Skip sentinel segment. */ + for (unsigned i = 0; i < count; i++) + { + hb_codepoint_t start = this->startCount[i]; + hb_codepoint_t end = this->endCount[i]; + unsigned rangeOffset = this->idRangeOffset[i]; + if (rangeOffset == 0) + { + for (hb_codepoint_t codepoint = start; codepoint <= end; codepoint++) + { + hb_codepoint_t gid = (codepoint + this->idDelta[i]) & 0xFFFFu; + if (unlikely (!gid)) + continue; + unicodes->add (codepoint); + mapping->set (codepoint, gid); + } + } + else + { + for (hb_codepoint_t codepoint = start; codepoint <= end; codepoint++) + { + unsigned index = rangeOffset / 2 + (codepoint - this->startCount[i]) + i - this->segCount; + if (unlikely (index >= this->glyphIdArrayLength)) + break; + hb_codepoint_t gid = this->glyphIdArray[index]; + if (unlikely (!gid)) + continue; + unicodes->add (codepoint); + mapping->set (codepoint, gid); + } + } + } + } + + const HBUINT16 *endCount; + const HBUINT16 *startCount; + const HBUINT16 *idDelta; + const HBUINT16 *idRangeOffset; + const HBUINT16 *glyphIdArray; + unsigned int segCount; + unsigned int glyphIdArrayLength; + }; + + bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const + { + accelerator_t accel (this); + return accel.get_glyph_func (&accel, codepoint, glyph); + } + void collect_unicodes (hb_set_t *out) const + { + accelerator_t accel (this); + accel.collect_unicodes (out); + } + + void collect_mapping (hb_set_t *unicodes, /* OUT */ + hb_map_t *mapping /* OUT */) const + { + accelerator_t accel (this); + accel.collect_mapping (unicodes, mapping); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (unlikely (!c->check_struct (this))) + return_trace (false); + + if (unlikely (!c->check_range (this, length))) + { + /* Some broken fonts have too long of a "length" value. + * If that is the case, just change the value to truncate + * the subtable at the end of the blob. */ + uint16_t new_length = (uint16_t) hb_min ((uintptr_t) 65535, + (uintptr_t) (c->end - + (char *) this)); + if (!c->try_set (&length, new_length)) + return_trace (false); + } + + return_trace (16 + 4 * (unsigned int) segCountX2 <= length); + } + + + + protected: + HBUINT16 format; /* Format number is set to 4. */ + HBUINT16 length; /* This is the length in bytes of the + * subtable. */ + HBUINT16 language; /* Ignore. */ + HBUINT16 segCountX2; /* 2 x segCount. */ + HBUINT16 searchRange; /* 2 * (2**floor(log2(segCount))) */ + HBUINT16 entrySelector; /* log2(searchRange/2) */ + HBUINT16 rangeShift; /* 2 x segCount - searchRange */ + + UnsizedArrayOf + values; +#if 0 + HBUINT16 endCount[segCount]; /* End characterCode for each segment, + * last=0xFFFFu. */ + HBUINT16 reservedPad; /* Set to 0. */ + HBUINT16 startCount[segCount]; /* Start character code for each segment. */ + HBINT16 idDelta[segCount]; /* Delta for all character codes in segment. */ + HBUINT16 idRangeOffset[segCount];/* Offsets into glyphIdArray or 0 */ + UnsizedArrayOf + glyphIdArray; /* Glyph index array (arbitrary length) */ +#endif + + public: + DEFINE_SIZE_ARRAY (14, values); +}; + +struct CmapSubtableLongGroup +{ + friend struct CmapSubtableFormat12; + friend struct CmapSubtableFormat13; + template + friend struct CmapSubtableLongSegmented; + friend struct cmap; + + int cmp (hb_codepoint_t codepoint) const + { + if (codepoint < startCharCode) return -1; + if (codepoint > endCharCode) return +1; + return 0; + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + private: + HBUINT32 startCharCode; /* First character code in this group. */ + HBUINT32 endCharCode; /* Last character code in this group. */ + HBUINT32 glyphID; /* Glyph index; interpretation depends on + * subtable format. */ + public: + DEFINE_SIZE_STATIC (12); +}; +DECLARE_NULL_NAMESPACE_BYTES (OT, CmapSubtableLongGroup); + +template +struct CmapSubtableTrimmed +{ + bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const + { + /* Rely on our implicit array bound-checking. */ + hb_codepoint_t gid = glyphIdArray[codepoint - startCharCode]; + if (unlikely (!gid)) + return false; + *glyph = gid; + return true; + } + + unsigned get_language () const + { + return language; + } + + void collect_unicodes (hb_set_t *out) const + { + hb_codepoint_t start = startCharCode; + unsigned int count = glyphIdArray.len; + for (unsigned int i = 0; i < count; i++) + if (glyphIdArray[i]) + out->add (start + i); + } + + void collect_mapping (hb_set_t *unicodes, /* OUT */ + hb_map_t *mapping /* OUT */) const + { + hb_codepoint_t start_cp = startCharCode; + unsigned count = glyphIdArray.len; + for (unsigned i = 0; i < count; i++) + if (glyphIdArray[i]) + { + hb_codepoint_t unicode = start_cp + i; + hb_codepoint_t glyphid = glyphIdArray[i]; + unicodes->add (unicode); + mapping->set (unicode, glyphid); + } + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && glyphIdArray.sanitize (c)); + } + + protected: + UINT formatReserved; /* Subtable format and (maybe) padding. */ + UINT length; /* Byte length of this subtable. */ + UINT language; /* Ignore. */ + UINT startCharCode; /* First character code covered. */ + ArrayOf + glyphIdArray; /* Array of glyph index values for character + * codes in the range. */ + public: + DEFINE_SIZE_ARRAY (5 * sizeof (UINT), glyphIdArray); +}; + +struct CmapSubtableFormat6 : CmapSubtableTrimmed {}; +struct CmapSubtableFormat10 : CmapSubtableTrimmed {}; + +template +struct CmapSubtableLongSegmented +{ + friend struct cmap; + + bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const + { + hb_codepoint_t gid = T::group_get_glyph (groups.bsearch (codepoint), codepoint); + if (unlikely (!gid)) + return false; + *glyph = gid; + return true; + } + + unsigned get_language () const + { + return language; + } + + void collect_unicodes (hb_set_t *out, unsigned int num_glyphs) const + { + for (unsigned int i = 0; i < this->groups.len; i++) + { + hb_codepoint_t start = this->groups[i].startCharCode; + hb_codepoint_t end = hb_min ((hb_codepoint_t) this->groups[i].endCharCode, + (hb_codepoint_t) HB_UNICODE_MAX); + hb_codepoint_t gid = this->groups[i].glyphID; + if (!gid) + { + /* Intention is: if (hb_is_same (T, CmapSubtableFormat13)) continue; */ + if (! T::group_get_glyph (this->groups[i], end)) continue; + start++; + gid++; + } + if (unlikely ((unsigned int) gid >= num_glyphs)) continue; + if (unlikely ((unsigned int) (gid + end - start) >= num_glyphs)) + end = start + (hb_codepoint_t) num_glyphs - gid; + + out->add_range (start, hb_min (end, 0x10FFFFu)); + } + } + + void collect_mapping (hb_set_t *unicodes, /* OUT */ + hb_map_t *mapping, /* OUT */ + unsigned num_glyphs) const + { + hb_codepoint_t last_end = 0; + for (unsigned i = 0; i < this->groups.len; i++) + { + hb_codepoint_t start = this->groups[i].startCharCode; + hb_codepoint_t end = hb_min ((hb_codepoint_t) this->groups[i].endCharCode, + (hb_codepoint_t) HB_UNICODE_MAX); + if (unlikely (start > end || start < last_end)) { + // Range is not in order and is invalid, skip it. + continue; + } + last_end = end; + + + hb_codepoint_t gid = this->groups[i].glyphID; + if (!gid) + { + /* Intention is: if (hb_is_same (T, CmapSubtableFormat13)) continue; */ + if (! T::group_get_glyph (this->groups[i], end)) continue; + start++; + gid++; + } + if (unlikely ((unsigned int) gid >= num_glyphs)) continue; + if (unlikely ((unsigned int) (gid + end - start) >= num_glyphs)) + end = start + (hb_codepoint_t) num_glyphs - gid; + + for (unsigned cp = start; cp <= end; cp++) + { + unicodes->add (cp); + mapping->set (cp, gid); + gid++; + } + } + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && groups.sanitize (c)); + } + + protected: + HBUINT16 format; /* Subtable format; set to 12. */ + HBUINT16 reserved; /* Reserved; set to 0. */ + HBUINT32 length; /* Byte length of this subtable. */ + HBUINT32 language; /* Ignore. */ + SortedArray32Of + groups; /* Groupings. */ + public: + DEFINE_SIZE_ARRAY (16, groups); +}; + +struct CmapSubtableFormat12 : CmapSubtableLongSegmented +{ + static hb_codepoint_t group_get_glyph (const CmapSubtableLongGroup &group, + hb_codepoint_t u) + { return likely (group.startCharCode <= group.endCharCode) ? + group.glyphID + (u - group.startCharCode) : 0; } + + + template + void serialize (hb_serialize_context_t *c, + Iterator it) + { + if (!it) return; + unsigned table_initpos = c->length (); + if (unlikely (!c->extend_min (this))) return; + + hb_codepoint_t startCharCode = (hb_codepoint_t) -1, endCharCode = (hb_codepoint_t) -1; + hb_codepoint_t glyphID = 0; + + for (const auto& _ : +it) + { + if (startCharCode == (hb_codepoint_t) -1) + { + startCharCode = _.first; + endCharCode = _.first; + glyphID = _.second; + } + else if (!_is_gid_consecutive (endCharCode, startCharCode, glyphID, _.first, _.second)) + { + CmapSubtableLongGroup grouprecord; + grouprecord.startCharCode = startCharCode; + grouprecord.endCharCode = endCharCode; + grouprecord.glyphID = glyphID; + c->copy (grouprecord); + + startCharCode = _.first; + endCharCode = _.first; + glyphID = _.second; + } + else + endCharCode = _.first; + } + + CmapSubtableLongGroup record; + record.startCharCode = startCharCode; + record.endCharCode = endCharCode; + record.glyphID = glyphID; + c->copy (record); + + this->format = 12; + this->reserved = 0; + this->length = c->length () - table_initpos; + this->groups.len = (this->length - min_size) / CmapSubtableLongGroup::static_size; + } + + static size_t get_sub_table_size (const hb_sorted_vector_t &groups_data) + { return 16 + 12 * groups_data.length; } + + private: + static bool _is_gid_consecutive (hb_codepoint_t endCharCode, + hb_codepoint_t startCharCode, + hb_codepoint_t glyphID, + hb_codepoint_t cp, + hb_codepoint_t new_gid) + { + return (cp - 1 == endCharCode) && + new_gid == glyphID + (cp - startCharCode); + } + +}; + +struct CmapSubtableFormat13 : CmapSubtableLongSegmented +{ + static hb_codepoint_t group_get_glyph (const CmapSubtableLongGroup &group, + hb_codepoint_t u HB_UNUSED) + { return group.glyphID; } +}; + +typedef enum +{ + GLYPH_VARIANT_NOT_FOUND = 0, + GLYPH_VARIANT_FOUND = 1, + GLYPH_VARIANT_USE_DEFAULT = 2 +} glyph_variant_t; + +struct UnicodeValueRange +{ + int cmp (const hb_codepoint_t &codepoint) const + { + if (codepoint < startUnicodeValue) return -1; + if (codepoint > startUnicodeValue + additionalCount) return +1; + return 0; + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + HBUINT24 startUnicodeValue; /* First value in this range. */ + HBUINT8 additionalCount; /* Number of additional values in this + * range. */ + public: + DEFINE_SIZE_STATIC (4); +}; + +struct DefaultUVS : SortedArray32Of +{ + void collect_unicodes (hb_set_t *out) const + { + unsigned int count = len; + for (unsigned int i = 0; i < count; i++) + { + hb_codepoint_t first = arrayZ[i].startUnicodeValue; + hb_codepoint_t last = hb_min ((hb_codepoint_t) (first + arrayZ[i].additionalCount), + (hb_codepoint_t) HB_UNICODE_MAX); + out->add_range (first, hb_min (last, 0x10FFFFu)); + } + } + + DefaultUVS* copy (hb_serialize_context_t *c, + const hb_set_t *unicodes) const + { + DefaultUVS *out = c->start_embed (); + if (unlikely (!out)) return nullptr; + auto snap = c->snapshot (); + + HBUINT32 len; + len = 0; + if (unlikely (!c->copy (len))) return nullptr; + unsigned init_len = c->length (); + + hb_codepoint_t lastCode = HB_MAP_VALUE_INVALID; + int count = -1; + + for (const UnicodeValueRange& _ : as_array ()) + { + for (const unsigned addcnt : hb_range ((unsigned) _.additionalCount + 1)) + { + unsigned curEntry = (unsigned) _.startUnicodeValue + addcnt; + if (!unicodes->has (curEntry)) continue; + count += 1; + if (lastCode == HB_MAP_VALUE_INVALID) + lastCode = curEntry; + else if (lastCode + count != curEntry) + { + UnicodeValueRange rec; + rec.startUnicodeValue = lastCode; + rec.additionalCount = count - 1; + c->copy (rec); + + lastCode = curEntry; + count = 0; + } + } + } + + if (lastCode != HB_MAP_VALUE_INVALID) + { + UnicodeValueRange rec; + rec.startUnicodeValue = lastCode; + rec.additionalCount = count; + c->copy (rec); + } + + if (c->length () - init_len == 0) + { + c->revert (snap); + return nullptr; + } + else + { + if (unlikely (!c->check_assign (out->len, + (c->length () - init_len) / UnicodeValueRange::static_size, + HB_SERIALIZE_ERROR_INT_OVERFLOW))) return nullptr; + return out; + } + } + + public: + DEFINE_SIZE_ARRAY (4, *this); +}; + +struct UVSMapping +{ + int cmp (const hb_codepoint_t &codepoint) const + { return unicodeValue.cmp (codepoint); } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + HBUINT24 unicodeValue; /* Base Unicode value of the UVS */ + HBGlyphID16 glyphID; /* Glyph ID of the UVS */ + public: + DEFINE_SIZE_STATIC (5); +}; + +struct NonDefaultUVS : SortedArray32Of +{ + void collect_unicodes (hb_set_t *out) const + { + for (const auto& a : as_array ()) + out->add (a.unicodeValue); + } + + void collect_mapping (hb_set_t *unicodes, /* OUT */ + hb_map_t *mapping /* OUT */) const + { + for (const auto& a : as_array ()) + { + hb_codepoint_t unicode = a.unicodeValue; + hb_codepoint_t glyphid = a.glyphID; + unicodes->add (unicode); + mapping->set (unicode, glyphid); + } + } + + void closure_glyphs (const hb_set_t *unicodes, + hb_set_t *glyphset) const + { + + as_array () + | hb_filter (unicodes, &UVSMapping::unicodeValue) + | hb_map (&UVSMapping::glyphID) + | hb_sink (glyphset) + ; + } + + NonDefaultUVS* copy (hb_serialize_context_t *c, + const hb_set_t *unicodes, + const hb_set_t *glyphs_requested, + const hb_map_t *glyph_map) const + { + NonDefaultUVS *out = c->start_embed (); + if (unlikely (!out)) return nullptr; + + auto it = + + as_array () + | hb_filter ([&] (const UVSMapping& _) + { + return unicodes->has (_.unicodeValue) || glyphs_requested->has (_.glyphID); + }) + ; + + if (!it) return nullptr; + + HBUINT32 len; + len = it.len (); + if (unlikely (!c->copy (len))) return nullptr; + + for (const UVSMapping& _ : it) + { + UVSMapping mapping; + mapping.unicodeValue = _.unicodeValue; + mapping.glyphID = glyph_map->get (_.glyphID); + c->copy (mapping); + } + + return out; + } + + public: + DEFINE_SIZE_ARRAY (4, *this); +}; + +struct VariationSelectorRecord +{ + glyph_variant_t get_glyph (hb_codepoint_t codepoint, + hb_codepoint_t *glyph, + const void *base) const + { + if ((base+defaultUVS).bfind (codepoint)) + return GLYPH_VARIANT_USE_DEFAULT; + const UVSMapping &nonDefault = (base+nonDefaultUVS).bsearch (codepoint); + if (nonDefault.glyphID) + { + *glyph = nonDefault.glyphID; + return GLYPH_VARIANT_FOUND; + } + return GLYPH_VARIANT_NOT_FOUND; + } + + VariationSelectorRecord(const VariationSelectorRecord& other) + { + *this = other; + } + + void operator= (const VariationSelectorRecord& other) + { + varSelector = other.varSelector; + HBUINT32 offset = other.defaultUVS; + defaultUVS = offset; + offset = other.nonDefaultUVS; + nonDefaultUVS = offset; + } + + void collect_unicodes (hb_set_t *out, const void *base) const + { + (base+defaultUVS).collect_unicodes (out); + (base+nonDefaultUVS).collect_unicodes (out); + } + + void collect_mapping (const void *base, + hb_set_t *unicodes, /* OUT */ + hb_map_t *mapping /* OUT */) const + { + (base+defaultUVS).collect_unicodes (unicodes); + (base+nonDefaultUVS).collect_mapping (unicodes, mapping); + } + + int cmp (const hb_codepoint_t &variation_selector) const + { return varSelector.cmp (variation_selector); } + + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + defaultUVS.sanitize (c, base) && + nonDefaultUVS.sanitize (c, base)); + } + + hb_pair_t + copy (hb_serialize_context_t *c, + const hb_set_t *unicodes, + const hb_set_t *glyphs_requested, + const hb_map_t *glyph_map, + const void *base) const + { + auto snap = c->snapshot (); + auto *out = c->embed (*this); + if (unlikely (!out)) return hb_pair (0, 0); + + out->defaultUVS = 0; + out->nonDefaultUVS = 0; + + unsigned non_default_uvs_objidx = 0; + if (nonDefaultUVS != 0) + { + c->push (); + if (c->copy (base+nonDefaultUVS, unicodes, glyphs_requested, glyph_map)) + non_default_uvs_objidx = c->pop_pack (); + else c->pop_discard (); + } + + unsigned default_uvs_objidx = 0; + if (defaultUVS != 0) + { + c->push (); + if (c->copy (base+defaultUVS, unicodes)) + default_uvs_objidx = c->pop_pack (); + else c->pop_discard (); + } + + + if (!default_uvs_objidx && !non_default_uvs_objidx) + c->revert (snap); + + return hb_pair (default_uvs_objidx, non_default_uvs_objidx); + } + + HBUINT24 varSelector; /* Variation selector. */ + Offset32To + defaultUVS; /* Offset to Default UVS Table. May be 0. */ + Offset32To + nonDefaultUVS; /* Offset to Non-Default UVS Table. May be 0. */ + public: + DEFINE_SIZE_STATIC (11); +}; + +struct CmapSubtableFormat14 +{ + glyph_variant_t get_glyph_variant (hb_codepoint_t codepoint, + hb_codepoint_t variation_selector, + hb_codepoint_t *glyph) const + { return record.bsearch (variation_selector).get_glyph (codepoint, glyph, this); } + + void collect_variation_selectors (hb_set_t *out) const + { + for (const auto& a : record.as_array ()) + out->add (a.varSelector); + } + void collect_variation_unicodes (hb_codepoint_t variation_selector, + hb_set_t *out) const + { record.bsearch (variation_selector).collect_unicodes (out, this); } + + void serialize (hb_serialize_context_t *c, + const hb_set_t *unicodes, + const hb_set_t *glyphs_requested, + const hb_map_t *glyph_map, + const void *base) + { + auto snap = c->snapshot (); + unsigned table_initpos = c->length (); + const char* init_tail = c->tail; + + if (unlikely (!c->extend_min (this))) return; + this->format = 14; + + auto src_tbl = reinterpret_cast (base); + + /* + * Some versions of OTS require that offsets are in order. Due to the use + * of push()/pop_pack() serializing the variation records in order results + * in the offsets being in reverse order (first record has the largest + * offset). While this is perfectly valid, it will cause some versions of + * OTS to consider this table bad. + * + * So to prevent this issue we serialize the variation records in reverse + * order, so that the offsets are ordered from small to large. Since + * variation records are supposed to be in increasing order of varSelector + * we then have to reverse the order of the written variation selector + * records after everything is finalized. + */ + hb_vector_t> obj_indices; + for (int i = src_tbl->record.len - 1; i >= 0; i--) + { + hb_pair_t result = src_tbl->record[i].copy (c, unicodes, glyphs_requested, glyph_map, base); + if (result.first || result.second) + obj_indices.push (result); + } + + if (c->length () - table_initpos == CmapSubtableFormat14::min_size) + { + c->revert (snap); + return; + } + + if (unlikely (!c->check_success (!obj_indices.in_error ()))) + return; + + int tail_len = init_tail - c->tail; + c->check_assign (this->length, c->length () - table_initpos + tail_len, + HB_SERIALIZE_ERROR_INT_OVERFLOW); + c->check_assign (this->record.len, + (c->length () - table_initpos - CmapSubtableFormat14::min_size) / + VariationSelectorRecord::static_size, + HB_SERIALIZE_ERROR_INT_OVERFLOW); + + /* Correct the incorrect write order by reversing the order of the variation + records array. */ + _reverse_variation_records (); + + /* Now that records are in the right order, we can set up the offsets. */ + _add_links_to_variation_records (c, obj_indices); + } + + void _reverse_variation_records () + { + record.as_array ().reverse (); + } + + void _add_links_to_variation_records (hb_serialize_context_t *c, + const hb_vector_t>& obj_indices) + { + for (unsigned i = 0; i < obj_indices.length; i++) + { + /* + * Since the record array has been reversed (see comments in copy()) + * but obj_indices has not been, the indices at obj_indices[i] + * are for the variation record at record[j]. + */ + int j = obj_indices.length - 1 - i; + c->add_link (record[j].defaultUVS, obj_indices[i].first); + c->add_link (record[j].nonDefaultUVS, obj_indices[i].second); + } + } + + void closure_glyphs (const hb_set_t *unicodes, + hb_set_t *glyphset) const + { + + hb_iter (record) + | hb_filter (hb_bool, &VariationSelectorRecord::nonDefaultUVS) + | hb_map (&VariationSelectorRecord::nonDefaultUVS) + | hb_map (hb_add (this)) + | hb_apply ([=] (const NonDefaultUVS& _) { _.closure_glyphs (unicodes, glyphset); }) + ; + } + + void collect_unicodes (hb_set_t *out) const + { + for (const VariationSelectorRecord& _ : record) + _.collect_unicodes (out, this); + } + + void collect_mapping (hb_set_t *unicodes, /* OUT */ + hb_map_t *mapping /* OUT */) const + { + for (const VariationSelectorRecord& _ : record) + _.collect_mapping (this, unicodes, mapping); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + record.sanitize (c, this)); + } + + protected: + HBUINT16 format; /* Format number is set to 14. */ + HBUINT32 length; /* Byte length of this subtable. */ + SortedArray32Of + record; /* Variation selector records; sorted + * in increasing order of `varSelector'. */ + public: + DEFINE_SIZE_ARRAY (10, record); +}; + +struct CmapSubtable +{ + /* Note: We intentionally do NOT implement subtable formats 2 and 8. */ + + bool get_glyph (hb_codepoint_t codepoint, + hb_codepoint_t *glyph) const + { + switch (u.format) { + case 0: return u.format0 .get_glyph (codepoint, glyph); + case 4: return u.format4 .get_glyph (codepoint, glyph); + case 6: return u.format6 .get_glyph (codepoint, glyph); + case 10: return u.format10.get_glyph (codepoint, glyph); + case 12: return u.format12.get_glyph (codepoint, glyph); + case 13: return u.format13.get_glyph (codepoint, glyph); + case 14: + default: return false; + } + } + void collect_unicodes (hb_set_t *out, unsigned int num_glyphs = UINT_MAX) const + { + switch (u.format) { + case 0: u.format0 .collect_unicodes (out); return; + case 4: u.format4 .collect_unicodes (out); return; + case 6: u.format6 .collect_unicodes (out); return; + case 10: u.format10.collect_unicodes (out); return; + case 12: u.format12.collect_unicodes (out, num_glyphs); return; + case 13: u.format13.collect_unicodes (out, num_glyphs); return; + case 14: + default: return; + } + } + + void collect_mapping (hb_set_t *unicodes, /* OUT */ + hb_map_t *mapping, /* OUT */ + unsigned num_glyphs = UINT_MAX) const + { + switch (u.format) { + case 0: u.format0 .collect_mapping (unicodes, mapping); return; + case 4: u.format4 .collect_mapping (unicodes, mapping); return; + case 6: u.format6 .collect_mapping (unicodes, mapping); return; + case 10: u.format10.collect_mapping (unicodes, mapping); return; + case 12: u.format12.collect_mapping (unicodes, mapping, num_glyphs); return; + case 13: u.format13.collect_mapping (unicodes, mapping, num_glyphs); return; + case 14: + default: return; + } + } + + unsigned get_language () const + { + switch (u.format) { + case 0: return u.format0 .get_language (); + case 4: return u.format4 .get_language (); + case 6: return u.format6 .get_language (); + case 10: return u.format10.get_language (); + case 12: return u.format12.get_language (); + case 13: return u.format13.get_language (); + case 14: + default: return 0; + } + } + + template + void serialize (hb_serialize_context_t *c, + Iterator it, + unsigned format, + const hb_subset_plan_t *plan, + const void *base) + { + switch (format) { + case 4: return u.format4.serialize (c, it); + case 12: return u.format12.serialize (c, it); + case 14: return u.format14.serialize (c, plan->unicodes, plan->glyphs_requested, plan->glyph_map, base); + default: return; + } + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (!u.format.sanitize (c)) return_trace (false); + switch (u.format) { + case 0: return_trace (u.format0 .sanitize (c)); + case 4: return_trace (u.format4 .sanitize (c)); + case 6: return_trace (u.format6 .sanitize (c)); + case 10: return_trace (u.format10.sanitize (c)); + case 12: return_trace (u.format12.sanitize (c)); + case 13: return_trace (u.format13.sanitize (c)); + case 14: return_trace (u.format14.sanitize (c)); + default:return_trace (true); + } + } + + public: + union { + HBUINT16 format; /* Format identifier */ + CmapSubtableFormat0 format0; + CmapSubtableFormat4 format4; + CmapSubtableFormat6 format6; + CmapSubtableFormat10 format10; + CmapSubtableFormat12 format12; + CmapSubtableFormat13 format13; + CmapSubtableFormat14 format14; + } u; + public: + DEFINE_SIZE_UNION (2, format); +}; + + +struct EncodingRecord +{ + int cmp (const EncodingRecord &other) const + { + int ret; + ret = platformID.cmp (other.platformID); + if (ret) return ret; + ret = encodingID.cmp (other.encodingID); + if (ret) return ret; + return 0; + } + + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + subtable.sanitize (c, base)); + } + + template + EncodingRecord* copy (hb_serialize_context_t *c, + Iterator it, + unsigned format, + const void *base, + const hb_subset_plan_t *plan, + /* INOUT */ unsigned *objidx) const + { + TRACE_SERIALIZE (this); + auto snap = c->snapshot (); + auto *out = c->embed (this); + if (unlikely (!out)) return_trace (nullptr); + out->subtable = 0; + + if (*objidx == 0) + { + CmapSubtable *cmapsubtable = c->push (); + unsigned origin_length = c->length (); + cmapsubtable->serialize (c, it, format, plan, &(base+subtable)); + if (c->length () - origin_length > 0) *objidx = c->pop_pack (); + else c->pop_discard (); + } + + if (*objidx == 0) + { + c->revert (snap); + return_trace (nullptr); + } + + c->add_link (out->subtable, *objidx); + return_trace (out); + } + + HBUINT16 platformID; /* Platform ID. */ + HBUINT16 encodingID; /* Platform-specific encoding ID. */ + Offset32To + subtable; /* Byte offset from beginning of table to the subtable for this encoding. */ + public: + DEFINE_SIZE_STATIC (8); +}; + +struct SubtableUnicodesCache { + + private: + const void* base; + hb_hashmap_t> cached_unicodes; + + public: + SubtableUnicodesCache(const void* cmap_base) + : base(cmap_base), cached_unicodes() {} + + hb_set_t* set_for (const EncodingRecord* record) + { + if (!cached_unicodes.has ((intptr_t) record)) + { + hb_set_t *s = hb_set_create (); + if (unlikely (s->in_error ())) + return hb_set_get_empty (); + + (base+record->subtable).collect_unicodes (s); + + if (unlikely (!cached_unicodes.set ((intptr_t) record, hb::unique_ptr {s}))) + return hb_set_get_empty (); + + return s; + } + return cached_unicodes.get ((intptr_t) record); + } + +}; + +static inline uint_fast16_t +_hb_symbol_pua_map (unsigned codepoint) +{ + if (codepoint <= 0x00FFu) + { + /* For symbol-encoded OpenType fonts, we duplicate the + * U+F000..F0FF range at U+0000..U+00FF. That's what + * Windows seems to do, and that's hinted about at: + * https://docs.microsoft.com/en-us/typography/opentype/spec/recom + * under "Non-Standard (Symbol) Fonts". */ + return 0xF000u + codepoint; + } + return 0; +} + +struct cmap +{ + static constexpr hb_tag_t tableTag = HB_OT_TAG_cmap; + + template + bool serialize (hb_serialize_context_t *c, + Iterator it, + EncodingRecIter encodingrec_iter, + const void *base, + const hb_subset_plan_t *plan, + bool drop_format_4 = false) + { + if (unlikely (!c->extend_min ((*this)))) return false; + this->version = 0; + + unsigned format4objidx = 0, format12objidx = 0, format14objidx = 0; + auto snap = c->snapshot (); + + SubtableUnicodesCache unicodes_cache (base); + for (const EncodingRecord& _ : encodingrec_iter) + { + if (c->in_error ()) + return false; + + unsigned format = (base+_.subtable).u.format; + if (format != 4 && format != 12 && format != 14) continue; + + hb_set_t* unicodes_set = unicodes_cache.set_for (&_); + + if (!drop_format_4 && format == 4) + { + c->copy (_, + it | hb_filter (*unicodes_set, hb_first), 4u, base, plan, &format4objidx); + if (c->in_error () && c->only_overflow ()) + { + // cmap4 overflowed, reset and retry serialization without format 4 subtables. + c->revert (snap); + return serialize (c, it, + encodingrec_iter, + base, + plan, + true); + } + } + + else if (format == 12) + { + if (_can_drop (_, *unicodes_set, base, unicodes_cache, + it | hb_map (hb_first), encodingrec_iter)) continue; + c->copy (_, + it | hb_filter (*unicodes_set, hb_first), 12u, base, plan, &format12objidx); + } + else if (format == 14) c->copy (_, it, 14u, base, plan, &format14objidx); + } + c->check_assign(this->encodingRecord.len, + (c->length () - cmap::min_size)/EncodingRecord::static_size, + HB_SERIALIZE_ERROR_INT_OVERFLOW); + + // Fail if format 4 was dropped and there is no cmap12. + return !drop_format_4 || format12objidx; + } + + template + bool _can_drop (const EncodingRecord& cmap12, + const hb_set_t& cmap12_unicodes, + const void* base, + SubtableUnicodesCache& unicodes_cache, + Iterator subset_unicodes, + EncodingRecordIterator encoding_records) + { + for (auto cp : + subset_unicodes | hb_filter (cmap12_unicodes)) + { + if (cp >= 0x10000) return false; + } + + unsigned target_platform; + unsigned target_encoding; + unsigned target_language = (base+cmap12.subtable).get_language (); + + if (cmap12.platformID == 0 && cmap12.encodingID == 4) + { + target_platform = 0; + target_encoding = 3; + } else if (cmap12.platformID == 3 && cmap12.encodingID == 10) { + target_platform = 3; + target_encoding = 1; + } else { + return false; + } + + for (const auto& _ : encoding_records) + { + if (_.platformID != target_platform + || _.encodingID != target_encoding + || (base+_.subtable).get_language() != target_language) + continue; + + hb_set_t* sibling_unicodes = unicodes_cache.set_for (&_); + + auto cmap12 = + subset_unicodes | hb_filter (cmap12_unicodes); + auto sibling = + subset_unicodes | hb_filter (*sibling_unicodes); + for (; cmap12 && sibling; cmap12++, sibling++) + { + unsigned a = *cmap12; + unsigned b = *sibling; + if (a != b) return false; + } + + return !cmap12 && !sibling; + } + + return false; + } + + void closure_glyphs (const hb_set_t *unicodes, + hb_set_t *glyphset) const + { + + hb_iter (encodingRecord) + | hb_map (&EncodingRecord::subtable) + | hb_map (hb_add (this)) + | hb_filter ([&] (const CmapSubtable& _) { return _.u.format == 14; }) + | hb_apply ([=] (const CmapSubtable& _) { _.u.format14.closure_glyphs (unicodes, glyphset); }) + ; + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + + cmap *cmap_prime = c->serializer->start_embed (); + if (unlikely (!c->serializer->check_success (cmap_prime))) return_trace (false); + + auto encodingrec_iter = + + hb_iter (encodingRecord) + | hb_filter ([&] (const EncodingRecord& _) + { + if ((_.platformID == 0 && _.encodingID == 3) || + (_.platformID == 0 && _.encodingID == 4) || + (_.platformID == 3 && _.encodingID == 1) || + (_.platformID == 3 && _.encodingID == 10) || + (this + _.subtable).u.format == 14) + return true; + + return false; + }) + ; + + if (unlikely (!encodingrec_iter.len ())) return_trace (false); + + const EncodingRecord *unicode_bmp= nullptr, *unicode_ucs4 = nullptr, *ms_bmp = nullptr, *ms_ucs4 = nullptr; + bool has_format12 = false; + + for (const EncodingRecord& _ : encodingrec_iter) + { + unsigned format = (this + _.subtable).u.format; + if (format == 12) has_format12 = true; + + const EncodingRecord *table = std::addressof (_); + if (_.platformID == 0 && _.encodingID == 3) unicode_bmp = table; + else if (_.platformID == 0 && _.encodingID == 4) unicode_ucs4 = table; + else if (_.platformID == 3 && _.encodingID == 1) ms_bmp = table; + else if (_.platformID == 3 && _.encodingID == 10) ms_ucs4 = table; + } + + if (unlikely (!has_format12 && !unicode_bmp && !ms_bmp)) return_trace (false); + if (unlikely (has_format12 && (!unicode_ucs4 && !ms_ucs4))) return_trace (false); + + auto it = + + c->plan->unicode_to_new_gid_list.iter () + | hb_filter ([&] (const hb_pair_t _) + { return (_.second != HB_MAP_VALUE_INVALID); }) + ; + + return_trace (cmap_prime->serialize (c->serializer, it, encodingrec_iter, this, c->plan)); + } + + const CmapSubtable *find_best_subtable (bool *symbol = nullptr) const + { + if (symbol) *symbol = false; + + const CmapSubtable *subtable; + + /* Symbol subtable. + * Prefer symbol if available. + * https://github.com/harfbuzz/harfbuzz/issues/1918 */ + if ((subtable = this->find_subtable (3, 0))) + { + if (symbol) *symbol = true; + return subtable; + } + + /* 32-bit subtables. */ + if ((subtable = this->find_subtable (3, 10))) return subtable; + if ((subtable = this->find_subtable (0, 6))) return subtable; + if ((subtable = this->find_subtable (0, 4))) return subtable; + + /* 16-bit subtables. */ + if ((subtable = this->find_subtable (3, 1))) return subtable; + if ((subtable = this->find_subtable (0, 3))) return subtable; + if ((subtable = this->find_subtable (0, 2))) return subtable; + if ((subtable = this->find_subtable (0, 1))) return subtable; + if ((subtable = this->find_subtable (0, 0))) return subtable; + + /* Meh. */ + return &Null (CmapSubtable); + } + + struct accelerator_t + { + accelerator_t (hb_face_t *face) + { + this->table = hb_sanitize_context_t ().reference_table (face); + bool symbol; + this->subtable = table->find_best_subtable (&symbol); + this->subtable_uvs = &Null (CmapSubtableFormat14); + { + const CmapSubtable *st = table->find_subtable (0, 5); + if (st && st->u.format == 14) + subtable_uvs = &st->u.format14; + } + + this->get_glyph_data = subtable; + if (unlikely (symbol)) + { + switch ((unsigned) face->table.OS2->get_font_page ()) { + case OS2::font_page_t::FONT_PAGE_NONE: + this->get_glyph_funcZ = get_glyph_from_symbol; + break; +#ifndef HB_NO_OT_SHAPER_ARABIC_FALLBACK + case OS2::font_page_t::FONT_PAGE_SIMP_ARABIC: + this->get_glyph_funcZ = get_glyph_from_symbol; + break; + case OS2::font_page_t::FONT_PAGE_TRAD_ARABIC: + this->get_glyph_funcZ = get_glyph_from_symbol; + break; +#endif + default: + this->get_glyph_funcZ = get_glyph_from; + break; + } + } + else + { + switch (subtable->u.format) { + /* Accelerate format 4 and format 12. */ + default: + this->get_glyph_funcZ = get_glyph_from; + break; + case 12: + this->get_glyph_funcZ = get_glyph_from; + break; + case 4: + { + this->format4_accel.init (&subtable->u.format4); + this->get_glyph_data = &this->format4_accel; + this->get_glyph_funcZ = this->format4_accel.get_glyph_func; + break; + } + } + } + } + ~accelerator_t () { this->table.destroy (); } + + bool get_nominal_glyph (hb_codepoint_t unicode, + hb_codepoint_t *glyph) const + { + if (unlikely (!this->get_glyph_funcZ)) return false; + return this->get_glyph_funcZ (this->get_glyph_data, unicode, glyph); + } + unsigned int get_nominal_glyphs (unsigned int count, + const hb_codepoint_t *first_unicode, + unsigned int unicode_stride, + hb_codepoint_t *first_glyph, + unsigned int glyph_stride) const + { + if (unlikely (!this->get_glyph_funcZ)) return 0; + + hb_cmap_get_glyph_func_t get_glyph_funcZ = this->get_glyph_funcZ; + const void *get_glyph_data = this->get_glyph_data; + + unsigned int done; + for (done = 0; + done < count && get_glyph_funcZ (get_glyph_data, *first_unicode, first_glyph); + done++) + { + first_unicode = &StructAtOffsetUnaligned (first_unicode, unicode_stride); + first_glyph = &StructAtOffsetUnaligned (first_glyph, glyph_stride); + } + return done; + } + + bool get_variation_glyph (hb_codepoint_t unicode, + hb_codepoint_t variation_selector, + hb_codepoint_t *glyph) const + { + switch (this->subtable_uvs->get_glyph_variant (unicode, + variation_selector, + glyph)) + { + case GLYPH_VARIANT_NOT_FOUND: return false; + case GLYPH_VARIANT_FOUND: return true; + case GLYPH_VARIANT_USE_DEFAULT: break; + } + + return get_nominal_glyph (unicode, glyph); + } + + void collect_unicodes (hb_set_t *out, unsigned int num_glyphs) const + { subtable->collect_unicodes (out, num_glyphs); } + void collect_mapping (hb_set_t *unicodes, hb_map_t *mapping, + unsigned num_glyphs = UINT_MAX) const + { subtable->collect_mapping (unicodes, mapping, num_glyphs); } + void collect_variation_selectors (hb_set_t *out) const + { subtable_uvs->collect_variation_selectors (out); } + void collect_variation_unicodes (hb_codepoint_t variation_selector, + hb_set_t *out) const + { subtable_uvs->collect_variation_unicodes (variation_selector, out); } + + protected: + typedef bool (*hb_cmap_get_glyph_func_t) (const void *obj, + hb_codepoint_t codepoint, + hb_codepoint_t *glyph); + typedef uint_fast16_t (*hb_pua_remap_func_t) (unsigned); + + template + HB_INTERNAL static bool get_glyph_from (const void *obj, + hb_codepoint_t codepoint, + hb_codepoint_t *glyph) + { + const Type *typed_obj = (const Type *) obj; + return typed_obj->get_glyph (codepoint, glyph); + } + + template + HB_INTERNAL static bool get_glyph_from_symbol (const void *obj, + hb_codepoint_t codepoint, + hb_codepoint_t *glyph) + { + const Type *typed_obj = (const Type *) obj; + if (likely (typed_obj->get_glyph (codepoint, glyph))) + return true; + + if (hb_codepoint_t c = remap (codepoint)) + return typed_obj->get_glyph (c, glyph); + + return false; + } + + private: + hb_nonnull_ptr_t subtable; + hb_nonnull_ptr_t subtable_uvs; + + hb_cmap_get_glyph_func_t get_glyph_funcZ; + const void *get_glyph_data; + + CmapSubtableFormat4::accelerator_t format4_accel; + + public: + hb_blob_ptr_t table; + }; + + protected: + + const CmapSubtable *find_subtable (unsigned int platform_id, + unsigned int encoding_id) const + { + EncodingRecord key; + key.platformID = platform_id; + key.encodingID = encoding_id; + + const EncodingRecord &result = encodingRecord.bsearch (key); + if (!result.subtable) + return nullptr; + + return &(this+result.subtable); + } + + const EncodingRecord *find_encodingrec (unsigned int platform_id, + unsigned int encoding_id) const + { + EncodingRecord key; + key.platformID = platform_id; + key.encodingID = encoding_id; + + return encodingRecord.as_array ().bsearch (key); + } + + bool find_subtable (unsigned format) const + { + auto it = + + hb_iter (encodingRecord) + | hb_map (&EncodingRecord::subtable) + | hb_map (hb_add (this)) + | hb_filter ([&] (const CmapSubtable& _) { return _.u.format == format; }) + ; + + return it.len (); + } + + public: + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + likely (version == 0) && + encodingRecord.sanitize (c, this)); + } + + protected: + HBUINT16 version; /* Table version number (0). */ + SortedArray16Of + encodingRecord; /* Encoding tables. */ + public: + DEFINE_SIZE_ARRAY (4, encodingRecord); +}; + +struct cmap_accelerator_t : cmap::accelerator_t { + cmap_accelerator_t (hb_face_t *face) : cmap::accelerator_t (face) {} +}; + +} /* namespace OT */ + + +#endif /* HB_OT_CMAP_TABLE_HH */ diff --git a/libs/harfbuzz/src/hb-ot-color-cbdt-table.hh b/libs/harfbuzz/src/hb-ot-color-cbdt-table.hh new file mode 100644 index 00000000..23fa56c4 --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-color-cbdt-table.hh @@ -0,0 +1,997 @@ +/* + * Copyright © 2016 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): Seigo Nonaka, Calder Kitagawa + */ + +#ifndef HB_OT_COLOR_CBDT_TABLE_HH +#define HB_OT_COLOR_CBDT_TABLE_HH + +#include "hb-open-type.hh" + +/* + * CBLC -- Color Bitmap Location + * https://docs.microsoft.com/en-us/typography/opentype/spec/cblc + * https://docs.microsoft.com/en-us/typography/opentype/spec/eblc + * CBDT -- Color Bitmap Data + * https://docs.microsoft.com/en-us/typography/opentype/spec/cbdt + * https://docs.microsoft.com/en-us/typography/opentype/spec/ebdt + */ +#define HB_OT_TAG_CBLC HB_TAG('C','B','L','C') +#define HB_OT_TAG_CBDT HB_TAG('C','B','D','T') + + +namespace OT { + +struct cblc_bitmap_size_subset_context_t +{ + const char *cbdt; + unsigned int cbdt_length; + hb_vector_t *cbdt_prime; + unsigned int size; /* INOUT + * Input: old size of IndexSubtable + * Output: new size of IndexSubtable + */ + unsigned int num_tables; /* INOUT + * Input: old number of subtables. + * Output: new number of subtables. + */ + hb_codepoint_t start_glyph; /* OUT */ + hb_codepoint_t end_glyph; /* OUT */ +}; + +static inline bool +_copy_data_to_cbdt (hb_vector_t *cbdt_prime, + const void *data, + unsigned length) +{ + unsigned int new_len = cbdt_prime->length + length; + if (unlikely (!cbdt_prime->alloc (new_len))) return false; + memcpy (cbdt_prime->arrayZ + cbdt_prime->length, data, length); + cbdt_prime->length = new_len; + return true; +} + +struct SmallGlyphMetrics +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + void get_extents (hb_font_t *font, hb_glyph_extents_t *extents) const + { + extents->x_bearing = font->em_scale_x (bearingX); + extents->y_bearing = font->em_scale_y (bearingY); + extents->width = font->em_scale_x (width); + extents->height = font->em_scale_y (-static_cast(height)); + } + + HBUINT8 height; + HBUINT8 width; + HBINT8 bearingX; + HBINT8 bearingY; + HBUINT8 advance; + public: + DEFINE_SIZE_STATIC (5); +}; + +struct BigGlyphMetrics : SmallGlyphMetrics +{ + HBINT8 vertBearingX; + HBINT8 vertBearingY; + HBUINT8 vertAdvance; + public: + DEFINE_SIZE_STATIC (8); +}; + +struct SBitLineMetrics +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + HBINT8 ascender; + HBINT8 decender; + HBUINT8 widthMax; + HBINT8 caretSlopeNumerator; + HBINT8 caretSlopeDenominator; + HBINT8 caretOffset; + HBINT8 minOriginSB; + HBINT8 minAdvanceSB; + HBINT8 maxBeforeBL; + HBINT8 minAfterBL; + HBINT8 padding1; + HBINT8 padding2; + public: + DEFINE_SIZE_STATIC (12); +}; + + +/* + * Index Subtables. + */ + +struct IndexSubtableHeader +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + HBUINT16 indexFormat; + HBUINT16 imageFormat; + HBUINT32 imageDataOffset; + public: + DEFINE_SIZE_STATIC (8); +}; + +template +struct IndexSubtableFormat1Or3 +{ + bool sanitize (hb_sanitize_context_t *c, unsigned int glyph_count) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + offsetArrayZ.sanitize (c, glyph_count + 1)); + } + + bool get_image_data (unsigned int idx, + unsigned int *offset, + unsigned int *length) const + { + if (unlikely (offsetArrayZ[idx + 1] <= offsetArrayZ[idx])) + return false; + + *offset = header.imageDataOffset + offsetArrayZ[idx]; + *length = offsetArrayZ[idx + 1] - offsetArrayZ[idx]; + return true; + } + + bool add_offset (hb_serialize_context_t *c, + unsigned int offset, + unsigned int *size /* OUT (accumulated) */) + { + TRACE_SERIALIZE (this); + Offset embedded_offset; + embedded_offset = offset; + *size += sizeof (OffsetType); + auto *o = c->embed (embedded_offset); + return_trace ((bool) o); + } + + IndexSubtableHeader header; + UnsizedArrayOf> + offsetArrayZ; + public: + DEFINE_SIZE_ARRAY (8, offsetArrayZ); +}; + +struct IndexSubtableFormat1 : IndexSubtableFormat1Or3 {}; +struct IndexSubtableFormat3 : IndexSubtableFormat1Or3 {}; + +struct IndexSubtable +{ + bool sanitize (hb_sanitize_context_t *c, unsigned int glyph_count) const + { + TRACE_SANITIZE (this); + if (!u.header.sanitize (c)) return_trace (false); + switch (u.header.indexFormat) + { + case 1: return_trace (u.format1.sanitize (c, glyph_count)); + case 3: return_trace (u.format3.sanitize (c, glyph_count)); + default:return_trace (true); + } + } + + bool + finish_subtable (hb_serialize_context_t *c, + unsigned int cbdt_prime_len, + unsigned int num_glyphs, + unsigned int *size /* OUT (accumulated) */) + { + TRACE_SERIALIZE (this); + + unsigned int local_offset = cbdt_prime_len - u.header.imageDataOffset; + switch (u.header.indexFormat) + { + case 1: return_trace (u.format1.add_offset (c, local_offset, size)); + case 3: { + if (!u.format3.add_offset (c, local_offset, size)) + return_trace (false); + if (!(num_glyphs & 0x01)) // Pad to 32-bit alignment if needed. + return_trace (u.format3.add_offset (c, 0, size)); + return_trace (true); + } + // TODO: implement 2, 4, 5. + case 2: case 4: // No-op. + case 5: // Pad to 32-bit aligned. + default: return_trace (false); + } + } + + bool + fill_missing_glyphs (hb_serialize_context_t *c, + unsigned int cbdt_prime_len, + unsigned int num_missing, + unsigned int *size /* OUT (accumulated) */, + unsigned int *num_glyphs /* OUT (accumulated) */) + { + TRACE_SERIALIZE (this); + + unsigned int local_offset = cbdt_prime_len - u.header.imageDataOffset; + switch (u.header.indexFormat) + { + case 1: { + for (unsigned int i = 0; i < num_missing; i++) + { + if (unlikely (!u.format1.add_offset (c, local_offset, size))) + return_trace (false); + *num_glyphs += 1; + } + return_trace (true); + } + case 3: { + for (unsigned int i = 0; i < num_missing; i++) + { + if (unlikely (!u.format3.add_offset (c, local_offset, size))) + return_trace (false); + *num_glyphs += 1; + } + return_trace (true); + } + // TODO: implement 2, 4, 5. + case 2: // Add empty space in cbdt_prime?. + case 4: case 5: // No-op as sparse is supported. + default: return_trace (false); + } + } + + bool + copy_glyph_at_idx (hb_serialize_context_t *c, unsigned int idx, + const char *cbdt, unsigned int cbdt_length, + hb_vector_t *cbdt_prime /* INOUT */, + IndexSubtable *subtable_prime /* INOUT */, + unsigned int *size /* OUT (accumulated) */) const + { + TRACE_SERIALIZE (this); + + unsigned int offset, length, format; + if (unlikely (!get_image_data (idx, &offset, &length, &format))) return_trace (false); + if (unlikely (offset > cbdt_length || cbdt_length - offset < length)) return_trace (false); + + auto *header_prime = subtable_prime->get_header (); + unsigned int new_local_offset = cbdt_prime->length - (unsigned int) header_prime->imageDataOffset; + if (unlikely (!_copy_data_to_cbdt (cbdt_prime, cbdt + offset, length))) return_trace (false); + + return_trace (subtable_prime->add_offset (c, new_local_offset, size)); + } + + bool + add_offset (hb_serialize_context_t *c, unsigned int local_offset, + unsigned int *size /* OUT (accumulated) */) + { + TRACE_SERIALIZE (this); + switch (u.header.indexFormat) + { + case 1: return_trace (u.format1.add_offset (c, local_offset, size)); + case 3: return_trace (u.format3.add_offset (c, local_offset, size)); + // TODO: Implement tables 2, 4, 5 + case 2: // Should be a no-op. + case 4: case 5: // Handle sparse cases. + default: return_trace (false); + } + } + + bool get_extents (hb_glyph_extents_t *extents HB_UNUSED) const + { + switch (u.header.indexFormat) + { + case 2: case 5: /* TODO */ + case 1: case 3: case 4: /* Variable-metrics formats do not have metrics here. */ + default:return (false); + } + } + + bool + get_image_data (unsigned int idx, unsigned int *offset, + unsigned int *length, unsigned int *format) const + { + *format = u.header.imageFormat; + switch (u.header.indexFormat) + { + case 1: return u.format1.get_image_data (idx, offset, length); + case 3: return u.format3.get_image_data (idx, offset, length); + default: return false; + } + } + + const IndexSubtableHeader* get_header () const { return &u.header; } + + void populate_header (unsigned index_format, + unsigned image_format, + unsigned int image_data_offset, + unsigned int *size) + { + u.header.indexFormat = index_format; + u.header.imageFormat = image_format; + u.header.imageDataOffset = image_data_offset; + switch (u.header.indexFormat) + { + case 1: *size += IndexSubtableFormat1::min_size; break; + case 3: *size += IndexSubtableFormat3::min_size; break; + } + } + + protected: + union { + IndexSubtableHeader header; + IndexSubtableFormat1 format1; + IndexSubtableFormat3 format3; + /* TODO: Format 2, 4, 5. */ + } u; + public: + DEFINE_SIZE_UNION (8, header); +}; + +struct IndexSubtableRecord +{ + /* XXX Remove this and fix by not inserting it into vector. */ + IndexSubtableRecord& operator = (const IndexSubtableRecord &o) + { + firstGlyphIndex = o.firstGlyphIndex; + lastGlyphIndex = o.lastGlyphIndex; + offsetToSubtable = (unsigned) o.offsetToSubtable; + assert (offsetToSubtable.is_null ()); + return *this; + } + + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + firstGlyphIndex <= lastGlyphIndex && + offsetToSubtable.sanitize (c, base, lastGlyphIndex - firstGlyphIndex + 1)); + } + + const IndexSubtable* get_subtable (const void *base) const + { + return &(base+offsetToSubtable); + } + + bool add_new_subtable (hb_subset_context_t* c, + cblc_bitmap_size_subset_context_t *bitmap_size_context, + IndexSubtableRecord *record, + const hb_vector_t> *lookup, /* IN */ + const void *base, + unsigned int *start /* INOUT */) const + { + TRACE_SERIALIZE (this); + + auto *subtable = c->serializer->start_embed (); + if (unlikely (!subtable)) return_trace (false); + if (unlikely (!c->serializer->extend_min (subtable))) return_trace (false); + + auto *old_subtable = get_subtable (base); + auto *old_header = old_subtable->get_header (); + + subtable->populate_header (old_header->indexFormat, + old_header->imageFormat, + bitmap_size_context->cbdt_prime->length, + &bitmap_size_context->size); + + unsigned int num_glyphs = 0; + bool early_exit = false; + for (unsigned int i = *start; i < lookup->length; i++) + { + hb_codepoint_t new_gid = (*lookup)[i].first; + const IndexSubtableRecord *next_record = (*lookup)[i].second; + const IndexSubtable *next_subtable = next_record->get_subtable (base); + auto *next_header = next_subtable->get_header (); + if (next_header != old_header) + { + *start = i; + early_exit = true; + break; + } + unsigned int num_missing = record->add_glyph_for_subset (new_gid); + if (unlikely (!subtable->fill_missing_glyphs (c->serializer, + bitmap_size_context->cbdt_prime->length, + num_missing, + &bitmap_size_context->size, + &num_glyphs))) + return_trace (false); + + hb_codepoint_t old_gid = 0; + c->plan->old_gid_for_new_gid (new_gid, &old_gid); + if (old_gid < next_record->firstGlyphIndex) + return_trace (false); + + unsigned int old_idx = (unsigned int) old_gid - next_record->firstGlyphIndex; + if (unlikely (!next_subtable->copy_glyph_at_idx (c->serializer, + old_idx, + bitmap_size_context->cbdt, + bitmap_size_context->cbdt_length, + bitmap_size_context->cbdt_prime, + subtable, + &bitmap_size_context->size))) + return_trace (false); + num_glyphs += 1; + } + if (!early_exit) + *start = lookup->length; + if (unlikely (!subtable->finish_subtable (c->serializer, + bitmap_size_context->cbdt_prime->length, + num_glyphs, + &bitmap_size_context->size))) + return_trace (false); + return_trace (true); + } + + bool add_new_record (hb_subset_context_t *c, + cblc_bitmap_size_subset_context_t *bitmap_size_context, + const hb_vector_t> *lookup, /* IN */ + const void *base, + unsigned int *start, /* INOUT */ + hb_vector_t* records /* INOUT */) const + { + TRACE_SERIALIZE (this); + auto snap = c->serializer->snapshot (); + unsigned int old_size = bitmap_size_context->size; + unsigned int old_cbdt_prime_length = bitmap_size_context->cbdt_prime->length; + + // Set to invalid state to indicate filling glyphs is not yet started. + if (unlikely (!c->serializer->check_success (records->resize (records->length + 1)))) + return_trace (false); + + (*records)[records->length - 1].firstGlyphIndex = 1; + (*records)[records->length - 1].lastGlyphIndex = 0; + bitmap_size_context->size += IndexSubtableRecord::min_size; + + c->serializer->push (); + + if (unlikely (!add_new_subtable (c, bitmap_size_context, &((*records)[records->length - 1]), lookup, base, start))) + { + c->serializer->pop_discard (); + c->serializer->revert (snap); + bitmap_size_context->cbdt_prime->shrink (old_cbdt_prime_length); + bitmap_size_context->size = old_size; + records->resize (records->length - 1); + return_trace (false); + } + + bitmap_size_context->num_tables += 1; + return_trace (true); + } + + unsigned int add_glyph_for_subset (hb_codepoint_t gid) + { + if (firstGlyphIndex > lastGlyphIndex) + { + firstGlyphIndex = gid; + lastGlyphIndex = gid; + return 0; + } + // TODO maybe assert? this shouldn't occur. + if (lastGlyphIndex > gid) + return 0; + unsigned int num_missing = (unsigned int) (gid - lastGlyphIndex - 1); + lastGlyphIndex = gid; + return num_missing; + } + + bool get_extents (hb_glyph_extents_t *extents, const void *base) const + { return (base+offsetToSubtable).get_extents (extents); } + + bool get_image_data (unsigned int gid, + const void *base, + unsigned int *offset, + unsigned int *length, + unsigned int *format) const + { + if (gid < firstGlyphIndex || gid > lastGlyphIndex) return false; + return (base+offsetToSubtable).get_image_data (gid - firstGlyphIndex, + offset, length, format); + } + + HBGlyphID16 firstGlyphIndex; + HBGlyphID16 lastGlyphIndex; + Offset32To offsetToSubtable; + public: + DEFINE_SIZE_STATIC (8); +}; + +struct IndexSubtableArray +{ + friend struct CBDT; + + bool sanitize (hb_sanitize_context_t *c, unsigned int count) const + { + TRACE_SANITIZE (this); + return_trace (indexSubtablesZ.sanitize (c, count, this)); + } + + void + build_lookup (hb_subset_context_t *c, cblc_bitmap_size_subset_context_t *bitmap_size_context, + hb_vector_t> *lookup /* OUT */) const + { + bool start_glyph_is_set = false; + for (hb_codepoint_t new_gid = 0; new_gid < c->plan->num_output_glyphs (); new_gid++) + { + hb_codepoint_t old_gid; + if (unlikely (!c->plan->old_gid_for_new_gid (new_gid, &old_gid))) continue; + + const IndexSubtableRecord* record = find_table (old_gid, bitmap_size_context->num_tables); + if (unlikely (!record)) continue; + + // Don't add gaps to the lookup. The best way to determine if a glyph is a + // gap is that it has no image data. + unsigned int offset, length, format; + if (unlikely (!record->get_image_data (old_gid, this, &offset, &length, &format))) continue; + + lookup->push (hb_pair_t (new_gid, record)); + + if (!start_glyph_is_set) + { + bitmap_size_context->start_glyph = new_gid; + start_glyph_is_set = true; + } + + bitmap_size_context->end_glyph = new_gid; + } + } + + bool + subset (hb_subset_context_t *c, + cblc_bitmap_size_subset_context_t *bitmap_size_context) const + { + TRACE_SUBSET (this); + + auto *dst = c->serializer->start_embed (); + if (unlikely (!dst)) return_trace (false); + + hb_vector_t> lookup; + build_lookup (c, bitmap_size_context, &lookup); + if (unlikely (!c->serializer->propagate_error (lookup))) + return false; + + bitmap_size_context->size = 0; + bitmap_size_context->num_tables = 0; + hb_vector_t records; + for (unsigned int start = 0; start < lookup.length;) + { + if (unlikely (!lookup[start].second->add_new_record (c, bitmap_size_context, &lookup, this, &start, &records))) + { + // Discard any leftover pushes to the serializer from successful records. + for (unsigned int i = 0; i < records.length; i++) + c->serializer->pop_discard (); + return_trace (false); + } + } + + /* Workaround to ensure offset ordering is from least to greatest when + * resolving links. */ + hb_vector_t objidxs; + for (unsigned int i = 0; i < records.length; i++) + objidxs.push (c->serializer->pop_pack ()); + for (unsigned int i = 0; i < records.length; i++) + { + IndexSubtableRecord* record = c->serializer->embed (records[i]); + if (unlikely (!record)) return_trace (false); + c->serializer->add_link (record->offsetToSubtable, objidxs[records.length - 1 - i]); + } + return_trace (true); + } + + public: + const IndexSubtableRecord* find_table (hb_codepoint_t glyph, unsigned int numTables) const + { + for (unsigned int i = 0; i < numTables; ++i) + { + unsigned int firstGlyphIndex = indexSubtablesZ[i].firstGlyphIndex; + unsigned int lastGlyphIndex = indexSubtablesZ[i].lastGlyphIndex; + if (firstGlyphIndex <= glyph && glyph <= lastGlyphIndex) + return &indexSubtablesZ[i]; + } + return nullptr; + } + + protected: + UnsizedArrayOf indexSubtablesZ; +}; + +struct BitmapSizeTable +{ + friend struct CBLC; + friend struct CBDT; + + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + indexSubtableArrayOffset.sanitize (c, base, numberOfIndexSubtables) && + horizontal.sanitize (c) && + vertical.sanitize (c)); + } + + const IndexSubtableRecord * + find_table (hb_codepoint_t glyph, const void *base, const void **out_base) const + { + *out_base = &(base+indexSubtableArrayOffset); + return (base+indexSubtableArrayOffset).find_table (glyph, numberOfIndexSubtables); + } + + bool + subset (hb_subset_context_t *c, const void *base, + const char *cbdt, unsigned int cbdt_length, + hb_vector_t *cbdt_prime /* INOUT */) const + { + TRACE_SUBSET (this); + auto *out_table = c->serializer->embed (this); + if (unlikely (!out_table)) return_trace (false); + + cblc_bitmap_size_subset_context_t bitmap_size_context; + bitmap_size_context.cbdt = cbdt; + bitmap_size_context.cbdt_length = cbdt_length; + bitmap_size_context.cbdt_prime = cbdt_prime; + bitmap_size_context.size = indexTablesSize; + bitmap_size_context.num_tables = numberOfIndexSubtables; + bitmap_size_context.start_glyph = 1; + bitmap_size_context.end_glyph = 0; + + if (!out_table->indexSubtableArrayOffset.serialize_subset (c, + indexSubtableArrayOffset, + base, + &bitmap_size_context)) + return_trace (false); + if (!bitmap_size_context.size || + !bitmap_size_context.num_tables || + bitmap_size_context.start_glyph > bitmap_size_context.end_glyph) + return_trace (false); + + out_table->indexTablesSize = bitmap_size_context.size; + out_table->numberOfIndexSubtables = bitmap_size_context.num_tables; + out_table->startGlyphIndex = bitmap_size_context.start_glyph; + out_table->endGlyphIndex = bitmap_size_context.end_glyph; + return_trace (true); + } + + protected: + NNOffset32To + indexSubtableArrayOffset; + HBUINT32 indexTablesSize; + HBUINT32 numberOfIndexSubtables; + HBUINT32 colorRef; + SBitLineMetrics horizontal; + SBitLineMetrics vertical; + HBGlyphID16 startGlyphIndex; + HBGlyphID16 endGlyphIndex; + HBUINT8 ppemX; + HBUINT8 ppemY; + HBUINT8 bitDepth; + HBINT8 flags; + public: + DEFINE_SIZE_STATIC (48); +}; + + +/* + * Glyph Bitmap Data Formats. + */ + +struct GlyphBitmapDataFormat17 +{ + SmallGlyphMetrics glyphMetrics; + Array32Of data; + public: + DEFINE_SIZE_ARRAY (9, data); +}; + +struct GlyphBitmapDataFormat18 +{ + BigGlyphMetrics glyphMetrics; + Array32Of data; + public: + DEFINE_SIZE_ARRAY (12, data); +}; + +struct GlyphBitmapDataFormat19 +{ + Array32Of data; + public: + DEFINE_SIZE_ARRAY (4, data); +}; + +struct CBLC +{ + friend struct CBDT; + + static constexpr hb_tag_t tableTag = HB_OT_TAG_CBLC; + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + likely (version.major == 2 || version.major == 3) && + sizeTables.sanitize (c, this)); + } + + static bool + sink_cbdt (hb_subset_context_t *c, hb_vector_t* cbdt_prime) + { + hb_blob_t *cbdt_prime_blob = hb_blob_create (cbdt_prime->arrayZ, + cbdt_prime->length, + HB_MEMORY_MODE_WRITABLE, + cbdt_prime->arrayZ, + hb_free); + cbdt_prime->init (); // Leak arrayZ to the blob. + bool ret = c->plan->add_table (HB_OT_TAG_CBDT, cbdt_prime_blob); + hb_blob_destroy (cbdt_prime_blob); + return ret; + } + + bool + subset_size_table (hb_subset_context_t *c, const BitmapSizeTable& table, + const char *cbdt /* IN */, unsigned int cbdt_length, + CBLC *cblc_prime /* INOUT */, hb_vector_t *cbdt_prime /* INOUT */) const + { + TRACE_SUBSET (this); + cblc_prime->sizeTables.len++; + + auto snap = c->serializer->snapshot (); + auto cbdt_prime_len = cbdt_prime->length; + + if (!table.subset (c, this, cbdt, cbdt_length, cbdt_prime)) + { + cblc_prime->sizeTables.len--; + c->serializer->revert (snap); + cbdt_prime->shrink (cbdt_prime_len); + return_trace (false); + } + return_trace (true); + } + + // Implemented in cc file as it depends on definition of CBDT. + HB_INTERNAL bool subset (hb_subset_context_t *c) const; + + protected: + const BitmapSizeTable &choose_strike (hb_font_t *font) const + { + unsigned count = sizeTables.len; + if (unlikely (!count)) + return Null (BitmapSizeTable); + + unsigned int requested_ppem = hb_max (font->x_ppem, font->y_ppem); + if (!requested_ppem) + requested_ppem = 1<<30; /* Choose largest strike. */ + unsigned int best_i = 0; + unsigned int best_ppem = hb_max (sizeTables[0].ppemX, sizeTables[0].ppemY); + + for (unsigned int i = 1; i < count; i++) + { + unsigned int ppem = hb_max (sizeTables[i].ppemX, sizeTables[i].ppemY); + if ((requested_ppem <= ppem && ppem < best_ppem) || + (requested_ppem > best_ppem && ppem > best_ppem)) + { + best_i = i; + best_ppem = ppem; + } + } + + return sizeTables[best_i]; + } + + protected: + FixedVersion<> version; + Array32Of sizeTables; + public: + DEFINE_SIZE_ARRAY (8, sizeTables); +}; + +struct CBDT +{ + static constexpr hb_tag_t tableTag = HB_OT_TAG_CBDT; + + struct accelerator_t + { + accelerator_t (hb_face_t *face) + { + this->cblc = hb_sanitize_context_t ().reference_table (face); + this->cbdt = hb_sanitize_context_t ().reference_table (face); + + upem = hb_face_get_upem (face); + } + ~accelerator_t () + { + this->cblc.destroy (); + this->cbdt.destroy (); + } + + bool + get_extents (hb_font_t *font, hb_codepoint_t glyph, hb_glyph_extents_t *extents) const + { + const void *base; + const BitmapSizeTable &strike = this->cblc->choose_strike (font); + const IndexSubtableRecord *subtable_record = strike.find_table (glyph, cblc, &base); + if (!subtable_record || !strike.ppemX || !strike.ppemY) + return false; + + if (subtable_record->get_extents (extents, base)) + return true; + + unsigned int image_offset = 0, image_length = 0, image_format = 0; + if (!subtable_record->get_image_data (glyph, base, &image_offset, &image_length, &image_format)) + return false; + + unsigned int cbdt_len = cbdt.get_length (); + if (unlikely (image_offset > cbdt_len || cbdt_len - image_offset < image_length)) + return false; + + switch (image_format) + { + case 17: { + if (unlikely (image_length < GlyphBitmapDataFormat17::min_size)) + return false; + auto &glyphFormat17 = StructAtOffset (this->cbdt, image_offset); + glyphFormat17.glyphMetrics.get_extents (font, extents); + break; + } + case 18: { + if (unlikely (image_length < GlyphBitmapDataFormat18::min_size)) + return false; + auto &glyphFormat18 = StructAtOffset (this->cbdt, image_offset); + glyphFormat18.glyphMetrics.get_extents (font, extents); + break; + } + default: return false; /* TODO: Support other image formats. */ + } + + /* Convert to font units. */ + float x_scale = upem / (float) strike.ppemX; + float y_scale = upem / (float) strike.ppemY; + extents->x_bearing = roundf (extents->x_bearing * x_scale); + extents->y_bearing = roundf (extents->y_bearing * y_scale); + extents->width = roundf (extents->width * x_scale); + extents->height = roundf (extents->height * y_scale); + + return true; + } + + hb_blob_t* + reference_png (hb_font_t *font, hb_codepoint_t glyph) const + { + const void *base; + const BitmapSizeTable &strike = this->cblc->choose_strike (font); + const IndexSubtableRecord *subtable_record = strike.find_table (glyph, cblc, &base); + if (!subtable_record || !strike.ppemX || !strike.ppemY) + return hb_blob_get_empty (); + + unsigned int image_offset = 0, image_length = 0, image_format = 0; + if (!subtable_record->get_image_data (glyph, base, &image_offset, &image_length, &image_format)) + return hb_blob_get_empty (); + + unsigned int cbdt_len = cbdt.get_length (); + if (unlikely (image_offset > cbdt_len || cbdt_len - image_offset < image_length)) + return hb_blob_get_empty (); + + switch (image_format) + { + case 17: + { + if (unlikely (image_length < GlyphBitmapDataFormat17::min_size)) + return hb_blob_get_empty (); + auto &glyphFormat17 = StructAtOffset (this->cbdt, image_offset); + return hb_blob_create_sub_blob (cbdt.get_blob (), + image_offset + GlyphBitmapDataFormat17::min_size, + glyphFormat17.data.len); + } + case 18: + { + if (unlikely (image_length < GlyphBitmapDataFormat18::min_size)) + return hb_blob_get_empty (); + auto &glyphFormat18 = StructAtOffset (this->cbdt, image_offset); + return hb_blob_create_sub_blob (cbdt.get_blob (), + image_offset + GlyphBitmapDataFormat18::min_size, + glyphFormat18.data.len); + } + case 19: + { + if (unlikely (image_length < GlyphBitmapDataFormat19::min_size)) + return hb_blob_get_empty (); + auto &glyphFormat19 = StructAtOffset (this->cbdt, image_offset); + return hb_blob_create_sub_blob (cbdt.get_blob (), + image_offset + GlyphBitmapDataFormat19::min_size, + glyphFormat19.data.len); + } + default: return hb_blob_get_empty (); /* TODO: Support other image formats. */ + } + } + + bool has_data () const { return cbdt.get_length (); } + + private: + hb_blob_ptr_t cblc; + hb_blob_ptr_t cbdt; + + unsigned int upem; + }; + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + likely (version.major == 2 || version.major == 3)); + } + + protected: + FixedVersion<> version; + UnsizedArrayOf dataZ; + public: + DEFINE_SIZE_ARRAY (4, dataZ); +}; + +inline bool +CBLC::subset (hb_subset_context_t *c) const +{ + TRACE_SUBSET (this); + + auto *cblc_prime = c->serializer->start_embed (); + + // Use a vector as a secondary buffer as the tables need to be built in parallel. + hb_vector_t cbdt_prime; + + if (unlikely (!cblc_prime)) return_trace (false); + if (unlikely (!c->serializer->extend_min (cblc_prime))) return_trace (false); + cblc_prime->version = version; + + hb_blob_t* cbdt_blob = hb_sanitize_context_t ().reference_table (c->plan->source); + unsigned int cbdt_length; + CBDT* cbdt = (CBDT *) hb_blob_get_data (cbdt_blob, &cbdt_length); + if (unlikely (cbdt_length < CBDT::min_size)) + { + hb_blob_destroy (cbdt_blob); + return_trace (false); + } + _copy_data_to_cbdt (&cbdt_prime, cbdt, CBDT::min_size); + + for (const BitmapSizeTable& table : + sizeTables.iter ()) + subset_size_table (c, table, (const char *) cbdt, cbdt_length, cblc_prime, &cbdt_prime); + + hb_blob_destroy (cbdt_blob); + + return_trace (CBLC::sink_cbdt (c, &cbdt_prime)); +} + +struct CBDT_accelerator_t : CBDT::accelerator_t { + CBDT_accelerator_t (hb_face_t *face) : CBDT::accelerator_t (face) {} +}; + + +} /* namespace OT */ + +#endif /* HB_OT_COLOR_CBDT_TABLE_HH */ diff --git a/libs/harfbuzz/src/hb-ot-color-colr-table.hh b/libs/harfbuzz/src/hb-ot-color-colr-table.hh new file mode 100644 index 00000000..908bf550 --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-color-colr-table.hh @@ -0,0 +1,1544 @@ +/* + * Copyright © 2018 Ebrahim Byagowi + * Copyright © 2020 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): Calder Kitagawa + */ + +#ifndef HB_OT_COLOR_COLR_TABLE_HH +#define HB_OT_COLOR_COLR_TABLE_HH + +#include "hb-open-type.hh" +#include "hb-ot-layout-common.hh" +#include "hb-ot-var-common.hh" + +/* + * COLR -- Color + * https://docs.microsoft.com/en-us/typography/opentype/spec/colr + */ +#define HB_OT_TAG_COLR HB_TAG('C','O','L','R') + +#ifndef HB_COLRV1_MAX_NESTING_LEVEL +#define HB_COLRV1_MAX_NESTING_LEVEL 100 +#endif + +#ifndef COLRV1_ENABLE_SUBSETTING +#define COLRV1_ENABLE_SUBSETTING 1 +#endif + +namespace OT { + +struct COLR; +struct hb_colrv1_closure_context_t : + hb_dispatch_context_t +{ + template + return_t dispatch (const T &obj) + { + if (unlikely (nesting_level_left == 0)) + return hb_empty_t (); + + if (paint_visited (&obj)) + return hb_empty_t (); + + nesting_level_left--; + obj.closurev1 (this); + nesting_level_left++; + return hb_empty_t (); + } + static return_t default_return_value () { return hb_empty_t (); } + + bool paint_visited (const void *paint) + { + hb_codepoint_t delta = (hb_codepoint_t) ((uintptr_t) paint - (uintptr_t) base); + if (visited_paint.in_error() || visited_paint.has (delta)) + return true; + + visited_paint.add (delta); + return false; + } + + const COLR* get_colr_table () const + { return reinterpret_cast (base); } + + void add_glyph (unsigned glyph_id) + { glyphs->add (glyph_id); } + + void add_layer_indices (unsigned first_layer_index, unsigned num_of_layers) + { layer_indices->add_range (first_layer_index, first_layer_index + num_of_layers - 1); } + + void add_palette_index (unsigned palette_index) + { palette_indices->add (palette_index); } + + public: + const void *base; + hb_set_t visited_paint; + hb_set_t *glyphs; + hb_set_t *layer_indices; + hb_set_t *palette_indices; + unsigned nesting_level_left; + + hb_colrv1_closure_context_t (const void *base_, + hb_set_t *glyphs_, + hb_set_t *layer_indices_, + hb_set_t *palette_indices_, + unsigned nesting_level_left_ = HB_COLRV1_MAX_NESTING_LEVEL) : + base (base_), + glyphs (glyphs_), + layer_indices (layer_indices_), + palette_indices (palette_indices_), + nesting_level_left (nesting_level_left_) + {} +}; + +struct LayerRecord +{ + operator hb_ot_color_layer_t () const { return {glyphId, colorIdx}; } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + public: + HBGlyphID16 glyphId; /* Glyph ID of layer glyph */ + Index colorIdx; /* Index value to use with a + * selected color palette. + * An index value of 0xFFFF + * is a special case indicating + * that the text foreground + * color (defined by a + * higher-level client) should + * be used and shall not be + * treated as actual index + * into CPAL ColorRecord array. */ + public: + DEFINE_SIZE_STATIC (4); +}; + +struct BaseGlyphRecord +{ + int cmp (hb_codepoint_t g) const + { return g < glyphId ? -1 : g > glyphId ? 1 : 0; } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + public: + HBGlyphID16 glyphId; /* Glyph ID of reference glyph */ + HBUINT16 firstLayerIdx; /* Index (from beginning of + * the Layer Records) to the + * layer record. There will be + * numLayers consecutive entries + * for this base glyph. */ + HBUINT16 numLayers; /* Number of color layers + * associated with this glyph */ + public: + DEFINE_SIZE_STATIC (6); +}; + +template +struct Variable +{ + Variable* copy (hb_serialize_context_t *c) const + { + TRACE_SERIALIZE (this); + return_trace (c->embed (this)); + } + + void closurev1 (hb_colrv1_closure_context_t* c) const + { value.closurev1 (c); } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + if (!value.subset (c)) return_trace (false); + return_trace (c->serializer->embed (varIdxBase)); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && value.sanitize (c)); + } + + protected: + T value; + VarIdx varIdxBase; + public: + DEFINE_SIZE_STATIC (4 + T::static_size); +}; + +template +struct NoVariable +{ + NoVariable* copy (hb_serialize_context_t *c) const + { + TRACE_SERIALIZE (this); + return_trace (c->embed (this)); + } + + void closurev1 (hb_colrv1_closure_context_t* c) const + { value.closurev1 (c); } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + return_trace (value.subset (c)); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && value.sanitize (c)); + } + + T value; + public: + DEFINE_SIZE_STATIC (T::static_size); +}; + +// Color structures + +struct ColorStop +{ + void closurev1 (hb_colrv1_closure_context_t* c) const + { c->add_palette_index (paletteIndex); } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (*this); + if (unlikely (!out)) return_trace (false); + return_trace (c->serializer->check_assign (out->paletteIndex, c->plan->colr_palettes->get (paletteIndex), + HB_SERIALIZE_ERROR_INT_OVERFLOW)); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + F2DOT14 stopOffset; + HBUINT16 paletteIndex; + F2DOT14 alpha; + public: + DEFINE_SIZE_STATIC (2 + 2 * F2DOT14::static_size); +}; + +struct Extend : HBUINT8 +{ + enum { + EXTEND_PAD = 0, + EXTEND_REPEAT = 1, + EXTEND_REFLECT = 2, + }; + public: + DEFINE_SIZE_STATIC (1); +}; + +template class Var> +struct ColorLine +{ + void closurev1 (hb_colrv1_closure_context_t* c) const + { + for (const auto &stop : stops.iter ()) + stop.closurev1 (c); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->start_embed (this); + if (unlikely (!out)) return_trace (false); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + + if (!c->serializer->check_assign (out->extend, extend, HB_SERIALIZE_ERROR_INT_OVERFLOW)) return_trace (false); + if (!c->serializer->check_assign (out->stops.len, stops.len, HB_SERIALIZE_ERROR_ARRAY_OVERFLOW)) return_trace (false); + + for (const auto& stop : stops.iter ()) + { + if (!stop.subset (c)) return_trace (false); + } + return_trace (true); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + stops.sanitize (c)); + } + + Extend extend; + Array16Of> stops; + public: + DEFINE_SIZE_ARRAY_SIZED (3, stops); +}; + +// Composition modes + +// Compositing modes are taken from https://www.w3.org/TR/compositing-1/ +// NOTE: a brief audit of major implementations suggests most support most +// or all of the specified modes. +struct CompositeMode : HBUINT8 +{ + enum { + // Porter-Duff modes + // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators + COMPOSITE_CLEAR = 0, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_clear + COMPOSITE_SRC = 1, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_src + COMPOSITE_DEST = 2, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_dst + COMPOSITE_SRC_OVER = 3, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_srcover + COMPOSITE_DEST_OVER = 4, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_dstover + COMPOSITE_SRC_IN = 5, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_srcin + COMPOSITE_DEST_IN = 6, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_dstin + COMPOSITE_SRC_OUT = 7, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_srcout + COMPOSITE_DEST_OUT = 8, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_dstout + COMPOSITE_SRC_ATOP = 9, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_srcatop + COMPOSITE_DEST_ATOP = 10, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_dstatop + COMPOSITE_XOR = 11, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_xor + COMPOSITE_PLUS = 12, // https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_plus + + // Blend modes + // https://www.w3.org/TR/compositing-1/#blending + COMPOSITE_SCREEN = 13, // https://www.w3.org/TR/compositing-1/#blendingscreen + COMPOSITE_OVERLAY = 14, // https://www.w3.org/TR/compositing-1/#blendingoverlay + COMPOSITE_DARKEN = 15, // https://www.w3.org/TR/compositing-1/#blendingdarken + COMPOSITE_LIGHTEN = 16, // https://www.w3.org/TR/compositing-1/#blendinglighten + COMPOSITE_COLOR_DODGE = 17, // https://www.w3.org/TR/compositing-1/#blendingcolordodge + COMPOSITE_COLOR_BURN = 18, // https://www.w3.org/TR/compositing-1/#blendingcolorburn + COMPOSITE_HARD_LIGHT = 19, // https://www.w3.org/TR/compositing-1/#blendinghardlight + COMPOSITE_SOFT_LIGHT = 20, // https://www.w3.org/TR/compositing-1/#blendingsoftlight + COMPOSITE_DIFFERENCE = 21, // https://www.w3.org/TR/compositing-1/#blendingdifference + COMPOSITE_EXCLUSION = 22, // https://www.w3.org/TR/compositing-1/#blendingexclusion + COMPOSITE_MULTIPLY = 23, // https://www.w3.org/TR/compositing-1/#blendingmultiply + + // Modes that, uniquely, do not operate on components + // https://www.w3.org/TR/compositing-1/#blendingnonseparable + COMPOSITE_HSL_HUE = 24, // https://www.w3.org/TR/compositing-1/#blendinghue + COMPOSITE_HSL_SATURATION = 25, // https://www.w3.org/TR/compositing-1/#blendingsaturation + COMPOSITE_HSL_COLOR = 26, // https://www.w3.org/TR/compositing-1/#blendingcolor + COMPOSITE_HSL_LUMINOSITY = 27, // https://www.w3.org/TR/compositing-1/#blendingluminosity + }; + public: + DEFINE_SIZE_STATIC (1); +}; + +struct Affine2x3 +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + F16DOT16 xx; + F16DOT16 yx; + F16DOT16 xy; + F16DOT16 yy; + F16DOT16 dx; + F16DOT16 dy; + public: + DEFINE_SIZE_STATIC (6 * F16DOT16::static_size); +}; + +struct PaintColrLayers +{ + void closurev1 (hb_colrv1_closure_context_t* c) const; + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + return_trace (c->serializer->check_assign (out->firstLayerIndex, c->plan->colrv1_layers->get (firstLayerIndex), + HB_SERIALIZE_ERROR_INT_OVERFLOW)); + + return_trace (true); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + HBUINT8 format; /* format = 1 */ + HBUINT8 numLayers; + HBUINT32 firstLayerIndex; /* index into COLRv1::layerList */ + public: + DEFINE_SIZE_STATIC (6); +}; + +struct PaintSolid +{ + void closurev1 (hb_colrv1_closure_context_t* c) const + { c->add_palette_index (paletteIndex); } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (*this); + if (unlikely (!out)) return_trace (false); + return_trace (c->serializer->check_assign (out->paletteIndex, c->plan->colr_palettes->get (paletteIndex), + HB_SERIALIZE_ERROR_INT_OVERFLOW)); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + HBUINT8 format; /* format = 2(noVar) or 3(Var)*/ + HBUINT16 paletteIndex; + F2DOT14 alpha; + public: + DEFINE_SIZE_STATIC (3 + F2DOT14::static_size); +}; + +template class Var> +struct PaintLinearGradient +{ + void closurev1 (hb_colrv1_closure_context_t* c) const + { (this+colorLine).closurev1 (c); } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + + return_trace (out->colorLine.serialize_subset (c, colorLine, this)); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && colorLine.sanitize (c, this)); + } + + HBUINT8 format; /* format = 4(noVar) or 5 (Var) */ + Offset24To> colorLine; /* Offset (from beginning of PaintLinearGradient + * table) to ColorLine subtable. */ + FWORD x0; + FWORD y0; + FWORD x1; + FWORD y1; + FWORD x2; + FWORD y2; + public: + DEFINE_SIZE_STATIC (4 + 6 * FWORD::static_size); +}; + +template class Var> +struct PaintRadialGradient +{ + void closurev1 (hb_colrv1_closure_context_t* c) const + { (this+colorLine).closurev1 (c); } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + + return_trace (out->colorLine.serialize_subset (c, colorLine, this)); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && colorLine.sanitize (c, this)); + } + + HBUINT8 format; /* format = 6(noVar) or 7 (Var) */ + Offset24To> colorLine; /* Offset (from beginning of PaintRadialGradient + * table) to ColorLine subtable. */ + FWORD x0; + FWORD y0; + UFWORD radius0; + FWORD x1; + FWORD y1; + UFWORD radius1; + public: + DEFINE_SIZE_STATIC (4 + 6 * FWORD::static_size); +}; + +template class Var> +struct PaintSweepGradient +{ + void closurev1 (hb_colrv1_closure_context_t* c) const + { (this+colorLine).closurev1 (c); } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + + return_trace (out->colorLine.serialize_subset (c, colorLine, this)); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && colorLine.sanitize (c, this)); + } + + HBUINT8 format; /* format = 8(noVar) or 9 (Var) */ + Offset24To> colorLine; /* Offset (from beginning of PaintSweepGradient + * table) to ColorLine subtable. */ + FWORD centerX; + FWORD centerY; + F2DOT14 startAngle; + F2DOT14 endAngle; + public: + DEFINE_SIZE_STATIC (4 + 2 * FWORD::static_size + 2 * F2DOT14::static_size); +}; + +struct Paint; + +// Paint a non-COLR glyph, filled as indicated by paint. +struct PaintGlyph +{ + void closurev1 (hb_colrv1_closure_context_t* c) const; + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + + if (! c->serializer->check_assign (out->gid, c->plan->glyph_map->get (gid), + HB_SERIALIZE_ERROR_INT_OVERFLOW)) + return_trace (false); + + return_trace (out->paint.serialize_subset (c, paint, this)); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && paint.sanitize (c, this)); + } + + HBUINT8 format; /* format = 10 */ + Offset24To paint; /* Offset (from beginning of PaintGlyph table) to Paint subtable. */ + HBUINT16 gid; + public: + DEFINE_SIZE_STATIC (6); +}; + +struct PaintColrGlyph +{ + void closurev1 (hb_colrv1_closure_context_t* c) const; + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + + return_trace (c->serializer->check_assign (out->gid, c->plan->glyph_map->get (gid), + HB_SERIALIZE_ERROR_INT_OVERFLOW)); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + HBUINT8 format; /* format = 11 */ + HBUINT16 gid; + public: + DEFINE_SIZE_STATIC (3); +}; + +template class Var> +struct PaintTransform +{ + HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const; + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + if (!out->transform.serialize_copy (c->serializer, transform, this)) return_trace (false); + return_trace (out->src.serialize_subset (c, src, this)); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + src.sanitize (c, this) && + transform.sanitize (c, this)); + } + + HBUINT8 format; /* format = 12(noVar) or 13 (Var) */ + Offset24To src; /* Offset (from beginning of PaintTransform table) to Paint subtable. */ + Offset24To> transform; + public: + DEFINE_SIZE_STATIC (7); +}; + +struct PaintTranslate +{ + HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const; + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + + return_trace (out->src.serialize_subset (c, src, this)); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && src.sanitize (c, this)); + } + + HBUINT8 format; /* format = 14(noVar) or 15 (Var) */ + Offset24To src; /* Offset (from beginning of PaintTranslate table) to Paint subtable. */ + FWORD dx; + FWORD dy; + public: + DEFINE_SIZE_STATIC (4 + 2 * FWORD::static_size); +}; + +struct PaintScale +{ + HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const; + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + + return_trace (out->src.serialize_subset (c, src, this)); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && src.sanitize (c, this)); + } + + HBUINT8 format; /* format = 16 (noVar) or 17(Var) */ + Offset24To src; /* Offset (from beginning of PaintScale table) to Paint subtable. */ + F2DOT14 scaleX; + F2DOT14 scaleY; + public: + DEFINE_SIZE_STATIC (4 + 2 * F2DOT14::static_size); +}; + +struct PaintScaleAroundCenter +{ + HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const; + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + + return_trace (out->src.serialize_subset (c, src, this)); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && src.sanitize (c, this)); + } + + HBUINT8 format; /* format = 18 (noVar) or 19(Var) */ + Offset24To src; /* Offset (from beginning of PaintScaleAroundCenter table) to Paint subtable. */ + F2DOT14 scaleX; + F2DOT14 scaleY; + FWORD centerX; + FWORD centerY; + public: + DEFINE_SIZE_STATIC (4 + 2 * F2DOT14::static_size + 2 * FWORD::static_size); +}; + +struct PaintScaleUniform +{ + HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const; + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + + return_trace (out->src.serialize_subset (c, src, this)); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && src.sanitize (c, this)); + } + + HBUINT8 format; /* format = 20 (noVar) or 21(Var) */ + Offset24To src; /* Offset (from beginning of PaintScaleUniform table) to Paint subtable. */ + F2DOT14 scale; + public: + DEFINE_SIZE_STATIC (4 + F2DOT14::static_size); +}; + +struct PaintScaleUniformAroundCenter +{ + HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const; + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + + return_trace (out->src.serialize_subset (c, src, this)); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && src.sanitize (c, this)); + } + + HBUINT8 format; /* format = 22 (noVar) or 23(Var) */ + Offset24To src; /* Offset (from beginning of PaintScaleUniformAroundCenter table) to Paint subtable. */ + F2DOT14 scale; + FWORD centerX; + FWORD centerY; + public: + DEFINE_SIZE_STATIC (4 + F2DOT14::static_size + 2 * FWORD::static_size); +}; + +struct PaintRotate +{ + HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const; + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + + return_trace (out->src.serialize_subset (c, src, this)); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && src.sanitize (c, this)); + } + + HBUINT8 format; /* format = 24 (noVar) or 25(Var) */ + Offset24To src; /* Offset (from beginning of PaintRotate table) to Paint subtable. */ + F2DOT14 angle; + public: + DEFINE_SIZE_STATIC (4 + F2DOT14::static_size); +}; + +struct PaintRotateAroundCenter +{ + HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const; + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + + return_trace (out->src.serialize_subset (c, src, this)); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && src.sanitize (c, this)); + } + + HBUINT8 format; /* format = 26 (noVar) or 27(Var) */ + Offset24To src; /* Offset (from beginning of PaintRotateAroundCenter table) to Paint subtable. */ + F2DOT14 angle; + FWORD centerX; + FWORD centerY; + public: + DEFINE_SIZE_STATIC (4 + F2DOT14::static_size + 2 * FWORD::static_size); +}; + +struct PaintSkew +{ + HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const; + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + + return_trace (out->src.serialize_subset (c, src, this)); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && src.sanitize (c, this)); + } + + HBUINT8 format; /* format = 28(noVar) or 29 (Var) */ + Offset24To src; /* Offset (from beginning of PaintSkew table) to Paint subtable. */ + F2DOT14 xSkewAngle; + F2DOT14 ySkewAngle; + public: + DEFINE_SIZE_STATIC (4 + 2 * F2DOT14::static_size); +}; + +struct PaintSkewAroundCenter +{ + HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const; + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + + return_trace (out->src.serialize_subset (c, src, this)); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && src.sanitize (c, this)); + } + + HBUINT8 format; /* format = 30(noVar) or 31 (Var) */ + Offset24To src; /* Offset (from beginning of PaintSkewAroundCenter table) to Paint subtable. */ + F2DOT14 xSkewAngle; + F2DOT14 ySkewAngle; + FWORD centerX; + FWORD centerY; + public: + DEFINE_SIZE_STATIC (4 + 2 * F2DOT14::static_size + 2 * FWORD::static_size); +}; + +struct PaintComposite +{ + void closurev1 (hb_colrv1_closure_context_t* c) const; + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + + if (!out->src.serialize_subset (c, src, this)) return_trace (false); + return_trace (out->backdrop.serialize_subset (c, backdrop, this)); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + src.sanitize (c, this) && + backdrop.sanitize (c, this)); + } + + HBUINT8 format; /* format = 32 */ + Offset24To src; /* Offset (from beginning of PaintComposite table) to source Paint subtable. */ + CompositeMode mode; /* If mode is unrecognized use COMPOSITE_CLEAR */ + Offset24To backdrop; /* Offset (from beginning of PaintComposite table) to backdrop Paint subtable. */ + public: + DEFINE_SIZE_STATIC (8); +}; + +struct ClipBoxFormat1 +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + public: + HBUINT8 format; /* format = 1(noVar) or 2(Var)*/ + FWORD xMin; + FWORD yMin; + FWORD xMax; + FWORD yMax; + public: + DEFINE_SIZE_STATIC (1 + 4 * FWORD::static_size); +}; + +struct ClipBoxFormat2 : Variable {}; + +struct ClipBox +{ + ClipBox* copy (hb_serialize_context_t *c) const + { + TRACE_SERIALIZE (this); + switch (u.format) { + case 1: return_trace (reinterpret_cast (c->embed (u.format1))); + case 2: return_trace (reinterpret_cast (c->embed (u.format2))); + default:return_trace (nullptr); + } + } + + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const + { + TRACE_DISPATCH (this, u.format); + if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ()); + switch (u.format) { + case 1: return_trace (c->dispatch (u.format1, std::forward (ds)...)); + case 2: return_trace (c->dispatch (u.format2, std::forward (ds)...)); + default:return_trace (c->default_return_value ()); + } + } + + protected: + union { + HBUINT8 format; /* Format identifier */ + ClipBoxFormat1 format1; + ClipBoxFormat2 format2; + } u; +}; + +struct ClipRecord +{ + ClipRecord* copy (hb_serialize_context_t *c, const void *base) const + { + TRACE_SERIALIZE (this); + auto *out = c->embed (this); + if (unlikely (!out)) return_trace (nullptr); + if (!out->clipBox.serialize_copy (c, clipBox, base)) return_trace (nullptr); + return_trace (out); + } + + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && clipBox.sanitize (c, base)); + } + + public: + HBUINT16 startGlyphID; // first gid clip applies to + HBUINT16 endGlyphID; // last gid clip applies to, inclusive + Offset24To clipBox; // Box or VarBox + public: + DEFINE_SIZE_STATIC (7); +}; + +struct ClipList +{ + unsigned serialize_clip_records (hb_serialize_context_t *c, + const hb_set_t& gids, + const hb_map_t& gid_offset_map) const + { + TRACE_SERIALIZE (this); + if (gids.is_empty () || + gid_offset_map.get_population () != gids.get_population ()) + return_trace (0); + + unsigned count = 0; + + hb_codepoint_t start_gid= gids.get_min (); + hb_codepoint_t prev_gid = start_gid; + + unsigned offset = gid_offset_map.get (start_gid); + unsigned prev_offset = offset; + for (const hb_codepoint_t _ : gids.iter ()) + { + if (_ == start_gid) continue; + + offset = gid_offset_map.get (_); + if (_ == prev_gid + 1 && offset == prev_offset) + { + prev_gid = _; + continue; + } + + ClipRecord record; + record.startGlyphID = start_gid; + record.endGlyphID = prev_gid; + record.clipBox = prev_offset; + + if (!c->copy (record, this)) return_trace (0); + count++; + + start_gid = _; + prev_gid = _; + prev_offset = offset; + } + + //last one + { + ClipRecord record; + record.startGlyphID = start_gid; + record.endGlyphID = prev_gid; + record.clipBox = prev_offset; + if (!c->copy (record, this)) return_trace (0); + count++; + } + return_trace (count); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + if (!c->serializer->check_assign (out->format, format, HB_SERIALIZE_ERROR_INT_OVERFLOW)) return_trace (false); + + const hb_set_t& glyphset = *c->plan->_glyphset_colred; + const hb_map_t &glyph_map = *c->plan->glyph_map; + + hb_map_t new_gid_offset_map; + hb_set_t new_gids; + for (const ClipRecord& record : clips.iter ()) + { + unsigned start_gid = record.startGlyphID; + unsigned end_gid = record.endGlyphID; + for (unsigned gid = start_gid; gid <= end_gid; gid++) + { + if (!glyphset.has (gid) || !glyph_map.has (gid)) continue; + unsigned new_gid = glyph_map.get (gid); + new_gid_offset_map.set (new_gid, record.clipBox); + new_gids.add (new_gid); + } + } + + unsigned count = serialize_clip_records (c->serializer, new_gids, new_gid_offset_map); + if (!count) return_trace (false); + return_trace (c->serializer->check_assign (out->clips.len, count, HB_SERIALIZE_ERROR_INT_OVERFLOW)); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && clips.sanitize (c, this)); + } + + HBUINT8 format; // Set to 1. + Array32Of clips; // Clip records, sorted by startGlyphID + public: + DEFINE_SIZE_ARRAY_SIZED (5, clips); +}; + +struct Paint +{ + + template + bool sanitize (hb_sanitize_context_t *c, Ts&&... ds) const + { + TRACE_SANITIZE (this); + + if (unlikely (!c->check_start_recursion (HB_COLRV1_MAX_NESTING_LEVEL))) + return_trace (c->no_dispatch_return_value ()); + + return_trace (c->end_recursion (this->dispatch (c, std::forward (ds)...))); + } + + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const + { + TRACE_DISPATCH (this, u.format); + if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ()); + switch (u.format) { + case 1: return_trace (c->dispatch (u.paintformat1, std::forward (ds)...)); + case 2: return_trace (c->dispatch (u.paintformat2, std::forward (ds)...)); + case 3: return_trace (c->dispatch (u.paintformat3, std::forward (ds)...)); + case 4: return_trace (c->dispatch (u.paintformat4, std::forward (ds)...)); + case 5: return_trace (c->dispatch (u.paintformat5, std::forward (ds)...)); + case 6: return_trace (c->dispatch (u.paintformat6, std::forward (ds)...)); + case 7: return_trace (c->dispatch (u.paintformat7, std::forward (ds)...)); + case 8: return_trace (c->dispatch (u.paintformat8, std::forward (ds)...)); + case 9: return_trace (c->dispatch (u.paintformat9, std::forward (ds)...)); + case 10: return_trace (c->dispatch (u.paintformat10, std::forward (ds)...)); + case 11: return_trace (c->dispatch (u.paintformat11, std::forward (ds)...)); + case 12: return_trace (c->dispatch (u.paintformat12, std::forward (ds)...)); + case 13: return_trace (c->dispatch (u.paintformat13, std::forward (ds)...)); + case 14: return_trace (c->dispatch (u.paintformat14, std::forward (ds)...)); + case 15: return_trace (c->dispatch (u.paintformat15, std::forward (ds)...)); + case 16: return_trace (c->dispatch (u.paintformat16, std::forward (ds)...)); + case 17: return_trace (c->dispatch (u.paintformat17, std::forward (ds)...)); + case 18: return_trace (c->dispatch (u.paintformat18, std::forward (ds)...)); + case 19: return_trace (c->dispatch (u.paintformat19, std::forward (ds)...)); + case 20: return_trace (c->dispatch (u.paintformat20, std::forward (ds)...)); + case 21: return_trace (c->dispatch (u.paintformat21, std::forward (ds)...)); + case 22: return_trace (c->dispatch (u.paintformat22, std::forward (ds)...)); + case 23: return_trace (c->dispatch (u.paintformat23, std::forward (ds)...)); + case 24: return_trace (c->dispatch (u.paintformat24, std::forward (ds)...)); + case 25: return_trace (c->dispatch (u.paintformat25, std::forward (ds)...)); + case 26: return_trace (c->dispatch (u.paintformat26, std::forward (ds)...)); + case 27: return_trace (c->dispatch (u.paintformat27, std::forward (ds)...)); + case 28: return_trace (c->dispatch (u.paintformat28, std::forward (ds)...)); + case 29: return_trace (c->dispatch (u.paintformat29, std::forward (ds)...)); + case 30: return_trace (c->dispatch (u.paintformat30, std::forward (ds)...)); + case 31: return_trace (c->dispatch (u.paintformat31, std::forward (ds)...)); + case 32: return_trace (c->dispatch (u.paintformat32, std::forward (ds)...)); + default:return_trace (c->default_return_value ()); + } + } + + protected: + union { + HBUINT8 format; + PaintColrLayers paintformat1; + PaintSolid paintformat2; + Variable paintformat3; + PaintLinearGradient paintformat4; + Variable> paintformat5; + PaintRadialGradient paintformat6; + Variable> paintformat7; + PaintSweepGradient paintformat8; + Variable> paintformat9; + PaintGlyph paintformat10; + PaintColrGlyph paintformat11; + PaintTransform paintformat12; + PaintTransform paintformat13; + PaintTranslate paintformat14; + Variable paintformat15; + PaintScale paintformat16; + Variable paintformat17; + PaintScaleAroundCenter paintformat18; + Variable paintformat19; + PaintScaleUniform paintformat20; + Variable paintformat21; + PaintScaleUniformAroundCenter paintformat22; + Variable paintformat23; + PaintRotate paintformat24; + Variable paintformat25; + PaintRotateAroundCenter paintformat26; + Variable paintformat27; + PaintSkew paintformat28; + Variable paintformat29; + PaintSkewAroundCenter paintformat30; + Variable paintformat31; + PaintComposite paintformat32; + } u; + public: + DEFINE_SIZE_MIN (2); +}; + +struct BaseGlyphPaintRecord +{ + int cmp (hb_codepoint_t g) const + { return g < glyphId ? -1 : g > glyphId ? 1 : 0; } + + bool serialize (hb_serialize_context_t *s, const hb_map_t* glyph_map, + const void* src_base, hb_subset_context_t *c) const + { + TRACE_SERIALIZE (this); + auto *out = s->embed (this); + if (unlikely (!out)) return_trace (false); + if (!s->check_assign (out->glyphId, glyph_map->get (glyphId), + HB_SERIALIZE_ERROR_INT_OVERFLOW)) + return_trace (false); + + return_trace (out->paint.serialize_subset (c, paint, src_base)); + } + + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && paint.sanitize (c, base))); + } + + public: + HBGlyphID16 glyphId; /* Glyph ID of reference glyph */ + Offset32To paint; /* Offset (from beginning of BaseGlyphPaintRecord array) to Paint, + * Typically PaintColrLayers */ + public: + DEFINE_SIZE_STATIC (6); +}; + +struct BaseGlyphList : SortedArray32Of +{ + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->start_embed (this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + const hb_set_t* glyphset = c->plan->_glyphset_colred; + + for (const auto& _ : as_array ()) + { + unsigned gid = _.glyphId; + if (!glyphset->has (gid)) continue; + + if (_.serialize (c->serializer, c->plan->glyph_map, this, c)) out->len++; + else return_trace (false); + } + + return_trace (out->len != 0); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (SortedArray32Of::sanitize (c, this)); + } +}; + +struct LayerList : Array32OfOffset32To +{ + const Paint& get_paint (unsigned i) const + { return this+(*this)[i]; } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->start_embed (this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + + for (const auto& _ : + hb_enumerate (*this) + | hb_filter (c->plan->colrv1_layers, hb_first)) + + { + auto *o = out->serialize_append (c->serializer); + if (unlikely (!o) || !o->serialize_subset (c, _.second, this)) + return_trace (false); + } + return_trace (true); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (Array32OfOffset32To::sanitize (c, this)); + } +}; + +struct COLR +{ + static constexpr hb_tag_t tableTag = HB_OT_TAG_COLR; + + bool has_data () const { return numBaseGlyphs; } + + unsigned int get_glyph_layers (hb_codepoint_t glyph, + unsigned int start_offset, + unsigned int *count, /* IN/OUT. May be NULL. */ + hb_ot_color_layer_t *layers /* OUT. May be NULL. */) const + { + const BaseGlyphRecord &record = (this+baseGlyphsZ).bsearch (numBaseGlyphs, glyph); + + hb_array_t all_layers = (this+layersZ).as_array (numLayers); + hb_array_t glyph_layers = all_layers.sub_array (record.firstLayerIdx, + record.numLayers); + if (count) + { + + glyph_layers.sub_array (start_offset, count) + | hb_sink (hb_array (layers, *count)) + ; + } + return glyph_layers.length; + } + + struct accelerator_t + { + accelerator_t (hb_face_t *face) + { colr = hb_sanitize_context_t ().reference_table (face); } + ~accelerator_t () { this->colr.destroy (); } + + bool is_valid () { return colr.get_blob ()->length; } + + void closure_glyphs (hb_codepoint_t glyph, + hb_set_t *related_ids /* OUT */) const + { colr->closure_glyphs (glyph, related_ids); } + + void closure_V0palette_indices (const hb_set_t *glyphs, + hb_set_t *palettes /* OUT */) const + { colr->closure_V0palette_indices (glyphs, palettes); } + + void closure_forV1 (hb_set_t *glyphset, + hb_set_t *layer_indices, + hb_set_t *palette_indices) const + { colr->closure_forV1 (glyphset, layer_indices, palette_indices); } + + private: + hb_blob_ptr_t colr; + }; + + void closure_glyphs (hb_codepoint_t glyph, + hb_set_t *related_ids /* OUT */) const + { + const BaseGlyphRecord *record = get_base_glyph_record (glyph); + if (!record) return; + + auto glyph_layers = (this+layersZ).as_array (numLayers).sub_array (record->firstLayerIdx, + record->numLayers); + if (!glyph_layers.length) return; + related_ids->add_array (&glyph_layers[0].glyphId, glyph_layers.length, LayerRecord::min_size); + } + + void closure_V0palette_indices (const hb_set_t *glyphs, + hb_set_t *palettes /* OUT */) const + { + if (!numBaseGlyphs || !numLayers) return; + hb_array_t baseGlyphs = (this+baseGlyphsZ).as_array (numBaseGlyphs); + hb_array_t all_layers = (this+layersZ).as_array (numLayers); + + for (const BaseGlyphRecord record : baseGlyphs) + { + if (!glyphs->has (record.glyphId)) continue; + hb_array_t glyph_layers = all_layers.sub_array (record.firstLayerIdx, + record.numLayers); + for (const LayerRecord layer : glyph_layers) + palettes->add (layer.colorIdx); + } + } + + void closure_forV1 (hb_set_t *glyphset, + hb_set_t *layer_indices, + hb_set_t *palette_indices) const + { + if (version != 1) return; + hb_set_t visited_glyphs; + + hb_colrv1_closure_context_t c (this, &visited_glyphs, layer_indices, palette_indices); + const BaseGlyphList &baseglyph_paintrecords = this+baseGlyphList; + + for (const BaseGlyphPaintRecord &baseglyph_paintrecord: baseglyph_paintrecords.iter ()) + { + unsigned gid = baseglyph_paintrecord.glyphId; + if (!glyphset->has (gid)) continue; + + const Paint &paint = &baseglyph_paintrecords+baseglyph_paintrecord.paint; + paint.dispatch (&c); + } + hb_set_union (glyphset, &visited_glyphs); + } + + const LayerList& get_layerList () const + { return (this+layerList); } + + const BaseGlyphList& get_baseglyphList () const + { return (this+baseGlyphList); } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + (this+baseGlyphsZ).sanitize (c, numBaseGlyphs) && + (this+layersZ).sanitize (c, numLayers) && + (version == 0 || + (COLRV1_ENABLE_SUBSETTING && version == 1 && + baseGlyphList.sanitize (c, this) && + layerList.sanitize (c, this) && + clipList.sanitize (c, this) && + varIdxMap.sanitize (c, this) && + varStore.sanitize (c, this)))); + } + + template + bool serialize_V0 (hb_serialize_context_t *c, + unsigned version, + BaseIterator base_it, + LayerIterator layer_it) + { + TRACE_SERIALIZE (this); + if (unlikely (base_it.len () != layer_it.len ())) + return_trace (false); + + this->version = version; + numLayers = 0; + numBaseGlyphs = base_it.len (); + if (numBaseGlyphs == 0) + { + baseGlyphsZ = 0; + layersZ = 0; + return_trace (true); + } + + c->push (); + for (const hb_item_type _ : + base_it.iter ()) + { + auto* record = c->embed (_); + if (unlikely (!record)) return_trace (false); + record->firstLayerIdx = numLayers; + numLayers += record->numLayers; + } + c->add_link (baseGlyphsZ, c->pop_pack ()); + + c->push (); + for (const hb_item_type& _ : + layer_it.iter ()) + _.as_array ().copy (c); + + c->add_link (layersZ, c->pop_pack ()); + + return_trace (true); + } + + const BaseGlyphRecord* get_base_glyph_record (hb_codepoint_t gid) const + { + const BaseGlyphRecord* record = &(this+baseGlyphsZ).bsearch (numBaseGlyphs, (unsigned int) gid); + if (record == &Null (BaseGlyphRecord) || + (record && (hb_codepoint_t) record->glyphId != gid)) + record = nullptr; + return record; + } + + const BaseGlyphPaintRecord* get_base_glyph_paintrecord (hb_codepoint_t gid) const + { + const BaseGlyphPaintRecord* record = &(this+baseGlyphList).bsearch ((unsigned) gid); + if ((record && (hb_codepoint_t) record->glyphId != gid)) + record = nullptr; + return record; + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + + const hb_map_t &reverse_glyph_map = *c->plan->reverse_glyph_map; + const hb_set_t& glyphset = *c->plan->_glyphset_colred; + + auto base_it = + + hb_range (c->plan->num_output_glyphs ()) + | hb_filter ([&](hb_codepoint_t new_gid) + { + hb_codepoint_t old_gid = reverse_glyph_map.get (new_gid); + if (glyphset.has (old_gid)) return true; + return false; + }) + | hb_map_retains_sorting ([&](hb_codepoint_t new_gid) + { + hb_codepoint_t old_gid = reverse_glyph_map.get (new_gid); + + const BaseGlyphRecord* old_record = get_base_glyph_record (old_gid); + if (unlikely (!old_record)) + return hb_pair_t (false, Null (BaseGlyphRecord)); + BaseGlyphRecord new_record = {}; + new_record.glyphId = new_gid; + new_record.numLayers = old_record->numLayers; + return hb_pair_t (true, new_record); + }) + | hb_filter (hb_first) + | hb_map_retains_sorting (hb_second) + ; + + auto layer_it = + + hb_range (c->plan->num_output_glyphs ()) + | hb_map (reverse_glyph_map) + | hb_filter (glyphset) + | hb_map_retains_sorting ([&](hb_codepoint_t old_gid) + { + const BaseGlyphRecord* old_record = get_base_glyph_record (old_gid); + hb_vector_t out_layers; + + if (unlikely (!old_record || + old_record->firstLayerIdx >= numLayers || + old_record->firstLayerIdx + old_record->numLayers > numLayers)) + return hb_pair_t> (false, out_layers); + + auto layers = (this+layersZ).as_array (numLayers).sub_array (old_record->firstLayerIdx, + old_record->numLayers); + out_layers.resize (layers.length); + for (unsigned int i = 0; i < layers.length; i++) { + out_layers[i] = layers[i]; + hb_codepoint_t new_gid = 0; + if (unlikely (!c->plan->new_gid_for_old_gid (out_layers[i].glyphId, &new_gid))) + return hb_pair_t> (false, out_layers); + out_layers[i].glyphId = new_gid; + out_layers[i].colorIdx = c->plan->colr_palettes->get (layers[i].colorIdx); + } + + return hb_pair_t> (true, out_layers); + }) + | hb_filter (hb_first) + | hb_map_retains_sorting (hb_second) + ; + + if (version == 0 && (!base_it || !layer_it)) + return_trace (false); + + COLR *colr_prime = c->serializer->start_embed (); + if (unlikely (!c->serializer->extend_min (colr_prime))) return_trace (false); + + if (version == 0) + return_trace (colr_prime->serialize_V0 (c->serializer, version, base_it, layer_it)); + + auto snap = c->serializer->snapshot (); + if (!c->serializer->allocate_size (5 * HBUINT32::static_size)) return_trace (false); + if (!colr_prime->baseGlyphList.serialize_subset (c, baseGlyphList, this)) + { + if (c->serializer->in_error ()) return_trace (false); + //no more COLRv1 glyphs: downgrade to version 0 + c->serializer->revert (snap); + return_trace (colr_prime->serialize_V0 (c->serializer, 0, base_it, layer_it)); + } + + if (!colr_prime->serialize_V0 (c->serializer, version, base_it, layer_it)) return_trace (false); + + colr_prime->layerList.serialize_subset (c, layerList, this); + colr_prime->clipList.serialize_subset (c, clipList, this); + colr_prime->varIdxMap.serialize_copy (c->serializer, varIdxMap, this); + //TODO: subset varStore once it's implemented in fonttools + return_trace (true); + } + + protected: + HBUINT16 version; /* Table version number (starts at 0). */ + HBUINT16 numBaseGlyphs; /* Number of Base Glyph Records. */ + NNOffset32To> + baseGlyphsZ; /* Offset to Base Glyph records. */ + NNOffset32To> + layersZ; /* Offset to Layer Records. */ + HBUINT16 numLayers; /* Number of Layer Records. */ + // Version-1 additions + Offset32To baseGlyphList; + Offset32To layerList; + Offset32To clipList; // Offset to ClipList table (may be NULL) + Offset32To varIdxMap; // Offset to DeltaSetIndexMap table (may be NULL) + Offset32To varStore; + public: + DEFINE_SIZE_MIN (14); +}; + +struct COLR_accelerator_t : COLR::accelerator_t { + COLR_accelerator_t (hb_face_t *face) : COLR::accelerator_t (face) {} +}; + +} /* namespace OT */ + + +#endif /* HB_OT_COLOR_COLR_TABLE_HH */ diff --git a/libs/harfbuzz/src/hb-ot-color-colrv1-closure.hh b/libs/harfbuzz/src/hb-ot-color-colrv1-closure.hh new file mode 100644 index 00000000..fbaf2ec2 --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-color-colrv1-closure.hh @@ -0,0 +1,108 @@ +/* + * Copyright © 2018 Ebrahim Byagowi + * Copyright © 2020 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. + * + */ + +#ifndef HB_OT_COLR_COLRV1_CLOSURE_HH +#define HB_OT_COLR_COLRV1_CLOSURE_HH + +#include "hb-open-type.hh" +#include "hb-ot-layout-common.hh" +#include "hb-ot-color-colr-table.hh" + +/* + * COLR -- Color + * https://docs.microsoft.com/en-us/typography/opentype/spec/colr + */ +namespace OT { + +HB_INTERNAL void PaintColrLayers::closurev1 (hb_colrv1_closure_context_t* c) const +{ + c->add_layer_indices (firstLayerIndex, numLayers); + const LayerList &paint_offset_lists = c->get_colr_table ()->get_layerList (); + for (unsigned i = firstLayerIndex; i < firstLayerIndex + numLayers; i++) + { + const Paint &paint = std::addressof (paint_offset_lists) + paint_offset_lists[i]; + paint.dispatch (c); + } +} + +HB_INTERNAL void PaintGlyph::closurev1 (hb_colrv1_closure_context_t* c) const +{ + c->add_glyph (gid); + (this+paint).dispatch (c); +} + +HB_INTERNAL void PaintColrGlyph::closurev1 (hb_colrv1_closure_context_t* c) const +{ + const COLR *colr_table = c->get_colr_table (); + const BaseGlyphPaintRecord* baseglyph_paintrecord = colr_table->get_base_glyph_paintrecord (gid); + if (!baseglyph_paintrecord) return; + c->add_glyph (gid); + + const BaseGlyphList &baseglyph_list = colr_table->get_baseglyphList (); + (&baseglyph_list+baseglyph_paintrecord->paint).dispatch (c); +} + +template class Var> +HB_INTERNAL void PaintTransform::closurev1 (hb_colrv1_closure_context_t* c) const +{ (this+src).dispatch (c); } + +HB_INTERNAL void PaintTranslate::closurev1 (hb_colrv1_closure_context_t* c) const +{ (this+src).dispatch (c); } + +HB_INTERNAL void PaintScale::closurev1 (hb_colrv1_closure_context_t* c) const +{ (this+src).dispatch (c); } + +HB_INTERNAL void PaintScaleAroundCenter::closurev1 (hb_colrv1_closure_context_t* c) const +{ (this+src).dispatch (c); } + +HB_INTERNAL void PaintScaleUniform::closurev1 (hb_colrv1_closure_context_t* c) const +{ (this+src).dispatch (c); } + +HB_INTERNAL void PaintScaleUniformAroundCenter::closurev1 (hb_colrv1_closure_context_t* c) const +{ (this+src).dispatch (c); } + +HB_INTERNAL void PaintRotate::closurev1 (hb_colrv1_closure_context_t* c) const +{ (this+src).dispatch (c); } + +HB_INTERNAL void PaintRotateAroundCenter::closurev1 (hb_colrv1_closure_context_t* c) const +{ (this+src).dispatch (c); } + +HB_INTERNAL void PaintSkew::closurev1 (hb_colrv1_closure_context_t* c) const +{ (this+src).dispatch (c); } + +HB_INTERNAL void PaintSkewAroundCenter::closurev1 (hb_colrv1_closure_context_t* c) const +{ (this+src).dispatch (c); } + +HB_INTERNAL void PaintComposite::closurev1 (hb_colrv1_closure_context_t* c) const +{ + (this+src).dispatch (c); + (this+backdrop).dispatch (c); +} + +} /* namespace OT */ + + +#endif /* HB_OT_COLR_COLRV1_CLOSURE_HH */ diff --git a/libs/harfbuzz/src/hb-ot-color-cpal-table.hh b/libs/harfbuzz/src/hb-ot-color-cpal-table.hh new file mode 100644 index 00000000..bcab77f7 --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-color-cpal-table.hh @@ -0,0 +1,322 @@ +/* + * Copyright © 2016 Google, Inc. + * Copyright © 2018 Ebrahim Byagowi + * + * 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): Sascha Brawer + */ + +#ifndef HB_OT_COLOR_CPAL_TABLE_HH +#define HB_OT_COLOR_CPAL_TABLE_HH + +#include "hb-open-type.hh" +#include "hb-ot-color.h" +#include "hb-ot-name.h" + + +/* + * CPAL -- Color Palette + * https://docs.microsoft.com/en-us/typography/opentype/spec/cpal + */ +#define HB_OT_TAG_CPAL HB_TAG('C','P','A','L') + +namespace OT { + + +struct CPALV1Tail +{ + friend struct CPAL; + + private: + hb_ot_color_palette_flags_t get_palette_flags (const void *base, + unsigned int palette_index, + unsigned int palette_count) const + { + if (!paletteFlagsZ) return HB_OT_COLOR_PALETTE_FLAG_DEFAULT; + return (hb_ot_color_palette_flags_t) (uint32_t) + (base+paletteFlagsZ).as_array (palette_count)[palette_index]; + } + + hb_ot_name_id_t get_palette_name_id (const void *base, + unsigned int palette_index, + unsigned int palette_count) const + { + if (!paletteLabelsZ) return HB_OT_NAME_ID_INVALID; + return (base+paletteLabelsZ).as_array (palette_count)[palette_index]; + } + + hb_ot_name_id_t get_color_name_id (const void *base, + unsigned int color_index, + unsigned int color_count) const + { + if (!colorLabelsZ) return HB_OT_NAME_ID_INVALID; + return (base+colorLabelsZ).as_array (color_count)[color_index]; + } + + public: + bool serialize (hb_serialize_context_t *c, + unsigned palette_count, + unsigned color_count, + const void *base, + const hb_map_t *color_index_map) const + { + TRACE_SERIALIZE (this); + auto *out = c->allocate_size (static_size); + if (unlikely (!out)) return_trace (false); + + out->paletteFlagsZ = 0; + if (paletteFlagsZ) + out->paletteFlagsZ.serialize_copy (c, paletteFlagsZ, base, 0, hb_serialize_context_t::Head, palette_count); + + out->paletteLabelsZ = 0; + if (paletteLabelsZ) + out->paletteLabelsZ.serialize_copy (c, paletteLabelsZ, base, 0, hb_serialize_context_t::Head, palette_count); + + const hb_array_t colorLabels = (base+colorLabelsZ).as_array (color_count); + if (colorLabelsZ) + { + c->push (); + for (const auto _ : colorLabels) + { + const hb_codepoint_t *v; + if (!color_index_map->has (_, &v)) continue; + NameID new_color_idx; + new_color_idx = *v; + if (!c->copy (new_color_idx)) + { + c->pop_discard (); + return_trace (false); + } + } + c->add_link (out->colorLabelsZ, c->pop_pack ()); + } + return_trace (true); + } + + bool sanitize (hb_sanitize_context_t *c, + const void *base, + unsigned int palette_count, + unsigned int color_count) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + (!paletteFlagsZ || (base+paletteFlagsZ).sanitize (c, palette_count)) && + (!paletteLabelsZ || (base+paletteLabelsZ).sanitize (c, palette_count)) && + (!colorLabelsZ || (base+colorLabelsZ).sanitize (c, color_count))); + } + + protected: + // TODO(garretrieger): these offsets can hold nulls so we should not be using non-null offsets + // here. Currently they are needed since UnsizedArrayOf doesn't define null_size + NNOffset32To> + paletteFlagsZ; /* Offset from the beginning of CPAL table to + * the Palette Type Array. Set to 0 if no array + * is provided. */ + NNOffset32To> + paletteLabelsZ; /* Offset from the beginning of CPAL table to + * the palette labels array. Set to 0 if no + * array is provided. */ + NNOffset32To> + colorLabelsZ; /* Offset from the beginning of CPAL table to + * the color labels array. Set to 0 + * if no array is provided. */ + public: + DEFINE_SIZE_STATIC (12); +}; + +typedef HBUINT32 BGRAColor; + +struct CPAL +{ + static constexpr hb_tag_t tableTag = HB_OT_TAG_CPAL; + + bool has_data () const { return numPalettes; } + + unsigned int get_size () const + { return min_size + numPalettes * sizeof (colorRecordIndicesZ[0]); } + + unsigned int get_palette_count () const { return numPalettes; } + unsigned int get_color_count () const { return numColors; } + + hb_ot_color_palette_flags_t get_palette_flags (unsigned int palette_index) const + { return v1 ().get_palette_flags (this, palette_index, numPalettes); } + + hb_ot_name_id_t get_palette_name_id (unsigned int palette_index) const + { return v1 ().get_palette_name_id (this, palette_index, numPalettes); } + + hb_ot_name_id_t get_color_name_id (unsigned int color_index) const + { return v1 ().get_color_name_id (this, color_index, numColors); } + + unsigned int get_palette_colors (unsigned int palette_index, + unsigned int start_offset, + unsigned int *color_count, /* IN/OUT. May be NULL. */ + hb_color_t *colors /* OUT. May be NULL. */) const + { + if (unlikely (palette_index >= numPalettes)) + { + if (color_count) *color_count = 0; + return 0; + } + unsigned int start_index = colorRecordIndicesZ[palette_index]; + hb_array_t all_colors ((this+colorRecordsZ).arrayZ, numColorRecords); + hb_array_t palette_colors = all_colors.sub_array (start_index, + numColors); + if (color_count) + { + + palette_colors.sub_array (start_offset, color_count) + | hb_sink (hb_array (colors, *color_count)) + ; + } + return numColors; + } + + private: + const CPALV1Tail& v1 () const + { + if (version == 0) return Null (CPALV1Tail); + return StructAfter (*this); + } + + public: + bool serialize (hb_serialize_context_t *c, + const hb_array_t &color_record_indices, + const hb_array_t &color_records, + const hb_vector_t& first_color_index_for_layer, + const hb_map_t& first_color_to_layer_index, + const hb_set_t &retained_color_indices) const + { + TRACE_SERIALIZE (this); + + // TODO(grieger): limit total final size. + + for (const auto idx : color_record_indices) + { + hb_codepoint_t layer_index = first_color_to_layer_index[idx]; + + HBUINT16 new_idx; + new_idx = layer_index * retained_color_indices.get_population (); + if (!c->copy (new_idx)) return_trace (false); + } + + c->push (); + for (unsigned first_color_index : first_color_index_for_layer) + { + for (hb_codepoint_t color_index : retained_color_indices) + { + if (!c->copy (color_records[first_color_index + color_index])) + { + c->pop_discard (); + return_trace (false); + } + } + } + + c->add_link (colorRecordsZ, c->pop_pack ()); + return_trace (true); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + if (!numPalettes) return_trace (false); + + const hb_map_t *color_index_map = c->plan->colr_palettes; + if (color_index_map->is_empty ()) return_trace (false); + + hb_set_t retained_color_indices; + for (const auto _ : color_index_map->keys ()) + { + if (_ == 0xFFFF) continue; + retained_color_indices.add (_); + } + if (retained_color_indices.is_empty ()) return_trace (false); + + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + + + out->version = version; + out->numColors = retained_color_indices.get_population (); + out->numPalettes = numPalettes; + + hb_vector_t first_color_index_for_layer; + hb_map_t first_color_to_layer_index; + + const hb_array_t colorRecordIndices = colorRecordIndicesZ.as_array (numPalettes); + for (const auto first_color_record_idx : colorRecordIndices) + { + if (first_color_to_layer_index.has (first_color_record_idx)) continue; + + first_color_index_for_layer.push (first_color_record_idx); + first_color_to_layer_index.set (first_color_record_idx, + first_color_index_for_layer.length - 1); + } + + out->numColorRecords = first_color_index_for_layer.length + * retained_color_indices.get_population (); + + const hb_array_t color_records = (this+colorRecordsZ).as_array (numColorRecords); + if (!out->serialize (c->serializer, + colorRecordIndices, + color_records, + first_color_index_for_layer, + first_color_to_layer_index, + retained_color_indices)) + return_trace (false); + + if (version == 1) + return_trace (v1 ().serialize (c->serializer, numPalettes, numColors, this, color_index_map)); + + return_trace (true); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + (this+colorRecordsZ).sanitize (c, numColorRecords) && + colorRecordIndicesZ.sanitize (c, numPalettes) && + (version == 0 || v1 ().sanitize (c, this, numPalettes, numColors))); + } + + protected: + HBUINT16 version; /* Table version number */ + /* Version 0 */ + HBUINT16 numColors; /* Number of colors in each palette. */ + HBUINT16 numPalettes; /* Number of palettes in the table. */ + HBUINT16 numColorRecords; /* Total number of color records, combined for + * all palettes. */ + NNOffset32To> + colorRecordsZ; /* Offset from the beginning of CPAL table to + * the first ColorRecord. */ + UnsizedArrayOf + colorRecordIndicesZ; /* Index of each palette’s first color record in + * the combined color record array. */ +/*CPALV1Tail v1;*/ + public: + DEFINE_SIZE_ARRAY (12, colorRecordIndicesZ); +}; + +} /* namespace OT */ + + +#endif /* HB_OT_COLOR_CPAL_TABLE_HH */ diff --git a/libs/harfbuzz/src/hb-ot-color-sbix-table.hh b/libs/harfbuzz/src/hb-ot-color-sbix-table.hh new file mode 100644 index 00000000..d0e2235f --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-color-sbix-table.hh @@ -0,0 +1,423 @@ +/* + * Copyright © 2018 Ebrahim Byagowi + * Copyright © 2020 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): Calder Kitagawa + */ + +#ifndef HB_OT_COLOR_SBIX_TABLE_HH +#define HB_OT_COLOR_SBIX_TABLE_HH + +#include "hb-open-type.hh" +#include "hb-ot-layout-common.hh" + +/* + * sbix -- Standard Bitmap Graphics + * https://docs.microsoft.com/en-us/typography/opentype/spec/sbix + * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6sbix.html + */ +#define HB_OT_TAG_sbix HB_TAG('s','b','i','x') + + +namespace OT { + + +struct SBIXGlyph +{ + SBIXGlyph* copy (hb_serialize_context_t *c, unsigned int data_length) const + { + TRACE_SERIALIZE (this); + SBIXGlyph* new_glyph = c->start_embed (); + if (unlikely (!new_glyph)) return_trace (nullptr); + if (unlikely (!c->extend_min (new_glyph))) return_trace (nullptr); + + new_glyph->xOffset = xOffset; + new_glyph->yOffset = yOffset; + new_glyph->graphicType = graphicType; + data.copy (c, data_length); + return_trace (new_glyph); + } + + HBINT16 xOffset; /* The horizontal (x-axis) offset from the left + * edge of the graphic to the glyph’s origin. + * That is, the x-coordinate of the point on the + * baseline at the left edge of the glyph. */ + HBINT16 yOffset; /* The vertical (y-axis) offset from the bottom + * edge of the graphic to the glyph’s origin. + * That is, the y-coordinate of the point on the + * baseline at the left edge of the glyph. */ + Tag graphicType; /* Indicates the format of the embedded graphic + * data: one of 'jpg ', 'png ' or 'tiff', or the + * special format 'dupe'. */ + UnsizedArrayOf + data; /* The actual embedded graphic data. The total + * length is inferred from sequential entries in + * the glyphDataOffsets array and the fixed size + * (8 bytes) of the preceding fields. */ + public: + DEFINE_SIZE_ARRAY (8, data); +}; + +struct SBIXStrike +{ + static unsigned int get_size (unsigned num_glyphs) + { return min_size + num_glyphs * HBUINT32::static_size; } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + imageOffsetsZ.sanitize_shallow (c, c->get_num_glyphs () + 1)); + } + + hb_blob_t *get_glyph_blob (unsigned int glyph_id, + hb_blob_t *sbix_blob, + hb_tag_t file_type, + int *x_offset, + int *y_offset, + unsigned int num_glyphs, + unsigned int *strike_ppem) const + { + if (unlikely (!ppem)) return hb_blob_get_empty (); /* To get Null() object out of the way. */ + + unsigned int retry_count = 8; + unsigned int sbix_len = sbix_blob->length; + unsigned int strike_offset = (const char *) this - (const char *) sbix_blob->data; + assert (strike_offset < sbix_len); + + retry: + if (unlikely (glyph_id >= num_glyphs || + imageOffsetsZ[glyph_id + 1] <= imageOffsetsZ[glyph_id] || + imageOffsetsZ[glyph_id + 1] - imageOffsetsZ[glyph_id] <= SBIXGlyph::min_size || + (unsigned int) imageOffsetsZ[glyph_id + 1] > sbix_len - strike_offset)) + return hb_blob_get_empty (); + + unsigned int glyph_offset = strike_offset + (unsigned int) imageOffsetsZ[glyph_id] + SBIXGlyph::min_size; + unsigned int glyph_length = imageOffsetsZ[glyph_id + 1] - imageOffsetsZ[glyph_id] - SBIXGlyph::min_size; + + const SBIXGlyph *glyph = &(this+imageOffsetsZ[glyph_id]); + + if (glyph->graphicType == HB_TAG ('d','u','p','e')) + { + if (glyph_length >= 2) + { + glyph_id = *((HBUINT16 *) &glyph->data); + if (retry_count--) + goto retry; + } + return hb_blob_get_empty (); + } + + if (unlikely (file_type != glyph->graphicType)) + return hb_blob_get_empty (); + + if (strike_ppem) *strike_ppem = ppem; + if (x_offset) *x_offset = glyph->xOffset; + if (y_offset) *y_offset = glyph->yOffset; + return hb_blob_create_sub_blob (sbix_blob, glyph_offset, glyph_length); + } + + bool subset (hb_subset_context_t *c, unsigned int available_len) const + { + TRACE_SUBSET (this); + unsigned int num_output_glyphs = c->plan->num_output_glyphs (); + + auto* out = c->serializer->start_embed (); + if (unlikely (!out)) return_trace (false); + auto snap = c->serializer->snapshot (); + if (unlikely (!c->serializer->extend (out, num_output_glyphs + 1))) return_trace (false); + out->ppem = ppem; + out->resolution = resolution; + HBUINT32 head; + head = get_size (num_output_glyphs + 1); + + bool has_glyphs = false; + for (unsigned new_gid = 0; new_gid < num_output_glyphs; new_gid++) + { + hb_codepoint_t old_gid; + if (!c->plan->old_gid_for_new_gid (new_gid, &old_gid) || + unlikely (imageOffsetsZ[old_gid].is_null () || + imageOffsetsZ[old_gid + 1].is_null () || + imageOffsetsZ[old_gid + 1] <= imageOffsetsZ[old_gid] || + imageOffsetsZ[old_gid + 1] - imageOffsetsZ[old_gid] <= SBIXGlyph::min_size) || + (unsigned int) imageOffsetsZ[old_gid + 1] > available_len) + { + out->imageOffsetsZ[new_gid] = head; + continue; + } + has_glyphs = true; + unsigned int delta = imageOffsetsZ[old_gid + 1] - imageOffsetsZ[old_gid]; + unsigned int glyph_data_length = delta - SBIXGlyph::min_size; + if (!(this+imageOffsetsZ[old_gid]).copy (c->serializer, glyph_data_length)) + return_trace (false); + out->imageOffsetsZ[new_gid] = head; + head += delta; + } + if (has_glyphs) + out->imageOffsetsZ[num_output_glyphs] = head; + else + c->serializer->revert (snap); + return_trace (has_glyphs); + } + + public: + HBUINT16 ppem; /* The PPEM size for which this strike was designed. */ + HBUINT16 resolution; /* The device pixel density (in PPI) for which this + * strike was designed. (E.g., 96 PPI, 192 PPI.) */ + protected: + UnsizedArrayOf> + imageOffsetsZ; /* Offset from the beginning of the strike data header + * to bitmap data for an individual glyph ID. */ + public: + DEFINE_SIZE_ARRAY (4, imageOffsetsZ); +}; + +struct sbix +{ + static constexpr hb_tag_t tableTag = HB_OT_TAG_sbix; + + bool has_data () const { return version; } + + const SBIXStrike &get_strike (unsigned int i) const { return this+strikes[i]; } + + struct accelerator_t + { + accelerator_t (hb_face_t *face) + { + table = hb_sanitize_context_t ().reference_table (face); + num_glyphs = face->get_num_glyphs (); + } + ~accelerator_t () { table.destroy (); } + + bool has_data () const { return table->has_data (); } + + bool get_extents (hb_font_t *font, + hb_codepoint_t glyph, + hb_glyph_extents_t *extents) const + { + /* We only support PNG right now, and following function checks type. */ + return get_png_extents (font, glyph, extents); + } + + hb_blob_t *reference_png (hb_font_t *font, + hb_codepoint_t glyph_id, + int *x_offset, + int *y_offset, + unsigned int *available_ppem) const + { + return choose_strike (font).get_glyph_blob (glyph_id, table.get_blob (), + HB_TAG ('p','n','g',' '), + x_offset, y_offset, + num_glyphs, available_ppem); + } + + private: + + const SBIXStrike &choose_strike (hb_font_t *font) const + { + unsigned count = table->strikes.len; + if (unlikely (!count)) + return Null (SBIXStrike); + + unsigned int requested_ppem = hb_max (font->x_ppem, font->y_ppem); + if (!requested_ppem) + requested_ppem = 1<<30; /* Choose largest strike. */ + /* TODO Add DPI sensitivity as well? */ + unsigned int best_i = 0; + unsigned int best_ppem = table->get_strike (0).ppem; + + for (unsigned int i = 1; i < count; i++) + { + unsigned int ppem = (table->get_strike (i)).ppem; + if ((requested_ppem <= ppem && ppem < best_ppem) || + (requested_ppem > best_ppem && ppem > best_ppem)) + { + best_i = i; + best_ppem = ppem; + } + } + + return table->get_strike (best_i); + } + + struct PNGHeader + { + HBUINT8 signature[8]; + struct + { + struct + { + HBUINT32 length; + Tag type; + } header; + HBUINT32 width; + HBUINT32 height; + HBUINT8 bitDepth; + HBUINT8 colorType; + HBUINT8 compressionMethod; + HBUINT8 filterMethod; + HBUINT8 interlaceMethod; + } IHDR; + + public: + DEFINE_SIZE_STATIC (29); + }; + + bool get_png_extents (hb_font_t *font, + hb_codepoint_t glyph, + hb_glyph_extents_t *extents) const + { + /* Following code is safe to call even without data. + * But faster to short-circuit. */ + if (!has_data ()) + return false; + + int x_offset = 0, y_offset = 0; + unsigned int strike_ppem = 0; + hb_blob_t *blob = reference_png (font, glyph, &x_offset, &y_offset, &strike_ppem); + + const PNGHeader &png = *blob->as(); + + if (png.IHDR.height >= 65536 || png.IHDR.width >= 65536) + { + hb_blob_destroy (blob); + return false; + } + + extents->x_bearing = x_offset; + extents->y_bearing = png.IHDR.height + y_offset; + extents->width = png.IHDR.width; + extents->height = -1 * png.IHDR.height; + + /* Convert to font units. */ + if (strike_ppem) + { + float scale = font->face->get_upem () / (float) strike_ppem; + extents->x_bearing = font->em_scalef_x (extents->x_bearing * scale); + extents->y_bearing = font->em_scalef_y (extents->y_bearing * scale); + extents->width = font->em_scalef_x (extents->width * scale); + extents->height = font->em_scalef_y (extents->height * scale); + } + else + { + extents->x_bearing = font->em_scale_x (extents->x_bearing); + extents->y_bearing = font->em_scale_y (extents->y_bearing); + extents->width = font->em_scale_x (extents->width); + extents->height = font->em_scale_y (extents->height); + } + + hb_blob_destroy (blob); + + return strike_ppem; + } + + private: + hb_blob_ptr_t table; + + unsigned int num_glyphs; + }; + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + version >= 1 && + strikes.sanitize (c, this))); + } + + bool + add_strike (hb_subset_context_t *c, unsigned i) const + { + if (strikes[i].is_null () || c->source_blob->length < (unsigned) strikes[i]) + return false; + + return (this+strikes[i]).subset (c, c->source_blob->length - (unsigned) strikes[i]); + } + + bool serialize_strike_offsets (hb_subset_context_t *c) const + { + TRACE_SERIALIZE (this); + + auto *out = c->serializer->start_embed> (); + if (unlikely (!out)) return_trace (false); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + + hb_vector_t*> new_strikes; + hb_vector_t objidxs; + for (int i = strikes.len - 1; i >= 0; --i) + { + auto* o = out->serialize_append (c->serializer); + if (unlikely (!o)) return_trace (false); + *o = 0; + auto snap = c->serializer->snapshot (); + c->serializer->push (); + bool ret = add_strike (c, i); + if (!ret) + { + c->serializer->pop_discard (); + out->pop (); + c->serializer->revert (snap); + } + else + { + objidxs.push (c->serializer->pop_pack ()); + new_strikes.push (o); + } + } + for (unsigned int i = 0; i < new_strikes.length; ++i) + c->serializer->add_link (*new_strikes[i], objidxs[new_strikes.length - 1 - i]); + + return_trace (true); + } + + bool subset (hb_subset_context_t* c) const + { + TRACE_SUBSET (this); + + sbix *sbix_prime = c->serializer->start_embed (); + if (unlikely (!sbix_prime)) return_trace (false); + if (unlikely (!c->serializer->embed (this->version))) return_trace (false); + if (unlikely (!c->serializer->embed (this->flags))) return_trace (false); + + return_trace (serialize_strike_offsets (c)); + } + + protected: + HBUINT16 version; /* Table version number — set to 1 */ + HBUINT16 flags; /* Bit 0: Set to 1. Bit 1: Draw outlines. + * Bits 2 to 15: reserved (set to 0). */ + Array32OfOffset32To + strikes; /* Offsets from the beginning of the 'sbix' + * table to data for each individual bitmap strike. */ + public: + DEFINE_SIZE_ARRAY (8, strikes); +}; + +struct sbix_accelerator_t : sbix::accelerator_t { + sbix_accelerator_t (hb_face_t *face) : sbix::accelerator_t (face) {} +}; + + +} /* namespace OT */ + +#endif /* HB_OT_COLOR_SBIX_TABLE_HH */ diff --git a/libs/harfbuzz/src/hb-ot-color-svg-table.hh b/libs/harfbuzz/src/hb-ot-color-svg-table.hh new file mode 100644 index 00000000..fc649f10 --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-color-svg-table.hh @@ -0,0 +1,126 @@ +/* + * Copyright © 2018 Ebrahim Byagowi + * + * 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. + */ + +#ifndef HB_OT_COLOR_SVG_TABLE_HH +#define HB_OT_COLOR_SVG_TABLE_HH + +#include "hb-open-type.hh" + +/* + * SVG -- SVG (Scalable Vector Graphics) + * https://docs.microsoft.com/en-us/typography/opentype/spec/svg + */ + +#define HB_OT_TAG_SVG HB_TAG('S','V','G',' ') + + +namespace OT { + + +struct SVGDocumentIndexEntry +{ + int cmp (hb_codepoint_t g) const + { return g < startGlyphID ? -1 : g > endGlyphID ? 1 : 0; } + + hb_blob_t *reference_blob (hb_blob_t *svg_blob, unsigned int index_offset) const + { + return hb_blob_create_sub_blob (svg_blob, + index_offset + (unsigned int) svgDoc, + svgDocLength); + } + + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + svgDoc.sanitize (c, base, svgDocLength)); + } + + protected: + HBUINT16 startGlyphID; /* The first glyph ID in the range described by + * this index entry. */ + HBUINT16 endGlyphID; /* The last glyph ID in the range described by + * this index entry. Must be >= startGlyphID. */ + NNOffset32To> + svgDoc; /* Offset from the beginning of the SVG Document Index + * to an SVG document. Must be non-zero. */ + HBUINT32 svgDocLength; /* Length of the SVG document. + * Must be non-zero. */ + public: + DEFINE_SIZE_STATIC (12); +}; + +struct SVG +{ + static constexpr hb_tag_t tableTag = HB_OT_TAG_SVG; + + bool has_data () const { return svgDocEntries; } + + struct accelerator_t + { + accelerator_t (hb_face_t *face) + { table = hb_sanitize_context_t ().reference_table (face); } + ~accelerator_t () { table.destroy (); } + + hb_blob_t *reference_blob_for_glyph (hb_codepoint_t glyph_id) const + { + return table->get_glyph_entry (glyph_id).reference_blob (table.get_blob (), + table->svgDocEntries); + } + + bool has_data () const { return table->has_data (); } + + private: + hb_blob_ptr_t table; + }; + + const SVGDocumentIndexEntry &get_glyph_entry (hb_codepoint_t glyph_id) const + { return (this+svgDocEntries).bsearch (glyph_id); } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + (this+svgDocEntries).sanitize_shallow (c))); + } + + protected: + HBUINT16 version; /* Table version (starting at 0). */ + Offset32To> + svgDocEntries; /* Offset (relative to the start of the SVG table) to the + * SVG Documents Index. Must be non-zero. */ + /* Array of SVG Document Index Entries. */ + HBUINT32 reserved; /* Set to 0. */ + public: + DEFINE_SIZE_STATIC (10); +}; + +struct SVG_accelerator_t : SVG::accelerator_t { + SVG_accelerator_t (hb_face_t *face) : SVG::accelerator_t (face) {} +}; + +} /* namespace OT */ + + +#endif /* HB_OT_COLOR_SVG_TABLE_HH */ diff --git a/libs/harfbuzz/src/hb-ot-color.cc b/libs/harfbuzz/src/hb-ot-color.cc new file mode 100644 index 00000000..696ca3e1 --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-color.cc @@ -0,0 +1,322 @@ +/* + * Copyright © 2016 Google, Inc. + * Copyright © 2018 Ebrahim Byagowi + * + * 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): Sascha Brawer, Behdad Esfahbod + */ + +#include "hb.hh" + +#ifndef HB_NO_COLOR + +#include "hb-ot.h" + +#include "hb-ot-color-cbdt-table.hh" +#include "hb-ot-color-colr-table.hh" +#include "hb-ot-color-cpal-table.hh" +#include "hb-ot-color-sbix-table.hh" +#include "hb-ot-color-svg-table.hh" + + +/** + * SECTION:hb-ot-color + * @title: hb-ot-color + * @short_description: OpenType Color Fonts + * @include: hb-ot.h + * + * Functions for fetching color-font information from OpenType font faces. + * + * HarfBuzz supports `COLR`/`CPAL`, `sbix`, `CBDT`, and `SVG` color fonts. + **/ + + +/* + * CPAL + */ + + +/** + * hb_ot_color_has_palettes: + * @face: #hb_face_t to work upon + * + * Tests whether a face includes a `CPAL` color-palette table. + * + * Return value: `true` if data found, `false` otherwise + * + * Since: 2.1.0 + */ +hb_bool_t +hb_ot_color_has_palettes (hb_face_t *face) +{ + return face->table.CPAL->has_data (); +} + +/** + * hb_ot_color_palette_get_count: + * @face: #hb_face_t to work upon + * + * Fetches the number of color palettes in a face. + * + * Return value: the number of palettes found + * + * Since: 2.1.0 + */ +unsigned int +hb_ot_color_palette_get_count (hb_face_t *face) +{ + return face->table.CPAL->get_palette_count (); +} + +/** + * hb_ot_color_palette_get_name_id: + * @face: #hb_face_t to work upon + * @palette_index: The index of the color palette + * + * Fetches the `name` table Name ID that provides display names for + * a `CPAL` color palette. + * + * Palette display names can be generic (e.g., "Default") or provide + * specific, themed names (e.g., "Spring", "Summer", "Fall", and "Winter"). + * + * Return value: the Named ID found for the palette. + * If the requested palette has no name the result is #HB_OT_NAME_ID_INVALID. + * + * Since: 2.1.0 + */ +hb_ot_name_id_t +hb_ot_color_palette_get_name_id (hb_face_t *face, + unsigned int palette_index) +{ + return face->table.CPAL->get_palette_name_id (palette_index); +} + +/** + * hb_ot_color_palette_color_get_name_id: + * @face: #hb_face_t to work upon + * @color_index: The index of the color + * + * Fetches the `name` table Name ID that provides display names for + * the specified color in a face's `CPAL` color palette. + * + * Display names can be generic (e.g., "Background") or specific + * (e.g., "Eye color"). + * + * Return value: the Name ID found for the color. + * + * Since: 2.1.0 + */ +hb_ot_name_id_t +hb_ot_color_palette_color_get_name_id (hb_face_t *face, + unsigned int color_index) +{ + return face->table.CPAL->get_color_name_id (color_index); +} + +/** + * hb_ot_color_palette_get_flags: + * @face: #hb_face_t to work upon + * @palette_index: The index of the color palette + * + * Fetches the flags defined for a color palette. + * + * Return value: the #hb_ot_color_palette_flags_t of the requested color palette + * + * Since: 2.1.0 + */ +hb_ot_color_palette_flags_t +hb_ot_color_palette_get_flags (hb_face_t *face, + unsigned int palette_index) +{ + return face->table.CPAL->get_palette_flags (palette_index); +} + +/** + * hb_ot_color_palette_get_colors: + * @face: #hb_face_t to work upon + * @palette_index: the index of the color palette to query + * @start_offset: offset of the first color to retrieve + * @color_count: (inout) (optional): Input = the maximum number of colors to return; + * Output = the actual number of colors returned (may be zero) + * @colors: (out) (array length=color_count) (nullable): The array of #hb_color_t records found + * + * Fetches a list of the colors in a color palette. + * + * After calling this function, @colors will be filled with the palette + * colors. If @colors is NULL, the function will just return the number + * of total colors without storing any actual colors; this can be used + * for allocating a buffer of suitable size before calling + * hb_ot_color_palette_get_colors() a second time. + * + * Return value: the total number of colors in the palette + * + * Since: 2.1.0 + */ +unsigned int +hb_ot_color_palette_get_colors (hb_face_t *face, + unsigned int palette_index, + unsigned int start_offset, + unsigned int *colors_count /* IN/OUT. May be NULL. */, + hb_color_t *colors /* OUT. May be NULL. */) +{ + return face->table.CPAL->get_palette_colors (palette_index, start_offset, colors_count, colors); +} + + +/* + * COLR + */ + +/** + * hb_ot_color_has_layers: + * @face: #hb_face_t to work upon + * + * Tests whether a face includes any `COLR` color layers. + * + * Return value: `true` if data found, `false` otherwise + * + * Since: 2.1.0 + */ +hb_bool_t +hb_ot_color_has_layers (hb_face_t *face) +{ + return face->table.COLR->has_data (); +} + +/** + * hb_ot_color_glyph_get_layers: + * @face: #hb_face_t to work upon + * @glyph: The glyph index to query + * @start_offset: offset of the first layer to retrieve + * @layer_count: (inout) (optional): Input = the maximum number of layers to return; + * Output = the actual number of layers returned (may be zero) + * @layers: (out) (array length=layer_count) (nullable): The array of layers found + * + * Fetches a list of all color layers for the specified glyph index in the specified + * face. The list returned will begin at the offset provided. + * + * Return value: Total number of layers available for the glyph index queried + * + * Since: 2.1.0 + */ +unsigned int +hb_ot_color_glyph_get_layers (hb_face_t *face, + hb_codepoint_t glyph, + unsigned int start_offset, + unsigned int *layer_count, /* IN/OUT. May be NULL. */ + hb_ot_color_layer_t *layers /* OUT. May be NULL. */) +{ + return face->table.COLR->get_glyph_layers (glyph, start_offset, layer_count, layers); +} + + +/* + * SVG + */ + +/** + * hb_ot_color_has_svg: + * @face: #hb_face_t to work upon. + * + * Tests whether a face includes any `SVG` glyph images. + * + * Return value: `true` if data found, `false` otherwise. + * + * Since: 2.1.0 + */ +hb_bool_t +hb_ot_color_has_svg (hb_face_t *face) +{ + return face->table.SVG->has_data (); +} + +/** + * hb_ot_color_glyph_reference_svg: + * @face: #hb_face_t to work upon + * @glyph: a svg glyph index + * + * Fetches the SVG document for a glyph. The blob may be either plain text or gzip-encoded. + * + * If the glyph has no SVG document, the singleton empty blob is returned. + * + * Return value: (transfer full): An #hb_blob_t containing the SVG document of the glyph, if available + * + * Since: 2.1.0 + */ +hb_blob_t * +hb_ot_color_glyph_reference_svg (hb_face_t *face, hb_codepoint_t glyph) +{ + return face->table.SVG->reference_blob_for_glyph (glyph); +} + + +/* + * PNG: CBDT or sbix + */ + +/** + * hb_ot_color_has_png: + * @face: #hb_face_t to work upon + * + * Tests whether a face has PNG glyph images (either in `CBDT` or `sbix` tables). + * + * Return value: `true` if data found, `false` otherwise + * + * Since: 2.1.0 + */ +hb_bool_t +hb_ot_color_has_png (hb_face_t *face) +{ + return face->table.CBDT->has_data () || face->table.sbix->has_data (); +} + +/** + * hb_ot_color_glyph_reference_png: + * @font: #hb_font_t to work upon + * @glyph: a glyph index + * + * Fetches the PNG image for a glyph. This function takes a font object, not a face object, + * as input. To get an optimally sized PNG blob, the PPEM values must be set on the @font + * object. If PPEM is unset, the blob returned will be the largest PNG available. + * + * If the glyph has no PNG image, the singleton empty blob is returned. + * + * Return value: (transfer full): An #hb_blob_t containing the PNG image for the glyph, if available + * + * Since: 2.1.0 + */ +hb_blob_t * +hb_ot_color_glyph_reference_png (hb_font_t *font, hb_codepoint_t glyph) +{ + hb_blob_t *blob = hb_blob_get_empty (); + + if (font->face->table.sbix->has_data ()) + blob = font->face->table.sbix->reference_png (font, glyph, nullptr, nullptr, nullptr); + + if (!blob->length && font->face->table.CBDT->has_data ()) + blob = font->face->table.CBDT->reference_png (font, glyph); + + return blob; +} + + +#endif diff --git a/libs/harfbuzz/src/hb-ot-color.h b/libs/harfbuzz/src/hb-ot-color.h new file mode 100644 index 00000000..d11e07e2 --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-color.h @@ -0,0 +1,146 @@ +/* + * Copyright © 2016 Google, Inc. + * Copyright © 2018 Khaled Hosny + * Copyright © 2018 Ebrahim Byagowi + * + * 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): Sascha Brawer, Behdad Esfahbod + */ + +#if !defined(HB_OT_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR) +#error "Include instead." +#endif + +#ifndef HB_OT_COLOR_H +#define HB_OT_COLOR_H + +#include "hb.h" +#include "hb-ot-name.h" + +HB_BEGIN_DECLS + + +/* + * Color palettes. + */ + +HB_EXTERN hb_bool_t +hb_ot_color_has_palettes (hb_face_t *face); + +HB_EXTERN unsigned int +hb_ot_color_palette_get_count (hb_face_t *face); + +HB_EXTERN hb_ot_name_id_t +hb_ot_color_palette_get_name_id (hb_face_t *face, + unsigned int palette_index); + +HB_EXTERN hb_ot_name_id_t +hb_ot_color_palette_color_get_name_id (hb_face_t *face, + unsigned int color_index); + +/** + * hb_ot_color_palette_flags_t: + * @HB_OT_COLOR_PALETTE_FLAG_DEFAULT: Default indicating that there is nothing special + * to note about a color palette. + * @HB_OT_COLOR_PALETTE_FLAG_USABLE_WITH_LIGHT_BACKGROUND: Flag indicating that the color + * palette is appropriate to use when displaying the font on a light background such as white. + * @HB_OT_COLOR_PALETTE_FLAG_USABLE_WITH_DARK_BACKGROUND: Flag indicating that the color + * palette is appropriate to use when displaying the font on a dark background such as black. + * + * Flags that describe the properties of color palette. + * + * Since: 2.1.0 + */ +typedef enum { /*< flags >*/ + HB_OT_COLOR_PALETTE_FLAG_DEFAULT = 0x00000000u, + HB_OT_COLOR_PALETTE_FLAG_USABLE_WITH_LIGHT_BACKGROUND = 0x00000001u, + HB_OT_COLOR_PALETTE_FLAG_USABLE_WITH_DARK_BACKGROUND = 0x00000002u +} hb_ot_color_palette_flags_t; + +HB_EXTERN hb_ot_color_palette_flags_t +hb_ot_color_palette_get_flags (hb_face_t *face, + unsigned int palette_index); + +HB_EXTERN unsigned int +hb_ot_color_palette_get_colors (hb_face_t *face, + unsigned int palette_index, + unsigned int start_offset, + unsigned int *color_count, /* IN/OUT. May be NULL. */ + hb_color_t *colors /* OUT. May be NULL. */); + + +/* + * Color layers. + */ + +HB_EXTERN hb_bool_t +hb_ot_color_has_layers (hb_face_t *face); + +/** + * hb_ot_color_layer_t: + * @glyph: the glyph ID of the layer + * @color_index: the palette color index of the layer + * + * Pairs of glyph and color index. + * + * A color index of 0xFFFF does not refer to a palette + * color, but indicates that the foreground color should + * be used. + * + * Since: 2.1.0 + **/ +typedef struct hb_ot_color_layer_t { + hb_codepoint_t glyph; + unsigned int color_index; +} hb_ot_color_layer_t; + +HB_EXTERN unsigned int +hb_ot_color_glyph_get_layers (hb_face_t *face, + hb_codepoint_t glyph, + unsigned int start_offset, + unsigned int *layer_count, /* IN/OUT. May be NULL. */ + hb_ot_color_layer_t *layers /* OUT. May be NULL. */); + +/* + * SVG + */ + +HB_EXTERN hb_bool_t +hb_ot_color_has_svg (hb_face_t *face); + +HB_EXTERN hb_blob_t * +hb_ot_color_glyph_reference_svg (hb_face_t *face, hb_codepoint_t glyph); + +/* + * PNG: CBDT or sbix + */ + +HB_EXTERN hb_bool_t +hb_ot_color_has_png (hb_face_t *face); + +HB_EXTERN hb_blob_t * +hb_ot_color_glyph_reference_png (hb_font_t *font, hb_codepoint_t glyph); + + +HB_END_DECLS + +#endif /* HB_OT_COLOR_H */ diff --git a/libs/harfbuzz/src/hb-ot-deprecated.h b/libs/harfbuzz/src/hb-ot-deprecated.h new file mode 100644 index 00000000..5192ff73 --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-deprecated.h @@ -0,0 +1,141 @@ +/* + * Copyright © 2018 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 + */ + +#if !defined(HB_OT_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR) +#error "Include instead." +#endif + +#ifndef HB_OT_DEPRECATED_H +#define HB_OT_DEPRECATED_H + +#include "hb.h" +#include "hb-ot-name.h" + + +HB_BEGIN_DECLS + +#ifndef HB_DISABLE_DEPRECATED + + +/* https://github.com/harfbuzz/harfbuzz/issues/1734 */ +/** + * HB_MATH_GLYPH_PART_FLAG_EXTENDER: + * + * Use #HB_OT_MATH_GLYPH_PART_FLAG_EXTENDER instead. + * + * Deprecated: 2.5.1 + */ +#define HB_MATH_GLYPH_PART_FLAG_EXTENDER HB_OT_MATH_GLYPH_PART_FLAG_EXTENDER + +/* https://github.com/harfbuzz/harfbuzz/pull/3417 */ +/** + * HB_OT_MATH_SCRIPT: + * + * Use #HB_SCRIPT_MATH or #HB_OT_TAG_MATH_SCRIPT instead. + * + * Previous versions of this documentation recommended passing + * #HB_OT_MATH_SCRIPT to hb_buffer_set_script() to enable math shaping, but this + * usage is no longer supported. Use #HB_SCRIPT_MATH instead. + * + * Since: 1.3.3 + * Deprecated: 3.4.0 + */ +#define HB_OT_MATH_SCRIPT HB_OT_TAG_MATH_SCRIPT + + +/* Like hb_ot_layout_table_find_script, but takes zero-terminated array of scripts to test */ +HB_EXTERN HB_DEPRECATED_FOR (hb_ot_layout_table_select_script) hb_bool_t +hb_ot_layout_table_choose_script (hb_face_t *face, + hb_tag_t table_tag, + const hb_tag_t *script_tags, + unsigned int *script_index, + hb_tag_t *chosen_script); + +HB_EXTERN HB_DEPRECATED_FOR (hb_ot_layout_script_select_language) hb_bool_t +hb_ot_layout_script_find_language (hb_face_t *face, + hb_tag_t table_tag, + unsigned int script_index, + hb_tag_t language_tag, + unsigned int *language_index); + +HB_EXTERN HB_DEPRECATED_FOR (hb_ot_tags_from_script_and_language) void +hb_ot_tags_from_script (hb_script_t script, + hb_tag_t *script_tag_1, + hb_tag_t *script_tag_2); + +HB_EXTERN HB_DEPRECATED_FOR (hb_ot_tags_from_script_and_language) hb_tag_t +hb_ot_tag_from_language (hb_language_t language); + + +/** + * HB_OT_VAR_NO_AXIS_INDEX: + * + * Do not use. + * + * Since: 1.4.2 + * Deprecated: 2.2.0 + */ +#define HB_OT_VAR_NO_AXIS_INDEX 0xFFFFFFFFu + +/** + * hb_ot_var_axis_t: + * @tag: axis tag + * @name_id: axis name identifier + * @min_value: minimum value of the axis + * @default_value: default value of the axis + * @max_value: maximum value of the axis + * + * Use #hb_ot_var_axis_info_t instead. + * + * Since: 1.4.2 + * Deprecated: 2.2.0 + */ +typedef struct hb_ot_var_axis_t { + hb_tag_t tag; + hb_ot_name_id_t name_id; + float min_value; + float default_value; + float max_value; +} hb_ot_var_axis_t; + +HB_EXTERN HB_DEPRECATED_FOR (hb_ot_var_get_axis_infos) unsigned int +hb_ot_var_get_axes (hb_face_t *face, + unsigned int start_offset, + unsigned int *axes_count /* IN/OUT */, + hb_ot_var_axis_t *axes_array /* OUT */); + +HB_EXTERN HB_DEPRECATED_FOR (hb_ot_var_find_axis_info) hb_bool_t +hb_ot_var_find_axis (hb_face_t *face, + hb_tag_t axis_tag, + unsigned int *axis_index, + hb_ot_var_axis_t *axis_info); + + +#endif + +HB_END_DECLS + +#endif /* HB_OT_DEPRECATED_H */ diff --git a/libs/harfbuzz/src/hb-ot-face-table-list.hh b/libs/harfbuzz/src/hb-ot-face-table-list.hh new file mode 100644 index 00000000..c05034b3 --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-face-table-list.hh @@ -0,0 +1,151 @@ +/* + * Copyright © 2007,2008,2009 Red Hat, Inc. + * Copyright © 2012,2013 Google, Inc. + * Copyright © 2019, Facebook 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. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + * Facebook Author(s): Behdad Esfahbod + */ + +#ifndef HB_OT_FACE_TABLE_LIST_HH +#define HB_OT_FACE_TABLE_LIST_HH +#endif /* HB_OT_FACE_TABLE_LIST_HH */ /* Dummy header guards */ + +#ifndef HB_OT_CORE_TABLE +#define HB_OT_CORE_TABLE(Namespace, Type) HB_OT_TABLE (Namespace, Type) +#define _HB_OT_CORE_TABLE_UNDEF +#endif + +#ifndef HB_OT_ACCELERATOR +#define HB_OT_ACCELERATOR(Namespace, Type) HB_OT_TABLE (Namespace, Type) +#define _HB_OT_ACCELERATOR_UNDEF +#endif + + +/* This lists font tables that the hb_face_t will contain and lazily + * load. Don't add a table unless it's used though. This is not + * exactly zero-cost. */ + +/* v--- Add new tables in the right place here. */ + + +/* OpenType fundamentals. */ +HB_OT_CORE_TABLE (OT, head) +HB_OT_CORE_TABLE (OT, maxp) +#if !defined(HB_NO_FACE_COLLECT_UNICODES) || !defined(HB_NO_OT_FONT) +HB_OT_ACCELERATOR (OT, cmap) +#endif +HB_OT_TABLE (OT, hhea) +HB_OT_ACCELERATOR (OT, hmtx) +HB_OT_TABLE (OT, OS2) +#if !defined(HB_NO_OT_FONT_GLYPH_NAMES) || !defined(HB_NO_METRICS) || !defined(HB_NO_STYLE) +HB_OT_ACCELERATOR (OT, post) +#endif +#ifndef HB_NO_NAME +HB_OT_ACCELERATOR (OT, name) +#endif +#ifndef HB_NO_STYLE +HB_OT_TABLE (OT, STAT) +#endif +#ifndef HB_NO_META +HB_OT_ACCELERATOR (OT, meta) +#endif + +/* Vertical layout. */ +#ifndef HB_NO_VERTICAL +HB_OT_TABLE (OT, vhea) +HB_OT_ACCELERATOR (OT, vmtx) +HB_OT_TABLE (OT, VORG) +#endif + +/* TrueType outlines. */ +HB_OT_CORE_TABLE (OT, loca) // Also used to determine number of glyphs +HB_OT_ACCELERATOR (OT, glyf) + +/* CFF outlines. */ +#ifndef HB_NO_CFF +HB_OT_ACCELERATOR (OT, cff1) +HB_OT_ACCELERATOR (OT, cff2) +#endif + +/* OpenType variations. */ +#ifndef HB_NO_VAR +HB_OT_TABLE (OT, fvar) +HB_OT_TABLE (OT, avar) +HB_OT_ACCELERATOR (OT, gvar) +HB_OT_TABLE (OT, MVAR) +#endif + +/* Legacy kern. */ +#ifndef HB_NO_OT_KERN +HB_OT_TABLE (OT, kern) +#endif + +/* OpenType shaping. */ +#ifndef HB_NO_OT_LAYOUT +HB_OT_ACCELERATOR (OT, GDEF) +HB_OT_ACCELERATOR (OT, GSUB) +HB_OT_ACCELERATOR (OT, GPOS) +//HB_OT_TABLE (OT, JSTF) +#endif + +/* OpenType baseline. */ +#ifndef HB_NO_BASE +HB_OT_TABLE (OT, BASE) +#endif + +/* AAT shaping. */ +#ifndef HB_NO_AAT +HB_OT_TABLE (AAT, morx) +HB_OT_TABLE (AAT, mort) +HB_OT_TABLE (AAT, kerx) +HB_OT_TABLE (AAT, ankr) +HB_OT_TABLE (AAT, trak) +HB_OT_TABLE (AAT, ltag) +HB_OT_TABLE (AAT, feat) +// HB_OT_TABLE (AAT, opbd) +#endif + +/* OpenType color fonts. */ +#ifndef HB_NO_COLOR +HB_OT_TABLE (OT, COLR) +HB_OT_TABLE (OT, CPAL) +HB_OT_ACCELERATOR (OT, CBDT) +HB_OT_ACCELERATOR (OT, sbix) +HB_OT_ACCELERATOR (OT, SVG) +#endif + +/* OpenType math. */ +#ifndef HB_NO_MATH +HB_OT_TABLE (OT, MATH) +#endif + + +#ifdef _HB_OT_ACCELERATOR_UNDEF +#undef HB_OT_ACCELERATOR +#endif + +#ifdef _HB_OT_CORE_TABLE_UNDEF +#undef HB_OT_CORE_TABLE +#endif diff --git a/libs/harfbuzz/src/hb-ot-face.cc b/libs/harfbuzz/src/hb-ot-face.cc new file mode 100644 index 00000000..5ef8df43 --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-face.cc @@ -0,0 +1,58 @@ +/* + * Copyright © 2018 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 + */ + +#include "hb-ot-face.hh" + +#include "hb-ot-cmap-table.hh" +#include "hb-ot-glyf-table.hh" +#include "hb-ot-cff1-table.hh" +#include "hb-ot-cff2-table.hh" +#include "hb-ot-hmtx-table.hh" +#include "hb-ot-kern-table.hh" +#include "hb-ot-meta-table.hh" +#include "hb-ot-name-table.hh" +#include "hb-ot-post-table.hh" +#include "hb-ot-color-cbdt-table.hh" +#include "hb-ot-color-sbix-table.hh" +#include "hb-ot-color-svg-table.hh" +#include "hb-ot-layout-gdef-table.hh" +#include "hb-ot-layout-gsub-table.hh" +#include "hb-ot-layout-gpos-table.hh" + + +void hb_ot_face_t::init0 (hb_face_t *face) +{ + this->face = face; +#define HB_OT_TABLE(Namespace, Type) Type.init0 (); +#include "hb-ot-face-table-list.hh" +#undef HB_OT_TABLE +} +void hb_ot_face_t::fini () +{ +#define HB_OT_TABLE(Namespace, Type) Type.fini (); +#include "hb-ot-face-table-list.hh" +#undef HB_OT_TABLE +} diff --git a/libs/harfbuzz/src/hb-ot-face.hh b/libs/harfbuzz/src/hb-ot-face.hh new file mode 100644 index 00000000..415dae8e --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-face.hh @@ -0,0 +1,77 @@ +/* + * Copyright © 2007,2008,2009 Red Hat, Inc. + * Copyright © 2012,2013 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. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_OT_FACE_HH +#define HB_OT_FACE_HH + +#include "hb.hh" + +#include "hb-machinery.hh" + + +/* + * hb_ot_face_t + */ + +/* Declare tables. */ +#define HB_OT_TABLE(Namespace, Type) namespace Namespace { struct Type; } +#define HB_OT_ACCELERATOR(Namespace, Type) HB_OT_TABLE (Namespace, Type##_accelerator_t) +#include "hb-ot-face-table-list.hh" +#undef HB_OT_ACCELERATOR +#undef HB_OT_TABLE + +struct hb_ot_face_t +{ + HB_INTERNAL void init0 (hb_face_t *face); + HB_INTERNAL void fini (); + +#define HB_OT_TABLE_ORDER(Namespace, Type) \ + HB_PASTE (ORDER_, HB_PASTE (Namespace, HB_PASTE (_, Type))) + enum order_t + { + ORDER_ZERO, +#define HB_OT_TABLE(Namespace, Type) HB_OT_TABLE_ORDER (Namespace, Type), +#include "hb-ot-face-table-list.hh" +#undef HB_OT_TABLE + }; + + hb_face_t *face; /* MUST be JUST before the lazy loaders. */ +#define HB_OT_TABLE(Namespace, Type) \ + hb_table_lazy_loader_t Type; +#define HB_OT_CORE_TABLE(Namespace, Type) \ + hb_table_lazy_loader_t Type; +#define HB_OT_ACCELERATOR(Namespace, Type) \ + hb_face_lazy_loader_t Type; +#include "hb-ot-face-table-list.hh" +#undef HB_OT_ACCELERATOR +#undef HB_OT_CORE_TABLE +#undef HB_OT_TABLE +}; + + +#endif /* HB_OT_FACE_HH */ diff --git a/libs/harfbuzz/src/hb-ot-font.cc b/libs/harfbuzz/src/hb-ot-font.cc new file mode 100644 index 00000000..825b3085 --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-font.cc @@ -0,0 +1,531 @@ +/* + * Copyright © 2011,2014 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, Roozbeh Pournader + */ + +#include "hb.hh" + +#ifndef HB_NO_OT_FONT + +#include "hb-ot.h" + +#include "hb-cache.hh" +#include "hb-font.hh" +#include "hb-machinery.hh" +#include "hb-ot-face.hh" + +#include "hb-ot-cmap-table.hh" +#include "hb-ot-glyf-table.hh" +#include "hb-ot-cff1-table.hh" +#include "hb-ot-cff2-table.hh" +#include "hb-ot-hmtx-table.hh" +#include "hb-ot-post-table.hh" +#include "hb-ot-stat-table.hh" // Just so we compile it; unused otherwise. +#include "hb-ot-vorg-table.hh" +#include "hb-ot-color-cbdt-table.hh" +#include "hb-ot-color-sbix-table.hh" + + +/** + * SECTION:hb-ot-font + * @title: hb-ot-font + * @short_description: OpenType font implementation + * @include: hb-ot.h + * + * Functions for using OpenType fonts with hb_shape(). Note that fonts returned + * by hb_font_create() default to using these functions, so most clients would + * never need to call these functions directly. + **/ + +using hb_ot_font_advance_cache_t = hb_cache_t<24, 16, 8, true>; + +struct hb_ot_font_t +{ + const hb_ot_face_t *ot_face; + + /* h_advance caching */ + mutable hb_atomic_int_t cached_coords_serial; + mutable hb_atomic_ptr_t advance_cache; +}; + +static hb_ot_font_t * +_hb_ot_font_create (hb_font_t *font) +{ + hb_ot_font_t *ot_font = (hb_ot_font_t *) hb_calloc (1, sizeof (hb_ot_font_t)); + if (unlikely (!ot_font)) + return nullptr; + + ot_font->ot_face = &font->face->table; + + return ot_font; +} + +static void +_hb_ot_font_destroy (void *font_data) +{ + hb_ot_font_t *ot_font = (hb_ot_font_t *) font_data; + + auto *cache = ot_font->advance_cache.get_relaxed (); + if (cache) + { + cache->fini (); + hb_free (cache); + } + + hb_free (ot_font); +} + +static hb_bool_t +hb_ot_get_nominal_glyph (hb_font_t *font HB_UNUSED, + void *font_data, + hb_codepoint_t unicode, + hb_codepoint_t *glyph, + void *user_data HB_UNUSED) +{ + const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data; + const hb_ot_face_t *ot_face = ot_font->ot_face; + return ot_face->cmap->get_nominal_glyph (unicode, glyph); +} + +static unsigned int +hb_ot_get_nominal_glyphs (hb_font_t *font HB_UNUSED, + void *font_data, + unsigned int count, + const hb_codepoint_t *first_unicode, + unsigned int unicode_stride, + hb_codepoint_t *first_glyph, + unsigned int glyph_stride, + void *user_data HB_UNUSED) +{ + const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data; + const hb_ot_face_t *ot_face = ot_font->ot_face; + return ot_face->cmap->get_nominal_glyphs (count, + first_unicode, unicode_stride, + first_glyph, glyph_stride); +} + +static hb_bool_t +hb_ot_get_variation_glyph (hb_font_t *font HB_UNUSED, + void *font_data, + hb_codepoint_t unicode, + hb_codepoint_t variation_selector, + hb_codepoint_t *glyph, + void *user_data HB_UNUSED) +{ + const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data; + const hb_ot_face_t *ot_face = ot_font->ot_face; + return ot_face->cmap->get_variation_glyph (unicode, variation_selector, glyph); +} + +static void +hb_ot_get_glyph_h_advances (hb_font_t* font, void* font_data, + unsigned count, + const hb_codepoint_t *first_glyph, + unsigned glyph_stride, + hb_position_t *first_advance, + unsigned advance_stride, + void *user_data HB_UNUSED) +{ + const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data; + const hb_ot_face_t *ot_face = ot_font->ot_face; + const OT::hmtx_accelerator_t &hmtx = *ot_face->hmtx; + +#ifndef HB_NO_VAR + const OT::HVAR &HVAR = *hmtx.var_table; + const OT::VariationStore &varStore = &HVAR + HVAR.varStore; + OT::VariationStore::cache_t *varStore_cache = font->num_coords * count >= 128 ? varStore.create_cache () : nullptr; + + bool use_cache = font->num_coords; +#else + OT::VariationStore::cache_t *varStore_cache = nullptr; + bool use_cache = false; +#endif + + hb_ot_font_advance_cache_t *cache = nullptr; + if (use_cache) + { + retry: + cache = ot_font->advance_cache.get_acquire (); + if (unlikely (!cache)) + { + cache = (hb_ot_font_advance_cache_t *) hb_malloc (sizeof (hb_ot_font_advance_cache_t)); + if (unlikely (!cache)) + { + use_cache = false; + goto out; + } + + cache->init (); + if (unlikely (!ot_font->advance_cache.cmpexch (nullptr, cache))) + { + hb_free (cache); + goto retry; + } + ot_font->cached_coords_serial.set_release (font->serial_coords); + } + } + out: + + if (!use_cache) + { + for (unsigned int i = 0; i < count; i++) + { + *first_advance = font->em_scale_x (hmtx.get_advance_with_var_unscaled (*first_glyph, font, varStore_cache)); + first_glyph = &StructAtOffsetUnaligned (first_glyph, glyph_stride); + first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride); + } + } + else + { /* Use cache. */ + if (ot_font->cached_coords_serial.get_acquire () != (int) font->serial_coords) + { + ot_font->advance_cache->init (); + ot_font->cached_coords_serial.set_release (font->serial_coords); + } + + for (unsigned int i = 0; i < count; i++) + { + hb_position_t v; + unsigned cv; + if (ot_font->advance_cache->get (*first_glyph, &cv)) + v = cv; + else + { + v = hmtx.get_advance_with_var_unscaled (*first_glyph, font, varStore_cache); + ot_font->advance_cache->set (*first_glyph, v); + } + *first_advance = font->em_scale_x (v); + first_glyph = &StructAtOffsetUnaligned (first_glyph, glyph_stride); + first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride); + } + } + +#ifndef HB_NO_VAR + OT::VariationStore::destroy_cache (varStore_cache); +#endif +} + +#ifndef HB_NO_VERTICAL +static void +hb_ot_get_glyph_v_advances (hb_font_t* font, void* font_data, + unsigned count, + const hb_codepoint_t *first_glyph, + unsigned glyph_stride, + hb_position_t *first_advance, + unsigned advance_stride, + void *user_data HB_UNUSED) +{ + const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data; + const hb_ot_face_t *ot_face = ot_font->ot_face; + const OT::vmtx_accelerator_t &vmtx = *ot_face->vmtx; + + if (vmtx.has_data ()) + { +#ifndef HB_NO_VAR + const OT::VVAR &VVAR = *vmtx.var_table; + const OT::VariationStore &varStore = &VVAR + VVAR.varStore; + OT::VariationStore::cache_t *varStore_cache = font->num_coords ? varStore.create_cache () : nullptr; +#else + OT::VariationStore::cache_t *varStore_cache = nullptr; +#endif + + for (unsigned int i = 0; i < count; i++) + { + *first_advance = font->em_scale_y (-(int) vmtx.get_advance_with_var_unscaled (*first_glyph, font, varStore_cache)); + first_glyph = &StructAtOffsetUnaligned (first_glyph, glyph_stride); + first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride); + } + +#ifndef HB_NO_VAR + OT::VariationStore::destroy_cache (varStore_cache); +#endif + } + else + { + hb_font_extents_t font_extents; + font->get_h_extents_with_fallback (&font_extents); + hb_position_t advance = -(font_extents.ascender - font_extents.descender); + + for (unsigned int i = 0; i < count; i++) + { + *first_advance = advance; + first_glyph = &StructAtOffsetUnaligned (first_glyph, glyph_stride); + first_advance = &StructAtOffsetUnaligned (first_advance, advance_stride); + } + } +} +#endif + +#ifndef HB_NO_VERTICAL +static hb_bool_t +hb_ot_get_glyph_v_origin (hb_font_t *font, + void *font_data, + hb_codepoint_t glyph, + hb_position_t *x, + hb_position_t *y, + void *user_data HB_UNUSED) +{ + const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data; + const hb_ot_face_t *ot_face = ot_font->ot_face; + + *x = font->get_glyph_h_advance (glyph) / 2; + + const OT::VORG &VORG = *ot_face->VORG; + if (VORG.has_data ()) + { + float delta = 0; + +#ifndef HB_NO_VAR + const OT::vmtx_accelerator_t &vmtx = *ot_face->vmtx; + const OT::VVAR &VVAR = *vmtx.var_table; + if (font->num_coords) + VVAR.get_vorg_delta_unscaled (glyph, + font->coords, font->num_coords, + &delta); +#endif + + *y = font->em_scalef_y (VORG.get_y_origin (glyph) + delta); + return true; + } + + hb_glyph_extents_t extents = {0}; + if (ot_face->glyf->get_extents (font, glyph, &extents)) + { + const OT::vmtx_accelerator_t &vmtx = *ot_face->vmtx; + int tsb = 0; + if (vmtx.get_leading_bearing_with_var_unscaled (font, glyph, &tsb)) + { + *y = extents.y_bearing + font->em_scale_y (tsb); + return true; + } + + hb_font_extents_t font_extents; + font->get_h_extents_with_fallback (&font_extents); + hb_position_t advance = font_extents.ascender - font_extents.descender; + int diff = advance - -extents.height; + *y = extents.y_bearing + (diff >> 1); + return true; + } + + hb_font_extents_t font_extents; + font->get_h_extents_with_fallback (&font_extents); + *y = font_extents.ascender; + + return true; +} +#endif + +static hb_bool_t +hb_ot_get_glyph_extents (hb_font_t *font, + void *font_data, + hb_codepoint_t glyph, + hb_glyph_extents_t *extents, + void *user_data HB_UNUSED) +{ + const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data; + const hb_ot_face_t *ot_face = ot_font->ot_face; + +#if !defined(HB_NO_OT_FONT_BITMAP) && !defined(HB_NO_COLOR) + if (ot_face->sbix->get_extents (font, glyph, extents)) return true; + if (ot_face->CBDT->get_extents (font, glyph, extents)) return true; +#endif + if (ot_face->glyf->get_extents (font, glyph, extents)) return true; +#ifndef HB_NO_OT_FONT_CFF + if (ot_face->cff1->get_extents (font, glyph, extents)) return true; + if (ot_face->cff2->get_extents (font, glyph, extents)) return true; +#endif + + // TODO Hook up side-bearings variations. + return false; +} + +#ifndef HB_NO_OT_FONT_GLYPH_NAMES +static hb_bool_t +hb_ot_get_glyph_name (hb_font_t *font HB_UNUSED, + void *font_data, + hb_codepoint_t glyph, + char *name, unsigned int size, + void *user_data HB_UNUSED) +{ + const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data; + const hb_ot_face_t *ot_face = ot_font->ot_face; + + if (ot_face->post->get_glyph_name (glyph, name, size)) return true; +#ifndef HB_NO_OT_FONT_CFF + if (ot_face->cff1->get_glyph_name (glyph, name, size)) return true; +#endif + return false; +} +static hb_bool_t +hb_ot_get_glyph_from_name (hb_font_t *font HB_UNUSED, + void *font_data, + const char *name, int len, + hb_codepoint_t *glyph, + void *user_data HB_UNUSED) +{ + const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data; + const hb_ot_face_t *ot_face = ot_font->ot_face; + + if (ot_face->post->get_glyph_from_name (name, len, glyph)) return true; +#ifndef HB_NO_OT_FONT_CFF + if (ot_face->cff1->get_glyph_from_name (name, len, glyph)) return true; +#endif + return false; +} +#endif + +static hb_bool_t +hb_ot_get_font_h_extents (hb_font_t *font, + void *font_data HB_UNUSED, + hb_font_extents_t *metrics, + void *user_data HB_UNUSED) +{ + return _hb_ot_metrics_get_position_common (font, HB_OT_METRICS_TAG_HORIZONTAL_ASCENDER, &metrics->ascender) && + _hb_ot_metrics_get_position_common (font, HB_OT_METRICS_TAG_HORIZONTAL_DESCENDER, &metrics->descender) && + _hb_ot_metrics_get_position_common (font, HB_OT_METRICS_TAG_HORIZONTAL_LINE_GAP, &metrics->line_gap); +} + +#ifndef HB_NO_VERTICAL +static hb_bool_t +hb_ot_get_font_v_extents (hb_font_t *font, + void *font_data HB_UNUSED, + hb_font_extents_t *metrics, + void *user_data HB_UNUSED) +{ + return _hb_ot_metrics_get_position_common (font, HB_OT_METRICS_TAG_VERTICAL_ASCENDER, &metrics->ascender) && + _hb_ot_metrics_get_position_common (font, HB_OT_METRICS_TAG_VERTICAL_DESCENDER, &metrics->descender) && + _hb_ot_metrics_get_position_common (font, HB_OT_METRICS_TAG_VERTICAL_LINE_GAP, &metrics->line_gap); +} +#endif + +#ifndef HB_NO_DRAW +static void +hb_ot_get_glyph_shape (hb_font_t *font, + void *font_data HB_UNUSED, + hb_codepoint_t glyph, + hb_draw_funcs_t *draw_funcs, void *draw_data, + void *user_data) +{ + hb_draw_session_t draw_session (draw_funcs, draw_data, font->slant_xy); + if (font->face->table.glyf->get_path (font, glyph, draw_session)) return; +#ifndef HB_NO_CFF + if (font->face->table.cff1->get_path (font, glyph, draw_session)) return; + if (font->face->table.cff2->get_path (font, glyph, draw_session)) return; +#endif +} +#endif + +static inline void free_static_ot_funcs (); + +static struct hb_ot_font_funcs_lazy_loader_t : hb_font_funcs_lazy_loader_t +{ + static hb_font_funcs_t *create () + { + hb_font_funcs_t *funcs = hb_font_funcs_create (); + + hb_font_funcs_set_nominal_glyph_func (funcs, hb_ot_get_nominal_glyph, nullptr, nullptr); + hb_font_funcs_set_nominal_glyphs_func (funcs, hb_ot_get_nominal_glyphs, nullptr, nullptr); + hb_font_funcs_set_variation_glyph_func (funcs, hb_ot_get_variation_glyph, nullptr, nullptr); + + hb_font_funcs_set_font_h_extents_func (funcs, hb_ot_get_font_h_extents, nullptr, nullptr); + hb_font_funcs_set_glyph_h_advances_func (funcs, hb_ot_get_glyph_h_advances, nullptr, nullptr); + //hb_font_funcs_set_glyph_h_origin_func (funcs, hb_ot_get_glyph_h_origin, nullptr, nullptr); + +#ifndef HB_NO_VERTICAL + hb_font_funcs_set_font_v_extents_func (funcs, hb_ot_get_font_v_extents, nullptr, nullptr); + hb_font_funcs_set_glyph_v_advances_func (funcs, hb_ot_get_glyph_v_advances, nullptr, nullptr); + hb_font_funcs_set_glyph_v_origin_func (funcs, hb_ot_get_glyph_v_origin, nullptr, nullptr); +#endif + +#ifndef HB_NO_DRAW + hb_font_funcs_set_glyph_shape_func (funcs, hb_ot_get_glyph_shape, nullptr, nullptr); +#endif + + hb_font_funcs_set_glyph_extents_func (funcs, hb_ot_get_glyph_extents, nullptr, nullptr); + //hb_font_funcs_set_glyph_contour_point_func (funcs, hb_ot_get_glyph_contour_point, nullptr, nullptr); + +#ifndef HB_NO_OT_FONT_GLYPH_NAMES + hb_font_funcs_set_glyph_name_func (funcs, hb_ot_get_glyph_name, nullptr, nullptr); + hb_font_funcs_set_glyph_from_name_func (funcs, hb_ot_get_glyph_from_name, nullptr, nullptr); +#endif + + hb_font_funcs_make_immutable (funcs); + + hb_atexit (free_static_ot_funcs); + + return funcs; + } +} static_ot_funcs; + +static inline +void free_static_ot_funcs () +{ + static_ot_funcs.free_instance (); +} + +static hb_font_funcs_t * +_hb_ot_get_font_funcs () +{ + return static_ot_funcs.get_unconst (); +} + + +/** + * hb_ot_font_set_funcs: + * @font: #hb_font_t to work upon + * + * Sets the font functions to use when working with @font. + * + * Since: 0.9.28 + **/ +void +hb_ot_font_set_funcs (hb_font_t *font) +{ + hb_ot_font_t *ot_font = _hb_ot_font_create (font); + if (unlikely (!ot_font)) + return; + + hb_font_set_funcs (font, + _hb_ot_get_font_funcs (), + ot_font, + _hb_ot_font_destroy); +} + +#ifndef HB_NO_VAR +bool +_glyf_get_leading_bearing_with_var_unscaled (hb_font_t *font, hb_codepoint_t glyph, bool is_vertical, + int *lsb) +{ + return font->face->table.glyf->get_leading_bearing_with_var_unscaled (font, glyph, is_vertical, lsb); +} + +unsigned +_glyf_get_advance_with_var_unscaled (hb_font_t *font, hb_codepoint_t glyph, bool is_vertical) +{ + return font->face->table.glyf->get_advance_with_var_unscaled (font, glyph, is_vertical); +} +#endif + + +#endif diff --git a/libs/harfbuzz/src/hb-ot-font.h b/libs/harfbuzz/src/hb-ot-font.h new file mode 100644 index 00000000..e7959d1a --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-font.h @@ -0,0 +1,45 @@ +/* + * Copyright © 2014 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, Roozbeh Pournader + */ + +#if !defined(HB_OT_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR) +#error "Include instead." +#endif + +#ifndef HB_OT_FONT_H +#define HB_OT_FONT_H + +#include "hb.h" + +HB_BEGIN_DECLS + + +HB_EXTERN void +hb_ot_font_set_funcs (hb_font_t *font); + + +HB_END_DECLS + +#endif /* HB_OT_FONT_H */ diff --git a/libs/harfbuzz/src/hb-ot-gasp-table.hh b/libs/harfbuzz/src/hb-ot-gasp-table.hh new file mode 100644 index 00000000..f2a9cad4 --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-gasp-table.hh @@ -0,0 +1,84 @@ +/* + * Copyright © 2018 Ebrahim Byagowi + * + * 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. + */ + +#ifndef HB_OT_GASP_TABLE_HH +#define HB_OT_GASP_TABLE_HH + +#include "hb-open-type.hh" +#include "hb-ot-hhea-table.hh" +#include "hb-ot-os2-table.hh" +#include "hb-ot-var-hvar-table.hh" + +/* + * gasp -- Grid-fitting and Scan-conversion Procedure + * https://docs.microsoft.com/en-us/typography/opentype/spec/gasp + */ +#define HB_OT_TAG_gasp HB_TAG('g','a','s','p') + + +namespace OT { + +struct GaspRange +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + public: + HBUINT16 rangeMaxPPEM; /* Upper limit of range, in PPEM */ + HBUINT16 rangeGaspBehavior; + /* Flags describing desired rasterizer behavior. */ + public: + DEFINE_SIZE_STATIC (4); +}; + +struct gasp +{ + static constexpr hb_tag_t tableTag = HB_OT_TAG_gasp; + + const GaspRange &get_gasp_range (unsigned int i) const + { return gaspRanges[i]; } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + gaspRanges.sanitize (c)); + } + + protected: + HBUINT16 version; /* Version number (set to 1) */ + Array16Of + gaspRanges; /* Number of records to follow + * Sorted by ppem */ + public: + DEFINE_SIZE_ARRAY (4, gaspRanges); +}; + +} /* namespace OT */ + + +#endif /* HB_OT_GASP_TABLE_HH */ diff --git a/libs/harfbuzz/src/hb-ot-glyf-table.hh b/libs/harfbuzz/src/hb-ot-glyf-table.hh new file mode 100644 index 00000000..c32ff763 --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-glyf-table.hh @@ -0,0 +1,35 @@ +/* + * Copyright © 2015 Google, Inc. + * Copyright © 2019 Adobe Inc. + * Copyright © 2019 Ebrahim Byagowi + * + * 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, Garret Rieger, Roderick Sheeter + * Adobe Author(s): Michiharu Ariza + */ + +#ifndef HB_OT_GLYF_TABLE_HH +#define HB_OT_GLYF_TABLE_HH + +#include "OT/glyf/glyf.hh" + +#endif /* HB_OT_GLYF_TABLE_HH */ diff --git a/libs/harfbuzz/src/hb-ot-hdmx-table.hh b/libs/harfbuzz/src/hb-ot-hdmx-table.hh new file mode 100644 index 00000000..dea2b7e2 --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-hdmx-table.hh @@ -0,0 +1,177 @@ +/* + * Copyright © 2018 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): Garret Rieger + */ + +#ifndef HB_OT_HDMX_TABLE_HH +#define HB_OT_HDMX_TABLE_HH + +#include "hb-open-type.hh" + +/* + * hdmx -- Horizontal Device Metrics + * https://docs.microsoft.com/en-us/typography/opentype/spec/hdmx + */ +#define HB_OT_TAG_hdmx HB_TAG('h','d','m','x') + + +namespace OT { + + +struct DeviceRecord +{ + static unsigned int get_size (unsigned count) + { return hb_ceil_to_4 (min_size + count * HBUINT8::static_size); } + + template + bool serialize (hb_serialize_context_t *c, unsigned pixelSize, Iterator it) + { + TRACE_SERIALIZE (this); + + unsigned length = it.len (); + + if (unlikely (!c->extend (this, length))) return_trace (false); + + this->pixelSize = pixelSize; + this->maxWidth = + + it + | hb_reduce (hb_max, 0u); + + + it + | hb_sink (widthsZ.as_array (length)); + + return_trace (true); + } + + bool sanitize (hb_sanitize_context_t *c, unsigned sizeDeviceRecord) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + c->check_range (this, sizeDeviceRecord))); + } + + HBUINT8 pixelSize; /* Pixel size for following widths (as ppem). */ + HBUINT8 maxWidth; /* Maximum width. */ + UnsizedArrayOf widthsZ; /* Array of widths (numGlyphs is from the 'maxp' table). */ + public: + DEFINE_SIZE_ARRAY (2, widthsZ); +}; + + +struct hdmx +{ + static constexpr hb_tag_t tableTag = HB_OT_TAG_hdmx; + + unsigned int get_size () const + { return min_size + numRecords * sizeDeviceRecord; } + + const DeviceRecord& operator [] (unsigned int i) const + { + /* XXX Null(DeviceRecord) is NOT safe as it's num-glyphs lengthed. + * https://github.com/harfbuzz/harfbuzz/issues/1300 */ + if (unlikely (i >= numRecords)) return Null (DeviceRecord); + return StructAtOffset (&this->firstDeviceRecord, i * sizeDeviceRecord); + } + + template + bool serialize (hb_serialize_context_t *c, unsigned version, Iterator it) + { + TRACE_SERIALIZE (this); + + if (unlikely (!c->extend_min ((*this)))) return_trace (false); + + this->version = version; + this->numRecords = it.len (); + this->sizeDeviceRecord = DeviceRecord::get_size (it ? (*it).second.len () : 0); + + for (const hb_item_type& _ : +it) + c->start_embed ()->serialize (c, _.first, _.second); + + return_trace (c->successful ()); + } + + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + + hdmx *hdmx_prime = c->serializer->start_embed (); + if (unlikely (!hdmx_prime)) return_trace (false); + + auto it = + + hb_range ((unsigned) numRecords) + | hb_map ([c, this] (unsigned _) + { + const DeviceRecord *device_record = + &StructAtOffset (&firstDeviceRecord, + _ * sizeDeviceRecord); + auto row = + + hb_range (c->plan->num_output_glyphs ()) + | hb_map (c->plan->reverse_glyph_map) + | hb_map ([this, c, device_record] (hb_codepoint_t _) + { + if (c->plan->is_empty_glyph (_)) + return Null (HBUINT8); + return device_record->widthsZ.as_array (get_num_glyphs ()) [_]; + }) + ; + return hb_pair ((unsigned) device_record->pixelSize, +row); + }) + ; + + hdmx_prime->serialize (c->serializer, version, it); + return_trace (true); + } + + unsigned get_num_glyphs () const + { + return sizeDeviceRecord - DeviceRecord::min_size; + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + !hb_unsigned_mul_overflows (numRecords, sizeDeviceRecord) && + sizeDeviceRecord >= DeviceRecord::min_size && + c->check_range (this, get_size ())); + } + + protected: + HBUINT16 version; /* Table version number (0) */ + HBUINT16 numRecords; /* Number of device records. */ + HBUINT32 sizeDeviceRecord; + /* Size of a device record, 32-bit aligned. */ + DeviceRecord firstDeviceRecord; + /* Array of device records. */ + public: + DEFINE_SIZE_MIN (8); +}; + +} /* namespace OT */ + + +#endif /* HB_OT_HDMX_TABLE_HH */ diff --git a/libs/harfbuzz/src/hb-ot-head-table.hh b/libs/harfbuzz/src/hb-ot-head-table.hh new file mode 100644 index 00000000..20991aab --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-head-table.hh @@ -0,0 +1,181 @@ +/* + * Copyright © 2010 Red Hat, Inc. + * Copyright © 2012 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. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_OT_HEAD_TABLE_HH +#define HB_OT_HEAD_TABLE_HH + +#include "hb-open-type.hh" + +/* + * head -- Font Header + * https://docs.microsoft.com/en-us/typography/opentype/spec/head + */ +#define HB_OT_TAG_head HB_TAG('h','e','a','d') + + +namespace OT { + + +struct head +{ + friend struct OpenTypeOffsetTable; + + static constexpr hb_tag_t tableTag = HB_OT_TAG_head; + + unsigned int get_upem () const + { + unsigned int upem = unitsPerEm; + /* If no valid head table found, assume 1000, which matches typical Type1 usage. */ + return 16 <= upem && upem <= 16384 ? upem : 1000; + } + + bool serialize (hb_serialize_context_t *c) const + { + TRACE_SERIALIZE (this); + return_trace ((bool) c->embed (this)); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + return_trace (serialize (c->serializer)); + } + + enum mac_style_flag_t { + BOLD = 1u<<0, + ITALIC = 1u<<1, + UNDERLINE = 1u<<2, + OUTLINE = 1u<<3, + SHADOW = 1u<<4, + CONDENSED = 1u<<5, + EXPANDED = 1u<<6, + }; + + bool is_bold () const { return macStyle & BOLD; } + bool is_italic () const { return macStyle & ITALIC; } + bool is_condensed () const { return macStyle & CONDENSED; } + bool is_expanded () const { return macStyle & EXPANDED; } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + version.major == 1 && + magicNumber == 0x5F0F3CF5u); + } + + protected: + FixedVersion<>version; /* Version of the head table--currently + * 0x00010000u for version 1.0. */ + FixedVersion<>fontRevision; /* Set by font manufacturer. */ + HBUINT32 checkSumAdjustment; /* To compute: set it to 0, sum the + * entire font as HBUINT32, then store + * 0xB1B0AFBAu - sum. */ + HBUINT32 magicNumber; /* Set to 0x5F0F3CF5u. */ + HBUINT16 flags; /* Bit 0: Baseline for font at y=0; + * Bit 1: Left sidebearing point at x=0; + * Bit 2: Instructions may depend on point size; + * Bit 3: Force ppem to integer values for all + * internal scaler math; may use fractional + * ppem sizes if this bit is clear; + * Bit 4: Instructions may alter advance width + * (the advance widths might not scale linearly); + * Bits 5-10: These should be set according to + * Apple's specification. However, they are not + * implemented in OpenType. + * Bit 5: This bit should be set in fonts that are + * intended to e laid out vertically, and in + * which the glyphs have been drawn such that an + * x-coordinate of 0 corresponds to the desired + * vertical baseline. + * Bit 6: This bit must be set to zero. + * Bit 7: This bit should be set if the font + * requires layout for correct linguistic + * rendering (e.g. Arabic fonts). + * Bit 8: This bit should be set for a GX font + * which has one or more metamorphosis effects + * designated as happening by default. + * Bit 9: This bit should be set if the font + * contains any strong right-to-left glyphs. + * Bit 10: This bit should be set if the font + * contains Indic-style rearrangement effects. + * Bit 11: Font data is 'lossless,' as a result + * of having been compressed and decompressed + * with the Agfa MicroType Express engine. + * Bit 12: Font converted (produce compatible metrics) + * Bit 13: Font optimized for ClearType™. + * Note, fonts that rely on embedded bitmaps (EBDT) + * for rendering should not be considered optimized + * for ClearType, and therefore should keep this bit + * cleared. + * Bit 14: Last Resort font. If set, indicates that + * the glyphs encoded in the cmap subtables are simply + * generic symbolic representations of code point + * ranges and don’t truly represent support for those + * code points. If unset, indicates that the glyphs + * encoded in the cmap subtables represent proper + * support for those code points. + * Bit 15: Reserved, set to 0. */ + HBUINT16 unitsPerEm; /* Valid range is from 16 to 16384. This value + * should be a power of 2 for fonts that have + * TrueType outlines. */ + LONGDATETIME created; /* Number of seconds since 12:00 midnight, + January 1, 1904. 64-bit integer */ + LONGDATETIME modified; /* Number of seconds since 12:00 midnight, + January 1, 1904. 64-bit integer */ + HBINT16 xMin; /* For all glyph bounding boxes. */ + HBINT16 yMin; /* For all glyph bounding boxes. */ + HBINT16 xMax; /* For all glyph bounding boxes. */ + HBINT16 yMax; /* For all glyph bounding boxes. */ + HBUINT16 macStyle; /* Bit 0: Bold (if set to 1); + * Bit 1: Italic (if set to 1) + * Bit 2: Underline (if set to 1) + * Bit 3: Outline (if set to 1) + * Bit 4: Shadow (if set to 1) + * Bit 5: Condensed (if set to 1) + * Bit 6: Extended (if set to 1) + * Bits 7-15: Reserved (set to 0). */ + HBUINT16 lowestRecPPEM; /* Smallest readable size in pixels. */ + HBINT16 fontDirectionHint; /* Deprecated (Set to 2). + * 0: Fully mixed directional glyphs; + * 1: Only strongly left to right; + * 2: Like 1 but also contains neutrals; + * -1: Only strongly right to left; + * -2: Like -1 but also contains neutrals. */ + public: + HBUINT16 indexToLocFormat; /* 0 for short offsets, 1 for long. */ + HBUINT16 glyphDataFormat; /* 0 for current format. */ + + DEFINE_SIZE_STATIC (54); +}; + + +} /* namespace OT */ + + +#endif /* HB_OT_HEAD_TABLE_HH */ diff --git a/libs/harfbuzz/src/hb-ot-hhea-table.hh b/libs/harfbuzz/src/hb-ot-hhea-table.hh new file mode 100644 index 00000000..d9c9bd35 --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-hhea-table.hh @@ -0,0 +1,104 @@ +/* + * Copyright © 2011,2012 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 HB_OT_HHEA_TABLE_HH +#define HB_OT_HHEA_TABLE_HH + +#include "hb-open-type.hh" + +/* + * hhea -- Horizontal Header + * https://docs.microsoft.com/en-us/typography/opentype/spec/hhea + * vhea -- Vertical Header + * https://docs.microsoft.com/en-us/typography/opentype/spec/vhea + */ +#define HB_OT_TAG_hhea HB_TAG('h','h','e','a') +#define HB_OT_TAG_vhea HB_TAG('v','h','e','a') + + +namespace OT { + + +template +struct _hea +{ + bool has_data () const { return version.major; } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && likely (version.major == 1)); + } + + public: + FixedVersion<>version; /* 0x00010000u for version 1.0. */ + FWORD ascender; /* Typographic ascent. */ + FWORD descender; /* Typographic descent. */ + FWORD lineGap; /* Typographic line gap. */ + UFWORD advanceMax; /* Maximum advance width/height value in + * metrics table. */ + FWORD minLeadingBearing; + /* Minimum left/top sidebearing value in + * metrics table. */ + FWORD minTrailingBearing; + /* Minimum right/bottom sidebearing value; + * calculated as Min(aw - lsb - + * (xMax - xMin)) for horizontal. */ + FWORD maxExtent; /* horizontal: Max(lsb + (xMax - xMin)), + * vertical: minLeadingBearing+(yMax-yMin). */ + HBINT16 caretSlopeRise; /* Used to calculate the slope of the + * cursor (rise/run); 1 for vertical caret, + * 0 for horizontal.*/ + HBINT16 caretSlopeRun; /* 0 for vertical caret, 1 for horizontal. */ + HBINT16 caretOffset; /* The amount by which a slanted + * highlight on a glyph needs + * to be shifted to produce the + * best appearance. Set to 0 for + * non-slanted fonts. */ + HBINT16 reserved1; /* Set to 0. */ + HBINT16 reserved2; /* Set to 0. */ + HBINT16 reserved3; /* Set to 0. */ + HBINT16 reserved4; /* Set to 0. */ + HBINT16 metricDataFormat;/* 0 for current format. */ + HBUINT16 numberOfLongMetrics; + /* Number of LongMetric entries in metric + * table. */ + public: + DEFINE_SIZE_STATIC (36); +}; + +struct hhea : _hea { + static constexpr hb_tag_t tableTag = HB_OT_TAG_hhea; +}; +struct vhea : _hea { + static constexpr hb_tag_t tableTag = HB_OT_TAG_vhea; +}; + + +} /* namespace OT */ + + +#endif /* HB_OT_HHEA_TABLE_HH */ diff --git a/libs/harfbuzz/src/hb-ot-hmtx-table.hh b/libs/harfbuzz/src/hb-ot-hmtx-table.hh new file mode 100644 index 00000000..96a394ba --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-hmtx-table.hh @@ -0,0 +1,409 @@ +/* + * Copyright © 2011,2012 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, Roderick Sheeter + */ + +#ifndef HB_OT_HMTX_TABLE_HH +#define HB_OT_HMTX_TABLE_HH + +#include "hb-open-type.hh" +#include "hb-ot-maxp-table.hh" +#include "hb-ot-hhea-table.hh" +#include "hb-ot-var-hvar-table.hh" +#include "hb-ot-metrics.hh" + +/* + * hmtx -- Horizontal Metrics + * https://docs.microsoft.com/en-us/typography/opentype/spec/hmtx + * vmtx -- Vertical Metrics + * https://docs.microsoft.com/en-us/typography/opentype/spec/vmtx + */ +#define HB_OT_TAG_hmtx HB_TAG('h','m','t','x') +#define HB_OT_TAG_vmtx HB_TAG('v','m','t','x') + + +HB_INTERNAL bool +_glyf_get_leading_bearing_with_var_unscaled (hb_font_t *font, hb_codepoint_t glyph, bool is_vertical, int *lsb); + +HB_INTERNAL unsigned +_glyf_get_advance_with_var_unscaled (hb_font_t *font, hb_codepoint_t glyph, bool is_vertical); + + +namespace OT { + + +struct LongMetric +{ + UFWORD advance; /* Advance width/height. */ + FWORD sb; /* Leading (left/top) side bearing. */ + public: + DEFINE_SIZE_STATIC (4); +}; + + +template +struct hmtxvmtx +{ + bool sanitize (hb_sanitize_context_t *c HB_UNUSED) const + { + TRACE_SANITIZE (this); + /* We don't check for anything specific here. The users of the + * struct do all the hard work... */ + return_trace (true); + } + + const hb_hashmap_t>* get_mtx_map (const hb_subset_plan_t *plan) const + { return T::is_horizontal ? plan->hmtx_map : plan->vmtx_map; } + + bool subset_update_header (hb_subset_plan_t *plan, + unsigned int num_hmetrics) const + { + hb_blob_t *src_blob = hb_sanitize_context_t ().reference_table (plan->source, H::tableTag); + hb_blob_t *dest_blob = hb_blob_copy_writable_or_fail (src_blob); + hb_blob_destroy (src_blob); + + if (unlikely (!dest_blob)) { + return false; + } + + unsigned int length; + H *table = (H *) hb_blob_get_data (dest_blob, &length); + table->numberOfLongMetrics = num_hmetrics; + + bool result = plan->add_table (H::tableTag, dest_blob); + hb_blob_destroy (dest_blob); + + return result; + } + + template + void serialize (hb_serialize_context_t *c, + Iterator it, + unsigned num_long_metrics) + { + unsigned idx = 0; + for (auto _ : it) + { + if (idx < num_long_metrics) + { + LongMetric lm; + lm.advance = _.first; + lm.sb = _.second; + if (unlikely (!c->embed (&lm))) return; + } + else + { + FWORD *sb = c->allocate_size (FWORD::static_size); + if (unlikely (!sb)) return; + *sb = _.second; + } + idx++; + } + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + + T *table_prime = c->serializer->start_embed (); + if (unlikely (!table_prime)) return_trace (false); + + accelerator_t _mtx (c->plan->source); + unsigned num_long_metrics; + const hb_hashmap_t> *mtx_map = get_mtx_map (c->plan); + { + /* Determine num_long_metrics to encode. */ + auto& plan = c->plan; + + num_long_metrics = plan->num_output_glyphs (); + unsigned int last_advance = get_new_gid_advance_unscaled (plan, mtx_map, num_long_metrics - 1, _mtx); + while (num_long_metrics > 1 && + last_advance == get_new_gid_advance_unscaled (plan, mtx_map, num_long_metrics - 2, _mtx)) + { + num_long_metrics--; + } + } + + auto it = + + hb_range (c->plan->num_output_glyphs ()) + | hb_map ([c, &_mtx, mtx_map] (unsigned _) + { + if (!mtx_map->has (_)) + { + hb_codepoint_t old_gid; + if (!c->plan->old_gid_for_new_gid (_, &old_gid)) + return hb_pair (0u, 0); + int lsb = 0; + (void) _mtx.get_leading_bearing_without_var_unscaled (old_gid, &lsb); + return hb_pair (_mtx.get_advance_without_var_unscaled (old_gid), +lsb); + } + return mtx_map->get (_); + }) + ; + + table_prime->serialize (c->serializer, it, num_long_metrics); + + if (unlikely (c->serializer->in_error ())) + return_trace (false); + + // Amend header num hmetrics + if (unlikely (!subset_update_header (c->plan, num_long_metrics))) + return_trace (false); + + return_trace (true); + } + + struct accelerator_t + { + friend struct hmtxvmtx; + + accelerator_t (hb_face_t *face) + { + table = hb_sanitize_context_t ().reference_table (face, T::tableTag); + var_table = hb_sanitize_context_t ().reference_table (face, T::variationsTag); + + default_advance = T::is_horizontal ? hb_face_get_upem (face) / 2 : hb_face_get_upem (face); + + /* Populate count variables and sort them out as we go */ + + unsigned int len = table.get_length (); + if (len & 1) + len--; + + num_long_metrics = T::is_horizontal ? + face->table.hhea->numberOfLongMetrics : +#ifndef HB_NO_VERTICAL + face->table.vhea->numberOfLongMetrics +#else + 0 +#endif + ; + if (unlikely (num_long_metrics * 4 > len)) + num_long_metrics = len / 4; + len -= num_long_metrics * 4; + + num_bearings = face->table.maxp->get_num_glyphs (); + + if (unlikely (num_bearings < num_long_metrics)) + num_bearings = num_long_metrics; + if (unlikely ((num_bearings - num_long_metrics) * 2 > len)) + num_bearings = num_long_metrics + len / 2; + len -= (num_bearings - num_long_metrics) * 2; + + /* We MUST set num_bearings to zero if num_long_metrics is zero. + * Our get_advance() depends on that. */ + if (unlikely (!num_long_metrics)) + num_bearings = num_long_metrics = 0; + + num_advances = num_bearings + len / 2; + num_glyphs = face->get_num_glyphs (); + if (num_glyphs < num_advances) + num_glyphs = num_advances; + } + ~accelerator_t () + { + table.destroy (); + var_table.destroy (); + } + + bool has_data () const { return (bool) num_bearings; } + + bool get_leading_bearing_without_var_unscaled (hb_codepoint_t glyph, + int *lsb) const + { + if (glyph < num_long_metrics) + { + *lsb = table->longMetricZ[glyph].sb; + return true; + } + + if (unlikely (glyph >= num_bearings)) + return false; + + const FWORD *bearings = (const FWORD *) &table->longMetricZ[num_long_metrics]; + *lsb = bearings[glyph - num_long_metrics]; + return true; + } + + bool get_leading_bearing_with_var_unscaled (hb_font_t *font, + hb_codepoint_t glyph, + int *lsb) const + { + if (!font->num_coords) + return get_leading_bearing_without_var_unscaled (glyph, lsb); + +#ifndef HB_NO_VAR + float delta; + if (var_table->get_lsb_delta_unscaled (glyph, font->coords, font->num_coords, &delta) && + get_leading_bearing_without_var_unscaled (glyph, lsb)) + { + *lsb += roundf (delta); + return true; + } + + return _glyf_get_leading_bearing_with_var_unscaled (font, glyph, T::tableTag == HB_OT_TAG_vmtx, lsb); +#else + return false; +#endif + } + + unsigned int get_advance_without_var_unscaled (hb_codepoint_t glyph) const + { + /* OpenType case. */ + if (glyph < num_bearings) + return table->longMetricZ[hb_min (glyph, (uint32_t) num_long_metrics - 1)].advance; + + /* If num_advances is zero, it means we don't have the metrics table + * for this direction: return default advance. Otherwise, there's a + * well-defined answer. */ + if (unlikely (!num_advances)) + return default_advance; + +#ifdef HB_NO_BEYOND_64K + return 0; +#endif + + if (unlikely (glyph >= num_glyphs)) + return 0; + + /* num_bearings <= glyph < num_glyphs; + * num_bearings <= num_advances */ + + /* TODO Optimize */ + + if (num_bearings == num_advances) + return get_advance_without_var_unscaled (num_bearings - 1); + + const FWORD *bearings = (const FWORD *) &table->longMetricZ[num_long_metrics]; + const UFWORD *advances = (const UFWORD *) &bearings[num_bearings - num_long_metrics]; + + return advances[hb_min (glyph - num_bearings, num_advances - num_bearings - 1)]; + } + + unsigned get_advance_with_var_unscaled (hb_codepoint_t glyph, + hb_font_t *font, + VariationStore::cache_t *store_cache = nullptr) const + { + unsigned int advance = get_advance_without_var_unscaled (glyph); + +#ifndef HB_NO_VAR + if (unlikely (glyph >= num_bearings) || !font->num_coords) + return advance; + + if (var_table.get_length ()) + return advance + roundf (var_table->get_advance_delta_unscaled (glyph, + font->coords, font->num_coords, + store_cache)); // TODO Optimize?! + + return _glyf_get_advance_with_var_unscaled (font, glyph, T::tableTag == HB_OT_TAG_vmtx); +#else + return advance; +#endif + } + + protected: + // 0 <= num_long_metrics <= num_bearings <= num_advances <= num_glyphs + unsigned num_long_metrics; + unsigned num_bearings; + unsigned num_advances; + unsigned num_glyphs; + + unsigned int default_advance; + + public: + hb_blob_ptr_t table; + hb_blob_ptr_t var_table; + }; + + /* get advance: when no variations, call get_advance_without_var_unscaled. + * when there're variations, get advance value from mtx_map in subset_plan*/ + unsigned get_new_gid_advance_unscaled (const hb_subset_plan_t *plan, + const hb_hashmap_t> *mtx_map, + unsigned new_gid, + const accelerator_t &_mtx) const + { + if (mtx_map->is_empty () || + (new_gid == 0 && !mtx_map->has (new_gid))) + { + hb_codepoint_t old_gid = 0; + return plan->old_gid_for_new_gid (new_gid, &old_gid) ? + _mtx.get_advance_without_var_unscaled (old_gid) : 0; + } + else + { return mtx_map->get (new_gid).first; } + } + + protected: + UnsizedArrayOf + longMetricZ; /* Paired advance width and leading + * bearing values for each glyph. The + * value numOfHMetrics comes from + * the 'hhea' table. If the font is + * monospaced, only one entry need + * be in the array, but that entry is + * required. The last entry applies to + * all subsequent glyphs. */ +/*UnsizedArrayOf leadingBearingX;*/ + /* Here the advance is assumed + * to be the same as the advance + * for the last entry above. The + * number of entries in this array is + * derived from numGlyphs (from 'maxp' + * table) minus numberOfLongMetrics. + * This generally is used with a run + * of monospaced glyphs (e.g., Kanji + * fonts or Courier fonts). Only one + * run is allowed and it must be at + * the end. This allows a monospaced + * font to vary the side bearing + * values for each glyph. */ +/*UnsizedArrayOfadvancesX;*/ + /* TODO Document. */ + public: + DEFINE_SIZE_ARRAY (0, longMetricZ); +}; + +struct hmtx : hmtxvmtx { + static constexpr hb_tag_t tableTag = HB_OT_TAG_hmtx; + static constexpr hb_tag_t variationsTag = HB_OT_TAG_HVAR; + static constexpr bool is_horizontal = true; +}; +struct vmtx : hmtxvmtx { + static constexpr hb_tag_t tableTag = HB_OT_TAG_vmtx; + static constexpr hb_tag_t variationsTag = HB_OT_TAG_VVAR; + static constexpr bool is_horizontal = false; +}; + +struct hmtx_accelerator_t : hmtx::accelerator_t { + hmtx_accelerator_t (hb_face_t *face) : hmtx::accelerator_t (face) {} +}; +struct vmtx_accelerator_t : vmtx::accelerator_t { + vmtx_accelerator_t (hb_face_t *face) : vmtx::accelerator_t (face) {} +}; + +} /* namespace OT */ + + +#endif /* HB_OT_HMTX_TABLE_HH */ diff --git a/libs/harfbuzz/src/hb-ot-kern-table.hh b/libs/harfbuzz/src/hb-ot-kern-table.hh new file mode 100644 index 00000000..ffa11bc2 --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-kern-table.hh @@ -0,0 +1,359 @@ +/* + * Copyright © 2017 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 HB_OT_KERN_TABLE_HH +#define HB_OT_KERN_TABLE_HH + +#include "hb-aat-layout-kerx-table.hh" + + +/* + * kern -- Kerning + * https://docs.microsoft.com/en-us/typography/opentype/spec/kern + * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6kern.html + */ +#define HB_OT_TAG_kern HB_TAG('k','e','r','n') + + +namespace OT { + + +template +struct KernSubTableFormat3 +{ + int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const + { + hb_array_t kernValue = kernValueZ.as_array (kernValueCount); + hb_array_t leftClass = StructAfter> (kernValue).as_array (glyphCount); + hb_array_t rightClass = StructAfter> (leftClass).as_array (glyphCount); + hb_array_t kernIndex = StructAfter> (rightClass).as_array (leftClassCount * rightClassCount); + + unsigned int leftC = leftClass[left]; + unsigned int rightC = rightClass[right]; + if (unlikely (leftC >= leftClassCount || rightC >= rightClassCount)) + return 0; + unsigned int i = leftC * rightClassCount + rightC; + return kernValue[kernIndex[i]]; + } + + bool apply (AAT::hb_aat_apply_context_t *c) const + { + TRACE_APPLY (this); + + if (!c->plan->requested_kerning) + return false; + + if (header.coverage & header.Backwards) + return false; + + hb_kern_machine_t machine (*this, header.coverage & header.CrossStream); + machine.kern (c->font, c->buffer, c->plan->kern_mask); + + return_trace (true); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + c->check_range (kernValueZ, + kernValueCount * sizeof (FWORD) + + glyphCount * 2 + + leftClassCount * rightClassCount)); + } + + protected: + KernSubTableHeader + header; + HBUINT16 glyphCount; /* The number of glyphs in this font. */ + HBUINT8 kernValueCount; /* The number of kerning values. */ + HBUINT8 leftClassCount; /* The number of left-hand classes. */ + HBUINT8 rightClassCount;/* The number of right-hand classes. */ + HBUINT8 flags; /* Set to zero (reserved for future use). */ + UnsizedArrayOf + kernValueZ; /* The kerning values. + * Length kernValueCount. */ +#if 0 + UnsizedArrayOf + leftClass; /* The left-hand classes. + * Length glyphCount. */ + UnsizedArrayOf + rightClass; /* The right-hand classes. + * Length glyphCount. */ + UnsizedArrayOfkernIndex; + /* The indices into the kernValue array. + * Length leftClassCount * rightClassCount */ +#endif + public: + DEFINE_SIZE_ARRAY (KernSubTableHeader::static_size + 6, kernValueZ); +}; + +template +struct KernSubTable +{ + unsigned int get_size () const { return u.header.length; } + unsigned int get_type () const { return u.header.format; } + + int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const + { + switch (get_type ()) { + /* This method hooks up to hb_font_t's get_h_kerning. Only support Format0. */ + case 0: return u.format0.get_kerning (left, right); + default:return 0; + } + } + + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const + { + unsigned int subtable_type = get_type (); + TRACE_DISPATCH (this, subtable_type); + switch (subtable_type) { + case 0: return_trace (c->dispatch (u.format0)); +#ifndef HB_NO_AAT_SHAPE + case 1: return_trace (u.header.apple ? c->dispatch (u.format1, std::forward (ds)...) : c->default_return_value ()); +#endif + case 2: return_trace (c->dispatch (u.format2)); +#ifndef HB_NO_AAT_SHAPE + case 3: return_trace (u.header.apple ? c->dispatch (u.format3, std::forward (ds)...) : c->default_return_value ()); +#endif + default: return_trace (c->default_return_value ()); + } + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (unlikely (!u.header.sanitize (c) || + u.header.length < u.header.min_size || + !c->check_range (this, u.header.length))) return_trace (false); + + return_trace (dispatch (c)); + } + + public: + union { + KernSubTableHeader header; + AAT::KerxSubTableFormat0 format0; + AAT::KerxSubTableFormat1 format1; + AAT::KerxSubTableFormat2 format2; + KernSubTableFormat3 format3; + } u; + public: + DEFINE_SIZE_MIN (KernSubTableHeader::static_size); +}; + + +struct KernOTSubTableHeader +{ + static constexpr bool apple = false; + typedef AAT::ObsoleteTypes Types; + + unsigned tuple_count () const { return 0; } + bool is_horizontal () const { return (coverage & Horizontal); } + + enum Coverage + { + Horizontal = 0x01u, + Minimum = 0x02u, + CrossStream = 0x04u, + Override = 0x08u, + + /* Not supported: */ + Backwards = 0x00u, + Variation = 0x00u, + }; + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + public: + HBUINT16 versionZ; /* Unused. */ + HBUINT16 length; /* Length of the subtable (including this header). */ + HBUINT8 format; /* Subtable format. */ + HBUINT8 coverage; /* Coverage bits. */ + public: + DEFINE_SIZE_STATIC (6); +}; + +struct KernOT : AAT::KerxTable +{ + friend struct AAT::KerxTable; + + static constexpr hb_tag_t tableTag = HB_OT_TAG_kern; + static constexpr unsigned minVersion = 0u; + + typedef KernOTSubTableHeader SubTableHeader; + typedef SubTableHeader::Types Types; + typedef KernSubTable SubTable; + + protected: + HBUINT16 version; /* Version--0x0000u */ + HBUINT16 tableCount; /* Number of subtables in the kerning table. */ + SubTable firstSubTable; /* Subtables. */ + public: + DEFINE_SIZE_MIN (4); +}; + + +struct KernAATSubTableHeader +{ + static constexpr bool apple = true; + typedef AAT::ObsoleteTypes Types; + + unsigned tuple_count () const { return 0; } + bool is_horizontal () const { return !(coverage & Vertical); } + + enum Coverage + { + Vertical = 0x80u, + CrossStream = 0x40u, + Variation = 0x20u, + + /* Not supported: */ + Backwards = 0x00u, + }; + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + public: + HBUINT32 length; /* Length of the subtable (including this header). */ + HBUINT8 coverage; /* Coverage bits. */ + HBUINT8 format; /* Subtable format. */ + HBUINT16 tupleIndex; /* The tuple index (used for variations fonts). + * This value specifies which tuple this subtable covers. + * Note: We don't implement. */ + public: + DEFINE_SIZE_STATIC (8); +}; + +struct KernAAT : AAT::KerxTable +{ + friend struct AAT::KerxTable; + + static constexpr hb_tag_t tableTag = HB_OT_TAG_kern; + static constexpr unsigned minVersion = 0x00010000u; + + typedef KernAATSubTableHeader SubTableHeader; + typedef SubTableHeader::Types Types; + typedef KernSubTable SubTable; + + protected: + HBUINT32 version; /* Version--0x00010000u */ + HBUINT32 tableCount; /* Number of subtables in the kerning table. */ + SubTable firstSubTable; /* Subtables. */ + public: + DEFINE_SIZE_MIN (8); +}; + +struct kern +{ + static constexpr hb_tag_t tableTag = HB_OT_TAG_kern; + + bool has_data () const { return u.version32; } + unsigned get_type () const { return u.major; } + + bool has_state_machine () const + { + switch (get_type ()) { + case 0: return u.ot.has_state_machine (); +#ifndef HB_NO_AAT_SHAPE + case 1: return u.aat.has_state_machine (); +#endif + default:return false; + } + } + + bool has_cross_stream () const + { + switch (get_type ()) { + case 0: return u.ot.has_cross_stream (); +#ifndef HB_NO_AAT_SHAPE + case 1: return u.aat.has_cross_stream (); +#endif + default:return false; + } + } + + int get_h_kerning (hb_codepoint_t left, hb_codepoint_t right) const + { + switch (get_type ()) { + case 0: return u.ot.get_h_kerning (left, right); +#ifndef HB_NO_AAT_SHAPE + case 1: return u.aat.get_h_kerning (left, right); +#endif + default:return 0; + } + } + + bool apply (AAT::hb_aat_apply_context_t *c) const + { return dispatch (c); } + + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const + { + unsigned int subtable_type = get_type (); + TRACE_DISPATCH (this, subtable_type); + switch (subtable_type) { + case 0: return_trace (c->dispatch (u.ot, std::forward (ds)...)); +#ifndef HB_NO_AAT_SHAPE + case 1: return_trace (c->dispatch (u.aat, std::forward (ds)...)); +#endif + default: return_trace (c->default_return_value ()); + } + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (!u.version32.sanitize (c)) return_trace (false); + return_trace (dispatch (c)); + } + + protected: + union { + HBUINT32 version32; + HBUINT16 major; + KernOT ot; +#ifndef HB_NO_AAT_SHAPE + KernAAT aat; +#endif + } u; + public: + DEFINE_SIZE_UNION (4, version32); +}; + +} /* namespace OT */ + + +#endif /* HB_OT_KERN_TABLE_HH */ diff --git a/libs/harfbuzz/src/hb-ot-layout-base-table.hh b/libs/harfbuzz/src/hb-ot-layout-base-table.hh new file mode 100644 index 00000000..8179e5ac --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-layout-base-table.hh @@ -0,0 +1,525 @@ +/* + * Copyright © 2016 Elie Roux + * Copyright © 2018 Google, Inc. + * Copyright © 2018-2019 Ebrahim Byagowi + * + * 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 HB_OT_LAYOUT_BASE_TABLE_HH +#define HB_OT_LAYOUT_BASE_TABLE_HH + +#include "hb-open-type.hh" +#include "hb-ot-layout-common.hh" + +namespace OT { + +/* + * BASE -- Baseline + * https://docs.microsoft.com/en-us/typography/opentype/spec/base + */ + +struct BaseCoordFormat1 +{ + hb_position_t get_coord (hb_font_t *font, hb_direction_t direction) const + { + return HB_DIRECTION_IS_HORIZONTAL (direction) ? font->em_scale_y (coordinate) : font->em_scale_x (coordinate); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + protected: + HBUINT16 format; /* Format identifier--format = 1 */ + FWORD coordinate; /* X or Y value, in design units */ + public: + DEFINE_SIZE_STATIC (4); +}; + +struct BaseCoordFormat2 +{ + hb_position_t get_coord (hb_font_t *font, hb_direction_t direction) const + { + /* TODO */ + return HB_DIRECTION_IS_HORIZONTAL (direction) ? font->em_scale_y (coordinate) : font->em_scale_x (coordinate); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + protected: + HBUINT16 format; /* Format identifier--format = 2 */ + FWORD coordinate; /* X or Y value, in design units */ + HBGlyphID16 referenceGlyph; /* Glyph ID of control glyph */ + HBUINT16 coordPoint; /* Index of contour point on the + * reference glyph */ + public: + DEFINE_SIZE_STATIC (8); +}; + +struct BaseCoordFormat3 +{ + hb_position_t get_coord (hb_font_t *font, + const VariationStore &var_store, + hb_direction_t direction) const + { + const Device &device = this+deviceTable; + + return HB_DIRECTION_IS_HORIZONTAL (direction) + ? font->em_scale_y (coordinate) + device.get_y_delta (font, var_store) + : font->em_scale_x (coordinate) + device.get_x_delta (font, var_store); + } + + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + deviceTable.sanitize (c, this))); + } + + protected: + HBUINT16 format; /* Format identifier--format = 3 */ + FWORD coordinate; /* X or Y value, in design units */ + Offset16To + deviceTable; /* Offset to Device table for X or + * Y value, from beginning of + * BaseCoord table (may be NULL). */ + public: + DEFINE_SIZE_STATIC (6); +}; + +struct BaseCoord +{ + bool has_data () const { return u.format; } + + hb_position_t get_coord (hb_font_t *font, + const VariationStore &var_store, + hb_direction_t direction) const + { + switch (u.format) { + case 1: return u.format1.get_coord (font, direction); + case 2: return u.format2.get_coord (font, direction); + case 3: return u.format3.get_coord (font, var_store, direction); + default:return 0; + } + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (unlikely (!u.format.sanitize (c))) return_trace (false); + switch (u.format) { + case 1: return_trace (u.format1.sanitize (c)); + case 2: return_trace (u.format2.sanitize (c)); + case 3: return_trace (u.format3.sanitize (c)); + default:return_trace (false); + } + } + + protected: + union { + HBUINT16 format; + BaseCoordFormat1 format1; + BaseCoordFormat2 format2; + BaseCoordFormat3 format3; + } u; + public: + DEFINE_SIZE_UNION (2, format); +}; + +struct FeatMinMaxRecord +{ + int cmp (hb_tag_t key) const { return tag.cmp (key); } + + bool has_data () const { return tag; } + + void get_min_max (const BaseCoord **min, const BaseCoord **max) const + { + if (likely (min)) *min = &(this+minCoord); + if (likely (max)) *max = &(this+maxCoord); + } + + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + minCoord.sanitize (c, this) && + maxCoord.sanitize (c, this))); + } + + protected: + Tag tag; /* 4-byte feature identification tag--must + * match feature tag in FeatureList */ + Offset16To + minCoord; /* Offset to BaseCoord table that defines + * the minimum extent value, from beginning + * of MinMax table (may be NULL) */ + Offset16To + maxCoord; /* Offset to BaseCoord table that defines + * the maximum extent value, from beginning + * of MinMax table (may be NULL) */ + public: + DEFINE_SIZE_STATIC (8); + +}; + +struct MinMax +{ + void get_min_max (hb_tag_t feature_tag, + const BaseCoord **min, + const BaseCoord **max) const + { + const FeatMinMaxRecord &minMaxCoord = featMinMaxRecords.bsearch (feature_tag); + if (minMaxCoord.has_data ()) + minMaxCoord.get_min_max (min, max); + else + { + if (likely (min)) *min = &(this+minCoord); + if (likely (max)) *max = &(this+maxCoord); + } + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + minCoord.sanitize (c, this) && + maxCoord.sanitize (c, this) && + featMinMaxRecords.sanitize (c, this))); + } + + protected: + Offset16To + minCoord; /* Offset to BaseCoord table that defines + * minimum extent value, from the beginning + * of MinMax table (may be NULL) */ + Offset16To + maxCoord; /* Offset to BaseCoord table that defines + * maximum extent value, from the beginning + * of MinMax table (may be NULL) */ + SortedArray16Of + featMinMaxRecords; + /* Array of FeatMinMaxRecords, in alphabetical + * order by featureTableTag */ + public: + DEFINE_SIZE_ARRAY (6, featMinMaxRecords); +}; + +struct BaseValues +{ + const BaseCoord &get_base_coord (int baseline_tag_index) const + { + if (baseline_tag_index == -1) baseline_tag_index = defaultIndex; + return this+baseCoords[baseline_tag_index]; + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + baseCoords.sanitize (c, this))); + } + + protected: + Index defaultIndex; /* Index number of default baseline for this + * script — equals index position of baseline tag + * in baselineTags array of the BaseTagList */ + Array16OfOffset16To + baseCoords; /* Number of BaseCoord tables defined — should equal + * baseTagCount in the BaseTagList + * + * Array of offsets to BaseCoord tables, from beginning of + * BaseValues table — order matches baselineTags array in + * the BaseTagList */ + public: + DEFINE_SIZE_ARRAY (4, baseCoords); +}; + +struct BaseLangSysRecord +{ + int cmp (hb_tag_t key) const { return baseLangSysTag.cmp (key); } + + bool has_data () const { return baseLangSysTag; } + + const MinMax &get_min_max () const { return this+minMax; } + + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + minMax.sanitize (c, this))); + } + + protected: + Tag baseLangSysTag; /* 4-byte language system identification tag */ + Offset16To + minMax; /* Offset to MinMax table, from beginning + * of BaseScript table */ + public: + DEFINE_SIZE_STATIC (6); +}; + +struct BaseScript +{ + const MinMax &get_min_max (hb_tag_t language_tag) const + { + const BaseLangSysRecord& record = baseLangSysRecords.bsearch (language_tag); + return record.has_data () ? record.get_min_max () : this+defaultMinMax; + } + + const BaseCoord &get_base_coord (int baseline_tag_index) const + { return (this+baseValues).get_base_coord (baseline_tag_index); } + + bool has_data () const { return baseValues; } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + baseValues.sanitize (c, this) && + defaultMinMax.sanitize (c, this) && + baseLangSysRecords.sanitize (c, this))); + } + + protected: + Offset16To + baseValues; /* Offset to BaseValues table, from beginning + * of BaseScript table (may be NULL) */ + Offset16To + defaultMinMax; /* Offset to MinMax table, from beginning of + * BaseScript table (may be NULL) */ + SortedArray16Of + baseLangSysRecords; + /* Number of BaseLangSysRecords + * defined — may be zero (0) */ + + public: + DEFINE_SIZE_ARRAY (6, baseLangSysRecords); +}; + +struct BaseScriptList; +struct BaseScriptRecord +{ + int cmp (hb_tag_t key) const { return baseScriptTag.cmp (key); } + + bool has_data () const { return baseScriptTag; } + + const BaseScript &get_base_script (const BaseScriptList *list) const + { return list+baseScript; } + + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + baseScript.sanitize (c, base))); + } + + protected: + Tag baseScriptTag; /* 4-byte script identification tag */ + Offset16To + baseScript; /* Offset to BaseScript table, from beginning + * of BaseScriptList */ + + public: + DEFINE_SIZE_STATIC (6); +}; + +struct BaseScriptList +{ + const BaseScript &get_base_script (hb_tag_t script) const + { + const BaseScriptRecord *record = &baseScriptRecords.bsearch (script); + if (!record->has_data ()) record = &baseScriptRecords.bsearch (HB_TAG ('D','F','L','T')); + return record->has_data () ? record->get_base_script (this) : Null (BaseScript); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + baseScriptRecords.sanitize (c, this)); + } + + protected: + SortedArray16Of + baseScriptRecords; + + public: + DEFINE_SIZE_ARRAY (2, baseScriptRecords); +}; + +struct Axis +{ + bool get_baseline (hb_tag_t baseline_tag, + hb_tag_t script_tag, + hb_tag_t language_tag, + const BaseCoord **coord) const + { + const BaseScript &base_script = (this+baseScriptList).get_base_script (script_tag); + if (!base_script.has_data ()) + { + *coord = nullptr; + return false; + } + + if (likely (coord)) + { + unsigned int tag_index = 0; + if (!(this+baseTagList).bfind (baseline_tag, &tag_index)) + { + *coord = nullptr; + return false; + } + *coord = &base_script.get_base_coord (tag_index); + } + + return true; + } + + bool get_min_max (hb_tag_t script_tag, + hb_tag_t language_tag, + hb_tag_t feature_tag, + const BaseCoord **min_coord, + const BaseCoord **max_coord) const + { + const BaseScript &base_script = (this+baseScriptList).get_base_script (script_tag); + if (!base_script.has_data ()) + { + *min_coord = *max_coord = nullptr; + return false; + } + + base_script.get_min_max (language_tag).get_min_max (feature_tag, min_coord, max_coord); + + return true; + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + (this+baseTagList).sanitize (c) && + (this+baseScriptList).sanitize (c))); + } + + protected: + Offset16To> + baseTagList; /* Offset to BaseTagList table, from beginning + * of Axis table (may be NULL) + * Array of 4-byte baseline identification tags — must + * be in alphabetical order */ + Offset16To + baseScriptList; /* Offset to BaseScriptList table, from beginning + * of Axis table + * Array of BaseScriptRecords, in alphabetical order + * by baseScriptTag */ + + public: + DEFINE_SIZE_STATIC (4); +}; + +struct BASE +{ + static constexpr hb_tag_t tableTag = HB_OT_TAG_BASE; + + const Axis &get_axis (hb_direction_t direction) const + { return HB_DIRECTION_IS_VERTICAL (direction) ? this+vAxis : this+hAxis; } + + const VariationStore &get_var_store () const + { return version.to_int () < 0x00010001u ? Null (VariationStore) : this+varStore; } + + bool get_baseline (hb_font_t *font, + hb_tag_t baseline_tag, + hb_direction_t direction, + hb_tag_t script_tag, + hb_tag_t language_tag, + hb_position_t *base) const + { + const BaseCoord *base_coord = nullptr; + if (unlikely (!get_axis (direction).get_baseline (baseline_tag, script_tag, language_tag, &base_coord) || + !base_coord || !base_coord->has_data ())) + return false; + + if (likely (base)) + *base = base_coord->get_coord (font, get_var_store (), direction); + + return true; + } + + /* TODO: Expose this separately sometime? */ + bool get_min_max (hb_font_t *font, + hb_direction_t direction, + hb_tag_t script_tag, + hb_tag_t language_tag, + hb_tag_t feature_tag, + hb_position_t *min, + hb_position_t *max) + { + const BaseCoord *min_coord, *max_coord; + if (!get_axis (direction).get_min_max (script_tag, language_tag, feature_tag, + &min_coord, &max_coord)) + return false; + + const VariationStore &var_store = get_var_store (); + if (likely (min && min_coord)) *min = min_coord->get_coord (font, var_store, direction); + if (likely (max && max_coord)) *max = max_coord->get_coord (font, var_store, direction); + return true; + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + likely (version.major == 1) && + hAxis.sanitize (c, this) && + vAxis.sanitize (c, this) && + (version.to_int () < 0x00010001u || varStore.sanitize (c, this)))); + } + + protected: + FixedVersion<>version; /* Version of the BASE table */ + Offset16TohAxis; /* Offset to horizontal Axis table, from beginning + * of BASE table (may be NULL) */ + Offset16TovAxis; /* Offset to vertical Axis table, from beginning + * of BASE table (may be NULL) */ + Offset32To + varStore; /* Offset to the table of Item Variation + * Store--from beginning of BASE + * header (may be NULL). Introduced + * in version 0x00010001. */ + public: + DEFINE_SIZE_MIN (8); +}; + + +} /* namespace OT */ + + +#endif /* HB_OT_LAYOUT_BASE_TABLE_HH */ diff --git a/libs/harfbuzz/src/hb-ot-layout-common.hh b/libs/harfbuzz/src/hb-ot-layout-common.hh new file mode 100644 index 00000000..579abf01 --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-layout-common.hh @@ -0,0 +1,3607 @@ +/* + * Copyright © 2007,2008,2009 Red Hat, Inc. + * Copyright © 2010,2012 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. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_OT_LAYOUT_COMMON_HH +#define HB_OT_LAYOUT_COMMON_HH + +#include "hb.hh" +#include "hb-ot-layout.hh" +#include "hb-open-type.hh" +#include "hb-set.hh" +#include "hb-bimap.hh" + +#include "OT/Layout/Common/Coverage.hh" +#include "OT/Layout/types.hh" + +// TODO(garretrieger): cleanup these after migration. +using OT::Layout::Common::Coverage; +using OT::Layout::Common::RangeRecord; +using OT::Layout::SmallTypes; +using OT::Layout::MediumTypes; + +#ifndef HB_MAX_NESTING_LEVEL +#define HB_MAX_NESTING_LEVEL 64 +#endif +#ifndef HB_MAX_CONTEXT_LENGTH +#define HB_MAX_CONTEXT_LENGTH 64 +#endif +#ifndef HB_CLOSURE_MAX_STAGES +/* + * The maximum number of times a lookup can be applied during shaping. + * Used to limit the number of iterations of the closure algorithm. + * This must be larger than the number of times add_gsub_pause() is + * called in a collect_features call of any shaper. + */ +#define HB_CLOSURE_MAX_STAGES 12 +#endif + +#ifndef HB_MAX_SCRIPTS +#define HB_MAX_SCRIPTS 500 +#endif + +#ifndef HB_MAX_LANGSYS +#define HB_MAX_LANGSYS 2000 +#endif + +#ifndef HB_MAX_LANGSYS_FEATURE_COUNT +#define HB_MAX_LANGSYS_FEATURE_COUNT 50000 +#endif + +#ifndef HB_MAX_FEATURE_INDICES +#define HB_MAX_FEATURE_INDICES 1500 +#endif + +#ifndef HB_MAX_LOOKUP_VISIT_COUNT +#define HB_MAX_LOOKUP_VISIT_COUNT 35000 +#endif + + +namespace OT { + +template +static inline bool ClassDef_serialize (hb_serialize_context_t *c, + Iterator it); + +static bool ClassDef_remap_and_serialize ( + hb_serialize_context_t *c, + const hb_set_t &klasses, + bool use_class_zero, + hb_sorted_vector_t> &glyph_and_klass, /* IN/OUT */ + hb_map_t *klass_map /*IN/OUT*/); + +struct hb_collect_feature_substitutes_with_var_context_t +{ + const hb_map_t *axes_index_tag_map; + const hb_hashmap_t *axes_location; + hb_hashmap_t> *record_cond_idx_map; + hb_hashmap_t *feature_substitutes_map; + + // not stored in subset_plan + hb_set_t *feature_indices; + bool apply; + unsigned cur_record_idx; + hb_hashmap_t, unsigned> *conditionset_map; +}; + +struct hb_prune_langsys_context_t +{ + hb_prune_langsys_context_t (const void *table_, + hb_hashmap_t> *script_langsys_map_, + const hb_map_t *duplicate_feature_map_, + hb_set_t *new_collected_feature_indexes_) + :table (table_), + script_langsys_map (script_langsys_map_), + duplicate_feature_map (duplicate_feature_map_), + new_feature_indexes (new_collected_feature_indexes_), + script_count (0),langsys_feature_count (0) {} + + bool visitScript () + { return script_count++ < HB_MAX_SCRIPTS; } + + bool visitLangsys (unsigned feature_count) + { + langsys_feature_count += feature_count; + return langsys_feature_count < HB_MAX_LANGSYS_FEATURE_COUNT; + } + + public: + const void *table; + hb_hashmap_t> *script_langsys_map; + const hb_map_t *duplicate_feature_map; + hb_set_t *new_feature_indexes; + + private: + unsigned script_count; + unsigned langsys_feature_count; +}; + +struct hb_subset_layout_context_t : + hb_dispatch_context_t +{ + const char *get_name () { return "SUBSET_LAYOUT"; } + static return_t default_return_value () { return hb_empty_t (); } + + bool visitScript () + { + return script_count++ < HB_MAX_SCRIPTS; + } + + bool visitLangSys () + { + return langsys_count++ < HB_MAX_LANGSYS; + } + + bool visitFeatureIndex (int count) + { + feature_index_count += count; + return feature_index_count < HB_MAX_FEATURE_INDICES; + } + + bool visitLookupIndex() + { + lookup_index_count++; + return lookup_index_count < HB_MAX_LOOKUP_VISIT_COUNT; + } + + hb_subset_context_t *subset_context; + const hb_tag_t table_tag; + const hb_map_t *lookup_index_map; + const hb_hashmap_t> *script_langsys_map; + const hb_map_t *feature_index_map; + const hb_hashmap_t *feature_substitutes_map; + hb_hashmap_t> *feature_record_cond_idx_map; + + unsigned cur_script_index; + unsigned cur_feature_var_record_idx; + + hb_subset_layout_context_t (hb_subset_context_t *c_, + hb_tag_t tag_) : + subset_context (c_), + table_tag (tag_), + cur_script_index (0xFFFFu), + cur_feature_var_record_idx (0u), + script_count (0), + langsys_count (0), + feature_index_count (0), + lookup_index_count (0) + { + if (tag_ == HB_OT_TAG_GSUB) + { + lookup_index_map = c_->plan->gsub_lookups; + script_langsys_map = c_->plan->gsub_langsys; + feature_index_map = c_->plan->gsub_features; + feature_substitutes_map = c_->plan->gsub_feature_substitutes_map; + feature_record_cond_idx_map = c_->plan->user_axes_location->is_empty () ? nullptr : c_->plan->gsub_feature_record_cond_idx_map; + } + else + { + lookup_index_map = c_->plan->gpos_lookups; + script_langsys_map = c_->plan->gpos_langsys; + feature_index_map = c_->plan->gpos_features; + feature_substitutes_map = c_->plan->gpos_feature_substitutes_map; + feature_record_cond_idx_map = c_->plan->user_axes_location->is_empty () ? nullptr : c_->plan->gpos_feature_record_cond_idx_map; + } + } + + private: + unsigned script_count; + unsigned langsys_count; + unsigned feature_index_count; + unsigned lookup_index_count; +}; + +struct VariationStore; +struct hb_collect_variation_indices_context_t : + hb_dispatch_context_t +{ + template + return_t dispatch (const T &obj) { obj.collect_variation_indices (this); return hb_empty_t (); } + static return_t default_return_value () { return hb_empty_t (); } + + hb_set_t *layout_variation_indices; + hb_hashmap_t> *varidx_delta_map; + hb_font_t *font; + const VariationStore *var_store; + const hb_set_t *glyph_set; + const hb_map_t *gpos_lookups; + float *store_cache; + + hb_collect_variation_indices_context_t (hb_set_t *layout_variation_indices_, + hb_hashmap_t> *varidx_delta_map_, + hb_font_t *font_, + const VariationStore *var_store_, + const hb_set_t *glyph_set_, + const hb_map_t *gpos_lookups_, + float *store_cache_) : + layout_variation_indices (layout_variation_indices_), + varidx_delta_map (varidx_delta_map_), + font (font_), + var_store (var_store_), + glyph_set (glyph_set_), + gpos_lookups (gpos_lookups_), + store_cache (store_cache_) {} +}; + +template +struct subset_offset_array_t +{ + subset_offset_array_t (hb_subset_context_t *subset_context_, + OutputArray& out_, + const void *base_) : subset_context (subset_context_), + out (out_), base (base_) {} + + template + bool operator () (T&& offset) + { + auto snap = subset_context->serializer->snapshot (); + auto *o = out.serialize_append (subset_context->serializer); + if (unlikely (!o)) return false; + bool ret = o->serialize_subset (subset_context, offset, base); + if (!ret) + { + out.pop (); + subset_context->serializer->revert (snap); + } + return ret; + } + + private: + hb_subset_context_t *subset_context; + OutputArray &out; + const void *base; +}; + + +template +struct subset_offset_array_arg_t +{ + subset_offset_array_arg_t (hb_subset_context_t *subset_context_, + OutputArray& out_, + const void *base_, + Arg &&arg_) : subset_context (subset_context_), out (out_), + base (base_), arg (arg_) {} + + template + bool operator () (T&& offset) + { + auto snap = subset_context->serializer->snapshot (); + auto *o = out.serialize_append (subset_context->serializer); + if (unlikely (!o)) return false; + bool ret = o->serialize_subset (subset_context, offset, base, arg); + if (!ret) + { + out.pop (); + subset_context->serializer->revert (snap); + } + return ret; + } + + private: + hb_subset_context_t *subset_context; + OutputArray &out; + const void *base; + Arg &&arg; +}; + +/* + * Helper to subset an array of offsets. Subsets the thing pointed to by each offset + * and discards the offset in the array if the subset operation results in an empty + * thing. + */ +struct +{ + template + subset_offset_array_t + operator () (hb_subset_context_t *subset_context, OutputArray& out, + const void *base) const + { return subset_offset_array_t (subset_context, out, base); } + + /* Variant with one extra argument passed to serialize_subset */ + template + subset_offset_array_arg_t + operator () (hb_subset_context_t *subset_context, OutputArray& out, + const void *base, Arg &&arg) const + { return subset_offset_array_arg_t (subset_context, out, base, arg); } +} +HB_FUNCOBJ (subset_offset_array); + +template +struct subset_record_array_t +{ + subset_record_array_t (hb_subset_layout_context_t *c_, OutputArray* out_, + const void *base_) : subset_layout_context (c_), + out (out_), base (base_) {} + + template + void + operator () (T&& record) + { + auto snap = subset_layout_context->subset_context->serializer->snapshot (); + bool ret = record.subset (subset_layout_context, base); + if (!ret) subset_layout_context->subset_context->serializer->revert (snap); + else out->len++; + } + + private: + hb_subset_layout_context_t *subset_layout_context; + OutputArray *out; + const void *base; +}; + +template +struct subset_record_array_arg_t +{ + subset_record_array_arg_t (hb_subset_layout_context_t *c_, OutputArray* out_, + const void *base_, + Arg &&arg_) : subset_layout_context (c_), + out (out_), base (base_), arg (arg_) {} + + template + void + operator () (T&& record) + { + auto snap = subset_layout_context->subset_context->serializer->snapshot (); + bool ret = record.subset (subset_layout_context, base, arg); + if (!ret) subset_layout_context->subset_context->serializer->revert (snap); + else out->len++; + } + + private: + hb_subset_layout_context_t *subset_layout_context; + OutputArray *out; + const void *base; + Arg &&arg; +}; + +/* + * Helper to subset a RecordList/record array. Subsets each Record in the array and + * discards the record if the subset operation returns false. + */ +struct +{ + template + subset_record_array_t + operator () (hb_subset_layout_context_t *c, OutputArray* out, + const void *base) const + { return subset_record_array_t (c, out, base); } + + /* Variant with one extra argument passed to subset */ + template + subset_record_array_arg_t + operator () (hb_subset_layout_context_t *c, OutputArray* out, + const void *base, Arg &&arg) const + { return subset_record_array_arg_t (c, out, base, arg); } +} +HB_FUNCOBJ (subset_record_array); + + +template +struct serialize_math_record_array_t +{ + serialize_math_record_array_t (hb_serialize_context_t *serialize_context_, + OutputArray& out_, + const void *base_) : serialize_context (serialize_context_), + out (out_), base (base_) {} + + template + bool operator () (T&& record) + { + if (!serialize_context->copy (record, base)) return false; + out.len++; + return true; + } + + private: + hb_serialize_context_t *serialize_context; + OutputArray &out; + const void *base; +}; + +/* + * Helper to serialize an array of MATH records. + */ +struct +{ + template + serialize_math_record_array_t + operator () (hb_serialize_context_t *serialize_context, OutputArray& out, + const void *base) const + { return serialize_math_record_array_t (serialize_context, out, base); } + +} +HB_FUNCOBJ (serialize_math_record_array); + +/* + * + * OpenType Layout Common Table Formats + * + */ + + +/* + * Script, ScriptList, LangSys, Feature, FeatureList, Lookup, LookupList + */ + +struct IndexArray : Array16Of +{ + bool intersects (const hb_map_t *indexes) const + { return hb_any (*this, indexes); } + + template + void serialize (hb_serialize_context_t *c, + hb_subset_layout_context_t *l, + Iterator it) + { + if (!it) return; + if (unlikely (!c->extend_min ((*this)))) return; + + for (const auto _ : it) + { + if (!l->visitLookupIndex()) break; + + Index i; + i = _; + c->copy (i); + this->len++; + } + } + + unsigned int get_indexes (unsigned int start_offset, + unsigned int *_count /* IN/OUT */, + unsigned int *_indexes /* OUT */) const + { + if (_count) + { + + this->sub_array (start_offset, _count) + | hb_sink (hb_array (_indexes, *_count)) + ; + } + return this->len; + } + + void add_indexes_to (hb_set_t* output /* OUT */) const + { + output->add_array (as_array ()); + } +}; + + +/* https://docs.microsoft.com/en-us/typography/opentype/spec/features_pt#size */ +struct FeatureParamsSize +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (unlikely (!c->check_struct (this))) return_trace (false); + + /* This subtable has some "history", if you will. Some earlier versions of + * Adobe tools calculated the offset of the FeatureParams subtable from the + * beginning of the FeatureList table! Now, that is dealt with in the + * Feature implementation. But we still need to be able to tell junk from + * real data. Note: We don't check that the nameID actually exists. + * + * Read Roberts wrote on 9/15/06 on opentype-list@indx.co.uk : + * + * Yes, it is correct that a new version of the AFDKO (version 2.0) will be + * coming out soon, and that the makeotf program will build a font with a + * 'size' feature that is correct by the specification. + * + * The specification for this feature tag is in the "OpenType Layout Tag + * Registry". You can see a copy of this at: + * https://docs.microsoft.com/en-us/typography/opentype/spec/features_pt#tag-size + * + * Here is one set of rules to determine if the 'size' feature is built + * correctly, or as by the older versions of MakeOTF. You may be able to do + * better. + * + * Assume that the offset to the size feature is according to specification, + * and make the following value checks. If it fails, assume the size + * feature is calculated as versions of MakeOTF before the AFDKO 2.0 built it. + * If this fails, reject the 'size' feature. The older makeOTF's calculated the + * offset from the beginning of the FeatureList table, rather than from the + * beginning of the 'size' Feature table. + * + * If "design size" == 0: + * fails check + * + * Else if ("subfamily identifier" == 0 and + * "range start" == 0 and + * "range end" == 0 and + * "range start" == 0 and + * "menu name ID" == 0) + * passes check: this is the format used when there is a design size + * specified, but there is no recommended size range. + * + * Else if ("design size" < "range start" or + * "design size" > "range end" or + * "range end" <= "range start" or + * "menu name ID" < 256 or + * "menu name ID" > 32767 or + * menu name ID is not a name ID which is actually in the name table) + * fails test + * Else + * passes test. + */ + + if (!designSize) + return_trace (false); + else if (subfamilyID == 0 && + subfamilyNameID == 0 && + rangeStart == 0 && + rangeEnd == 0) + return_trace (true); + else if (designSize < rangeStart || + designSize > rangeEnd || + subfamilyNameID < 256 || + subfamilyNameID > 32767) + return_trace (false); + else + return_trace (true); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + return_trace ((bool) c->serializer->embed (*this)); + } + + HBUINT16 designSize; /* Represents the design size in 720/inch + * units (decipoints). The design size entry + * must be non-zero. When there is a design + * size but no recommended size range, the + * rest of the array will consist of zeros. */ + HBUINT16 subfamilyID; /* Has no independent meaning, but serves + * as an identifier that associates fonts + * in a subfamily. All fonts which share a + * Preferred or Font Family name and which + * differ only by size range shall have the + * same subfamily value, and no fonts which + * differ in weight or style shall have the + * same subfamily value. If this value is + * zero, the remaining fields in the array + * will be ignored. */ + NameID subfamilyNameID;/* If the preceding value is non-zero, this + * value must be set in the range 256 - 32767 + * (inclusive). It records the value of a + * field in the name table, which must + * contain English-language strings encoded + * in Windows Unicode and Macintosh Roman, + * and may contain additional strings + * localized to other scripts and languages. + * Each of these strings is the name an + * application should use, in combination + * with the family name, to represent the + * subfamily in a menu. Applications will + * choose the appropriate version based on + * their selection criteria. */ + HBUINT16 rangeStart; /* Large end of the recommended usage range + * (inclusive), stored in 720/inch units + * (decipoints). */ + HBUINT16 rangeEnd; /* Small end of the recommended usage range + (exclusive), stored in 720/inch units + * (decipoints). */ + public: + DEFINE_SIZE_STATIC (10); +}; + +/* https://docs.microsoft.com/en-us/typography/opentype/spec/features_pt#ssxx */ +struct FeatureParamsStylisticSet +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + /* Right now minorVersion is at zero. Which means, any table supports + * the uiNameID field. */ + return_trace (c->check_struct (this)); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + return_trace ((bool) c->serializer->embed (*this)); + } + + HBUINT16 version; /* (set to 0): This corresponds to a “minor” + * version number. Additional data may be + * added to the end of this Feature Parameters + * table in the future. */ + + NameID uiNameID; /* The 'name' table name ID that specifies a + * string (or strings, for multiple languages) + * for a user-interface label for this + * feature. The values of uiLabelNameId and + * sampleTextNameId are expected to be in the + * font-specific name ID range (256-32767), + * though that is not a requirement in this + * Feature Parameters specification. The + * user-interface label for the feature can + * be provided in multiple languages. An + * English string should be included as a + * fallback. The string should be kept to a + * minimal length to fit comfortably with + * different application interfaces. */ + public: + DEFINE_SIZE_STATIC (4); +}; + +/* https://docs.microsoft.com/en-us/typography/opentype/spec/features_ae#cv01-cv99 */ +struct FeatureParamsCharacterVariants +{ + unsigned + get_characters (unsigned start_offset, unsigned *char_count, hb_codepoint_t *chars) const + { + if (char_count) + { + + characters.sub_array (start_offset, char_count) + | hb_sink (hb_array (chars, *char_count)) + ; + } + return characters.len; + } + + unsigned get_size () const + { return min_size + characters.len * HBUINT24::static_size; } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + return_trace ((bool) c->serializer->embed (*this)); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + characters.sanitize (c)); + } + + HBUINT16 format; /* Format number is set to 0. */ + NameID featUILableNameID; /* The ‘name’ table name ID that + * specifies a string (or strings, + * for multiple languages) for a + * user-interface label for this + * feature. (May be NULL.) */ + NameID featUITooltipTextNameID;/* The ‘name’ table name ID that + * specifies a string (or strings, + * for multiple languages) that an + * application can use for tooltip + * text for this feature. (May be + * nullptr.) */ + NameID sampleTextNameID; /* The ‘name’ table name ID that + * specifies sample text that + * illustrates the effect of this + * feature. (May be NULL.) */ + HBUINT16 numNamedParameters; /* Number of named parameters. (May + * be zero.) */ + NameID firstParamUILabelNameID;/* The first ‘name’ table name ID + * used to specify strings for + * user-interface labels for the + * feature parameters. (Must be zero + * if numParameters is zero.) */ + Array16Of + characters; /* Array of the Unicode Scalar Value + * of the characters for which this + * feature provides glyph variants. + * (May be zero.) */ + public: + DEFINE_SIZE_ARRAY (14, characters); +}; + +struct FeatureParams +{ + bool sanitize (hb_sanitize_context_t *c, hb_tag_t tag) const + { +#ifdef HB_NO_LAYOUT_FEATURE_PARAMS + return true; +#endif + TRACE_SANITIZE (this); + if (tag == HB_TAG ('s','i','z','e')) + return_trace (u.size.sanitize (c)); + if ((tag & 0xFFFF0000u) == HB_TAG ('s','s','\0','\0')) /* ssXX */ + return_trace (u.stylisticSet.sanitize (c)); + if ((tag & 0xFFFF0000u) == HB_TAG ('c','v','\0','\0')) /* cvXX */ + return_trace (u.characterVariants.sanitize (c)); + return_trace (true); + } + + bool subset (hb_subset_context_t *c, const Tag* tag) const + { + TRACE_SUBSET (this); + if (!tag) return_trace (false); + if (*tag == HB_TAG ('s','i','z','e')) + return_trace (u.size.subset (c)); + if ((*tag & 0xFFFF0000u) == HB_TAG ('s','s','\0','\0')) /* ssXX */ + return_trace (u.stylisticSet.subset (c)); + if ((*tag & 0xFFFF0000u) == HB_TAG ('c','v','\0','\0')) /* cvXX */ + return_trace (u.characterVariants.subset (c)); + return_trace (false); + } + +#ifndef HB_NO_LAYOUT_FEATURE_PARAMS + const FeatureParamsSize& get_size_params (hb_tag_t tag) const + { + if (tag == HB_TAG ('s','i','z','e')) + return u.size; + return Null (FeatureParamsSize); + } + const FeatureParamsStylisticSet& get_stylistic_set_params (hb_tag_t tag) const + { + if ((tag & 0xFFFF0000u) == HB_TAG ('s','s','\0','\0')) /* ssXX */ + return u.stylisticSet; + return Null (FeatureParamsStylisticSet); + } + const FeatureParamsCharacterVariants& get_character_variants_params (hb_tag_t tag) const + { + if ((tag & 0xFFFF0000u) == HB_TAG ('c','v','\0','\0')) /* cvXX */ + return u.characterVariants; + return Null (FeatureParamsCharacterVariants); + } +#endif + + private: + union { + FeatureParamsSize size; + FeatureParamsStylisticSet stylisticSet; + FeatureParamsCharacterVariants characterVariants; + } u; + public: + DEFINE_SIZE_MIN (0); +}; + +struct Record_sanitize_closure_t { + hb_tag_t tag; + const void *list_base; +}; + +struct Feature +{ + unsigned int get_lookup_count () const + { return lookupIndex.len; } + hb_tag_t get_lookup_index (unsigned int i) const + { return lookupIndex[i]; } + unsigned int get_lookup_indexes (unsigned int start_index, + unsigned int *lookup_count /* IN/OUT */, + unsigned int *lookup_tags /* OUT */) const + { return lookupIndex.get_indexes (start_index, lookup_count, lookup_tags); } + void add_lookup_indexes_to (hb_set_t *lookup_indexes) const + { lookupIndex.add_indexes_to (lookup_indexes); } + + const FeatureParams &get_feature_params () const + { return this+featureParams; } + + bool intersects_lookup_indexes (const hb_map_t *lookup_indexes) const + { return lookupIndex.intersects (lookup_indexes); } + + bool subset (hb_subset_context_t *c, + hb_subset_layout_context_t *l, + const Tag *tag = nullptr) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->start_embed (*this); + if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false); + + out->featureParams.serialize_subset (c, featureParams, this, tag); + + auto it = + + hb_iter (lookupIndex) + | hb_filter (l->lookup_index_map) + | hb_map (l->lookup_index_map) + ; + + out->lookupIndex.serialize (c->serializer, l, it); + // The decision to keep or drop this feature is already made before we get here + // so always retain it. + return_trace (true); + } + + bool sanitize (hb_sanitize_context_t *c, + const Record_sanitize_closure_t *closure = nullptr) const + { + TRACE_SANITIZE (this); + if (unlikely (!(c->check_struct (this) && lookupIndex.sanitize (c)))) + return_trace (false); + + /* Some earlier versions of Adobe tools calculated the offset of the + * FeatureParams subtable from the beginning of the FeatureList table! + * + * If sanitizing "failed" for the FeatureParams subtable, try it with the + * alternative location. We would know sanitize "failed" if old value + * of the offset was non-zero, but it's zeroed now. + * + * Only do this for the 'size' feature, since at the time of the faulty + * Adobe tools, only the 'size' feature had FeatureParams defined. + */ + + if (likely (featureParams.is_null ())) + return_trace (true); + + unsigned int orig_offset = featureParams; + if (unlikely (!featureParams.sanitize (c, this, closure ? closure->tag : HB_TAG_NONE))) + return_trace (false); + + if (featureParams == 0 && closure && + closure->tag == HB_TAG ('s','i','z','e') && + closure->list_base && closure->list_base < this) + { + unsigned int new_offset_int = orig_offset - + (((char *) this) - ((char *) closure->list_base)); + + Offset16To new_offset; + /* Check that it would not overflow. */ + new_offset = new_offset_int; + if (new_offset == new_offset_int && + c->try_set (&featureParams, new_offset_int) && + !featureParams.sanitize (c, this, closure ? closure->tag : HB_TAG_NONE)) + return_trace (false); + } + + return_trace (true); + } + + Offset16To + featureParams; /* Offset to Feature Parameters table (if one + * has been defined for the feature), relative + * to the beginning of the Feature Table; = Null + * if not required */ + IndexArray lookupIndex; /* Array of LookupList indices */ + public: + DEFINE_SIZE_ARRAY_SIZED (4, lookupIndex); +}; + +template +struct Record +{ + int cmp (hb_tag_t a) const { return tag.cmp (a); } + + bool subset (hb_subset_layout_context_t *c, const void *base, const void *f_sub = nullptr) const + { + TRACE_SUBSET (this); + auto *out = c->subset_context->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + + if (!f_sub) + return_trace (out->offset.serialize_subset (c->subset_context, offset, base, c, &tag)); + + const Feature& f = *reinterpret_cast (f_sub); + auto *s = c->subset_context->serializer; + s->push (); + + out->offset = 0; + bool ret = f.subset (c->subset_context, c, &tag); + if (ret) + s->add_link (out->offset, s->pop_pack ()); + else + s->pop_discard (); + + return_trace (ret); + } + + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + const Record_sanitize_closure_t closure = {tag, base}; + return_trace (c->check_struct (this) && offset.sanitize (c, base, &closure)); + } + + Tag tag; /* 4-byte Tag identifier */ + Offset16To + offset; /* Offset from beginning of object holding + * the Record */ + public: + DEFINE_SIZE_STATIC (6); +}; + +template +struct RecordArrayOf : SortedArray16Of> +{ + const Offset16To& get_offset (unsigned int i) const + { return (*this)[i].offset; } + Offset16To& get_offset (unsigned int i) + { return (*this)[i].offset; } + const Tag& get_tag (unsigned int i) const + { return (*this)[i].tag; } + unsigned int get_tags (unsigned int start_offset, + unsigned int *record_count /* IN/OUT */, + hb_tag_t *record_tags /* OUT */) const + { + if (record_count) + { + + this->sub_array (start_offset, record_count) + | hb_map (&Record::tag) + | hb_sink (hb_array (record_tags, *record_count)) + ; + } + return this->len; + } + bool find_index (hb_tag_t tag, unsigned int *index) const + { + return this->bfind (tag, index, HB_NOT_FOUND_STORE, Index::NOT_FOUND_INDEX); + } +}; + +template +struct RecordListOf : RecordArrayOf +{ + const Type& operator [] (unsigned int i) const + { return this+this->get_offset (i); } + + bool subset (hb_subset_context_t *c, + hb_subset_layout_context_t *l) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + + + this->iter () + | hb_apply (subset_record_array (l, out, this)) + ; + return_trace (true); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (RecordArrayOf::sanitize (c, this)); + } +}; + +struct RecordListOfFeature : RecordListOf +{ + bool subset (hb_subset_context_t *c, + hb_subset_layout_context_t *l) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->start_embed (*this); + if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false); + + unsigned count = this->len; + + + hb_zip (*this, hb_range (count)) + | hb_filter (l->feature_index_map, hb_second) + | hb_apply ([l, out, this] (const hb_pair_t&, unsigned>& _) + { + const Feature *f_sub = nullptr; + const Feature **f = nullptr; + if (l->feature_substitutes_map->has (_.second, &f)) + f_sub = *f; + + subset_record_array (l, out, this, f_sub) (_.first); + }) + ; + + return_trace (true); + } +}; + +typedef RecordListOf FeatureList; + + +struct LangSys +{ + unsigned int get_feature_count () const + { return featureIndex.len; } + hb_tag_t get_feature_index (unsigned int i) const + { return featureIndex[i]; } + unsigned int get_feature_indexes (unsigned int start_offset, + unsigned int *feature_count /* IN/OUT */, + unsigned int *feature_indexes /* OUT */) const + { return featureIndex.get_indexes (start_offset, feature_count, feature_indexes); } + void add_feature_indexes_to (hb_set_t *feature_indexes) const + { featureIndex.add_indexes_to (feature_indexes); } + + bool has_required_feature () const { return reqFeatureIndex != 0xFFFFu; } + unsigned int get_required_feature_index () const + { + if (reqFeatureIndex == 0xFFFFu) + return Index::NOT_FOUND_INDEX; + return reqFeatureIndex; + } + + LangSys* copy (hb_serialize_context_t *c) const + { + TRACE_SERIALIZE (this); + return_trace (c->embed (*this)); + } + + bool compare (const LangSys& o, const hb_map_t *feature_index_map) const + { + if (reqFeatureIndex != o.reqFeatureIndex) + return false; + + auto iter = + + hb_iter (featureIndex) + | hb_filter (feature_index_map) + | hb_map (feature_index_map) + ; + + auto o_iter = + + hb_iter (o.featureIndex) + | hb_filter (feature_index_map) + | hb_map (feature_index_map) + ; + + for (; iter && o_iter; iter++, o_iter++) + { + unsigned a = *iter; + unsigned b = *o_iter; + if (a != b) return false; + } + + if (iter || o_iter) return false; + + return true; + } + + void collect_features (hb_prune_langsys_context_t *c) const + { + if (!has_required_feature () && !get_feature_count ()) return; + if (has_required_feature () && + c->duplicate_feature_map->has (reqFeatureIndex)) + c->new_feature_indexes->add (get_required_feature_index ()); + + + hb_iter (featureIndex) + | hb_filter (c->duplicate_feature_map) + | hb_sink (c->new_feature_indexes) + ; + } + + bool subset (hb_subset_context_t *c, + hb_subset_layout_context_t *l, + const Tag *tag = nullptr) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->start_embed (*this); + if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false); + + const unsigned *v; + out->reqFeatureIndex = l->feature_index_map->has (reqFeatureIndex, &v) ? *v : 0xFFFFu; + + if (!l->visitFeatureIndex (featureIndex.len)) + return_trace (false); + + auto it = + + hb_iter (featureIndex) + | hb_filter (l->feature_index_map) + | hb_map (l->feature_index_map) + ; + + bool ret = bool (it); + out->featureIndex.serialize (c->serializer, l, it); + return_trace (ret); + } + + bool sanitize (hb_sanitize_context_t *c, + const Record_sanitize_closure_t * = nullptr) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && featureIndex.sanitize (c)); + } + + Offset16 lookupOrderZ; /* = Null (reserved for an offset to a + * reordering table) */ + HBUINT16 reqFeatureIndex;/* Index of a feature required for this + * language system--if no required features + * = 0xFFFFu */ + IndexArray featureIndex; /* Array of indices into the FeatureList */ + public: + DEFINE_SIZE_ARRAY_SIZED (6, featureIndex); +}; +DECLARE_NULL_NAMESPACE_BYTES (OT, LangSys); + +struct Script +{ + unsigned int get_lang_sys_count () const + { return langSys.len; } + const Tag& get_lang_sys_tag (unsigned int i) const + { return langSys.get_tag (i); } + unsigned int get_lang_sys_tags (unsigned int start_offset, + unsigned int *lang_sys_count /* IN/OUT */, + hb_tag_t *lang_sys_tags /* OUT */) const + { return langSys.get_tags (start_offset, lang_sys_count, lang_sys_tags); } + const LangSys& get_lang_sys (unsigned int i) const + { + if (i == Index::NOT_FOUND_INDEX) return get_default_lang_sys (); + return this+langSys[i].offset; + } + bool find_lang_sys_index (hb_tag_t tag, unsigned int *index) const + { return langSys.find_index (tag, index); } + + bool has_default_lang_sys () const { return defaultLangSys != 0; } + const LangSys& get_default_lang_sys () const { return this+defaultLangSys; } + + void prune_langsys (hb_prune_langsys_context_t *c, + unsigned script_index) const + { + if (!has_default_lang_sys () && !get_lang_sys_count ()) return; + if (!c->visitScript ()) return; + + if (!c->script_langsys_map->has (script_index)) + { + if (unlikely (!c->script_langsys_map->set (script_index, hb::unique_ptr {hb_set_create ()}))) + return; + } + + unsigned langsys_count = get_lang_sys_count (); + if (has_default_lang_sys ()) + { + //only collect features from non-redundant langsys + const LangSys& d = get_default_lang_sys (); + if (c->visitLangsys (d.get_feature_count ())) { + d.collect_features (c); + } + + for (auto _ : + hb_zip (langSys, hb_range (langsys_count))) + { + const LangSys& l = this+_.first.offset; + if (!c->visitLangsys (l.get_feature_count ())) continue; + if (l.compare (d, c->duplicate_feature_map)) continue; + + l.collect_features (c); + c->script_langsys_map->get (script_index)->add (_.second); + } + } + else + { + for (auto _ : + hb_zip (langSys, hb_range (langsys_count))) + { + const LangSys& l = this+_.first.offset; + if (!c->visitLangsys (l.get_feature_count ())) continue; + l.collect_features (c); + c->script_langsys_map->get (script_index)->add (_.second); + } + } + } + + bool subset (hb_subset_context_t *c, + hb_subset_layout_context_t *l, + const Tag *tag) const + { + TRACE_SUBSET (this); + if (!l->visitScript ()) return_trace (false); + if (tag && !c->plan->layout_scripts->has (*tag)) + return false; + + auto *out = c->serializer->start_embed (*this); + if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false); + + bool defaultLang = false; + if (has_default_lang_sys ()) + { + c->serializer->push (); + const LangSys& ls = this+defaultLangSys; + bool ret = ls.subset (c, l); + if (!ret && tag && *tag != HB_TAG ('D', 'F', 'L', 'T')) + { + c->serializer->pop_discard (); + out->defaultLangSys = 0; + } + else + { + c->serializer->add_link (out->defaultLangSys, c->serializer->pop_pack ()); + defaultLang = true; + } + } + + const hb_set_t *active_langsys = l->script_langsys_map->get (l->cur_script_index); + if (active_langsys) + { + unsigned count = langSys.len; + + hb_zip (langSys, hb_range (count)) + | hb_filter (active_langsys, hb_second) + | hb_map (hb_first) + | hb_filter ([=] (const Record& record) {return l->visitLangSys (); }) + | hb_apply (subset_record_array (l, &(out->langSys), this)) + ; + } + + return_trace (bool (out->langSys.len) || defaultLang || l->table_tag == HB_OT_TAG_GSUB); + } + + bool sanitize (hb_sanitize_context_t *c, + const Record_sanitize_closure_t * = nullptr) const + { + TRACE_SANITIZE (this); + return_trace (defaultLangSys.sanitize (c, this) && langSys.sanitize (c, this)); + } + + protected: + Offset16To + defaultLangSys; /* Offset to DefaultLangSys table--from + * beginning of Script table--may be Null */ + RecordArrayOf + langSys; /* Array of LangSysRecords--listed + * alphabetically by LangSysTag */ + public: + DEFINE_SIZE_ARRAY_SIZED (4, langSys); +}; + +struct RecordListOfScript : RecordListOfINT32_MAX. + * + * Return value: the total number of kern values available or zero + * + * Since: 3.4.0 + **/ +unsigned int +hb_ot_math_get_glyph_kernings (hb_font_t *font, + hb_codepoint_t glyph, + hb_ot_math_kern_t kern, + unsigned int start_offset, + unsigned int *entries_count, /* IN/OUT */ + hb_ot_math_kern_entry_t *kern_entries /* OUT */) +{ + return font->face->table.MATH->get_glyph_info().get_kernings (glyph, + kern, + start_offset, + entries_count, + kern_entries, + font); +} + +/** + * hb_ot_math_get_glyph_variants: + * @font: #hb_font_t to work upon + * @glyph: The index of the glyph to stretch + * @direction: The direction of the stretching (horizontal or vertical) + * @start_offset: offset of the first variant to retrieve + * @variants_count: (inout): Input = the maximum number of variants to return; + * Output = the actual number of variants returned + * @variants: (out) (array length=variants_count): array of variants returned + * + * Fetches the MathGlyphConstruction for the specified font, glyph index, and + * direction. The corresponding list of size variants is returned as a list of + * #hb_ot_math_glyph_variant_t structs. + * + * The @direction parameter is only used to select between horizontal + * or vertical directions for the construction. Even though all #hb_direction_t + * values are accepted, only the result of #HB_DIRECTION_IS_HORIZONTAL is + * considered. + * + * Return value: the total number of size variants available or zero + * + * Since: 1.3.3 + **/ +unsigned int +hb_ot_math_get_glyph_variants (hb_font_t *font, + hb_codepoint_t glyph, + hb_direction_t direction, + unsigned int start_offset, + unsigned int *variants_count, /* IN/OUT */ + hb_ot_math_glyph_variant_t *variants /* OUT */) +{ + return font->face->table.MATH->get_variants().get_glyph_variants (glyph, direction, font, + start_offset, + variants_count, + variants); +} + +/** + * hb_ot_math_get_min_connector_overlap: + * @font: #hb_font_t to work upon + * @direction: direction of the stretching (horizontal or vertical) + * + * Fetches the MathVariants table for the specified font and returns the + * minimum overlap of connecting glyphs that are required to draw a glyph + * assembly in the specified direction. + * + * The @direction parameter is only used to select between horizontal + * or vertical directions for the construction. Even though all #hb_direction_t + * values are accepted, only the result of #HB_DIRECTION_IS_HORIZONTAL is + * considered. + * + * Return value: requested minimum connector overlap or zero + * + * Since: 1.3.3 + **/ +hb_position_t +hb_ot_math_get_min_connector_overlap (hb_font_t *font, + hb_direction_t direction) +{ + return font->face->table.MATH->get_variants().get_min_connector_overlap (direction, font); +} + +/** + * hb_ot_math_get_glyph_assembly: + * @font: #hb_font_t to work upon + * @glyph: The index of the glyph to stretch + * @direction: direction of the stretching (horizontal or vertical) + * @start_offset: offset of the first glyph part to retrieve + * @parts_count: (inout): Input = maximum number of glyph parts to return; + * Output = actual number of parts returned + * @parts: (out) (array length=parts_count): the glyph parts returned + * @italics_correction: (out): italics correction of the glyph assembly + * + * Fetches the GlyphAssembly for the specified font, glyph index, and direction. + * Returned are a list of #hb_ot_math_glyph_part_t glyph parts that can be + * used to draw the glyph and an italics-correction value (if one is defined + * in the font). + * + * The @direction parameter is only used to select between horizontal + * or vertical directions for the construction. Even though all #hb_direction_t + * values are accepted, only the result of #HB_DIRECTION_IS_HORIZONTAL is + * considered. + * + * Return value: the total number of parts in the glyph assembly + * + * Since: 1.3.3 + **/ +unsigned int +hb_ot_math_get_glyph_assembly (hb_font_t *font, + hb_codepoint_t glyph, + hb_direction_t direction, + unsigned int start_offset, + unsigned int *parts_count, /* IN/OUT */ + hb_ot_math_glyph_part_t *parts, /* OUT */ + hb_position_t *italics_correction /* OUT */) +{ + return font->face->table.MATH->get_variants().get_glyph_parts (glyph, + direction, + font, + start_offset, + parts_count, + parts, + italics_correction); +} + + +#endif diff --git a/libs/harfbuzz/src/hb-ot-math.h b/libs/harfbuzz/src/hb-ot-math.h new file mode 100644 index 00000000..1378a063 --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-math.h @@ -0,0 +1,333 @@ +/* + * Copyright © 2016 Igalia S.L. + * + * 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. + * + * Igalia Author(s): Frédéric Wang + */ + +#if !defined(HB_OT_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR) +#error "Include instead." +#endif + +#ifndef HB_OT_MATH_H +#define HB_OT_MATH_H + +#include "hb.h" + +HB_BEGIN_DECLS + + +/* + * MATH + */ + +/** + * HB_OT_TAG_MATH: + * + * OpenType [Mathematical Typesetting Table](https://docs.microsoft.com/en-us/typography/opentype/spec/math). + * + * Since: 1.3.3 + */ +#define HB_OT_TAG_MATH HB_TAG('M','A','T','H') + +/** + * HB_OT_TAG_MATH_SCRIPT: + * + * OpenType script tag, `math`, for features specific to math shaping. + * + * #HB_OT_TAG_MATH_SCRIPT is not a valid #hb_script_t and should only be + * used with functions that accept raw OpenType script tags, such as + * #hb_ot_layout_collect_features. In other cases, #HB_SCRIPT_MATH should be + * used instead. + * + * Since: 3.4.0 + */ +#define HB_OT_TAG_MATH_SCRIPT HB_TAG('m','a','t','h') + +/* Types */ + +/** + * hb_ot_math_constant_t: + * @HB_OT_MATH_CONSTANT_SCRIPT_PERCENT_SCALE_DOWN: scriptPercentScaleDown + * @HB_OT_MATH_CONSTANT_SCRIPT_SCRIPT_PERCENT_SCALE_DOWN: scriptScriptPercentScaleDown + * @HB_OT_MATH_CONSTANT_DELIMITED_SUB_FORMULA_MIN_HEIGHT: delimitedSubFormulaMinHeight + * @HB_OT_MATH_CONSTANT_DISPLAY_OPERATOR_MIN_HEIGHT: displayOperatorMinHeight + * @HB_OT_MATH_CONSTANT_MATH_LEADING: mathLeading + * @HB_OT_MATH_CONSTANT_AXIS_HEIGHT: axisHeight + * @HB_OT_MATH_CONSTANT_ACCENT_BASE_HEIGHT: accentBaseHeight + * @HB_OT_MATH_CONSTANT_FLATTENED_ACCENT_BASE_HEIGHT: flattenedAccentBaseHeight + * @HB_OT_MATH_CONSTANT_SUBSCRIPT_SHIFT_DOWN: subscriptShiftDown + * @HB_OT_MATH_CONSTANT_SUBSCRIPT_TOP_MAX: subscriptTopMax + * @HB_OT_MATH_CONSTANT_SUBSCRIPT_BASELINE_DROP_MIN: subscriptBaselineDropMin + * @HB_OT_MATH_CONSTANT_SUPERSCRIPT_SHIFT_UP: superscriptShiftUp + * @HB_OT_MATH_CONSTANT_SUPERSCRIPT_SHIFT_UP_CRAMPED: superscriptShiftUpCramped + * @HB_OT_MATH_CONSTANT_SUPERSCRIPT_BOTTOM_MIN: superscriptBottomMin + * @HB_OT_MATH_CONSTANT_SUPERSCRIPT_BASELINE_DROP_MAX: superscriptBaselineDropMax + * @HB_OT_MATH_CONSTANT_SUB_SUPERSCRIPT_GAP_MIN: subSuperscriptGapMin + * @HB_OT_MATH_CONSTANT_SUPERSCRIPT_BOTTOM_MAX_WITH_SUBSCRIPT: superscriptBottomMaxWithSubscript + * @HB_OT_MATH_CONSTANT_SPACE_AFTER_SCRIPT: spaceAfterScript + * @HB_OT_MATH_CONSTANT_UPPER_LIMIT_GAP_MIN: upperLimitGapMin + * @HB_OT_MATH_CONSTANT_UPPER_LIMIT_BASELINE_RISE_MIN: upperLimitBaselineRiseMin + * @HB_OT_MATH_CONSTANT_LOWER_LIMIT_GAP_MIN: lowerLimitGapMin + * @HB_OT_MATH_CONSTANT_LOWER_LIMIT_BASELINE_DROP_MIN: lowerLimitBaselineDropMin + * @HB_OT_MATH_CONSTANT_STACK_TOP_SHIFT_UP: stackTopShiftUp + * @HB_OT_MATH_CONSTANT_STACK_TOP_DISPLAY_STYLE_SHIFT_UP: stackTopDisplayStyleShiftUp + * @HB_OT_MATH_CONSTANT_STACK_BOTTOM_SHIFT_DOWN: stackBottomShiftDown + * @HB_OT_MATH_CONSTANT_STACK_BOTTOM_DISPLAY_STYLE_SHIFT_DOWN: stackBottomDisplayStyleShiftDown + * @HB_OT_MATH_CONSTANT_STACK_GAP_MIN: stackGapMin + * @HB_OT_MATH_CONSTANT_STACK_DISPLAY_STYLE_GAP_MIN: stackDisplayStyleGapMin + * @HB_OT_MATH_CONSTANT_STRETCH_STACK_TOP_SHIFT_UP: stretchStackTopShiftUp + * @HB_OT_MATH_CONSTANT_STRETCH_STACK_BOTTOM_SHIFT_DOWN: stretchStackBottomShiftDown + * @HB_OT_MATH_CONSTANT_STRETCH_STACK_GAP_ABOVE_MIN: stretchStackGapAboveMin + * @HB_OT_MATH_CONSTANT_STRETCH_STACK_GAP_BELOW_MIN: stretchStackGapBelowMin + * @HB_OT_MATH_CONSTANT_FRACTION_NUMERATOR_SHIFT_UP: fractionNumeratorShiftUp + * @HB_OT_MATH_CONSTANT_FRACTION_NUMERATOR_DISPLAY_STYLE_SHIFT_UP: fractionNumeratorDisplayStyleShiftUp + * @HB_OT_MATH_CONSTANT_FRACTION_DENOMINATOR_SHIFT_DOWN: fractionDenominatorShiftDown + * @HB_OT_MATH_CONSTANT_FRACTION_DENOMINATOR_DISPLAY_STYLE_SHIFT_DOWN: fractionDenominatorDisplayStyleShiftDown + * @HB_OT_MATH_CONSTANT_FRACTION_NUMERATOR_GAP_MIN: fractionNumeratorGapMin + * @HB_OT_MATH_CONSTANT_FRACTION_NUM_DISPLAY_STYLE_GAP_MIN: fractionNumDisplayStyleGapMin + * @HB_OT_MATH_CONSTANT_FRACTION_RULE_THICKNESS: fractionRuleThickness + * @HB_OT_MATH_CONSTANT_FRACTION_DENOMINATOR_GAP_MIN: fractionDenominatorGapMin + * @HB_OT_MATH_CONSTANT_FRACTION_DENOM_DISPLAY_STYLE_GAP_MIN: fractionDenomDisplayStyleGapMin + * @HB_OT_MATH_CONSTANT_SKEWED_FRACTION_HORIZONTAL_GAP: skewedFractionHorizontalGap + * @HB_OT_MATH_CONSTANT_SKEWED_FRACTION_VERTICAL_GAP: skewedFractionVerticalGap + * @HB_OT_MATH_CONSTANT_OVERBAR_VERTICAL_GAP: overbarVerticalGap + * @HB_OT_MATH_CONSTANT_OVERBAR_RULE_THICKNESS: overbarRuleThickness + * @HB_OT_MATH_CONSTANT_OVERBAR_EXTRA_ASCENDER: overbarExtraAscender + * @HB_OT_MATH_CONSTANT_UNDERBAR_VERTICAL_GAP: underbarVerticalGap + * @HB_OT_MATH_CONSTANT_UNDERBAR_RULE_THICKNESS: underbarRuleThickness + * @HB_OT_MATH_CONSTANT_UNDERBAR_EXTRA_DESCENDER: underbarExtraDescender + * @HB_OT_MATH_CONSTANT_RADICAL_VERTICAL_GAP: radicalVerticalGap + * @HB_OT_MATH_CONSTANT_RADICAL_DISPLAY_STYLE_VERTICAL_GAP: radicalDisplayStyleVerticalGap + * @HB_OT_MATH_CONSTANT_RADICAL_RULE_THICKNESS: radicalRuleThickness + * @HB_OT_MATH_CONSTANT_RADICAL_EXTRA_ASCENDER: radicalExtraAscender + * @HB_OT_MATH_CONSTANT_RADICAL_KERN_BEFORE_DEGREE: radicalKernBeforeDegree + * @HB_OT_MATH_CONSTANT_RADICAL_KERN_AFTER_DEGREE: radicalKernAfterDegree + * @HB_OT_MATH_CONSTANT_RADICAL_DEGREE_BOTTOM_RAISE_PERCENT: radicalDegreeBottomRaisePercent + * + * The 'MATH' table constants, refer to + * [OpenType documentation](https://docs.microsoft.com/en-us/typography/opentype/spec/math#mathconstants-table) + * For more explanations. + * + * Since: 1.3.3 + */ +typedef enum { + HB_OT_MATH_CONSTANT_SCRIPT_PERCENT_SCALE_DOWN = 0, + HB_OT_MATH_CONSTANT_SCRIPT_SCRIPT_PERCENT_SCALE_DOWN = 1, + HB_OT_MATH_CONSTANT_DELIMITED_SUB_FORMULA_MIN_HEIGHT = 2, + HB_OT_MATH_CONSTANT_DISPLAY_OPERATOR_MIN_HEIGHT = 3, + HB_OT_MATH_CONSTANT_MATH_LEADING = 4, + HB_OT_MATH_CONSTANT_AXIS_HEIGHT = 5, + HB_OT_MATH_CONSTANT_ACCENT_BASE_HEIGHT = 6, + HB_OT_MATH_CONSTANT_FLATTENED_ACCENT_BASE_HEIGHT = 7, + HB_OT_MATH_CONSTANT_SUBSCRIPT_SHIFT_DOWN = 8, + HB_OT_MATH_CONSTANT_SUBSCRIPT_TOP_MAX = 9, + HB_OT_MATH_CONSTANT_SUBSCRIPT_BASELINE_DROP_MIN = 10, + HB_OT_MATH_CONSTANT_SUPERSCRIPT_SHIFT_UP = 11, + HB_OT_MATH_CONSTANT_SUPERSCRIPT_SHIFT_UP_CRAMPED = 12, + HB_OT_MATH_CONSTANT_SUPERSCRIPT_BOTTOM_MIN = 13, + HB_OT_MATH_CONSTANT_SUPERSCRIPT_BASELINE_DROP_MAX = 14, + HB_OT_MATH_CONSTANT_SUB_SUPERSCRIPT_GAP_MIN = 15, + HB_OT_MATH_CONSTANT_SUPERSCRIPT_BOTTOM_MAX_WITH_SUBSCRIPT = 16, + HB_OT_MATH_CONSTANT_SPACE_AFTER_SCRIPT = 17, + HB_OT_MATH_CONSTANT_UPPER_LIMIT_GAP_MIN = 18, + HB_OT_MATH_CONSTANT_UPPER_LIMIT_BASELINE_RISE_MIN = 19, + HB_OT_MATH_CONSTANT_LOWER_LIMIT_GAP_MIN = 20, + HB_OT_MATH_CONSTANT_LOWER_LIMIT_BASELINE_DROP_MIN = 21, + HB_OT_MATH_CONSTANT_STACK_TOP_SHIFT_UP = 22, + HB_OT_MATH_CONSTANT_STACK_TOP_DISPLAY_STYLE_SHIFT_UP = 23, + HB_OT_MATH_CONSTANT_STACK_BOTTOM_SHIFT_DOWN = 24, + HB_OT_MATH_CONSTANT_STACK_BOTTOM_DISPLAY_STYLE_SHIFT_DOWN = 25, + HB_OT_MATH_CONSTANT_STACK_GAP_MIN = 26, + HB_OT_MATH_CONSTANT_STACK_DISPLAY_STYLE_GAP_MIN = 27, + HB_OT_MATH_CONSTANT_STRETCH_STACK_TOP_SHIFT_UP = 28, + HB_OT_MATH_CONSTANT_STRETCH_STACK_BOTTOM_SHIFT_DOWN = 29, + HB_OT_MATH_CONSTANT_STRETCH_STACK_GAP_ABOVE_MIN = 30, + HB_OT_MATH_CONSTANT_STRETCH_STACK_GAP_BELOW_MIN = 31, + HB_OT_MATH_CONSTANT_FRACTION_NUMERATOR_SHIFT_UP = 32, + HB_OT_MATH_CONSTANT_FRACTION_NUMERATOR_DISPLAY_STYLE_SHIFT_UP = 33, + HB_OT_MATH_CONSTANT_FRACTION_DENOMINATOR_SHIFT_DOWN = 34, + HB_OT_MATH_CONSTANT_FRACTION_DENOMINATOR_DISPLAY_STYLE_SHIFT_DOWN = 35, + HB_OT_MATH_CONSTANT_FRACTION_NUMERATOR_GAP_MIN = 36, + HB_OT_MATH_CONSTANT_FRACTION_NUM_DISPLAY_STYLE_GAP_MIN = 37, + HB_OT_MATH_CONSTANT_FRACTION_RULE_THICKNESS = 38, + HB_OT_MATH_CONSTANT_FRACTION_DENOMINATOR_GAP_MIN = 39, + HB_OT_MATH_CONSTANT_FRACTION_DENOM_DISPLAY_STYLE_GAP_MIN = 40, + HB_OT_MATH_CONSTANT_SKEWED_FRACTION_HORIZONTAL_GAP = 41, + HB_OT_MATH_CONSTANT_SKEWED_FRACTION_VERTICAL_GAP = 42, + HB_OT_MATH_CONSTANT_OVERBAR_VERTICAL_GAP = 43, + HB_OT_MATH_CONSTANT_OVERBAR_RULE_THICKNESS = 44, + HB_OT_MATH_CONSTANT_OVERBAR_EXTRA_ASCENDER = 45, + HB_OT_MATH_CONSTANT_UNDERBAR_VERTICAL_GAP = 46, + HB_OT_MATH_CONSTANT_UNDERBAR_RULE_THICKNESS = 47, + HB_OT_MATH_CONSTANT_UNDERBAR_EXTRA_DESCENDER = 48, + HB_OT_MATH_CONSTANT_RADICAL_VERTICAL_GAP = 49, + HB_OT_MATH_CONSTANT_RADICAL_DISPLAY_STYLE_VERTICAL_GAP = 50, + HB_OT_MATH_CONSTANT_RADICAL_RULE_THICKNESS = 51, + HB_OT_MATH_CONSTANT_RADICAL_EXTRA_ASCENDER = 52, + HB_OT_MATH_CONSTANT_RADICAL_KERN_BEFORE_DEGREE = 53, + HB_OT_MATH_CONSTANT_RADICAL_KERN_AFTER_DEGREE = 54, + HB_OT_MATH_CONSTANT_RADICAL_DEGREE_BOTTOM_RAISE_PERCENT = 55 +} hb_ot_math_constant_t; + +/** + * hb_ot_math_kern_t: + * @HB_OT_MATH_KERN_TOP_RIGHT: The top right corner of the glyph. + * @HB_OT_MATH_KERN_TOP_LEFT: The top left corner of the glyph. + * @HB_OT_MATH_KERN_BOTTOM_RIGHT: The bottom right corner of the glyph. + * @HB_OT_MATH_KERN_BOTTOM_LEFT: The bottom left corner of the glyph. + * + * The math kerning-table types defined for the four corners + * of a glyph. + * + * Since: 1.3.3 + */ +typedef enum { + HB_OT_MATH_KERN_TOP_RIGHT = 0, + HB_OT_MATH_KERN_TOP_LEFT = 1, + HB_OT_MATH_KERN_BOTTOM_RIGHT = 2, + HB_OT_MATH_KERN_BOTTOM_LEFT = 3 +} hb_ot_math_kern_t; + +/** + * hb_ot_math_kern_entry_t: + * @max_correction_height: The maximum height at which this entry should be used + * @kern_value: The kern value of the entry + * + * Data type to hold math kerning (cut-in) information for a glyph. + * + * Since: 3.4.0 + */ +typedef struct { + hb_position_t max_correction_height; + hb_position_t kern_value; +} hb_ot_math_kern_entry_t; + +/** + * hb_ot_math_glyph_variant_t: + * @glyph: The glyph index of the variant + * @advance: The advance width of the variant + * + * Data type to hold math-variant information for a glyph. + * + * Since: 1.3.3 + */ +typedef struct hb_ot_math_glyph_variant_t { + hb_codepoint_t glyph; + hb_position_t advance; +} hb_ot_math_glyph_variant_t; + +/** + * hb_ot_math_glyph_part_flags_t: + * @HB_OT_MATH_GLYPH_PART_FLAG_EXTENDER: This is an extender glyph part that + * can be repeated to reach the desired length. + * + * Flags for math glyph parts. + * + * Since: 1.3.3 + */ +typedef enum { /*< flags >*/ + HB_OT_MATH_GLYPH_PART_FLAG_EXTENDER = 0x00000001u /* Extender glyph */ +} hb_ot_math_glyph_part_flags_t; + +/** + * hb_ot_math_glyph_part_t: + * @glyph: The glyph index of the variant part + * @start_connector_length: The length of the connector on the starting side of the variant part + * @end_connector_length: The length of the connector on the ending side of the variant part + * @full_advance: The total advance of the part + * @flags: #hb_ot_math_glyph_part_flags_t flags for the part + * + * Data type to hold information for a "part" component of a math-variant glyph. + * Large variants for stretchable math glyphs (such as parentheses) can be constructed + * on the fly from parts. + * + * Since: 1.3.3 + */ +typedef struct hb_ot_math_glyph_part_t { + hb_codepoint_t glyph; + hb_position_t start_connector_length; + hb_position_t end_connector_length; + hb_position_t full_advance; + hb_ot_math_glyph_part_flags_t flags; +} hb_ot_math_glyph_part_t; + +/* Methods */ + +HB_EXTERN hb_bool_t +hb_ot_math_has_data (hb_face_t *face); + +HB_EXTERN hb_position_t +hb_ot_math_get_constant (hb_font_t *font, + hb_ot_math_constant_t constant); + +HB_EXTERN hb_position_t +hb_ot_math_get_glyph_italics_correction (hb_font_t *font, + hb_codepoint_t glyph); + +HB_EXTERN hb_position_t +hb_ot_math_get_glyph_top_accent_attachment (hb_font_t *font, + hb_codepoint_t glyph); + +HB_EXTERN hb_bool_t +hb_ot_math_is_glyph_extended_shape (hb_face_t *face, + hb_codepoint_t glyph); + +HB_EXTERN hb_position_t +hb_ot_math_get_glyph_kerning (hb_font_t *font, + hb_codepoint_t glyph, + hb_ot_math_kern_t kern, + hb_position_t correction_height); + +HB_EXTERN unsigned int +hb_ot_math_get_glyph_kernings (hb_font_t *font, + hb_codepoint_t glyph, + hb_ot_math_kern_t kern, + unsigned int start_offset, + unsigned int *entries_count, /* IN/OUT */ + hb_ot_math_kern_entry_t *kern_entries /* OUT */); + +HB_EXTERN unsigned int +hb_ot_math_get_glyph_variants (hb_font_t *font, + hb_codepoint_t glyph, + hb_direction_t direction, + unsigned int start_offset, + unsigned int *variants_count, /* IN/OUT */ + hb_ot_math_glyph_variant_t *variants /* OUT */); + +HB_EXTERN hb_position_t +hb_ot_math_get_min_connector_overlap (hb_font_t *font, + hb_direction_t direction); + +HB_EXTERN unsigned int +hb_ot_math_get_glyph_assembly (hb_font_t *font, + hb_codepoint_t glyph, + hb_direction_t direction, + unsigned int start_offset, + unsigned int *parts_count, /* IN/OUT */ + hb_ot_math_glyph_part_t *parts, /* OUT */ + hb_position_t *italics_correction /* OUT */); + + +HB_END_DECLS + +#endif /* HB_OT_MATH_H */ diff --git a/libs/harfbuzz/src/hb-ot-maxp-table.hh b/libs/harfbuzz/src/hb-ot-maxp-table.hh new file mode 100644 index 00000000..3a019ef7 --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-maxp-table.hh @@ -0,0 +1,142 @@ +/* + * Copyright © 2011,2012 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 HB_OT_MAXP_TABLE_HH +#define HB_OT_MAXP_TABLE_HH + +#include "hb-open-type.hh" + +namespace OT { + + +/* + * maxp -- Maximum Profile + * https://docs.microsoft.com/en-us/typography/opentype/spec/maxp + */ + +#define HB_OT_TAG_maxp HB_TAG('m','a','x','p') + +struct maxpV1Tail +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + HBUINT16 maxPoints; /* Maximum points in a non-composite glyph. */ + HBUINT16 maxContours; /* Maximum contours in a non-composite glyph. */ + HBUINT16 maxCompositePoints; /* Maximum points in a composite glyph. */ + HBUINT16 maxCompositeContours; /* Maximum contours in a composite glyph. */ + HBUINT16 maxZones; /* 1 if instructions do not use the twilight zone (Z0), + * or 2 if instructions do use Z0; should be set to 2 in + * most cases. */ + HBUINT16 maxTwilightPoints; /* Maximum points used in Z0. */ + HBUINT16 maxStorage; /* Number of Storage Area locations. */ + HBUINT16 maxFunctionDefs; /* Number of FDEFs, equal to the highest function number + 1. */ + HBUINT16 maxInstructionDefs; /* Number of IDEFs. */ + HBUINT16 maxStackElements; /* Maximum stack depth. (This includes Font and CVT + * Programs, as well as the instructions for each glyph.) */ + HBUINT16 maxSizeOfInstructions; /* Maximum byte count for glyph instructions. */ + HBUINT16 maxComponentElements; /* Maximum number of components referenced at + * "top level" for any composite glyph. */ + HBUINT16 maxComponentDepth; /* Maximum levels of recursion; 1 for simple components. */ + public: + DEFINE_SIZE_STATIC (26); +}; + + +struct maxp +{ + static constexpr hb_tag_t tableTag = HB_OT_TAG_maxp; + + unsigned int get_num_glyphs () const { return numGlyphs; } + + void set_num_glyphs (unsigned int count) + { + numGlyphs = count; + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (unlikely (!c->check_struct (this))) + return_trace (false); + + if (version.major == 1) + { + const maxpV1Tail &v1 = StructAfter (*this); + return_trace (v1.sanitize (c)); + } + return_trace (likely (version.major == 0 && version.minor == 0x5000u)); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + maxp *maxp_prime = c->serializer->embed (this); + if (unlikely (!maxp_prime)) return_trace (false); + + maxp_prime->numGlyphs = c->plan->num_output_glyphs (); + if (maxp_prime->version.major == 1) + { + const maxpV1Tail *src_v1 = &StructAfter (*this); + maxpV1Tail *dest_v1 = c->serializer->embed (src_v1); + if (unlikely (!dest_v1)) return_trace (false); + + if (c->plan->flags & HB_SUBSET_FLAGS_NO_HINTING) + drop_hint_fields (dest_v1); + } + + return_trace (true); + } + + static void drop_hint_fields (maxpV1Tail* dest_v1) + { + dest_v1->maxZones = 1; + dest_v1->maxTwilightPoints = 0; + dest_v1->maxStorage = 0; + dest_v1->maxFunctionDefs = 0; + dest_v1->maxInstructionDefs = 0; + dest_v1->maxStackElements = 0; + dest_v1->maxSizeOfInstructions = 0; + } + + protected: + FixedVersion<>version;/* Version of the maxp table (0.5 or 1.0), + * 0x00005000u or 0x00010000u. */ + HBUINT16 numGlyphs; + /* The number of glyphs in the font. */ +/*maxpV1Tail v1Tail[HB_VAR_ARRAY]; */ + public: + DEFINE_SIZE_STATIC (6); +}; + + +} /* namespace OT */ + + +#endif /* HB_OT_MAXP_TABLE_HH */ diff --git a/libs/harfbuzz/src/hb-ot-meta-table.hh b/libs/harfbuzz/src/hb-ot-meta-table.hh new file mode 100644 index 00000000..93e64c53 --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-meta-table.hh @@ -0,0 +1,129 @@ +/* + * Copyright © 2019 Ebrahim Byagowi + * + * 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. + */ + +#ifndef HB_OT_META_TABLE_HH +#define HB_OT_META_TABLE_HH + +#include "hb-open-type.hh" + +/* + * meta -- Metadata Table + * https://docs.microsoft.com/en-us/typography/opentype/spec/meta + * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6meta.html + */ +#define HB_OT_TAG_meta HB_TAG ('m','e','t','a') + + +namespace OT { + + +struct DataMap +{ + int cmp (hb_tag_t a) const { return tag.cmp (a); } + + hb_tag_t get_tag () const { return tag; } + + hb_blob_t *reference_entry (hb_blob_t *meta_blob) const + { return hb_blob_create_sub_blob (meta_blob, dataZ, dataLength); } + + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + dataZ.sanitize (c, base, dataLength))); + } + + protected: + Tag tag; /* A tag indicating the type of metadata. */ + NNOffset32To> + dataZ; /* Offset in bytes from the beginning of the + * metadata table to the data for this tag. */ + HBUINT32 dataLength; /* Length of the data. The data is not required to + * be padded to any byte boundary. */ + public: + DEFINE_SIZE_STATIC (12); +}; + +struct meta +{ + static constexpr hb_tag_t tableTag = HB_OT_TAG_meta; + + struct accelerator_t + { + accelerator_t (hb_face_t *face) + { table = hb_sanitize_context_t ().reference_table (face); } + ~accelerator_t () { table.destroy (); } + + hb_blob_t *reference_entry (hb_tag_t tag) const + { return table->dataMaps.lsearch (tag).reference_entry (table.get_blob ()); } + + unsigned int get_entries (unsigned int start_offset, + unsigned int *count, + hb_ot_meta_tag_t *entries) const + { + if (count) + { + + table->dataMaps.sub_array (start_offset, count) + | hb_map (&DataMap::get_tag) + | hb_map ([](hb_tag_t tag) { return (hb_ot_meta_tag_t) tag; }) + | hb_sink (hb_array (entries, *count)) + ; + } + return table->dataMaps.len; + } + + private: + hb_blob_ptr_t table; + }; + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + version == 1 && + dataMaps.sanitize (c, this))); + } + + protected: + HBUINT32 version; /* Version number of the metadata table — set to 1. */ + HBUINT32 flags; /* Flags — currently unused; set to 0. */ + HBUINT32 dataOffset; + /* Per Apple specification: + * Offset from the beginning of the table to the data. + * Per OT specification: + * Reserved. Not used; should be set to 0. */ + Array32Of + dataMaps;/* Array of data map records. */ + public: + DEFINE_SIZE_ARRAY (16, dataMaps); +}; + +struct meta_accelerator_t : meta::accelerator_t { + meta_accelerator_t (hb_face_t *face) : meta::accelerator_t (face) {} +}; + +} /* namespace OT */ + + +#endif /* HB_OT_META_TABLE_HH */ diff --git a/libs/harfbuzz/src/hb-ot-meta.cc b/libs/harfbuzz/src/hb-ot-meta.cc new file mode 100644 index 00000000..35c8eb52 --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-meta.cc @@ -0,0 +1,79 @@ +/* + * Copyright © 2019 Ebrahim Byagowi + * + * 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. + */ + +#include "hb.hh" + +#ifndef HB_NO_META + +#include "hb-ot-meta-table.hh" + +/** + * SECTION:hb-ot-meta + * @title: hb-ot-meta + * @short_description: OpenType Metadata + * @include: hb-ot.h + * + * Functions for fetching metadata from fonts. + **/ + +/** + * hb_ot_meta_get_entry_tags: + * @face: a face object + * @start_offset: iteration's start offset + * @entries_count:(inout) (optional): buffer size as input, filled size as output + * @entries: (out caller-allocates) (array length=entries_count): entries tags buffer + * + * Fetches all available feature types. + * + * Return value: Number of all available feature types. + * + * Since: 2.6.0 + **/ +unsigned int +hb_ot_meta_get_entry_tags (hb_face_t *face, + unsigned int start_offset, + unsigned int *entries_count, /* IN/OUT. May be NULL. */ + hb_ot_meta_tag_t *entries /* OUT. May be NULL. */) +{ + return face->table.meta->get_entries (start_offset, entries_count, entries); +} + +/** + * hb_ot_meta_reference_entry: + * @face: a #hb_face_t object. + * @meta_tag: tag of metadata you like to have. + * + * It fetches metadata entry of a given tag from a font. + * + * Returns: (transfer full): A blob containing the blob. + * + * Since: 2.6.0 + **/ +hb_blob_t * +hb_ot_meta_reference_entry (hb_face_t *face, hb_ot_meta_tag_t meta_tag) +{ + return face->table.meta->reference_entry (meta_tag); +} + +#endif diff --git a/libs/harfbuzz/src/hb-ot-meta.h b/libs/harfbuzz/src/hb-ot-meta.h new file mode 100644 index 00000000..7748eb49 --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-meta.h @@ -0,0 +1,72 @@ +/* + * Copyright © 2019 Ebrahim Byagowi + * + * 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. + */ + +#if !defined(HB_OT_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR) +#error "Include instead." +#endif + +#ifndef HB_OT_META_H +#define HB_OT_META_H + +#include "hb.h" + +HB_BEGIN_DECLS + +/** + * hb_ot_meta_tag_t: + * @HB_OT_META_TAG_DESIGN_LANGUAGES: Design languages. Text, using only + * Basic Latin (ASCII) characters. Indicates languages and/or scripts + * for the user audiences that the font was primarily designed for. + * @HB_OT_META_TAG_SUPPORTED_LANGUAGES: Supported languages. Text, using + * only Basic Latin (ASCII) characters. Indicates languages and/or scripts + * that the font is declared to be capable of supporting. + * + * Known metadata tags from https://docs.microsoft.com/en-us/typography/opentype/spec/meta + * + * Since: 2.6.0 + **/ +typedef enum { +/* + HB_OT_META_TAG_APPL = HB_TAG ('a','p','p','l'), + HB_OT_META_TAG_BILD = HB_TAG ('b','i','l','d'), +*/ + HB_OT_META_TAG_DESIGN_LANGUAGES = HB_TAG ('d','l','n','g'), + HB_OT_META_TAG_SUPPORTED_LANGUAGES = HB_TAG ('s','l','n','g'), + + /*< private >*/ + _HB_OT_META_TAG_MAX_VALUE = HB_TAG_MAX_SIGNED /*< skip >*/ +} hb_ot_meta_tag_t; + +HB_EXTERN unsigned int +hb_ot_meta_get_entry_tags (hb_face_t *face, + unsigned int start_offset, + unsigned int *entries_count, /* IN/OUT. May be NULL. */ + hb_ot_meta_tag_t *entries /* OUT. May be NULL. */); + +HB_EXTERN hb_blob_t * +hb_ot_meta_reference_entry (hb_face_t *face, hb_ot_meta_tag_t meta_tag); + +HB_END_DECLS + +#endif /* HB_OT_META_H */ diff --git a/libs/harfbuzz/src/hb-ot-metrics.cc b/libs/harfbuzz/src/hb-ot-metrics.cc new file mode 100644 index 00000000..5b12482b --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-metrics.cc @@ -0,0 +1,436 @@ +/* + * Copyright © 2018-2019 Ebrahim Byagowi + * + * 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. + */ + +#include "hb.hh" + +#include "hb-ot-var-mvar-table.hh" +#include "hb-ot-gasp-table.hh" // Just so we compile it; unused otherwise. +#include "hb-ot-os2-table.hh" +#include "hb-ot-post-table.hh" +#include "hb-ot-hhea-table.hh" +#include "hb-ot-metrics.hh" +#include "hb-ot-face.hh" + + +/** + * SECTION:hb-ot-metrics + * @title: hb-ot-metrics + * @short_description: OpenType Metrics + * @include: hb-ot.h + * + * Functions for fetching metrics from fonts. + **/ + +static float +_fix_ascender_descender (float value, hb_ot_metrics_tag_t metrics_tag) +{ + if (metrics_tag == HB_OT_METRICS_TAG_HORIZONTAL_ASCENDER || + metrics_tag == HB_OT_METRICS_TAG_VERTICAL_ASCENDER) + return fabs ((double) value); + if (metrics_tag == HB_OT_METRICS_TAG_HORIZONTAL_DESCENDER || + metrics_tag == HB_OT_METRICS_TAG_VERTICAL_DESCENDER) + return -fabs ((double) value); + return value; +} + +/* The common part of _get_position logic needed on hb-ot-font and here + to be able to have slim builds without the not always needed parts */ +bool +_hb_ot_metrics_get_position_common (hb_font_t *font, + hb_ot_metrics_tag_t metrics_tag, + hb_position_t *position /* OUT. May be NULL. */) +{ + hb_face_t *face = font->face; + switch ((unsigned) metrics_tag) + { +#ifndef HB_NO_VAR +#define GET_VAR face->table.MVAR->get_var (metrics_tag, font->coords, font->num_coords) +#else +#define GET_VAR .0f +#endif +#define GET_METRIC_X(TABLE, ATTR) \ + (face->table.TABLE->has_data () && \ + ((void) (position && (*position = font->em_scalef_x (_fix_ascender_descender ( \ + face->table.TABLE->ATTR + GET_VAR, metrics_tag)))), true)) +#define GET_METRIC_Y(TABLE, ATTR) \ + (face->table.TABLE->has_data () && \ + ((void) (position && (*position = font->em_scalef_y (_fix_ascender_descender ( \ + face->table.TABLE->ATTR + GET_VAR, metrics_tag)))), true)) + + case HB_OT_METRICS_TAG_HORIZONTAL_ASCENDER: + return (face->table.OS2->use_typo_metrics () && GET_METRIC_Y (OS2, sTypoAscender)) || + GET_METRIC_Y (hhea, ascender); + case HB_OT_METRICS_TAG_HORIZONTAL_DESCENDER: + return (face->table.OS2->use_typo_metrics () && GET_METRIC_Y (OS2, sTypoDescender)) || + GET_METRIC_Y (hhea, descender); + case HB_OT_METRICS_TAG_HORIZONTAL_LINE_GAP: + return (face->table.OS2->use_typo_metrics () && GET_METRIC_Y (OS2, sTypoLineGap)) || + GET_METRIC_Y (hhea, lineGap); + +#ifndef HB_NO_VERTICAL + case HB_OT_METRICS_TAG_VERTICAL_ASCENDER: return GET_METRIC_X (vhea, ascender); + case HB_OT_METRICS_TAG_VERTICAL_DESCENDER: return GET_METRIC_X (vhea, descender); + case HB_OT_METRICS_TAG_VERTICAL_LINE_GAP: return GET_METRIC_X (vhea, lineGap); +#endif + +#undef GET_METRIC_Y +#undef GET_METRIC_X +#undef GET_VAR + default: assert (0); return false; + } +} + +#ifndef HB_NO_METRICS + +#if 0 +static bool +_get_gasp (hb_face_t *face, float *result, hb_ot_metrics_tag_t metrics_tag) +{ + const OT::GaspRange& range = face->table.gasp->get_gasp_range (metrics_tag - HB_TAG ('g','s','p','0')); + if (&range == &Null (OT::GaspRange)) return false; + if (result) *result = range.rangeMaxPPEM + font->face->table.MVAR->get_var (metrics_tag, font->coords, font->num_coords); + return true; +} +#endif + +/* Private tags for https://github.com/harfbuzz/harfbuzz/issues/1866 */ +#define _HB_OT_METRICS_TAG_HORIZONTAL_ASCENDER_OS2 HB_TAG ('O','a','s','c') +#define _HB_OT_METRICS_TAG_HORIZONTAL_ASCENDER_HHEA HB_TAG ('H','a','s','c') +#define _HB_OT_METRICS_TAG_HORIZONTAL_DESCENDER_OS2 HB_TAG ('O','d','s','c') +#define _HB_OT_METRICS_TAG_HORIZONTAL_DESCENDER_HHEA HB_TAG ('H','d','s','c') +#define _HB_OT_METRICS_TAG_HORIZONTAL_LINE_GAP_OS2 HB_TAG ('O','l','g','p') +#define _HB_OT_METRICS_TAG_HORIZONTAL_LINE_GAP_HHEA HB_TAG ('H','l','g','p') + +/** + * hb_ot_metrics_get_position: + * @font: an #hb_font_t object. + * @metrics_tag: tag of metrics value you like to fetch. + * @position: (out) (optional): result of metrics value from the font. + * + * Fetches metrics value corresponding to @metrics_tag from @font. + * + * Returns: Whether found the requested metrics in the font. + * Since: 2.6.0 + **/ +hb_bool_t +hb_ot_metrics_get_position (hb_font_t *font, + hb_ot_metrics_tag_t metrics_tag, + hb_position_t *position /* OUT. May be NULL. */) +{ + hb_face_t *face = font->face; + switch ((unsigned) metrics_tag) + { + case HB_OT_METRICS_TAG_HORIZONTAL_ASCENDER: + case HB_OT_METRICS_TAG_HORIZONTAL_DESCENDER: + case HB_OT_METRICS_TAG_HORIZONTAL_LINE_GAP: + case HB_OT_METRICS_TAG_VERTICAL_ASCENDER: + case HB_OT_METRICS_TAG_VERTICAL_DESCENDER: + case HB_OT_METRICS_TAG_VERTICAL_LINE_GAP: return _hb_ot_metrics_get_position_common (font, metrics_tag, position); +#ifndef HB_NO_VAR +#define GET_VAR hb_ot_metrics_get_variation (font, metrics_tag) +#else +#define GET_VAR 0 +#endif +#define GET_METRIC_X(TABLE, ATTR) \ + (face->table.TABLE->has_data () && \ + ((void) (position && (*position = font->em_scalef_x (face->table.TABLE->ATTR + GET_VAR))), true)) +#define GET_METRIC_Y(TABLE, ATTR) \ + (face->table.TABLE->has_data () && \ + ((void) (position && (*position = font->em_scalef_y (face->table.TABLE->ATTR + GET_VAR))), true)) + case HB_OT_METRICS_TAG_HORIZONTAL_CLIPPING_ASCENT: return GET_METRIC_Y (OS2, usWinAscent); + case HB_OT_METRICS_TAG_HORIZONTAL_CLIPPING_DESCENT: return GET_METRIC_Y (OS2, usWinDescent); + + case HB_OT_METRICS_TAG_HORIZONTAL_CARET_RISE: + case HB_OT_METRICS_TAG_HORIZONTAL_CARET_RUN: + { + unsigned mult = 1u; + + if (font->slant) + { + unsigned rise = face->table.hhea->caretSlopeRise; + unsigned upem = face->get_upem (); + mult = (rise && rise < upem) ? hb_min (upem / rise, 256u) : 1u; + } + + if (metrics_tag == HB_OT_METRICS_TAG_HORIZONTAL_CARET_RISE) + { + bool ret = GET_METRIC_Y (hhea, caretSlopeRise); + + if (position) + *position *= mult; + + return ret; + } + else + { + hb_position_t rise = 0; + + if (font->slant && position && GET_METRIC_Y (hhea, caretSlopeRise)) + rise = *position; + + bool ret = GET_METRIC_X (hhea, caretSlopeRun); + + if (position) + { + *position *= mult; + + if (font->slant) + *position += _hb_roundf (mult * font->slant_xy * rise); + } + + return ret; + } + } + case HB_OT_METRICS_TAG_HORIZONTAL_CARET_OFFSET: return GET_METRIC_X (hhea, caretOffset); + +#ifndef HB_NO_VERTICAL + case HB_OT_METRICS_TAG_VERTICAL_CARET_RISE: return GET_METRIC_X (vhea, caretSlopeRise); + case HB_OT_METRICS_TAG_VERTICAL_CARET_RUN: return GET_METRIC_Y (vhea, caretSlopeRun); + case HB_OT_METRICS_TAG_VERTICAL_CARET_OFFSET: return GET_METRIC_Y (vhea, caretOffset); +#endif + case HB_OT_METRICS_TAG_X_HEIGHT: return GET_METRIC_Y (OS2->v2 (), sxHeight); + case HB_OT_METRICS_TAG_CAP_HEIGHT: return GET_METRIC_Y (OS2->v2 (), sCapHeight); + case HB_OT_METRICS_TAG_SUBSCRIPT_EM_X_SIZE: return GET_METRIC_X (OS2, ySubscriptXSize); + case HB_OT_METRICS_TAG_SUBSCRIPT_EM_Y_SIZE: return GET_METRIC_Y (OS2, ySubscriptYSize); + case HB_OT_METRICS_TAG_SUBSCRIPT_EM_X_OFFSET: return GET_METRIC_X (OS2, ySubscriptXOffset); + case HB_OT_METRICS_TAG_SUBSCRIPT_EM_Y_OFFSET: return GET_METRIC_Y (OS2, ySubscriptYOffset); + case HB_OT_METRICS_TAG_SUPERSCRIPT_EM_X_SIZE: return GET_METRIC_X (OS2, ySuperscriptXSize); + case HB_OT_METRICS_TAG_SUPERSCRIPT_EM_Y_SIZE: return GET_METRIC_Y (OS2, ySuperscriptYSize); + case HB_OT_METRICS_TAG_SUPERSCRIPT_EM_X_OFFSET: return GET_METRIC_X (OS2, ySuperscriptXOffset); + case HB_OT_METRICS_TAG_SUPERSCRIPT_EM_Y_OFFSET: return GET_METRIC_Y (OS2, ySuperscriptYOffset); + case HB_OT_METRICS_TAG_STRIKEOUT_SIZE: return GET_METRIC_Y (OS2, yStrikeoutSize); + case HB_OT_METRICS_TAG_STRIKEOUT_OFFSET: return GET_METRIC_Y (OS2, yStrikeoutPosition); + case HB_OT_METRICS_TAG_UNDERLINE_SIZE: return GET_METRIC_Y (post->table, underlineThickness); + case HB_OT_METRICS_TAG_UNDERLINE_OFFSET: return GET_METRIC_Y (post->table, underlinePosition); + + /* Private tags */ + case _HB_OT_METRICS_TAG_HORIZONTAL_ASCENDER_OS2: return GET_METRIC_Y (OS2, sTypoAscender); + case _HB_OT_METRICS_TAG_HORIZONTAL_ASCENDER_HHEA: return GET_METRIC_Y (hhea, ascender); + case _HB_OT_METRICS_TAG_HORIZONTAL_DESCENDER_OS2: return GET_METRIC_Y (OS2, sTypoDescender); + case _HB_OT_METRICS_TAG_HORIZONTAL_DESCENDER_HHEA: return GET_METRIC_Y (hhea, descender); + case _HB_OT_METRICS_TAG_HORIZONTAL_LINE_GAP_OS2: return GET_METRIC_Y (OS2, sTypoLineGap); + case _HB_OT_METRICS_TAG_HORIZONTAL_LINE_GAP_HHEA: return GET_METRIC_Y (hhea, lineGap); +#undef GET_METRIC_Y +#undef GET_METRIC_X +#undef GET_VAR + default: return false; + } +} + +/** + * hb_ot_metrics_get_position_with_fallback: + * @font: an #hb_font_t object. + * @metrics_tag: tag of metrics value you like to fetch. + * @position: (out) (optional): result of metrics value from the font. + * + * Fetches metrics value corresponding to @metrics_tag from @font, + * and synthesizes a value if it the value is missing in the font. + * + * Since: 4.0.0 + **/ +void +hb_ot_metrics_get_position_with_fallback (hb_font_t *font, + hb_ot_metrics_tag_t metrics_tag, + hb_position_t *position /* OUT */) +{ + hb_font_extents_t font_extents; + hb_codepoint_t glyph; + hb_glyph_extents_t extents; + + if (hb_ot_metrics_get_position (font, metrics_tag, position)) + { + if ((metrics_tag != HB_OT_METRICS_TAG_STRIKEOUT_SIZE && + metrics_tag != HB_OT_METRICS_TAG_UNDERLINE_SIZE) || + *position != 0) + return; + } + + switch (metrics_tag) + { + case HB_OT_METRICS_TAG_HORIZONTAL_ASCENDER: + case HB_OT_METRICS_TAG_HORIZONTAL_CLIPPING_ASCENT: + hb_font_get_extents_for_direction (font, HB_DIRECTION_LTR, &font_extents); + *position = font_extents.ascender; + break; + + case HB_OT_METRICS_TAG_VERTICAL_ASCENDER: + hb_font_get_extents_for_direction (font, HB_DIRECTION_TTB, &font_extents); + *position = font_extents.ascender; + break; + + case HB_OT_METRICS_TAG_HORIZONTAL_DESCENDER: + case HB_OT_METRICS_TAG_HORIZONTAL_CLIPPING_DESCENT: + hb_font_get_extents_for_direction (font, HB_DIRECTION_LTR, &font_extents); + *position = font_extents.descender; + break; + + case HB_OT_METRICS_TAG_VERTICAL_DESCENDER: + hb_font_get_extents_for_direction (font, HB_DIRECTION_TTB, &font_extents); + *position = font_extents.ascender; + break; + + case HB_OT_METRICS_TAG_HORIZONTAL_LINE_GAP: + hb_font_get_extents_for_direction (font, HB_DIRECTION_LTR, &font_extents); + *position = font_extents.line_gap; + break; + + case HB_OT_METRICS_TAG_VERTICAL_LINE_GAP: + hb_font_get_extents_for_direction (font, HB_DIRECTION_TTB, &font_extents); + *position = font_extents.line_gap; + break; + + case HB_OT_METRICS_TAG_HORIZONTAL_CARET_RISE: + case HB_OT_METRICS_TAG_VERTICAL_CARET_RISE: + *position = 1; + break; + + case HB_OT_METRICS_TAG_HORIZONTAL_CARET_RUN: + case HB_OT_METRICS_TAG_VERTICAL_CARET_RUN: + *position = 0; + break; + + case HB_OT_METRICS_TAG_HORIZONTAL_CARET_OFFSET: + case HB_OT_METRICS_TAG_VERTICAL_CARET_OFFSET: + *position = 0; + break; + + case HB_OT_METRICS_TAG_X_HEIGHT: + if (hb_font_get_nominal_glyph (font, 'x', &glyph) && + hb_font_get_glyph_extents (font, glyph, &extents)) + *position = extents.y_bearing; + else + *position = font->y_scale / 2; + break; + + case HB_OT_METRICS_TAG_CAP_HEIGHT: + if (hb_font_get_nominal_glyph (font, 'O', &glyph) && + hb_font_get_glyph_extents (font, glyph, &extents)) + *position = extents.height + 2 * extents.y_bearing; + else + *position = font->y_scale * 2 / 3; + break; + + case HB_OT_METRICS_TAG_STRIKEOUT_SIZE: + case HB_OT_METRICS_TAG_UNDERLINE_SIZE: + *position = font->y_scale / 18; + break; + + case HB_OT_METRICS_TAG_STRIKEOUT_OFFSET: + { + hb_position_t ascender; + hb_ot_metrics_get_position_with_fallback (font, + HB_OT_METRICS_TAG_HORIZONTAL_ASCENDER, + &ascender); + *position = ascender / 2; + } + break; + + case HB_OT_METRICS_TAG_UNDERLINE_OFFSET: + *position = - font->y_scale / 18; + break; + + case HB_OT_METRICS_TAG_SUBSCRIPT_EM_X_SIZE: + case HB_OT_METRICS_TAG_SUPERSCRIPT_EM_X_SIZE: + *position = font->x_scale * 10 / 12; + break; + + case HB_OT_METRICS_TAG_SUBSCRIPT_EM_Y_SIZE: + case HB_OT_METRICS_TAG_SUPERSCRIPT_EM_Y_SIZE: + *position = font->y_scale * 10 / 12; + break; + + case HB_OT_METRICS_TAG_SUBSCRIPT_EM_X_OFFSET: + case HB_OT_METRICS_TAG_SUPERSCRIPT_EM_X_OFFSET: + *position = 0; + break; + + case HB_OT_METRICS_TAG_SUBSCRIPT_EM_Y_OFFSET: + case HB_OT_METRICS_TAG_SUPERSCRIPT_EM_Y_OFFSET: + *position = font->y_scale / 5; + break; + + case _HB_OT_METRICS_TAG_MAX_VALUE: + default: + *position = 0; + break; + } +} + +#ifndef HB_NO_VAR +/** + * hb_ot_metrics_get_variation: + * @font: an #hb_font_t object. + * @metrics_tag: tag of metrics value you like to fetch. + * + * Fetches metrics value corresponding to @metrics_tag from @font with the + * current font variation settings applied. + * + * Returns: The requested metric value. + * + * Since: 2.6.0 + **/ +float +hb_ot_metrics_get_variation (hb_font_t *font, hb_ot_metrics_tag_t metrics_tag) +{ + return font->face->table.MVAR->get_var (metrics_tag, font->coords, font->num_coords); +} + +/** + * hb_ot_metrics_get_x_variation: + * @font: an #hb_font_t object. + * @metrics_tag: tag of metrics value you like to fetch. + * + * Fetches horizontal metrics value corresponding to @metrics_tag from @font + * with the current font variation settings applied. + * + * Returns: The requested metric value. + * + * Since: 2.6.0 + **/ +hb_position_t +hb_ot_metrics_get_x_variation (hb_font_t *font, hb_ot_metrics_tag_t metrics_tag) +{ + return font->em_scalef_x (hb_ot_metrics_get_variation (font, metrics_tag)); +} + +/** + * hb_ot_metrics_get_y_variation: + * @font: an #hb_font_t object. + * @metrics_tag: tag of metrics value you like to fetch. + * + * Fetches vertical metrics value corresponding to @metrics_tag from @font with + * the current font variation settings applied. + * + * Returns: The requested metric value. + * + * Since: 2.6.0 + **/ +hb_position_t +hb_ot_metrics_get_y_variation (hb_font_t *font, hb_ot_metrics_tag_t metrics_tag) +{ + return font->em_scalef_y (hb_ot_metrics_get_variation (font, metrics_tag)); +} +#endif + +#endif diff --git a/libs/harfbuzz/src/hb-ot-metrics.h b/libs/harfbuzz/src/hb-ot-metrics.h new file mode 100644 index 00000000..30de5000 --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-metrics.h @@ -0,0 +1,129 @@ +/* + * Copyright © 2018 Ebrahim Byagowi + * + * 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. + */ + +#if !defined(HB_OT_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR) +#error "Include instead." +#endif + +#ifndef HB_OT_METRICS_H +#define HB_OT_METRICS_H + +#include "hb.h" +#include "hb-ot-name.h" + +HB_BEGIN_DECLS + + +/** + * hb_ot_metrics_tag_t: + * @HB_OT_METRICS_TAG_HORIZONTAL_ASCENDER: horizontal ascender. + * @HB_OT_METRICS_TAG_HORIZONTAL_DESCENDER: horizontal descender. + * @HB_OT_METRICS_TAG_HORIZONTAL_LINE_GAP: horizontal line gap. + * @HB_OT_METRICS_TAG_HORIZONTAL_CLIPPING_ASCENT: horizontal clipping ascent. + * @HB_OT_METRICS_TAG_HORIZONTAL_CLIPPING_DESCENT: horizontal clipping descent. + * @HB_OT_METRICS_TAG_VERTICAL_ASCENDER: vertical ascender. + * @HB_OT_METRICS_TAG_VERTICAL_DESCENDER: vertical descender. + * @HB_OT_METRICS_TAG_VERTICAL_LINE_GAP: vertical line gap. + * @HB_OT_METRICS_TAG_HORIZONTAL_CARET_RISE: horizontal caret rise. + * @HB_OT_METRICS_TAG_HORIZONTAL_CARET_RUN: horizontal caret run. + * @HB_OT_METRICS_TAG_HORIZONTAL_CARET_OFFSET: horizontal caret offset. + * @HB_OT_METRICS_TAG_VERTICAL_CARET_RISE: vertical caret rise. + * @HB_OT_METRICS_TAG_VERTICAL_CARET_RUN: vertical caret run. + * @HB_OT_METRICS_TAG_VERTICAL_CARET_OFFSET: vertical caret offset. + * @HB_OT_METRICS_TAG_X_HEIGHT: x height. + * @HB_OT_METRICS_TAG_CAP_HEIGHT: cap height. + * @HB_OT_METRICS_TAG_SUBSCRIPT_EM_X_SIZE: subscript em x size. + * @HB_OT_METRICS_TAG_SUBSCRIPT_EM_Y_SIZE: subscript em y size. + * @HB_OT_METRICS_TAG_SUBSCRIPT_EM_X_OFFSET: subscript em x offset. + * @HB_OT_METRICS_TAG_SUBSCRIPT_EM_Y_OFFSET: subscript em y offset. + * @HB_OT_METRICS_TAG_SUPERSCRIPT_EM_X_SIZE: superscript em x size. + * @HB_OT_METRICS_TAG_SUPERSCRIPT_EM_Y_SIZE: superscript em y size. + * @HB_OT_METRICS_TAG_SUPERSCRIPT_EM_X_OFFSET: superscript em x offset. + * @HB_OT_METRICS_TAG_SUPERSCRIPT_EM_Y_OFFSET: superscript em y offset. + * @HB_OT_METRICS_TAG_STRIKEOUT_SIZE: strikeout size. + * @HB_OT_METRICS_TAG_STRIKEOUT_OFFSET: strikeout offset. + * @HB_OT_METRICS_TAG_UNDERLINE_SIZE: underline size. + * @HB_OT_METRICS_TAG_UNDERLINE_OFFSET: underline offset. + * + * Metric tags corresponding to [MVAR Value + * Tags](https://docs.microsoft.com/en-us/typography/opentype/spec/mvar#value-tags) + * + * Since: 2.6.0 + **/ +typedef enum { + HB_OT_METRICS_TAG_HORIZONTAL_ASCENDER = HB_TAG ('h','a','s','c'), + HB_OT_METRICS_TAG_HORIZONTAL_DESCENDER = HB_TAG ('h','d','s','c'), + HB_OT_METRICS_TAG_HORIZONTAL_LINE_GAP = HB_TAG ('h','l','g','p'), + HB_OT_METRICS_TAG_HORIZONTAL_CLIPPING_ASCENT = HB_TAG ('h','c','l','a'), + HB_OT_METRICS_TAG_HORIZONTAL_CLIPPING_DESCENT = HB_TAG ('h','c','l','d'), + HB_OT_METRICS_TAG_VERTICAL_ASCENDER = HB_TAG ('v','a','s','c'), + HB_OT_METRICS_TAG_VERTICAL_DESCENDER = HB_TAG ('v','d','s','c'), + HB_OT_METRICS_TAG_VERTICAL_LINE_GAP = HB_TAG ('v','l','g','p'), + HB_OT_METRICS_TAG_HORIZONTAL_CARET_RISE = HB_TAG ('h','c','r','s'), + HB_OT_METRICS_TAG_HORIZONTAL_CARET_RUN = HB_TAG ('h','c','r','n'), + HB_OT_METRICS_TAG_HORIZONTAL_CARET_OFFSET = HB_TAG ('h','c','o','f'), + HB_OT_METRICS_TAG_VERTICAL_CARET_RISE = HB_TAG ('v','c','r','s'), + HB_OT_METRICS_TAG_VERTICAL_CARET_RUN = HB_TAG ('v','c','r','n'), + HB_OT_METRICS_TAG_VERTICAL_CARET_OFFSET = HB_TAG ('v','c','o','f'), + HB_OT_METRICS_TAG_X_HEIGHT = HB_TAG ('x','h','g','t'), + HB_OT_METRICS_TAG_CAP_HEIGHT = HB_TAG ('c','p','h','t'), + HB_OT_METRICS_TAG_SUBSCRIPT_EM_X_SIZE = HB_TAG ('s','b','x','s'), + HB_OT_METRICS_TAG_SUBSCRIPT_EM_Y_SIZE = HB_TAG ('s','b','y','s'), + HB_OT_METRICS_TAG_SUBSCRIPT_EM_X_OFFSET = HB_TAG ('s','b','x','o'), + HB_OT_METRICS_TAG_SUBSCRIPT_EM_Y_OFFSET = HB_TAG ('s','b','y','o'), + HB_OT_METRICS_TAG_SUPERSCRIPT_EM_X_SIZE = HB_TAG ('s','p','x','s'), + HB_OT_METRICS_TAG_SUPERSCRIPT_EM_Y_SIZE = HB_TAG ('s','p','y','s'), + HB_OT_METRICS_TAG_SUPERSCRIPT_EM_X_OFFSET = HB_TAG ('s','p','x','o'), + HB_OT_METRICS_TAG_SUPERSCRIPT_EM_Y_OFFSET = HB_TAG ('s','p','y','o'), + HB_OT_METRICS_TAG_STRIKEOUT_SIZE = HB_TAG ('s','t','r','s'), + HB_OT_METRICS_TAG_STRIKEOUT_OFFSET = HB_TAG ('s','t','r','o'), + HB_OT_METRICS_TAG_UNDERLINE_SIZE = HB_TAG ('u','n','d','s'), + HB_OT_METRICS_TAG_UNDERLINE_OFFSET = HB_TAG ('u','n','d','o'), + + /*< private >*/ + _HB_OT_METRICS_TAG_MAX_VALUE = HB_TAG_MAX_SIGNED /*< skip >*/ +} hb_ot_metrics_tag_t; + +HB_EXTERN hb_bool_t +hb_ot_metrics_get_position (hb_font_t *font, + hb_ot_metrics_tag_t metrics_tag, + hb_position_t *position /* OUT. May be NULL. */); + +HB_EXTERN void +hb_ot_metrics_get_position_with_fallback (hb_font_t *font, + hb_ot_metrics_tag_t metrics_tag, + hb_position_t *position /* OUT */); + +HB_EXTERN float +hb_ot_metrics_get_variation (hb_font_t *font, hb_ot_metrics_tag_t metrics_tag); + +HB_EXTERN hb_position_t +hb_ot_metrics_get_x_variation (hb_font_t *font, hb_ot_metrics_tag_t metrics_tag); + +HB_EXTERN hb_position_t +hb_ot_metrics_get_y_variation (hb_font_t *font, hb_ot_metrics_tag_t metrics_tag); + +HB_END_DECLS + +#endif /* HB_OT_METRICS_H */ diff --git a/libs/harfbuzz/src/hb-ot-metrics.hh b/libs/harfbuzz/src/hb-ot-metrics.hh new file mode 100644 index 00000000..19a5e9ed --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-metrics.hh @@ -0,0 +1,35 @@ +/* + * Copyright © 2018 Ebrahim Byagowi + * + * 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. + */ + +#ifndef HB_OT_METRICS_HH +#define HB_OT_METRICS_HH + +#include "hb.hh" + +HB_INTERNAL bool +_hb_ot_metrics_get_position_common (hb_font_t *font, + hb_ot_metrics_tag_t metrics_tag, + hb_position_t *position /* OUT. May be NULL. */); + +#endif /* HB_OT_METRICS_HH */ diff --git a/libs/harfbuzz/src/hb-ot-name-language-static.hh b/libs/harfbuzz/src/hb-ot-name-language-static.hh new file mode 100644 index 00000000..0e0f2d63 --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-name-language-static.hh @@ -0,0 +1,456 @@ +/* + * Copyright © 2018 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 HB_OT_NAME_LANGUAGE_STATIC_HH +#define HB_OT_NAME_LANGUAGE_STATIC_HH + +#include "hb-ot-name-language.hh" + +/* Following two tables were generated by joining FreeType, FontConfig, + * and OpenType specification language lists, then filled in missing + * entries using: + * https://docs.microsoft.com/en-us/windows/desktop/intl/language-identifier-constants-and-strings + */ + +struct hb_ot_language_map_t +{ + int cmp (unsigned int key) const + { return key < code ? -1 : key > code ? +1 : 0; } + + uint16_t code; + char lang[6]; +}; + +static const hb_ot_language_map_t +_hb_ms_language_map[] = +{ + {0x0001, "ar"}, /* ??? */ + {0x0004, "zh"}, /* ??? */ + {0x0009, "en"}, /* ??? */ + {0x0401, "ar"}, /* Arabic (Saudi Arabia) */ + {0x0402, "bg"}, /* Bulgarian (Bulgaria) */ + {0x0403, "ca"}, /* Catalan (Catalan) */ + {0x0404, "zh-tw"}, /* Chinese (Taiwan) */ + {0x0405, "cs"}, /* Czech (Czech Republic) */ + {0x0406, "da"}, /* Danish (Denmark) */ + {0x0407, "de"}, /* German (Germany) */ + {0x0408, "el"}, /* Greek (Greece) */ + {0x0409, "en"}, /* English (United States) */ + {0x040A, "es"}, /* Spanish (Traditional Sort) (Spain) */ + {0x040B, "fi"}, /* Finnish (Finland) */ + {0x040C, "fr"}, /* French (France) */ + {0x040D, "he"}, /* Hebrew (Israel) */ + {0x040E, "hu"}, /* Hungarian (Hungary) */ + {0x040F, "is"}, /* Icelandic (Iceland) */ + {0x0410, "it"}, /* Italian (Italy) */ + {0x0411, "ja"}, /* Japanese (Japan) */ + {0x0412, "ko"}, /* Korean (Korea) */ + {0x0413, "nl"}, /* Dutch (Netherlands) */ + {0x0414, "no"}, /* Norwegian (Bokmal) (Norway) */ + {0x0415, "pl"}, /* Polish (Poland) */ + {0x0416, "pt"}, /* Portuguese (Brazil) */ + {0x0417, "rm"}, /* Romansh (Switzerland) */ + {0x0418, "ro"}, /* Romanian (Romania) */ + {0x0419, "ru"}, /* Russian (Russia) */ + {0x041A, "hr"}, /* Croatian (Croatia) */ + {0x041B, "sk"}, /* Slovak (Slovakia) */ + {0x041C, "sq"}, /* Albanian (Albania) */ + {0x041D, "sv"}, /* Swedish (Sweden) */ + {0x041E, "th"}, /* Thai (Thailand) */ + {0x041F, "tr"}, /* Turkish (Turkey) */ + {0x0420, "ur"}, /* Urdu (Islamic Republic of Pakistan) */ + {0x0421, "id"}, /* Indonesian (Indonesia) */ + {0x0422, "uk"}, /* Ukrainian (Ukraine) */ + {0x0423, "be"}, /* Belarusian (Belarus) */ + {0x0424, "sl"}, /* Slovenian (Slovenia) */ + {0x0425, "et"}, /* Estonian (Estonia) */ + {0x0426, "lv"}, /* Latvian (Latvia) */ + {0x0427, "lt"}, /* Lithuanian (Lithuania) */ + {0x0428, "tg"}, /* Tajik (Cyrillic) (Tajikistan) */ + {0x0429, "fa"}, /* Persian (Iran) */ + {0x042A, "vi"}, /* Vietnamese (Vietnam) */ + {0x042B, "hy"}, /* Armenian (Armenia) */ + {0x042C, "az"}, /* Azeri (Latin) (Azerbaijan) */ + {0x042D, "eu"}, /* Basque (Basque) */ + {0x042E, "hsb"}, /* Upper Sorbian (Germany) */ + {0x042F, "mk"}, /* Macedonian (FYROM) (Former Yugoslav Republic of Macedonia) */ + {0x0430, "st"}, /* ??? */ + {0x0431, "ts"}, /* ??? */ + {0x0432, "tn"}, /* Setswana (South Africa) */ + {0x0433, "ven"}, /* ??? */ + {0x0434, "xh"}, /* isiXhosa (South Africa) */ + {0x0435, "zu"}, /* isiZulu (South Africa) */ + {0x0436, "af"}, /* Afrikaans (South Africa) */ + {0x0437, "ka"}, /* Georgian (Georgia) */ + {0x0438, "fo"}, /* Faroese (Faroe Islands) */ + {0x0439, "hi"}, /* Hindi (India) */ + {0x043A, "mt"}, /* Maltese (Malta) */ + {0x043B, "se"}, /* Sami (Northern) (Norway) */ + {0x043C, "ga"}, /* ??? */ + {0x043D, "yi"}, /* ??? */ + {0x043E, "ms"}, /* Malay (Malaysia) */ + {0x043F, "kk"}, /* Kazakh (Kazakhstan) */ + {0x0440, "ky"}, /* Kyrgyz (Kyrgyzstan) */ + {0x0441, "sw"}, /* Kiswahili (Kenya) */ + {0x0442, "tk"}, /* Turkmen (Turkmenistan) */ + {0x0443, "uz"}, /* Uzbek (Latin) (Uzbekistan) */ + {0x0444, "tt"}, /* Tatar (Russia) */ + {0x0445, "bn"}, /* Bengali (India) */ + {0x0446, "pa"}, /* Punjabi (India) */ + {0x0447, "gu"}, /* Gujarati (India) */ + {0x0448, "or"}, /* Odia (formerly Oriya) (India) */ + {0x0449, "ta"}, /* Tamil (India) */ + {0x044A, "te"}, /* Telugu (India) */ + {0x044B, "kn"}, /* Kannada (India) */ + {0x044C, "ml"}, /* Malayalam (India) */ + {0x044D, "as"}, /* Assamese (India) */ + {0x044E, "mr"}, /* Marathi (India) */ + {0x044F, "sa"}, /* Sanskrit (India) */ + {0x0450, "mn"}, /* Mongolian (Cyrillic) (Mongolia) */ + {0x0451, "bo"}, /* Tibetan (PRC) */ + {0x0452, "cy"}, /* Welsh (United Kingdom) */ + {0x0453, "km"}, /* Khmer (Cambodia) */ + {0x0454, "lo"}, /* Lao (Lao P.D.R.) */ + {0x0455, "my"}, /* ??? */ + {0x0456, "gl"}, /* Galician (Galician) */ + {0x0457, "kok"}, /* Konkani (India) */ + {0x0458, "mni"}, /* ??? */ + {0x0459, "sd"}, /* ??? */ + {0x045A, "syr"}, /* Syriac (Syria) */ + {0x045B, "si"}, /* Sinhala (Sri Lanka) */ + {0x045C, "chr"}, /* ??? */ + {0x045D, "iu"}, /* Inuktitut (Canada) */ + {0x045E, "am"}, /* Amharic (Ethiopia) */ + {0x0460, "ks"}, /* ??? */ + {0x0461, "ne"}, /* Nepali (Nepal) */ + {0x0462, "fy"}, /* Frisian (Netherlands) */ + {0x0463, "ps"}, /* Pashto (Afghanistan) */ + {0x0464, "phi"}, /* Filipino (Philippines) */ + {0x0465, "div"}, /* Divehi (Maldives) */ + {0x0468, "ha"}, /* Hausa (Latin) (Nigeria) */ + {0x046A, "yo"}, /* Yoruba (Nigeria) */ + {0x046B, "quz"}, /* Quechua (Bolivia) */ + {0x046C, "nso"}, /* Sesotho sa Leboa (South Africa) */ + {0x046D, "ba"}, /* Bashkir (Russia) */ + {0x046E, "lb"}, /* Luxembourgish (Luxembourg) */ + {0x046F, "kl"}, /* Greenlandic (Greenland) */ + {0x0470, "ibo"}, /* Igbo (Nigeria) */ + {0x0471, "kau"}, /* ??? */ + {0x0472, "om"}, /* ??? */ + {0x0473, "ti"}, /* ??? */ + {0x0474, "gn"}, /* ??? */ + {0x0475, "haw"}, /* ??? */ + {0x0476, "la"}, /* ??? */ + {0x0477, "so"}, /* ??? */ + {0x0478, "ii"}, /* Yi (PRC) */ + {0x0479, "pap"}, /* ??? */ + {0x047A, "arn"}, /* Mapudungun (Chile) */ + {0x047C, "moh"}, /* Mohawk (Mohawk) */ + {0x047E, "br"}, /* Breton (France) */ + {0x0480, "ug"}, /* Uighur (PRC) */ + {0x0481, "mi"}, /* Maori (New Zealand) */ + {0x0482, "oc"}, /* Occitan (France) */ + {0x0483, "co"}, /* Corsican (France) */ + {0x0484, "gsw"}, /* Alsatian (France) */ + {0x0485, "sah"}, /* Yakut (Russia) */ + {0x0486, "qut"}, /* K'iche (Guatemala) */ + {0x0487, "rw"}, /* Kinyarwanda (Rwanda) */ + {0x0488, "wo"}, /* Wolof (Senegal) */ + {0x048C, "fa"}, /* Dari (Afghanistan) */ + {0x0801, "ar"}, /* Arabic (Iraq) */ + {0x0804, "zh-cn"}, /* Chinese (People’s Republic of China) */ + {0x0807, "de"}, /* German (Switzerland) */ + {0x0809, "en"}, /* English (United Kingdom) */ + {0x080A, "es"}, /* Spanish (Mexico) */ + {0x080C, "fr"}, /* French (Belgium) */ + {0x0810, "it"}, /* Italian (Switzerland) */ + {0x0812, "ko"}, /* ??? */ + {0x0813, "nl"}, /* Dutch (Belgium) */ + {0x0814, "nn"}, /* Norwegian (Nynorsk) (Norway) */ + {0x0816, "pt"}, /* Portuguese (Portugal) */ + {0x0818, "mo"}, /* ??? */ + {0x0819, "ru"}, /* ??? */ + {0x081A, "sr"}, /* Serbian (Latin) (Serbia) */ + {0x081D, "sv"}, /* Sweden (Finland) */ + {0x0820, "ur"}, /* ??? */ + {0x0827, "lt"}, /* ??? */ + {0x082C, "az"}, /* Azeri (Cyrillic) (Azerbaijan) */ + {0x082E, "dsb"}, /* Lower Sorbian (Germany) */ +//{0x083B, ""}, /* Sami (Northern) (Sweden) */ + {0x083C, "gd"}, /* Irish (Ireland) */ + {0x083E, "ms"}, /* Malay (Brunei Darussalam) */ + {0x0843, "uz"}, /* Uzbek (Cyrillic) (Uzbekistan) */ + {0x0845, "bn"}, /* Bengali (Bangladesh) */ + {0x0846, "ar"}, /* ??? */ + {0x0850, "mn"}, /* Mongolian (Traditional) (People’s Republic of China) */ + {0x0851, "dz"}, /* ??? */ + {0x085D, "iu"}, /* Inuktitut (Latin) (Canada) */ + {0x085F, "tzm"}, /* Tamazight (Latin) (Algeria) */ + {0x0861, "ne"}, /* ??? */ +//{0x086B, ""}, /* Quechua (Ecuador) */ + {0x0873, "ti"}, /* ??? */ + {0x0C01, "ar"}, /* Arabic (Egypt) */ + {0x0C04, "zh-hk"}, /* Chinese (Hong Kong S.A.R.) */ + {0x0C07, "de"}, /* German (Austria) */ + {0x0C09, "en"}, /* English (Australia) */ + {0x0C0A, "es"}, /* Spanish (Modern Sort) (Spain) */ + {0x0C0C, "fr"}, /* French (Canada) */ + {0x0C1A, "sr"}, /* Serbian (Cyrillic) (Serbia) */ + {0x0C3B, "se"}, /* Sami (Northern) (Finland) */ +//{0x0C6B, ""}, /* Quechua (Peru) */ + {0x1001, "ar"}, /* Arabic (Libya) */ + {0x1004, "zh-sg"}, /* Chinese (Singapore) */ + {0x1007, "de"}, /* German (Luxembourg) */ + {0x1009, "en"}, /* English (Canada) */ + {0x100A, "es"}, /* Spanish (Guatemala) */ + {0x100C, "fr"}, /* French (Switzerland) */ + {0x101A, "hr"}, /* Croatian (Latin) (Bosnia and Herzegovina) */ + {0x103B, "smj"}, /* Sami (Lule) (Norway) */ + {0x1401, "ar"}, /* Arabic (Algeria) */ +//{0x1404, ""}, /* Chinese (Macao S.A.R.) */ + {0x1407, "de"}, /* German (Liechtenstein) */ + {0x1409, "en"}, /* English (New Zealand) */ + {0x140A, "es"}, /* Spanish (Costa Rica) */ + {0x140C, "fr"}, /* French (Luxembourg) */ + {0x141A, "bs"}, /* Bosnian (Latin) (Bosnia and Herzegovina) */ +//{0x143B, ""}, /* Sami (Lule) (Sweden) */ + {0x1801, "ar"}, /* Arabic (Morocco) */ + {0x1809, "en"}, /* English (Ireland) */ + {0x180A, "es"}, /* Spanish (Panama) */ + {0x180C, "fr"}, /* French (Principality of Monaco) */ +//{0x181A, ""}, /* Serbian (Latin) (Bosnia and Herzegovina) */ + {0x183B, "sma"}, /* Sami (Southern) (Norway) */ + {0x1C01, "ar"}, /* Arabic (Tunisia) */ + {0x1C09, "en"}, /* English (South Africa) */ + {0x1C0A, "es"}, /* Spanish (Dominican Republic) */ + {0x1C0C, "fr"}, /* ??? */ +//{0x1C1A, ""}, /* Serbian (Cyrillic) (Bosnia and Herzegovina) */ +//{0x1C3B, ""}, /* Sami (Southern) (Sweden) */ + {0x2001, "ar"}, /* Arabic (Oman) */ + {0x2009, "en"}, /* English (Jamaica) */ + {0x200A, "es"}, /* Spanish (Venezuela) */ + {0x200C, "fr"}, /* ??? */ + {0x201A, "bs"}, /* Bosnian (Cyrillic) (Bosnia and Herzegovina) */ + {0x203B, "sms"}, /* Sami (Skolt) (Finland) */ + {0x2401, "ar"}, /* Arabic (Yemen) */ + {0x2409, "en"}, /* English (Caribbean) */ + {0x240A, "es"}, /* Spanish (Colombia) */ + {0x240C, "fr"}, /* ??? */ + {0x243B, "smn"}, /* Sami (Inari) (Finland) */ + {0x2801, "ar"}, /* Arabic (Syria) */ + {0x2809, "en"}, /* English (Belize) */ + {0x280A, "es"}, /* Spanish (Peru) */ + {0x280C, "fr"}, /* ??? */ + {0x2C01, "ar"}, /* Arabic (Jordan) */ + {0x2C09, "en"}, /* English (Trinidad and Tobago) */ + {0x2C0A, "es"}, /* Spanish (Argentina) */ + {0x2C0C, "fr"}, /* ??? */ + {0x3001, "ar"}, /* Arabic (Lebanon) */ + {0x3009, "en"}, /* English (Zimbabwe) */ + {0x300A, "es"}, /* Spanish (Ecuador) */ + {0x300C, "fr"}, /* ??? */ + {0x3401, "ar"}, /* Arabic (Kuwait) */ + {0x3409, "en"}, /* English (Republic of the Philippines) */ + {0x340A, "es"}, /* Spanish (Chile) */ + {0x340C, "fr"}, /* ??? */ + {0x3801, "ar"}, /* Arabic (U.A.E.) */ + {0x380A, "es"}, /* Spanish (Uruguay) */ + {0x380C, "fr"}, /* ??? */ + {0x3C01, "ar"}, /* Arabic (Bahrain) */ + {0x3C09, "en"}, /* ??? */ + {0x3C0A, "es"}, /* Spanish (Paraguay) */ + {0x3C0C, "fr"}, /* ??? */ + {0x4001, "ar"}, /* Arabic (Qatar) */ + {0x4009, "en"}, /* English (India) */ + {0x400A, "es"}, /* Spanish (Bolivia) */ + {0x4409, "en"}, /* English (Malaysia) */ + {0x440A, "es"}, /* Spanish (El Salvador) */ + {0x4809, "en"}, /* English (Singapore) */ + {0x480A, "es"}, /* Spanish (Honduras) */ + {0x4C0A, "es"}, /* Spanish (Nicaragua) */ + {0x500A, "es"}, /* Spanish (Puerto Rico) */ + {0x540A, "es"}, /* Spanish (United States) */ + {0xE40A, "es"}, /* ??? */ + {0xE40C, "fr"}, /* ??? */ +}; + +static const hb_ot_language_map_t +_hb_mac_language_map[] = +{ + { 0, "en"}, /* English */ + { 1, "fr"}, /* French */ + { 2, "de"}, /* German */ + { 3, "it"}, /* Italian */ + { 4, "nl"}, /* Dutch */ + { 5, "sv"}, /* Swedish */ + { 6, "es"}, /* Spanish */ + { 7, "da"}, /* Danish */ + { 8, "pt"}, /* Portuguese */ + { 9, "no"}, /* Norwegian */ + { 10, "he"}, /* Hebrew */ + { 11, "ja"}, /* Japanese */ + { 12, "ar"}, /* Arabic */ + { 13, "fi"}, /* Finnish */ + { 14, "el"}, /* Greek */ + { 15, "is"}, /* Icelandic */ + { 16, "mt"}, /* Maltese */ + { 17, "tr"}, /* Turkish */ + { 18, "hr"}, /* Croatian */ + { 19, "zh-tw"}, /* Chinese (Traditional) */ + { 20, "ur"}, /* Urdu */ + { 21, "hi"}, /* Hindi */ + { 22, "th"}, /* Thai */ + { 23, "ko"}, /* Korean */ + { 24, "lt"}, /* Lithuanian */ + { 25, "pl"}, /* Polish */ + { 26, "hu"}, /* Hungarian */ + { 27, "et"}, /* Estonian */ + { 28, "lv"}, /* Latvian */ +//{ 29, ""}, /* Sami */ + { 30, "fo"}, /* Faroese */ + { 31, "fa"}, /* Farsi/Persian */ + { 32, "ru"}, /* Russian */ + { 33, "zh-cn"}, /* Chinese (Simplified) */ + { 34, "nl"}, /* Flemish */ + { 35, "ga"}, /* Irish Gaelic */ + { 36, "sq"}, /* Albanian */ + { 37, "ro"}, /* Romanian */ + { 38, "cs"}, /* Czech */ + { 39, "sk"}, /* Slovak */ + { 40, "sl"}, /* Slovenian */ + { 41, "yi"}, /* Yiddish */ + { 42, "sr"}, /* Serbian */ + { 43, "mk"}, /* Macedonian */ + { 44, "bg"}, /* Bulgarian */ + { 45, "uk"}, /* Ukrainian */ + { 46, "be"}, /* Byelorussian */ + { 47, "uz"}, /* Uzbek */ + { 48, "kk"}, /* Kazakh */ + { 49, "az"}, /* Azerbaijani (Cyrillic script) */ + { 50, "az"}, /* Azerbaijani (Arabic script) */ + { 51, "hy"}, /* Armenian */ + { 52, "ka"}, /* Georgian */ + { 53, "mo"}, /* Moldavian */ + { 54, "ky"}, /* Kirghiz */ + { 55, "tg"}, /* Tajiki */ + { 56, "tk"}, /* Turkmen */ + { 57, "mn"}, /* Mongolian (Mongolian script) */ + { 58, "mn"}, /* Mongolian (Cyrillic script) */ + { 59, "ps"}, /* Pashto */ + { 60, "ku"}, /* Kurdish */ + { 61, "ks"}, /* Kashmiri */ + { 62, "sd"}, /* Sindhi */ + { 63, "bo"}, /* Tibetan */ + { 64, "ne"}, /* Nepali */ + { 65, "sa"}, /* Sanskrit */ + { 66, "mr"}, /* Marathi */ + { 67, "bn"}, /* Bengali */ + { 68, "as"}, /* Assamese */ + { 69, "gu"}, /* Gujarati */ + { 70, "pa"}, /* Punjabi */ + { 71, "or"}, /* Oriya */ + { 72, "ml"}, /* Malayalam */ + { 73, "kn"}, /* Kannada */ + { 74, "ta"}, /* Tamil */ + { 75, "te"}, /* Telugu */ + { 76, "si"}, /* Sinhalese */ + { 77, "my"}, /* Burmese */ + { 78, "km"}, /* Khmer */ + { 79, "lo"}, /* Lao */ + { 80, "vi"}, /* Vietnamese */ + { 81, "id"}, /* Indonesian */ + { 82, "tl"}, /* Tagalog */ + { 83, "ms"}, /* Malay (Roman script) */ + { 84, "ms"}, /* Malay (Arabic script) */ + { 85, "am"}, /* Amharic */ + { 86, "ti"}, /* Tigrinya */ + { 87, "om"}, /* Galla */ + { 88, "so"}, /* Somali */ + { 89, "sw"}, /* Swahili */ + { 90, "rw"}, /* Kinyarwanda/Ruanda */ + { 91, "rn"}, /* Rundi */ + { 92, "ny"}, /* Nyanja/Chewa */ + { 93, "mg"}, /* Malagasy */ + { 94, "eo"}, /* Esperanto */ + {128, "cy"}, /* Welsh */ + {129, "eu"}, /* Basque */ + {130, "ca"}, /* Catalan */ + {131, "la"}, /* Latin */ + {132, "qu"}, /* Quechua */ + {133, "gn"}, /* Guarani */ + {134, "ay"}, /* Aymara */ + {135, "tt"}, /* Tatar */ + {136, "ug"}, /* Uighur */ + {137, "dz"}, /* Dzongkha */ + {138, "jw"}, /* Javanese (Roman script) */ + {139, "su"}, /* Sundanese (Roman script) */ + {140, "gl"}, /* Galician */ + {141, "af"}, /* Afrikaans */ + {142, "br"}, /* Breton */ + {143, "iu"}, /* Inuktitut */ + {144, "gd"}, /* Scottish Gaelic */ + {145, "gv"}, /* Manx Gaelic */ + {146, "ga"}, /* Irish Gaelic (with dot above) */ + {147, "to"}, /* Tongan */ + {148, "el"}, /* Greek (polytonic) */ + {149, "ik"}, /* Greenlandic */ + {150, "az"}, /* Azerbaijani (Roman script) */ +}; + + +static hb_language_t +_hb_ot_name_language_for (unsigned int code, + const hb_ot_language_map_t *array, + unsigned int len) +{ +#ifdef HB_NO_OT_NAME_LANGUAGE + return HB_LANGUAGE_INVALID; +#endif + auto *entry = hb_bsearch (code, array, len); + + if (entry) + return hb_language_from_string (entry->lang, -1); + + return HB_LANGUAGE_INVALID; +} + +hb_language_t +_hb_ot_name_language_for_ms_code (unsigned int code) +{ + return _hb_ot_name_language_for (code, + _hb_ms_language_map, + ARRAY_LENGTH (_hb_ms_language_map)); +} + +hb_language_t +_hb_ot_name_language_for_mac_code (unsigned int code) +{ + return _hb_ot_name_language_for (code, + _hb_mac_language_map, + ARRAY_LENGTH (_hb_mac_language_map)); +} + +#endif /* HB_OT_NAME_LANGUAGE_STATIC_HH */ diff --git a/libs/harfbuzz/src/hb-ot-name-language.hh b/libs/harfbuzz/src/hb-ot-name-language.hh new file mode 100644 index 00000000..903076c0 --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-name-language.hh @@ -0,0 +1,40 @@ +/* + * Copyright © 2018 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 HB_OT_NAME_LANGUAGE_HH +#define HB_OT_NAME_LANGUAGE_HH + +#include "hb.hh" + + +HB_INTERNAL hb_language_t +_hb_ot_name_language_for_ms_code (unsigned int code); + +HB_INTERNAL hb_language_t +_hb_ot_name_language_for_mac_code (unsigned int code); + + +#endif /* HB_OT_NAME_LANGUAGE_HH */ diff --git a/libs/harfbuzz/src/hb-ot-name-table.hh b/libs/harfbuzz/src/hb-ot-name-table.hh new file mode 100644 index 00000000..1f2131ff --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-name-table.hh @@ -0,0 +1,402 @@ +/* + * Copyright © 2011,2012 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 HB_OT_NAME_TABLE_HH +#define HB_OT_NAME_TABLE_HH + +#include "hb-open-type.hh" +#include "hb-ot-name-language.hh" +#include "hb-aat-layout.hh" + + +namespace OT { + + +#define entry_score var.u16[0] +#define entry_index var.u16[1] + + +/* + * name -- Naming + * https://docs.microsoft.com/en-us/typography/opentype/spec/name + */ +#define HB_OT_TAG_name HB_TAG('n','a','m','e') + +#define UNSUPPORTED 42 + +struct NameRecord +{ + hb_language_t language (hb_face_t *face) const + { +#ifndef HB_NO_OT_NAME_LANGUAGE + unsigned int p = platformID; + unsigned int l = languageID; + + if (p == 3) + return _hb_ot_name_language_for_ms_code (l); + + if (p == 1) + return _hb_ot_name_language_for_mac_code (l); + +#ifndef HB_NO_OT_NAME_LANGUAGE_AAT + if (p == 0) + return face->table.ltag->get_language (l); +#endif + +#endif + return HB_LANGUAGE_INVALID; + } + + uint16_t score () const + { + /* Same order as in cmap::find_best_subtable(). */ + unsigned int p = platformID; + unsigned int e = encodingID; + + /* 32-bit. */ + if (p == 3 && e == 10) return 0; + if (p == 0 && e == 6) return 1; + if (p == 0 && e == 4) return 2; + + /* 16-bit. */ + if (p == 3 && e == 1) return 3; + if (p == 0 && e == 3) return 4; + if (p == 0 && e == 2) return 5; + if (p == 0 && e == 1) return 6; + if (p == 0 && e == 0) return 7; + + /* Symbol. */ + if (p == 3 && e == 0) return 8; + + /* We treat all Mac Latin names as ASCII only. */ + if (p == 1 && e == 0) return 10; /* 10 is magic number :| */ + + return UNSUPPORTED; + } + + NameRecord* copy (hb_serialize_context_t *c, const void *base) const + { + TRACE_SERIALIZE (this); + auto *out = c->embed (this); + if (unlikely (!out)) return_trace (nullptr); + out->offset.serialize_copy (c, offset, base, 0, hb_serialize_context_t::Tail, length); + return_trace (out); + } + + bool isUnicode () const + { + unsigned int p = platformID; + unsigned int e = encodingID; + + return (p == 0 || + (p == 3 && (e == 0 || e == 1 || e == 10))); + } + + static int cmp (const void *pa, const void *pb) + { + const NameRecord *a = (const NameRecord *)pa; + const NameRecord *b = (const NameRecord *)pb; + + if (a->platformID != b->platformID) + return a->platformID - b->platformID; + + if (a->encodingID != b->encodingID) + return a->encodingID - b->encodingID; + + if (a->languageID != b->languageID) + return a->languageID - b->languageID; + + if (a->nameID != b->nameID) + return a->nameID - b->nameID; + + if (a->length != b->length) + return a->length - b->length; + + return 0; + } + + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && offset.sanitize (c, base, length)); + } + + HBUINT16 platformID; /* Platform ID. */ + HBUINT16 encodingID; /* Platform-specific encoding ID. */ + HBUINT16 languageID; /* Language ID. */ + HBUINT16 nameID; /* Name ID. */ + HBUINT16 length; /* String length (in bytes). */ + NNOffset16To> + offset; /* String offset from start of storage area (in bytes). */ + public: + DEFINE_SIZE_STATIC (12); +}; + +static int +_hb_ot_name_entry_cmp_key (const void *pa, const void *pb, bool exact) +{ + const hb_ot_name_entry_t *a = (const hb_ot_name_entry_t *) pa; + const hb_ot_name_entry_t *b = (const hb_ot_name_entry_t *) pb; + + /* Compare by name_id, then language. */ + + if (a->name_id != b->name_id) + return a->name_id - b->name_id; + + if (a->language == b->language) return 0; + if (!a->language) return -1; + if (!b->language) return +1; + + const char *astr = hb_language_to_string (a->language); + const char *bstr = hb_language_to_string (b->language); + + signed c = strcmp (astr, bstr); + + // 'a' is the user request, and 'b' is string in the font. + // If eg. user asks for "en-us" and font has "en", approve. + if (!exact && c && + hb_language_matches (b->language, a->language)) + return 0; + + return c; +} + +static int +_hb_ot_name_entry_cmp (const void *pa, const void *pb) +{ + /* Compare by name_id, then language, then score, then index. */ + + int v = _hb_ot_name_entry_cmp_key (pa, pb, true); + if (v) + return v; + + const hb_ot_name_entry_t *a = (const hb_ot_name_entry_t *) pa; + const hb_ot_name_entry_t *b = (const hb_ot_name_entry_t *) pb; + + if (a->entry_score != b->entry_score) + return a->entry_score - b->entry_score; + + if (a->entry_index != b->entry_index) + return a->entry_index - b->entry_index; + + return 0; +} + +struct name +{ + static constexpr hb_tag_t tableTag = HB_OT_TAG_name; + + unsigned int get_size () const + { return min_size + count * nameRecordZ.item_size; } + + template + bool serialize (hb_serialize_context_t *c, + Iterator it, + const void *src_string_pool) + { + TRACE_SERIALIZE (this); + + if (unlikely (!c->extend_min ((*this)))) return_trace (false); + + this->format = 0; + this->count = it.len (); + + NameRecord *name_records = (NameRecord *) hb_calloc (it.len (), NameRecord::static_size); + if (unlikely (!name_records)) return_trace (false); + + hb_array_t records (name_records, it.len ()); + + for (const NameRecord& record : it) + { + memcpy (name_records, &record, NameRecord::static_size); + name_records++; + } + + records.qsort (); + + c->copy_all (records, src_string_pool); + hb_free (records.arrayZ); + + + if (unlikely (c->ran_out_of_room ())) return_trace (false); + + this->stringOffset = c->length (); + + return_trace (true); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + + name *name_prime = c->serializer->start_embed (); + if (unlikely (!name_prime)) return_trace (false); + + auto it = + + nameRecordZ.as_array (count) + | hb_filter (c->plan->name_ids, &NameRecord::nameID) + | hb_filter (c->plan->name_languages, &NameRecord::languageID) + | hb_filter ([&] (const NameRecord& namerecord) { + return + (c->plan->flags & HB_SUBSET_FLAGS_NAME_LEGACY) + || namerecord.isUnicode (); + }) + ; + + name_prime->serialize (c->serializer, it, std::addressof (this + stringOffset)); + return_trace (name_prime->count); + } + + bool sanitize_records (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + const void *string_pool = (this+stringOffset).arrayZ; + return_trace (nameRecordZ.sanitize (c, count, string_pool)); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + likely (format == 0 || format == 1) && + c->check_array (nameRecordZ.arrayZ, count) && + c->check_range (this, stringOffset) && + sanitize_records (c)); + } + + struct accelerator_t + { + accelerator_t (hb_face_t *face) + { + this->table = hb_sanitize_context_t ().reference_table (face); + assert (this->table.get_length () >= this->table->stringOffset); + this->pool = (const char *) (const void *) (this->table+this->table->stringOffset); + this->pool_len = this->table.get_length () - this->table->stringOffset; + const hb_array_t all_names (this->table->nameRecordZ.arrayZ, + this->table->count); + + this->names.alloc (all_names.length); + + for (unsigned int i = 0; i < all_names.length; i++) + { + hb_ot_name_entry_t *entry = this->names.push (); + + entry->name_id = all_names[i].nameID; + entry->language = all_names[i].language (face); + entry->entry_score = all_names[i].score (); + entry->entry_index = i; + } + + this->names.qsort (_hb_ot_name_entry_cmp); + /* Walk and pick best only for each name_id,language pair, + * while dropping unsupported encodings. */ + unsigned int j = 0; + for (unsigned int i = 0; i < this->names.length; i++) + { + if (this->names[i].entry_score == UNSUPPORTED || + this->names[i].language == HB_LANGUAGE_INVALID) + continue; + if (i && + this->names[i - 1].name_id == this->names[i].name_id && + this->names[i - 1].language == this->names[i].language) + continue; + this->names[j++] = this->names[i]; + } + this->names.resize (j); + } + ~accelerator_t () + { + this->table.destroy (); + } + + int get_index (hb_ot_name_id_t name_id, + hb_language_t language, + unsigned int *width=nullptr) const + { + const hb_ot_name_entry_t key = {name_id, {0}, language}; + const hb_ot_name_entry_t *entry = hb_bsearch (key, (const hb_ot_name_entry_t *) this->names, + this->names.length, + sizeof (hb_ot_name_entry_t), + _hb_ot_name_entry_cmp_key, + true); + + if (!entry) + { + entry = hb_bsearch (key, (const hb_ot_name_entry_t *) this->names, + this->names.length, + sizeof (hb_ot_name_entry_t), + _hb_ot_name_entry_cmp_key, + false); + } + + if (!entry) + return -1; + + if (width) + *width = entry->entry_score < 10 ? 2 : 1; + + return entry->entry_index; + } + + hb_bytes_t get_name (unsigned int idx) const + { + const hb_array_t all_names (table->nameRecordZ.arrayZ, table->count); + const NameRecord &record = all_names[idx]; + const hb_bytes_t string_pool (pool, pool_len); + return string_pool.sub_array (record.offset, record.length); + } + + private: + const char *pool; + unsigned int pool_len; + public: + hb_blob_ptr_t table; + hb_vector_t names; + }; + + /* We only implement format 0 for now. */ + HBUINT16 format; /* Format selector (=0/1). */ + HBUINT16 count; /* Number of name records. */ + NNOffset16To> + stringOffset; /* Offset to start of string storage (from start of table). */ + UnsizedArrayOf + nameRecordZ; /* The name records where count is the number of records. */ + public: + DEFINE_SIZE_ARRAY (6, nameRecordZ); +}; + +#undef entry_index +#undef entry_score + +struct name_accelerator_t : name::accelerator_t { + name_accelerator_t (hb_face_t *face) : name::accelerator_t (face) {} +}; + +} /* namespace OT */ + + +#endif /* HB_OT_NAME_TABLE_HH */ diff --git a/libs/harfbuzz/src/hb-ot-name.cc b/libs/harfbuzz/src/hb-ot-name.cc new file mode 100644 index 00000000..c35ac5b3 --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-name.cc @@ -0,0 +1,231 @@ +/* + * Copyright © 2018 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 + */ + +#include "hb.hh" + +#ifndef HB_NO_NAME + +#include "hb-ot-name-table.hh" + +#include "hb-utf.hh" + + +/** + * SECTION:hb-ot-name + * @title: hb-ot-name + * @short_description: OpenType font name information + * @include: hb-ot.h + * + * Functions for fetching name strings from OpenType fonts. + **/ + + +/** + * hb_ot_name_list_names: + * @face: font face. + * @num_entries: (out) (optional): number of returned entries. + * + * Enumerates all available name IDs and language combinations. Returned + * array is owned by the @face and should not be modified. It can be + * used as long as @face is alive. + * + * Returns: (transfer none) (array length=num_entries): Array of available name entries. + * Since: 2.1.0 + **/ +const hb_ot_name_entry_t * +hb_ot_name_list_names (hb_face_t *face, + unsigned int *num_entries /* OUT */) +{ + const OT::name_accelerator_t &name = *face->table.name; + if (num_entries) *num_entries = name.names.length; + return (const hb_ot_name_entry_t *) name.names; +} + + +template +static inline unsigned int +hb_ot_name_convert_utf (hb_bytes_t bytes, + unsigned int *text_size /* IN/OUT */, + typename out_utf_t::codepoint_t *text /* OUT */) +{ + unsigned int src_len = bytes.length / sizeof (typename in_utf_t::codepoint_t); + const typename in_utf_t::codepoint_t *src = (const typename in_utf_t::codepoint_t *) bytes.arrayZ; + const typename in_utf_t::codepoint_t *src_end = src + src_len; + + typename out_utf_t::codepoint_t *dst = text; + + hb_codepoint_t unicode; + const hb_codepoint_t replacement = HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT; + + if (text_size && *text_size) + { + (*text_size)--; /* Same room for NUL-termination. */ + const typename out_utf_t::codepoint_t *dst_end = text + *text_size; + + while (src < src_end && dst < dst_end) + { + const typename in_utf_t::codepoint_t *src_next = in_utf_t::next (src, src_end, &unicode, replacement); + typename out_utf_t::codepoint_t *dst_next = out_utf_t::encode (dst, dst_end, unicode); + if (dst_next == dst) + break; /* Out-of-room. */ + + dst = dst_next; + src = src_next; + } + + *text_size = dst - text; + *dst = 0; /* NUL-terminate. */ + } + + /* Accumulate length of rest. */ + unsigned int dst_len = dst - text; + while (src < src_end) + { + src = in_utf_t::next (src, src_end, &unicode, replacement); + dst_len += out_utf_t::encode_len (unicode); + } + return dst_len; +} + +template +static inline unsigned int +hb_ot_name_get_utf (hb_face_t *face, + hb_ot_name_id_t name_id, + hb_language_t language, + unsigned int *text_size /* IN/OUT */, + typename utf_t::codepoint_t *text /* OUT */) +{ + const OT::name_accelerator_t &name = *face->table.name; + + if (!language) + language = hb_language_from_string ("en", 2); + + unsigned int width; + int idx = name.get_index (name_id, language, &width); + if (idx != -1) + { + hb_bytes_t bytes = name.get_name (idx); + + if (width == 2) /* UTF16-BE */ + return hb_ot_name_convert_utf (bytes, text_size, text); + + if (width == 1) /* ASCII */ + return hb_ot_name_convert_utf (bytes, text_size, text); + } + + if (text_size) + { + if (*text_size) + *text = 0; + *text_size = 0; + } + return 0; +} + +/** + * hb_ot_name_get_utf8: + * @face: font face. + * @name_id: OpenType name identifier to fetch. + * @language: language to fetch the name for. + * @text_size: (inout) (optional): input size of @text buffer, and output size of + * text written to buffer. + * @text: (out caller-allocates) (array length=text_size): buffer to write fetched name into. + * + * Fetches a font name from the OpenType 'name' table. + * If @language is #HB_LANGUAGE_INVALID, English ("en") is assumed. + * Returns string in UTF-8 encoding. A NUL terminator is always written + * for convenience, and isn't included in the output @text_size. + * + * Returns: full length of the requested string, or 0 if not found. + * Since: 2.1.0 + **/ +unsigned int +hb_ot_name_get_utf8 (hb_face_t *face, + hb_ot_name_id_t name_id, + hb_language_t language, + unsigned int *text_size /* IN/OUT */, + char *text /* OUT */) +{ + return hb_ot_name_get_utf (face, name_id, language, text_size, + (hb_utf8_t::codepoint_t *) text); +} + +/** + * hb_ot_name_get_utf16: + * @face: font face. + * @name_id: OpenType name identifier to fetch. + * @language: language to fetch the name for. + * @text_size: (inout) (optional): input size of @text buffer, and output size of + * text written to buffer. + * @text: (out caller-allocates) (array length=text_size): buffer to write fetched name into. + * + * Fetches a font name from the OpenType 'name' table. + * If @language is #HB_LANGUAGE_INVALID, English ("en") is assumed. + * Returns string in UTF-16 encoding. A NUL terminator is always written + * for convenience, and isn't included in the output @text_size. + * + * Returns: full length of the requested string, or 0 if not found. + * Since: 2.1.0 + **/ +unsigned int +hb_ot_name_get_utf16 (hb_face_t *face, + hb_ot_name_id_t name_id, + hb_language_t language, + unsigned int *text_size /* IN/OUT */, + uint16_t *text /* OUT */) +{ + return hb_ot_name_get_utf (face, name_id, language, text_size, text); +} + +/** + * hb_ot_name_get_utf32: + * @face: font face. + * @name_id: OpenType name identifier to fetch. + * @language: language to fetch the name for. + * @text_size: (inout) (optional): input size of @text buffer, and output size of + * text written to buffer. + * @text: (out caller-allocates) (array length=text_size): buffer to write fetched name into. + * + * Fetches a font name from the OpenType 'name' table. + * If @language is #HB_LANGUAGE_INVALID, English ("en") is assumed. + * Returns string in UTF-32 encoding. A NUL terminator is always written + * for convenience, and isn't included in the output @text_size. + * + * Returns: full length of the requested string, or 0 if not found. + * Since: 2.1.0 + **/ +unsigned int +hb_ot_name_get_utf32 (hb_face_t *face, + hb_ot_name_id_t name_id, + hb_language_t language, + unsigned int *text_size /* IN/OUT */, + uint32_t *text /* OUT */) +{ + return hb_ot_name_get_utf (face, name_id, language, text_size, text); +} + + +#endif diff --git a/libs/harfbuzz/src/hb-ot-name.h b/libs/harfbuzz/src/hb-ot-name.h new file mode 100644 index 00000000..1ea4b55e --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-name.h @@ -0,0 +1,158 @@ +/* + * Copyright © 2018 Ebrahim Byagowi. + * + * 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. + */ + +#if !defined(HB_OT_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR) +#error "Include instead." +#endif + +#ifndef HB_OT_NAME_H +#define HB_OT_NAME_H + +#include "hb.h" + +HB_BEGIN_DECLS + + +/** + * hb_ot_name_id_t: + * @HB_OT_NAME_ID_COPYRIGHT: Copyright notice + * @HB_OT_NAME_ID_FONT_FAMILY: Font Family name + * @HB_OT_NAME_ID_FONT_SUBFAMILY: Font Subfamily name + * @HB_OT_NAME_ID_UNIQUE_ID: Unique font identifier + * @HB_OT_NAME_ID_FULL_NAME: Full font name that reflects + * all family and relevant subfamily descriptors + * @HB_OT_NAME_ID_VERSION_STRING: Version string + * @HB_OT_NAME_ID_POSTSCRIPT_NAME: PostScript name for the font + * @HB_OT_NAME_ID_TRADEMARK: Trademark + * @HB_OT_NAME_ID_MANUFACTURER: Manufacturer Name + * @HB_OT_NAME_ID_DESIGNER: Designer + * @HB_OT_NAME_ID_DESCRIPTION: Description + * @HB_OT_NAME_ID_VENDOR_URL: URL of font vendor + * @HB_OT_NAME_ID_DESIGNER_URL: URL of typeface designer + * @HB_OT_NAME_ID_LICENSE: License Description + * @HB_OT_NAME_ID_LICENSE_URL: URL where additional licensing + * information can be found + * @HB_OT_NAME_ID_TYPOGRAPHIC_FAMILY: Typographic Family name + * @HB_OT_NAME_ID_TYPOGRAPHIC_SUBFAMILY: Typographic Subfamily name + * @HB_OT_NAME_ID_MAC_FULL_NAME: Compatible Full Name for MacOS + * @HB_OT_NAME_ID_SAMPLE_TEXT: Sample text + * @HB_OT_NAME_ID_CID_FINDFONT_NAME: PostScript CID findfont name + * @HB_OT_NAME_ID_WWS_FAMILY: WWS Family Name + * @HB_OT_NAME_ID_WWS_SUBFAMILY: WWS Subfamily Name + * @HB_OT_NAME_ID_LIGHT_BACKGROUND: Light Background Palette + * @HB_OT_NAME_ID_DARK_BACKGROUND: Dark Background Palette + * @HB_OT_NAME_ID_VARIATIONS_PS_PREFIX: Variations PostScript Name Prefix + * @HB_OT_NAME_ID_INVALID: Value to represent a nonexistent name ID. + * + * An integral type representing an OpenType 'name' table name identifier. + * There are predefined name IDs, as well as name IDs return from other + * API. These can be used to fetch name strings from a font face. + * + * For more information on these fields, see the + * [OpenType spec](https://docs.microsoft.com/en-us/typography/opentype/spec/name#name-ids). + * + * Since: 2.0.0 + **/ +enum +{ + HB_OT_NAME_ID_COPYRIGHT = 0, + HB_OT_NAME_ID_FONT_FAMILY = 1, + HB_OT_NAME_ID_FONT_SUBFAMILY = 2, + HB_OT_NAME_ID_UNIQUE_ID = 3, + HB_OT_NAME_ID_FULL_NAME = 4, + HB_OT_NAME_ID_VERSION_STRING = 5, + HB_OT_NAME_ID_POSTSCRIPT_NAME = 6, + HB_OT_NAME_ID_TRADEMARK = 7, + HB_OT_NAME_ID_MANUFACTURER = 8, + HB_OT_NAME_ID_DESIGNER = 9, + HB_OT_NAME_ID_DESCRIPTION = 10, + HB_OT_NAME_ID_VENDOR_URL = 11, + HB_OT_NAME_ID_DESIGNER_URL = 12, + HB_OT_NAME_ID_LICENSE = 13, + HB_OT_NAME_ID_LICENSE_URL = 14, +/*HB_OT_NAME_ID_RESERVED = 15,*/ + HB_OT_NAME_ID_TYPOGRAPHIC_FAMILY = 16, + HB_OT_NAME_ID_TYPOGRAPHIC_SUBFAMILY = 17, + HB_OT_NAME_ID_MAC_FULL_NAME = 18, + HB_OT_NAME_ID_SAMPLE_TEXT = 19, + HB_OT_NAME_ID_CID_FINDFONT_NAME = 20, + HB_OT_NAME_ID_WWS_FAMILY = 21, + HB_OT_NAME_ID_WWS_SUBFAMILY = 22, + HB_OT_NAME_ID_LIGHT_BACKGROUND = 23, + HB_OT_NAME_ID_DARK_BACKGROUND = 24, + HB_OT_NAME_ID_VARIATIONS_PS_PREFIX = 25, + + HB_OT_NAME_ID_INVALID = 0xFFFF +}; + +typedef unsigned int hb_ot_name_id_t; + + +/** + * hb_ot_name_entry_t: + * @name_id: name ID + * @language: language + * + * Structure representing a name ID in a particular language. + * + * Since: 2.1.0 + **/ +typedef struct hb_ot_name_entry_t { + hb_ot_name_id_t name_id; + /*< private >*/ + hb_var_int_t var; + /*< public >*/ + hb_language_t language; +} hb_ot_name_entry_t; + +HB_EXTERN const hb_ot_name_entry_t * +hb_ot_name_list_names (hb_face_t *face, + unsigned int *num_entries /* OUT */); + + +HB_EXTERN unsigned int +hb_ot_name_get_utf8 (hb_face_t *face, + hb_ot_name_id_t name_id, + hb_language_t language, + unsigned int *text_size /* IN/OUT */, + char *text /* OUT */); + +HB_EXTERN unsigned int +hb_ot_name_get_utf16 (hb_face_t *face, + hb_ot_name_id_t name_id, + hb_language_t language, + unsigned int *text_size /* IN/OUT */, + uint16_t *text /* OUT */); + +HB_EXTERN unsigned int +hb_ot_name_get_utf32 (hb_face_t *face, + hb_ot_name_id_t name_id, + hb_language_t language, + unsigned int *text_size /* IN/OUT */, + uint32_t *text /* OUT */); + + +HB_END_DECLS + +#endif /* HB_OT_NAME_H */ diff --git a/libs/harfbuzz/src/hb-ot-os2-table.hh b/libs/harfbuzz/src/hb-ot-os2-table.hh new file mode 100644 index 00000000..c6e8fad6 --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-os2-table.hh @@ -0,0 +1,361 @@ +/* + * Copyright © 2011,2012 Google, Inc. + * Copyright © 2018 Ebrahim Byagowi + * + * 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 HB_OT_OS2_TABLE_HH +#define HB_OT_OS2_TABLE_HH + +#include "hb-open-type.hh" +#include "hb-ot-os2-unicode-ranges.hh" + +#include "hb-set.hh" + +/* + * OS/2 and Windows Metrics + * https://docs.microsoft.com/en-us/typography/opentype/spec/os2 + */ +#define HB_OT_TAG_OS2 HB_TAG('O','S','/','2') + + +namespace OT { + +struct OS2V1Tail +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + public: + HBUINT32 ulCodePageRange1; + HBUINT32 ulCodePageRange2; + public: + DEFINE_SIZE_STATIC (8); +}; + +struct OS2V2Tail +{ + bool has_data () const { return sxHeight || sCapHeight; } + + const OS2V2Tail * operator -> () const { return this; } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + public: + HBINT16 sxHeight; + HBINT16 sCapHeight; + HBUINT16 usDefaultChar; + HBUINT16 usBreakChar; + HBUINT16 usMaxContext; + public: + DEFINE_SIZE_STATIC (10); +}; + +struct OS2V5Tail +{ + inline bool get_optical_size (unsigned int *lower, unsigned int *upper) const + { + unsigned int lower_optical_size = usLowerOpticalPointSize; + unsigned int upper_optical_size = usUpperOpticalPointSize; + + /* Per https://docs.microsoft.com/en-us/typography/opentype/spec/os2#lps */ + if (lower_optical_size < upper_optical_size && + lower_optical_size >= 1 && lower_optical_size <= 0xFFFE && + upper_optical_size >= 2 && upper_optical_size <= 0xFFFF) + { + *lower = lower_optical_size; + *upper = upper_optical_size; + return true; + } + return false; + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + public: + HBUINT16 usLowerOpticalPointSize; + HBUINT16 usUpperOpticalPointSize; + public: + DEFINE_SIZE_STATIC (4); +}; + +struct OS2 +{ + static constexpr hb_tag_t tableTag = HB_OT_TAG_OS2; + + bool has_data () const { return usWeightClass || usWidthClass || usFirstCharIndex || usLastCharIndex; } + + const OS2V1Tail &v1 () const { return version >= 1 ? v1X : Null (OS2V1Tail); } + const OS2V2Tail &v2 () const { return version >= 2 ? v2X : Null (OS2V2Tail); } + const OS2V5Tail &v5 () const { return version >= 5 ? v5X : Null (OS2V5Tail); } + + enum selection_flag_t { + ITALIC = 1u<<0, + UNDERSCORE = 1u<<1, + NEGATIVE = 1u<<2, + OUTLINED = 1u<<3, + STRIKEOUT = 1u<<4, + BOLD = 1u<<5, + REGULAR = 1u<<6, + USE_TYPO_METRICS = 1u<<7, + WWS = 1u<<8, + OBLIQUE = 1u<<9 + }; + + bool is_italic () const { return fsSelection & ITALIC; } + bool is_oblique () const { return fsSelection & OBLIQUE; } + bool use_typo_metrics () const { return fsSelection & USE_TYPO_METRICS; } + + enum width_class_t { + FWIDTH_ULTRA_CONDENSED = 1, /* 50% */ + FWIDTH_EXTRA_CONDENSED = 2, /* 62.5% */ + FWIDTH_CONDENSED = 3, /* 75% */ + FWIDTH_SEMI_CONDENSED = 4, /* 87.5% */ + FWIDTH_NORMAL = 5, /* 100% */ + FWIDTH_SEMI_EXPANDED = 6, /* 112.5% */ + FWIDTH_EXPANDED = 7, /* 125% */ + FWIDTH_EXTRA_EXPANDED = 8, /* 150% */ + FWIDTH_ULTRA_EXPANDED = 9 /* 200% */ + }; + + float get_width () const + { + switch (usWidthClass) { + case FWIDTH_ULTRA_CONDENSED:return 50.f; + case FWIDTH_EXTRA_CONDENSED:return 62.5f; + case FWIDTH_CONDENSED: return 75.f; + case FWIDTH_SEMI_CONDENSED: return 87.5f; + default: + case FWIDTH_NORMAL: return 100.f; + case FWIDTH_SEMI_EXPANDED: return 112.5f; + case FWIDTH_EXPANDED: return 125.f; + case FWIDTH_EXTRA_EXPANDED: return 150.f; + case FWIDTH_ULTRA_EXPANDED: return 200.f; + } + } + + float map_wdth_to_widthclass(float width) const + { + if (width < 50) return 1.0f; + if (width > 200) return 9.0f; + + float ratio = (width - 50) / 12.5f; + int a = (int) floorf (ratio); + int b = (int) ceilf (ratio); + + /* follow this maping: + * https://docs.microsoft.com/en-us/typography/opentype/spec/os2#uswidthclass + */ + if (b <= 6) // 50-125 + { + if (a == b) return a + 1.0f; + } + else if (b == 7) // no mapping for 137.5 + { + a = 6; + b = 8; + } + else if (b == 8) + { + if (a == b) return 8.0f; // 150 + a = 6; + } + else + { + if (a == b && a == 12) return 9.0f; //200 + b = 12; + a = 8; + } + + float va = 50 + a * 12.5f; + float vb = 50 + b * 12.5f; + + float ret = a + (width - va) / (vb - va); + if (a <= 6) ret += 1.0f; + return ret; + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + OS2 *os2_prime = c->serializer->embed (this); + if (unlikely (!os2_prime)) return_trace (false); + if (c->plan->flags & HB_SUBSET_FLAGS_NO_PRUNE_UNICODE_RANGES) + return_trace (true); + + /* when --gids option is not used, no need to do collect_mapping that is + * iterating all codepoints in each subtable, which is not efficient */ + uint16_t min_cp, max_cp; + find_min_and_max_codepoint (c->plan->unicodes, &min_cp, &max_cp); + os2_prime->usFirstCharIndex = min_cp; + os2_prime->usLastCharIndex = max_cp; + + _update_unicode_ranges (c->plan->unicodes, os2_prime->ulUnicodeRange); + + if (c->plan->user_axes_location->has (HB_TAG ('w','g','h','t')) && + !c->plan->pinned_at_default) + { + float weight_class = c->plan->user_axes_location->get (HB_TAG ('w','g','h','t')); + if (!c->serializer->check_assign (os2_prime->usWeightClass, + roundf (hb_clamp (weight_class, 1.0f, 1000.0f)), + HB_SERIALIZE_ERROR_INT_OVERFLOW)) + return_trace (false); + } + + if (c->plan->user_axes_location->has (HB_TAG ('w','d','t','h')) && + !c->plan->pinned_at_default) + { + float width = c->plan->user_axes_location->get (HB_TAG ('w','d','t','h')); + if (!c->serializer->check_assign (os2_prime->usWidthClass, + roundf (map_wdth_to_widthclass (width)), + HB_SERIALIZE_ERROR_INT_OVERFLOW)) + return_trace (false); + } + + return_trace (true); + } + + void _update_unicode_ranges (const hb_set_t *codepoints, + HBUINT32 ulUnicodeRange[4]) const + { + HBUINT32 newBits[4]; + for (unsigned int i = 0; i < 4; i++) + newBits[i] = 0; + + hb_codepoint_t cp = HB_SET_VALUE_INVALID; + while (codepoints->next (&cp)) { + unsigned int bit = _hb_ot_os2_get_unicode_range_bit (cp); + if (bit < 128) + { + unsigned int block = bit / 32; + unsigned int bit_in_block = bit % 32; + unsigned int mask = 1 << bit_in_block; + newBits[block] = newBits[block] | mask; + } + if (cp >= 0x10000 && cp <= 0x110000) + { + /* the spec says that bit 57 ("Non Plane 0") implies that there's + at least one codepoint beyond the BMP; so I also include all + the non-BMP codepoints here */ + newBits[1] = newBits[1] | (1 << 25); + } + } + + for (unsigned int i = 0; i < 4; i++) + ulUnicodeRange[i] = ulUnicodeRange[i] & newBits[i]; // set bits only if set in the original + } + + static void find_min_and_max_codepoint (const hb_set_t *codepoints, + uint16_t *min_cp, /* OUT */ + uint16_t *max_cp /* OUT */) + { + *min_cp = hb_min (0xFFFFu, codepoints->get_min ()); + *max_cp = hb_min (0xFFFFu, codepoints->get_max ()); + } + + /* https://github.com/Microsoft/Font-Validator/blob/520aaae/OTFontFileVal/val_OS2.cs#L644-L681 + * https://docs.microsoft.com/en-us/typography/legacy/legacy_arabic_fonts */ + enum font_page_t + { + FONT_PAGE_NONE = 0, + FONT_PAGE_HEBREW = 0xB100, /* Hebrew Windows 3.1 font page */ + FONT_PAGE_SIMP_ARABIC = 0xB200, /* Simplified Arabic Windows 3.1 font page */ + FONT_PAGE_TRAD_ARABIC = 0xB300, /* Traditional Arabic Windows 3.1 font page */ + FONT_PAGE_OEM_ARABIC = 0xB400, /* OEM Arabic Windows 3.1 font page */ + FONT_PAGE_SIMP_FARSI = 0xBA00, /* Simplified Farsi Windows 3.1 font page */ + FONT_PAGE_TRAD_FARSI = 0xBB00, /* Traditional Farsi Windows 3.1 font page */ + FONT_PAGE_THAI = 0xDE00 /* Thai Windows 3.1 font page */ + }; + font_page_t get_font_page () const + { return (font_page_t) (version == 0 ? fsSelection & 0xFF00 : 0); } + + unsigned get_size () const + { + unsigned result = min_size; + if (version >= 1) result += v1X.get_size (); + if (version >= 2) result += v2X.get_size (); + if (version >= 5) result += v5X.get_size (); + return result; + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (unlikely (!c->check_struct (this))) return_trace (false); + if (unlikely (version >= 1 && !v1X.sanitize (c))) return_trace (false); + if (unlikely (version >= 2 && !v2X.sanitize (c))) return_trace (false); + if (unlikely (version >= 5 && !v5X.sanitize (c))) return_trace (false); + return_trace (true); + } + + public: + HBUINT16 version; + HBINT16 xAvgCharWidth; + HBUINT16 usWeightClass; + HBUINT16 usWidthClass; + HBUINT16 fsType; + HBINT16 ySubscriptXSize; + HBINT16 ySubscriptYSize; + HBINT16 ySubscriptXOffset; + HBINT16 ySubscriptYOffset; + HBINT16 ySuperscriptXSize; + HBINT16 ySuperscriptYSize; + HBINT16 ySuperscriptXOffset; + HBINT16 ySuperscriptYOffset; + HBINT16 yStrikeoutSize; + HBINT16 yStrikeoutPosition; + HBINT16 sFamilyClass; + HBUINT8 panose[10]; + HBUINT32 ulUnicodeRange[4]; + Tag achVendID; + HBUINT16 fsSelection; + HBUINT16 usFirstCharIndex; + HBUINT16 usLastCharIndex; + HBINT16 sTypoAscender; + HBINT16 sTypoDescender; + HBINT16 sTypoLineGap; + HBUINT16 usWinAscent; + HBUINT16 usWinDescent; + OS2V1Tail v1X; + OS2V2Tail v2X; + OS2V5Tail v5X; + public: + DEFINE_SIZE_MIN (78); +}; + +} /* namespace OT */ + + +#endif /* HB_OT_OS2_TABLE_HH */ diff --git a/libs/harfbuzz/src/hb-ot-os2-unicode-ranges.hh b/libs/harfbuzz/src/hb-ot-os2-unicode-ranges.hh new file mode 100644 index 00000000..9613d2d1 --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-os2-unicode-ranges.hh @@ -0,0 +1,231 @@ +/* + * Copyright © 2018 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): Garret Rieger + */ + +#ifndef HB_OT_OS2_UNICODE_RANGES_HH +#define HB_OT_OS2_UNICODE_RANGES_HH + +#include "hb.hh" + +namespace OT { + +struct OS2Range +{ + int cmp (hb_codepoint_t key) const + { return (key < start) ? -1 : key <= end ? 0 : +1; } + + hb_codepoint_t start; + hb_codepoint_t end; + unsigned int bit; +}; + +/* Note: The contents of this array was generated using gen-os2-unicode-ranges.py. */ +static const OS2Range _hb_os2_unicode_ranges[] = +{ + { 0x0, 0x7F, 0}, // Basic Latin + { 0x80, 0xFF, 1}, // Latin-1 Supplement + { 0x100, 0x17F, 2}, // Latin Extended-A + { 0x180, 0x24F, 3}, // Latin Extended-B + { 0x250, 0x2AF, 4}, // IPA Extensions + { 0x2B0, 0x2FF, 5}, // Spacing Modifier Letters + { 0x300, 0x36F, 6}, // Combining Diacritical Marks + { 0x370, 0x3FF, 7}, // Greek and Coptic + { 0x400, 0x4FF, 9}, // Cyrillic + { 0x500, 0x52F, 9}, // Cyrillic Supplement + { 0x530, 0x58F, 10}, // Armenian + { 0x590, 0x5FF, 11}, // Hebrew + { 0x600, 0x6FF, 13}, // Arabic + { 0x700, 0x74F, 71}, // Syriac + { 0x750, 0x77F, 13}, // Arabic Supplement + { 0x780, 0x7BF, 72}, // Thaana + { 0x7C0, 0x7FF, 14}, // NKo + { 0x900, 0x97F, 15}, // Devanagari + { 0x980, 0x9FF, 16}, // Bengali + { 0xA00, 0xA7F, 17}, // Gurmukhi + { 0xA80, 0xAFF, 18}, // Gujarati + { 0xB00, 0xB7F, 19}, // Oriya + { 0xB80, 0xBFF, 20}, // Tamil + { 0xC00, 0xC7F, 21}, // Telugu + { 0xC80, 0xCFF, 22}, // Kannada + { 0xD00, 0xD7F, 23}, // Malayalam + { 0xD80, 0xDFF, 73}, // Sinhala + { 0xE00, 0xE7F, 24}, // Thai + { 0xE80, 0xEFF, 25}, // Lao + { 0xF00, 0xFFF, 70}, // Tibetan + { 0x1000, 0x109F, 74}, // Myanmar + { 0x10A0, 0x10FF, 26}, // Georgian + { 0x1100, 0x11FF, 28}, // Hangul Jamo + { 0x1200, 0x137F, 75}, // Ethiopic + { 0x1380, 0x139F, 75}, // Ethiopic Supplement + { 0x13A0, 0x13FF, 76}, // Cherokee + { 0x1400, 0x167F, 77}, // Unified Canadian Aboriginal Syllabics + { 0x1680, 0x169F, 78}, // Ogham + { 0x16A0, 0x16FF, 79}, // Runic + { 0x1700, 0x171F, 84}, // Tagalog + { 0x1720, 0x173F, 84}, // Hanunoo + { 0x1740, 0x175F, 84}, // Buhid + { 0x1760, 0x177F, 84}, // Tagbanwa + { 0x1780, 0x17FF, 80}, // Khmer + { 0x1800, 0x18AF, 81}, // Mongolian + { 0x1900, 0x194F, 93}, // Limbu + { 0x1950, 0x197F, 94}, // Tai Le + { 0x1980, 0x19DF, 95}, // New Tai Lue + { 0x19E0, 0x19FF, 80}, // Khmer Symbols + { 0x1A00, 0x1A1F, 96}, // Buginese + { 0x1B00, 0x1B7F, 27}, // Balinese + { 0x1B80, 0x1BBF, 112}, // Sundanese + { 0x1C00, 0x1C4F, 113}, // Lepcha + { 0x1C50, 0x1C7F, 114}, // Ol Chiki + { 0x1D00, 0x1D7F, 4}, // Phonetic Extensions + { 0x1D80, 0x1DBF, 4}, // Phonetic Extensions Supplement + { 0x1DC0, 0x1DFF, 6}, // Combining Diacritical Marks Supplement + { 0x1E00, 0x1EFF, 29}, // Latin Extended Additional + { 0x1F00, 0x1FFF, 30}, // Greek Extended + { 0x2000, 0x206F, 31}, // General Punctuation + { 0x2070, 0x209F, 32}, // Superscripts And Subscripts + { 0x20A0, 0x20CF, 33}, // Currency Symbols + { 0x20D0, 0x20FF, 34}, // Combining Diacritical Marks For Symbols + { 0x2100, 0x214F, 35}, // Letterlike Symbols + { 0x2150, 0x218F, 36}, // Number Forms + { 0x2190, 0x21FF, 37}, // Arrows + { 0x2200, 0x22FF, 38}, // Mathematical Operators + { 0x2300, 0x23FF, 39}, // Miscellaneous Technical + { 0x2400, 0x243F, 40}, // Control Pictures + { 0x2440, 0x245F, 41}, // Optical Character Recognition + { 0x2460, 0x24FF, 42}, // Enclosed Alphanumerics + { 0x2500, 0x257F, 43}, // Box Drawing + { 0x2580, 0x259F, 44}, // Block Elements + { 0x25A0, 0x25FF, 45}, // Geometric Shapes + { 0x2600, 0x26FF, 46}, // Miscellaneous Symbols + { 0x2700, 0x27BF, 47}, // Dingbats + { 0x27C0, 0x27EF, 38}, // Miscellaneous Mathematical Symbols-A + { 0x27F0, 0x27FF, 37}, // Supplemental Arrows-A + { 0x2800, 0x28FF, 82}, // Braille Patterns + { 0x2900, 0x297F, 37}, // Supplemental Arrows-B + { 0x2980, 0x29FF, 38}, // Miscellaneous Mathematical Symbols-B + { 0x2A00, 0x2AFF, 38}, // Supplemental Mathematical Operators + { 0x2B00, 0x2BFF, 37}, // Miscellaneous Symbols and Arrows + { 0x2C00, 0x2C5F, 97}, // Glagolitic + { 0x2C60, 0x2C7F, 29}, // Latin Extended-C + { 0x2C80, 0x2CFF, 8}, // Coptic + { 0x2D00, 0x2D2F, 26}, // Georgian Supplement + { 0x2D30, 0x2D7F, 98}, // Tifinagh + { 0x2D80, 0x2DDF, 75}, // Ethiopic Extended + { 0x2DE0, 0x2DFF, 9}, // Cyrillic Extended-A + { 0x2E00, 0x2E7F, 31}, // Supplemental Punctuation + { 0x2E80, 0x2EFF, 59}, // CJK Radicals Supplement + { 0x2F00, 0x2FDF, 59}, // Kangxi Radicals + { 0x2FF0, 0x2FFF, 59}, // Ideographic Description Characters + { 0x3000, 0x303F, 48}, // CJK Symbols And Punctuation + { 0x3040, 0x309F, 49}, // Hiragana + { 0x30A0, 0x30FF, 50}, // Katakana + { 0x3100, 0x312F, 51}, // Bopomofo + { 0x3130, 0x318F, 52}, // Hangul Compatibility Jamo + { 0x3190, 0x319F, 59}, // Kanbun + { 0x31A0, 0x31BF, 51}, // Bopomofo Extended + { 0x31C0, 0x31EF, 61}, // CJK Strokes + { 0x31F0, 0x31FF, 50}, // Katakana Phonetic Extensions + { 0x3200, 0x32FF, 54}, // Enclosed CJK Letters And Months + { 0x3300, 0x33FF, 55}, // CJK Compatibility + { 0x3400, 0x4DBF, 59}, // CJK Unified Ideographs Extension A + { 0x4DC0, 0x4DFF, 99}, // Yijing Hexagram Symbols + { 0x4E00, 0x9FFF, 59}, // CJK Unified Ideographs + { 0xA000, 0xA48F, 83}, // Yi Syllables + { 0xA490, 0xA4CF, 83}, // Yi Radicals + { 0xA500, 0xA63F, 12}, // Vai + { 0xA640, 0xA69F, 9}, // Cyrillic Extended-B + { 0xA700, 0xA71F, 5}, // Modifier Tone Letters + { 0xA720, 0xA7FF, 29}, // Latin Extended-D + { 0xA800, 0xA82F, 100}, // Syloti Nagri + { 0xA840, 0xA87F, 53}, // Phags-pa + { 0xA880, 0xA8DF, 115}, // Saurashtra + { 0xA900, 0xA92F, 116}, // Kayah Li + { 0xA930, 0xA95F, 117}, // Rejang + { 0xAA00, 0xAA5F, 118}, // Cham + { 0xAC00, 0xD7AF, 56}, // Hangul Syllables + { 0xD800, 0xDFFF, 57}, // Non-Plane 0 * + { 0xE000, 0xF8FF, 60}, // Private Use Area (plane 0) + { 0xF900, 0xFAFF, 61}, // CJK Compatibility Ideographs + { 0xFB00, 0xFB4F, 62}, // Alphabetic Presentation Forms + { 0xFB50, 0xFDFF, 63}, // Arabic Presentation Forms-A + { 0xFE00, 0xFE0F, 91}, // Variation Selectors + { 0xFE10, 0xFE1F, 65}, // Vertical Forms + { 0xFE20, 0xFE2F, 64}, // Combining Half Marks + { 0xFE30, 0xFE4F, 65}, // CJK Compatibility Forms + { 0xFE50, 0xFE6F, 66}, // Small Form Variants + { 0xFE70, 0xFEFF, 67}, // Arabic Presentation Forms-B + { 0xFF00, 0xFFEF, 68}, // Halfwidth And Fullwidth Forms + { 0xFFF0, 0xFFFF, 69}, // Specials + { 0x10000, 0x1007F, 101}, // Linear B Syllabary + { 0x10080, 0x100FF, 101}, // Linear B Ideograms + { 0x10100, 0x1013F, 101}, // Aegean Numbers + { 0x10140, 0x1018F, 102}, // Ancient Greek Numbers + { 0x10190, 0x101CF, 119}, // Ancient Symbols + { 0x101D0, 0x101FF, 120}, // Phaistos Disc + { 0x10280, 0x1029F, 121}, // Lycian + { 0x102A0, 0x102DF, 121}, // Carian + { 0x10300, 0x1032F, 85}, // Old Italic + { 0x10330, 0x1034F, 86}, // Gothic + { 0x10380, 0x1039F, 103}, // Ugaritic + { 0x103A0, 0x103DF, 104}, // Old Persian + { 0x10400, 0x1044F, 87}, // Deseret + { 0x10450, 0x1047F, 105}, // Shavian + { 0x10480, 0x104AF, 106}, // Osmanya + { 0x10800, 0x1083F, 107}, // Cypriot Syllabary + { 0x10900, 0x1091F, 58}, // Phoenician + { 0x10920, 0x1093F, 121}, // Lydian + { 0x10A00, 0x10A5F, 108}, // Kharoshthi + { 0x12000, 0x123FF, 110}, // Cuneiform + { 0x12400, 0x1247F, 110}, // Cuneiform Numbers and Punctuation + { 0x1D000, 0x1D0FF, 88}, // Byzantine Musical Symbols + { 0x1D100, 0x1D1FF, 88}, // Musical Symbols + { 0x1D200, 0x1D24F, 88}, // Ancient Greek Musical Notation + { 0x1D300, 0x1D35F, 109}, // Tai Xuan Jing Symbols + { 0x1D360, 0x1D37F, 111}, // Counting Rod Numerals + { 0x1D400, 0x1D7FF, 89}, // Mathematical Alphanumeric Symbols + { 0x1F000, 0x1F02F, 122}, // Mahjong Tiles + { 0x1F030, 0x1F09F, 122}, // Domino Tiles + { 0x20000, 0x2A6DF, 59}, // CJK Unified Ideographs Extension B + { 0x2F800, 0x2FA1F, 61}, // CJK Compatibility Ideographs Supplement + { 0xE0000, 0xE007F, 92}, // Tags + { 0xE0100, 0xE01EF, 91}, // Variation Selectors Supplement + { 0xF0000, 0xFFFFD, 90}, // Private Use (plane 15) + {0x100000, 0x10FFFD, 90}, // Private Use (plane 16) +}; + +/** + * _hb_ot_os2_get_unicode_range_bit: + * Returns the bit to be set in os/2 ulUnicodeOS2Range for a given codepoint. + **/ +static unsigned int +_hb_ot_os2_get_unicode_range_bit (hb_codepoint_t cp) +{ + auto *range = hb_sorted_array (_hb_os2_unicode_ranges).bsearch (cp); + return range ? range->bit : -1; +} + +} /* namespace OT */ + +#endif /* HB_OT_OS2_UNICODE_RANGES_HH */ diff --git a/libs/harfbuzz/src/hb-ot-post-macroman.hh b/libs/harfbuzz/src/hb-ot-post-macroman.hh new file mode 100644 index 00000000..b4df8aae --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-post-macroman.hh @@ -0,0 +1,294 @@ +/* + * Copyright © 2017 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 HB_OT_POST_MACROMAN_HH +#if 0 /* Make checks happy. */ +#define HB_OT_POST_MACROMAN_HH +#include "hb.hh" +#endif + + +_S(".notdef") +_S(".null") +_S("nonmarkingreturn") +_S("space") +_S("exclam") +_S("quotedbl") +_S("numbersign") +_S("dollar") +_S("percent") +_S("ampersand") +_S("quotesingle") +_S("parenleft") +_S("parenright") +_S("asterisk") +_S("plus") +_S("comma") +_S("hyphen") +_S("period") +_S("slash") +_S("zero") +_S("one") +_S("two") +_S("three") +_S("four") +_S("five") +_S("six") +_S("seven") +_S("eight") +_S("nine") +_S("colon") +_S("semicolon") +_S("less") +_S("equal") +_S("greater") +_S("question") +_S("at") +_S("A") +_S("B") +_S("C") +_S("D") +_S("E") +_S("F") +_S("G") +_S("H") +_S("I") +_S("J") +_S("K") +_S("L") +_S("M") +_S("N") +_S("O") +_S("P") +_S("Q") +_S("R") +_S("S") +_S("T") +_S("U") +_S("V") +_S("W") +_S("X") +_S("Y") +_S("Z") +_S("bracketleft") +_S("backslash") +_S("bracketright") +_S("asciicircum") +_S("underscore") +_S("grave") +_S("a") +_S("b") +_S("c") +_S("d") +_S("e") +_S("f") +_S("g") +_S("h") +_S("i") +_S("j") +_S("k") +_S("l") +_S("m") +_S("n") +_S("o") +_S("p") +_S("q") +_S("r") +_S("s") +_S("t") +_S("u") +_S("v") +_S("w") +_S("x") +_S("y") +_S("z") +_S("braceleft") +_S("bar") +_S("braceright") +_S("asciitilde") +_S("Adieresis") +_S("Aring") +_S("Ccedilla") +_S("Eacute") +_S("Ntilde") +_S("Odieresis") +_S("Udieresis") +_S("aacute") +_S("agrave") +_S("acircumflex") +_S("adieresis") +_S("atilde") +_S("aring") +_S("ccedilla") +_S("eacute") +_S("egrave") +_S("ecircumflex") +_S("edieresis") +_S("iacute") +_S("igrave") +_S("icircumflex") +_S("idieresis") +_S("ntilde") +_S("oacute") +_S("ograve") +_S("ocircumflex") +_S("odieresis") +_S("otilde") +_S("uacute") +_S("ugrave") +_S("ucircumflex") +_S("udieresis") +_S("dagger") +_S("degree") +_S("cent") +_S("sterling") +_S("section") +_S("bullet") +_S("paragraph") +_S("germandbls") +_S("registered") +_S("copyright") +_S("trademark") +_S("acute") +_S("dieresis") +_S("notequal") +_S("AE") +_S("Oslash") +_S("infinity") +_S("plusminus") +_S("lessequal") +_S("greaterequal") +_S("yen") +_S("mu") +_S("partialdiff") +_S("summation") +_S("product") +_S("pi") +_S("integral") +_S("ordfeminine") +_S("ordmasculine") +_S("Omega") +_S("ae") +_S("oslash") +_S("questiondown") +_S("exclamdown") +_S("logicalnot") +_S("radical") +_S("florin") +_S("approxequal") +_S("Delta") +_S("guillemotleft") +_S("guillemotright") +_S("ellipsis") +_S("nonbreakingspace") +_S("Agrave") +_S("Atilde") +_S("Otilde") +_S("OE") +_S("oe") +_S("endash") +_S("emdash") +_S("quotedblleft") +_S("quotedblright") +_S("quoteleft") +_S("quoteright") +_S("divide") +_S("lozenge") +_S("ydieresis") +_S("Ydieresis") +_S("fraction") +_S("currency") +_S("guilsinglleft") +_S("guilsinglright") +_S("fi") +_S("fl") +_S("daggerdbl") +_S("periodcentered") +_S("quotesinglbase") +_S("quotedblbase") +_S("perthousand") +_S("Acircumflex") +_S("Ecircumflex") +_S("Aacute") +_S("Edieresis") +_S("Egrave") +_S("Iacute") +_S("Icircumflex") +_S("Idieresis") +_S("Igrave") +_S("Oacute") +_S("Ocircumflex") +_S("apple") +_S("Ograve") +_S("Uacute") +_S("Ucircumflex") +_S("Ugrave") +_S("dotlessi") +_S("circumflex") +_S("tilde") +_S("macron") +_S("breve") +_S("dotaccent") +_S("ring") +_S("cedilla") +_S("hungarumlaut") +_S("ogonek") +_S("caron") +_S("Lslash") +_S("lslash") +_S("Scaron") +_S("scaron") +_S("Zcaron") +_S("zcaron") +_S("brokenbar") +_S("Eth") +_S("eth") +_S("Yacute") +_S("yacute") +_S("Thorn") +_S("thorn") +_S("minus") +_S("multiply") +_S("onesuperior") +_S("twosuperior") +_S("threesuperior") +_S("onehalf") +_S("onequarter") +_S("threequarters") +_S("franc") +_S("Gbreve") +_S("gbreve") +_S("Idotaccent") +_S("Scedilla") +_S("scedilla") +_S("Cacute") +_S("cacute") +_S("Ccaron") +_S("ccaron") +_S("dcroat") + + +#endif /* HB_OT_POST_MACROMAN_HH */ diff --git a/libs/harfbuzz/src/hb-ot-post-table-v2subset.hh b/libs/harfbuzz/src/hb-ot-post-table-v2subset.hh new file mode 100644 index 00000000..4d427e54 --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-post-table-v2subset.hh @@ -0,0 +1,136 @@ +/* + * Copyright © 2021 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. + * + */ + +#ifndef HB_OT_POST_TABLE_V2SUBSET_HH +#define HB_OT_POST_TABLE_V2SUBSET_HH + +#include "hb-open-type.hh" +#include "hb-ot-post-table.hh" + +/* + * post -- PostScript + * https://docs.microsoft.com/en-us/typography/opentype/spec/post + */ + +namespace OT { +template +HB_INTERNAL bool postV2Tail::serialize (hb_serialize_context_t *c, + Iterator it, + const void* _post) const +{ + TRACE_SERIALIZE (this); + auto *out = c->start_embed (this); + if (unlikely (!c->check_success (out))) return_trace (false); + if (!out->glyphNameIndex.serialize (c, + it + | hb_map (hb_second))) + return_trace (false); + + hb_set_t copied_indices; + for (const auto& _ : + it ) + { + unsigned glyph_id = _.first; + unsigned new_index = _.second; + + if (new_index < 258) continue; + if (copied_indices.has (new_index)) continue; + copied_indices.add (new_index); + + hb_bytes_t s = reinterpret_cast (_post)->find_glyph_name (glyph_id); + HBUINT8 *o = c->allocate_size (HBUINT8::static_size * (s.length + 1)); + if (unlikely (!o)) return_trace (false); + if (!c->check_assign (o[0], s.length, HB_SERIALIZE_ERROR_INT_OVERFLOW)) return_trace (false); + hb_memcpy (o+1, s.arrayZ, HBUINT8::static_size * s.length); + } + + return_trace (true); +} + +HB_INTERNAL bool postV2Tail::subset (hb_subset_context_t *c) const +{ + TRACE_SUBSET (this); + + const hb_map_t &reverse_glyph_map = *c->plan->reverse_glyph_map; + unsigned num_glyphs = c->plan->num_output_glyphs (); + hb_map_t old_new_index_map, old_gid_new_index_map; + unsigned i = 0; + + post::accelerator_t _post (c->plan->source); + + hb_hashmap_t glyph_name_to_new_index; + for (hb_codepoint_t new_gid = 0; new_gid < num_glyphs; new_gid++) + { + hb_codepoint_t old_gid = reverse_glyph_map.get (new_gid); + unsigned old_index = glyphNameIndex[old_gid]; + + unsigned new_index; + const unsigned *new_index2; + if (old_index <= 257) new_index = old_index; + else if (old_new_index_map.has (old_index, &new_index2)) + { + new_index = *new_index2; + } else { + hb_bytes_t s = _post.find_glyph_name (old_gid); + new_index = glyph_name_to_new_index.get (s); + if (new_index == (unsigned)-1) + { + int standard_glyph_index = -1; + for (unsigned i = 0; i < format1_names_length; i++) + { + if (s == format1_names (i)) + { + standard_glyph_index = i; + break; + } + } + + if (standard_glyph_index == -1) + { + new_index = 258 + i; + i++; + } + else + { new_index = standard_glyph_index; } + glyph_name_to_new_index.set (s, new_index); + } + old_new_index_map.set (old_index, new_index); + } + old_gid_new_index_map.set (old_gid, new_index); + } + + auto index_iter = + + hb_range (num_glyphs) + | hb_map (reverse_glyph_map) + | hb_map_retains_sorting ([&](hb_codepoint_t old_gid) + { + unsigned new_index = old_gid_new_index_map.get (old_gid); + return hb_pair_t (old_gid, new_index); + }) + ; + + return_trace (serialize (c->serializer, index_iter, &_post)); +} + +} /* namespace OT */ +#endif /* HB_OT_POST_TABLE_V2SUBSET_HH */ diff --git a/libs/harfbuzz/src/hb-ot-post-table.hh b/libs/harfbuzz/src/hb-ot-post-table.hh new file mode 100644 index 00000000..59c1de37 --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-post-table.hh @@ -0,0 +1,324 @@ +/* + * Copyright © 2016 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 HB_OT_POST_TABLE_HH +#define HB_OT_POST_TABLE_HH + +#include "hb-open-type.hh" + +#define HB_STRING_ARRAY_NAME format1_names +#define HB_STRING_ARRAY_LIST "hb-ot-post-macroman.hh" +#include "hb-string-array.hh" +#undef HB_STRING_ARRAY_LIST +#undef HB_STRING_ARRAY_NAME + +/* + * post -- PostScript + * https://docs.microsoft.com/en-us/typography/opentype/spec/post + */ +#define HB_OT_TAG_post HB_TAG('p','o','s','t') + + +namespace OT { + + +struct postV2Tail +{ + friend struct post; + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (glyphNameIndex.sanitize (c)); + } + + template + bool serialize (hb_serialize_context_t *c, + Iterator it, + const void* _post) const; + + bool subset (hb_subset_context_t *c) const; + + protected: + Array16Of glyphNameIndex; /* This is not an offset, but is the + * ordinal number of the glyph in 'post' + * string tables. */ +/*UnsizedArrayOf + namesX;*/ /* Glyph names with length bytes [variable] + * (a Pascal string). */ + + public: + DEFINE_SIZE_ARRAY (2, glyphNameIndex); +}; + +struct post +{ + static constexpr hb_tag_t tableTag = HB_OT_TAG_post; + + bool serialize (hb_serialize_context_t *c, bool glyph_names) const + { + TRACE_SERIALIZE (this); + post *post_prime = c->allocate_min (); + if (unlikely (!post_prime)) return_trace (false); + + memcpy (post_prime, this, post::min_size); + if (!glyph_names) + return_trace (c->check_assign (post_prime->version.major, 3, + HB_SERIALIZE_ERROR_INT_OVERFLOW)); // Version 3 does not have any glyph names. + + return_trace (true); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + post *post_prime = c->serializer->start_embed (); + if (unlikely (!post_prime)) return_trace (false); + + bool glyph_names = c->plan->flags & HB_SUBSET_FLAGS_GLYPH_NAMES; + if (!serialize (c->serializer, glyph_names)) + return_trace (false); + + if (c->plan->user_axes_location->has (HB_TAG ('s','l','n','t')) && + !c->plan->pinned_at_default) + { + float italic_angle = c->plan->user_axes_location->get (HB_TAG ('s','l','n','t')); + italic_angle = hb_max (-90.f, hb_min (italic_angle, 90.f)); + post_prime->italicAngle.set_float (italic_angle); + } + + if (glyph_names && version.major == 2) + return_trace (v2X.subset (c)); + + return_trace (true); + } + + struct accelerator_t + { + friend struct postV2Tail; + + accelerator_t (hb_face_t *face) + { + table = hb_sanitize_context_t ().reference_table (face); + unsigned int table_length = table.get_length (); + + version = table->version.to_int (); + if (version != 0x00020000) return; + + const postV2Tail &v2 = table->v2X; + + glyphNameIndex = &v2.glyphNameIndex; + pool = &StructAfter (v2.glyphNameIndex); + + const uint8_t *end = (const uint8_t *) (const void *) table + table_length; + for (const uint8_t *data = pool; + index_to_offset.length < 65535 && data < end && data + *data < end; + data += 1 + *data) + index_to_offset.push (data - pool); + } + ~accelerator_t () + { + hb_free (gids_sorted_by_name.get_acquire ()); + table.destroy (); + } + + bool get_glyph_name (hb_codepoint_t glyph, + char *buf, unsigned int buf_len) const + { + hb_bytes_t s = find_glyph_name (glyph); + if (!s.length) return false; + if (!buf_len) return true; + unsigned int len = hb_min (buf_len - 1, s.length); + strncpy (buf, s.arrayZ, len); + buf[len] = '\0'; + return true; + } + + bool get_glyph_from_name (const char *name, int len, + hb_codepoint_t *glyph) const + { + unsigned int count = get_glyph_count (); + if (unlikely (!count)) return false; + + if (len < 0) len = strlen (name); + + if (unlikely (!len)) return false; + + retry: + uint16_t *gids = gids_sorted_by_name.get_acquire (); + + if (unlikely (!gids)) + { + gids = (uint16_t *) hb_malloc (count * sizeof (gids[0])); + if (unlikely (!gids)) + return false; /* Anything better?! */ + + for (unsigned int i = 0; i < count; i++) + gids[i] = i; + hb_qsort (gids, count, sizeof (gids[0]), cmp_gids, (void *) this); + + if (unlikely (!gids_sorted_by_name.cmpexch (nullptr, gids))) + { + hb_free (gids); + goto retry; + } + } + + hb_bytes_t st (name, len); + auto* gid = hb_bsearch (st, gids, count, sizeof (gids[0]), cmp_key, (void *) this); + if (gid) + { + *glyph = *gid; + return true; + } + + return false; + } + + hb_blob_ptr_t table; + + protected: + + unsigned int get_glyph_count () const + { + if (version == 0x00010000) + return format1_names_length; + + if (version == 0x00020000) + return glyphNameIndex->len; + + return 0; + } + + static int cmp_gids (const void *pa, const void *pb, void *arg) + { + const accelerator_t *thiz = (const accelerator_t *) arg; + uint16_t a = * (const uint16_t *) pa; + uint16_t b = * (const uint16_t *) pb; + return thiz->find_glyph_name (b).cmp (thiz->find_glyph_name (a)); + } + + static int cmp_key (const void *pk, const void *po, void *arg) + { + const accelerator_t *thiz = (const accelerator_t *) arg; + const hb_bytes_t *key = (const hb_bytes_t *) pk; + uint16_t o = * (const uint16_t *) po; + return thiz->find_glyph_name (o).cmp (*key); + } + + hb_bytes_t find_glyph_name (hb_codepoint_t glyph) const + { + if (version == 0x00010000) + { + if (glyph >= format1_names_length) + return hb_bytes_t (); + + return format1_names (glyph); + } + + if (version != 0x00020000 || glyph >= glyphNameIndex->len) + return hb_bytes_t (); + + unsigned int index = glyphNameIndex->arrayZ[glyph]; + if (index < format1_names_length) + return format1_names (index); + index -= format1_names_length; + + if (index >= index_to_offset.length) + return hb_bytes_t (); + unsigned int offset = index_to_offset[index]; + + const uint8_t *data = pool + offset; + unsigned int name_length = *data; + data++; + + return hb_bytes_t ((const char *) data, name_length); + } + + private: + uint32_t version; + const Array16Of *glyphNameIndex = nullptr; + hb_vector_t index_to_offset; + const uint8_t *pool = nullptr; + hb_atomic_ptr_t gids_sorted_by_name; + }; + + bool has_data () const { return version.to_int (); } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + (version.to_int () == 0x00010000 || + (version.to_int () == 0x00020000 && v2X.sanitize (c)) || + version.to_int () == 0x00030000)); + } + + public: + FixedVersion<>version; /* 0x00010000 for version 1.0 + * 0x00020000 for version 2.0 + * 0x00025000 for version 2.5 (deprecated) + * 0x00030000 for version 3.0 */ + F16DOT16 italicAngle; /* Italic angle in counter-clockwise degrees + * from the vertical. Zero for upright text, + * negative for text that leans to the right + * (forward). */ + FWORD underlinePosition; /* This is the suggested distance of the top + * of the underline from the baseline + * (negative values indicate below baseline). + * The PostScript definition of this FontInfo + * dictionary key (the y coordinate of the + * center of the stroke) is not used for + * historical reasons. The value of the + * PostScript key may be calculated by + * subtracting half the underlineThickness + * from the value of this field. */ + FWORD underlineThickness; /* Suggested values for the underline + thickness. */ + HBUINT32 isFixedPitch; /* Set to 0 if the font is proportionally + * spaced, non-zero if the font is not + * proportionally spaced (i.e. monospaced). */ + HBUINT32 minMemType42; /* Minimum memory usage when an OpenType font + * is downloaded. */ + HBUINT32 maxMemType42; /* Maximum memory usage when an OpenType font + * is downloaded. */ + HBUINT32 minMemType1; /* Minimum memory usage when an OpenType font + * is downloaded as a Type 1 font. */ + HBUINT32 maxMemType1; /* Maximum memory usage when an OpenType font + * is downloaded as a Type 1 font. */ + postV2Tail v2X; + DEFINE_SIZE_MIN (32); +}; + +struct post_accelerator_t : post::accelerator_t { + post_accelerator_t (hb_face_t *face) : post::accelerator_t (face) {} +}; + + +} /* namespace OT */ + + +#endif /* HB_OT_POST_TABLE_HH */ diff --git a/libs/harfbuzz/src/hb-ot-shape-fallback.cc b/libs/harfbuzz/src/hb-ot-shape-fallback.cc new file mode 100644 index 00000000..b2eedb02 --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-shape-fallback.cc @@ -0,0 +1,615 @@ +/* + * Copyright © 2011,2012 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 + */ + +#include "hb.hh" + +#ifndef HB_NO_OT_SHAPE + +#include "hb-ot-shape-fallback.hh" +#include "hb-kern.hh" + +static unsigned int +recategorize_combining_class (hb_codepoint_t u, + unsigned int klass) +{ + if (klass >= 200) + return klass; + + /* Thai / Lao need some per-character work. */ + if ((u & ~0xFF) == 0x0E00u) + { + if (unlikely (klass == 0)) + { + switch (u) + { + case 0x0E31u: + case 0x0E34u: + case 0x0E35u: + case 0x0E36u: + case 0x0E37u: + case 0x0E47u: + case 0x0E4Cu: + case 0x0E4Du: + case 0x0E4Eu: + klass = HB_UNICODE_COMBINING_CLASS_ABOVE_RIGHT; + break; + + case 0x0EB1u: + case 0x0EB4u: + case 0x0EB5u: + case 0x0EB6u: + case 0x0EB7u: + case 0x0EBBu: + case 0x0ECCu: + case 0x0ECDu: + klass = HB_UNICODE_COMBINING_CLASS_ABOVE; + break; + + case 0x0EBCu: + klass = HB_UNICODE_COMBINING_CLASS_BELOW; + break; + } + } else { + /* Thai virama is below-right */ + if (u == 0x0E3Au) + klass = HB_UNICODE_COMBINING_CLASS_BELOW_RIGHT; + } + } + + switch (klass) + { + + /* Hebrew */ + + case HB_MODIFIED_COMBINING_CLASS_CCC10: /* sheva */ + case HB_MODIFIED_COMBINING_CLASS_CCC11: /* hataf segol */ + case HB_MODIFIED_COMBINING_CLASS_CCC12: /* hataf patah */ + case HB_MODIFIED_COMBINING_CLASS_CCC13: /* hataf qamats */ + case HB_MODIFIED_COMBINING_CLASS_CCC14: /* hiriq */ + case HB_MODIFIED_COMBINING_CLASS_CCC15: /* tsere */ + case HB_MODIFIED_COMBINING_CLASS_CCC16: /* segol */ + case HB_MODIFIED_COMBINING_CLASS_CCC17: /* patah */ + case HB_MODIFIED_COMBINING_CLASS_CCC18: /* qamats & qamats qatan */ + case HB_MODIFIED_COMBINING_CLASS_CCC20: /* qubuts */ + case HB_MODIFIED_COMBINING_CLASS_CCC22: /* meteg */ + return HB_UNICODE_COMBINING_CLASS_BELOW; + + case HB_MODIFIED_COMBINING_CLASS_CCC23: /* rafe */ + return HB_UNICODE_COMBINING_CLASS_ATTACHED_ABOVE; + + case HB_MODIFIED_COMBINING_CLASS_CCC24: /* shin dot */ + return HB_UNICODE_COMBINING_CLASS_ABOVE_RIGHT; + + case HB_MODIFIED_COMBINING_CLASS_CCC25: /* sin dot */ + case HB_MODIFIED_COMBINING_CLASS_CCC19: /* holam & holam haser for vav */ + return HB_UNICODE_COMBINING_CLASS_ABOVE_LEFT; + + case HB_MODIFIED_COMBINING_CLASS_CCC26: /* point varika */ + return HB_UNICODE_COMBINING_CLASS_ABOVE; + + case HB_MODIFIED_COMBINING_CLASS_CCC21: /* dagesh */ + break; + + + /* Arabic and Syriac */ + + case HB_MODIFIED_COMBINING_CLASS_CCC27: /* fathatan */ + case HB_MODIFIED_COMBINING_CLASS_CCC28: /* dammatan */ + case HB_MODIFIED_COMBINING_CLASS_CCC30: /* fatha */ + case HB_MODIFIED_COMBINING_CLASS_CCC31: /* damma */ + case HB_MODIFIED_COMBINING_CLASS_CCC33: /* shadda */ + case HB_MODIFIED_COMBINING_CLASS_CCC34: /* sukun */ + case HB_MODIFIED_COMBINING_CLASS_CCC35: /* superscript alef */ + case HB_MODIFIED_COMBINING_CLASS_CCC36: /* superscript alaph */ + return HB_UNICODE_COMBINING_CLASS_ABOVE; + + case HB_MODIFIED_COMBINING_CLASS_CCC29: /* kasratan */ + case HB_MODIFIED_COMBINING_CLASS_CCC32: /* kasra */ + return HB_UNICODE_COMBINING_CLASS_BELOW; + + + /* Thai */ + + case HB_MODIFIED_COMBINING_CLASS_CCC103: /* sara u / sara uu */ + return HB_UNICODE_COMBINING_CLASS_BELOW_RIGHT; + + case HB_MODIFIED_COMBINING_CLASS_CCC107: /* mai */ + return HB_UNICODE_COMBINING_CLASS_ABOVE_RIGHT; + + + /* Lao */ + + case HB_MODIFIED_COMBINING_CLASS_CCC118: /* sign u / sign uu */ + return HB_UNICODE_COMBINING_CLASS_BELOW; + + case HB_MODIFIED_COMBINING_CLASS_CCC122: /* mai */ + return HB_UNICODE_COMBINING_CLASS_ABOVE; + + + /* Tibetan */ + + case HB_MODIFIED_COMBINING_CLASS_CCC129: /* sign aa */ + return HB_UNICODE_COMBINING_CLASS_BELOW; + + case HB_MODIFIED_COMBINING_CLASS_CCC130: /* sign i*/ + return HB_UNICODE_COMBINING_CLASS_ABOVE; + + case HB_MODIFIED_COMBINING_CLASS_CCC132: /* sign u */ + return HB_UNICODE_COMBINING_CLASS_BELOW; + + } + + return klass; +} + +void +_hb_ot_shape_fallback_mark_position_recategorize_marks (const hb_ot_shape_plan_t *plan HB_UNUSED, + hb_font_t *font HB_UNUSED, + hb_buffer_t *buffer) +{ +#ifdef HB_NO_OT_SHAPE_FALLBACK + return; +#endif + + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; + for (unsigned int i = 0; i < count; i++) + if (_hb_glyph_info_get_general_category (&info[i]) == HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK) { + unsigned int combining_class = _hb_glyph_info_get_modified_combining_class (&info[i]); + combining_class = recategorize_combining_class (info[i].codepoint, combining_class); + _hb_glyph_info_set_modified_combining_class (&info[i], combining_class); + } +} + + +static void +zero_mark_advances (hb_buffer_t *buffer, + unsigned int start, + unsigned int end, + bool adjust_offsets_when_zeroing) +{ + hb_glyph_info_t *info = buffer->info; + for (unsigned int i = start; i < end; i++) + if (_hb_glyph_info_get_general_category (&info[i]) == HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK) + { + if (adjust_offsets_when_zeroing) + { + buffer->pos[i].x_offset -= buffer->pos[i].x_advance; + buffer->pos[i].y_offset -= buffer->pos[i].y_advance; + } + buffer->pos[i].x_advance = 0; + buffer->pos[i].y_advance = 0; + } +} + +static inline void +position_mark (const hb_ot_shape_plan_t *plan HB_UNUSED, + hb_font_t *font, + hb_buffer_t *buffer, + hb_glyph_extents_t &base_extents, + unsigned int i, + unsigned int combining_class) +{ + hb_glyph_extents_t mark_extents; + if (!font->get_glyph_extents (buffer->info[i].codepoint, &mark_extents)) + return; + + hb_position_t y_gap = font->y_scale / 16; + + hb_glyph_position_t &pos = buffer->pos[i]; + pos.x_offset = pos.y_offset = 0; + + + /* We don't position LEFT and RIGHT marks. */ + + /* X positioning */ + switch (combining_class) + { + case HB_UNICODE_COMBINING_CLASS_DOUBLE_BELOW: + case HB_UNICODE_COMBINING_CLASS_DOUBLE_ABOVE: + if (buffer->props.direction == HB_DIRECTION_LTR) { + pos.x_offset += base_extents.x_bearing + base_extents.width - mark_extents.width / 2 - mark_extents.x_bearing; + break; + } else if (buffer->props.direction == HB_DIRECTION_RTL) { + pos.x_offset += base_extents.x_bearing - mark_extents.width / 2 - mark_extents.x_bearing; + break; + } + HB_FALLTHROUGH; + + default: + case HB_UNICODE_COMBINING_CLASS_ATTACHED_BELOW: + case HB_UNICODE_COMBINING_CLASS_ATTACHED_ABOVE: + case HB_UNICODE_COMBINING_CLASS_BELOW: + case HB_UNICODE_COMBINING_CLASS_ABOVE: + /* Center align. */ + pos.x_offset += base_extents.x_bearing + (base_extents.width - mark_extents.width) / 2 - mark_extents.x_bearing; + break; + + case HB_UNICODE_COMBINING_CLASS_ATTACHED_BELOW_LEFT: + case HB_UNICODE_COMBINING_CLASS_BELOW_LEFT: + case HB_UNICODE_COMBINING_CLASS_ABOVE_LEFT: + /* Left align. */ + pos.x_offset += base_extents.x_bearing - mark_extents.x_bearing; + break; + + case HB_UNICODE_COMBINING_CLASS_ATTACHED_ABOVE_RIGHT: + case HB_UNICODE_COMBINING_CLASS_BELOW_RIGHT: + case HB_UNICODE_COMBINING_CLASS_ABOVE_RIGHT: + /* Right align. */ + pos.x_offset += base_extents.x_bearing + base_extents.width - mark_extents.width - mark_extents.x_bearing; + break; + } + + /* Y positioning */ + switch (combining_class) + { + case HB_UNICODE_COMBINING_CLASS_DOUBLE_BELOW: + case HB_UNICODE_COMBINING_CLASS_BELOW_LEFT: + case HB_UNICODE_COMBINING_CLASS_BELOW: + case HB_UNICODE_COMBINING_CLASS_BELOW_RIGHT: + /* Add gap, fall-through. */ + base_extents.height -= y_gap; + HB_FALLTHROUGH; + + case HB_UNICODE_COMBINING_CLASS_ATTACHED_BELOW_LEFT: + case HB_UNICODE_COMBINING_CLASS_ATTACHED_BELOW: + pos.y_offset = base_extents.y_bearing + base_extents.height - mark_extents.y_bearing; + /* Never shift up "below" marks. */ + if ((y_gap > 0) == (pos.y_offset > 0)) + { + base_extents.height -= pos.y_offset; + pos.y_offset = 0; + } + base_extents.height += mark_extents.height; + break; + + case HB_UNICODE_COMBINING_CLASS_DOUBLE_ABOVE: + case HB_UNICODE_COMBINING_CLASS_ABOVE_LEFT: + case HB_UNICODE_COMBINING_CLASS_ABOVE: + case HB_UNICODE_COMBINING_CLASS_ABOVE_RIGHT: + /* Add gap, fall-through. */ + base_extents.y_bearing += y_gap; + base_extents.height -= y_gap; + HB_FALLTHROUGH; + + case HB_UNICODE_COMBINING_CLASS_ATTACHED_ABOVE: + case HB_UNICODE_COMBINING_CLASS_ATTACHED_ABOVE_RIGHT: + pos.y_offset = base_extents.y_bearing - (mark_extents.y_bearing + mark_extents.height); + /* Don't shift down "above" marks too much. */ + if ((y_gap > 0) != (pos.y_offset > 0)) + { + int correction = -pos.y_offset / 2; + base_extents.y_bearing += correction; + base_extents.height -= correction; + pos.y_offset += correction; + } + base_extents.y_bearing -= mark_extents.height; + base_extents.height += mark_extents.height; + break; + } +} + +static inline void +position_around_base (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer, + unsigned int base, + unsigned int end, + bool adjust_offsets_when_zeroing) +{ + hb_direction_t horiz_dir = HB_DIRECTION_INVALID; + + buffer->unsafe_to_break (base, end); + + hb_glyph_extents_t base_extents; + if (!font->get_glyph_extents (buffer->info[base].codepoint, + &base_extents)) + { + /* If extents don't work, zero marks and go home. */ + zero_mark_advances (buffer, base + 1, end, adjust_offsets_when_zeroing); + return; + } + base_extents.y_bearing += buffer->pos[base].y_offset; + /* Use horizontal advance for horizontal positioning. + * Generally a better idea. Also works for zero-ink glyphs. See: + * https://github.com/harfbuzz/harfbuzz/issues/1532 */ + base_extents.x_bearing = 0; + base_extents.width = font->get_glyph_h_advance (buffer->info[base].codepoint); + + unsigned int lig_id = _hb_glyph_info_get_lig_id (&buffer->info[base]); + /* Use integer for num_lig_components such that it doesn't convert to unsigned + * when we divide or multiply by it. */ + int num_lig_components = _hb_glyph_info_get_lig_num_comps (&buffer->info[base]); + + hb_position_t x_offset = 0, y_offset = 0; + if (HB_DIRECTION_IS_FORWARD (buffer->props.direction)) { + x_offset -= buffer->pos[base].x_advance; + y_offset -= buffer->pos[base].y_advance; + } + + hb_glyph_extents_t component_extents = base_extents; + int last_lig_component = -1; + unsigned int last_combining_class = 255; + hb_glyph_extents_t cluster_extents = base_extents; /* Initialization is just to shut gcc up. */ + hb_glyph_info_t *info = buffer->info; + for (unsigned int i = base + 1; i < end; i++) + if (_hb_glyph_info_get_modified_combining_class (&info[i])) + { + if (num_lig_components > 1) { + unsigned int this_lig_id = _hb_glyph_info_get_lig_id (&info[i]); + int this_lig_component = _hb_glyph_info_get_lig_comp (&info[i]) - 1; + /* Conditions for attaching to the last component. */ + if (!lig_id || lig_id != this_lig_id || this_lig_component >= num_lig_components) + this_lig_component = num_lig_components - 1; + if (last_lig_component != this_lig_component) + { + last_lig_component = this_lig_component; + last_combining_class = 255; + component_extents = base_extents; + if (unlikely (horiz_dir == HB_DIRECTION_INVALID)) { + if (HB_DIRECTION_IS_HORIZONTAL (plan->props.direction)) + horiz_dir = plan->props.direction; + else + horiz_dir = hb_script_get_horizontal_direction (plan->props.script); + } + if (horiz_dir == HB_DIRECTION_LTR) + component_extents.x_bearing += (this_lig_component * component_extents.width) / num_lig_components; + else + component_extents.x_bearing += ((num_lig_components - 1 - this_lig_component) * component_extents.width) / num_lig_components; + component_extents.width /= num_lig_components; + } + } + + unsigned int this_combining_class = _hb_glyph_info_get_modified_combining_class (&info[i]); + if (last_combining_class != this_combining_class) + { + last_combining_class = this_combining_class; + cluster_extents = component_extents; + } + + position_mark (plan, font, buffer, cluster_extents, i, this_combining_class); + + buffer->pos[i].x_advance = 0; + buffer->pos[i].y_advance = 0; + buffer->pos[i].x_offset += x_offset; + buffer->pos[i].y_offset += y_offset; + + } else { + if (HB_DIRECTION_IS_FORWARD (buffer->props.direction)) { + x_offset -= buffer->pos[i].x_advance; + y_offset -= buffer->pos[i].y_advance; + } else { + x_offset += buffer->pos[i].x_advance; + y_offset += buffer->pos[i].y_advance; + } + } +} + +static inline void +position_cluster (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer, + unsigned int start, + unsigned int end, + bool adjust_offsets_when_zeroing) +{ + if (end - start < 2) + return; + + /* Find the base glyph */ + hb_glyph_info_t *info = buffer->info; + for (unsigned int i = start; i < end; i++) + if (!_hb_glyph_info_is_unicode_mark (&info[i])) + { + /* Find mark glyphs */ + unsigned int j; + for (j = i + 1; j < end; j++) + if (!_hb_glyph_info_is_unicode_mark (&info[j])) + break; + + position_around_base (plan, font, buffer, i, j, adjust_offsets_when_zeroing); + + i = j - 1; + } +} + +void +_hb_ot_shape_fallback_mark_position (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer, + bool adjust_offsets_when_zeroing) +{ +#ifdef HB_NO_OT_SHAPE_FALLBACK + return; +#endif + + if (!buffer->message (font, "start fallback mark")) + return; + + _hb_buffer_assert_gsubgpos_vars (buffer); + + unsigned int start = 0; + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; + for (unsigned int i = 1; i < count; i++) + if (likely (!_hb_glyph_info_is_unicode_mark (&info[i]))) { + position_cluster (plan, font, buffer, start, i, adjust_offsets_when_zeroing); + start = i; + } + position_cluster (plan, font, buffer, start, count, adjust_offsets_when_zeroing); + + (void) buffer->message (font, "end fallback mark"); +} + + +#ifndef HB_DISABLE_DEPRECATED +struct hb_ot_shape_fallback_kern_driver_t +{ + hb_ot_shape_fallback_kern_driver_t (hb_font_t *font_, + hb_buffer_t *buffer) : + font (font_), direction (buffer->props.direction) {} + + hb_position_t get_kerning (hb_codepoint_t first, hb_codepoint_t second) const + { + hb_position_t kern = 0; + font->get_glyph_kerning_for_direction (first, second, + direction, + &kern, &kern); + return kern; + } + + hb_font_t *font; + hb_direction_t direction; +}; +#endif + +/* Performs font-assisted kerning. */ +void +_hb_ot_shape_fallback_kern (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer) +{ +#ifdef HB_NO_OT_SHAPE_FALLBACK + return; +#endif + +#ifndef HB_DISABLE_DEPRECATED + if (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction) ? + !font->has_glyph_h_kerning_func () : + !font->has_glyph_v_kerning_func ()) + return; + + if (!buffer->message (font, "start fallback kern")) + return; + + bool reverse = HB_DIRECTION_IS_BACKWARD (buffer->props.direction); + + if (reverse) + buffer->reverse (); + + hb_ot_shape_fallback_kern_driver_t driver (font, buffer); + OT::hb_kern_machine_t machine (driver); + machine.kern (font, buffer, plan->kern_mask, false); + + if (reverse) + buffer->reverse (); + + (void) buffer->message (font, "end fallback kern"); +#endif +} + + +/* Adjusts width of various spaces. */ +void +_hb_ot_shape_fallback_spaces (const hb_ot_shape_plan_t *plan HB_UNUSED, + hb_font_t *font, + hb_buffer_t *buffer) +{ + hb_glyph_info_t *info = buffer->info; + hb_glyph_position_t *pos = buffer->pos; + bool horizontal = HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction); + unsigned int count = buffer->len; + for (unsigned int i = 0; i < count; i++) + if (_hb_glyph_info_is_unicode_space (&info[i]) && !_hb_glyph_info_ligated (&info[i])) + { + /* If font had no ASCII space and we used the invisible glyph, give it a 1/4 EM default advance. */ + if (buffer->invisible && info[i].codepoint == buffer->invisible) + { + if (horizontal) + pos[i].x_advance = +font->x_scale / 4; + else + pos[i].y_advance = -font->y_scale / 4; + } + + hb_unicode_funcs_t::space_t space_type = _hb_glyph_info_get_unicode_space_fallback_type (&info[i]); + hb_codepoint_t glyph; + typedef hb_unicode_funcs_t t; + switch (space_type) + { + case t::NOT_SPACE: /* Shouldn't happen. */ + case t::SPACE: + break; + + case t::SPACE_EM: + case t::SPACE_EM_2: + case t::SPACE_EM_3: + case t::SPACE_EM_4: + case t::SPACE_EM_5: + case t::SPACE_EM_6: + case t::SPACE_EM_16: + if (horizontal) + pos[i].x_advance = +(font->x_scale + ((int) space_type)/2) / (int) space_type; + else + pos[i].y_advance = -(font->y_scale + ((int) space_type)/2) / (int) space_type; + break; + + case t::SPACE_4_EM_18: + if (horizontal) + pos[i].x_advance = (int64_t) +font->x_scale * 4 / 18; + else + pos[i].y_advance = (int64_t) -font->y_scale * 4 / 18; + break; + + case t::SPACE_FIGURE: + for (char u = '0'; u <= '9'; u++) + if (font->get_nominal_glyph (u, &glyph)) + { + if (horizontal) + pos[i].x_advance = font->get_glyph_h_advance (glyph); + else + pos[i].y_advance = font->get_glyph_v_advance (glyph); + break; + } + break; + + case t::SPACE_PUNCTUATION: + if (font->get_nominal_glyph ('.', &glyph) || + font->get_nominal_glyph (',', &glyph)) + { + if (horizontal) + pos[i].x_advance = font->get_glyph_h_advance (glyph); + else + pos[i].y_advance = font->get_glyph_v_advance (glyph); + } + break; + + case t::SPACE_NARROW: + /* Half-space? + * Unicode doc https://unicode.org/charts/PDF/U2000.pdf says ~1/4 or 1/5 of EM. + * However, in my testing, many fonts have their regular space being about that + * size. To me, a percentage of the space width makes more sense. Half is as + * good as any. */ + if (horizontal) + pos[i].x_advance /= 2; + else + pos[i].y_advance /= 2; + break; + } + } +} + + +#endif diff --git a/libs/harfbuzz/src/hb-ot-shape-fallback.hh b/libs/harfbuzz/src/hb-ot-shape-fallback.hh new file mode 100644 index 00000000..5faf5f2d --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-shape-fallback.hh @@ -0,0 +1,54 @@ +/* + * Copyright © 2012 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 HB_OT_SHAPE_FALLBACK_HH +#define HB_OT_SHAPE_FALLBACK_HH + +#include "hb.hh" + +#include "hb-ot-shape.hh" + + +HB_INTERNAL void _hb_ot_shape_fallback_mark_position (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer, + bool adjust_offsets_when_zeroing); + +HB_INTERNAL void _hb_ot_shape_fallback_mark_position_recategorize_marks (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer); + + +HB_INTERNAL void _hb_ot_shape_fallback_kern (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer); + +HB_INTERNAL void _hb_ot_shape_fallback_spaces (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer); + + +#endif /* HB_OT_SHAPE_FALLBACK_HH */ diff --git a/libs/harfbuzz/src/hb-ot-shape-normalize.cc b/libs/harfbuzz/src/hb-ot-shape-normalize.cc new file mode 100644 index 00000000..7db0b25b --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-shape-normalize.cc @@ -0,0 +1,485 @@ +/* + * Copyright © 2011,2012 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 + */ + +#include "hb.hh" + +#ifndef HB_NO_OT_SHAPE + +#include "hb-ot-shape-normalize.hh" +#include "hb-ot-shaper.hh" +#include "hb-ot-shape.hh" + + +/* + * HIGHLEVEL DESIGN: + * + * This file exports one main function: _hb_ot_shape_normalize(). + * + * This function closely reflects the Unicode Normalization Algorithm, + * yet it's different. + * + * Each shaper specifies whether it prefers decomposed (NFD) or composed (NFC). + * The logic however tries to use whatever the font can support. + * + * In general what happens is that: each grapheme is decomposed in a chain + * of 1:2 decompositions, marks reordered, and then recomposed if desired, + * so far it's like Unicode Normalization. However, the decomposition and + * recomposition only happens if the font supports the resulting characters. + * + * The goals are: + * + * - Try to render all canonically equivalent strings similarly. To really + * achieve this we have to always do the full decomposition and then + * selectively recompose from there. It's kinda too expensive though, so + * we skip some cases. For example, if composed is desired, we simply + * don't touch 1-character clusters that are supported by the font, even + * though their NFC may be different. + * + * - When a font has a precomposed character for a sequence but the 'ccmp' + * feature in the font is not adequate, use the precomposed character + * which typically has better mark positioning. + * + * - When a font does not support a combining mark, but supports it precomposed + * with previous base, use that. This needs the itemizer to have this + * knowledge too. We need to provide assistance to the itemizer. + * + * - When a font does not support a character but supports its canonical + * decomposition, well, use the decomposition. + * + * - The shapers can customize the compose and decompose functions to + * offload some of their requirements to the normalizer. For example, the + * Indic shaper may want to disallow recomposing of two matras. + */ + +static bool +decompose_unicode (const hb_ot_shape_normalize_context_t *c, + hb_codepoint_t ab, + hb_codepoint_t *a, + hb_codepoint_t *b) +{ + return (bool) c->unicode->decompose (ab, a, b); +} + +static bool +compose_unicode (const hb_ot_shape_normalize_context_t *c, + hb_codepoint_t a, + hb_codepoint_t b, + hb_codepoint_t *ab) +{ + return (bool) c->unicode->compose (a, b, ab); +} + +static inline void +set_glyph (hb_glyph_info_t &info, hb_font_t *font) +{ + (void) font->get_nominal_glyph (info.codepoint, &info.glyph_index()); +} + +static inline void +output_char (hb_buffer_t *buffer, hb_codepoint_t unichar, hb_codepoint_t glyph) +{ + /* This is very confusing indeed. */ + buffer->cur().glyph_index() = glyph; + (void) buffer->output_glyph (unichar); + _hb_glyph_info_set_unicode_props (&buffer->prev(), buffer); +} + +static inline void +next_char (hb_buffer_t *buffer, hb_codepoint_t glyph) +{ + buffer->cur().glyph_index() = glyph; + (void) buffer->next_glyph (); +} + +static inline void +skip_char (hb_buffer_t *buffer) +{ + buffer->skip_glyph (); +} + +/* Returns 0 if didn't decompose, number of resulting characters otherwise. */ +static inline unsigned int +decompose (const hb_ot_shape_normalize_context_t *c, bool shortest, hb_codepoint_t ab) +{ + hb_codepoint_t a = 0, b = 0, a_glyph = 0, b_glyph = 0; + hb_buffer_t * const buffer = c->buffer; + hb_font_t * const font = c->font; + + if (!c->decompose (c, ab, &a, &b) || + (b && !font->get_nominal_glyph (b, &b_glyph))) + return 0; + + bool has_a = (bool) font->get_nominal_glyph (a, &a_glyph); + if (shortest && has_a) { + /* Output a and b */ + output_char (buffer, a, a_glyph); + if (likely (b)) { + output_char (buffer, b, b_glyph); + return 2; + } + return 1; + } + + if (unsigned ret = decompose (c, shortest, a)) { + if (b) { + output_char (buffer, b, b_glyph); + return ret + 1; + } + return ret; + } + + if (has_a) { + output_char (buffer, a, a_glyph); + if (likely (b)) { + output_char (buffer, b, b_glyph); + return 2; + } + return 1; + } + + return 0; +} + +static inline void +decompose_current_character (const hb_ot_shape_normalize_context_t *c, bool shortest) +{ + hb_buffer_t * const buffer = c->buffer; + hb_codepoint_t u = buffer->cur().codepoint; + hb_codepoint_t glyph = 0; + + if (shortest && c->font->get_nominal_glyph (u, &glyph, c->not_found)) + { + next_char (buffer, glyph); + return; + } + + if (decompose (c, shortest, u)) + { + skip_char (buffer); + return; + } + + if (!shortest && c->font->get_nominal_glyph (u, &glyph, c->not_found)) + { + next_char (buffer, glyph); + return; + } + + if (_hb_glyph_info_is_unicode_space (&buffer->cur())) + { + hb_codepoint_t space_glyph; + hb_unicode_funcs_t::space_t space_type = buffer->unicode->space_fallback_type (u); + if (space_type != hb_unicode_funcs_t::NOT_SPACE && + (c->font->get_nominal_glyph (0x0020, &space_glyph) || (space_glyph = buffer->invisible))) + { + _hb_glyph_info_set_unicode_space_fallback_type (&buffer->cur(), space_type); + next_char (buffer, space_glyph); + buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_SPACE_FALLBACK; + return; + } + } + + if (u == 0x2011u) + { + /* U+2011 is the only sensible character that is a no-break version of another character + * and not a space. The space ones are handled already. Handle this lone one. */ + hb_codepoint_t other_glyph; + if (c->font->get_nominal_glyph (0x2010u, &other_glyph)) + { + next_char (buffer, other_glyph); + return; + } + } + + next_char (buffer, glyph); /* glyph is initialized in earlier branches. */ +} + +static inline void +handle_variation_selector_cluster (const hb_ot_shape_normalize_context_t *c, + unsigned int end, + bool short_circuit HB_UNUSED) +{ + /* Currently if there's a variation-selector we give-up on normalization, it's just too hard. */ + hb_buffer_t * const buffer = c->buffer; + hb_font_t * const font = c->font; + for (; buffer->idx < end - 1 && buffer->successful;) { + if (unlikely (buffer->unicode->is_variation_selector (buffer->cur(+1).codepoint))) { + if (font->get_variation_glyph (buffer->cur().codepoint, buffer->cur(+1).codepoint, &buffer->cur().glyph_index())) + { + hb_codepoint_t unicode = buffer->cur().codepoint; + (void) buffer->replace_glyphs (2, 1, &unicode); + } + else + { + /* Just pass on the two characters separately, let GSUB do its magic. */ + set_glyph (buffer->cur(), font); + (void) buffer->next_glyph (); + set_glyph (buffer->cur(), font); + (void) buffer->next_glyph (); + } + /* Skip any further variation selectors. */ + while (buffer->idx < end && + buffer->successful && + unlikely (buffer->unicode->is_variation_selector (buffer->cur().codepoint))) + { + set_glyph (buffer->cur(), font); + (void) buffer->next_glyph (); + } + } + else + { + set_glyph (buffer->cur(), font); + (void) buffer->next_glyph (); + } + } + if (likely (buffer->idx < end)) + { + set_glyph (buffer->cur(), font); + (void) buffer->next_glyph (); + } +} + +static inline void +decompose_multi_char_cluster (const hb_ot_shape_normalize_context_t *c, unsigned int end, bool short_circuit) +{ + hb_buffer_t * const buffer = c->buffer; + for (unsigned int i = buffer->idx; i < end && buffer->successful; i++) + if (unlikely (buffer->unicode->is_variation_selector (buffer->info[i].codepoint))) { + handle_variation_selector_cluster (c, end, short_circuit); + return; + } + + while (buffer->idx < end && buffer->successful) + decompose_current_character (c, short_circuit); +} + + +static int +compare_combining_class (const hb_glyph_info_t *pa, const hb_glyph_info_t *pb) +{ + unsigned int a = _hb_glyph_info_get_modified_combining_class (pa); + unsigned int b = _hb_glyph_info_get_modified_combining_class (pb); + + return a < b ? -1 : a == b ? 0 : +1; +} + + +void +_hb_ot_shape_normalize (const hb_ot_shape_plan_t *plan, + hb_buffer_t *buffer, + hb_font_t *font) +{ + if (unlikely (!buffer->len)) return; + + _hb_buffer_assert_unicode_vars (buffer); + + hb_ot_shape_normalization_mode_t mode = plan->shaper->normalization_preference; + if (mode == HB_OT_SHAPE_NORMALIZATION_MODE_AUTO) + { + if (plan->has_gpos_mark) + // https://github.com/harfbuzz/harfbuzz/issues/653#issuecomment-423905920 + //mode = HB_OT_SHAPE_NORMALIZATION_MODE_DECOMPOSED; + mode = HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS; + else + mode = HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS; + } + + const hb_ot_shape_normalize_context_t c = { + plan, + buffer, + font, + buffer->unicode, + buffer->not_found, + plan->shaper->decompose ? plan->shaper->decompose : decompose_unicode, + plan->shaper->compose ? plan->shaper->compose : compose_unicode + }; + + bool always_short_circuit = mode == HB_OT_SHAPE_NORMALIZATION_MODE_NONE; + bool might_short_circuit = always_short_circuit || + (mode != HB_OT_SHAPE_NORMALIZATION_MODE_DECOMPOSED && + mode != HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT); + unsigned int count; + + /* We do a fairly straightforward yet custom normalization process in three + * separate rounds: decompose, reorder, recompose (if desired). Currently + * this makes two buffer swaps. We can make it faster by moving the last + * two rounds into the inner loop for the first round, but it's more readable + * this way. */ + + + /* First round, decompose */ + + bool all_simple = true; + { + buffer->clear_output (); + count = buffer->len; + buffer->idx = 0; + do + { + unsigned int end; + for (end = buffer->idx + 1; end < count; end++) + if (unlikely (_hb_glyph_info_is_unicode_mark (&buffer->info[end]))) + break; + + if (end < count) + end--; /* Leave one base for the marks to cluster with. */ + + /* From idx to end are simple clusters. */ + if (might_short_circuit) + { + unsigned int done = font->get_nominal_glyphs (end - buffer->idx, + &buffer->cur().codepoint, + sizeof (buffer->info[0]), + &buffer->cur().glyph_index(), + sizeof (buffer->info[0])); + if (unlikely (!buffer->next_glyphs (done))) break; + } + while (buffer->idx < end && buffer->successful) + decompose_current_character (&c, might_short_circuit); + + if (buffer->idx == count || !buffer->successful) + break; + + all_simple = false; + + /* Find all the marks now. */ + for (end = buffer->idx + 1; end < count; end++) + if (!_hb_glyph_info_is_unicode_mark(&buffer->info[end])) + break; + + /* idx to end is one non-simple cluster. */ + decompose_multi_char_cluster (&c, end, always_short_circuit); + } + while (buffer->idx < count && buffer->successful); + buffer->sync (); + } + + + /* Second round, reorder (inplace) */ + + if (!all_simple && buffer->message(font, "start reorder")) + { + count = buffer->len; + for (unsigned int i = 0; i < count; i++) + { + if (_hb_glyph_info_get_modified_combining_class (&buffer->info[i]) == 0) + continue; + + unsigned int end; + for (end = i + 1; end < count; end++) + if (_hb_glyph_info_get_modified_combining_class (&buffer->info[end]) == 0) + break; + + /* We are going to do a O(n^2). Only do this if the sequence is short. */ + if (end - i > HB_OT_SHAPE_MAX_COMBINING_MARKS) { + i = end; + continue; + } + + buffer->sort (i, end, compare_combining_class); + + if (plan->shaper->reorder_marks) + plan->shaper->reorder_marks (plan, buffer, i, end); + + i = end; + } + (void) buffer->message(font, "end reorder"); + } + if (buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_CGJ) + { + /* For all CGJ, check if it prevented any reordering at all. + * If it did NOT, then make it skippable. + * https://github.com/harfbuzz/harfbuzz/issues/554 + */ + for (unsigned int i = 1; i + 1 < buffer->len; i++) + if (buffer->info[i].codepoint == 0x034Fu/*CGJ*/ && + (info_cc(buffer->info[i+1]) == 0 || info_cc(buffer->info[i-1]) <= info_cc(buffer->info[i+1]))) + { + _hb_glyph_info_unhide (&buffer->info[i]); + } + } + + + /* Third round, recompose */ + + if (!all_simple && + buffer->successful && + (mode == HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS || + mode == HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT)) + { + /* As noted in the comment earlier, we don't try to combine + * ccc=0 chars with their previous Starter. */ + + buffer->clear_output (); + count = buffer->len; + unsigned int starter = 0; + (void) buffer->next_glyph (); + while (buffer->idx < count /* No need for: && buffer->successful */) + { + hb_codepoint_t composed, glyph; + if (/* We don't try to compose a non-mark character with it's preceding starter. + * This is both an optimization to avoid trying to compose every two neighboring + * glyphs in most scripts AND a desired feature for Hangul. Apparently Hangul + * fonts are not designed to mix-and-match pre-composed syllables and Jamo. */ + _hb_glyph_info_is_unicode_mark(&buffer->cur())) + { + if (/* If there's anything between the starter and this char, they should have CCC + * smaller than this character's. */ + (starter == buffer->out_len - 1 || + info_cc (buffer->prev()) < info_cc (buffer->cur())) && + /* And compose. */ + c.compose (&c, + buffer->out_info[starter].codepoint, + buffer->cur().codepoint, + &composed) && + /* And the font has glyph for the composite. */ + font->get_nominal_glyph (composed, &glyph)) + { + /* Composes. */ + if (unlikely (!buffer->next_glyph ())) break; /* Copy to out-buffer. */ + buffer->merge_out_clusters (starter, buffer->out_len); + buffer->out_len--; /* Remove the second composable. */ + /* Modify starter and carry on. */ + buffer->out_info[starter].codepoint = composed; + buffer->out_info[starter].glyph_index() = glyph; + _hb_glyph_info_set_unicode_props (&buffer->out_info[starter], buffer); + + continue; + } + } + + /* Blocked, or doesn't compose. */ + if (unlikely (!buffer->next_glyph ())) break; + + if (info_cc (buffer->prev()) == 0) + starter = buffer->out_len - 1; + } + buffer->sync (); + } +} + + +#endif diff --git a/libs/harfbuzz/src/hb-ot-shape-normalize.hh b/libs/harfbuzz/src/hb-ot-shape-normalize.hh new file mode 100644 index 00000000..12c78a23 --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-shape-normalize.hh @@ -0,0 +1,71 @@ +/* + * Copyright © 2012 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 HB_OT_SHAPE_NORMALIZE_HH +#define HB_OT_SHAPE_NORMALIZE_HH + +#include "hb.hh" + + +/* buffer var allocations, used during the normalization process */ +#define glyph_index() var1.u32 + +struct hb_ot_shape_plan_t; + +enum hb_ot_shape_normalization_mode_t { + HB_OT_SHAPE_NORMALIZATION_MODE_NONE, + HB_OT_SHAPE_NORMALIZATION_MODE_DECOMPOSED, + HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS, /* Never composes base-to-base */ + HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT, /* Always fully decomposes and then recompose back */ + + HB_OT_SHAPE_NORMALIZATION_MODE_AUTO, /* See hb-ot-shape-normalize.cc for logic. */ + HB_OT_SHAPE_NORMALIZATION_MODE_DEFAULT = HB_OT_SHAPE_NORMALIZATION_MODE_AUTO +}; + +HB_INTERNAL void _hb_ot_shape_normalize (const hb_ot_shape_plan_t *shaper, + hb_buffer_t *buffer, + hb_font_t *font); + + +struct hb_ot_shape_normalize_context_t +{ + const hb_ot_shape_plan_t *plan; + hb_buffer_t *buffer; + hb_font_t *font; + hb_unicode_funcs_t *unicode; + const hb_codepoint_t not_found; + bool (*decompose) (const hb_ot_shape_normalize_context_t *c, + hb_codepoint_t ab, + hb_codepoint_t *a, + hb_codepoint_t *b); + bool (*compose) (const hb_ot_shape_normalize_context_t *c, + hb_codepoint_t a, + hb_codepoint_t b, + hb_codepoint_t *ab); +}; + + +#endif /* HB_OT_SHAPE_NORMALIZE_HH */ diff --git a/libs/harfbuzz/src/hb-ot-shape.cc b/libs/harfbuzz/src/hb-ot-shape.cc new file mode 100644 index 00000000..249b5a86 --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-shape.cc @@ -0,0 +1,1317 @@ +/* + * Copyright © 2009,2010 Red Hat, Inc. + * Copyright © 2010,2011,2012 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. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#include "hb.hh" + +#ifndef HB_NO_OT_SHAPE + +#ifdef HB_NO_OT_LAYOUT +#error "Cannot compile 'ot' shaper with HB_NO_OT_LAYOUT." +#endif + +#include "hb-shaper-impl.hh" + +#include "hb-ot-shape.hh" +#include "hb-ot-shaper.hh" +#include "hb-ot-shape-fallback.hh" +#include "hb-ot-shape-normalize.hh" + +#include "hb-ot-face.hh" + +#include "hb-set.hh" + +#include "hb-aat-layout.hh" + +static inline bool +_hb_codepoint_is_regional_indicator (hb_codepoint_t u) +{ return hb_in_range (u, 0x1F1E6u, 0x1F1FFu); } + +#ifndef HB_NO_AAT_SHAPE +static inline bool +_hb_apply_morx (hb_face_t *face, const hb_segment_properties_t &props) +{ + /* https://github.com/harfbuzz/harfbuzz/issues/2124 */ + return hb_aat_layout_has_substitution (face) && + (HB_DIRECTION_IS_HORIZONTAL (props.direction) || !hb_ot_layout_has_substitution (face)); +} +#endif + +/** + * SECTION:hb-ot-shape + * @title: hb-ot-shape + * @short_description: OpenType shaping support + * @include: hb-ot.h + * + * Support functions for OpenType shaping related queries. + **/ + + +static void +hb_ot_shape_collect_features (hb_ot_shape_planner_t *planner, + const hb_feature_t *user_features, + unsigned int num_user_features); + +hb_ot_shape_planner_t::hb_ot_shape_planner_t (hb_face_t *face, + const hb_segment_properties_t &props) : + face (face), + props (props), + map (face, props), + aat_map (face, props) +#ifndef HB_NO_AAT_SHAPE + , apply_morx (_hb_apply_morx (face, props)) +#endif +{ + shaper = hb_ot_shaper_categorize (this); + + script_zero_marks = shaper->zero_width_marks != HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE; + script_fallback_mark_positioning = shaper->fallback_position; + + /* https://github.com/harfbuzz/harfbuzz/issues/1528 */ + if (apply_morx && shaper != &_hb_ot_shaper_default) + shaper = &_hb_ot_shaper_dumber; +} + +void +hb_ot_shape_planner_t::compile (hb_ot_shape_plan_t &plan, + const hb_ot_shape_plan_key_t &key) +{ + plan.props = props; + plan.shaper = shaper; + map.compile (plan.map, key); +#ifndef HB_NO_AAT_SHAPE + if (apply_morx) + aat_map.compile (plan.aat_map); +#endif + +#ifndef HB_NO_OT_SHAPE_FRACTIONS + plan.frac_mask = plan.map.get_1_mask (HB_TAG ('f','r','a','c')); + plan.numr_mask = plan.map.get_1_mask (HB_TAG ('n','u','m','r')); + plan.dnom_mask = plan.map.get_1_mask (HB_TAG ('d','n','o','m')); + plan.has_frac = plan.frac_mask || (plan.numr_mask && plan.dnom_mask); +#endif + + plan.rtlm_mask = plan.map.get_1_mask (HB_TAG ('r','t','l','m')); + plan.has_vert = !!plan.map.get_1_mask (HB_TAG ('v','e','r','t')); + + hb_tag_t kern_tag = HB_DIRECTION_IS_HORIZONTAL (props.direction) ? + HB_TAG ('k','e','r','n') : HB_TAG ('v','k','r','n'); +#ifndef HB_NO_OT_KERN + plan.kern_mask = plan.map.get_mask (kern_tag); + plan.requested_kerning = !!plan.kern_mask; +#endif +#ifndef HB_NO_AAT_SHAPE + plan.trak_mask = plan.map.get_mask (HB_TAG ('t','r','a','k')); + plan.requested_tracking = !!plan.trak_mask; +#endif + + bool has_gpos_kern = plan.map.get_feature_index (1, kern_tag) != HB_OT_LAYOUT_NO_FEATURE_INDEX; + bool disable_gpos = plan.shaper->gpos_tag && + plan.shaper->gpos_tag != plan.map.chosen_script[1]; + + /* + * Decide who provides glyph classes. GDEF or Unicode. + */ + + if (!hb_ot_layout_has_glyph_classes (face)) + plan.fallback_glyph_classes = true; + + /* + * Decide who does substitutions. GSUB, morx, or fallback. + */ + +#ifndef HB_NO_AAT_SHAPE + plan.apply_morx = apply_morx; +#endif + + /* + * Decide who does positioning. GPOS, kerx, kern, or fallback. + */ + +#ifndef HB_NO_AAT_SHAPE + bool has_kerx = hb_aat_layout_has_positioning (face); + bool has_gsub = !apply_morx && hb_ot_layout_has_substitution (face); +#endif + bool has_gpos = !disable_gpos && hb_ot_layout_has_positioning (face); + if (false) + ; +#ifndef HB_NO_AAT_SHAPE + /* Prefer GPOS over kerx if GSUB is present; + * https://github.com/harfbuzz/harfbuzz/issues/3008 */ + else if (has_kerx && !(has_gsub && has_gpos)) + plan.apply_kerx = true; +#endif + else if (has_gpos) + plan.apply_gpos = true; + + if (!plan.apply_kerx && (!has_gpos_kern || !plan.apply_gpos)) + { +#ifndef HB_NO_AAT_SHAPE + if (has_kerx) + plan.apply_kerx = true; + else +#endif +#ifndef HB_NO_OT_KERN + if (hb_ot_layout_has_kerning (face)) + plan.apply_kern = true; +#endif + } + + plan.apply_fallback_kern = !(plan.apply_gpos || plan.apply_kerx || plan.apply_kern); + + plan.zero_marks = script_zero_marks && + !plan.apply_kerx && + (!plan.apply_kern +#ifndef HB_NO_OT_KERN + || !hb_ot_layout_has_machine_kerning (face) +#endif + ); + plan.has_gpos_mark = !!plan.map.get_1_mask (HB_TAG ('m','a','r','k')); + + plan.adjust_mark_positioning_when_zeroing = !plan.apply_gpos && + !plan.apply_kerx && + (!plan.apply_kern +#ifndef HB_NO_OT_KERN + || !hb_ot_layout_has_cross_kerning (face) +#endif + ); + + plan.fallback_mark_positioning = plan.adjust_mark_positioning_when_zeroing && + script_fallback_mark_positioning; + +#ifndef HB_NO_AAT_SHAPE + /* If we're using morx shaping, we cancel mark position adjustment because + Apple Color Emoji assumes this will NOT be done when forming emoji sequences; + https://github.com/harfbuzz/harfbuzz/issues/2967. */ + if (plan.apply_morx) + plan.adjust_mark_positioning_when_zeroing = false; + + /* Currently we always apply trak. */ + plan.apply_trak = plan.requested_tracking && hb_aat_layout_has_tracking (face); +#endif +} + +bool +hb_ot_shape_plan_t::init0 (hb_face_t *face, + const hb_shape_plan_key_t *key) +{ + map.init (); +#ifndef HB_NO_AAT_SHAPE + aat_map.init (); +#endif + + hb_ot_shape_planner_t planner (face, + key->props); + + hb_ot_shape_collect_features (&planner, + key->user_features, + key->num_user_features); + + planner.compile (*this, key->ot); + + if (shaper->data_create) + { + data = shaper->data_create (this); + if (unlikely (!data)) + { + map.fini (); +#ifndef HB_NO_AAT_SHAPE + aat_map.fini (); +#endif + return false; + } + } + + return true; +} + +void +hb_ot_shape_plan_t::fini () +{ + if (shaper->data_destroy) + shaper->data_destroy (const_cast (data)); + + map.fini (); +#ifndef HB_NO_AAT_SHAPE + aat_map.fini (); +#endif +} + +void +hb_ot_shape_plan_t::substitute (hb_font_t *font, + hb_buffer_t *buffer) const +{ +#ifndef HB_NO_AAT_SHAPE + if (unlikely (apply_morx)) + hb_aat_layout_substitute (this, font, buffer); + else +#endif + map.substitute (this, font, buffer); +} + +void +hb_ot_shape_plan_t::position (hb_font_t *font, + hb_buffer_t *buffer) const +{ + if (this->apply_gpos) + map.position (this, font, buffer); +#ifndef HB_NO_AAT_SHAPE + else if (this->apply_kerx) + hb_aat_layout_position (this, font, buffer); +#endif + +#ifndef HB_NO_OT_KERN + if (this->apply_kern) + hb_ot_layout_kern (this, font, buffer); +#endif + else if (this->apply_fallback_kern) + _hb_ot_shape_fallback_kern (this, font, buffer); + +#ifndef HB_NO_AAT_SHAPE + if (this->apply_trak) + hb_aat_layout_track (this, font, buffer); +#endif +} + + +static const hb_ot_map_feature_t +common_features[] = +{ + {HB_TAG('a','b','v','m'), F_GLOBAL}, + {HB_TAG('b','l','w','m'), F_GLOBAL}, + {HB_TAG('c','c','m','p'), F_GLOBAL}, + {HB_TAG('l','o','c','l'), F_GLOBAL}, + {HB_TAG('m','a','r','k'), F_GLOBAL_MANUAL_JOINERS}, + {HB_TAG('m','k','m','k'), F_GLOBAL_MANUAL_JOINERS}, + {HB_TAG('r','l','i','g'), F_GLOBAL}, +}; + + +static const hb_ot_map_feature_t +horizontal_features[] = +{ + {HB_TAG('c','a','l','t'), F_GLOBAL}, + {HB_TAG('c','l','i','g'), F_GLOBAL}, + {HB_TAG('c','u','r','s'), F_GLOBAL}, + {HB_TAG('d','i','s','t'), F_GLOBAL}, + {HB_TAG('k','e','r','n'), F_GLOBAL_HAS_FALLBACK}, + {HB_TAG('l','i','g','a'), F_GLOBAL}, + {HB_TAG('r','c','l','t'), F_GLOBAL}, +}; + +static void +hb_ot_shape_collect_features (hb_ot_shape_planner_t *planner, + const hb_feature_t *user_features, + unsigned int num_user_features) +{ + hb_ot_map_builder_t *map = &planner->map; + + map->enable_feature (HB_TAG('r','v','r','n')); + map->add_gsub_pause (nullptr); + + switch (planner->props.direction) + { + case HB_DIRECTION_LTR: + map->enable_feature (HB_TAG ('l','t','r','a')); + map->enable_feature (HB_TAG ('l','t','r','m')); + break; + case HB_DIRECTION_RTL: + map->enable_feature (HB_TAG ('r','t','l','a')); + map->add_feature (HB_TAG ('r','t','l','m')); + break; + case HB_DIRECTION_TTB: + case HB_DIRECTION_BTT: + case HB_DIRECTION_INVALID: + default: + break; + } + +#ifndef HB_NO_OT_SHAPE_FRACTIONS + /* Automatic fractions. */ + map->add_feature (HB_TAG ('f','r','a','c')); + map->add_feature (HB_TAG ('n','u','m','r')); + map->add_feature (HB_TAG ('d','n','o','m')); +#endif + + /* Random! */ + map->enable_feature (HB_TAG ('r','a','n','d'), F_RANDOM, HB_OT_MAP_MAX_VALUE); + +#ifndef HB_NO_AAT_SHAPE + /* Tracking. We enable dummy feature here just to allow disabling + * AAT 'trak' table using features. + * https://github.com/harfbuzz/harfbuzz/issues/1303 */ + map->enable_feature (HB_TAG ('t','r','a','k'), F_HAS_FALLBACK); +#endif + + map->enable_feature (HB_TAG ('H','a','r','f')); /* Considered required. */ + map->enable_feature (HB_TAG ('H','A','R','F')); /* Considered discretionary. */ + + if (planner->shaper->collect_features) + planner->shaper->collect_features (planner); + + map->enable_feature (HB_TAG ('B','u','z','z')); /* Considered required. */ + map->enable_feature (HB_TAG ('B','U','Z','Z')); /* Considered discretionary. */ + + for (unsigned int i = 0; i < ARRAY_LENGTH (common_features); i++) + map->add_feature (common_features[i]); + + if (HB_DIRECTION_IS_HORIZONTAL (planner->props.direction)) + for (unsigned int i = 0; i < ARRAY_LENGTH (horizontal_features); i++) + map->add_feature (horizontal_features[i]); + else + { + /* We only apply `vert` feature. See: + * https://github.com/harfbuzz/harfbuzz/commit/d71c0df2d17f4590d5611239577a6cb532c26528 + * https://lists.freedesktop.org/archives/harfbuzz/2013-August/003490.html */ + + /* We really want to find a 'vert' feature if there's any in the font, no + * matter which script/langsys it is listed (or not) under. + * See various bugs referenced from: + * https://github.com/harfbuzz/harfbuzz/issues/63 */ + map->enable_feature (HB_TAG ('v','e','r','t'), F_GLOBAL_SEARCH); + } + + for (unsigned int i = 0; i < num_user_features; i++) + { + const hb_feature_t *feature = &user_features[i]; + map->add_feature (feature->tag, + (feature->start == HB_FEATURE_GLOBAL_START && + feature->end == HB_FEATURE_GLOBAL_END) ? F_GLOBAL : F_NONE, + feature->value); + } + +#ifndef HB_NO_AAT_SHAPE + if (planner->apply_morx) + { + hb_aat_map_builder_t *aat_map = &planner->aat_map; + for (unsigned int i = 0; i < num_user_features; i++) + { + const hb_feature_t *feature = &user_features[i]; + aat_map->add_feature (feature->tag, feature->value); + } + } +#endif + + if (planner->shaper->override_features) + planner->shaper->override_features (planner); +} + + +/* + * shaper face data + */ + +struct hb_ot_face_data_t {}; + +hb_ot_face_data_t * +_hb_ot_shaper_face_data_create (hb_face_t *face) +{ + return (hb_ot_face_data_t *) HB_SHAPER_DATA_SUCCEEDED; +} + +void +_hb_ot_shaper_face_data_destroy (hb_ot_face_data_t *data) +{ +} + + +/* + * shaper font data + */ + +struct hb_ot_font_data_t {}; + +hb_ot_font_data_t * +_hb_ot_shaper_font_data_create (hb_font_t *font HB_UNUSED) +{ + return (hb_ot_font_data_t *) HB_SHAPER_DATA_SUCCEEDED; +} + +void +_hb_ot_shaper_font_data_destroy (hb_ot_font_data_t *data HB_UNUSED) +{ +} + + +/* + * shaper + */ + +struct hb_ot_shape_context_t +{ + hb_ot_shape_plan_t *plan; + hb_font_t *font; + hb_face_t *face; + hb_buffer_t *buffer; + const hb_feature_t *user_features; + unsigned int num_user_features; + + /* Transient stuff */ + hb_direction_t target_direction; +}; + + + +/* Main shaper */ + + +/* Prepare */ + +static void +hb_set_unicode_props (hb_buffer_t *buffer) +{ + /* Implement enough of Unicode Graphemes here that shaping + * in reverse-direction wouldn't break graphemes. Namely, + * we mark all marks and ZWJ and ZWJ,Extended_Pictographic + * sequences as continuations. The foreach_grapheme() + * macro uses this bit. + * + * https://www.unicode.org/reports/tr29/#Regex_Definitions + */ + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; + for (unsigned int i = 0; i < count; i++) + { + _hb_glyph_info_set_unicode_props (&info[i], buffer); + + /* Marks are already set as continuation by the above line. + * Handle Emoji_Modifier and ZWJ-continuation. */ + if (unlikely (_hb_glyph_info_get_general_category (&info[i]) == HB_UNICODE_GENERAL_CATEGORY_MODIFIER_SYMBOL && + hb_in_range (info[i].codepoint, 0x1F3FBu, 0x1F3FFu))) + { + _hb_glyph_info_set_continuation (&info[i]); + } + /* Regional_Indicators are hairy as hell... + * https://github.com/harfbuzz/harfbuzz/issues/2265 */ + else if (unlikely (i && _hb_codepoint_is_regional_indicator (info[i].codepoint))) + { + if (_hb_codepoint_is_regional_indicator (info[i - 1].codepoint) && + !_hb_glyph_info_is_continuation (&info[i - 1])) + _hb_glyph_info_set_continuation (&info[i]); + } +#ifndef HB_NO_EMOJI_SEQUENCES + else if (unlikely (_hb_glyph_info_is_zwj (&info[i]))) + { + _hb_glyph_info_set_continuation (&info[i]); + if (i + 1 < count && + _hb_unicode_is_emoji_Extended_Pictographic (info[i + 1].codepoint)) + { + i++; + _hb_glyph_info_set_unicode_props (&info[i], buffer); + _hb_glyph_info_set_continuation (&info[i]); + } + } +#endif + /* Or part of the Other_Grapheme_Extend that is not marks. + * As of Unicode 15 that is just: + * + * 200C ; Other_Grapheme_Extend # Cf ZERO WIDTH NON-JOINER + * FF9E..FF9F ; Other_Grapheme_Extend # Lm [2] HALFWIDTH KATAKANA VOICED SOUND MARK..HALFWIDTH KATAKANA SEMI-VOICED SOUND MARK + * E0020..E007F ; Other_Grapheme_Extend # Cf [96] TAG SPACE..CANCEL TAG + * + * ZWNJ is special, we don't want to merge it as there's no need, and keeping + * it separate results in more granular clusters. + * Tags are used for Emoji sub-region flag sequences: + * https://github.com/harfbuzz/harfbuzz/issues/1556 + * Katakana ones were requested: + * https://github.com/harfbuzz/harfbuzz/issues/3844 + */ + else if (unlikely (hb_in_ranges (info[i].codepoint, 0xFF9Eu, 0xFF9Fu, 0xE0020u, 0xE007Fu))) + _hb_glyph_info_set_continuation (&info[i]); + } +} + +static void +hb_insert_dotted_circle (hb_buffer_t *buffer, hb_font_t *font) +{ + if (unlikely (buffer->flags & HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE)) + return; + + if (!(buffer->flags & HB_BUFFER_FLAG_BOT) || + buffer->context_len[0] || + !_hb_glyph_info_is_unicode_mark (&buffer->info[0])) + return; + + if (!font->has_glyph (0x25CCu)) + return; + + hb_glyph_info_t dottedcircle = {0}; + dottedcircle.codepoint = 0x25CCu; + _hb_glyph_info_set_unicode_props (&dottedcircle, buffer); + + buffer->clear_output (); + + buffer->idx = 0; + hb_glyph_info_t info = dottedcircle; + info.cluster = buffer->cur().cluster; + info.mask = buffer->cur().mask; + (void) buffer->output_info (info); + + buffer->sync (); +} + +static void +hb_form_clusters (hb_buffer_t *buffer) +{ + if (!(buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_NON_ASCII)) + return; + + if (buffer->cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES) + foreach_grapheme (buffer, start, end) + buffer->merge_clusters (start, end); + else + foreach_grapheme (buffer, start, end) + buffer->unsafe_to_break (start, end); +} + +static void +hb_ensure_native_direction (hb_buffer_t *buffer) +{ + hb_direction_t direction = buffer->props.direction; + hb_direction_t horiz_dir = hb_script_get_horizontal_direction (buffer->props.script); + + /* Numeric runs in natively-RTL scripts are actually native-LTR, so we reset + * the horiz_dir if the run contains at least one decimal-number char, and no + * letter chars (ideally we should be checking for chars with strong + * directionality but hb-unicode currently lacks bidi categories). + * + * This allows digit sequences in Arabic etc to be shaped in "native" + * direction, so that features like ligatures will work as intended. + * + * https://github.com/harfbuzz/harfbuzz/issues/501 + * + * Similar thing about Regional_Indicators; They are bidi=L, but Script=Common. + * If they are present in a run of natively-RTL text, they get assigned a script + * with natively RTL direction, which would result in wrong shaping if we + * assign such native RTL direction to them then. Detect that as well. + * + * https://github.com/harfbuzz/harfbuzz/issues/3314 + */ + if (unlikely (horiz_dir == HB_DIRECTION_RTL && direction == HB_DIRECTION_LTR)) + { + bool found_number = false, found_letter = false, found_ri = false; + const auto* info = buffer->info; + const auto count = buffer->len; + for (unsigned i = 0; i < count; i++) + { + auto gc = _hb_glyph_info_get_general_category (&info[i]); + if (gc == HB_UNICODE_GENERAL_CATEGORY_DECIMAL_NUMBER) + found_number = true; + else if (HB_UNICODE_GENERAL_CATEGORY_IS_LETTER (gc)) + { + found_letter = true; + break; + } + else if (_hb_codepoint_is_regional_indicator (info[i].codepoint)) + found_ri = true; + } + if ((found_number || found_ri) && !found_letter) + horiz_dir = HB_DIRECTION_LTR; + } + + /* TODO vertical: + * The only BTT vertical script is Ogham, but it's not clear to me whether OpenType + * Ogham fonts are supposed to be implemented BTT or not. Need to research that + * first. */ + if ((HB_DIRECTION_IS_HORIZONTAL (direction) && + direction != horiz_dir && horiz_dir != HB_DIRECTION_INVALID) || + (HB_DIRECTION_IS_VERTICAL (direction) && + direction != HB_DIRECTION_TTB)) + { + _hb_ot_layout_reverse_graphemes (buffer); + buffer->props.direction = HB_DIRECTION_REVERSE (buffer->props.direction); + } +} + + +/* + * Substitute + */ + +#ifndef HB_NO_VERTICAL +static hb_codepoint_t +hb_vert_char_for (hb_codepoint_t u) +{ + switch (u >> 8) + { + case 0x20: switch (u) { + case 0x2013u: return 0xfe32u; // EN DASH + case 0x2014u: return 0xfe31u; // EM DASH + case 0x2025u: return 0xfe30u; // TWO DOT LEADER + case 0x2026u: return 0xfe19u; // HORIZONTAL ELLIPSIS + } break; + case 0x30: switch (u) { + case 0x3001u: return 0xfe11u; // IDEOGRAPHIC COMMA + case 0x3002u: return 0xfe12u; // IDEOGRAPHIC FULL STOP + case 0x3008u: return 0xfe3fu; // LEFT ANGLE BRACKET + case 0x3009u: return 0xfe40u; // RIGHT ANGLE BRACKET + case 0x300au: return 0xfe3du; // LEFT DOUBLE ANGLE BRACKET + case 0x300bu: return 0xfe3eu; // RIGHT DOUBLE ANGLE BRACKET + case 0x300cu: return 0xfe41u; // LEFT CORNER BRACKET + case 0x300du: return 0xfe42u; // RIGHT CORNER BRACKET + case 0x300eu: return 0xfe43u; // LEFT WHITE CORNER BRACKET + case 0x300fu: return 0xfe44u; // RIGHT WHITE CORNER BRACKET + case 0x3010u: return 0xfe3bu; // LEFT BLACK LENTICULAR BRACKET + case 0x3011u: return 0xfe3cu; // RIGHT BLACK LENTICULAR BRACKET + case 0x3014u: return 0xfe39u; // LEFT TORTOISE SHELL BRACKET + case 0x3015u: return 0xfe3au; // RIGHT TORTOISE SHELL BRACKET + case 0x3016u: return 0xfe17u; // LEFT WHITE LENTICULAR BRACKET + case 0x3017u: return 0xfe18u; // RIGHT WHITE LENTICULAR BRACKET + } break; + case 0xfe: switch (u) { + case 0xfe4fu: return 0xfe34u; // WAVY LOW LINE + } break; + case 0xff: switch (u) { + case 0xff01u: return 0xfe15u; // FULLWIDTH EXCLAMATION MARK + case 0xff08u: return 0xfe35u; // FULLWIDTH LEFT PARENTHESIS + case 0xff09u: return 0xfe36u; // FULLWIDTH RIGHT PARENTHESIS + case 0xff0cu: return 0xfe10u; // FULLWIDTH COMMA + case 0xff1au: return 0xfe13u; // FULLWIDTH COLON + case 0xff1bu: return 0xfe14u; // FULLWIDTH SEMICOLON + case 0xff1fu: return 0xfe16u; // FULLWIDTH QUESTION MARK + case 0xff3bu: return 0xfe47u; // FULLWIDTH LEFT SQUARE BRACKET + case 0xff3du: return 0xfe48u; // FULLWIDTH RIGHT SQUARE BRACKET + case 0xff3fu: return 0xfe33u; // FULLWIDTH LOW LINE + case 0xff5bu: return 0xfe37u; // FULLWIDTH LEFT CURLY BRACKET + case 0xff5du: return 0xfe38u; // FULLWIDTH RIGHT CURLY BRACKET + } break; + } + + return u; +} +#endif + +static inline void +hb_ot_rotate_chars (const hb_ot_shape_context_t *c) +{ + hb_buffer_t *buffer = c->buffer; + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; + + if (HB_DIRECTION_IS_BACKWARD (c->target_direction)) + { + hb_unicode_funcs_t *unicode = buffer->unicode; + hb_mask_t rtlm_mask = c->plan->rtlm_mask; + + for (unsigned int i = 0; i < count; i++) { + hb_codepoint_t codepoint = unicode->mirroring (info[i].codepoint); + if (unlikely (codepoint != info[i].codepoint && c->font->has_glyph (codepoint))) + info[i].codepoint = codepoint; + else + info[i].mask |= rtlm_mask; + } + } + +#ifndef HB_NO_VERTICAL + if (HB_DIRECTION_IS_VERTICAL (c->target_direction) && !c->plan->has_vert) + { + for (unsigned int i = 0; i < count; i++) { + hb_codepoint_t codepoint = hb_vert_char_for (info[i].codepoint); + if (unlikely (codepoint != info[i].codepoint && c->font->has_glyph (codepoint))) + info[i].codepoint = codepoint; + } + } +#endif +} + +static inline void +hb_ot_shape_setup_masks_fraction (const hb_ot_shape_context_t *c) +{ +#ifdef HB_NO_OT_SHAPE_FRACTIONS + return; +#endif + + if (!(c->buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_NON_ASCII) || + !c->plan->has_frac) + return; + + hb_buffer_t *buffer = c->buffer; + + hb_mask_t pre_mask, post_mask; + if (HB_DIRECTION_IS_FORWARD (buffer->props.direction)) + { + pre_mask = c->plan->numr_mask | c->plan->frac_mask; + post_mask = c->plan->frac_mask | c->plan->dnom_mask; + } + else + { + pre_mask = c->plan->frac_mask | c->plan->dnom_mask; + post_mask = c->plan->numr_mask | c->plan->frac_mask; + } + + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; + for (unsigned int i = 0; i < count; i++) + { + if (info[i].codepoint == 0x2044u) /* FRACTION SLASH */ + { + unsigned int start = i, end = i + 1; + while (start && + _hb_glyph_info_get_general_category (&info[start - 1]) == + HB_UNICODE_GENERAL_CATEGORY_DECIMAL_NUMBER) + start--; + while (end < count && + _hb_glyph_info_get_general_category (&info[end]) == + HB_UNICODE_GENERAL_CATEGORY_DECIMAL_NUMBER) + end++; + + buffer->unsafe_to_break (start, end); + + for (unsigned int j = start; j < i; j++) + info[j].mask |= pre_mask; + info[i].mask |= c->plan->frac_mask; + for (unsigned int j = i + 1; j < end; j++) + info[j].mask |= post_mask; + + i = end - 1; + } + } +} + +static inline void +hb_ot_shape_initialize_masks (const hb_ot_shape_context_t *c) +{ + hb_ot_map_t *map = &c->plan->map; + hb_buffer_t *buffer = c->buffer; + + hb_mask_t global_mask = map->get_global_mask (); + buffer->reset_masks (global_mask); +} + +static inline void +hb_ot_shape_setup_masks (const hb_ot_shape_context_t *c) +{ + hb_ot_map_t *map = &c->plan->map; + hb_buffer_t *buffer = c->buffer; + + hb_ot_shape_setup_masks_fraction (c); + + if (c->plan->shaper->setup_masks) + c->plan->shaper->setup_masks (c->plan, buffer, c->font); + + for (unsigned int i = 0; i < c->num_user_features; i++) + { + const hb_feature_t *feature = &c->user_features[i]; + if (!(feature->start == HB_FEATURE_GLOBAL_START && feature->end == HB_FEATURE_GLOBAL_END)) { + unsigned int shift; + hb_mask_t mask = map->get_mask (feature->tag, &shift); + buffer->set_masks (feature->value << shift, mask, feature->start, feature->end); + } + } +} + +static void +hb_ot_zero_width_default_ignorables (const hb_buffer_t *buffer) +{ + if (!(buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_DEFAULT_IGNORABLES) || + (buffer->flags & HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES) || + (buffer->flags & HB_BUFFER_FLAG_REMOVE_DEFAULT_IGNORABLES)) + return; + + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; + hb_glyph_position_t *pos = buffer->pos; + unsigned int i = 0; + for (i = 0; i < count; i++) + if (unlikely (_hb_glyph_info_is_default_ignorable (&info[i]))) + pos[i].x_advance = pos[i].y_advance = pos[i].x_offset = pos[i].y_offset = 0; +} + +static void +hb_ot_hide_default_ignorables (hb_buffer_t *buffer, + hb_font_t *font) +{ + if (!(buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_DEFAULT_IGNORABLES) || + (buffer->flags & HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES)) + return; + + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; + + hb_codepoint_t invisible = buffer->invisible; + if (!(buffer->flags & HB_BUFFER_FLAG_REMOVE_DEFAULT_IGNORABLES) && + (invisible || font->get_nominal_glyph (' ', &invisible))) + { + /* Replace default-ignorables with a zero-advance invisible glyph. */ + for (unsigned int i = 0; i < count; i++) + { + if (_hb_glyph_info_is_default_ignorable (&info[i])) + info[i].codepoint = invisible; + } + } + else + hb_ot_layout_delete_glyphs_inplace (buffer, _hb_glyph_info_is_default_ignorable); +} + + +static inline void +hb_ot_map_glyphs_fast (hb_buffer_t *buffer) +{ + /* Normalization process sets up glyph_index(), we just copy it. */ + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; + for (unsigned int i = 0; i < count; i++) + info[i].codepoint = info[i].glyph_index(); + + buffer->content_type = HB_BUFFER_CONTENT_TYPE_GLYPHS; +} + +static inline void +hb_synthesize_glyph_classes (hb_buffer_t *buffer) +{ + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; + for (unsigned int i = 0; i < count; i++) + { + hb_ot_layout_glyph_props_flags_t klass; + + /* Never mark default-ignorables as marks. + * They won't get in the way of lookups anyway, + * but having them as mark will cause them to be skipped + * over if the lookup-flag says so, but at least for the + * Mongolian variation selectors, looks like Uniscribe + * marks them as non-mark. Some Mongolian fonts without + * GDEF rely on this. Another notable character that + * this applies to is COMBINING GRAPHEME JOINER. */ + klass = (_hb_glyph_info_get_general_category (&info[i]) != + HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK || + _hb_glyph_info_is_default_ignorable (&info[i])) ? + HB_OT_LAYOUT_GLYPH_PROPS_BASE_GLYPH : + HB_OT_LAYOUT_GLYPH_PROPS_MARK; + _hb_glyph_info_set_glyph_props (&info[i], klass); + } +} + +static inline void +hb_ot_substitute_default (const hb_ot_shape_context_t *c) +{ + hb_buffer_t *buffer = c->buffer; + + hb_ot_rotate_chars (c); + + HB_BUFFER_ALLOCATE_VAR (buffer, glyph_index); + + _hb_ot_shape_normalize (c->plan, buffer, c->font); + + hb_ot_shape_setup_masks (c); + + /* This is unfortunate to go here, but necessary... */ + if (c->plan->fallback_mark_positioning) + _hb_ot_shape_fallback_mark_position_recategorize_marks (c->plan, c->font, buffer); + + hb_ot_map_glyphs_fast (buffer); + + HB_BUFFER_DEALLOCATE_VAR (buffer, glyph_index); +} + +static inline void +hb_ot_substitute_plan (const hb_ot_shape_context_t *c) +{ + hb_buffer_t *buffer = c->buffer; + + hb_ot_layout_substitute_start (c->font, buffer); + + if (c->plan->fallback_glyph_classes) + hb_synthesize_glyph_classes (c->buffer); + + c->plan->substitute (c->font, buffer); +} + +static inline void +hb_ot_substitute_pre (const hb_ot_shape_context_t *c) +{ + hb_ot_substitute_default (c); + + _hb_buffer_allocate_gsubgpos_vars (c->buffer); + + hb_ot_substitute_plan (c); + +#ifndef HB_NO_AAT_SHAPE + if (c->plan->apply_morx && c->plan->apply_gpos) + hb_aat_layout_remove_deleted_glyphs (c->buffer); +#endif +} + +static inline void +hb_ot_substitute_post (const hb_ot_shape_context_t *c) +{ +#ifndef HB_NO_AAT_SHAPE + if (c->plan->apply_morx && !c->plan->apply_gpos) + hb_aat_layout_remove_deleted_glyphs (c->buffer); +#endif + + hb_ot_hide_default_ignorables (c->buffer, c->font); + + if (c->plan->shaper->postprocess_glyphs && + c->buffer->message(c->font, "start postprocess-glyphs")) { + c->plan->shaper->postprocess_glyphs (c->plan, c->buffer, c->font); + (void) c->buffer->message(c->font, "end postprocess-glyphs"); + } +} + + +/* + * Position + */ + +static inline void +adjust_mark_offsets (hb_glyph_position_t *pos) +{ + pos->x_offset -= pos->x_advance; + pos->y_offset -= pos->y_advance; +} + +static inline void +zero_mark_width (hb_glyph_position_t *pos) +{ + pos->x_advance = 0; + pos->y_advance = 0; +} + +static inline void +zero_mark_widths_by_gdef (hb_buffer_t *buffer, bool adjust_offsets) +{ + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; + for (unsigned int i = 0; i < count; i++) + if (_hb_glyph_info_is_mark (&info[i])) + { + if (adjust_offsets) + adjust_mark_offsets (&buffer->pos[i]); + zero_mark_width (&buffer->pos[i]); + } +} + +static inline void +hb_ot_position_default (const hb_ot_shape_context_t *c) +{ + hb_direction_t direction = c->buffer->props.direction; + unsigned int count = c->buffer->len; + hb_glyph_info_t *info = c->buffer->info; + hb_glyph_position_t *pos = c->buffer->pos; + + if (HB_DIRECTION_IS_HORIZONTAL (direction)) + { + c->font->get_glyph_h_advances (count, &info[0].codepoint, sizeof(info[0]), + &pos[0].x_advance, sizeof(pos[0])); + /* The nil glyph_h_origin() func returns 0, so no need to apply it. */ + if (c->font->has_glyph_h_origin_func ()) + for (unsigned int i = 0; i < count; i++) + c->font->subtract_glyph_h_origin (info[i].codepoint, + &pos[i].x_offset, + &pos[i].y_offset); + } + else + { + c->font->get_glyph_v_advances (count, &info[0].codepoint, sizeof(info[0]), + &pos[0].y_advance, sizeof(pos[0])); + for (unsigned int i = 0; i < count; i++) + { + c->font->subtract_glyph_v_origin (info[i].codepoint, + &pos[i].x_offset, + &pos[i].y_offset); + } + } + if (c->buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_SPACE_FALLBACK) + _hb_ot_shape_fallback_spaces (c->plan, c->font, c->buffer); +} + +static inline void +hb_ot_position_plan (const hb_ot_shape_context_t *c) +{ + unsigned int count = c->buffer->len; + hb_glyph_info_t *info = c->buffer->info; + hb_glyph_position_t *pos = c->buffer->pos; + + /* If the font has no GPOS and direction is forward, then when + * zeroing mark widths, we shift the mark with it, such that the + * mark is positioned hanging over the previous glyph. When + * direction is backward we don't shift and it will end up + * hanging over the next glyph after the final reordering. + * + * Note: If fallback positinoing happens, we don't care about + * this as it will be overridden. + */ + bool adjust_offsets_when_zeroing = c->plan->adjust_mark_positioning_when_zeroing && + HB_DIRECTION_IS_FORWARD (c->buffer->props.direction); + + /* We change glyph origin to what GPOS expects (horizontal), apply GPOS, change it back. */ + + /* The nil glyph_h_origin() func returns 0, so no need to apply it. */ + if (c->font->has_glyph_h_origin_func ()) + for (unsigned int i = 0; i < count; i++) + c->font->add_glyph_h_origin (info[i].codepoint, + &pos[i].x_offset, + &pos[i].y_offset); + + hb_ot_layout_position_start (c->font, c->buffer); + + if (c->plan->zero_marks) + switch (c->plan->shaper->zero_width_marks) + { + case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_EARLY: + zero_mark_widths_by_gdef (c->buffer, adjust_offsets_when_zeroing); + break; + + default: + case HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE: + case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE: + break; + } + + c->plan->position (c->font, c->buffer); + + if (c->plan->zero_marks) + switch (c->plan->shaper->zero_width_marks) + { + case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE: + zero_mark_widths_by_gdef (c->buffer, adjust_offsets_when_zeroing); + break; + + default: + case HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE: + case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_EARLY: + break; + } + + /* Finish off. Has to follow a certain order. */ + hb_ot_layout_position_finish_advances (c->font, c->buffer); + hb_ot_zero_width_default_ignorables (c->buffer); +#ifndef HB_NO_AAT_SHAPE + if (c->plan->apply_morx) + hb_aat_layout_zero_width_deleted_glyphs (c->buffer); +#endif + hb_ot_layout_position_finish_offsets (c->font, c->buffer); + + /* The nil glyph_h_origin() func returns 0, so no need to apply it. */ + if (c->font->has_glyph_h_origin_func ()) + for (unsigned int i = 0; i < count; i++) + c->font->subtract_glyph_h_origin (info[i].codepoint, + &pos[i].x_offset, + &pos[i].y_offset); + + if (c->plan->fallback_mark_positioning) + _hb_ot_shape_fallback_mark_position (c->plan, c->font, c->buffer, + adjust_offsets_when_zeroing); +} + +static inline void +hb_ot_position (const hb_ot_shape_context_t *c) +{ + c->buffer->clear_positions (); + + hb_ot_position_default (c); + + hb_ot_position_plan (c); + + if (HB_DIRECTION_IS_BACKWARD (c->buffer->props.direction)) + hb_buffer_reverse (c->buffer); + + _hb_buffer_deallocate_gsubgpos_vars (c->buffer); +} + +static inline void +hb_propagate_flags (hb_buffer_t *buffer) +{ + /* Propagate cluster-level glyph flags to be the same on all cluster glyphs. + * Simplifies using them. */ + + if (!(buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_GLYPH_FLAGS)) + return; + + /* If we are producing SAFE_TO_INSERT_TATWEEL, then do two things: + * + * - If the places that the Arabic shaper marked as SAFE_TO_INSERT_TATWEEL, + * are UNSAFE_TO_BREAK, then clear the SAFE_TO_INSERT_TATWEEL, + * - Any place that is SAFE_TO_INSERT_TATWEEL, is also now UNSAFE_TO_BREAK. + * + * We couldn't make this interaction earlier. It has to be done here. + */ + bool flip_tatweel = buffer->flags & HB_BUFFER_FLAG_PRODUCE_SAFE_TO_INSERT_TATWEEL; + + bool clear_concat = (buffer->flags & HB_BUFFER_FLAG_PRODUCE_UNSAFE_TO_CONCAT) == 0; + + hb_glyph_info_t *info = buffer->info; + + foreach_cluster (buffer, start, end) + { + unsigned int mask = 0; + for (unsigned int i = start; i < end; i++) + mask |= info[i].mask & HB_GLYPH_FLAG_DEFINED; + + if (flip_tatweel) + { + if (mask & HB_GLYPH_FLAG_UNSAFE_TO_BREAK) + mask &= ~HB_GLYPH_FLAG_SAFE_TO_INSERT_TATWEEL; + if (mask & HB_GLYPH_FLAG_SAFE_TO_INSERT_TATWEEL) + mask |= HB_GLYPH_FLAG_UNSAFE_TO_BREAK | HB_GLYPH_FLAG_UNSAFE_TO_CONCAT; + } + + if (clear_concat) + mask &= ~HB_GLYPH_FLAG_UNSAFE_TO_CONCAT; + + for (unsigned int i = start; i < end; i++) + info[i].mask = mask; + } +} + +/* Pull it all together! */ + +static void +hb_ot_shape_internal (hb_ot_shape_context_t *c) +{ + /* Save the original direction, we use it later. */ + c->target_direction = c->buffer->props.direction; + + _hb_buffer_allocate_unicode_vars (c->buffer); + + hb_ot_shape_initialize_masks (c); + hb_set_unicode_props (c->buffer); + hb_insert_dotted_circle (c->buffer, c->font); + + hb_form_clusters (c->buffer); + + hb_ensure_native_direction (c->buffer); + + if (c->plan->shaper->preprocess_text && + c->buffer->message(c->font, "start preprocess-text")) + { + c->plan->shaper->preprocess_text (c->plan, c->buffer, c->font); + (void) c->buffer->message(c->font, "end preprocess-text"); + } + + hb_ot_substitute_pre (c); + hb_ot_position (c); + hb_ot_substitute_post (c); + + hb_propagate_flags (c->buffer); + + _hb_buffer_deallocate_unicode_vars (c->buffer); + + c->buffer->props.direction = c->target_direction; + + c->buffer->leave (); +} + + +hb_bool_t +_hb_ot_shape (hb_shape_plan_t *shape_plan, + hb_font_t *font, + hb_buffer_t *buffer, + const hb_feature_t *features, + unsigned int num_features) +{ + hb_ot_shape_context_t c = {&shape_plan->ot, font, font->face, buffer, features, num_features}; + hb_ot_shape_internal (&c); + + return true; +} + + +/** + * hb_ot_shape_plan_collect_lookups: + * @shape_plan: #hb_shape_plan_t to query + * @table_tag: GSUB or GPOS + * @lookup_indexes: (out): The #hb_set_t set of lookups returned + * + * Computes the complete set of GSUB or GPOS lookups that are applicable + * under a given @shape_plan. + * + * Since: 0.9.7 + **/ +void +hb_ot_shape_plan_collect_lookups (hb_shape_plan_t *shape_plan, + hb_tag_t table_tag, + hb_set_t *lookup_indexes /* OUT */) +{ + shape_plan->ot.collect_lookups (table_tag, lookup_indexes); +} + + +/* TODO Move this to hb-ot-shape-normalize, make it do decompose, and make it public. */ +static void +add_char (hb_font_t *font, + hb_unicode_funcs_t *unicode, + hb_bool_t mirror, + hb_codepoint_t u, + hb_set_t *glyphs) +{ + hb_codepoint_t glyph; + if (font->get_nominal_glyph (u, &glyph)) + glyphs->add (glyph); + if (mirror) + { + hb_codepoint_t m = unicode->mirroring (u); + if (m != u && font->get_nominal_glyph (m, &glyph)) + glyphs->add (glyph); + } +} + + +/** + * hb_ot_shape_glyphs_closure: + * @font: #hb_font_t to work upon + * @buffer: The input buffer to compute from + * @features: (array length=num_features): The features enabled on the buffer + * @num_features: The number of features enabled on the buffer + * @glyphs: (out): The #hb_set_t set of glyphs comprising the transitive closure of the query + * + * Computes the transitive closure of glyphs needed for a specified + * input buffer under the given font and feature list. The closure is + * computed as a set, not as a list. + * + * Since: 0.9.2 + **/ +void +hb_ot_shape_glyphs_closure (hb_font_t *font, + hb_buffer_t *buffer, + const hb_feature_t *features, + unsigned int num_features, + hb_set_t *glyphs) +{ + const char *shapers[] = {"ot", nullptr}; + hb_shape_plan_t *shape_plan = hb_shape_plan_create_cached (font->face, &buffer->props, + features, num_features, shapers); + + bool mirror = hb_script_get_horizontal_direction (buffer->props.script) == HB_DIRECTION_RTL; + + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; + for (unsigned int i = 0; i < count; i++) + add_char (font, buffer->unicode, mirror, info[i].codepoint, glyphs); + + hb_set_t *lookups = hb_set_create (); + hb_ot_shape_plan_collect_lookups (shape_plan, HB_OT_TAG_GSUB, lookups); + hb_ot_layout_lookups_substitute_closure (font->face, lookups, glyphs); + + hb_set_destroy (lookups); + + hb_shape_plan_destroy (shape_plan); +} + + +#endif diff --git a/libs/harfbuzz/src/hb-ot-shape.h b/libs/harfbuzz/src/hb-ot-shape.h new file mode 100644 index 00000000..afdff728 --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-shape.h @@ -0,0 +1,53 @@ +/* + * Copyright © 2013 Red Hat, 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. + * + * Red Hat Author(s): Behdad Esfahbod + */ + +#if !defined(HB_OT_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR) +#error "Include instead." +#endif + +#ifndef HB_OT_SHAPE_H +#define HB_OT_SHAPE_H + +#include "hb.h" + +HB_BEGIN_DECLS + +/* TODO port to shape-plan / set. */ +HB_EXTERN void +hb_ot_shape_glyphs_closure (hb_font_t *font, + hb_buffer_t *buffer, + const hb_feature_t *features, + unsigned int num_features, + hb_set_t *glyphs); + +HB_EXTERN void +hb_ot_shape_plan_collect_lookups (hb_shape_plan_t *shape_plan, + hb_tag_t table_tag, + hb_set_t *lookup_indexes /* OUT */); + +HB_END_DECLS + +#endif /* HB_OT_SHAPE_H */ diff --git a/libs/harfbuzz/src/hb-ot-shape.hh b/libs/harfbuzz/src/hb-ot-shape.hh new file mode 100644 index 00000000..cd6f15cb --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-shape.hh @@ -0,0 +1,173 @@ +/* + * Copyright © 2010 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 HB_OT_SHAPE_HH +#define HB_OT_SHAPE_HH + +#include "hb.hh" + +#include "hb-ot-map.hh" +#include "hb-aat-map.hh" + + +struct hb_ot_shape_plan_key_t +{ + unsigned int variations_index[2]; + + void init (hb_face_t *face, + const int *coords, + unsigned num_coords) + { + for (unsigned int table_index = 0; table_index < 2; table_index++) + hb_ot_layout_table_find_feature_variations (face, + table_tags[table_index], + coords, + num_coords, + &variations_index[table_index]); + } + + bool equal (const hb_ot_shape_plan_key_t *other) + { + return 0 == memcmp (this, other, sizeof (*this)); + } +}; + + +struct hb_shape_plan_key_t; + +struct hb_ot_shape_plan_t +{ + ~hb_ot_shape_plan_t () { fini (); } + + hb_segment_properties_t props; + const struct hb_ot_shaper_t *shaper; + hb_ot_map_t map; + hb_aat_map_t aat_map; + const void *data; +#ifndef HB_NO_OT_SHAPE_FRACTIONS + hb_mask_t frac_mask, numr_mask, dnom_mask; +#else + static constexpr hb_mask_t frac_mask = 0; + static constexpr hb_mask_t numr_mask = 0; + static constexpr hb_mask_t dnom_mask = 0; +#endif + hb_mask_t rtlm_mask; +#ifndef HB_NO_OT_KERN + hb_mask_t kern_mask; +#else + static constexpr hb_mask_t kern_mask = 0; +#endif +#ifndef HB_NO_AAT_SHAPE + hb_mask_t trak_mask; +#else + static constexpr hb_mask_t trak_mask = 0; +#endif + +#ifndef HB_NO_OT_KERN + bool requested_kerning : 1; +#else + static constexpr bool requested_kerning = false; +#endif +#ifndef HB_NO_AAT_SHAPE + bool requested_tracking : 1; +#else + static constexpr bool requested_tracking = false; +#endif +#ifndef HB_NO_OT_SHAPE_FRACTIONS + bool has_frac : 1; +#else + static constexpr bool has_frac = false; +#endif + bool has_vert : 1; + bool has_gpos_mark : 1; + bool zero_marks : 1; + bool fallback_glyph_classes : 1; + bool fallback_mark_positioning : 1; + bool adjust_mark_positioning_when_zeroing : 1; + + bool apply_gpos : 1; +#ifndef HB_NO_OT_KERN + bool apply_kern : 1; +#else + static constexpr bool apply_kern = false; +#endif + bool apply_fallback_kern : 1; +#ifndef HB_NO_AAT_SHAPE + bool apply_kerx : 1; + bool apply_morx : 1; + bool apply_trak : 1; +#else + static constexpr bool apply_kerx = false; + static constexpr bool apply_morx = false; + static constexpr bool apply_trak = false; +#endif + + void collect_lookups (hb_tag_t table_tag, hb_set_t *lookups) const + { + unsigned int table_index; + switch (table_tag) { + case HB_OT_TAG_GSUB: table_index = 0; break; + case HB_OT_TAG_GPOS: table_index = 1; break; + default: return; + } + map.collect_lookups (table_index, lookups); + } + + HB_INTERNAL bool init0 (hb_face_t *face, + const hb_shape_plan_key_t *key); + HB_INTERNAL void fini (); + + HB_INTERNAL void substitute (hb_font_t *font, hb_buffer_t *buffer) const; + HB_INTERNAL void position (hb_font_t *font, hb_buffer_t *buffer) const; +}; + +struct hb_shape_plan_t; + +struct hb_ot_shape_planner_t +{ + /* In the order that they are filled in. */ + hb_face_t *face; + hb_segment_properties_t props; + hb_ot_map_builder_t map; + hb_aat_map_builder_t aat_map; +#ifndef HB_NO_AAT_SHAPE + bool apply_morx : 1; +#else + static constexpr bool apply_morx = false; +#endif + bool script_zero_marks : 1; + bool script_fallback_mark_positioning : 1; + const struct hb_ot_shaper_t *shaper; + + HB_INTERNAL hb_ot_shape_planner_t (hb_face_t *face, + const hb_segment_properties_t &props); + + HB_INTERNAL void compile (hb_ot_shape_plan_t &plan, + const hb_ot_shape_plan_key_t &key); +}; + + +#endif /* HB_OT_SHAPE_HH */ diff --git a/libs/harfbuzz/src/hb-ot-shaper-arabic-fallback.hh b/libs/harfbuzz/src/hb-ot-shaper-arabic-fallback.hh new file mode 100644 index 00000000..b9f92f72 --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-shaper-arabic-fallback.hh @@ -0,0 +1,376 @@ +/* + * Copyright © 2012 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 HB_OT_SHAPER_ARABIC_FALLBACK_HH +#define HB_OT_SHAPER_ARABIC_FALLBACK_HH + +#include "hb.hh" + +#include "hb-ot-shape.hh" +#include "hb-ot-layout-gsub-table.hh" + + +/* Features ordered the same as the entries in shaping_table rows, + * followed by rlig. Don't change. + * + * We currently support one subtable per lookup, and one lookup + * per feature. But we allow duplicate features, so we use that! + */ +static const hb_tag_t arabic_fallback_features[] = +{ + HB_TAG('i','n','i','t'), + HB_TAG('m','e','d','i'), + HB_TAG('f','i','n','a'), + HB_TAG('i','s','o','l'), + HB_TAG('r','l','i','g'), + HB_TAG('r','l','i','g'), + HB_TAG('r','l','i','g'), +}; + +static OT::SubstLookup * +arabic_fallback_synthesize_lookup_single (const hb_ot_shape_plan_t *plan HB_UNUSED, + hb_font_t *font, + unsigned int feature_index) +{ + OT::HBGlyphID16 glyphs[SHAPING_TABLE_LAST - SHAPING_TABLE_FIRST + 1]; + OT::HBGlyphID16 substitutes[SHAPING_TABLE_LAST - SHAPING_TABLE_FIRST + 1]; + unsigned int num_glyphs = 0; + + /* Populate arrays */ + for (hb_codepoint_t u = SHAPING_TABLE_FIRST; u < SHAPING_TABLE_LAST + 1; u++) + { + hb_codepoint_t s = shaping_table[u - SHAPING_TABLE_FIRST][feature_index]; + hb_codepoint_t u_glyph, s_glyph; + + if (!s || + !hb_font_get_glyph (font, u, 0, &u_glyph) || + !hb_font_get_glyph (font, s, 0, &s_glyph) || + u_glyph == s_glyph || + u_glyph > 0xFFFFu || s_glyph > 0xFFFFu) + continue; + + glyphs[num_glyphs] = u_glyph; + substitutes[num_glyphs] = s_glyph; + + num_glyphs++; + } + + if (!num_glyphs) + return nullptr; + + /* Bubble-sort or something equally good! + * May not be good-enough for presidential candidate interviews, but good-enough for us... */ + hb_stable_sort (&glyphs[0], num_glyphs, + (int(*)(const OT::HBUINT16*, const OT::HBUINT16 *)) OT::HBGlyphID16::cmp, + &substitutes[0]); + + + /* Each glyph takes four bytes max, and there's some overhead. */ + char buf[(SHAPING_TABLE_LAST - SHAPING_TABLE_FIRST + 1) * 4 + 128]; + hb_serialize_context_t c (buf, sizeof (buf)); + OT::SubstLookup *lookup = c.start_serialize (); + bool ret = lookup->serialize_single (&c, + OT::LookupFlag::IgnoreMarks, + hb_sorted_array (glyphs, num_glyphs), + hb_array (substitutes, num_glyphs)); + c.end_serialize (); + + return ret && !c.in_error () ? c.copy () : nullptr; +} + +template +static OT::SubstLookup * +arabic_fallback_synthesize_lookup_ligature (const hb_ot_shape_plan_t *plan HB_UNUSED, + hb_font_t *font, + const T &ligature_table, + unsigned lookup_flags) +{ + OT::HBGlyphID16 first_glyphs[ARRAY_LENGTH_CONST (ligature_table)]; + unsigned int first_glyphs_indirection[ARRAY_LENGTH_CONST (ligature_table)]; + unsigned int ligature_per_first_glyph_count_list[ARRAY_LENGTH_CONST (first_glyphs)]; + unsigned int num_first_glyphs = 0; + + /* We know that all our ligatures have the same number of components. */ + OT::HBGlyphID16 ligature_list[ARRAY_LENGTH_CONST (first_glyphs) * ARRAY_LENGTH_CONST(ligature_table[0].ligatures)]; + unsigned int component_count_list[ARRAY_LENGTH_CONST (ligature_list)]; + OT::HBGlyphID16 component_list[ARRAY_LENGTH_CONST (ligature_list) * + ARRAY_LENGTH_CONST (ligature_table[0].ligatures[0].components)]; + unsigned int num_ligatures = 0; + unsigned int num_components = 0; + + /* Populate arrays */ + + /* Sort out the first-glyphs */ + for (unsigned int first_glyph_idx = 0; first_glyph_idx < ARRAY_LENGTH (first_glyphs); first_glyph_idx++) + { + hb_codepoint_t first_u = ligature_table[first_glyph_idx].first; + hb_codepoint_t first_glyph; + if (!hb_font_get_glyph (font, first_u, 0, &first_glyph)) + continue; + first_glyphs[num_first_glyphs] = first_glyph; + ligature_per_first_glyph_count_list[num_first_glyphs] = 0; + first_glyphs_indirection[num_first_glyphs] = first_glyph_idx; + num_first_glyphs++; + } + hb_stable_sort (&first_glyphs[0], num_first_glyphs, + (int(*)(const OT::HBUINT16*, const OT::HBUINT16 *)) OT::HBGlyphID16::cmp, + &first_glyphs_indirection[0]); + + /* Now that the first-glyphs are sorted, walk again, populate ligatures. */ + for (unsigned int i = 0; i < num_first_glyphs; i++) + { + unsigned int first_glyph_idx = first_glyphs_indirection[i]; + + for (unsigned int ligature_idx = 0; ligature_idx < ARRAY_LENGTH (ligature_table[0].ligatures); ligature_idx++) + { + hb_codepoint_t ligature_u = ligature_table[first_glyph_idx].ligatures[ligature_idx].ligature; + hb_codepoint_t ligature_glyph; + if (!hb_font_get_glyph (font, ligature_u, 0, &ligature_glyph)) + continue; + + const auto &components = ligature_table[first_glyph_idx].ligatures[ligature_idx].components; + unsigned component_count = ARRAY_LENGTH_CONST (components); + + for (unsigned i = 0; i < component_count; i++) + { + hb_codepoint_t component_u = ligature_table[first_glyph_idx].ligatures[ligature_idx].components[i]; + hb_codepoint_t component_glyph; + if (!component_u || + !hb_font_get_glyph (font, component_u, 0, &component_glyph)) + continue; + + component_list[num_components++] = component_glyph; + } + + component_count_list[num_ligatures] = 1 + component_count; + ligature_list[num_ligatures] = ligature_glyph; + + ligature_per_first_glyph_count_list[i]++; + + num_ligatures++; + } + } + + if (!num_ligatures) + return nullptr; + + + /* 16 bytes per ligature ought to be enough... */ + char buf[ARRAY_LENGTH_CONST (ligature_list) * 16 + 128]; + hb_serialize_context_t c (buf, sizeof (buf)); + OT::SubstLookup *lookup = c.start_serialize (); + bool ret = lookup->serialize_ligature (&c, + lookup_flags, + hb_sorted_array (first_glyphs, num_first_glyphs), + hb_array (ligature_per_first_glyph_count_list, num_first_glyphs), + hb_array (ligature_list, num_ligatures), + hb_array (component_count_list, num_ligatures), + hb_array (component_list, num_components)); + c.end_serialize (); + + return ret && !c.in_error () ? c.copy () : nullptr; +} + +static OT::SubstLookup * +arabic_fallback_synthesize_lookup (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + unsigned int feature_index) +{ + if (feature_index < 4) + return arabic_fallback_synthesize_lookup_single (plan, font, feature_index); + else + { + switch (feature_index) { + case 4: return arabic_fallback_synthesize_lookup_ligature (plan, font, ligature_3_table, OT::LookupFlag::IgnoreMarks); + case 5: return arabic_fallback_synthesize_lookup_ligature (plan, font, ligature_table, OT::LookupFlag::IgnoreMarks); + case 6: return arabic_fallback_synthesize_lookup_ligature (plan, font, ligature_mark_table, 0); + } + } + assert (false); + return nullptr; +} + +#define ARABIC_FALLBACK_MAX_LOOKUPS ARRAY_LENGTH_CONST (arabic_fallback_features) + +struct arabic_fallback_plan_t +{ + unsigned int num_lookups; + bool free_lookups; + + hb_mask_t mask_array[ARABIC_FALLBACK_MAX_LOOKUPS]; + OT::SubstLookup *lookup_array[ARABIC_FALLBACK_MAX_LOOKUPS]; + OT::hb_ot_layout_lookup_accelerator_t accel_array[ARABIC_FALLBACK_MAX_LOOKUPS]; +}; + +#if defined(_WIN32) && !defined(HB_NO_WIN1256) +#define HB_WITH_WIN1256 +#endif + +#ifdef HB_WITH_WIN1256 +#include "hb-ot-shaper-arabic-win1256.hh" +#endif + +struct ManifestLookup +{ + public: + OT::Tag tag; + OT::Offset16To lookupOffset; + public: + DEFINE_SIZE_STATIC (6); +}; +typedef OT::Array16Of Manifest; + +static bool +arabic_fallback_plan_init_win1256 (arabic_fallback_plan_t *fallback_plan HB_UNUSED, + const hb_ot_shape_plan_t *plan HB_UNUSED, + hb_font_t *font HB_UNUSED) +{ +#ifdef HB_WITH_WIN1256 + /* Does this font look like it's Windows-1256-encoded? */ + hb_codepoint_t g; + if (!(hb_font_get_glyph (font, 0x0627u, 0, &g) && g == 199 /* ALEF */ && + hb_font_get_glyph (font, 0x0644u, 0, &g) && g == 225 /* LAM */ && + hb_font_get_glyph (font, 0x0649u, 0, &g) && g == 236 /* ALEF MAKSURA */ && + hb_font_get_glyph (font, 0x064Au, 0, &g) && g == 237 /* YEH */ && + hb_font_get_glyph (font, 0x0652u, 0, &g) && g == 250 /* SUKUN */)) + return false; + + const Manifest &manifest = reinterpret_cast (arabic_win1256_gsub_lookups.manifest); + static_assert (sizeof (arabic_win1256_gsub_lookups.manifestData) <= + ARABIC_FALLBACK_MAX_LOOKUPS * sizeof (ManifestLookup), ""); + + unsigned j = 0; + unsigned int count = manifest.len; + for (unsigned int i = 0; i < count; i++) + { + fallback_plan->mask_array[j] = plan->map.get_1_mask (manifest[i].tag); + if (fallback_plan->mask_array[j]) + { + fallback_plan->lookup_array[j] = const_cast (&(&manifest+manifest[i].lookupOffset)); + if (fallback_plan->lookup_array[j]) + { + fallback_plan->accel_array[j].init (*fallback_plan->lookup_array[j]); + j++; + } + } + } + + fallback_plan->num_lookups = j; + fallback_plan->free_lookups = false; + + return j > 0; +#else + return false; +#endif +} + +static bool +arabic_fallback_plan_init_unicode (arabic_fallback_plan_t *fallback_plan, + const hb_ot_shape_plan_t *plan, + hb_font_t *font) +{ + static_assert ((ARRAY_LENGTH_CONST (arabic_fallback_features) <= ARABIC_FALLBACK_MAX_LOOKUPS), ""); + unsigned int j = 0; + for (unsigned int i = 0; i < ARRAY_LENGTH(arabic_fallback_features) ; i++) + { + fallback_plan->mask_array[j] = plan->map.get_1_mask (arabic_fallback_features[i]); + if (fallback_plan->mask_array[j]) + { + fallback_plan->lookup_array[j] = arabic_fallback_synthesize_lookup (plan, font, i); + if (fallback_plan->lookup_array[j]) + { + fallback_plan->accel_array[j].init (*fallback_plan->lookup_array[j]); + j++; + } + } + } + + fallback_plan->num_lookups = j; + fallback_plan->free_lookups = true; + + return j > 0; +} + +static arabic_fallback_plan_t * +arabic_fallback_plan_create (const hb_ot_shape_plan_t *plan, + hb_font_t *font) +{ + arabic_fallback_plan_t *fallback_plan = (arabic_fallback_plan_t *) hb_calloc (1, sizeof (arabic_fallback_plan_t)); + if (unlikely (!fallback_plan)) + return const_cast (&Null (arabic_fallback_plan_t)); + + fallback_plan->num_lookups = 0; + fallback_plan->free_lookups = false; + + /* Try synthesizing GSUB table using Unicode Arabic Presentation Forms, + * in case the font has cmap entries for the presentation-forms characters. */ + if (arabic_fallback_plan_init_unicode (fallback_plan, plan, font)) + return fallback_plan; + + /* See if this looks like a Windows-1256-encoded font. If it does, use a + * hand-coded GSUB table. */ + if (arabic_fallback_plan_init_win1256 (fallback_plan, plan, font)) + return fallback_plan; + + assert (fallback_plan->num_lookups == 0); + hb_free (fallback_plan); + return const_cast (&Null (arabic_fallback_plan_t)); +} + +static void +arabic_fallback_plan_destroy (arabic_fallback_plan_t *fallback_plan) +{ + if (!fallback_plan || fallback_plan->num_lookups == 0) + return; + + for (unsigned int i = 0; i < fallback_plan->num_lookups; i++) + if (fallback_plan->lookup_array[i]) + { + fallback_plan->accel_array[i].fini (); + if (fallback_plan->free_lookups) + hb_free (fallback_plan->lookup_array[i]); + } + + hb_free (fallback_plan); +} + +static void +arabic_fallback_plan_shape (arabic_fallback_plan_t *fallback_plan, + hb_font_t *font, + hb_buffer_t *buffer) +{ + OT::hb_ot_apply_context_t c (0, font, buffer); + for (unsigned int i = 0; i < fallback_plan->num_lookups; i++) + if (fallback_plan->lookup_array[i]) { + c.set_lookup_mask (fallback_plan->mask_array[i]); + hb_ot_layout_substitute_lookup (&c, + *fallback_plan->lookup_array[i], + fallback_plan->accel_array[i]); + } +} + + +#endif /* HB_OT_SHAPER_ARABIC_FALLBACK_HH */ diff --git a/libs/harfbuzz/src/hb-ot-shaper-arabic-joining-list.hh b/libs/harfbuzz/src/hb-ot-shaper-arabic-joining-list.hh new file mode 100644 index 00000000..c7b57820 --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-shaper-arabic-joining-list.hh @@ -0,0 +1,47 @@ +/* == Start of generated function == */ +/* + * The following function is generated by running: + * + * ./gen-arabic-joining-list.py ArabicShaping.txt Scripts.txt + * + * on files with these headers: + * + * # ArabicShaping-15.0.0.txt + * # Date: 2022-02-14, 18:50:00 GMT [KW, RP] + * # Scripts-15.0.0.txt + * # Date: 2022-04-26, 23:15:02 GMT + */ + +#ifndef HB_OT_SHAPER_ARABIC_JOINING_LIST_HH +#define HB_OT_SHAPER_ARABIC_JOINING_LIST_HH + +static bool +has_arabic_joining (hb_script_t script) +{ + /* List of scripts that have data in arabic-table. */ + switch ((int) script) + { + case HB_SCRIPT_ADLAM: + case HB_SCRIPT_ARABIC: + case HB_SCRIPT_CHORASMIAN: + case HB_SCRIPT_HANIFI_ROHINGYA: + case HB_SCRIPT_MANDAIC: + case HB_SCRIPT_MANICHAEAN: + case HB_SCRIPT_MONGOLIAN: + case HB_SCRIPT_NKO: + case HB_SCRIPT_OLD_UYGHUR: + case HB_SCRIPT_PHAGS_PA: + case HB_SCRIPT_PSALTER_PAHLAVI: + case HB_SCRIPT_SOGDIAN: + case HB_SCRIPT_SYRIAC: + return true; + + default: + return false; + } +} + + +#endif /* HB_OT_SHAPER_ARABIC_JOINING_LIST_HH */ + +/* == End of generated function == */ diff --git a/libs/harfbuzz/src/hb-ot-shaper-arabic-pua.hh b/libs/harfbuzz/src/hb-ot-shaper-arabic-pua.hh new file mode 100644 index 00000000..ba86772f --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-shaper-arabic-pua.hh @@ -0,0 +1,118 @@ +/* == Start of generated table == */ +/* + * The following table is generated by running: + * + * ./gen-arabic-pua.py + * + */ + +#ifndef HB_OT_SHAPER_ARABIC_PUA_HH +#define HB_OT_SHAPER_ARABIC_PUA_HH + +static const uint8_t +_hb_arabic_u8[464] = +{ + 84, 86, 85, 85, 85, 85, 85,213, 16, 34, 34, 34, 34, 34, 35, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 36, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 82, 16, 0, 0, 0, 0, 1, 2, 3, 4, + 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 7, + 0, 0, 8, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 0, 0, 0, 22, 0, 23, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 16, 34, 34, 34, 35, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 66, 16, 50, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68,101, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 71, 68, 68, 68, 68, 68, 68, 68,152,186, 76, 77, 68,254, 16, 50, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 0, 0, 5, 6, + 0, 0, 0, 0, 0, 0, 7, 8, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 10, 0, + 0, 0, 0, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 12, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 13, 0, 0, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 23, 23, 29, 30, 31, 32, 33, 0, 0, 0, 0, + 0, 0, 0, 34, 0, 0, 0, 35, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 36, 37, 38, 0, 0, 0, 0, 0, 0, 0, 39, 0, 0, 40, + 41, 42, 0, 43, 44, 0, 0, 45, 46, 0, 47, 48, 49, 0, 0, 0, + 0, 50, 0, 0, 51, 52, 0, 53, 54, 55, 56, 57, 58, 0, 0, 0, + 0, 0, 59, 60, 61, 62, 63, 64, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 65, 0, 0, 66, + 0, 0, 67, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, + 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, +}; +static const uint16_t +_hb_arabic_u16[720] = +{ + 0, 0, 0, 0, 0, 0, 0, 0,61728,61729,61730, 0, 0,61733, 0, 0, + 61736,61737,61738,61739,61790,61741,61742,61743,61872,61873,61874,61875,61876,61877,61878,61879, + 61880,61881,61754,61755, 0,61757, 0,61759, 0, 0, 0,61787,61788,61789, 0, 0, + 0, 0, 0,61731, 0, 0, 0, 0, 0, 0, 0,61732, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0,61734, 0, 0, 0, 0, 0, 0, 0,61735, + 0, 0, 0, 0,61740, 0, 0, 0, 0, 0, 0,61755, 0, 0, 0,61759, + 0,61869,61765,61763,61883,61767,61882,61761,61770,61865,61772,61774,61777,61780,61783,61784, + 61785,61786,61792,61794,61796,61798,61800,61801,61802,61806,61810,61696,61696,61696,61696,61696, + 61791,61813,61816,61818,61820,61822,61921,61860,61861,61868,61864,61895,61896,61899,61892,61893, + 61898,61897,61894,61696,61696,61696,61696,61696,61696,61696,61696,61696,61696,61696,61696, 0, + 61744,61745,61746,61747,61748,61749,61750,61751,61752,61753, 0,61790,61790, 0, 0, 0, + 0, 0, 0, 0,61708,61709,61710,61711,61756,61758, 0, 0, 0, 0, 0, 0, + 0,61765,61766,61763,61764,61883,61883,61767,61768,61882,61871,61870,61870,61761,61762,61770, + 61770,61769,61769,61865,61866,61772,61772,61771,61771,61774,61774,61773,61773,61777,61776,61775, + 61775,61780,61779,61778,61778,61783,61782,61781,61781,61784,61784,61785,61785,61786,61786,61792, + 61792,61794,61794,61793,61793,61796,61796,61795,61795,61798,61798,61797,61797,61800,61800,61799, + 61799,61801,61801,61801,61801,61802,61802,61802,61802,61806,61805,61803,61804,61810,61809,61807, + 61808,61813,61813,61811,61812,61816,61816,61814,61815,61818,61818,61817,61817,61820,61820,61819, + 61819,61822,61822,61821,61821,61921,61921,61823,61823,61860,61859,61857,61858,61861,61861,61868, + 61867,61864,61863,61862,61862,61888,61889,61886,61887,61890,61891,61885,61884, 0, 0, 0, + 0, 0, 0, 0,61984,61985,61986, 0, 0,61989, 0, 0,61992,61993,61994,61995, + 62046,61997,61998,61999, 0, 0,62010,62011, 0,62013, 0,62015, 0, 0, 0,62043, + 0,62045, 0, 0, 0, 0, 0,61987, 0, 0, 0,61988, 0, 0, 0,61990, + 0, 0, 0,61991,61996, 0, 0, 0, 0, 0, 0,62011, 0, 0, 0,62015, + 0,62165,62021,62019,62170,62023,62169,62017,62028,62161,62032,62036,62040,62048,62052,62053, + 62055,62057,62059,62064,62068,62072,62078,62114,62115,62122,62126,61952,61952,61952,61952,61952, + 62047,62130,62134,62138,62142,62146,62150,62154,62155,62164,62160,62183,62184,62187,62180,62181, + 62186,62185,62182,61952,61952,61952,61952, 0,62000,62001,62002,62003,62004,62005,62006,62007, + 62008,62009, 0,62046,62046, 0, 0, 0,61964,61965,61966,61967,62012,62014, 0, 0, + 61954, 0,61981, 0, 0, 0,61955, 0,61982, 0,61956, 0, 0, 0,62111, 0, + 0, 0, 0,61970,61971,61972,61957, 0,61980, 0, 0, 0, 0, 0,61958, 0, + 61983, 0, 0, 0, 0, 0,62191, 0,62188,62189,62192, 0, 0, 0,61973, 0, + 0,62098, 0, 0,61974, 0, 0,62099, 0, 0,62101, 0, 0,61975, 0, 0, + 62100, 0, 0, 0,62080,62081,62082,62102, 0,62083,62084,62085,62103, 0, 0, 0, + 62106, 0,62107, 0,62108, 0, 0, 0,61976, 0, 0, 0, 0,62086,62087,62088, + 62109,61978,62089,62090,62091,62110,62093,62094, 0,62104, 0, 0, 0, 0,62095,62096, + 62097,62105, 0, 0,61977, 0, 0, 0, 0, 0,62075,62077,61968, 0, 0, 0, + 0,62021,62022,62019,62020,62170,62171,62023,62024,62169,62168,62166,62167,62017,62018,62028, + 62027,62025,62026,62161,62162,62032,62031,62029,62030,62036,62035,62033,62034,62040,62039,62037, + 62038,62048,62044,62041,62042,62052,62051,62049,62050,62053,62054,62055,62056,62057,62058,62059, + 62060,62064,62063,62061,62062,62068,62067,62065,62066,62072,62071,62069,62070,62078,62076,62073, + 62074,62114,62113,62079,62193,62118,62117,62115,62116,62122,62121,62119,62120,62126,62125,62123, + 62124,62130,62129,62127,62128,62134,62133,62131,62132,62138,62137,62135,62136,62142,62141,62139, + 62140,62146,62145,62143,62144,62150,62149,62147,62148,62154,62153,62151,62152,62155,62156,62164, + 62163,62160,62159,62157,62158,62176,62177,62174,62175,62178,62179,62172,62173, 0, 0, 0, +}; + +static inline unsigned +_hb_arabic_b2 (const uint8_t* a, unsigned i) +{ + return (a[i>>2]>>((i&3u)<<1))&3u; +} +static inline unsigned +_hb_arabic_b4 (const uint8_t* a, unsigned i) +{ + return (a[i>>1]>>((i&1u)<<2))&15u; +} +static inline uint_fast16_t +_hb_arabic_pua_simp_map (unsigned u) +{ + return u<65277u?_hb_arabic_u16[((_hb_arabic_u8[40+(((_hb_arabic_b4(8+_hb_arabic_u8,((_hb_arabic_b2(_hb_arabic_u8,u>>3>>4>>4))<<4)+((u>>3>>4)&15u)))<<4)+((u>>3)&15u))])<<3)+((u)&7u)]:0; +} +static inline uint_fast16_t +_hb_arabic_pua_trad_map (unsigned u) +{ + return u<65277u?_hb_arabic_u16[320+(((_hb_arabic_u8[208+(((_hb_arabic_b4(168+_hb_arabic_u8,((_hb_arabic_b4(136+_hb_arabic_u8,u>>2>>4>>4))<<4)+((u>>2>>4)&15u)))<<4)+((u>>2)&15u))])<<2)+((u)&3u))]:0; +} + +#endif /* HB_OT_SHAPER_ARABIC_PUA_HH */ + +/* == End of generated table == */ diff --git a/libs/harfbuzz/src/hb-ot-shaper-arabic-table.hh b/libs/harfbuzz/src/hb-ot-shaper-arabic-table.hh new file mode 100644 index 00000000..d7670f2f --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-shaper-arabic-table.hh @@ -0,0 +1,556 @@ +/* == Start of generated table == */ +/* + * The following table is generated by running: + * + * ./gen-arabic-table.py ArabicShaping.txt UnicodeData.txt Blocks.txt + * + * on files with these headers: + * + * # ArabicShaping-15.0.0.txt + * # Date: 2022-02-14, 18:50:00 GMT [KW, RP] + * # Blocks-15.0.0.txt + * # Date: 2022-01-28, 20:58:00 GMT [KW] + * UnicodeData.txt does not have a header. + */ + +#ifndef HB_OT_SHAPER_ARABIC_TABLE_HH +#define HB_OT_SHAPER_ARABIC_TABLE_HH + + +#define A JOINING_GROUP_ALAPH +#define DR JOINING_GROUP_DALATH_RISH +#define C JOINING_TYPE_C +#define D JOINING_TYPE_D +#define L JOINING_TYPE_L +#define R JOINING_TYPE_R +#define T JOINING_TYPE_T +#define U JOINING_TYPE_U +#define X JOINING_TYPE_X + +static const uint8_t joining_table[] = +{ + +#define joining_offset_0x0600u 0 + + /* Arabic */ + + /* 0600 */ U,U,U,U,U,U,X,X,U,X,X,U,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X, + /* 0620 */ D,U,R,R,R,R,D,R,D,R,D,D,D,D,D,R,R,R,R,D,D,D,D,D,D,D,D,D,D,D,D,D, + /* 0640 */ C,D,D,D,D,D,D,D,R,D,D,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X, + /* 0660 */ X,X,X,X,X,X,X,X,X,X,X,X,X,X,D,D,X,R,R,R,U,R,R,R,D,D,D,D,D,D,D,D, + /* 0680 */ D,D,D,D,D,D,D,D,R,R,R,R,R,R,R,R,R,R,R,R,R,R,R,R,R,R,D,D,D,D,D,D, + /* 06A0 */ D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D, + /* 06C0 */ R,D,D,R,R,R,R,R,R,R,R,R,D,R,D,R,D,D,R,R,X,R,X,X,X,X,X,X,X,U,X,X, + /* 06E0 */ X,X,X,X,X,X,X,X,X,X,X,X,X,X,R,R,X,X,X,X,X,X,X,X,X,X,D,D,D,X,X,D, + + /* Syriac */ + + /* 0700 */ X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,T,A,X,D,D,D,DR,DR,R,R,R,D,D,D,D,R,D, + /* 0720 */ D,D,D,D,D,D,D,D,R,D,DR,D,R,D,D,DR,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X, + /* 0740 */ X,X,X,X,X,X,X,X,X,X,X,X,X,R,D,D, + + /* Arabic Supplement */ + + /* 0740 */ D,D,D,D,D,D,D,D,D,R,R,R,D,D,D,D, + /* 0760 */ D,D,D,D,D,D,D,D,D,D,D,R,R,D,D,D,D,R,D,R,R,D,D,D,R,R,D,D,D,D,D,D, + + /* FILLER */ + + /* 0780 */ X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X, + /* 07A0 */ X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X, + + /* NKo */ + + /* 07C0 */ X,X,X,X,X,X,X,X,X,X,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D, + /* 07E0 */ D,D,D,D,D,D,D,D,D,D,D,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,C,X,X,X,X,X, + + /* FILLER */ + + /* 0800 */ X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X, + /* 0820 */ X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X, + + /* Mandaic */ + + /* 0840 */ R,D,D,D,D,D,R,R,D,R,D,D,D,D,D,D,D,D,D,D,R,D,R,R,R,X,X,X,X,X,X,X, + + /* Syriac Supplement */ + + /* 0860 */ D,U,D,D,D,D,U,R,D,R,R,X,X,X,X,X, + + /* Arabic Extended-B */ + + /* 0860 */ R,R,R,R,R,R,R,R,R,R,R,R,R,R,R,R, + /* 0880 */ R,R,R,C,C,C,D,U,U,D,D,D,D,D,R,X,U,U,X,X,X,X,X,X,X,X,X,X,X,X,X,X, + + /* Arabic Extended-A */ + + /* 08A0 */ D,D,D,D,D,D,D,D,D,D,R,R,R,U,R,D,D,R,R,D,D,D,D,D,D,R,D,D,D,D,D,D, + /* 08C0 */ D,D,D,D,D,D,D,D,D,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X, + /* 08E0 */ X,X,U, + +#define joining_offset_0x1806u 739 + + /* Mongolian */ + + /* 1800 */ U,D,X,X,C,X,X,X,U,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X, + /* 1820 */ D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D, + /* 1840 */ D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D, + /* 1860 */ D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,X,X,X,X,X,X,X, + /* 1880 */ U,U,U,U,U,T,T,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D, + /* 18A0 */ D,D,D,D,D,D,D,D,D,X,D, + +#define joining_offset_0x200cu 904 + + /* General Punctuation */ + + /* 2000 */ U,C,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X, + /* 2020 */ X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,U,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X, + /* 2040 */ X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X, + /* 2060 */ X,X,X,X,X,X,U,U,U,U, + +#define joining_offset_0xa840u 998 + + /* Phags-pa */ + + /* A840 */ D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D, + /* A860 */ D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,L,U, + +#define joining_offset_0x10ac0u 1050 + + /* Manichaean */ + + /* 10AC0 */ D,D,D,D,D,R,U,R,U,R,R,U,U,L,R,R,R,R,R,D,D,D,D,L,D,D,D,D,D,R,D,D, + /* 10AE0 */ D,R,U,U,R,X,X,X,X,X,X,D,D,D,D,R, + +#define joining_offset_0x10b80u 1098 + + /* Psalter Pahlavi */ + + /* 10B80 */ D,R,D,R,R,R,D,D,D,R,D,D,R,D,R,R,D,R,X,X,X,X,X,X,X,X,X,X,X,X,X,X, + /* 10BA0 */ X,X,X,X,X,X,X,X,X,R,R,R,R,D,D,U, + +#define joining_offset_0x10d00u 1146 + + /* Hanifi Rohingya */ + + /* 10D00 */ L,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D, + /* 10D20 */ D,D,R,D, + +#define joining_offset_0x10f30u 1182 + + /* Sogdian */ + + /* 10F20 */ D,D,D,R,D,D,D,D,D,D,D,D,D,D,D,D, + /* 10F40 */ D,D,D,D,D,U,X,X,X,X,X,X,X,X,X,X,X,D,D,D,R,X,X,X,X,X,X,X,X,X,X,X, + /* 10F60 */ X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X, + + /* Old Uyghur */ + + /* 10F60 */ D,D,D,D,R,R,D,D,D,D,D,D,D,D,D,D, + /* 10F80 */ D,D,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X, + /* 10FA0 */ X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X, + + /* Chorasmian */ + + /* 10FA0 */ D,U,D,D,R,R,R,U,D,R,R,D,D,R,D,D, + /* 10FC0 */ U,D,R,R,D,U,U,U,U,R,D,L, + +#define joining_offset_0x110bdu 1338 + + /* Kaithi */ + + /* 110A0 */ U,X,X, + /* 110C0 */ X,X,X,X,X,X,X,X,X,X,X,X,X,U, + +#define joining_offset_0x1e900u 1355 + + /* Adlam */ + + /* 1E900 */ D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D, + /* 1E920 */ D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D, + /* 1E940 */ D,D,D,D,X,X,X,X,X,X,X,T, + +}; /* Table items: 1431; occupancy: 57% */ + + +static unsigned int +joining_type (hb_codepoint_t u) +{ + switch (u >> 12) + { + case 0x0u: + if (hb_in_range (u, 0x0600u, 0x08E2u)) return joining_table[u - 0x0600u + joining_offset_0x0600u]; + break; + + case 0x1u: + if (hb_in_range (u, 0x1806u, 0x18AAu)) return joining_table[u - 0x1806u + joining_offset_0x1806u]; + break; + + case 0x2u: + if (hb_in_range (u, 0x200Cu, 0x2069u)) return joining_table[u - 0x200Cu + joining_offset_0x200cu]; + break; + + case 0xAu: + if (hb_in_range (u, 0xA840u, 0xA873u)) return joining_table[u - 0xA840u + joining_offset_0xa840u]; + break; + + case 0x10u: + if (hb_in_range (u, 0x10AC0u, 0x10AEFu)) return joining_table[u - 0x10AC0u + joining_offset_0x10ac0u]; + if (hb_in_range (u, 0x10B80u, 0x10BAFu)) return joining_table[u - 0x10B80u + joining_offset_0x10b80u]; + if (hb_in_range (u, 0x10D00u, 0x10D23u)) return joining_table[u - 0x10D00u + joining_offset_0x10d00u]; + if (hb_in_range (u, 0x10F30u, 0x10FCBu)) return joining_table[u - 0x10F30u + joining_offset_0x10f30u]; + break; + + case 0x11u: + if (hb_in_range (u, 0x110BDu, 0x110CDu)) return joining_table[u - 0x110BDu + joining_offset_0x110bdu]; + break; + + case 0x1Eu: + if (hb_in_range (u, 0x1E900u, 0x1E94Bu)) return joining_table[u - 0x1E900u + joining_offset_0x1e900u]; + break; + + default: + break; + } + return X; +} + +#undef A +#undef DR +#undef C +#undef D +#undef L +#undef R +#undef T +#undef U +#undef X + + +static const uint16_t shaping_table[][4] = +{ + {0x0000u, 0x0000u, 0x0000u, 0xFE80u}, /* U+0621 ARABIC LETTER HAMZA ISOLATED FORM */ + {0x0000u, 0x0000u, 0xFE82u, 0xFE81u}, /* U+0622 ARABIC LETTER ALEF WITH MADDA ABOVE */ + {0x0000u, 0x0000u, 0xFE84u, 0xFE83u}, /* U+0623 ARABIC LETTER ALEF WITH HAMZA ABOVE */ + {0x0000u, 0x0000u, 0xFE86u, 0xFE85u}, /* U+0624 ARABIC LETTER WAW WITH HAMZA ABOVE */ + {0x0000u, 0x0000u, 0xFE88u, 0xFE87u}, /* U+0625 ARABIC LETTER ALEF WITH HAMZA BELOW */ + {0xFE8Bu, 0xFE8Cu, 0xFE8Au, 0xFE89u}, /* U+0626 ARABIC LETTER YEH WITH HAMZA ABOVE */ + {0x0000u, 0x0000u, 0xFE8Eu, 0xFE8Du}, /* U+0627 ARABIC LETTER ALEF */ + {0xFE91u, 0xFE92u, 0xFE90u, 0xFE8Fu}, /* U+0628 ARABIC LETTER BEH */ + {0x0000u, 0x0000u, 0xFE94u, 0xFE93u}, /* U+0629 ARABIC LETTER TEH MARBUTA */ + {0xFE97u, 0xFE98u, 0xFE96u, 0xFE95u}, /* U+062A ARABIC LETTER TEH */ + {0xFE9Bu, 0xFE9Cu, 0xFE9Au, 0xFE99u}, /* U+062B ARABIC LETTER THEH */ + {0xFE9Fu, 0xFEA0u, 0xFE9Eu, 0xFE9Du}, /* U+062C ARABIC LETTER JEEM */ + {0xFEA3u, 0xFEA4u, 0xFEA2u, 0xFEA1u}, /* U+062D ARABIC LETTER HAH */ + {0xFEA7u, 0xFEA8u, 0xFEA6u, 0xFEA5u}, /* U+062E ARABIC LETTER KHAH */ + {0x0000u, 0x0000u, 0xFEAAu, 0xFEA9u}, /* U+062F ARABIC LETTER DAL */ + {0x0000u, 0x0000u, 0xFEACu, 0xFEABu}, /* U+0630 ARABIC LETTER THAL */ + {0x0000u, 0x0000u, 0xFEAEu, 0xFEADu}, /* U+0631 ARABIC LETTER REH */ + {0x0000u, 0x0000u, 0xFEB0u, 0xFEAFu}, /* U+0632 ARABIC LETTER ZAIN */ + {0xFEB3u, 0xFEB4u, 0xFEB2u, 0xFEB1u}, /* U+0633 ARABIC LETTER SEEN */ + {0xFEB7u, 0xFEB8u, 0xFEB6u, 0xFEB5u}, /* U+0634 ARABIC LETTER SHEEN */ + {0xFEBBu, 0xFEBCu, 0xFEBAu, 0xFEB9u}, /* U+0635 ARABIC LETTER SAD */ + {0xFEBFu, 0xFEC0u, 0xFEBEu, 0xFEBDu}, /* U+0636 ARABIC LETTER DAD */ + {0xFEC3u, 0xFEC4u, 0xFEC2u, 0xFEC1u}, /* U+0637 ARABIC LETTER TAH */ + {0xFEC7u, 0xFEC8u, 0xFEC6u, 0xFEC5u}, /* U+0638 ARABIC LETTER ZAH */ + {0xFECBu, 0xFECCu, 0xFECAu, 0xFEC9u}, /* U+0639 ARABIC LETTER AIN */ + {0xFECFu, 0xFED0u, 0xFECEu, 0xFECDu}, /* U+063A ARABIC LETTER GHAIN */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+063B */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+063C */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+063D */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+063E */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+063F */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0640 */ + {0xFED3u, 0xFED4u, 0xFED2u, 0xFED1u}, /* U+0641 ARABIC LETTER FEH */ + {0xFED7u, 0xFED8u, 0xFED6u, 0xFED5u}, /* U+0642 ARABIC LETTER QAF */ + {0xFEDBu, 0xFEDCu, 0xFEDAu, 0xFED9u}, /* U+0643 ARABIC LETTER KAF */ + {0xFEDFu, 0xFEE0u, 0xFEDEu, 0xFEDDu}, /* U+0644 ARABIC LETTER LAM */ + {0xFEE3u, 0xFEE4u, 0xFEE2u, 0xFEE1u}, /* U+0645 ARABIC LETTER MEEM */ + {0xFEE7u, 0xFEE8u, 0xFEE6u, 0xFEE5u}, /* U+0646 ARABIC LETTER NOON */ + {0xFEEBu, 0xFEECu, 0xFEEAu, 0xFEE9u}, /* U+0647 ARABIC LETTER HEH */ + {0x0000u, 0x0000u, 0xFEEEu, 0xFEEDu}, /* U+0648 ARABIC LETTER WAW */ + {0xFBE8u, 0xFBE9u, 0xFEF0u, 0xFEEFu}, /* U+0649 ARABIC LETTER */ + {0xFEF3u, 0xFEF4u, 0xFEF2u, 0xFEF1u}, /* U+064A ARABIC LETTER YEH */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+064B */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+064C */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+064D */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+064E */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+064F */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0650 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0651 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0652 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0653 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0654 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0655 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0656 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0657 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0658 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0659 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+065A */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+065B */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+065C */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+065D */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+065E */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+065F */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0660 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0661 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0662 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0663 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0664 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0665 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0666 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0667 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0668 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0669 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+066A */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+066B */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+066C */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+066D */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+066E */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+066F */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0670 */ + {0x0000u, 0x0000u, 0xFB51u, 0xFB50u}, /* U+0671 ARABIC LETTER ALEF WASLA */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0672 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0673 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0674 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0675 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0676 */ + {0x0000u, 0x0000u, 0x0000u, 0xFBDDu}, /* U+0677 ARABIC LETTER U WITH HAMZA ABOVE ISOLATED FORM */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0678 */ + {0xFB68u, 0xFB69u, 0xFB67u, 0xFB66u}, /* U+0679 ARABIC LETTER TTEH */ + {0xFB60u, 0xFB61u, 0xFB5Fu, 0xFB5Eu}, /* U+067A ARABIC LETTER TTEHEH */ + {0xFB54u, 0xFB55u, 0xFB53u, 0xFB52u}, /* U+067B ARABIC LETTER BEEH */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+067C */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+067D */ + {0xFB58u, 0xFB59u, 0xFB57u, 0xFB56u}, /* U+067E ARABIC LETTER PEH */ + {0xFB64u, 0xFB65u, 0xFB63u, 0xFB62u}, /* U+067F ARABIC LETTER TEHEH */ + {0xFB5Cu, 0xFB5Du, 0xFB5Bu, 0xFB5Au}, /* U+0680 ARABIC LETTER BEHEH */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0681 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0682 */ + {0xFB78u, 0xFB79u, 0xFB77u, 0xFB76u}, /* U+0683 ARABIC LETTER NYEH */ + {0xFB74u, 0xFB75u, 0xFB73u, 0xFB72u}, /* U+0684 ARABIC LETTER DYEH */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0685 */ + {0xFB7Cu, 0xFB7Du, 0xFB7Bu, 0xFB7Au}, /* U+0686 ARABIC LETTER TCHEH */ + {0xFB80u, 0xFB81u, 0xFB7Fu, 0xFB7Eu}, /* U+0687 ARABIC LETTER TCHEHEH */ + {0x0000u, 0x0000u, 0xFB89u, 0xFB88u}, /* U+0688 ARABIC LETTER DDAL */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0689 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+068A */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+068B */ + {0x0000u, 0x0000u, 0xFB85u, 0xFB84u}, /* U+068C ARABIC LETTER DAHAL */ + {0x0000u, 0x0000u, 0xFB83u, 0xFB82u}, /* U+068D ARABIC LETTER DDAHAL */ + {0x0000u, 0x0000u, 0xFB87u, 0xFB86u}, /* U+068E ARABIC LETTER DUL */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+068F */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0690 */ + {0x0000u, 0x0000u, 0xFB8Du, 0xFB8Cu}, /* U+0691 ARABIC LETTER RREH */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0692 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0693 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0694 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0695 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0696 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0697 */ + {0x0000u, 0x0000u, 0xFB8Bu, 0xFB8Au}, /* U+0698 ARABIC LETTER JEH */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0699 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+069A */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+069B */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+069C */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+069D */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+069E */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+069F */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+06A0 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+06A1 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+06A2 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+06A3 */ + {0xFB6Cu, 0xFB6Du, 0xFB6Bu, 0xFB6Au}, /* U+06A4 ARABIC LETTER VEH */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+06A5 */ + {0xFB70u, 0xFB71u, 0xFB6Fu, 0xFB6Eu}, /* U+06A6 ARABIC LETTER PEHEH */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+06A7 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+06A8 */ + {0xFB90u, 0xFB91u, 0xFB8Fu, 0xFB8Eu}, /* U+06A9 ARABIC LETTER KEHEH */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+06AA */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+06AB */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+06AC */ + {0xFBD5u, 0xFBD6u, 0xFBD4u, 0xFBD3u}, /* U+06AD ARABIC LETTER NG */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+06AE */ + {0xFB94u, 0xFB95u, 0xFB93u, 0xFB92u}, /* U+06AF ARABIC LETTER GAF */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+06B0 */ + {0xFB9Cu, 0xFB9Du, 0xFB9Bu, 0xFB9Au}, /* U+06B1 ARABIC LETTER NGOEH */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+06B2 */ + {0xFB98u, 0xFB99u, 0xFB97u, 0xFB96u}, /* U+06B3 ARABIC LETTER GUEH */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+06B4 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+06B5 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+06B6 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+06B7 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+06B8 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+06B9 */ + {0x0000u, 0x0000u, 0xFB9Fu, 0xFB9Eu}, /* U+06BA ARABIC LETTER NOON GHUNNA */ + {0xFBA2u, 0xFBA3u, 0xFBA1u, 0xFBA0u}, /* U+06BB ARABIC LETTER RNOON */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+06BC */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+06BD */ + {0xFBACu, 0xFBADu, 0xFBABu, 0xFBAAu}, /* U+06BE ARABIC LETTER HEH DOACHASHMEE */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+06BF */ + {0x0000u, 0x0000u, 0xFBA5u, 0xFBA4u}, /* U+06C0 ARABIC LETTER HEH WITH YEH ABOVE */ + {0xFBA8u, 0xFBA9u, 0xFBA7u, 0xFBA6u}, /* U+06C1 ARABIC LETTER HEH GOAL */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+06C2 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+06C3 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+06C4 */ + {0x0000u, 0x0000u, 0xFBE1u, 0xFBE0u}, /* U+06C5 ARABIC LETTER KIRGHIZ OE */ + {0x0000u, 0x0000u, 0xFBDAu, 0xFBD9u}, /* U+06C6 ARABIC LETTER OE */ + {0x0000u, 0x0000u, 0xFBD8u, 0xFBD7u}, /* U+06C7 ARABIC LETTER U */ + {0x0000u, 0x0000u, 0xFBDCu, 0xFBDBu}, /* U+06C8 ARABIC LETTER YU */ + {0x0000u, 0x0000u, 0xFBE3u, 0xFBE2u}, /* U+06C9 ARABIC LETTER KIRGHIZ YU */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+06CA */ + {0x0000u, 0x0000u, 0xFBDFu, 0xFBDEu}, /* U+06CB ARABIC LETTER VE */ + {0xFBFEu, 0xFBFFu, 0xFBFDu, 0xFBFCu}, /* U+06CC ARABIC LETTER FARSI YEH */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+06CD */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+06CE */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+06CF */ + {0xFBE6u, 0xFBE7u, 0xFBE5u, 0xFBE4u}, /* U+06D0 ARABIC LETTER E */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+06D1 */ + {0x0000u, 0x0000u, 0xFBAFu, 0xFBAEu}, /* U+06D2 ARABIC LETTER YEH BARREE */ + {0x0000u, 0x0000u, 0xFBB1u, 0xFBB0u}, /* U+06D3 ARABIC LETTER YEH BARREE WITH HAMZA ABOVE */ +}; + +#define SHAPING_TABLE_FIRST 0x0621u +#define SHAPING_TABLE_LAST 0x06D3u + + +static const struct ligature_set_t { + uint16_t first; + struct ligature_pairs_t { + uint16_t components[1]; + uint16_t ligature; + } ligatures[14]; +} ligature_table[] = +{ + { 0xFE91u, { + { {0xFEE2u}, 0xFC08u }, /* ARABIC LIGATURE BEH WITH MEEM ISOLATED FORM */ + { {0xFEE4u}, 0xFC9Fu }, /* ARABIC LIGATURE BEH WITH MEEM INITIAL FORM */ + { {0xFEA0u}, 0xFC9Cu }, /* ARABIC LIGATURE BEH WITH JEEM INITIAL FORM */ + { {0xFEA4u}, 0xFC9Du }, /* ARABIC LIGATURE BEH WITH HAH INITIAL FORM */ + { {0xFEA8u}, 0xFC9Eu }, /* ARABIC LIGATURE BEH WITH KHAH INITIAL FORM */ + }}, + { 0xFE92u, { + { {0xFEAEu}, 0xFC6Au }, /* ARABIC LIGATURE BEH WITH REH FINAL FORM */ + { {0xFEE6u}, 0xFC6Du }, /* ARABIC LIGATURE BEH WITH NOON FINAL FORM */ + { {0xFEF2u}, 0xFC6Fu }, /* ARABIC LIGATURE BEH WITH YEH FINAL FORM */ + }}, + { 0xFE97u, { + { {0xFEE2u}, 0xFC0Eu }, /* ARABIC LIGATURE TEH WITH MEEM ISOLATED FORM */ + { {0xFEE4u}, 0xFCA4u }, /* ARABIC LIGATURE TEH WITH MEEM INITIAL FORM */ + { {0xFEA0u}, 0xFCA1u }, /* ARABIC LIGATURE TEH WITH JEEM INITIAL FORM */ + { {0xFEA4u}, 0xFCA2u }, /* ARABIC LIGATURE TEH WITH HAH INITIAL FORM */ + { {0xFEA8u}, 0xFCA3u }, /* ARABIC LIGATURE TEH WITH KHAH INITIAL FORM */ + }}, + { 0xFE98u, { + { {0xFEAEu}, 0xFC70u }, /* ARABIC LIGATURE TEH WITH REH FINAL FORM */ + { {0xFEE6u}, 0xFC73u }, /* ARABIC LIGATURE TEH WITH NOON FINAL FORM */ + { {0xFEF2u}, 0xFC75u }, /* ARABIC LIGATURE TEH WITH YEH FINAL FORM */ + }}, + { 0xFE9Bu, { + { {0xFEE2u}, 0xFC12u }, /* ARABIC LIGATURE THEH WITH MEEM ISOLATED FORM */ + }}, + { 0xFE9Fu, { + { {0xFEE4u}, 0xFCA8u }, /* ARABIC LIGATURE JEEM WITH MEEM INITIAL FORM */ + }}, + { 0xFEA3u, { + { {0xFEE4u}, 0xFCAAu }, /* ARABIC LIGATURE HAH WITH MEEM INITIAL FORM */ + }}, + { 0xFEA7u, { + { {0xFEE4u}, 0xFCACu }, /* ARABIC LIGATURE KHAH WITH MEEM INITIAL FORM */ + }}, + { 0xFEB3u, { + { {0xFEE4u}, 0xFCB0u }, /* ARABIC LIGATURE SEEN WITH MEEM INITIAL FORM */ + }}, + { 0xFEB7u, { + { {0xFEE4u}, 0xFD30u }, /* ARABIC LIGATURE SHEEN WITH MEEM INITIAL FORM */ + }}, + { 0xFED3u, { + { {0xFEF2u}, 0xFC32u }, /* ARABIC LIGATURE FEH WITH YEH ISOLATED FORM */ + }}, + { 0xFEDFu, { + { {0xFE9Eu}, 0xFC3Fu }, /* ARABIC LIGATURE LAM WITH JEEM ISOLATED FORM */ + { {0xFEA0u}, 0xFCC9u }, /* ARABIC LIGATURE LAM WITH JEEM INITIAL FORM */ + { {0xFEA2u}, 0xFC40u }, /* ARABIC LIGATURE LAM WITH HAH ISOLATED FORM */ + { {0xFEA4u}, 0xFCCAu }, /* ARABIC LIGATURE LAM WITH HAH INITIAL FORM */ + { {0xFEA6u}, 0xFC41u }, /* ARABIC LIGATURE LAM WITH KHAH ISOLATED FORM */ + { {0xFEA8u}, 0xFCCBu }, /* ARABIC LIGATURE LAM WITH KHAH INITIAL FORM */ + { {0xFEE2u}, 0xFC42u }, /* ARABIC LIGATURE LAM WITH MEEM ISOLATED FORM */ + { {0xFEE4u}, 0xFCCCu }, /* ARABIC LIGATURE LAM WITH MEEM INITIAL FORM */ + { {0xFEF2u}, 0xFC44u }, /* ARABIC LIGATURE LAM WITH YEH ISOLATED FORM */ + { {0xFEECu}, 0xFCCDu }, /* ARABIC LIGATURE LAM WITH HEH INITIAL FORM */ + { {0xFE82u}, 0xFEF5u }, /* ARABIC LIGATURE LAM WITH ALEF WITH MADDA ABOVE ISOLATED FORM */ + { {0xFE84u}, 0xFEF7u }, /* ARABIC LIGATURE LAM WITH ALEF WITH HAMZA ABOVE ISOLATED FORM */ + { {0xFE88u}, 0xFEF9u }, /* ARABIC LIGATURE LAM WITH ALEF WITH HAMZA BELOW ISOLATED FORM */ + { {0xFE8Eu}, 0xFEFBu }, /* ARABIC LIGATURE LAM WITH ALEF ISOLATED FORM */ + }}, + { 0xFEE0u, { + { {0xFEF0u}, 0xFC86u }, /* ARABIC LIGATURE LAM WITH ALEF MAKSURA FINAL FORM */ + { {0xFE82u}, 0xFEF6u }, /* ARABIC LIGATURE LAM WITH ALEF WITH MADDA ABOVE FINAL FORM */ + { {0xFE84u}, 0xFEF8u }, /* ARABIC LIGATURE LAM WITH ALEF WITH HAMZA ABOVE FINAL FORM */ + { {0xFE88u}, 0xFEFAu }, /* ARABIC LIGATURE LAM WITH ALEF WITH HAMZA BELOW FINAL FORM */ + { {0xFE8Eu}, 0xFEFCu }, /* ARABIC LIGATURE LAM WITH ALEF FINAL FORM */ + }}, + { 0xFEE3u, { + { {0xFEA0u}, 0xFCCEu }, /* ARABIC LIGATURE MEEM WITH JEEM INITIAL FORM */ + { {0xFEA4u}, 0xFCCFu }, /* ARABIC LIGATURE MEEM WITH HAH INITIAL FORM */ + { {0xFEA8u}, 0xFCD0u }, /* ARABIC LIGATURE MEEM WITH KHAH INITIAL FORM */ + { {0xFEE4u}, 0xFCD1u }, /* ARABIC LIGATURE MEEM WITH MEEM INITIAL FORM */ + }}, + { 0xFEE7u, { + { {0xFEE2u}, 0xFC4Eu }, /* ARABIC LIGATURE NOON WITH MEEM ISOLATED FORM */ + { {0xFEE4u}, 0xFCD5u }, /* ARABIC LIGATURE NOON WITH MEEM INITIAL FORM */ + { {0xFEA0u}, 0xFCD2u }, /* ARABIC LIGATURE NOON WITH JEEM INITIAL FORM */ + { {0xFEA4u}, 0xFCD3u }, /* ARABIC LIGATURE NOON WITH HAH INITIAL FORM */ + }}, + { 0xFEE8u, { + { {0xFEF2u}, 0xFC8Fu }, /* ARABIC LIGATURE NOON WITH YEH FINAL FORM */ + }}, + { 0xFEF3u, { + { {0xFEA0u}, 0xFCDAu }, /* ARABIC LIGATURE YEH WITH JEEM INITIAL FORM */ + { {0xFEA4u}, 0xFCDBu }, /* ARABIC LIGATURE YEH WITH HAH INITIAL FORM */ + { {0xFEA8u}, 0xFCDCu }, /* ARABIC LIGATURE YEH WITH KHAH INITIAL FORM */ + { {0xFEE4u}, 0xFCDDu }, /* ARABIC LIGATURE YEH WITH MEEM INITIAL FORM */ + }}, + { 0xFEF4u, { + { {0xFEAEu}, 0xFC91u }, /* ARABIC LIGATURE YEH WITH REH FINAL FORM */ + { {0xFEE6u}, 0xFC94u }, /* ARABIC LIGATURE YEH WITH NOON FINAL FORM */ + }}, +}; + + +static const struct ligature_mark_set_t { + uint16_t first; + struct ligature_pairs_t { + uint16_t components[1]; + uint16_t ligature; + } ligatures[5]; +} ligature_mark_table[] = +{ + { 0x0651u, { + { {0x064Cu}, 0xFC5Eu }, /* ARABIC LIGATURE SHADDA WITH DAMMATAN ISOLATED FORM */ + { {0x064Eu}, 0xFC60u }, /* ARABIC LIGATURE SHADDA WITH FATHA ISOLATED FORM */ + { {0x064Fu}, 0xFC61u }, /* ARABIC LIGATURE SHADDA WITH DAMMA ISOLATED FORM */ + { {0x0650u}, 0xFC62u }, /* ARABIC LIGATURE SHADDA WITH KASRA ISOLATED FORM */ + { {0x064Bu}, 0xF2EEu }, /* PUA ARABIC LIGATURE SHADDA WITH FATHATAN ISOLATED FORM */ + }}, +}; + + +static const struct ligature_3_set_t { + uint16_t first; + struct ligature_triplets_t { + uint16_t components[2]; + uint16_t ligature; + } ligatures[3]; +} ligature_3_table[] = +{ + { 0xFEDFu, { + { {0xFEE4u, 0xFEA4u}, 0xFD88u}, /* ARABIC LIGATURE LAM WITH MEEM WITH HAH INITIAL FORM */ + { {0xFEE0u, 0xFEEAu}, 0xF201u}, /* PUA ARABIC LIGATURE LELLAH ISOLATED FORM */ + { {0xFEE4u, 0xFEA0u}, 0xF211u}, /* PUA ARABIC LIGATURE LAM WITH MEEM WITH JEEM INITIAL FORM */ + }}, +}; + + +#endif /* HB_OT_SHAPER_ARABIC_TABLE_HH */ + +/* == End of generated table == */ diff --git a/libs/harfbuzz/src/hb-ot-shaper-arabic-win1256.hh b/libs/harfbuzz/src/hb-ot-shaper-arabic-win1256.hh new file mode 100644 index 00000000..b8d481c8 --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-shaper-arabic-win1256.hh @@ -0,0 +1,349 @@ +/* + * Copyright © 2014 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 HB_OT_SHAPER_ARABIC_WIN1256_HH + + +/* + * The macros in the first part of this file are generic macros that can + * be used to define the bytes for OpenType table data in code in a + * readable manner. We can move the macros to reside with their respective + * struct types, but since we only use these to define one data table, the + * Windows-1256 Arabic shaping table in this file, we keep them here. + */ + + +/* First we measure, then we cut. */ +#ifndef OT_MEASURE +#define OT_MEASURE +#define OT_TABLE_START static const struct TABLE_NAME { +#define OT_TABLE_END } +#define OT_LABEL_START(Name) unsigned char Name[ +#define OT_LABEL_END ]; +#define OT_UINT8(u8) +1/*byte*/ +#define OT_UINT16(u16) +2/*bytes*/ +#else +#undef OT_MEASURE +#define OT_TABLE_START TABLE_NAME = { +#define OT_TABLE_END }; +#define OT_LABEL_START(Name) { +#define OT_LABEL_END }, +#define OT_UINT8(u8) (u8), +#define OT_UINT16(u16) (unsigned char)((u16)>>8), (unsigned char)((u16)&0xFFu), +#define OT_COUNT(Name, ItemSize) ((unsigned int) sizeof(((struct TABLE_NAME*)0)->Name) \ + / (unsigned int)(ItemSize) \ + /* OT_ASSERT it's divisible (and positive). */) +#define OT_DISTANCE(From,To) ((unsigned int) \ + ((char*)(&((struct TABLE_NAME*)0)->To) - \ + (char*)(&((struct TABLE_NAME*)0)->From)) \ + /* OT_ASSERT it's positive. */) +#endif + + +#define OT_LABEL(Name) \ + OT_LABEL_END \ + OT_LABEL_START(Name) + +/* Whenever we receive an argument that is a list, it will expand to + * contain commas. That cannot be passed to another macro because the + * commas will throw off the preprocessor. The solution is to wrap + * the passed-in argument in OT_LIST() before passing to the next macro. + * Unfortunately this trick requires vararg macros. */ +#define OT_LIST(...) __VA_ARGS__ + + +/* + * Basic Types + */ + +#define OT_TAG(a,b,c,d) \ + OT_UINT8(a) OT_UINT8(b) OT_UINT8(c) OT_UINT8(d) + +#define OT_OFFSET(From, To) /* Offset from From to To in bytes */ \ + OT_UINT16(OT_DISTANCE(From, To)) + +#define OT_GLYPHID /* GlyphID */ \ + OT_UINT16 +/* Shorthand. */ +#define G OT_GLYPHID + +#define OT_UARRAY(Name, Items) \ + OT_LABEL_START(Name) \ + OT_UINT16(OT_COUNT(Name##Data, 2)) \ + OT_LABEL(Name##Data) \ + Items \ + OT_LABEL_END + +#define OT_UHEADLESSARRAY(Name, Items) \ + OT_LABEL_START(Name) \ + OT_UINT16(OT_COUNT(Name##Data, 2) + 1) \ + OT_LABEL(Name##Data) \ + Items \ + OT_LABEL_END + + +/* + * Common Types + */ + +#define OT_LOOKUP_FLAG_IGNORE_MARKS 0x08u + +#define OT_LOOKUP(Name, LookupType, LookupFlag, SubLookupOffsets) \ + OT_LABEL_START(Name) \ + OT_UINT16(LookupType) \ + OT_UINT16(LookupFlag) \ + OT_LABEL_END \ + OT_UARRAY(Name##SubLookupOffsetsArray, OT_LIST(SubLookupOffsets)) + +#define OT_SUBLOOKUP(Name, SubFormat, Items) \ + OT_LABEL_START(Name) \ + OT_UINT16(SubFormat) \ + Items + +#define OT_COVERAGE1(Name, Items) \ + OT_LABEL_START(Name) \ + OT_UINT16(1) \ + OT_LABEL_END \ + OT_UARRAY(Name##Glyphs, OT_LIST(Items)) + + +/* + * GSUB + */ + +#define OT_LOOKUP_TYPE_SUBST_SINGLE 1u +#define OT_LOOKUP_TYPE_SUBST_LIGATURE 4u + +#define OT_SUBLOOKUP_SINGLE_SUBST_FORMAT2(Name, FromGlyphs, ToGlyphs) \ + OT_SUBLOOKUP(Name, 2, \ + OT_OFFSET(Name, Name##Coverage) \ + OT_LABEL_END \ + OT_UARRAY(Name##Substitute, OT_LIST(ToGlyphs)) \ + ) \ + OT_COVERAGE1(Name##Coverage, OT_LIST(FromGlyphs)) \ + /* static_assert_expr (len(FromGlyphs) == len(ToGlyphs)) */ + +#define OT_SUBLOOKUP_LIGATURE_SUBST_FORMAT1(Name, FirstGlyphs, LigatureSetOffsets) \ + OT_SUBLOOKUP(Name, 1, \ + OT_OFFSET(Name, Name##Coverage) \ + OT_LABEL_END \ + OT_UARRAY(Name##LigatureSetOffsetsArray, OT_LIST(LigatureSetOffsets)) \ + ) \ + OT_COVERAGE1(Name##Coverage, OT_LIST(FirstGlyphs)) \ + /* static_assert_expr (len(FirstGlyphs) == len(LigatureSetOffsets)) */ + +#define OT_LIGATURE_SET(Name, LigatureSetOffsets) \ + OT_UARRAY(Name, OT_LIST(LigatureSetOffsets)) + +#define OT_LIGATURE(Name, Components, LigGlyph) \ + OT_LABEL_START(Name) \ + LigGlyph \ + OT_LABEL_END \ + OT_UHEADLESSARRAY(Name##ComponentsArray, OT_LIST(Components)) + +/* + * + * Start of Windows-1256 shaping table. + * + */ + +/* Table name. */ +#define TABLE_NAME arabic_win1256_gsub_lookups + +/* Table manifest. */ +#define MANIFEST(Items) \ + OT_LABEL_START(manifest) \ + OT_UINT16(OT_COUNT(manifestData, 6)) \ + OT_LABEL(manifestData) \ + Items \ + OT_LABEL_END + +#define MANIFEST_LOOKUP(Tag, Name) \ + Tag \ + OT_OFFSET(manifest, Name) + + +/* + * Table Start + */ +OT_TABLE_START + + +/* + * Manifest + */ +MANIFEST( + MANIFEST_LOOKUP(OT_TAG('r','l','i','g'), rligLookup) + MANIFEST_LOOKUP(OT_TAG('i','n','i','t'), initLookup) + MANIFEST_LOOKUP(OT_TAG('m','e','d','i'), mediLookup) + MANIFEST_LOOKUP(OT_TAG('f','i','n','a'), finaLookup) + MANIFEST_LOOKUP(OT_TAG('r','l','i','g'), rligMarksLookup) +) + +/* + * Lookups + */ +OT_LOOKUP(initLookup, OT_LOOKUP_TYPE_SUBST_SINGLE, OT_LOOKUP_FLAG_IGNORE_MARKS, + OT_OFFSET(initLookup, initmediSubLookup) + OT_OFFSET(initLookup, initSubLookup) +) +OT_LOOKUP(mediLookup, OT_LOOKUP_TYPE_SUBST_SINGLE, OT_LOOKUP_FLAG_IGNORE_MARKS, + OT_OFFSET(mediLookup, initmediSubLookup) + OT_OFFSET(mediLookup, mediSubLookup) + OT_OFFSET(mediLookup, medifinaLamAlefSubLookup) +) +OT_LOOKUP(finaLookup, OT_LOOKUP_TYPE_SUBST_SINGLE, OT_LOOKUP_FLAG_IGNORE_MARKS, + OT_OFFSET(finaLookup, finaSubLookup) + /* We don't need this one currently as the sequence inherits masks + * from the first item. Just in case we change that in the future + * to be smart about Arabic masks when ligating... */ + OT_OFFSET(finaLookup, medifinaLamAlefSubLookup) +) +OT_LOOKUP(rligLookup, OT_LOOKUP_TYPE_SUBST_LIGATURE, OT_LOOKUP_FLAG_IGNORE_MARKS, + OT_OFFSET(rligLookup, lamAlefLigaturesSubLookup) +) +OT_LOOKUP(rligMarksLookup, OT_LOOKUP_TYPE_SUBST_LIGATURE, 0, + OT_OFFSET(rligMarksLookup, shaddaLigaturesSubLookup) +) + +/* + * init/medi/fina forms + */ +OT_SUBLOOKUP_SINGLE_SUBST_FORMAT2(initmediSubLookup, + G(198) G(200) G(201) G(202) G(203) G(204) G(205) G(206) G(211) + G(212) G(213) G(214) G(223) G(225) G(227) G(228) G(236) G(237), + G(162) G(4) G(5) G(5) G(6) G(7) G(9) G(11) G(13) + G(14) G(15) G(26) G(140) G(141) G(142) G(143) G(154) G(154) +) +OT_SUBLOOKUP_SINGLE_SUBST_FORMAT2(initSubLookup, + G(218) G(219) G(221) G(222) G(229), + G(27) G(30) G(128) G(131) G(144) +) +OT_SUBLOOKUP_SINGLE_SUBST_FORMAT2(mediSubLookup, + G(218) G(219) G(221) G(222) G(229), + G(28) G(31) G(129) G(138) G(149) +) +OT_SUBLOOKUP_SINGLE_SUBST_FORMAT2(finaSubLookup, + G(194) G(195) G(197) G(198) G(199) G(201) G(204) G(205) G(206) + G(218) G(219) G(229) G(236) G(237), + G(2) G(1) G(3) G(181) G(0) G(159) G(8) G(10) G(12) + G(29) G(127) G(152) G(160) G(156) +) +OT_SUBLOOKUP_SINGLE_SUBST_FORMAT2(medifinaLamAlefSubLookup, + G(165) G(178) G(180) G(252), + G(170) G(179) G(185) G(255) +) + +/* + * Lam+Alef ligatures + */ +OT_SUBLOOKUP_LIGATURE_SUBST_FORMAT1(lamAlefLigaturesSubLookup, + G(225), + OT_OFFSET(lamAlefLigaturesSubLookup, lamLigatureSet) +) +OT_LIGATURE_SET(lamLigatureSet, + OT_OFFSET(lamLigatureSet, lamInitLigature1) + OT_OFFSET(lamLigatureSet, lamInitLigature2) + OT_OFFSET(lamLigatureSet, lamInitLigature3) + OT_OFFSET(lamLigatureSet, lamInitLigature4) +) +OT_LIGATURE(lamInitLigature1, G(199), G(165)) +OT_LIGATURE(lamInitLigature2, G(195), G(178)) +OT_LIGATURE(lamInitLigature3, G(194), G(180)) +OT_LIGATURE(lamInitLigature4, G(197), G(252)) + +/* + * Shadda ligatures + */ +OT_SUBLOOKUP_LIGATURE_SUBST_FORMAT1(shaddaLigaturesSubLookup, + G(248), + OT_OFFSET(shaddaLigaturesSubLookup, shaddaLigatureSet) +) +OT_LIGATURE_SET(shaddaLigatureSet, + OT_OFFSET(shaddaLigatureSet, shaddaLigature1) + OT_OFFSET(shaddaLigatureSet, shaddaLigature2) + OT_OFFSET(shaddaLigatureSet, shaddaLigature3) +) +OT_LIGATURE(shaddaLigature1, G(243), G(172)) +OT_LIGATURE(shaddaLigature2, G(245), G(173)) +OT_LIGATURE(shaddaLigature3, G(246), G(175)) + +/* + * Table end + */ +OT_TABLE_END + + +/* + * Clean up + */ + +#undef MANIFEST +#undef MANIFEST_LOOKUP + +#undef OT_TABLE_START +#undef OT_TABLE_END +#undef OT_LABEL_START +#undef OT_LABEL_END +#undef OT_UINT8 +#undef OT_UINT16 +#undef OT_COUNT +#undef OT_DISTANCE + +#undef OT_LABEL +#undef OT_LIST + +#undef OT_TAG +#undef OT_OFFSET +#undef OT_GLYPHID +#undef G +#undef OT_UARRAY +#undef OT_UHEADLESSARRAY + +#undef OT_LOOKUP_FLAG_IGNORE_MARKS +#undef OT_LOOKUP +#undef OT_SUBLOOKUP +#undef OT_COVERAGE1 +#undef OT_LOOKUP_TYPE_SUBST_SINGLE +#undef OT_LOOKUP_TYPE_SUBST_LIGATURE +#undef OT_SUBLOOKUP_SINGLE_SUBST_FORMAT2 +#undef OT_SUBLOOKUP_LIGATURE_SUBST_FORMAT1 +#undef OT_LIGATURE_SET +#undef OT_LIGATURE + + +/* + * Include a second time to get the table data... + */ +#if 0 +#include "hb.hh" /* Make check-includes.sh happy. */ +#endif +#ifdef OT_MEASURE +#include "hb-ot-shaper-arabic-win1256.hh" +#endif + +#define HB_OT_SHAPER_ARABIC_WIN1256_HH +#endif /* HB_OT_SHAPER_ARABIC_WIN1256_HH */ diff --git a/libs/harfbuzz/src/hb-ot-shaper-arabic.cc b/libs/harfbuzz/src/hb-ot-shaper-arabic.cc new file mode 100644 index 00000000..ed2748b8 --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-shaper-arabic.cc @@ -0,0 +1,752 @@ +/* + * Copyright © 2010,2012 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 + */ + +#include "hb.hh" + +#ifndef HB_NO_OT_SHAPE + +#include "hb-ot-shaper-arabic.hh" +#include "hb-ot-shape.hh" + + +/* buffer var allocations */ +#define arabic_shaping_action() ot_shaper_var_u8_auxiliary() /* arabic shaping action */ + +#define HB_BUFFER_SCRATCH_FLAG_ARABIC_HAS_STCH HB_BUFFER_SCRATCH_FLAG_SHAPER0 + +/* See: + * https://github.com/harfbuzz/harfbuzz/commit/6e6f82b6f3dde0fc6c3c7d991d9ec6cfff57823d#commitcomment-14248516 */ +#define HB_ARABIC_GENERAL_CATEGORY_IS_WORD(gen_cat) \ + (FLAG_UNSAFE (gen_cat) & \ + (FLAG (HB_UNICODE_GENERAL_CATEGORY_UNASSIGNED) | \ + FLAG (HB_UNICODE_GENERAL_CATEGORY_PRIVATE_USE) | \ + /*FLAG (HB_UNICODE_GENERAL_CATEGORY_LOWERCASE_LETTER) |*/ \ + FLAG (HB_UNICODE_GENERAL_CATEGORY_MODIFIER_LETTER) | \ + FLAG (HB_UNICODE_GENERAL_CATEGORY_OTHER_LETTER) | \ + /*FLAG (HB_UNICODE_GENERAL_CATEGORY_TITLECASE_LETTER) |*/ \ + /*FLAG (HB_UNICODE_GENERAL_CATEGORY_UPPERCASE_LETTER) |*/ \ + FLAG (HB_UNICODE_GENERAL_CATEGORY_SPACING_MARK) | \ + FLAG (HB_UNICODE_GENERAL_CATEGORY_ENCLOSING_MARK) | \ + FLAG (HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK) | \ + FLAG (HB_UNICODE_GENERAL_CATEGORY_DECIMAL_NUMBER) | \ + FLAG (HB_UNICODE_GENERAL_CATEGORY_LETTER_NUMBER) | \ + FLAG (HB_UNICODE_GENERAL_CATEGORY_OTHER_NUMBER) | \ + FLAG (HB_UNICODE_GENERAL_CATEGORY_CURRENCY_SYMBOL) | \ + FLAG (HB_UNICODE_GENERAL_CATEGORY_MODIFIER_SYMBOL) | \ + FLAG (HB_UNICODE_GENERAL_CATEGORY_MATH_SYMBOL) | \ + FLAG (HB_UNICODE_GENERAL_CATEGORY_OTHER_SYMBOL))) + + +/* + * Joining types: + */ + +/* + * Bits used in the joining tables + */ +enum hb_arabic_joining_type_t { + JOINING_TYPE_U = 0, + JOINING_TYPE_L = 1, + JOINING_TYPE_R = 2, + JOINING_TYPE_D = 3, + JOINING_TYPE_C = JOINING_TYPE_D, + JOINING_GROUP_ALAPH = 4, + JOINING_GROUP_DALATH_RISH = 5, + NUM_STATE_MACHINE_COLS = 6, + + JOINING_TYPE_T = 7, + JOINING_TYPE_X = 8 /* means: use general-category to choose between U or T. */ +}; + +#include "hb-ot-shaper-arabic-table.hh" + +static unsigned int get_joining_type (hb_codepoint_t u, hb_unicode_general_category_t gen_cat) +{ + unsigned int j_type = joining_type(u); + if (likely (j_type != JOINING_TYPE_X)) + return j_type; + + return (FLAG_UNSAFE(gen_cat) & + (FLAG(HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK) | + FLAG(HB_UNICODE_GENERAL_CATEGORY_ENCLOSING_MARK) | + FLAG(HB_UNICODE_GENERAL_CATEGORY_FORMAT)) + ) ? JOINING_TYPE_T : JOINING_TYPE_U; +} + +#define FEATURE_IS_SYRIAC(tag) hb_in_range ((unsigned char) (tag), '2', '3') + +static const hb_tag_t arabic_features[] = +{ + HB_TAG('i','s','o','l'), + HB_TAG('f','i','n','a'), + HB_TAG('f','i','n','2'), + HB_TAG('f','i','n','3'), + HB_TAG('m','e','d','i'), + HB_TAG('m','e','d','2'), + HB_TAG('i','n','i','t'), + HB_TAG_NONE +}; + + +/* Same order as the feature array */ +enum arabic_action_t { + ISOL, + FINA, + FIN2, + FIN3, + MEDI, + MED2, + INIT, + + NONE, + + ARABIC_NUM_FEATURES = NONE, + + /* We abuse the same byte for other things... */ + STCH_FIXED, + STCH_REPEATING, +}; + +static const struct arabic_state_table_entry { + uint8_t prev_action; + uint8_t curr_action; + uint16_t next_state; +} arabic_state_table[][NUM_STATE_MACHINE_COLS] = +{ + /* jt_U, jt_L, jt_R, jt_D, jg_ALAPH, jg_DALATH_RISH */ + + /* State 0: prev was U, not willing to join. */ + { {NONE,NONE,0}, {NONE,ISOL,2}, {NONE,ISOL,1}, {NONE,ISOL,2}, {NONE,ISOL,1}, {NONE,ISOL,6}, }, + + /* State 1: prev was R or ISOL/ALAPH, not willing to join. */ + { {NONE,NONE,0}, {NONE,ISOL,2}, {NONE,ISOL,1}, {NONE,ISOL,2}, {NONE,FIN2,5}, {NONE,ISOL,6}, }, + + /* State 2: prev was D/L in ISOL form, willing to join. */ + { {NONE,NONE,0}, {NONE,ISOL,2}, {INIT,FINA,1}, {INIT,FINA,3}, {INIT,FINA,4}, {INIT,FINA,6}, }, + + /* State 3: prev was D in FINA form, willing to join. */ + { {NONE,NONE,0}, {NONE,ISOL,2}, {MEDI,FINA,1}, {MEDI,FINA,3}, {MEDI,FINA,4}, {MEDI,FINA,6}, }, + + /* State 4: prev was FINA ALAPH, not willing to join. */ + { {NONE,NONE,0}, {NONE,ISOL,2}, {MED2,ISOL,1}, {MED2,ISOL,2}, {MED2,FIN2,5}, {MED2,ISOL,6}, }, + + /* State 5: prev was FIN2/FIN3 ALAPH, not willing to join. */ + { {NONE,NONE,0}, {NONE,ISOL,2}, {ISOL,ISOL,1}, {ISOL,ISOL,2}, {ISOL,FIN2,5}, {ISOL,ISOL,6}, }, + + /* State 6: prev was DALATH/RISH, not willing to join. */ + { {NONE,NONE,0}, {NONE,ISOL,2}, {NONE,ISOL,1}, {NONE,ISOL,2}, {NONE,FIN3,5}, {NONE,ISOL,6}, } +}; + + +static void +arabic_fallback_shape (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer); + +static void +record_stch (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer); + +static void +deallocate_buffer_var (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer) +{ + HB_BUFFER_DEALLOCATE_VAR (buffer, arabic_shaping_action); +} + +static void +collect_features_arabic (hb_ot_shape_planner_t *plan) +{ + hb_ot_map_builder_t *map = &plan->map; + + /* We apply features according to the Arabic spec, with pauses + * in between most. + * + * The pause between init/medi/... and rlig is required. See eg: + * https://bugzilla.mozilla.org/show_bug.cgi?id=644184 + * + * The pauses between init/medi/... themselves are not necessarily + * needed as only one of those features is applied to any character. + * The only difference it makes is when fonts have contextual + * substitutions. We now follow the order of the spec, which makes + * for better experience if that's what Uniscribe is doing. + * + * At least for Arabic, looks like Uniscribe has a pause between + * rlig and calt. Otherwise the IranNastaliq's ALLAH ligature won't + * work. However, testing shows that rlig and calt are applied + * together for Mongolian in Uniscribe. As such, we only add a + * pause for Arabic, not other scripts. + */ + + + map->enable_feature (HB_TAG('s','t','c','h')); + map->add_gsub_pause (record_stch); + + map->enable_feature (HB_TAG('c','c','m','p'), F_MANUAL_ZWJ); + map->enable_feature (HB_TAG('l','o','c','l'), F_MANUAL_ZWJ); + + map->add_gsub_pause (nullptr); + + for (unsigned int i = 0; i < ARABIC_NUM_FEATURES; i++) + { + bool has_fallback = plan->props.script == HB_SCRIPT_ARABIC && !FEATURE_IS_SYRIAC (arabic_features[i]); + map->add_feature (arabic_features[i], F_MANUAL_ZWJ | (has_fallback ? F_HAS_FALLBACK : F_NONE)); + map->add_gsub_pause (nullptr); + } + map->add_gsub_pause (deallocate_buffer_var); + + /* Normally, Unicode says a ZWNJ means "don't ligate". In Arabic script + * however, it says a ZWJ should also mean "don't ligate". So we run + * the main ligating features as MANUAL_ZWJ. */ + + map->enable_feature (HB_TAG('r','l','i','g'), F_MANUAL_ZWJ | F_HAS_FALLBACK); + + if (plan->props.script == HB_SCRIPT_ARABIC) + map->add_gsub_pause (arabic_fallback_shape); + + map->enable_feature (HB_TAG('c','a','l','t'), F_MANUAL_ZWJ); + /* https://github.com/harfbuzz/harfbuzz/issues/1573 */ + if (!map->has_feature (HB_TAG('r','c','l','t'))) + { + map->add_gsub_pause (nullptr); + map->enable_feature (HB_TAG('r','c','l','t'), F_MANUAL_ZWJ); + } + + map->enable_feature (HB_TAG('l','i','g','a'), F_MANUAL_ZWJ); + map->enable_feature (HB_TAG('c','l','i','g'), F_MANUAL_ZWJ); + + /* The spec includes 'cswh'. Earlier versions of Windows + * used to enable this by default, but testing suggests + * that Windows 8 and later do not enable it by default, + * and spec now says 'Off by default'. + * We disabled this in ae23c24c32. + * Note that IranNastaliq uses this feature extensively + * to fixup broken glyph sequences. Oh well... + * Test case: U+0643,U+0640,U+0631. */ + //map->enable_feature (HB_TAG('c','s','w','h'), F_MANUAL_ZWJ); + map->enable_feature (HB_TAG('m','s','e','t'), F_MANUAL_ZWJ); +} + +#include "hb-ot-shaper-arabic-fallback.hh" + +struct arabic_shape_plan_t +{ + /* The "+ 1" in the next array is to accommodate for the "NONE" command, + * which is not an OpenType feature, but this simplifies the code by not + * having to do a "if (... < NONE) ..." and just rely on the fact that + * mask_array[NONE] == 0. */ + hb_mask_t mask_array[ARABIC_NUM_FEATURES + 1]; + + hb_atomic_ptr_t fallback_plan; + + unsigned int do_fallback : 1; + unsigned int has_stch : 1; +}; + +void * +data_create_arabic (const hb_ot_shape_plan_t *plan) +{ + arabic_shape_plan_t *arabic_plan = (arabic_shape_plan_t *) hb_calloc (1, sizeof (arabic_shape_plan_t)); + if (unlikely (!arabic_plan)) + return nullptr; + + arabic_plan->do_fallback = plan->props.script == HB_SCRIPT_ARABIC; + arabic_plan->has_stch = !!plan->map.get_1_mask (HB_TAG ('s','t','c','h')); + for (unsigned int i = 0; i < ARABIC_NUM_FEATURES; i++) { + arabic_plan->mask_array[i] = plan->map.get_1_mask (arabic_features[i]); + arabic_plan->do_fallback = arabic_plan->do_fallback && + (FEATURE_IS_SYRIAC (arabic_features[i]) || + plan->map.needs_fallback (arabic_features[i])); + } + + return arabic_plan; +} + +void +data_destroy_arabic (void *data) +{ + arabic_shape_plan_t *arabic_plan = (arabic_shape_plan_t *) data; + + arabic_fallback_plan_destroy (arabic_plan->fallback_plan); + + hb_free (data); +} + +static void +arabic_joining (hb_buffer_t *buffer) +{ + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; + unsigned int prev = UINT_MAX, state = 0; + + /* Check pre-context */ + for (unsigned int i = 0; i < buffer->context_len[0]; i++) + { + unsigned int this_type = get_joining_type (buffer->context[0][i], buffer->unicode->general_category (buffer->context[0][i])); + + if (unlikely (this_type == JOINING_TYPE_T)) + continue; + + const arabic_state_table_entry *entry = &arabic_state_table[state][this_type]; + state = entry->next_state; + break; + } + + for (unsigned int i = 0; i < count; i++) + { + unsigned int this_type = get_joining_type (info[i].codepoint, _hb_glyph_info_get_general_category (&info[i])); + + if (unlikely (this_type == JOINING_TYPE_T)) { + info[i].arabic_shaping_action() = NONE; + continue; + } + + const arabic_state_table_entry *entry = &arabic_state_table[state][this_type]; + + if (entry->prev_action != NONE && prev != UINT_MAX) + { + info[prev].arabic_shaping_action() = entry->prev_action; + buffer->safe_to_insert_tatweel (prev, i + 1); + } + else + { + if (prev == UINT_MAX) + { + if (this_type >= JOINING_TYPE_R) + buffer->unsafe_to_concat_from_outbuffer (0, i + 1); + } + else + { + if (this_type >= JOINING_TYPE_R || + (2 <= state && state <= 5) /* States that have a possible prev_action. */) + buffer->unsafe_to_concat (prev, i + 1); + } + } + + info[i].arabic_shaping_action() = entry->curr_action; + + prev = i; + state = entry->next_state; + } + + for (unsigned int i = 0; i < buffer->context_len[1]; i++) + { + unsigned int this_type = get_joining_type (buffer->context[1][i], buffer->unicode->general_category (buffer->context[1][i])); + + if (unlikely (this_type == JOINING_TYPE_T)) + continue; + + const arabic_state_table_entry *entry = &arabic_state_table[state][this_type]; + if (entry->prev_action != NONE && prev != UINT_MAX) + { + info[prev].arabic_shaping_action() = entry->prev_action; + buffer->safe_to_insert_tatweel (prev, buffer->len); + } + else if (2 <= state && state <= 5) /* States that have a possible prev_action. */ + { + buffer->unsafe_to_concat (prev, buffer->len); + } + break; + } +} + +static void +mongolian_variation_selectors (hb_buffer_t *buffer) +{ + /* Copy arabic_shaping_action() from base to Mongolian variation selectors. */ + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; + for (unsigned int i = 1; i < count; i++) + if (unlikely (hb_in_ranges (info[i].codepoint, 0x180Bu, 0x180Du, 0x180Fu, 0x180Fu))) + info[i].arabic_shaping_action() = info[i - 1].arabic_shaping_action(); +} + +void +setup_masks_arabic_plan (const arabic_shape_plan_t *arabic_plan, + hb_buffer_t *buffer, + hb_script_t script) +{ + HB_BUFFER_ALLOCATE_VAR (buffer, arabic_shaping_action); + + arabic_joining (buffer); + if (script == HB_SCRIPT_MONGOLIAN) + mongolian_variation_selectors (buffer); + + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; + for (unsigned int i = 0; i < count; i++) + info[i].mask |= arabic_plan->mask_array[info[i].arabic_shaping_action()]; +} + +static void +setup_masks_arabic (const hb_ot_shape_plan_t *plan, + hb_buffer_t *buffer, + hb_font_t *font HB_UNUSED) +{ + const arabic_shape_plan_t *arabic_plan = (const arabic_shape_plan_t *) plan->data; + setup_masks_arabic_plan (arabic_plan, buffer, plan->props.script); +} + +static void +arabic_fallback_shape (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer) +{ +#ifdef HB_NO_OT_SHAPER_ARABIC_FALLBACK + return; +#endif + + const arabic_shape_plan_t *arabic_plan = (const arabic_shape_plan_t *) plan->data; + + if (!arabic_plan->do_fallback) + return; + +retry: + arabic_fallback_plan_t *fallback_plan = arabic_plan->fallback_plan; + if (unlikely (!fallback_plan)) + { + /* This sucks. We need a font to build the fallback plan... */ + fallback_plan = arabic_fallback_plan_create (plan, font); + if (unlikely (!arabic_plan->fallback_plan.cmpexch (nullptr, fallback_plan))) + { + arabic_fallback_plan_destroy (fallback_plan); + goto retry; + } + } + + arabic_fallback_plan_shape (fallback_plan, font, buffer); +} + +/* + * Stretch feature: "stch". + * See example here: + * https://docs.microsoft.com/en-us/typography/script-development/syriac + * We implement this in a generic way, such that the Arabic subtending + * marks can use it as well. + */ + +static void +record_stch (const hb_ot_shape_plan_t *plan, + hb_font_t *font HB_UNUSED, + hb_buffer_t *buffer) +{ + const arabic_shape_plan_t *arabic_plan = (const arabic_shape_plan_t *) plan->data; + if (!arabic_plan->has_stch) + return; + + /* 'stch' feature was just applied. Look for anything that multiplied, + * and record it for stch treatment later. Note that rtlm, frac, etc + * are applied before stch, but we assume that they didn't result in + * anything multiplying into 5 pieces, so it's safe-ish... */ + + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; + for (unsigned int i = 0; i < count; i++) + if (unlikely (_hb_glyph_info_multiplied (&info[i]))) + { + unsigned int comp = _hb_glyph_info_get_lig_comp (&info[i]); + info[i].arabic_shaping_action() = comp % 2 ? STCH_REPEATING : STCH_FIXED; + buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_ARABIC_HAS_STCH; + } +} + +static void +apply_stch (const hb_ot_shape_plan_t *plan HB_UNUSED, + hb_buffer_t *buffer, + hb_font_t *font) +{ + if (likely (!(buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_ARABIC_HAS_STCH))) + return; + + /* The Arabic shaper currently always processes in RTL mode, so we should + * stretch / position the stretched pieces to the left / preceding glyphs. */ + + /* We do a two pass implementation: + * First pass calculates the exact number of extra glyphs we need, + * We then enlarge buffer to have that much room, + * Second pass applies the stretch, copying things to the end of buffer. + */ + + int sign = font->x_scale < 0 ? -1 : +1; + unsigned int extra_glyphs_needed = 0; // Set during MEASURE, used during CUT + enum { MEASURE, CUT } /* step_t */; + + for (unsigned int step = MEASURE; step <= CUT; step = step + 1) + { + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; + hb_glyph_position_t *pos = buffer->pos; + unsigned int new_len = count + extra_glyphs_needed; // write head during CUT + unsigned int j = new_len; + for (unsigned int i = count; i; i--) + { + if (!hb_in_range (info[i - 1].arabic_shaping_action(), STCH_FIXED, STCH_REPEATING)) + { + if (step == CUT) + { + --j; + info[j] = info[i - 1]; + pos[j] = pos[i - 1]; + } + continue; + } + + /* Yay, justification! */ + + hb_position_t w_total = 0; // Total to be filled + hb_position_t w_fixed = 0; // Sum of fixed tiles + hb_position_t w_repeating = 0; // Sum of repeating tiles + int n_fixed = 0; + int n_repeating = 0; + + unsigned int end = i; + while (i && + hb_in_range (info[i - 1].arabic_shaping_action(), STCH_FIXED, STCH_REPEATING)) + { + i--; + hb_position_t width = font->get_glyph_h_advance (info[i].codepoint); + if (info[i].arabic_shaping_action() == STCH_FIXED) + { + w_fixed += width; + n_fixed++; + } + else + { + w_repeating += width; + n_repeating++; + } + } + unsigned int start = i; + unsigned int context = i; + while (context && + !hb_in_range (info[context - 1].arabic_shaping_action(), STCH_FIXED, STCH_REPEATING) && + (_hb_glyph_info_is_default_ignorable (&info[context - 1]) || + HB_ARABIC_GENERAL_CATEGORY_IS_WORD (_hb_glyph_info_get_general_category (&info[context - 1])))) + { + context--; + w_total += pos[context].x_advance; + } + i++; // Don't touch i again. + + DEBUG_MSG (ARABIC, nullptr, "%s stretch at (%d,%d,%d)", + step == MEASURE ? "measuring" : "cutting", context, start, end); + DEBUG_MSG (ARABIC, nullptr, "rest of word: count=%d width %d", start - context, w_total); + DEBUG_MSG (ARABIC, nullptr, "fixed tiles: count=%d width=%d", n_fixed, w_fixed); + DEBUG_MSG (ARABIC, nullptr, "repeating tiles: count=%d width=%d", n_repeating, w_repeating); + + /* Number of additional times to repeat each repeating tile. */ + int n_copies = 0; + + hb_position_t w_remaining = w_total - w_fixed; + if (sign * w_remaining > sign * w_repeating && sign * w_repeating > 0) + n_copies = (sign * w_remaining) / (sign * w_repeating) - 1; + + /* See if we can improve the fit by adding an extra repeat and squeezing them together a bit. */ + hb_position_t extra_repeat_overlap = 0; + hb_position_t shortfall = sign * w_remaining - sign * w_repeating * (n_copies + 1); + if (shortfall > 0 && n_repeating > 0) + { + ++n_copies; + hb_position_t excess = (n_copies + 1) * sign * w_repeating - sign * w_remaining; + if (excess > 0) + extra_repeat_overlap = excess / (n_copies * n_repeating); + } + + if (step == MEASURE) + { + extra_glyphs_needed += n_copies * n_repeating; + DEBUG_MSG (ARABIC, nullptr, "will add extra %d copies of repeating tiles", n_copies); + } + else + { + buffer->unsafe_to_break (context, end); + hb_position_t x_offset = 0; + for (unsigned int k = end; k > start; k--) + { + hb_position_t width = font->get_glyph_h_advance (info[k - 1].codepoint); + + unsigned int repeat = 1; + if (info[k - 1].arabic_shaping_action() == STCH_REPEATING) + repeat += n_copies; + + DEBUG_MSG (ARABIC, nullptr, "appending %d copies of glyph %d; j=%d", + repeat, info[k - 1].codepoint, j); + for (unsigned int n = 0; n < repeat; n++) + { + x_offset -= width; + if (n > 0) + x_offset += extra_repeat_overlap; + pos[k - 1].x_offset = x_offset; + /* Append copy. */ + --j; + info[j] = info[k - 1]; + pos[j] = pos[k - 1]; + } + } + } + } + + if (step == MEASURE) + { + if (unlikely (!buffer->ensure (count + extra_glyphs_needed))) + break; + } + else + { + assert (j == 0); + buffer->len = new_len; + } + } +} + + +static void +postprocess_glyphs_arabic (const hb_ot_shape_plan_t *plan, + hb_buffer_t *buffer, + hb_font_t *font) +{ + apply_stch (plan, buffer, font); +} + +/* https://www.unicode.org/reports/tr53/ */ + +static hb_codepoint_t +modifier_combining_marks[] = +{ + 0x0654u, /* ARABIC HAMZA ABOVE */ + 0x0655u, /* ARABIC HAMZA BELOW */ + 0x0658u, /* ARABIC MARK NOON GHUNNA */ + 0x06DCu, /* ARABIC SMALL HIGH SEEN */ + 0x06E3u, /* ARABIC SMALL LOW SEEN */ + 0x06E7u, /* ARABIC SMALL HIGH YEH */ + 0x06E8u, /* ARABIC SMALL HIGH NOON */ + 0x08CAu, /* ARABIC SMALL HIGH FARSI YEH */ + 0x08CBu, /* ARABIC SMALL HIGH YEH BARREE WITH TWO DOTS BELOW */ + 0x08CDu, /* ARABIC SMALL HIGH ZAH */ + 0x08CEu, /* ARABIC LARGE ROUND DOT ABOVE */ + 0x08CFu, /* ARABIC LARGE ROUND DOT BELOW */ + 0x08D3u, /* ARABIC SMALL LOW WAW */ + 0x08F3u, /* ARABIC SMALL HIGH WAW */ +}; + +static inline bool +info_is_mcm (const hb_glyph_info_t &info) +{ + hb_codepoint_t u = info.codepoint; + for (unsigned int i = 0; i < ARRAY_LENGTH (modifier_combining_marks); i++) + if (u == modifier_combining_marks[i]) + return true; + return false; +} + +static void +reorder_marks_arabic (const hb_ot_shape_plan_t *plan HB_UNUSED, + hb_buffer_t *buffer, + unsigned int start, + unsigned int end) +{ + hb_glyph_info_t *info = buffer->info; + + DEBUG_MSG (ARABIC, buffer, "Reordering marks from %d to %d", start, end); + + unsigned int i = start; + for (unsigned int cc = 220; cc <= 230; cc += 10) + { + DEBUG_MSG (ARABIC, buffer, "Looking for %d's starting at %d", cc, i); + while (i < end && info_cc(info[i]) < cc) + i++; + DEBUG_MSG (ARABIC, buffer, "Looking for %d's stopped at %d", cc, i); + + if (i == end) + break; + + if (info_cc(info[i]) > cc) + continue; + + unsigned int j = i; + while (j < end && info_cc(info[j]) == cc && info_is_mcm (info[j])) + j++; + + if (i == j) + continue; + + DEBUG_MSG (ARABIC, buffer, "Found %d's from %d to %d", cc, i, j); + + /* Shift it! */ + DEBUG_MSG (ARABIC, buffer, "Shifting %d's: %d %d", cc, i, j); + hb_glyph_info_t temp[HB_OT_SHAPE_MAX_COMBINING_MARKS]; + assert (j - i <= ARRAY_LENGTH (temp)); + buffer->merge_clusters (start, j); + memmove (temp, &info[i], (j - i) * sizeof (hb_glyph_info_t)); + memmove (&info[start + j - i], &info[start], (i - start) * sizeof (hb_glyph_info_t)); + memmove (&info[start], temp, (j - i) * sizeof (hb_glyph_info_t)); + + /* Renumber CC such that the reordered sequence is still sorted. + * 22 and 26 are chosen because they are smaller than all Arabic categories, + * and are folded back to 220/230 respectively during fallback mark positioning. + * + * We do this because the CGJ-handling logic in the normalizer relies on + * mark sequences having an increasing order even after this reordering. + * https://github.com/harfbuzz/harfbuzz/issues/554 + * This, however, does break some obscure sequences, where the normalizer + * might compose a sequence that it should not. For example, in the seequence + * ALEF, HAMZAH, MADDAH, we should NOT try to compose ALEF+MADDAH, but with this + * renumbering, we will. + */ + unsigned int new_start = start + j - i; + unsigned int new_cc = cc == 220 ? HB_MODIFIED_COMBINING_CLASS_CCC22 : HB_MODIFIED_COMBINING_CLASS_CCC26; + while (start < new_start) + { + _hb_glyph_info_set_modified_combining_class (&info[start], new_cc); + start++; + } + + i = j; + } +} + +const hb_ot_shaper_t _hb_ot_shaper_arabic = +{ + collect_features_arabic, + nullptr, /* override_features */ + data_create_arabic, + data_destroy_arabic, + nullptr, /* preprocess_text */ + postprocess_glyphs_arabic, + nullptr, /* decompose */ + nullptr, /* compose */ + setup_masks_arabic, + reorder_marks_arabic, + HB_TAG_NONE, /* gpos_tag */ + HB_OT_SHAPE_NORMALIZATION_MODE_DEFAULT, + HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE, + true, /* fallback_position */ +}; + + +#endif diff --git a/libs/harfbuzz/src/hb-ot-shaper-arabic.hh b/libs/harfbuzz/src/hb-ot-shaper-arabic.hh new file mode 100644 index 00000000..a025b1a3 --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-shaper-arabic.hh @@ -0,0 +1,50 @@ +/* + * Copyright © 2015 Mozilla Foundation. + * Copyright © 2015 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. + * + * Mozilla Author(s): Jonathan Kew + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_OT_SHAPER_ARABIC_HH +#define HB_OT_SHAPER_ARABIC_HH + +#include "hb.hh" + +#include "hb-ot-shaper.hh" + + +struct arabic_shape_plan_t; + +HB_INTERNAL void * +data_create_arabic (const hb_ot_shape_plan_t *plan); + +HB_INTERNAL void +data_destroy_arabic (void *data); + +HB_INTERNAL void +setup_masks_arabic_plan (const arabic_shape_plan_t *arabic_plan, + hb_buffer_t *buffer, + hb_script_t script); + +#endif /* HB_OT_SHAPER_ARABIC_HH */ diff --git a/libs/harfbuzz/src/hb-ot-shaper-default.cc b/libs/harfbuzz/src/hb-ot-shaper-default.cc new file mode 100644 index 00000000..2f6f499e --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-shaper-default.cc @@ -0,0 +1,73 @@ +/* + * Copyright © 2010,2012 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 + */ + +#include "hb.hh" + +#ifndef HB_NO_OT_SHAPE + +#include "hb-ot-shaper.hh" + + +const hb_ot_shaper_t _hb_ot_shaper_default = +{ + nullptr, /* collect_features */ + nullptr, /* override_features */ + nullptr, /* data_create */ + nullptr, /* data_destroy */ + nullptr, /* preprocess_text */ + nullptr, /* postprocess_glyphs */ + nullptr, /* decompose */ + nullptr, /* compose */ + nullptr, /* setup_masks */ + nullptr, /* reorder_marks */ + HB_TAG_NONE, /* gpos_tag */ + HB_OT_SHAPE_NORMALIZATION_MODE_DEFAULT, + HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE, + true, /* fallback_position */ +}; + +/* Same as default but no mark advance zeroing / fallback positioning. + * Dumbest shaper ever, basically. */ +const hb_ot_shaper_t _hb_ot_shaper_dumber = +{ + nullptr, /* collect_features */ + nullptr, /* override_features */ + nullptr, /* data_create */ + nullptr, /* data_destroy */ + nullptr, /* preprocess_text */ + nullptr, /* postprocess_glyphs */ + nullptr, /* decompose */ + nullptr, /* compose */ + nullptr, /* setup_masks */ + nullptr, /* reorder_marks */ + HB_TAG_NONE, /* gpos_tag */ + HB_OT_SHAPE_NORMALIZATION_MODE_DEFAULT, + HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE, + false, /* fallback_position */ +}; + + +#endif diff --git a/libs/harfbuzz/src/hb-ot-shaper-hangul.cc b/libs/harfbuzz/src/hb-ot-shaper-hangul.cc new file mode 100644 index 00000000..c90476bc --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-shaper-hangul.cc @@ -0,0 +1,436 @@ +/* + * Copyright © 2013 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 + */ + +#include "hb.hh" + +#ifndef HB_NO_OT_SHAPE + +#include "hb-ot-shaper.hh" + + +/* Hangul shaper */ + + +/* Same order as the feature array below */ +enum { + _JMO, + + LJMO, + VJMO, + TJMO, + + FIRST_HANGUL_FEATURE = LJMO, + HANGUL_FEATURE_COUNT = TJMO + 1 +}; + +static const hb_tag_t hangul_features[HANGUL_FEATURE_COUNT] = +{ + HB_TAG_NONE, + HB_TAG('l','j','m','o'), + HB_TAG('v','j','m','o'), + HB_TAG('t','j','m','o') +}; + +static void +collect_features_hangul (hb_ot_shape_planner_t *plan) +{ + hb_ot_map_builder_t *map = &plan->map; + + for (unsigned int i = FIRST_HANGUL_FEATURE; i < HANGUL_FEATURE_COUNT; i++) + map->add_feature (hangul_features[i]); +} + +static void +override_features_hangul (hb_ot_shape_planner_t *plan) +{ + /* Uniscribe does not apply 'calt' for Hangul, and certain fonts + * (Noto Sans CJK, Source Sans Han, etc) apply all of jamo lookups + * in calt, which is not desirable. */ + plan->map.disable_feature (HB_TAG('c','a','l','t')); +} + +struct hangul_shape_plan_t +{ + hb_mask_t mask_array[HANGUL_FEATURE_COUNT]; +}; + +static void * +data_create_hangul (const hb_ot_shape_plan_t *plan) +{ + hangul_shape_plan_t *hangul_plan = (hangul_shape_plan_t *) hb_calloc (1, sizeof (hangul_shape_plan_t)); + if (unlikely (!hangul_plan)) + return nullptr; + + for (unsigned int i = 0; i < HANGUL_FEATURE_COUNT; i++) + hangul_plan->mask_array[i] = plan->map.get_1_mask (hangul_features[i]); + + return hangul_plan; +} + +static void +data_destroy_hangul (void *data) +{ + hb_free (data); +} + +/* Constants for algorithmic hangul syllable [de]composition. */ +#define LBase 0x1100u +#define VBase 0x1161u +#define TBase 0x11A7u +#define LCount 19u +#define VCount 21u +#define TCount 28u +#define SBase 0xAC00u +#define NCount (VCount * TCount) +#define SCount (LCount * NCount) + +#define isCombiningL(u) (hb_in_range ((u), LBase, LBase+LCount-1)) +#define isCombiningV(u) (hb_in_range ((u), VBase, VBase+VCount-1)) +#define isCombiningT(u) (hb_in_range ((u), TBase+1, TBase+TCount-1)) +#define isCombinedS(u) (hb_in_range ((u), SBase, SBase+SCount-1)) + +#define isL(u) (hb_in_ranges ((u), 0x1100u, 0x115Fu, 0xA960u, 0xA97Cu)) +#define isV(u) (hb_in_ranges ((u), 0x1160u, 0x11A7u, 0xD7B0u, 0xD7C6u)) +#define isT(u) (hb_in_ranges ((u), 0x11A8u, 0x11FFu, 0xD7CBu, 0xD7FBu)) + +#define isHangulTone(u) (hb_in_range ((u), 0x302Eu, 0x302Fu)) + +/* buffer var allocations */ +#define hangul_shaping_feature() ot_shaper_var_u8_auxiliary() /* hangul jamo shaping feature */ + +static bool +is_zero_width_char (hb_font_t *font, + hb_codepoint_t unicode) +{ + hb_codepoint_t glyph; + return hb_font_get_glyph (font, unicode, 0, &glyph) && hb_font_get_glyph_h_advance (font, glyph) == 0; +} + +static void +preprocess_text_hangul (const hb_ot_shape_plan_t *plan HB_UNUSED, + hb_buffer_t *buffer, + hb_font_t *font) +{ + HB_BUFFER_ALLOCATE_VAR (buffer, hangul_shaping_feature); + + /* Hangul syllables come in two shapes: LV, and LVT. Of those: + * + * - LV can be precomposed, or decomposed. Lets call those + * and , + * - LVT can be fully precomposed, partially precomposed, or + * fully decomposed. Ie. , , or . + * + * The composition / decomposition is mechanical. However, not + * all sequences compose, and not all sequences + * compose. + * + * Here are the specifics: + * + * - : U+1100..115F, U+A960..A97F + * - : U+1160..11A7, U+D7B0..D7C7 + * - : U+11A8..11FF, U+D7CB..D7FB + * + * - Only the sequences for some of the U+11xx ranges combine. + * - Only sequences for some of the Ts in U+11xx range combine. + * + * Here is what we want to accomplish in this shaper: + * + * - If the whole syllable can be precomposed, do that, + * - Otherwise, fully decompose and apply ljmo/vjmo/tjmo features. + * - If a valid syllable is followed by a Hangul tone mark, reorder the tone + * mark to precede the whole syllable - unless it is a zero-width glyph, in + * which case we leave it untouched, assuming it's designed to overstrike. + * + * That is, of the different possible syllables: + * + * + * + * + * + * + * + * + * - needs no work. + * + * - and can stay the way they are if the font supports them, otherwise we + * should fully decompose them if font supports. + * + * - and we should compose if the whole thing can be composed. + * + * - we should compose if the whole thing can be composed, otherwise we should + * decompose. + */ + + buffer->clear_output (); + unsigned int start = 0, end = 0; /* Extent of most recently seen syllable; + * valid only if start < end + */ + unsigned int count = buffer->len; + + for (buffer->idx = 0; buffer->idx < count && buffer->successful;) + { + hb_codepoint_t u = buffer->cur().codepoint; + + if (isHangulTone (u)) + { + /* + * We could cache the width of the tone marks and the existence of dotted-circle, + * but the use of the Hangul tone mark characters seems to be rare enough that + * I didn't bother for now. + */ + if (start < end && end == buffer->out_len) + { + /* Tone mark follows a valid syllable; move it in front, unless it's zero width. */ + buffer->unsafe_to_break_from_outbuffer (start, buffer->idx); + if (unlikely (!buffer->next_glyph ())) break; + if (!is_zero_width_char (font, u)) + { + buffer->merge_out_clusters (start, end + 1); + hb_glyph_info_t *info = buffer->out_info; + hb_glyph_info_t tone = info[end]; + memmove (&info[start + 1], &info[start], (end - start) * sizeof (hb_glyph_info_t)); + info[start] = tone; + } + } + else + { + /* No valid syllable as base for tone mark; try to insert dotted circle. */ + if (!(buffer->flags & HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE) && + font->has_glyph (0x25CCu)) + { + hb_codepoint_t chars[2]; + if (!is_zero_width_char (font, u)) + { + chars[0] = u; + chars[1] = 0x25CCu; + } else + { + chars[0] = 0x25CCu; + chars[1] = u; + } + (void) buffer->replace_glyphs (1, 2, chars); + } + else + { + /* No dotted circle available in the font; just leave tone mark untouched. */ + (void) buffer->next_glyph (); + } + } + start = end = buffer->out_len; + continue; + } + + start = buffer->out_len; /* Remember current position as a potential syllable start; + * will only be used if we set end to a later position. + */ + + if (isL (u) && buffer->idx + 1 < count) + { + hb_codepoint_t l = u; + hb_codepoint_t v = buffer->cur(+1).codepoint; + if (isV (v)) + { + /* Have or . */ + hb_codepoint_t t = 0; + unsigned int tindex = 0; + if (buffer->idx + 2 < count) + { + t = buffer->cur(+2).codepoint; + if (isT (t)) + tindex = t - TBase; /* Only used if isCombiningT (t); otherwise invalid. */ + else + t = 0; /* The next character was not a trailing jamo. */ + } + buffer->unsafe_to_break (buffer->idx, buffer->idx + (t ? 3 : 2)); + + /* We've got a syllable ; see if it can potentially be composed. */ + if (isCombiningL (l) && isCombiningV (v) && (t == 0 || isCombiningT (t))) + { + /* Try to compose; if this succeeds, end is set to start+1. */ + hb_codepoint_t s = SBase + (l - LBase) * NCount + (v - VBase) * TCount + tindex; + if (font->has_glyph (s)) + { + (void) buffer->replace_glyphs (t ? 3 : 2, 1, &s); + end = start + 1; + continue; + } + } + + /* We didn't compose, either because it's an Old Hangul syllable without a + * precomposed character in Unicode, or because the font didn't support the + * necessary precomposed glyph. + * Set jamo features on the individual glyphs, and advance past them. + */ + buffer->cur().hangul_shaping_feature() = LJMO; + (void) buffer->next_glyph (); + buffer->cur().hangul_shaping_feature() = VJMO; + (void) buffer->next_glyph (); + if (t) + { + buffer->cur().hangul_shaping_feature() = TJMO; + (void) buffer->next_glyph (); + end = start + 3; + } + else + end = start + 2; + if (unlikely (!buffer->successful)) + break; + if (buffer->cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES) + buffer->merge_out_clusters (start, end); + continue; + } + } + + else if (isCombinedS (u)) + { + /* Have , , or */ + hb_codepoint_t s = u; + bool has_glyph = font->has_glyph (s); + unsigned int lindex = (s - SBase) / NCount; + unsigned int nindex = (s - SBase) % NCount; + unsigned int vindex = nindex / TCount; + unsigned int tindex = nindex % TCount; + + if (!tindex && + buffer->idx + 1 < count && + isCombiningT (buffer->cur(+1).codepoint)) + { + /* , try to combine. */ + unsigned int new_tindex = buffer->cur(+1).codepoint - TBase; + hb_codepoint_t new_s = s + new_tindex; + if (font->has_glyph (new_s)) + { + (void) buffer->replace_glyphs (2, 1, &new_s); + end = start + 1; + continue; + } + else + buffer->unsafe_to_break (buffer->idx, buffer->idx + 2); /* Mark unsafe between LV and T. */ + } + + /* Otherwise, decompose if font doesn't support or , + * or if having non-combining . Note that we already handled + * combining above. */ + if (!has_glyph || + (!tindex && + buffer->idx + 1 < count && + isT (buffer->cur(+1).codepoint))) + { + hb_codepoint_t decomposed[3] = {LBase + lindex, + VBase + vindex, + TBase + tindex}; + if (font->has_glyph (decomposed[0]) && + font->has_glyph (decomposed[1]) && + (!tindex || font->has_glyph (decomposed[2]))) + { + unsigned int s_len = tindex ? 3 : 2; + (void) buffer->replace_glyphs (1, s_len, decomposed); + + /* If we decomposed an LV because of a non-combining T following, + * we want to include this T in the syllable. + */ + if (has_glyph && !tindex) + { + (void) buffer->next_glyph (); + s_len++; + } + if (unlikely (!buffer->successful)) + break; + + /* We decomposed S: apply jamo features to the individual glyphs + * that are now in buffer->out_info. + */ + hb_glyph_info_t *info = buffer->out_info; + end = start + s_len; + + unsigned int i = start; + info[i++].hangul_shaping_feature() = LJMO; + info[i++].hangul_shaping_feature() = VJMO; + if (i < end) + info[i++].hangul_shaping_feature() = TJMO; + + if (buffer->cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES) + buffer->merge_out_clusters (start, end); + continue; + } + else if ((!tindex && buffer->idx + 1 < count && isT (buffer->cur(+1).codepoint))) + buffer->unsafe_to_break (buffer->idx, buffer->idx + 2); /* Mark unsafe between LV and T. */ + } + + if (has_glyph) + { + /* We didn't decompose the S, so just advance past it and fall through. */ + end = start + 1; + } + } + + /* Didn't find a recognizable syllable, so we leave end <= start; + * this will prevent tone-mark reordering happening. + */ + (void) buffer->next_glyph (); + } + buffer->sync (); +} + +static void +setup_masks_hangul (const hb_ot_shape_plan_t *plan, + hb_buffer_t *buffer, + hb_font_t *font HB_UNUSED) +{ + const hangul_shape_plan_t *hangul_plan = (const hangul_shape_plan_t *) plan->data; + + if (likely (hangul_plan)) + { + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; + for (unsigned int i = 0; i < count; i++, info++) + info->mask |= hangul_plan->mask_array[info->hangul_shaping_feature()]; + } + + HB_BUFFER_DEALLOCATE_VAR (buffer, hangul_shaping_feature); +} + + +const hb_ot_shaper_t _hb_ot_shaper_hangul = +{ + collect_features_hangul, + override_features_hangul, + data_create_hangul, + data_destroy_hangul, + preprocess_text_hangul, + nullptr, /* postprocess_glyphs */ + nullptr, /* decompose */ + nullptr, /* compose */ + setup_masks_hangul, + nullptr, /* reorder_marks */ + HB_TAG_NONE, /* gpos_tag */ + HB_OT_SHAPE_NORMALIZATION_MODE_NONE, + HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE, + false, /* fallback_position */ +}; + + +#endif diff --git a/libs/harfbuzz/src/hb-ot-shaper-hebrew.cc b/libs/harfbuzz/src/hb-ot-shaper-hebrew.cc new file mode 100644 index 00000000..e18edd6b --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-shaper-hebrew.cc @@ -0,0 +1,211 @@ +/* + * Copyright © 2010,2012 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 + */ + +#include "hb.hh" + +#ifndef HB_NO_OT_SHAPE + +#include "hb-ot-shaper.hh" + + +static bool +compose_hebrew (const hb_ot_shape_normalize_context_t *c, + hb_codepoint_t a, + hb_codepoint_t b, + hb_codepoint_t *ab) +{ + /* Hebrew presentation-form shaping. + * https://bugzilla.mozilla.org/show_bug.cgi?id=728866 + * Hebrew presentation forms with dagesh, for characters U+05D0..05EA; + * Note that some letters do not have a dagesh presForm encoded. + */ + static const hb_codepoint_t sDageshForms[0x05EAu - 0x05D0u + 1] = { + 0xFB30u, /* ALEF */ + 0xFB31u, /* BET */ + 0xFB32u, /* GIMEL */ + 0xFB33u, /* DALET */ + 0xFB34u, /* HE */ + 0xFB35u, /* VAV */ + 0xFB36u, /* ZAYIN */ + 0x0000u, /* HET */ + 0xFB38u, /* TET */ + 0xFB39u, /* YOD */ + 0xFB3Au, /* FINAL KAF */ + 0xFB3Bu, /* KAF */ + 0xFB3Cu, /* LAMED */ + 0x0000u, /* FINAL MEM */ + 0xFB3Eu, /* MEM */ + 0x0000u, /* FINAL NUN */ + 0xFB40u, /* NUN */ + 0xFB41u, /* SAMEKH */ + 0x0000u, /* AYIN */ + 0xFB43u, /* FINAL PE */ + 0xFB44u, /* PE */ + 0x0000u, /* FINAL TSADI */ + 0xFB46u, /* TSADI */ + 0xFB47u, /* QOF */ + 0xFB48u, /* RESH */ + 0xFB49u, /* SHIN */ + 0xFB4Au /* TAV */ + }; + + bool found = (bool) c->unicode->compose (a, b, ab); + +#ifdef HB_NO_OT_SHAPER_HEBREW_FALLBACK + return found; +#endif + + if (!found && !c->plan->has_gpos_mark) + { + /* Special-case Hebrew presentation forms that are excluded from + * standard normalization, but wanted for old fonts. */ + switch (b) { + case 0x05B4u: /* HIRIQ */ + if (a == 0x05D9u) { /* YOD */ + *ab = 0xFB1Du; + found = true; + } + break; + case 0x05B7u: /* PATAH */ + if (a == 0x05F2u) { /* YIDDISH YOD YOD */ + *ab = 0xFB1Fu; + found = true; + } else if (a == 0x05D0u) { /* ALEF */ + *ab = 0xFB2Eu; + found = true; + } + break; + case 0x05B8u: /* QAMATS */ + if (a == 0x05D0u) { /* ALEF */ + *ab = 0xFB2Fu; + found = true; + } + break; + case 0x05B9u: /* HOLAM */ + if (a == 0x05D5u) { /* VAV */ + *ab = 0xFB4Bu; + found = true; + } + break; + case 0x05BCu: /* DAGESH */ + if (a >= 0x05D0u && a <= 0x05EAu) { + *ab = sDageshForms[a - 0x05D0u]; + found = (*ab != 0); + } else if (a == 0xFB2Au) { /* SHIN WITH SHIN DOT */ + *ab = 0xFB2Cu; + found = true; + } else if (a == 0xFB2Bu) { /* SHIN WITH SIN DOT */ + *ab = 0xFB2Du; + found = true; + } + break; + case 0x05BFu: /* RAFE */ + switch (a) { + case 0x05D1u: /* BET */ + *ab = 0xFB4Cu; + found = true; + break; + case 0x05DBu: /* KAF */ + *ab = 0xFB4Du; + found = true; + break; + case 0x05E4u: /* PE */ + *ab = 0xFB4Eu; + found = true; + break; + } + break; + case 0x05C1u: /* SHIN DOT */ + if (a == 0x05E9u) { /* SHIN */ + *ab = 0xFB2Au; + found = true; + } else if (a == 0xFB49u) { /* SHIN WITH DAGESH */ + *ab = 0xFB2Cu; + found = true; + } + break; + case 0x05C2u: /* SIN DOT */ + if (a == 0x05E9u) { /* SHIN */ + *ab = 0xFB2Bu; + found = true; + } else if (a == 0xFB49u) { /* SHIN WITH DAGESH */ + *ab = 0xFB2Du; + found = true; + } + break; + } + } + + return found; +} + +static void +reorder_marks_hebrew (const hb_ot_shape_plan_t *plan HB_UNUSED, + hb_buffer_t *buffer, + unsigned int start, + unsigned int end) +{ + hb_glyph_info_t *info = buffer->info; + + for (unsigned i = start + 2; i < end; i++) + { + unsigned c0 = info_cc (info[i - 2]); + unsigned c1 = info_cc (info[i - 1]); + unsigned c2 = info_cc (info[i - 0]); + + if ((c0 == HB_MODIFIED_COMBINING_CLASS_CCC17 || c0 == HB_MODIFIED_COMBINING_CLASS_CCC18) /* patach or qamats */ && + (c1 == HB_MODIFIED_COMBINING_CLASS_CCC10 || c1 == HB_MODIFIED_COMBINING_CLASS_CCC14) /* sheva or hiriq */ && + (c2 == HB_MODIFIED_COMBINING_CLASS_CCC22 || c2 == HB_UNICODE_COMBINING_CLASS_BELOW) /* meteg or below */) + { + buffer->merge_clusters (i - 1, i + 1); + hb_swap (info[i - 1], info[i]); + break; + } + } + + +} + +const hb_ot_shaper_t _hb_ot_shaper_hebrew = +{ + nullptr, /* collect_features */ + nullptr, /* override_features */ + nullptr, /* data_create */ + nullptr, /* data_destroy */ + nullptr, /* preprocess_text */ + nullptr, /* postprocess_glyphs */ + nullptr, /* decompose */ + compose_hebrew, + nullptr, /* setup_masks */ + reorder_marks_hebrew, + HB_TAG ('h','e','b','r'), /* gpos_tag. https://github.com/harfbuzz/harfbuzz/issues/347#issuecomment-267838368 */ + HB_OT_SHAPE_NORMALIZATION_MODE_DEFAULT, + HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE, + true, /* fallback_position */ +}; + + +#endif diff --git a/libs/harfbuzz/src/hb-ot-shaper-indic-machine.hh b/libs/harfbuzz/src/hb-ot-shaper-indic-machine.hh new file mode 100644 index 00000000..d52b13f6 --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-shaper-indic-machine.hh @@ -0,0 +1,589 @@ + +#line 1 "hb-ot-shaper-indic-machine.rl" +/* + * Copyright © 2011,2012 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 HB_OT_SHAPER_INDIC_MACHINE_HH +#define HB_OT_SHAPER_INDIC_MACHINE_HH + +#include "hb.hh" + +#include "hb-ot-layout.hh" +#include "hb-ot-shaper-indic.hh" + +/* buffer var allocations */ +#define indic_category() ot_shaper_var_u8_category() /* indic_category_t */ +#define indic_position() ot_shaper_var_u8_auxiliary() /* indic_position_t */ + +using indic_category_t = unsigned; +using indic_position_t = ot_position_t; + +#define I_Cat(Cat) indic_syllable_machine_ex_##Cat + +enum indic_syllable_type_t { + indic_consonant_syllable, + indic_vowel_syllable, + indic_standalone_cluster, + indic_symbol_cluster, + indic_broken_cluster, + indic_non_indic_cluster, +}; + + +#line 57 "hb-ot-shaper-indic-machine.hh" +#define indic_syllable_machine_ex_A 9u +#define indic_syllable_machine_ex_C 1u +#define indic_syllable_machine_ex_CM 16u +#define indic_syllable_machine_ex_CS 18u +#define indic_syllable_machine_ex_DOTTEDCIRCLE 11u +#define indic_syllable_machine_ex_H 4u +#define indic_syllable_machine_ex_M 7u +#define indic_syllable_machine_ex_N 3u +#define indic_syllable_machine_ex_PLACEHOLDER 10u +#define indic_syllable_machine_ex_RS 12u +#define indic_syllable_machine_ex_Ra 15u +#define indic_syllable_machine_ex_Repha 14u +#define indic_syllable_machine_ex_SM 8u +#define indic_syllable_machine_ex_Symbol 17u +#define indic_syllable_machine_ex_V 2u +#define indic_syllable_machine_ex_VD 9u +#define indic_syllable_machine_ex_X 0u +#define indic_syllable_machine_ex_ZWJ 6u +#define indic_syllable_machine_ex_ZWNJ 5u + + +#line 79 "hb-ot-shaper-indic-machine.hh" +static const unsigned char _indic_syllable_machine_trans_keys[] = { + 8u, 8u, 4u, 8u, 5u, 7u, 5u, 8u, 4u, 8u, 4u, 12u, 4u, 8u, 8u, 8u, + 5u, 7u, 5u, 8u, 4u, 8u, 4u, 12u, 4u, 12u, 4u, 12u, 8u, 8u, 5u, 7u, + 5u, 8u, 4u, 8u, 4u, 8u, 4u, 12u, 8u, 8u, 5u, 7u, 5u, 8u, 4u, 8u, + 4u, 8u, 5u, 8u, 8u, 8u, 1u, 18u, 3u, 16u, 3u, 16u, 4u, 16u, 1u, 15u, + 5u, 9u, 5u, 9u, 9u, 9u, 5u, 9u, 1u, 15u, 1u, 15u, 1u, 15u, 3u, 9u, + 4u, 9u, 5u, 9u, 4u, 9u, 5u, 9u, 3u, 9u, 5u, 9u, 3u, 16u, 3u, 16u, + 3u, 16u, 3u, 16u, 4u, 16u, 1u, 15u, 3u, 16u, 3u, 16u, 4u, 16u, 1u, 15u, + 5u, 9u, 9u, 9u, 5u, 9u, 1u, 15u, 1u, 15u, 3u, 9u, 4u, 9u, 5u, 9u, + 4u, 9u, 5u, 9u, 5u, 9u, 3u, 9u, 5u, 9u, 3u, 16u, 3u, 16u, 4u, 8u, + 3u, 16u, 3u, 16u, 4u, 16u, 1u, 15u, 3u, 16u, 1u, 15u, 5u, 9u, 9u, 9u, + 5u, 9u, 1u, 15u, 1u, 15u, 3u, 9u, 4u, 9u, 5u, 9u, 3u, 16u, 4u, 9u, + 5u, 9u, 5u, 9u, 3u, 9u, 5u, 9u, 3u, 16u, 4u, 12u, 4u, 8u, 3u, 16u, + 3u, 16u, 4u, 16u, 1u, 15u, 3u, 16u, 1u, 15u, 5u, 9u, 9u, 9u, 5u, 9u, + 1u, 15u, 1u, 15u, 3u, 9u, 4u, 9u, 5u, 9u, 3u, 16u, 4u, 9u, 5u, 9u, + 5u, 9u, 3u, 9u, 5u, 9u, 1u, 16u, 3u, 16u, 1u, 16u, 4u, 12u, 5u, 9u, + 9u, 9u, 5u, 9u, 1u, 15u, 3u, 9u, 5u, 9u, 5u, 9u, 9u, 9u, 5u, 9u, + 1u, 15u, 0 +}; + +static const char _indic_syllable_machine_key_spans[] = { + 1, 5, 3, 4, 5, 9, 5, 1, + 3, 4, 5, 9, 9, 9, 1, 3, + 4, 5, 5, 9, 1, 3, 4, 5, + 5, 4, 1, 18, 14, 14, 13, 15, + 5, 5, 1, 5, 15, 15, 15, 7, + 6, 5, 6, 5, 7, 5, 14, 14, + 14, 14, 13, 15, 14, 14, 13, 15, + 5, 1, 5, 15, 15, 7, 6, 5, + 6, 5, 5, 7, 5, 14, 14, 5, + 14, 14, 13, 15, 14, 15, 5, 1, + 5, 15, 15, 7, 6, 5, 14, 6, + 5, 5, 7, 5, 14, 9, 5, 14, + 14, 13, 15, 14, 15, 5, 1, 5, + 15, 15, 7, 6, 5, 14, 6, 5, + 5, 7, 5, 16, 14, 16, 9, 5, + 1, 5, 15, 7, 5, 5, 1, 5, + 15 +}; + +static const short _indic_syllable_machine_index_offsets[] = { + 0, 2, 8, 12, 17, 23, 33, 39, + 41, 45, 50, 56, 66, 76, 86, 88, + 92, 97, 103, 109, 119, 121, 125, 130, + 136, 142, 147, 149, 168, 183, 198, 212, + 228, 234, 240, 242, 248, 264, 280, 296, + 304, 311, 317, 324, 330, 338, 344, 359, + 374, 389, 404, 418, 434, 449, 464, 478, + 494, 500, 502, 508, 524, 540, 548, 555, + 561, 568, 574, 580, 588, 594, 609, 624, + 630, 645, 660, 674, 690, 705, 721, 727, + 729, 735, 751, 767, 775, 782, 788, 803, + 810, 816, 822, 830, 836, 851, 861, 867, + 882, 897, 911, 927, 942, 958, 964, 966, + 972, 988, 1004, 1012, 1019, 1025, 1040, 1047, + 1053, 1059, 1067, 1073, 1090, 1105, 1122, 1132, + 1138, 1140, 1146, 1162, 1170, 1176, 1182, 1184, + 1190 +}; + +static const unsigned char _indic_syllable_machine_indicies[] = { + 1, 0, 2, 3, 3, 4, 1, 0, + 3, 3, 4, 0, 3, 3, 4, 1, + 0, 5, 3, 3, 4, 1, 0, 2, + 3, 3, 4, 1, 0, 0, 0, 6, + 0, 8, 9, 9, 10, 11, 7, 11, + 7, 9, 9, 10, 7, 9, 9, 10, + 11, 7, 12, 9, 9, 10, 11, 7, + 8, 9, 9, 10, 11, 7, 7, 7, + 13, 7, 8, 9, 9, 10, 11, 7, + 7, 7, 14, 7, 16, 17, 17, 18, + 19, 15, 15, 15, 20, 15, 19, 15, + 17, 17, 18, 21, 17, 17, 18, 19, + 15, 16, 17, 17, 18, 19, 15, 22, + 17, 17, 18, 19, 15, 24, 25, 25, + 26, 27, 23, 23, 23, 28, 23, 27, + 23, 25, 25, 26, 23, 25, 25, 26, + 27, 23, 24, 25, 25, 26, 27, 23, + 29, 25, 25, 26, 27, 23, 17, 17, + 18, 1, 0, 31, 30, 33, 34, 35, + 36, 37, 38, 18, 19, 39, 40, 40, + 20, 32, 41, 42, 43, 44, 45, 32, + 47, 48, 49, 50, 4, 1, 51, 46, + 46, 6, 46, 46, 46, 52, 46, 53, + 48, 54, 54, 4, 1, 51, 46, 46, + 46, 46, 46, 46, 52, 46, 48, 54, + 54, 4, 1, 51, 46, 46, 46, 46, + 46, 46, 52, 46, 33, 46, 46, 46, + 55, 56, 46, 1, 51, 46, 46, 46, + 46, 46, 33, 46, 57, 57, 46, 1, + 51, 46, 51, 46, 46, 58, 51, 46, + 51, 46, 51, 46, 46, 46, 51, 46, + 33, 46, 59, 46, 57, 57, 46, 1, + 51, 46, 46, 46, 46, 46, 33, 46, + 33, 46, 46, 46, 57, 57, 46, 1, + 51, 46, 46, 46, 46, 46, 33, 46, + 33, 46, 46, 46, 57, 56, 46, 1, + 51, 46, 46, 46, 46, 46, 33, 46, + 60, 61, 62, 62, 4, 1, 51, 46, + 61, 62, 62, 4, 1, 51, 46, 62, + 62, 4, 1, 51, 46, 63, 64, 64, + 4, 1, 51, 46, 55, 65, 46, 1, + 51, 46, 55, 46, 57, 57, 46, 1, + 51, 46, 57, 65, 46, 1, 51, 46, + 47, 48, 54, 54, 4, 1, 51, 46, + 46, 46, 46, 46, 46, 52, 46, 47, + 48, 49, 54, 4, 1, 51, 46, 46, + 6, 46, 46, 46, 52, 46, 67, 68, + 69, 70, 10, 11, 71, 66, 66, 14, + 66, 66, 66, 72, 66, 73, 68, 74, + 70, 10, 11, 71, 66, 66, 66, 66, + 66, 66, 72, 66, 68, 74, 70, 10, + 11, 71, 66, 66, 66, 66, 66, 66, + 72, 66, 75, 66, 66, 66, 76, 77, + 66, 11, 71, 66, 66, 66, 66, 66, + 75, 66, 78, 68, 79, 80, 10, 11, + 71, 66, 66, 13, 66, 66, 66, 72, + 66, 81, 68, 74, 74, 10, 11, 71, + 66, 66, 66, 66, 66, 66, 72, 66, + 68, 74, 74, 10, 11, 71, 66, 66, + 66, 66, 66, 66, 72, 66, 75, 66, + 66, 66, 82, 77, 66, 11, 71, 66, + 66, 66, 66, 66, 75, 66, 71, 66, + 66, 83, 71, 66, 71, 66, 71, 66, + 66, 66, 71, 66, 75, 66, 84, 66, + 82, 82, 66, 11, 71, 66, 66, 66, + 66, 66, 75, 66, 75, 66, 66, 66, + 82, 82, 66, 11, 71, 66, 66, 66, + 66, 66, 75, 66, 85, 86, 87, 87, + 10, 11, 71, 66, 86, 87, 87, 10, + 11, 71, 66, 87, 87, 10, 11, 71, + 66, 88, 89, 89, 10, 11, 71, 66, + 76, 90, 66, 11, 71, 66, 82, 82, + 66, 11, 71, 66, 76, 66, 82, 82, + 66, 11, 71, 66, 82, 90, 66, 11, + 71, 66, 78, 68, 74, 74, 10, 11, + 71, 66, 66, 66, 66, 66, 66, 72, + 66, 78, 68, 79, 74, 10, 11, 71, + 66, 66, 13, 66, 66, 66, 72, 66, + 8, 9, 9, 10, 11, 66, 67, 68, + 74, 70, 10, 11, 71, 66, 66, 66, + 66, 66, 66, 72, 66, 92, 36, 93, + 93, 18, 19, 39, 91, 91, 91, 91, + 91, 91, 43, 91, 36, 93, 93, 18, + 19, 39, 91, 91, 91, 91, 91, 91, + 43, 91, 94, 91, 91, 91, 95, 96, + 91, 19, 39, 91, 91, 91, 91, 91, + 94, 91, 35, 36, 97, 98, 18, 19, + 39, 91, 91, 20, 91, 91, 91, 43, + 91, 94, 91, 91, 91, 99, 96, 91, + 19, 39, 91, 91, 91, 91, 91, 94, + 91, 39, 91, 91, 100, 39, 91, 39, + 91, 39, 91, 91, 91, 39, 91, 94, + 91, 101, 91, 99, 99, 91, 19, 39, + 91, 91, 91, 91, 91, 94, 91, 94, + 91, 91, 91, 99, 99, 91, 19, 39, + 91, 91, 91, 91, 91, 94, 91, 102, + 103, 104, 104, 18, 19, 39, 91, 103, + 104, 104, 18, 19, 39, 91, 104, 104, + 18, 19, 39, 91, 35, 36, 93, 93, + 18, 19, 39, 91, 91, 91, 91, 91, + 91, 43, 91, 105, 106, 106, 18, 19, + 39, 91, 95, 107, 91, 19, 39, 91, + 99, 99, 91, 19, 39, 91, 95, 91, + 99, 99, 91, 19, 39, 91, 99, 107, + 91, 19, 39, 91, 35, 36, 97, 93, + 18, 19, 39, 91, 91, 20, 91, 91, + 91, 43, 91, 16, 17, 17, 18, 19, + 108, 108, 108, 20, 108, 16, 17, 17, + 18, 19, 108, 110, 111, 112, 113, 26, + 27, 114, 109, 109, 28, 109, 109, 109, + 115, 109, 116, 111, 113, 113, 26, 27, + 114, 109, 109, 109, 109, 109, 109, 115, + 109, 111, 113, 113, 26, 27, 114, 109, + 109, 109, 109, 109, 109, 115, 109, 117, + 109, 109, 109, 118, 119, 109, 27, 114, + 109, 109, 109, 109, 109, 117, 109, 110, + 111, 112, 40, 26, 27, 114, 109, 109, + 28, 109, 109, 109, 115, 109, 117, 109, + 109, 109, 120, 119, 109, 27, 114, 109, + 109, 109, 109, 109, 117, 109, 114, 109, + 109, 121, 114, 109, 114, 109, 114, 109, + 109, 109, 114, 109, 117, 109, 122, 109, + 120, 120, 109, 27, 114, 109, 109, 109, + 109, 109, 117, 109, 117, 109, 109, 109, + 120, 120, 109, 27, 114, 109, 109, 109, + 109, 109, 117, 109, 123, 124, 125, 125, + 26, 27, 114, 109, 124, 125, 125, 26, + 27, 114, 109, 125, 125, 26, 27, 114, + 109, 110, 111, 113, 113, 26, 27, 114, + 109, 109, 109, 109, 109, 109, 115, 109, + 126, 127, 127, 26, 27, 114, 109, 118, + 128, 109, 27, 114, 109, 120, 120, 109, + 27, 114, 109, 118, 109, 120, 120, 109, + 27, 114, 109, 120, 128, 109, 27, 114, + 109, 33, 34, 35, 36, 97, 93, 18, + 19, 39, 40, 40, 20, 91, 91, 33, + 43, 91, 47, 129, 49, 50, 4, 1, + 51, 46, 46, 6, 46, 46, 46, 52, + 46, 33, 34, 35, 36, 130, 131, 18, + 132, 133, 46, 40, 20, 46, 46, 33, + 43, 46, 16, 134, 134, 18, 132, 51, + 46, 46, 20, 46, 133, 46, 46, 135, + 133, 46, 133, 46, 133, 46, 46, 46, + 133, 46, 33, 46, 59, 16, 134, 134, + 18, 132, 51, 46, 46, 46, 46, 46, + 33, 46, 137, 136, 138, 138, 136, 31, + 139, 136, 138, 138, 136, 31, 139, 136, + 139, 136, 136, 140, 139, 136, 139, 136, + 139, 136, 136, 136, 139, 136, 33, 108, + 108, 108, 108, 108, 108, 108, 108, 40, + 108, 108, 108, 108, 33, 108, 0 +}; + +static const unsigned char _indic_syllable_machine_trans_targs[] = { + 27, 33, 38, 2, 39, 45, 46, 27, + 55, 8, 61, 56, 68, 69, 72, 27, + 77, 15, 83, 78, 86, 27, 91, 27, + 100, 21, 106, 101, 109, 114, 27, 125, + 27, 28, 48, 73, 75, 93, 94, 79, + 95, 115, 116, 87, 123, 128, 27, 29, + 31, 5, 47, 34, 42, 30, 1, 32, + 36, 0, 35, 37, 40, 41, 3, 43, + 4, 44, 27, 49, 51, 12, 71, 57, + 64, 50, 6, 52, 66, 59, 53, 11, + 70, 54, 7, 58, 60, 62, 63, 9, + 65, 10, 67, 27, 74, 17, 76, 89, + 81, 13, 92, 14, 80, 82, 84, 85, + 16, 88, 18, 90, 27, 27, 96, 98, + 19, 23, 102, 110, 97, 99, 112, 104, + 20, 103, 105, 107, 108, 22, 111, 24, + 113, 117, 118, 122, 119, 120, 25, 121, + 27, 124, 26, 126, 127 +}; + +static const char _indic_syllable_machine_trans_actions[] = { + 1, 0, 2, 0, 2, 2, 2, 3, + 2, 0, 2, 0, 2, 2, 2, 4, + 2, 0, 5, 0, 5, 6, 2, 7, + 2, 0, 2, 0, 2, 2, 8, 0, + 11, 2, 2, 5, 0, 12, 12, 0, + 2, 5, 2, 5, 2, 0, 13, 2, + 0, 0, 2, 0, 2, 2, 0, 2, + 2, 0, 0, 2, 2, 2, 0, 0, + 0, 2, 14, 2, 0, 0, 2, 0, + 2, 2, 0, 2, 2, 2, 2, 0, + 2, 2, 0, 0, 2, 2, 2, 0, + 0, 0, 2, 15, 5, 0, 5, 2, + 2, 0, 5, 0, 0, 2, 5, 5, + 0, 0, 0, 2, 16, 17, 2, 0, + 0, 0, 0, 2, 2, 2, 2, 2, + 0, 0, 2, 2, 2, 0, 0, 0, + 2, 0, 18, 18, 0, 0, 0, 0, + 19, 2, 0, 0, 0 +}; + +static const char _indic_syllable_machine_to_state_actions[] = { + 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, 9, 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, 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, 0, 0, 0, 0, + 0 +}; + +static const char _indic_syllable_machine_from_state_actions[] = { + 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, 10, 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, 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, 0, 0, 0, 0, + 0 +}; + +static const short _indic_syllable_machine_eof_trans[] = { + 1, 1, 1, 1, 1, 1, 8, 8, + 8, 8, 8, 8, 8, 16, 16, 22, + 16, 16, 16, 24, 24, 24, 24, 24, + 24, 1, 31, 0, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, + 67, 67, 67, 67, 67, 67, 67, 67, + 67, 67, 67, 67, 67, 67, 67, 67, + 67, 67, 67, 67, 67, 67, 67, 67, + 67, 92, 92, 92, 92, 92, 92, 92, + 92, 92, 92, 92, 92, 92, 92, 92, + 92, 92, 92, 92, 92, 109, 109, 110, + 110, 110, 110, 110, 110, 110, 110, 110, + 110, 110, 110, 110, 110, 110, 110, 110, + 110, 110, 110, 92, 47, 47, 47, 47, + 47, 47, 47, 137, 137, 137, 137, 137, + 109 +}; + +static const int indic_syllable_machine_start = 27; +static const int indic_syllable_machine_first_final = 27; +static const int indic_syllable_machine_error = -1; + +static const int indic_syllable_machine_en_main = 27; + + +#line 58 "hb-ot-shaper-indic-machine.rl" + + + +#line 117 "hb-ot-shaper-indic-machine.rl" + + +#define found_syllable(syllable_type) \ + HB_STMT_START { \ + if (0) fprintf (stderr, "syllable %d..%d %s\n", ts, te, #syllable_type); \ + for (unsigned int i = ts; i < te; i++) \ + info[i].syllable() = (syllable_serial << 4) | syllable_type; \ + syllable_serial++; \ + if (unlikely (syllable_serial == 16)) syllable_serial = 1; \ + } HB_STMT_END + +inline void +find_syllables_indic (hb_buffer_t *buffer) +{ + unsigned int p, pe, eof, ts, te, act; + int cs; + hb_glyph_info_t *info = buffer->info; + +#line 426 "hb-ot-shaper-indic-machine.hh" + { + cs = indic_syllable_machine_start; + ts = 0; + te = 0; + act = 0; + } + +#line 137 "hb-ot-shaper-indic-machine.rl" + + + p = 0; + pe = eof = buffer->len; + + unsigned int syllable_serial = 1; + +#line 442 "hb-ot-shaper-indic-machine.hh" + { + int _slen; + int _trans; + const unsigned char *_keys; + const unsigned char *_inds; + if ( p == pe ) + goto _test_eof; +_resume: + switch ( _indic_syllable_machine_from_state_actions[cs] ) { + case 10: +#line 1 "NONE" + {ts = p;} + break; +#line 456 "hb-ot-shaper-indic-machine.hh" + } + + _keys = _indic_syllable_machine_trans_keys + (cs<<1); + _inds = _indic_syllable_machine_indicies + _indic_syllable_machine_index_offsets[cs]; + + _slen = _indic_syllable_machine_key_spans[cs]; + _trans = _inds[ _slen > 0 && _keys[0] <=( info[p].indic_category()) && + ( info[p].indic_category()) <= _keys[1] ? + ( info[p].indic_category()) - _keys[0] : _slen ]; + +_eof_trans: + cs = _indic_syllable_machine_trans_targs[_trans]; + + if ( _indic_syllable_machine_trans_actions[_trans] == 0 ) + goto _again; + + switch ( _indic_syllable_machine_trans_actions[_trans] ) { + case 2: +#line 1 "NONE" + {te = p+1;} + break; + case 11: +#line 113 "hb-ot-shaper-indic-machine.rl" + {te = p+1;{ found_syllable (indic_non_indic_cluster); }} + break; + case 13: +#line 108 "hb-ot-shaper-indic-machine.rl" + {te = p;p--;{ found_syllable (indic_consonant_syllable); }} + break; + case 14: +#line 109 "hb-ot-shaper-indic-machine.rl" + {te = p;p--;{ found_syllable (indic_vowel_syllable); }} + break; + case 17: +#line 110 "hb-ot-shaper-indic-machine.rl" + {te = p;p--;{ found_syllable (indic_standalone_cluster); }} + break; + case 19: +#line 111 "hb-ot-shaper-indic-machine.rl" + {te = p;p--;{ found_syllable (indic_symbol_cluster); }} + break; + case 15: +#line 112 "hb-ot-shaper-indic-machine.rl" + {te = p;p--;{ found_syllable (indic_broken_cluster); buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_BROKEN_SYLLABLE; }} + break; + case 16: +#line 113 "hb-ot-shaper-indic-machine.rl" + {te = p;p--;{ found_syllable (indic_non_indic_cluster); }} + break; + case 1: +#line 108 "hb-ot-shaper-indic-machine.rl" + {{p = ((te))-1;}{ found_syllable (indic_consonant_syllable); }} + break; + case 3: +#line 109 "hb-ot-shaper-indic-machine.rl" + {{p = ((te))-1;}{ found_syllable (indic_vowel_syllable); }} + break; + case 7: +#line 110 "hb-ot-shaper-indic-machine.rl" + {{p = ((te))-1;}{ found_syllable (indic_standalone_cluster); }} + break; + case 8: +#line 111 "hb-ot-shaper-indic-machine.rl" + {{p = ((te))-1;}{ found_syllable (indic_symbol_cluster); }} + break; + case 4: +#line 112 "hb-ot-shaper-indic-machine.rl" + {{p = ((te))-1;}{ found_syllable (indic_broken_cluster); buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_BROKEN_SYLLABLE; }} + break; + case 6: +#line 1 "NONE" + { switch( act ) { + case 1: + {{p = ((te))-1;} found_syllable (indic_consonant_syllable); } + break; + case 5: + {{p = ((te))-1;} found_syllable (indic_broken_cluster); buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_BROKEN_SYLLABLE; } + break; + case 6: + {{p = ((te))-1;} found_syllable (indic_non_indic_cluster); } + break; + } + } + break; + case 18: +#line 1 "NONE" + {te = p+1;} +#line 108 "hb-ot-shaper-indic-machine.rl" + {act = 1;} + break; + case 5: +#line 1 "NONE" + {te = p+1;} +#line 112 "hb-ot-shaper-indic-machine.rl" + {act = 5;} + break; + case 12: +#line 1 "NONE" + {te = p+1;} +#line 113 "hb-ot-shaper-indic-machine.rl" + {act = 6;} + break; +#line 559 "hb-ot-shaper-indic-machine.hh" + } + +_again: + switch ( _indic_syllable_machine_to_state_actions[cs] ) { + case 9: +#line 1 "NONE" + {ts = 0;} + break; +#line 568 "hb-ot-shaper-indic-machine.hh" + } + + if ( ++p != pe ) + goto _resume; + _test_eof: {} + if ( p == eof ) + { + if ( _indic_syllable_machine_eof_trans[cs] > 0 ) { + _trans = _indic_syllable_machine_eof_trans[cs] - 1; + goto _eof_trans; + } + } + + } + +#line 145 "hb-ot-shaper-indic-machine.rl" + +} + +#undef found_syllable + +#endif /* HB_OT_SHAPER_INDIC_MACHINE_HH */ diff --git a/libs/harfbuzz/src/hb-ot-shaper-indic-machine.rl b/libs/harfbuzz/src/hb-ot-shaper-indic-machine.rl new file mode 100644 index 00000000..3274a776 --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-shaper-indic-machine.rl @@ -0,0 +1,150 @@ +/* + * Copyright © 2011,2012 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 HB_OT_SHAPER_INDIC_MACHINE_HH +#define HB_OT_SHAPER_INDIC_MACHINE_HH + +#include "hb.hh" + +#include "hb-ot-layout.hh" +#include "hb-ot-shaper-indic.hh" + +/* buffer var allocations */ +#define indic_category() ot_shaper_var_u8_category() /* indic_category_t */ +#define indic_position() ot_shaper_var_u8_auxiliary() /* indic_position_t */ + +using indic_category_t = unsigned; +using indic_position_t = ot_position_t; + +#define I_Cat(Cat) indic_syllable_machine_ex_##Cat + +enum indic_syllable_type_t { + indic_consonant_syllable, + indic_vowel_syllable, + indic_standalone_cluster, + indic_symbol_cluster, + indic_broken_cluster, + indic_non_indic_cluster, +}; + +%%{ + machine indic_syllable_machine; + alphtype unsigned char; + write exports; + write data; +}%% + +%%{ + + +export X = 0; +export C = 1; +export V = 2; +export N = 3; +export H = 4; +export ZWNJ = 5; +export ZWJ = 6; +export M = 7; +export SM = 8; +export A = 9; +export VD = 9; +export PLACEHOLDER = 10; +export DOTTEDCIRCLE = 11; +export RS = 12; +export Repha = 14; +export Ra = 15; +export CM = 16; +export Symbol= 17; +export CS = 18; + + +c = (C | Ra); # is_consonant +n = ((ZWNJ?.RS)? (N.N?)?); # is_consonant_modifier +z = ZWJ|ZWNJ; # is_joiner +reph = (Ra H | Repha); # possible reph + +cn = c.ZWJ?.n?; +symbol = Symbol.N?; +matra_group = z*.M.N?.H?; +syllable_tail = (z?.SM.SM?.ZWNJ?)? (A | VD)*; +halant_group = (z?.H.(ZWJ.N?)?); +final_halant_group = halant_group | H.ZWNJ; +medial_group = CM?; +halant_or_matra_group = (final_halant_group | matra_group*); + +complex_syllable_tail = (halant_group.cn)* medial_group halant_or_matra_group syllable_tail; + +consonant_syllable = (Repha|CS)? cn complex_syllable_tail; +vowel_syllable = reph? V.n? (ZWJ | complex_syllable_tail); +standalone_cluster = ((Repha|CS)? PLACEHOLDER | reph? DOTTEDCIRCLE).n? complex_syllable_tail; +symbol_cluster = symbol syllable_tail; +broken_cluster = reph? n? complex_syllable_tail; +other = any; + +main := |* + consonant_syllable => { found_syllable (indic_consonant_syllable); }; + vowel_syllable => { found_syllable (indic_vowel_syllable); }; + standalone_cluster => { found_syllable (indic_standalone_cluster); }; + symbol_cluster => { found_syllable (indic_symbol_cluster); }; + broken_cluster => { found_syllable (indic_broken_cluster); buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_BROKEN_SYLLABLE; }; + other => { found_syllable (indic_non_indic_cluster); }; +*|; + + +}%% + +#define found_syllable(syllable_type) \ + HB_STMT_START { \ + if (0) fprintf (stderr, "syllable %d..%d %s\n", ts, te, #syllable_type); \ + for (unsigned int i = ts; i < te; i++) \ + info[i].syllable() = (syllable_serial << 4) | syllable_type; \ + syllable_serial++; \ + if (unlikely (syllable_serial == 16)) syllable_serial = 1; \ + } HB_STMT_END + +inline void +find_syllables_indic (hb_buffer_t *buffer) +{ + unsigned int p, pe, eof, ts, te, act; + int cs; + hb_glyph_info_t *info = buffer->info; + %%{ + write init; + getkey info[p].indic_category(); + }%% + + p = 0; + pe = eof = buffer->len; + + unsigned int syllable_serial = 1; + %%{ + write exec; + }%% +} + +#undef found_syllable + +#endif /* HB_OT_SHAPER_INDIC_MACHINE_HH */ diff --git a/libs/harfbuzz/src/hb-ot-shaper-indic-table.cc b/libs/harfbuzz/src/hb-ot-shaper-indic-table.cc new file mode 100644 index 00000000..bf6a2757 --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-shaper-indic-table.cc @@ -0,0 +1,560 @@ +/* == Start of generated table == */ +/* + * The following table is generated by running: + * + * ./gen-indic-table.py IndicSyllabicCategory.txt IndicPositionalCategory.txt Blocks.txt + * + * on files with these headers: + * + * # IndicSyllabicCategory-15.0.0.txt + * # Date: 2022-05-26, 02:18:00 GMT [KW, RP] + * # IndicPositionalCategory-15.0.0.txt + * # Date: 2022-05-26, 02:18:00 GMT [KW, RP] + * # Blocks-15.0.0.txt + * # Date: 2022-01-28, 20:58:00 GMT [KW] + */ + +#include "hb.hh" + +#ifndef HB_NO_OT_SHAPE + +#include "hb-ot-shaper-indic.hh" + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-macros" + +#include "hb-ot-shaper-indic-machine.hh" +#include "hb-ot-shaper-khmer-machine.hh" +#include "hb-ot-shaper-myanmar-machine.hh" + +/* indic */ +#define OT_X I_Cat(X) +#define OT_C I_Cat(C) +#define OT_V I_Cat(V) +#define OT_N I_Cat(N) +#define OT_H I_Cat(H) +#define OT_ZWNJ I_Cat(ZWNJ) +#define OT_ZWJ I_Cat(ZWJ) +#define OT_M I_Cat(M) +#define OT_SM I_Cat(SM) +#define OT_A I_Cat(A) +#define OT_VD I_Cat(VD) +#define OT_PLACEHOLDER I_Cat(PLACEHOLDER) +#define OT_DOTTEDCIRCLE I_Cat(DOTTEDCIRCLE) +#define OT_RS I_Cat(RS) +#define OT_Repha I_Cat(Repha) +#define OT_Ra I_Cat(Ra) +#define OT_CM I_Cat(CM) +#define OT_Symbol I_Cat(Symbol) +#define OT_CS I_Cat(CS) +/* khmer */ +#define OT_VAbv K_Cat(VAbv) +#define OT_VBlw K_Cat(VBlw) +#define OT_VPre K_Cat(VPre) +#define OT_VPst K_Cat(VPst) +#define OT_Robatic K_Cat(Robatic) +#define OT_Xgroup K_Cat(Xgroup) +#define OT_Ygroup K_Cat(Ygroup) +/* myanmar */ +static_assert (OT_VAbv == M_Cat(VAbv), ""); +static_assert (OT_VBlw == M_Cat(VBlw), ""); +static_assert (OT_VPre == M_Cat(VPre), ""); +static_assert (OT_VPst == M_Cat(VPst), ""); +#define OT_IV M_Cat(IV) +#define OT_As M_Cat(As) +#define OT_DB M_Cat(DB) +#define OT_GB M_Cat(GB) +#define OT_MH M_Cat(MH) +#define OT_MR M_Cat(MR) +#define OT_MW M_Cat(MW) +#define OT_MY M_Cat(MY) +#define OT_PT M_Cat(PT) +#define OT_VS M_Cat(VS) +#define OT_ML M_Cat(ML) + + +#define _OT_A OT_A /* 53 chars; A */ +#define _OT_As OT_As /* 1 chars; As */ +#define _OT_C OT_C /* 478 chars; C */ +#define _OT_CM OT_CM /* 1 chars; CM */ +#define _OT_CS OT_CS /* 2 chars; CS */ +#define _OT_DC OT_DOTTEDCIRCLE /* 1 chars; DOTTEDCIRCLE */ +#define _OT_H OT_H /* 11 chars; H */ +#define _OT_M OT_M /* 143 chars; M */ +#define _OT_MH OT_MH /* 1 chars; MH */ +#define _OT_ML OT_ML /* 1 chars; ML */ +#define _OT_MR OT_MR /* 1 chars; MR */ +#define _OT_MW OT_MW /* 2 chars; MW */ +#define _OT_MY OT_MY /* 3 chars; MY */ +#define _OT_N OT_N /* 17 chars; N */ +#define _OT_GB OT_PLACEHOLDER /* 165 chars; PLACEHOLDER */ +#define _OT_PT OT_PT /* 8 chars; PT */ +#define _OT_R OT_Ra /* 14 chars; Ra */ +#define _OT_Rf OT_Repha /* 1 chars; Repha */ +#define _OT_Rt OT_Robatic /* 3 chars; Robatic */ +#define _OT_SM OT_SM /* 56 chars; SM */ +#define _OT_S OT_Symbol /* 22 chars; Symbol */ +#define _OT_V OT_V /* 172 chars; V */ +#define _OT_VA OT_VAbv /* 18 chars; VAbv */ +#define _OT_VB OT_VBlw /* 7 chars; VBlw */ +#define _OT_VL OT_VPre /* 5 chars; VPre */ +#define _OT_VR OT_VPst /* 13 chars; VPst */ +#define _OT_VS OT_VS /* 16 chars; VS */ +#define _OT_X OT_X /* 2 chars; X */ +#define _OT_Xg OT_Xgroup /* 7 chars; Xgroup */ +#define _OT_Yg OT_Ygroup /* 4 chars; Ygroup */ +#define _OT_ZWJ OT_ZWJ /* 1 chars; ZWJ */ +#define _OT_ZWNJ OT_ZWNJ /* 1 chars; ZWNJ */ + +#define _POS_T POS_ABOVE_C /* 22 chars; ABOVE_C */ +#define _POS_A POS_AFTER_MAIN /* 3 chars; AFTER_MAIN */ +#define _POS_AP POS_AFTER_POST /* 50 chars; AFTER_POST */ +#define _POS_AS POS_AFTER_SUB /* 51 chars; AFTER_SUB */ +#define _POS_C POS_BASE_C /* 833 chars; BASE_C */ +#define _POS_BS POS_BEFORE_SUB /* 25 chars; BEFORE_SUB */ +#define _POS_B POS_BELOW_C /* 13 chars; BELOW_C */ +#define _POS_X POS_END /* 71 chars; END */ +#define _POS_R POS_POST_C /* 13 chars; POST_C */ +#define _POS_L POS_PRE_C /* 5 chars; PRE_C */ +#define _POS_LM POS_PRE_M /* 14 chars; PRE_M */ +#define _POS_SM POS_SMVD /* 130 chars; SMVD */ + +#pragma GCC diagnostic pop + +#define INDIC_COMBINE_CATEGORIES(S,M) ((S) | ((M) << 8)) + +#define _(S,M) INDIC_COMBINE_CATEGORIES (_OT_##S, _POS_##M) + + +static const uint16_t indic_table[] = { + + +#define indic_offset_0x0028u 0 + + + /* Basic Latin */ + + /* 0028 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(GB,C), _(X,X), _(X,X), + /* 0030 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), + /* 0038 */ _(GB,C), _(GB,C), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), + +#define indic_offset_0x00b0u 24 + + + /* Latin-1 Supplement */ + + /* 00B0 */ _(X,X), _(X,X),_(SM,SM),_(SM,SM), _(X,X), _(X,X), _(X,X), _(X,X), + /* 00B8 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), + /* 00C0 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), + /* 00C8 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), + /* 00D0 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(GB,C), + +#define indic_offset_0x0900u 64 + + + /* Devanagari */ + + /* 0900 */_(SM,SM),_(SM,SM),_(SM,SM),_(SM,SM), _(V,C), _(V,C), _(V,C), _(V,C), + /* 0908 */ _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), + /* 0910 */ _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), _(C,C), _(C,C), _(C,C), + /* 0918 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), + /* 0920 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), + /* 0928 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), + /* 0930 */ _(R,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), + /* 0938 */ _(C,C), _(C,C), _(M,AS), _(M,AS), _(N,X), _(S,SM), _(M,AS), _(M,LM), + /* 0940 */ _(M,AS), _(M,AS), _(M,AS), _(M,AS), _(M,AS), _(M,AS), _(M,AS), _(M,AS), + /* 0948 */ _(M,AS), _(M,AS), _(M,AS), _(M,AS), _(M,AS), _(H,B), _(M,LM), _(M,AS), + /* 0950 */ _(X,X), _(A,SM), _(A,SM),_(SM,SM),_(SM,SM), _(M,AS), _(M,AS), _(M,AS), + /* 0958 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), + /* 0960 */ _(V,C), _(V,C), _(M,AS), _(M,AS), _(X,X), _(X,X), _(GB,C), _(GB,C), + /* 0968 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), + /* 0970 */ _(X,X), _(X,X), _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), + /* 0978 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), + + /* Bengali */ + + /* 0980 */ _(GB,C),_(SM,SM),_(SM,SM),_(SM,SM), _(X,X), _(V,C), _(V,C), _(V,C), + /* 0988 */ _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), _(X,X), _(X,X), _(V,C), + /* 0990 */ _(V,C), _(X,X), _(X,X), _(V,C), _(V,C), _(C,C), _(C,C), _(C,C), + /* 0998 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), + /* 09A0 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), + /* 09A8 */ _(C,C), _(X,X), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), + /* 09B0 */ _(R,C), _(X,X), _(C,C), _(X,X), _(X,X), _(X,X), _(C,C), _(C,C), + /* 09B8 */ _(C,C), _(C,C), _(X,X), _(X,X), _(N,X), _(S,SM), _(M,AP), _(M,LM), + /* 09C0 */ _(M,AP), _(M,AS), _(M,AS), _(M,AS), _(M,AS), _(X,X), _(X,X), _(M,LM), + /* 09C8 */ _(M,LM), _(X,X), _(X,X), _(M,AP), _(M,AP), _(H,B), _(C,C), _(X,X), + /* 09D0 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(M,AP), + /* 09D8 */ _(X,X), _(X,X), _(X,X), _(X,X), _(C,C), _(C,C), _(X,X), _(C,C), + /* 09E0 */ _(V,C), _(V,C), _(M,AS), _(M,AS), _(X,X), _(X,X), _(GB,C), _(GB,C), + /* 09E8 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), + /* 09F0 */ _(R,C), _(C,C), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), + /* 09F8 */ _(X,X), _(X,X), _(X,X), _(X,X), _(GB,C), _(X,X),_(SM,SM), _(X,X), + + /* Gurmukhi */ + + /* 0A00 */ _(X,X),_(SM,SM),_(SM,SM),_(SM,SM), _(X,X), _(V,C), _(V,C), _(V,C), + /* 0A08 */ _(V,C), _(V,C), _(V,C), _(X,X), _(X,X), _(X,X), _(X,X), _(V,C), + /* 0A10 */ _(V,C), _(X,X), _(X,X), _(V,C), _(V,C), _(C,C), _(C,C), _(C,C), + /* 0A18 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), + /* 0A20 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), + /* 0A28 */ _(C,C), _(X,X), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), + /* 0A30 */ _(R,C), _(X,X), _(C,C), _(C,C), _(X,X), _(C,C), _(C,C), _(X,X), + /* 0A38 */ _(C,C), _(C,C), _(X,X), _(X,X), _(N,X), _(X,X), _(M,AP), _(M,LM), + /* 0A40 */ _(M,AP), _(M,AP), _(M,AP), _(X,X), _(X,X), _(X,X), _(X,X), _(M,AP), + /* 0A48 */ _(M,AP), _(X,X), _(X,X), _(M,AP), _(M,AP), _(H,B), _(X,X), _(X,X), + /* 0A50 */ _(X,X), _(M,B), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), + /* 0A58 */ _(X,X), _(C,C), _(C,C), _(C,C), _(C,C), _(X,X), _(C,C), _(X,X), + /* 0A60 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(GB,C), _(GB,C), + /* 0A68 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), + /* 0A70 */_(SM,SM),_(SM,SM), _(C,C), _(C,C), _(X,X), _(CM,C), _(X,X), _(X,X), + /* 0A78 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), + + /* Gujarati */ + + /* 0A80 */ _(X,X),_(SM,SM),_(SM,SM),_(SM,SM), _(X,X), _(V,C), _(V,C), _(V,C), + /* 0A88 */ _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), _(X,X), _(V,C), + /* 0A90 */ _(V,C), _(V,C), _(X,X), _(V,C), _(V,C), _(C,C), _(C,C), _(C,C), + /* 0A98 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), + /* 0AA0 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), + /* 0AA8 */ _(C,C), _(X,X), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), + /* 0AB0 */ _(R,C), _(X,X), _(C,C), _(C,C), _(X,X), _(C,C), _(C,C), _(C,C), + /* 0AB8 */ _(C,C), _(C,C), _(X,X), _(X,X), _(N,X), _(S,SM), _(M,AP), _(M,LM), + /* 0AC0 */ _(M,AP), _(M,AP), _(M,AP), _(M,AP), _(M,AP), _(M,AS), _(X,X), _(M,AS), + /* 0AC8 */ _(M,AS), _(M,AP), _(X,X), _(M,AP), _(M,AP), _(H,B), _(X,X), _(X,X), + /* 0AD0 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), + /* 0AD8 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), + /* 0AE0 */ _(V,C), _(V,C), _(M,AP), _(M,AP), _(X,X), _(X,X), _(GB,C), _(GB,C), + /* 0AE8 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), + /* 0AF0 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), + /* 0AF8 */ _(X,X), _(C,C), _(A,SM), _(N,X), _(A,SM), _(N,X), _(N,X), _(N,X), + + /* Oriya */ + + /* 0B00 */ _(X,X),_(SM,BS),_(SM,SM),_(SM,SM), _(X,X), _(V,C), _(V,C), _(V,C), + /* 0B08 */ _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), _(X,X), _(X,X), _(V,C), + /* 0B10 */ _(V,C), _(X,X), _(X,X), _(V,C), _(V,C), _(C,C), _(C,C), _(C,C), + /* 0B18 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), + /* 0B20 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), + /* 0B28 */ _(C,C), _(X,X), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), + /* 0B30 */ _(R,C), _(X,X), _(C,C), _(C,C), _(X,X), _(C,C), _(C,C), _(C,C), + /* 0B38 */ _(C,C), _(C,C), _(X,X), _(X,X), _(N,X), _(S,SM), _(M,AP), _(M,A), + /* 0B40 */ _(M,AP), _(M,AS), _(M,AS), _(M,AS), _(M,AS), _(X,X), _(X,X), _(M,LM), + /* 0B48 */ _(M,A), _(X,X), _(X,X), _(M,AP), _(M,AP), _(H,B), _(X,X), _(X,X), + /* 0B50 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(N,X), _(M,A), _(M,AP), + /* 0B58 */ _(X,X), _(X,X), _(X,X), _(X,X), _(C,C), _(C,C), _(X,X), _(C,C), + /* 0B60 */ _(V,C), _(V,C), _(M,AS), _(M,AS), _(X,X), _(X,X), _(GB,C), _(GB,C), + /* 0B68 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), + /* 0B70 */ _(X,X), _(C,C), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), + /* 0B78 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), + + /* Tamil */ + + /* 0B80 */ _(X,X), _(X,X),_(SM,SM), _(X,X), _(X,X), _(V,C), _(V,C), _(V,C), + /* 0B88 */ _(V,C), _(V,C), _(V,C), _(X,X), _(X,X), _(X,X), _(V,C), _(V,C), + /* 0B90 */ _(V,C), _(X,X), _(V,C), _(V,C), _(V,C), _(C,C), _(X,X), _(X,X), + /* 0B98 */ _(X,X), _(C,C), _(C,C), _(X,X), _(C,C), _(X,X), _(C,C), _(C,C), + /* 0BA0 */ _(X,X), _(X,X), _(X,X), _(C,C), _(C,C), _(X,X), _(X,X), _(X,X), + /* 0BA8 */ _(C,C), _(C,C), _(C,C), _(X,X), _(X,X), _(X,X), _(C,C), _(C,C), + /* 0BB0 */ _(R,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), + /* 0BB8 */ _(C,C), _(C,C), _(X,X), _(X,X), _(X,X), _(X,X), _(M,AP), _(M,AP), + /* 0BC0 */ _(M,AS), _(M,AP), _(M,AP), _(X,X), _(X,X), _(X,X), _(M,LM), _(M,LM), + /* 0BC8 */ _(M,LM), _(X,X), _(M,AP), _(M,AP), _(M,AP), _(H,T), _(X,X), _(X,X), + /* 0BD0 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(M,AP), + /* 0BD8 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), + /* 0BE0 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(GB,C), _(GB,C), + /* 0BE8 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), + /* 0BF0 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), + /* 0BF8 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), + + /* Telugu */ + + /* 0C00 */_(SM,SM),_(SM,SM),_(SM,SM),_(SM,SM),_(SM,SM), _(V,C), _(V,C), _(V,C), + /* 0C08 */ _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), _(X,X), _(V,C), _(V,C), + /* 0C10 */ _(V,C), _(X,X), _(V,C), _(V,C), _(V,C), _(C,C), _(C,C), _(C,C), + /* 0C18 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), + /* 0C20 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), + /* 0C28 */ _(C,C), _(X,X), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), + /* 0C30 */ _(R,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), + /* 0C38 */ _(C,C), _(C,C), _(X,X), _(X,X), _(N,X), _(S,SM), _(M,BS), _(M,BS), + /* 0C40 */ _(M,BS), _(M,BS), _(M,BS), _(M,AS), _(M,AS), _(X,X), _(M,BS), _(M,BS), + /* 0C48 */ _(M,BS), _(X,X), _(M,BS), _(M,BS), _(M,BS), _(H,T), _(X,X), _(X,X), + /* 0C50 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(M,BS), _(M,BS), _(X,X), + /* 0C58 */ _(C,C), _(C,C), _(C,C), _(X,X), _(X,X), _(C,C), _(X,X), _(X,X), + /* 0C60 */ _(V,C), _(V,C), _(M,BS), _(M,BS), _(X,X), _(X,X), _(GB,C), _(GB,C), + /* 0C68 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), + /* 0C70 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), + /* 0C78 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), + + /* Kannada */ + + /* 0C80 */ _(GB,C),_(SM,SM),_(SM,SM),_(SM,SM), _(X,X), _(V,C), _(V,C), _(V,C), + /* 0C88 */ _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), _(X,X), _(V,C), _(V,C), + /* 0C90 */ _(V,C), _(X,X), _(V,C), _(V,C), _(V,C), _(C,C), _(C,C), _(C,C), + /* 0C98 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), + /* 0CA0 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), + /* 0CA8 */ _(C,C), _(X,X), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), + /* 0CB0 */ _(R,C), _(C,C), _(C,C), _(C,C), _(X,X), _(C,C), _(C,C), _(C,C), + /* 0CB8 */ _(C,C), _(C,C), _(X,X), _(X,X), _(N,X), _(S,SM), _(M,BS), _(M,BS), + /* 0CC0 */ _(M,BS), _(M,BS), _(M,BS), _(M,AS), _(M,AS), _(X,X), _(M,BS), _(M,AS), + /* 0CC8 */ _(M,AS), _(X,X), _(M,AS), _(M,AS), _(M,BS), _(H,T), _(X,X), _(X,X), + /* 0CD0 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(M,AS), _(M,AS), _(X,X), + /* 0CD8 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(C,C), _(C,C), _(X,X), + /* 0CE0 */ _(V,C), _(V,C), _(M,BS), _(M,BS), _(X,X), _(X,X), _(GB,C), _(GB,C), + /* 0CE8 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), + /* 0CF0 */ _(X,X), _(CS,C), _(CS,C),_(SM,SM), _(X,X), _(X,X), _(X,X), _(X,X), + /* 0CF8 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), + + /* Malayalam */ + + /* 0D00 */_(SM,SM),_(SM,SM),_(SM,SM),_(SM,SM), _(GB,C), _(V,C), _(V,C), _(V,C), + /* 0D08 */ _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), _(X,X), _(V,C), _(V,C), + /* 0D10 */ _(V,C), _(X,X), _(V,C), _(V,C), _(V,C), _(C,C), _(C,C), _(C,C), + /* 0D18 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), + /* 0D20 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), + /* 0D28 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), + /* 0D30 */ _(R,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), + /* 0D38 */ _(C,C), _(C,C), _(C,C), _(M,AS), _(M,AS), _(S,SM), _(M,AP), _(M,AP), + /* 0D40 */ _(M,AP), _(M,AP), _(M,AP), _(M,AP), _(M,AP), _(X,X), _(M,LM), _(M,LM), + /* 0D48 */ _(M,LM), _(X,X), _(M,AP), _(M,AP), _(M,AP), _(H,T), _(Rf,X), _(X,X), + /* 0D50 */ _(X,X), _(X,X), _(X,X), _(X,X), _(C,C), _(C,C), _(C,C), _(M,AP), + /* 0D58 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(V,C), + /* 0D60 */ _(V,C), _(V,C), _(M,AP), _(M,AP), _(X,X), _(X,X), _(GB,C), _(GB,C), + /* 0D68 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), + /* 0D70 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), + /* 0D78 */ _(X,X), _(X,X), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), + +#define indic_offset_0x1000u 1216 + + + /* Myanmar */ + + /* 1000 */ _(C,C), _(C,C), _(C,C), _(C,C), _(R,C), _(C,C), _(C,C), _(C,C), + /* 1008 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), + /* 1010 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), + /* 1018 */ _(C,C), _(C,C), _(C,C), _(R,C), _(C,C), _(C,C), _(C,C), _(C,C), + /* 1020 */ _(C,C), _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), + /* 1028 */ _(V,C), _(V,C), _(V,C), _(VR,R), _(VR,R), _(VA,T), _(VA,T), _(VB,B), + /* 1030 */ _(VB,B), _(VL,L), _(A,SM), _(VA,T), _(VA,T), _(VA,T), _(A,SM), _(N,X), + /* 1038 */_(SM,SM), _(H,X), _(As,X), _(MY,X), _(MR,X), _(MW,X), _(MH,X), _(C,C), + /* 1040 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), + /* 1048 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(X,X), _(X,X), _(C,C), _(X,X), + /* 1050 */ _(C,C), _(C,C), _(V,C), _(V,C), _(V,C), _(V,C), _(VR,R), _(VR,R), + /* 1058 */ _(VB,B), _(VB,B), _(R,C), _(C,C), _(C,C), _(C,C), _(MY,X), _(MY,X), + /* 1060 */ _(ML,X), _(C,C), _(VR,R), _(PT,X), _(PT,X), _(C,C), _(C,C), _(VR,R), + /* 1068 */ _(VR,R), _(PT,X), _(PT,X), _(PT,X), _(PT,X), _(PT,X), _(C,C), _(C,C), + /* 1070 */ _(C,C), _(VA,T), _(VA,T), _(VA,T), _(VA,T), _(C,C), _(C,C), _(C,C), + /* 1078 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), + /* 1080 */ _(C,C), _(C,C), _(MW,X), _(VR,R), _(VL,L), _(VA,T), _(VA,T),_(SM,SM), + /* 1088 */_(SM,SM),_(SM,SM),_(SM,SM),_(SM,SM),_(SM,SM),_(SM,SM), _(C,C),_(SM,SM), + /* 1090 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), + /* 1098 */ _(GB,C), _(GB,C),_(SM,SM),_(SM,SM),_(SM,SM), _(VA,T), _(X,X), _(X,X), + +#define indic_offset_0x1780u 1376 + + + /* Khmer */ + + /* 1780 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), + /* 1788 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), + /* 1790 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), + /* 1798 */ _(C,C), _(C,C), _(R,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), + /* 17A0 */ _(C,C), _(C,C), _(C,C), _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), + /* 17A8 */ _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), + /* 17B0 */ _(V,C), _(V,C), _(V,C), _(V,C), _(X,X), _(X,X), _(VR,R), _(VA,T), + /* 17B8 */ _(VA,T), _(VA,T), _(VA,T), _(VB,B), _(VB,B), _(VB,B), _(VA,T), _(VR,R), + /* 17C0 */ _(VR,R), _(VL,L), _(VL,L), _(VL,L), _(VR,R), _(VR,R), _(Xg,X), _(Yg,X), + /* 17C8 */ _(Yg,X), _(Rt,X), _(Rt,X), _(Xg,X), _(Rt,X), _(Xg,X), _(Xg,X), _(Xg,X), + /* 17D0 */ _(Xg,X), _(Xg,X), _(H,X), _(Yg,X), _(X,X), _(X,X), _(X,X), _(X,X), + /* 17D8 */ _(X,X), _(GB,C), _(X,X), _(X,X), _(S,SM), _(Yg,X), _(X,X), _(X,X), + /* 17E0 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), + /* 17E8 */ _(GB,C), _(GB,C), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), + +#define indic_offset_0x1cd0u 1488 + + + /* Vedic Extensions */ + + /* 1CD0 */ _(A,SM), _(A,SM), _(A,SM), _(X,X), _(A,SM), _(A,SM), _(A,SM), _(A,SM), + /* 1CD8 */ _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM), + /* 1CE0 */ _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM), + /* 1CE8 */ _(A,SM), _(S,SM), _(S,SM), _(S,SM), _(S,SM), _(A,SM), _(S,SM), _(S,SM), + /* 1CF0 */ _(S,SM), _(S,SM), _(C,C), _(C,C), _(A,SM), _(C,C), _(C,C), _(A,SM), + /* 1CF8 */ _(A,SM), _(A,SM), _(GB,C), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), + +#define indic_offset_0x2008u 1536 + + + /* General Punctuation */ + + /* 2008 */ _(X,X), _(X,X), _(X,X), _(X,X),_(ZWNJ,X),_(ZWJ,X), _(X,X), _(X,X), + /* 2010 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(X,X), _(X,X), + /* 2018 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), + /* 2020 */ _(X,X), _(X,X), _(GB,C), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), + +#define indic_offset_0x2070u 1568 + + + /* Superscripts and Subscripts */ + + /* 2070 */ _(X,X), _(X,X), _(X,X), _(X,X),_(SM,SM), _(X,X), _(X,X), _(X,X), + /* 2078 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), + /* 2080 */ _(X,X), _(X,X),_(SM,SM),_(SM,SM),_(SM,SM), _(X,X), _(X,X), _(X,X), + +#define indic_offset_0x25f8u 1592 + + + /* Geometric Shapes */ + + /* 25F8 */ _(X,X), _(X,X), _(X,X), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(X,X), + +#define indic_offset_0xa8e0u 1600 + + + /* Devanagari Extended */ + + /* A8E0 */ _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM), + /* A8E8 */ _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM), + /* A8F0 */ _(A,SM), _(A,SM), _(S,SM), _(S,SM), _(S,SM), _(S,SM), _(S,SM), _(S,SM), + /* A8F8 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(V,C), _(M,AS), + +#define indic_offset_0xa9e0u 1632 + + + /* Myanmar Extended-B */ + + /* A9E0 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(VA,T), _(X,X), _(C,C), + /* A9E8 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), + /* A9F0 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), + /* A9F8 */ _(GB,C), _(GB,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(X,X), + +#define indic_offset_0xaa60u 1664 + + + /* Myanmar Extended-A */ + + /* AA60 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), + /* AA68 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), + /* AA70 */ _(X,X), _(C,C), _(C,C), _(C,C), _(GB,C), _(GB,C), _(GB,C), _(X,X), + /* AA78 */ _(X,X), _(X,X), _(C,C), _(PT,X), _(N,X), _(N,X), _(C,C), _(C,C), + +#define indic_offset_0xfe00u 1696 + + + /* Variation Selectors */ + + /* FE00 */ _(VS,X), _(VS,X), _(VS,X), _(VS,X), _(VS,X), _(VS,X), _(VS,X), _(VS,X), + /* FE08 */ _(VS,X), _(VS,X), _(VS,X), _(VS,X), _(VS,X), _(VS,X), _(VS,X), _(VS,X), + +#define indic_offset_0x11300u 1712 + + + /* Grantha */ + + /* 11300 */ _(X,X),_(SM,SM),_(SM,SM),_(SM,SM), _(X,X), _(X,X), _(X,X), _(X,X), + /* 11308 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), + /* 11310 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), + /* 11318 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), + /* 11320 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), + /* 11328 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), + /* 11330 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), + /* 11338 */ _(X,X), _(X,X), _(X,X), _(N,X), _(N,X), _(X,X), _(X,X), _(X,X), + +}; /* Table items: 1776; occupancy: 69% */ + +uint16_t +hb_indic_get_categories (hb_codepoint_t u) +{ + switch (u >> 12) + { + case 0x0u: + if (unlikely (u == 0x00A0u)) return _(GB,C); + if (hb_in_range (u, 0x0028u, 0x003Fu)) return indic_table[u - 0x0028u + indic_offset_0x0028u]; + if (hb_in_range (u, 0x00B0u, 0x00D7u)) return indic_table[u - 0x00B0u + indic_offset_0x00b0u]; + if (hb_in_range (u, 0x0900u, 0x0D7Fu)) return indic_table[u - 0x0900u + indic_offset_0x0900u]; + break; + + case 0x1u: + if (hb_in_range (u, 0x1000u, 0x109Fu)) return indic_table[u - 0x1000u + indic_offset_0x1000u]; + if (hb_in_range (u, 0x1780u, 0x17EFu)) return indic_table[u - 0x1780u + indic_offset_0x1780u]; + if (hb_in_range (u, 0x1CD0u, 0x1CFFu)) return indic_table[u - 0x1CD0u + indic_offset_0x1cd0u]; + break; + + case 0x2u: + if (unlikely (u == 0x25CCu)) return _(DC,C); + if (hb_in_range (u, 0x2008u, 0x2027u)) return indic_table[u - 0x2008u + indic_offset_0x2008u]; + if (hb_in_range (u, 0x2070u, 0x2087u)) return indic_table[u - 0x2070u + indic_offset_0x2070u]; + if (hb_in_range (u, 0x25F8u, 0x25FFu)) return indic_table[u - 0x25F8u + indic_offset_0x25f8u]; + break; + + case 0xAu: + if (hb_in_range (u, 0xA8E0u, 0xA8FFu)) return indic_table[u - 0xA8E0u + indic_offset_0xa8e0u]; + if (hb_in_range (u, 0xA9E0u, 0xA9FFu)) return indic_table[u - 0xA9E0u + indic_offset_0xa9e0u]; + if (hb_in_range (u, 0xAA60u, 0xAA7Fu)) return indic_table[u - 0xAA60u + indic_offset_0xaa60u]; + break; + + case 0xFu: + if (hb_in_range (u, 0xFE00u, 0xFE0Fu)) return indic_table[u - 0xFE00u + indic_offset_0xfe00u]; + break; + + case 0x11u: + if (hb_in_range (u, 0x11300u, 0x1133Fu)) return indic_table[u - 0x11300u + indic_offset_0x11300u]; + break; + + default: + break; + } + return _(X,X); +} + +#undef _ +#undef INDIC_COMBINE_CATEGORIES + +#undef _OT_A +#undef _OT_As +#undef _OT_C +#undef _OT_CM +#undef _OT_CS +#undef _OT_DC +#undef _OT_H +#undef _OT_M +#undef _OT_MH +#undef _OT_ML +#undef _OT_MR +#undef _OT_MW +#undef _OT_MY +#undef _OT_N +#undef _OT_GB +#undef _OT_PT +#undef _OT_R +#undef _OT_Rf +#undef _OT_Rt +#undef _OT_SM +#undef _OT_S +#undef _OT_V +#undef _OT_VA +#undef _OT_VB +#undef _OT_VL +#undef _OT_VR +#undef _OT_VS +#undef _OT_X +#undef _OT_Xg +#undef _OT_Yg +#undef _OT_ZWJ +#undef _OT_ZWNJ + +#undef _POS_T +#undef _POS_A +#undef _POS_AP +#undef _POS_AS +#undef _POS_C +#undef _POS_BS +#undef _POS_B +#undef _POS_X +#undef _POS_R +#undef _POS_L +#undef _POS_LM +#undef _POS_SM + +#endif + +/* == End of generated table == */ diff --git a/libs/harfbuzz/src/hb-ot-shaper-indic.cc b/libs/harfbuzz/src/hb-ot-shaper-indic.cc new file mode 100644 index 00000000..55509c11 --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-shaper-indic.cc @@ -0,0 +1,1540 @@ +/* + * Copyright © 2011,2012 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 + */ + +#include "hb.hh" + +#ifndef HB_NO_OT_SHAPE + +#include "hb-ot-shaper-indic.hh" +#include "hb-ot-shaper-indic-machine.hh" +#include "hb-ot-shaper-vowel-constraints.hh" +#include "hb-ot-layout.hh" + + +/* + * Indic shaper. + */ + + +static inline void +set_indic_properties (hb_glyph_info_t &info) +{ + hb_codepoint_t u = info.codepoint; + unsigned int type = hb_indic_get_categories (u); + + info.indic_category() = (indic_category_t) (type & 0xFFu); + info.indic_position() = (indic_position_t) (type >> 8); +} + + +static inline bool +is_one_of (const hb_glyph_info_t &info, unsigned int flags) +{ + /* If it ligated, all bets are off. */ + if (_hb_glyph_info_ligated (&info)) return false; + return !!(FLAG_UNSAFE (info.indic_category()) & flags); +} + +/* Note: + * + * We treat Vowels and placeholders as if they were consonants. This is safe because Vowels + * cannot happen in a consonant syllable. The plus side however is, we can call the + * consonant syllable logic from the vowel syllable function and get it all right! + * + * Keep in sync with consonant_categories in the generator. */ +#define CONSONANT_FLAGS_INDIC (FLAG (I_Cat(C)) | FLAG (I_Cat(CS)) | FLAG (I_Cat(Ra)) | FLAG (I_Cat(CM)) | FLAG (I_Cat(V)) | FLAG (I_Cat(PLACEHOLDER)) | FLAG (I_Cat(DOTTEDCIRCLE))) + +static inline bool +is_consonant (const hb_glyph_info_t &info) +{ + return is_one_of (info, CONSONANT_FLAGS_INDIC); +} + +#define JOINER_FLAGS (FLAG (I_Cat(ZWJ)) | FLAG (I_Cat(ZWNJ))) + +static inline bool +is_joiner (const hb_glyph_info_t &info) +{ + return is_one_of (info, JOINER_FLAGS); +} + +static inline bool +is_halant (const hb_glyph_info_t &info) +{ + return is_one_of (info, FLAG (I_Cat(H))); +} + +struct hb_indic_would_substitute_feature_t +{ + void init (const hb_ot_map_t *map, hb_tag_t feature_tag, bool zero_context_) + { + zero_context = zero_context_; + lookups = map->get_stage_lookups (0/*GSUB*/, + map->get_feature_stage (0/*GSUB*/, feature_tag)); + } + + bool would_substitute (const hb_codepoint_t *glyphs, + unsigned int glyphs_count, + hb_face_t *face) const + { + for (const auto &lookup : lookups) + if (hb_ot_layout_lookup_would_substitute (face, lookup.index, glyphs, glyphs_count, zero_context)) + return true; + return false; + } + + private: + hb_array_t lookups; + bool zero_context; +}; + + +/* + * Indic configurations. Note that we do not want to keep every single script-specific + * behavior in these tables necessarily. This should mainly be used for per-script + * properties that are cheaper keeping here, than in the code. Ie. if, say, one and + * only one script has an exception, that one script can be if'ed directly in the code, + * instead of adding a new flag in these structs. + */ + +enum reph_position_t { + REPH_POS_AFTER_MAIN = POS_AFTER_MAIN, + REPH_POS_BEFORE_SUB = POS_BEFORE_SUB, + REPH_POS_AFTER_SUB = POS_AFTER_SUB, + REPH_POS_BEFORE_POST = POS_BEFORE_POST, + REPH_POS_AFTER_POST = POS_AFTER_POST +}; +enum reph_mode_t { + REPH_MODE_IMPLICIT, /* Reph formed out of initial Ra,H sequence. */ + REPH_MODE_EXPLICIT, /* Reph formed out of initial Ra,H,ZWJ sequence. */ + REPH_MODE_LOG_REPHA /* Encoded Repha character, needs reordering. */ +}; +enum blwf_mode_t { + BLWF_MODE_PRE_AND_POST, /* Below-forms feature applied to pre-base and post-base. */ + BLWF_MODE_POST_ONLY /* Below-forms feature applied to post-base only. */ +}; +struct indic_config_t +{ + hb_script_t script; + bool has_old_spec; + hb_codepoint_t virama; + reph_position_t reph_pos; + reph_mode_t reph_mode; + blwf_mode_t blwf_mode; +}; + +static const indic_config_t indic_configs[] = +{ + /* Default. Should be first. */ + {HB_SCRIPT_INVALID, false, 0,REPH_POS_BEFORE_POST,REPH_MODE_IMPLICIT, BLWF_MODE_PRE_AND_POST}, + {HB_SCRIPT_DEVANAGARI,true, 0x094Du,REPH_POS_BEFORE_POST,REPH_MODE_IMPLICIT, BLWF_MODE_PRE_AND_POST}, + {HB_SCRIPT_BENGALI, true, 0x09CDu,REPH_POS_AFTER_SUB, REPH_MODE_IMPLICIT, BLWF_MODE_PRE_AND_POST}, + {HB_SCRIPT_GURMUKHI, true, 0x0A4Du,REPH_POS_BEFORE_SUB, REPH_MODE_IMPLICIT, BLWF_MODE_PRE_AND_POST}, + {HB_SCRIPT_GUJARATI, true, 0x0ACDu,REPH_POS_BEFORE_POST,REPH_MODE_IMPLICIT, BLWF_MODE_PRE_AND_POST}, + {HB_SCRIPT_ORIYA, true, 0x0B4Du,REPH_POS_AFTER_MAIN, REPH_MODE_IMPLICIT, BLWF_MODE_PRE_AND_POST}, + {HB_SCRIPT_TAMIL, true, 0x0BCDu,REPH_POS_AFTER_POST, REPH_MODE_IMPLICIT, BLWF_MODE_PRE_AND_POST}, + {HB_SCRIPT_TELUGU, true, 0x0C4Du,REPH_POS_AFTER_POST, REPH_MODE_EXPLICIT, BLWF_MODE_POST_ONLY}, + {HB_SCRIPT_KANNADA, true, 0x0CCDu,REPH_POS_AFTER_POST, REPH_MODE_IMPLICIT, BLWF_MODE_POST_ONLY}, + {HB_SCRIPT_MALAYALAM, true, 0x0D4Du,REPH_POS_AFTER_MAIN, REPH_MODE_LOG_REPHA,BLWF_MODE_PRE_AND_POST}, +}; + + +static const hb_ot_map_feature_t +indic_features[] = +{ + /* + * Basic features. + * These features are applied in order, one at a time, after initial_reordering, + * constrained to the syllable. + */ + {HB_TAG('n','u','k','t'), F_GLOBAL_MANUAL_JOINERS | F_PER_SYLLABLE}, + {HB_TAG('a','k','h','n'), F_GLOBAL_MANUAL_JOINERS | F_PER_SYLLABLE}, + {HB_TAG('r','p','h','f'), F_MANUAL_JOINERS | F_PER_SYLLABLE}, + {HB_TAG('r','k','r','f'), F_GLOBAL_MANUAL_JOINERS | F_PER_SYLLABLE}, + {HB_TAG('p','r','e','f'), F_MANUAL_JOINERS | F_PER_SYLLABLE}, + {HB_TAG('b','l','w','f'), F_MANUAL_JOINERS | F_PER_SYLLABLE}, + {HB_TAG('a','b','v','f'), F_MANUAL_JOINERS | F_PER_SYLLABLE}, + {HB_TAG('h','a','l','f'), F_MANUAL_JOINERS | F_PER_SYLLABLE}, + {HB_TAG('p','s','t','f'), F_MANUAL_JOINERS | F_PER_SYLLABLE}, + {HB_TAG('v','a','t','u'), F_GLOBAL_MANUAL_JOINERS | F_PER_SYLLABLE}, + {HB_TAG('c','j','c','t'), F_GLOBAL_MANUAL_JOINERS | F_PER_SYLLABLE}, + /* + * Other features. + * These features are applied all at once, after final_reordering, constrained + * to the syllable. + * Default Bengali font in Windows for example has intermixed + * lookups for init,pres,abvs,blws features. + */ + {HB_TAG('i','n','i','t'), F_MANUAL_JOINERS | F_PER_SYLLABLE}, + {HB_TAG('p','r','e','s'), F_GLOBAL_MANUAL_JOINERS | F_PER_SYLLABLE}, + {HB_TAG('a','b','v','s'), F_GLOBAL_MANUAL_JOINERS | F_PER_SYLLABLE}, + {HB_TAG('b','l','w','s'), F_GLOBAL_MANUAL_JOINERS | F_PER_SYLLABLE}, + {HB_TAG('p','s','t','s'), F_GLOBAL_MANUAL_JOINERS | F_PER_SYLLABLE}, + {HB_TAG('h','a','l','n'), F_GLOBAL_MANUAL_JOINERS | F_PER_SYLLABLE}, +}; + +/* + * Must be in the same order as the indic_features array. + */ +enum { + _INDIC_NUKT, + _INDIC_AKHN, + INDIC_RPHF, + _INDIC_RKRF, + INDIC_PREF, + INDIC_BLWF, + INDIC_ABVF, + INDIC_HALF, + INDIC_PSTF, + _INDIC_VATU, + _INDIC_CJCT, + + INDIC_INIT, + _INDIC_PRES, + _INDIC_ABVS, + _INDIC_BLWS, + _INDIC_PSTS, + _INDIC_HALN, + + INDIC_NUM_FEATURES, + INDIC_BASIC_FEATURES = INDIC_INIT, /* Don't forget to update this! */ +}; + +static void +setup_syllables_indic (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer); +static void +initial_reordering_indic (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer); +static void +final_reordering_indic (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer); + +static void +collect_features_indic (hb_ot_shape_planner_t *plan) +{ + hb_ot_map_builder_t *map = &plan->map; + + /* Do this before any lookups have been applied. */ + map->add_gsub_pause (setup_syllables_indic); + + map->enable_feature (HB_TAG('l','o','c','l'), F_PER_SYLLABLE); + /* The Indic specs do not require ccmp, but we apply it here since if + * there is a use of it, it's typically at the beginning. */ + map->enable_feature (HB_TAG('c','c','m','p'), F_PER_SYLLABLE); + + + unsigned int i = 0; + map->add_gsub_pause (initial_reordering_indic); + + for (; i < INDIC_BASIC_FEATURES; i++) { + map->add_feature (indic_features[i]); + map->add_gsub_pause (nullptr); + } + + map->add_gsub_pause (final_reordering_indic); + + for (; i < INDIC_NUM_FEATURES; i++) + map->add_feature (indic_features[i]); +} + +static void +override_features_indic (hb_ot_shape_planner_t *plan) +{ + plan->map.disable_feature (HB_TAG('l','i','g','a')); + plan->map.add_gsub_pause (hb_syllabic_clear_var); // Don't need syllables anymore, use stop to free buffer var +} + + +struct indic_shape_plan_t +{ + bool load_virama_glyph (hb_font_t *font, hb_codepoint_t *pglyph) const + { + hb_codepoint_t glyph = virama_glyph; + if (unlikely (glyph == (hb_codepoint_t) -1)) + { + if (!config->virama || !font->get_nominal_glyph (config->virama, &glyph)) + glyph = 0; + /* Technically speaking, the spec says we should apply 'locl' to virama too. + * Maybe one day... */ + + /* Our get_nominal_glyph() function needs a font, so we can't get the virama glyph + * during shape planning... Instead, overwrite it here. */ + virama_glyph = (int) glyph; + } + + *pglyph = glyph; + return glyph != 0; + } + + const indic_config_t *config; + + bool is_old_spec; +#ifndef HB_NO_UNISCRIBE_BUG_COMPATIBLE + bool uniscribe_bug_compatible; +#else + static constexpr bool uniscribe_bug_compatible = false; +#endif + mutable hb_atomic_int_t virama_glyph; + + hb_indic_would_substitute_feature_t rphf; + hb_indic_would_substitute_feature_t pref; + hb_indic_would_substitute_feature_t blwf; + hb_indic_would_substitute_feature_t pstf; + hb_indic_would_substitute_feature_t vatu; + + hb_mask_t mask_array[INDIC_NUM_FEATURES]; +}; + +static void * +data_create_indic (const hb_ot_shape_plan_t *plan) +{ + indic_shape_plan_t *indic_plan = (indic_shape_plan_t *) hb_calloc (1, sizeof (indic_shape_plan_t)); + if (unlikely (!indic_plan)) + return nullptr; + + indic_plan->config = &indic_configs[0]; + for (unsigned int i = 1; i < ARRAY_LENGTH (indic_configs); i++) + if (plan->props.script == indic_configs[i].script) { + indic_plan->config = &indic_configs[i]; + break; + } + + indic_plan->is_old_spec = indic_plan->config->has_old_spec && ((plan->map.chosen_script[0] & 0x000000FFu) != '2'); +#ifndef HB_NO_UNISCRIBE_BUG_COMPATIBLE + indic_plan->uniscribe_bug_compatible = hb_options ().uniscribe_bug_compatible; +#endif + indic_plan->virama_glyph = -1; + + /* Use zero-context would_substitute() matching for new-spec of the main + * Indic scripts, and scripts with one spec only, but not for old-specs. + * The new-spec for all dual-spec scripts says zero-context matching happens. + * + * However, testing with Malayalam shows that old and new spec both allow + * context. Testing with Bengali new-spec however shows that it doesn't. + * So, the heuristic here is the way it is. It should *only* be changed, + * as we discover more cases of what Windows does. DON'T TOUCH OTHERWISE. + */ + bool zero_context = !indic_plan->is_old_spec && plan->props.script != HB_SCRIPT_MALAYALAM; + indic_plan->rphf.init (&plan->map, HB_TAG('r','p','h','f'), zero_context); + indic_plan->pref.init (&plan->map, HB_TAG('p','r','e','f'), zero_context); + indic_plan->blwf.init (&plan->map, HB_TAG('b','l','w','f'), zero_context); + indic_plan->pstf.init (&plan->map, HB_TAG('p','s','t','f'), zero_context); + indic_plan->vatu.init (&plan->map, HB_TAG('v','a','t','u'), zero_context); + + for (unsigned int i = 0; i < ARRAY_LENGTH (indic_plan->mask_array); i++) + indic_plan->mask_array[i] = (indic_features[i].flags & F_GLOBAL) ? + 0 : plan->map.get_1_mask (indic_features[i].tag); + + return indic_plan; +} + +static void +data_destroy_indic (void *data) +{ + hb_free (data); +} + +static indic_position_t +consonant_position_from_face (const indic_shape_plan_t *indic_plan, + const hb_codepoint_t consonant, + const hb_codepoint_t virama, + hb_face_t *face) +{ + /* For old-spec, the order of glyphs is Consonant,Virama, + * whereas for new-spec, it's Virama,Consonant. However, + * some broken fonts (like Free Sans) simply copied lookups + * from old-spec to new-spec without modification. + * And oddly enough, Uniscribe seems to respect those lookups. + * Eg. in the sequence U+0924,U+094D,U+0930, Uniscribe finds + * base at 0. The font however, only has lookups matching + * 930,94D in 'blwf', not the expected 94D,930 (with new-spec + * table). As such, we simply match both sequences. Seems + * to work. + * + * Vatu is done as well, for: + * https://github.com/harfbuzz/harfbuzz/issues/1587 + */ + hb_codepoint_t glyphs[3] = {virama, consonant, virama}; + if (indic_plan->blwf.would_substitute (glyphs , 2, face) || + indic_plan->blwf.would_substitute (glyphs+1, 2, face) || + indic_plan->vatu.would_substitute (glyphs , 2, face) || + indic_plan->vatu.would_substitute (glyphs+1, 2, face)) + return POS_BELOW_C; + if (indic_plan->pstf.would_substitute (glyphs , 2, face) || + indic_plan->pstf.would_substitute (glyphs+1, 2, face)) + return POS_POST_C; + if (indic_plan->pref.would_substitute (glyphs , 2, face) || + indic_plan->pref.would_substitute (glyphs+1, 2, face)) + return POS_POST_C; + return POS_BASE_C; +} + +static void +setup_masks_indic (const hb_ot_shape_plan_t *plan HB_UNUSED, + hb_buffer_t *buffer, + hb_font_t *font HB_UNUSED) +{ + HB_BUFFER_ALLOCATE_VAR (buffer, indic_category); + HB_BUFFER_ALLOCATE_VAR (buffer, indic_position); + + /* We cannot setup masks here. We save information about characters + * and setup masks later on in a pause-callback. */ + + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; + for (unsigned int i = 0; i < count; i++) + set_indic_properties (info[i]); +} + +static void +setup_syllables_indic (const hb_ot_shape_plan_t *plan HB_UNUSED, + hb_font_t *font HB_UNUSED, + hb_buffer_t *buffer) +{ + HB_BUFFER_ALLOCATE_VAR (buffer, syllable); + find_syllables_indic (buffer); + foreach_syllable (buffer, start, end) + buffer->unsafe_to_break (start, end); +} + +static int +compare_indic_order (const hb_glyph_info_t *pa, const hb_glyph_info_t *pb) +{ + int a = pa->indic_position(); + int b = pb->indic_position(); + + return (int) a - (int) b; +} + + + +static void +update_consonant_positions_indic (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer) +{ + const indic_shape_plan_t *indic_plan = (const indic_shape_plan_t *) plan->data; + + hb_codepoint_t virama; + if (indic_plan->load_virama_glyph (font, &virama)) + { + hb_face_t *face = font->face; + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; + for (unsigned int i = 0; i < count; i++) + if (info[i].indic_position() == POS_BASE_C) + { + hb_codepoint_t consonant = info[i].codepoint; + info[i].indic_position() = consonant_position_from_face (indic_plan, consonant, virama, face); + } + } +} + + +/* Rules from: + * https://docs.microsqoft.com/en-us/typography/script-development/devanagari */ + +static void +initial_reordering_consonant_syllable (const hb_ot_shape_plan_t *plan, + hb_face_t *face, + hb_buffer_t *buffer, + unsigned int start, unsigned int end) +{ + const indic_shape_plan_t *indic_plan = (const indic_shape_plan_t *) plan->data; + hb_glyph_info_t *info = buffer->info; + + /* https://github.com/harfbuzz/harfbuzz/issues/435#issuecomment-335560167 + * // For compatibility with legacy usage in Kannada, + * // Ra+h+ZWJ must behave like Ra+ZWJ+h... + */ + if (buffer->props.script == HB_SCRIPT_KANNADA && + start + 3 <= end && + is_one_of (info[start ], FLAG (I_Cat(Ra))) && + is_one_of (info[start+1], FLAG (I_Cat(H))) && + is_one_of (info[start+2], FLAG (I_Cat(ZWJ)))) + { + buffer->merge_clusters (start+1, start+3); + hb_glyph_info_t tmp = info[start+1]; + info[start+1] = info[start+2]; + info[start+2] = tmp; + } + + /* 1. Find base consonant: + * + * The shaping engine finds the base consonant of the syllable, using the + * following algorithm: starting from the end of the syllable, move backwards + * until a consonant is found that does not have a below-base or post-base + * form (post-base forms have to follow below-base forms), or that is not a + * pre-base-reordering Ra, or arrive at the first consonant. The consonant + * stopped at will be the base. + * + * o If the syllable starts with Ra + Halant (in a script that has Reph) + * and has more than one consonant, Ra is excluded from candidates for + * base consonants. + */ + + unsigned int base = end; + bool has_reph = false; + + { + /* -> If the syllable starts with Ra + Halant (in a script that has Reph) + * and has more than one consonant, Ra is excluded from candidates for + * base consonants. */ + unsigned int limit = start; + if (indic_plan->mask_array[INDIC_RPHF] && + start + 3 <= end && + ( + (indic_plan->config->reph_mode == REPH_MODE_IMPLICIT && !is_joiner (info[start + 2])) || + (indic_plan->config->reph_mode == REPH_MODE_EXPLICIT && info[start + 2].indic_category() == I_Cat(ZWJ)) + )) + { + /* See if it matches the 'rphf' feature. */ + hb_codepoint_t glyphs[3] = {info[start].codepoint, + info[start + 1].codepoint, + indic_plan->config->reph_mode == REPH_MODE_EXPLICIT ? + info[start + 2].codepoint : 0}; + if (indic_plan->rphf.would_substitute (glyphs, 2, face) || + (indic_plan->config->reph_mode == REPH_MODE_EXPLICIT && + indic_plan->rphf.would_substitute (glyphs, 3, face))) + { + limit += 2; + while (limit < end && is_joiner (info[limit])) + limit++; + base = start; + has_reph = true; + } + } else if (indic_plan->config->reph_mode == REPH_MODE_LOG_REPHA && info[start].indic_category() == I_Cat(Repha)) + { + limit += 1; + while (limit < end && is_joiner (info[limit])) + limit++; + base = start; + has_reph = true; + } + + { + /* -> starting from the end of the syllable, move backwards */ + unsigned int i = end; + bool seen_below = false; + do { + i--; + /* -> until a consonant is found */ + if (is_consonant (info[i])) + { + /* -> that does not have a below-base or post-base form + * (post-base forms have to follow below-base forms), */ + if (info[i].indic_position() != POS_BELOW_C && + (info[i].indic_position() != POS_POST_C || seen_below)) + { + base = i; + break; + } + if (info[i].indic_position() == POS_BELOW_C) + seen_below = true; + + /* -> or that is not a pre-base-reordering Ra, + * + * IMPLEMENTATION NOTES: + * + * Our pre-base-reordering Ra's are marked POS_POST_C, so will be skipped + * by the logic above already. + */ + + /* -> or arrive at the first consonant. The consonant stopped at will + * be the base. */ + base = i; + } + else + { + /* A ZWJ after a Halant stops the base search, and requests an explicit + * half form. + * A ZWJ before a Halant, requests a subjoined form instead, and hence + * search continues. This is particularly important for Bengali + * sequence Ra,H,Ya that should form Ya-Phalaa by subjoining Ya. */ + if (start < i && + info[i].indic_category() == I_Cat(ZWJ) && + info[i - 1].indic_category() == I_Cat(H)) + break; + } + } while (i > limit); + } + + /* -> If the syllable starts with Ra + Halant (in a script that has Reph) + * and has more than one consonant, Ra is excluded from candidates for + * base consonants. + * + * Only do this for unforced Reph. (ie. not for Ra,H,ZWJ. */ + if (has_reph && base == start && limit - base <= 2) { + /* Have no other consonant, so Reph is not formed and Ra becomes base. */ + has_reph = false; + } + } + + + /* 2. Decompose and reorder Matras: + * + * Each matra and any syllable modifier sign in the syllable are moved to the + * appropriate position relative to the consonant(s) in the syllable. The + * shaping engine decomposes two- or three-part matras into their constituent + * parts before any repositioning. Matra characters are classified by which + * consonant in a conjunct they have affinity for and are reordered to the + * following positions: + * + * o Before first half form in the syllable + * o After subjoined consonants + * o After post-form consonant + * o After main consonant (for above marks) + * + * IMPLEMENTATION NOTES: + * + * The normalize() routine has already decomposed matras for us, so we don't + * need to worry about that. + */ + + + /* 3. Reorder marks to canonical order: + * + * Adjacent nukta and halant or nukta and vedic sign are always repositioned + * if necessary, so that the nukta is first. + * + * IMPLEMENTATION NOTES: + * + * We don't need to do this: the normalize() routine already did this for us. + */ + + + /* Reorder characters */ + + for (unsigned int i = start; i < base; i++) + info[i].indic_position() = hb_min (POS_PRE_C, (indic_position_t) info[i].indic_position()); + + if (base < end) + info[base].indic_position() = POS_BASE_C; + + /* Handle beginning Ra */ + if (has_reph) + info[start].indic_position() = POS_RA_TO_BECOME_REPH; + + /* For old-style Indic script tags, move the first post-base Halant after + * last consonant. + * + * Reports suggest that in some scripts Uniscribe does this only if there + * is *not* a Halant after last consonant already. We know that is the + * case for Kannada, while it reorders unconditionally in other scripts, + * eg. Malayalam, Bengali, and Devanagari. We don't currently know about + * other scripts, so we block Kannada. + * + * Kannada test case: + * U+0C9A,U+0CCD,U+0C9A,U+0CCD + * With some versions of Lohit Kannada. + * https://bugs.freedesktop.org/show_bug.cgi?id=59118 + * + * Malayalam test case: + * U+0D38,U+0D4D,U+0D31,U+0D4D,U+0D31,U+0D4D + * With lohit-ttf-20121122/Lohit-Malayalam.ttf + * + * Bengali test case: + * U+0998,U+09CD,U+09AF,U+09CD + * With Windows XP vrinda.ttf + * https://github.com/harfbuzz/harfbuzz/issues/1073 + * + * Devanagari test case: + * U+091F,U+094D,U+0930,U+094D + * With chandas.ttf + * https://github.com/harfbuzz/harfbuzz/issues/1071 + */ + if (indic_plan->is_old_spec) + { + bool disallow_double_halants = buffer->props.script == HB_SCRIPT_KANNADA; + for (unsigned int i = base + 1; i < end; i++) + if (info[i].indic_category() == I_Cat(H)) + { + unsigned int j; + for (j = end - 1; j > i; j--) + if (is_consonant (info[j]) || + (disallow_double_halants && info[j].indic_category() == I_Cat(H))) + break; + if (info[j].indic_category() != I_Cat(H) && j > i) { + /* Move Halant to after last consonant. */ + hb_glyph_info_t t = info[i]; + memmove (&info[i], &info[i + 1], (j - i) * sizeof (info[0])); + info[j] = t; + } + break; + } + } + + /* Attach misc marks to previous char to move with them. */ + { + indic_position_t last_pos = POS_START; + for (unsigned int i = start; i < end; i++) + { + if ((FLAG_UNSAFE (info[i].indic_category()) & (JOINER_FLAGS | FLAG (I_Cat(N)) | FLAG (I_Cat(RS)) | FLAG (I_Cat(CM)) | FLAG (I_Cat(H))))) + { + info[i].indic_position() = last_pos; + if (unlikely (info[i].indic_category() == I_Cat(H) && + info[i].indic_position() == POS_PRE_M)) + { + /* + * Uniscribe doesn't move the Halant with Left Matra. + * TEST: U+092B,U+093F,U+094D + * We follow. + */ + for (unsigned int j = i; j > start; j--) + if (info[j - 1].indic_position() != POS_PRE_M) { + info[i].indic_position() = info[j - 1].indic_position(); + break; + } + } + } else if (info[i].indic_position() != POS_SMVD) { + last_pos = (indic_position_t) info[i].indic_position(); + } + } + } + /* For post-base consonants let them own anything before them + * since the last consonant or matra. */ + { + unsigned int last = base; + for (unsigned int i = base + 1; i < end; i++) + if (is_consonant (info[i])) + { + for (unsigned int j = last + 1; j < i; j++) + if (info[j].indic_position() < POS_SMVD) + info[j].indic_position() = info[i].indic_position(); + last = i; + } else if (info[i].indic_category() == I_Cat(M)) + last = i; + } + + + { + /* Use syllable() for sort accounting temporarily. */ + unsigned int syllable = info[start].syllable(); + for (unsigned int i = start; i < end; i++) + info[i].syllable() = i - start; + + /* Sit tight, rock 'n roll! */ + hb_stable_sort (info + start, end - start, compare_indic_order); + /* Find base again */ + base = end; + for (unsigned int i = start; i < end; i++) + if (info[i].indic_position() == POS_BASE_C) + { + base = i; + break; + } + /* Things are out-of-control for post base positions, they may shuffle + * around like crazy. In old-spec mode, we move halants around, so in + * that case merge all clusters after base. Otherwise, check the sort + * order and merge as needed. + * For pre-base stuff, we handle cluster issues in final reordering. + * + * We could use buffer->sort() for this, if there was no special + * reordering of pre-base stuff happening later... + * We don't want to merge_clusters all of that, which buffer->sort() + * would. Here's a concrete example: + * + * Assume there's a pre-base consonant and explicit Halant before base, + * followed by a prebase-reordering (left) Matra: + * + * C,H,ZWNJ,B,M + * + * At this point in reordering we would have: + * + * M,C,H,ZWNJ,B + * + * whereas in final reordering we will bring the Matra closer to Base: + * + * C,H,ZWNJ,M,B + * + * That's why we don't want to merge-clusters anything before the Base + * at this point. But if something moved from after Base to before it, + * we should merge clusters from base to them. In final-reordering, we + * only move things around before base, and merge-clusters up to base. + * These two merge-clusters from the two sides of base will interlock + * to merge things correctly. See: + * https://github.com/harfbuzz/harfbuzz/issues/2272 + */ + if (indic_plan->is_old_spec || end - start > 127) + buffer->merge_clusters (base, end); + else + { + /* Note! syllable() is a one-byte field. */ + for (unsigned int i = base; i < end; i++) + if (info[i].syllable() != 255) + { + unsigned int min = i; + unsigned int max = i; + unsigned int j = start + info[i].syllable(); + while (j != i) + { + min = hb_min (min, j); + max = hb_max (max, j); + unsigned int next = start + info[j].syllable(); + info[j].syllable() = 255; /* So we don't process j later again. */ + j = next; + } + buffer->merge_clusters (hb_max (base, min), max + 1); + } + } + + /* Put syllable back in. */ + for (unsigned int i = start; i < end; i++) + info[i].syllable() = syllable; + } + + /* Setup masks now */ + + { + hb_mask_t mask; + + /* Reph */ + for (unsigned int i = start; i < end && info[i].indic_position() == POS_RA_TO_BECOME_REPH; i++) + info[i].mask |= indic_plan->mask_array[INDIC_RPHF]; + + /* Pre-base */ + mask = indic_plan->mask_array[INDIC_HALF]; + if (!indic_plan->is_old_spec && + indic_plan->config->blwf_mode == BLWF_MODE_PRE_AND_POST) + mask |= indic_plan->mask_array[INDIC_BLWF]; + for (unsigned int i = start; i < base; i++) + info[i].mask |= mask; + /* Base */ + mask = 0; + if (base < end) + info[base].mask |= mask; + /* Post-base */ + mask = indic_plan->mask_array[INDIC_BLWF] | + indic_plan->mask_array[INDIC_ABVF] | + indic_plan->mask_array[INDIC_PSTF]; + for (unsigned int i = base + 1; i < end; i++) + info[i].mask |= mask; + } + + if (indic_plan->is_old_spec && + buffer->props.script == HB_SCRIPT_DEVANAGARI) + { + /* Old-spec eye-lash Ra needs special handling. From the + * spec: + * + * "The feature 'below-base form' is applied to consonants + * having below-base forms and following the base consonant. + * The exception is vattu, which may appear below half forms + * as well as below the base glyph. The feature 'below-base + * form' will be applied to all such occurrences of Ra as well." + * + * Test case: U+0924,U+094D,U+0930,U+094d,U+0915 + * with Sanskrit 2003 font. + * + * However, note that Ra,Halant,ZWJ is the correct way to + * request eyelash form of Ra, so we wouldbn't inhibit it + * in that sequence. + * + * Test case: U+0924,U+094D,U+0930,U+094d,U+200D,U+0915 + */ + for (unsigned int i = start; i + 1 < base; i++) + if (info[i ].indic_category() == I_Cat(Ra) && + info[i+1].indic_category() == I_Cat(H) && + (i + 2 == base || + info[i+2].indic_category() != I_Cat(ZWJ))) + { + info[i ].mask |= indic_plan->mask_array[INDIC_BLWF]; + info[i+1].mask |= indic_plan->mask_array[INDIC_BLWF]; + } + } + + unsigned int pref_len = 2; + if (indic_plan->mask_array[INDIC_PREF] && base + pref_len < end) + { + /* Find a Halant,Ra sequence and mark it for pre-base-reordering processing. */ + for (unsigned int i = base + 1; i + pref_len - 1 < end; i++) { + hb_codepoint_t glyphs[2]; + for (unsigned int j = 0; j < pref_len; j++) + glyphs[j] = info[i + j].codepoint; + if (indic_plan->pref.would_substitute (glyphs, pref_len, face)) + { + for (unsigned int j = 0; j < pref_len; j++) + info[i++].mask |= indic_plan->mask_array[INDIC_PREF]; + break; + } + } + } + + /* Apply ZWJ/ZWNJ effects */ + for (unsigned int i = start + 1; i < end; i++) + if (is_joiner (info[i])) { + bool non_joiner = info[i].indic_category() == I_Cat(ZWNJ); + unsigned int j = i; + + do { + j--; + + /* ZWJ/ZWNJ should disable CJCT. They do that by simply + * being there, since we don't skip them for the CJCT + * feature (ie. F_MANUAL_ZWJ) */ + + /* A ZWNJ disables HALF. */ + if (non_joiner) + info[j].mask &= ~indic_plan->mask_array[INDIC_HALF]; + + } while (j > start && !is_consonant (info[j])); + } +} + +static void +initial_reordering_standalone_cluster (const hb_ot_shape_plan_t *plan, + hb_face_t *face, + hb_buffer_t *buffer, + unsigned int start, unsigned int end) +{ + /* We treat placeholder/dotted-circle as if they are consonants, so we + * should just chain. Only if not in compatibility mode that is... */ + + const indic_shape_plan_t *indic_plan = (const indic_shape_plan_t *) plan->data; + if (indic_plan->uniscribe_bug_compatible) + { + /* For dotted-circle, this is what Uniscribe does: + * If dotted-circle is the last glyph, it just does nothing. + * Ie. It doesn't form Reph. */ + if (buffer->info[end - 1].indic_category() == I_Cat(DOTTEDCIRCLE)) + return; + } + + initial_reordering_consonant_syllable (plan, face, buffer, start, end); +} + +static void +initial_reordering_syllable_indic (const hb_ot_shape_plan_t *plan, + hb_face_t *face, + hb_buffer_t *buffer, + unsigned int start, unsigned int end) +{ + indic_syllable_type_t syllable_type = (indic_syllable_type_t) (buffer->info[start].syllable() & 0x0F); + switch (syllable_type) + { + case indic_vowel_syllable: /* We made the vowels look like consonants. So let's call the consonant logic! */ + case indic_consonant_syllable: + initial_reordering_consonant_syllable (plan, face, buffer, start, end); + break; + + case indic_broken_cluster: /* We already inserted dotted-circles, so just call the standalone_cluster. */ + case indic_standalone_cluster: + initial_reordering_standalone_cluster (plan, face, buffer, start, end); + break; + + case indic_symbol_cluster: + case indic_non_indic_cluster: + break; + } +} + +static void +initial_reordering_indic (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer) +{ + if (!buffer->message (font, "start reordering indic initial")) + return; + + update_consonant_positions_indic (plan, font, buffer); + hb_syllabic_insert_dotted_circles (font, buffer, + indic_broken_cluster, + I_Cat(DOTTEDCIRCLE), + I_Cat(Repha), + POS_END); + + foreach_syllable (buffer, start, end) + initial_reordering_syllable_indic (plan, font->face, buffer, start, end); + + (void) buffer->message (font, "end reordering indic initial"); +} + +static void +final_reordering_syllable_indic (const hb_ot_shape_plan_t *plan, + hb_buffer_t *buffer, + unsigned int start, unsigned int end) +{ + const indic_shape_plan_t *indic_plan = (const indic_shape_plan_t *) plan->data; + hb_glyph_info_t *info = buffer->info; + + + /* This function relies heavily on halant glyphs. Lots of ligation + * and possibly multiple substitutions happened prior to this + * phase, and that might have messed up our properties. Recover + * from a particular case of that where we're fairly sure that a + * class of I_Cat(H) is desired but has been lost. */ + /* We don't call load_virama_glyph(), since we know it's already + * loaded. */ + hb_codepoint_t virama_glyph = indic_plan->virama_glyph; + if (virama_glyph) + { + for (unsigned int i = start; i < end; i++) + if (info[i].codepoint == virama_glyph && + _hb_glyph_info_ligated (&info[i]) && + _hb_glyph_info_multiplied (&info[i])) + { + /* This will make sure that this glyph passes is_halant() test. */ + info[i].indic_category() = I_Cat(H); + _hb_glyph_info_clear_ligated_and_multiplied (&info[i]); + } + } + + + /* 4. Final reordering: + * + * After the localized forms and basic shaping forms GSUB features have been + * applied (see below), the shaping engine performs some final glyph + * reordering before applying all the remaining font features to the entire + * syllable. + */ + + bool try_pref = !!indic_plan->mask_array[INDIC_PREF]; + + /* Find base again */ + unsigned int base; + for (base = start; base < end; base++) + if (info[base].indic_position() >= POS_BASE_C) + { + if (try_pref && base + 1 < end) + { + for (unsigned int i = base + 1; i < end; i++) + if ((info[i].mask & indic_plan->mask_array[INDIC_PREF]) != 0) + { + if (!(_hb_glyph_info_substituted (&info[i]) && + _hb_glyph_info_ligated_and_didnt_multiply (&info[i]))) + { + /* Ok, this was a 'pref' candidate but didn't form any. + * Base is around here... */ + base = i; + while (base < end && is_halant (info[base])) + base++; + info[base].indic_position() = POS_BASE_C; + + try_pref = false; + } + break; + } + } + /* For Malayalam, skip over unformed below- (but NOT post-) forms. */ + if (buffer->props.script == HB_SCRIPT_MALAYALAM) + { + for (unsigned int i = base + 1; i < end; i++) + { + while (i < end && is_joiner (info[i])) + i++; + if (i == end || !is_halant (info[i])) + break; + i++; /* Skip halant. */ + while (i < end && is_joiner (info[i])) + i++; + if (i < end && is_consonant (info[i]) && info[i].indic_position() == POS_BELOW_C) + { + base = i; + info[base].indic_position() = POS_BASE_C; + } + } + } + + if (start < base && info[base].indic_position() > POS_BASE_C) + base--; + break; + } + if (base == end && start < base && + is_one_of (info[base - 1], FLAG (I_Cat(ZWJ)))) + base--; + if (base < end) + while (start < base && + is_one_of (info[base], (FLAG (I_Cat(N)) | FLAG (I_Cat(H))))) + base--; + + + /* o Reorder matras: + * + * If a pre-base matra character had been reordered before applying basic + * features, the glyph can be moved closer to the main consonant based on + * whether half-forms had been formed. Actual position for the matra is + * defined as “after last standalone halant glyph, after initial matra + * position and before the main consonant”. If ZWJ or ZWNJ follow this + * halant, position is moved after it. + * + * IMPLEMENTATION NOTES: + * + * It looks like the last sentence is wrong. Testing, with Windows 7 Uniscribe + * and Devanagari shows that the behavior is best described as: + * + * "If ZWJ follows this halant, matra is NOT repositioned after this halant. + * If ZWNJ follows this halant, position is moved after it." + * + * Test case, with Adobe Devanagari or Nirmala UI: + * + * U+091F,U+094D,U+200C,U+092F,U+093F + * (Matra moves to the middle, after ZWNJ.) + * + * U+091F,U+094D,U+200D,U+092F,U+093F + * (Matra does NOT move, stays to the left.) + * + * https://github.com/harfbuzz/harfbuzz/issues/1070 + */ + + if (start + 1 < end && start < base) /* Otherwise there can't be any pre-base matra characters. */ + { + /* If we lost track of base, alas, position before last thingy. */ + unsigned int new_pos = base == end ? base - 2 : base - 1; + + /* Malayalam / Tamil do not have "half" forms or explicit virama forms. + * The glyphs formed by 'half' are Chillus or ligated explicit viramas. + * We want to position matra after them. + */ + if (buffer->props.script != HB_SCRIPT_MALAYALAM && buffer->props.script != HB_SCRIPT_TAMIL) + { + search: + while (new_pos > start && + !(is_one_of (info[new_pos], (FLAG (I_Cat(M)) | FLAG (I_Cat(H)))))) + new_pos--; + + /* If we found no Halant we are done. + * Otherwise only proceed if the Halant does + * not belong to the Matra itself! */ + if (is_halant (info[new_pos]) && + info[new_pos].indic_position() != POS_PRE_M) + { +#if 0 // See comment above + /* -> If ZWJ or ZWNJ follow this halant, position is moved after it. */ + if (new_pos + 1 < end && is_joiner (info[new_pos + 1])) + new_pos++; +#endif + if (new_pos + 1 < end) + { + /* -> If ZWJ follows this halant, matra is NOT repositioned after this halant. */ + if (info[new_pos + 1].indic_category() == I_Cat(ZWJ)) + { + /* Keep searching. */ + if (new_pos > start) + { + new_pos--; + goto search; + } + } + /* -> If ZWNJ follows this halant, position is moved after it. + * + * IMPLEMENTATION NOTES: + * + * This is taken care of by the state-machine. A Halant,ZWNJ is a terminating + * sequence for a consonant syllable; any pre-base matras occurring after it + * will belong to the subsequent syllable. + */ + } + } + else + new_pos = start; /* No move. */ + } + + if (start < new_pos && info[new_pos].indic_position () != POS_PRE_M) + { + /* Now go see if there's actually any matras... */ + for (unsigned int i = new_pos; i > start; i--) + if (info[i - 1].indic_position () == POS_PRE_M) + { + unsigned int old_pos = i - 1; + if (old_pos < base && base <= new_pos) /* Shouldn't actually happen. */ + base--; + + hb_glyph_info_t tmp = info[old_pos]; + memmove (&info[old_pos], &info[old_pos + 1], (new_pos - old_pos) * sizeof (info[0])); + info[new_pos] = tmp; + + /* Note: this merge_clusters() is intentionally *after* the reordering. + * Indic matra reordering is special and tricky... */ + buffer->merge_clusters (new_pos, hb_min (end, base + 1)); + + new_pos--; + } + } else { + for (unsigned int i = start; i < base; i++) + if (info[i].indic_position () == POS_PRE_M) { + buffer->merge_clusters (i, hb_min (end, base + 1)); + break; + } + } + } + + + /* o Reorder reph: + * + * Reph’s original position is always at the beginning of the syllable, + * (i.e. it is not reordered at the character reordering stage). However, + * it will be reordered according to the basic-forms shaping results. + * Possible positions for reph, depending on the script, are; after main, + * before post-base consonant forms, and after post-base consonant forms. + */ + + /* Two cases: + * + * - If repha is encoded as a sequence of characters (Ra,H or Ra,H,ZWJ), then + * we should only move it if the sequence ligated to the repha form. + * + * - If repha is encoded separately and in the logical position, we should only + * move it if it did NOT ligate. If it ligated, it's probably the font trying + * to make it work without the reordering. + */ + if (start + 1 < end && + info[start].indic_position() == POS_RA_TO_BECOME_REPH && + ((info[start].indic_category() == I_Cat(Repha)) ^ + _hb_glyph_info_ligated_and_didnt_multiply (&info[start]))) + { + unsigned int new_reph_pos; + reph_position_t reph_pos = indic_plan->config->reph_pos; + + /* 1. If reph should be positioned after post-base consonant forms, + * proceed to step 5. + */ + if (reph_pos == REPH_POS_AFTER_POST) + { + goto reph_step_5; + } + + /* 2. If the reph repositioning class is not after post-base: target + * position is after the first explicit halant glyph between the + * first post-reph consonant and last main consonant. If ZWJ or ZWNJ + * are following this halant, position is moved after it. If such + * position is found, this is the target position. Otherwise, + * proceed to the next step. + * + * Note: in old-implementation fonts, where classifications were + * fixed in shaping engine, there was no case where reph position + * will be found on this step. + */ + { + new_reph_pos = start + 1; + while (new_reph_pos < base && !is_halant (info[new_reph_pos])) + new_reph_pos++; + + if (new_reph_pos < base && is_halant (info[new_reph_pos])) + { + /* ->If ZWJ or ZWNJ are following this halant, position is moved after it. */ + if (new_reph_pos + 1 < base && is_joiner (info[new_reph_pos + 1])) + new_reph_pos++; + goto reph_move; + } + } + + /* 3. If reph should be repositioned after the main consonant: find the + * first consonant not ligated with main, or find the first + * consonant that is not a potential pre-base-reordering Ra. + */ + if (reph_pos == REPH_POS_AFTER_MAIN) + { + new_reph_pos = base; + while (new_reph_pos + 1 < end && info[new_reph_pos + 1].indic_position() <= POS_AFTER_MAIN) + new_reph_pos++; + if (new_reph_pos < end) + goto reph_move; + } + + /* 4. If reph should be positioned before post-base consonant, find + * first post-base classified consonant not ligated with main. If no + * consonant is found, the target position should be before the + * first matra, syllable modifier sign or vedic sign. + */ + /* This is our take on what step 4 is trying to say (and failing, BADLY). */ + if (reph_pos == REPH_POS_AFTER_SUB) + { + new_reph_pos = base; + while (new_reph_pos + 1 < end && + !( FLAG_UNSAFE (info[new_reph_pos + 1].indic_position()) & (FLAG (POS_POST_C) | FLAG (POS_AFTER_POST) | FLAG (POS_SMVD)))) + new_reph_pos++; + if (new_reph_pos < end) + goto reph_move; + } + + /* 5. If no consonant is found in steps 3 or 4, move reph to a position + * immediately before the first post-base matra, syllable modifier + * sign or vedic sign that has a reordering class after the intended + * reph position. For example, if the reordering position for reph + * is post-main, it will skip above-base matras that also have a + * post-main position. + */ + reph_step_5: + { + /* Copied from step 2. */ + new_reph_pos = start + 1; + while (new_reph_pos < base && !is_halant (info[new_reph_pos])) + new_reph_pos++; + + if (new_reph_pos < base && is_halant (info[new_reph_pos])) + { + /* ->If ZWJ or ZWNJ are following this halant, position is moved after it. */ + if (new_reph_pos + 1 < base && is_joiner (info[new_reph_pos + 1])) + new_reph_pos++; + goto reph_move; + } + } + /* See https://github.com/harfbuzz/harfbuzz/issues/2298#issuecomment-615318654 */ + + /* 6. Otherwise, reorder reph to the end of the syllable. + */ + { + new_reph_pos = end - 1; + while (new_reph_pos > start && info[new_reph_pos].indic_position() == POS_SMVD) + new_reph_pos--; + + /* + * If the Reph is to be ending up after a Matra,Halant sequence, + * position it before that Halant so it can interact with the Matra. + * However, if it's a plain Consonant,Halant we shouldn't do that. + * Uniscribe doesn't do this. + * TEST: U+0930,U+094D,U+0915,U+094B,U+094D + */ + if (!indic_plan->uniscribe_bug_compatible && + unlikely (is_halant (info[new_reph_pos]))) + { + for (unsigned int i = base + 1; i < new_reph_pos; i++) + if (info[i].indic_category() == I_Cat(M)) { + /* Ok, got it. */ + new_reph_pos--; + } + } + + goto reph_move; + } + + reph_move: + { + /* Move */ + buffer->merge_clusters (start, new_reph_pos + 1); + hb_glyph_info_t reph = info[start]; + memmove (&info[start], &info[start + 1], (new_reph_pos - start) * sizeof (info[0])); + info[new_reph_pos] = reph; + + if (start < base && base <= new_reph_pos) + base--; + } + } + + + /* o Reorder pre-base-reordering consonants: + * + * If a pre-base-reordering consonant is found, reorder it according to + * the following rules: + */ + + if (try_pref && base + 1 < end) /* Otherwise there can't be any pre-base-reordering Ra. */ + { + for (unsigned int i = base + 1; i < end; i++) + if ((info[i].mask & indic_plan->mask_array[INDIC_PREF]) != 0) + { + /* 1. Only reorder a glyph produced by substitution during application + * of the feature. (Note that a font may shape a Ra consonant with + * the feature generally but block it in certain contexts.) + */ + /* Note: We just check that something got substituted. We don't check that + * the feature actually did it... + * + * Reorder pref only if it ligated. */ + if (_hb_glyph_info_ligated_and_didnt_multiply (&info[i])) + { + /* + * 2. Try to find a target position the same way as for pre-base matra. + * If it is found, reorder pre-base consonant glyph. + * + * 3. If position is not found, reorder immediately before main + * consonant. + */ + + unsigned int new_pos = base; + /* Malayalam / Tamil do not have "half" forms or explicit virama forms. + * The glyphs formed by 'half' are Chillus or ligated explicit viramas. + * We want to position matra after them. + */ + if (buffer->props.script != HB_SCRIPT_MALAYALAM && buffer->props.script != HB_SCRIPT_TAMIL) + { + while (new_pos > start && + !(is_one_of (info[new_pos - 1], FLAG(I_Cat(M)) | FLAG (I_Cat(H))))) + new_pos--; + } + + if (new_pos > start && is_halant (info[new_pos - 1])) + { + /* -> If ZWJ or ZWNJ follow this halant, position is moved after it. */ + if (new_pos < end && is_joiner (info[new_pos])) + new_pos++; + } + + { + unsigned int old_pos = i; + + buffer->merge_clusters (new_pos, old_pos + 1); + hb_glyph_info_t tmp = info[old_pos]; + memmove (&info[new_pos + 1], &info[new_pos], (old_pos - new_pos) * sizeof (info[0])); + info[new_pos] = tmp; + + if (new_pos <= base && base < old_pos) + base++; + } + } + + break; + } + } + + + /* Apply 'init' to the Left Matra if it's a word start. */ + if (info[start].indic_position () == POS_PRE_M) + { + if (!start || + !(FLAG_UNSAFE (_hb_glyph_info_get_general_category (&info[start - 1])) & + FLAG_RANGE (HB_UNICODE_GENERAL_CATEGORY_FORMAT, HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK))) + info[start].mask |= indic_plan->mask_array[INDIC_INIT]; + else + buffer->unsafe_to_break (start - 1, start + 1); + } + + + /* + * Finish off the clusters and go home! + */ + if (indic_plan->uniscribe_bug_compatible) + { + switch ((hb_tag_t) plan->props.script) + { + case HB_SCRIPT_TAMIL: + break; + + default: + /* Uniscribe merges the entire syllable into a single cluster... Except for Tamil. + * This means, half forms are submerged into the main consonant's cluster. + * This is unnecessary, and makes cursor positioning harder, but that's what + * Uniscribe does. */ + buffer->merge_clusters (start, end); + break; + } + } +} + + +static void +final_reordering_indic (const hb_ot_shape_plan_t *plan, + hb_font_t *font HB_UNUSED, + hb_buffer_t *buffer) +{ + unsigned int count = buffer->len; + if (unlikely (!count)) return; + + if (buffer->message (font, "start reordering indic final")) { + foreach_syllable (buffer, start, end) + final_reordering_syllable_indic (plan, buffer, start, end); + (void) buffer->message (font, "end reordering indic final"); + } + + HB_BUFFER_DEALLOCATE_VAR (buffer, indic_category); + HB_BUFFER_DEALLOCATE_VAR (buffer, indic_position); +} + + +static void +preprocess_text_indic (const hb_ot_shape_plan_t *plan, + hb_buffer_t *buffer, + hb_font_t *font) +{ + const indic_shape_plan_t *indic_plan = (const indic_shape_plan_t *) plan->data; + if (!indic_plan->uniscribe_bug_compatible) + _hb_preprocess_text_vowel_constraints (plan, buffer, font); +} + +static bool +decompose_indic (const hb_ot_shape_normalize_context_t *c, + hb_codepoint_t ab, + hb_codepoint_t *a, + hb_codepoint_t *b) +{ + switch (ab) + { + /* Don't decompose these. */ + case 0x0931u : return false; /* DEVANAGARI LETTER RRA */ + // https://github.com/harfbuzz/harfbuzz/issues/779 + case 0x09DCu : return false; /* BENGALI LETTER RRA */ + case 0x09DDu : return false; /* BENGALI LETTER RHA */ + case 0x0B94u : return false; /* TAMIL LETTER AU */ + + + /* + * Decompose split matras that don't have Unicode decompositions. + */ + +#if 0 + /* Gujarati */ + /* This one has no decomposition in Unicode, but needs no decomposition either. */ + /* case 0x0AC9u : return false; */ + + /* Oriya */ + case 0x0B57u : *a = no decomp, -> RIGHT; return true; +#endif + } + + return (bool) c->unicode->decompose (ab, a, b); +} + +static bool +compose_indic (const hb_ot_shape_normalize_context_t *c, + hb_codepoint_t a, + hb_codepoint_t b, + hb_codepoint_t *ab) +{ + /* Avoid recomposing split matras. */ + if (HB_UNICODE_GENERAL_CATEGORY_IS_MARK (c->unicode->general_category (a))) + return false; + + /* Composition-exclusion exceptions that we want to recompose. */ + if (a == 0x09AFu && b == 0x09BCu) { *ab = 0x09DFu; return true; } + + return (bool) c->unicode->compose (a, b, ab); +} + + +const hb_ot_shaper_t _hb_ot_shaper_indic = +{ + collect_features_indic, + override_features_indic, + data_create_indic, + data_destroy_indic, + preprocess_text_indic, + nullptr, /* postprocess_glyphs */ + decompose_indic, + compose_indic, + setup_masks_indic, + nullptr, /* reorder_marks */ + HB_TAG_NONE, /* gpos_tag */ + HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT, + HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE, + false, /* fallback_position */ +}; + + +#endif diff --git a/libs/harfbuzz/src/hb-ot-shaper-indic.hh b/libs/harfbuzz/src/hb-ot-shaper-indic.hh new file mode 100644 index 00000000..4f822c26 --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-shaper-indic.hh @@ -0,0 +1,66 @@ +/* + * Copyright © 2012 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 HB_OT_SHAPER_INDIC_HH +#define HB_OT_SHAPER_INDIC_HH + +#include "hb.hh" + +#include "hb-ot-shaper-syllabic.hh" + + +/* Visual positions in a syllable from left to right. */ +enum ot_position_t { + POS_START = 0, + + POS_RA_TO_BECOME_REPH = 1, + POS_PRE_M = 2, + POS_PRE_C = 3, + + POS_BASE_C = 4, + POS_AFTER_MAIN = 5, + + POS_ABOVE_C = 6, + + POS_BEFORE_SUB = 7, + POS_BELOW_C = 8, + POS_AFTER_SUB = 9, + + POS_BEFORE_POST = 10, + POS_POST_C = 11, + POS_AFTER_POST = 12, + + POS_SMVD = 13, + + POS_END = 14 +}; + + +HB_INTERNAL uint16_t +hb_indic_get_categories (hb_codepoint_t u); + + +#endif /* HB_OT_SHAPER_INDIC_HH */ diff --git a/libs/harfbuzz/src/hb-ot-shaper-khmer-machine.hh b/libs/harfbuzz/src/hb-ot-shaper-khmer-machine.hh new file mode 100644 index 00000000..e18bd75e --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-shaper-khmer-machine.hh @@ -0,0 +1,428 @@ + +#line 1 "hb-ot-shaper-khmer-machine.rl" +/* + * Copyright © 2011,2012 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 HB_OT_SHAPER_KHMER_MACHINE_HH +#define HB_OT_SHAPER_KHMER_MACHINE_HH + +#include "hb.hh" + +#include "hb-ot-layout.hh" +#include "hb-ot-shaper-indic.hh" + +/* buffer var allocations */ +#define khmer_category() ot_shaper_var_u8_category() /* khmer_category_t */ + +using khmer_category_t = unsigned; + +#define K_Cat(Cat) khmer_syllable_machine_ex_##Cat + +enum khmer_syllable_type_t { + khmer_consonant_syllable, + khmer_broken_cluster, + khmer_non_khmer_cluster, +}; + + +#line 52 "hb-ot-shaper-khmer-machine.hh" +#define khmer_syllable_machine_ex_C 1u +#define khmer_syllable_machine_ex_DOTTEDCIRCLE 11u +#define khmer_syllable_machine_ex_H 4u +#define khmer_syllable_machine_ex_PLACEHOLDER 10u +#define khmer_syllable_machine_ex_Ra 15u +#define khmer_syllable_machine_ex_Robatic 25u +#define khmer_syllable_machine_ex_V 2u +#define khmer_syllable_machine_ex_VAbv 20u +#define khmer_syllable_machine_ex_VBlw 21u +#define khmer_syllable_machine_ex_VPre 22u +#define khmer_syllable_machine_ex_VPst 23u +#define khmer_syllable_machine_ex_Xgroup 26u +#define khmer_syllable_machine_ex_Ygroup 27u +#define khmer_syllable_machine_ex_ZWJ 6u +#define khmer_syllable_machine_ex_ZWNJ 5u + + +#line 70 "hb-ot-shaper-khmer-machine.hh" +static const unsigned char _khmer_syllable_machine_trans_keys[] = { + 5u, 26u, 5u, 26u, 1u, 15u, 5u, 26u, 5u, 26u, 5u, 26u, 5u, 26u, 5u, 26u, + 5u, 26u, 5u, 26u, 5u, 26u, 5u, 26u, 5u, 26u, 1u, 15u, 5u, 26u, 5u, 26u, + 5u, 26u, 5u, 26u, 5u, 26u, 5u, 26u, 5u, 26u, 1u, 27u, 4u, 27u, 1u, 15u, + 4u, 27u, 4u, 27u, 27u, 27u, 4u, 27u, 4u, 27u, 4u, 27u, 4u, 27u, 4u, 27u, + 4u, 27u, 1u, 15u, 4u, 27u, 4u, 27u, 27u, 27u, 4u, 27u, 4u, 27u, 4u, 27u, + 4u, 27u, 4u, 27u, 5u, 26u, 0 +}; + +static const char _khmer_syllable_machine_key_spans[] = { + 22, 22, 15, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 15, 22, 22, + 22, 22, 22, 22, 22, 27, 24, 15, + 24, 24, 1, 24, 24, 24, 24, 24, + 24, 15, 24, 24, 1, 24, 24, 24, + 24, 24, 22 +}; + +static const short _khmer_syllable_machine_index_offsets[] = { + 0, 23, 46, 62, 85, 108, 131, 154, + 177, 200, 223, 246, 269, 292, 308, 331, + 354, 377, 400, 423, 446, 469, 497, 522, + 538, 563, 588, 590, 615, 640, 665, 690, + 715, 740, 756, 781, 806, 808, 833, 858, + 883, 908, 933 +}; + +static const char _khmer_syllable_machine_indicies[] = { + 1, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 2, + 0, 0, 0, 0, 3, 4, 0, 1, + 1, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 4, 0, 5, 5, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 5, 0, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 2, 0, 0, + 0, 0, 0, 4, 0, 6, 6, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 2, 0, 7, 7, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 8, 0, 9, 9, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 2, 0, 0, 0, 0, 0, + 10, 0, 9, 9, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 10, + 0, 11, 11, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 2, 0, 0, 0, 0, 0, 12, 0, + 11, 11, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 12, 0, 1, + 1, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 2, 0, + 0, 0, 0, 13, 4, 0, 15, 15, + 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 16, 14, 14, + 14, 14, 17, 18, 14, 15, 15, 19, + 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 18, 19, 20, 20, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 20, 14, 15, 15, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 16, 14, 14, 14, 14, + 14, 18, 14, 21, 21, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, + 16, 14, 22, 22, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 23, + 14, 24, 24, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, + 16, 14, 14, 14, 14, 14, 25, 14, + 24, 24, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 25, 14, 26, + 26, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 16, 14, + 14, 14, 14, 14, 27, 14, 26, 26, + 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 27, 14, 29, 29, 28, + 30, 31, 31, 28, 28, 28, 13, 13, + 28, 28, 28, 29, 28, 28, 28, 28, + 16, 25, 27, 23, 28, 17, 18, 20, + 28, 33, 34, 34, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, + 32, 2, 10, 12, 8, 32, 13, 4, + 5, 32, 35, 35, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, + 35, 32, 33, 36, 36, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 2, 10, 12, 8, 32, 3, + 4, 5, 32, 37, 38, 38, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 2, 10, 12, 8, 32, + 32, 4, 5, 32, 5, 32, 37, 6, + 6, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, + 32, 8, 32, 32, 2, 5, 32, 37, + 7, 7, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 8, 5, 32, + 37, 39, 39, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, + 2, 32, 32, 8, 32, 32, 10, 5, + 32, 37, 40, 40, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, + 32, 2, 10, 32, 8, 32, 32, 12, + 5, 32, 33, 38, 38, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 2, 10, 12, 8, 32, 32, + 4, 5, 32, 33, 38, 38, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 2, 10, 12, 8, 32, + 3, 4, 5, 32, 42, 42, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 42, 41, 30, 43, 43, 41, + 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 16, 25, 27, 23, + 41, 17, 18, 20, 41, 44, 45, 45, + 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 16, 25, 27, + 23, 41, 41, 18, 20, 41, 20, 41, + 44, 21, 21, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 23, 41, 41, 16, 20, + 41, 44, 22, 22, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 23, + 20, 41, 44, 46, 46, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 16, 41, 41, 23, 41, 41, + 25, 20, 41, 44, 47, 47, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 16, 25, 41, 23, 41, + 41, 27, 20, 41, 30, 45, 45, 41, + 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 16, 25, 27, 23, + 41, 41, 18, 20, 41, 15, 15, 48, + 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 16, 48, 48, 48, + 48, 48, 18, 48, 0 +}; + +static const char _khmer_syllable_machine_trans_targs[] = { + 21, 1, 27, 31, 25, 26, 4, 5, + 28, 7, 29, 9, 30, 32, 21, 12, + 37, 41, 35, 21, 36, 15, 16, 38, + 18, 39, 20, 40, 21, 22, 33, 42, + 21, 23, 10, 24, 0, 2, 3, 6, + 8, 21, 34, 11, 13, 14, 17, 19, + 21 +}; + +static const char _khmer_syllable_machine_trans_actions[] = { + 1, 0, 2, 2, 2, 0, 0, 0, + 2, 0, 2, 0, 2, 2, 3, 0, + 2, 4, 4, 5, 0, 0, 0, 2, + 0, 2, 0, 2, 8, 2, 0, 9, + 10, 0, 0, 2, 0, 0, 0, 0, + 0, 11, 4, 0, 0, 0, 0, 0, + 12 +}; + +static const char _khmer_syllable_machine_to_state_actions[] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 6, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0 +}; + +static const char _khmer_syllable_machine_from_state_actions[] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 7, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0 +}; + +static const short _khmer_syllable_machine_eof_trans[] = { + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 15, 20, 15, 15, 15, + 15, 15, 15, 15, 15, 0, 33, 33, + 33, 33, 33, 33, 33, 33, 33, 33, + 33, 42, 42, 42, 42, 42, 42, 42, + 42, 42, 49 +}; + +static const int khmer_syllable_machine_start = 21; +static const int khmer_syllable_machine_first_final = 21; +static const int khmer_syllable_machine_error = -1; + +static const int khmer_syllable_machine_en_main = 21; + + +#line 53 "hb-ot-shaper-khmer-machine.rl" + + + +#line 102 "hb-ot-shaper-khmer-machine.rl" + + +#define found_syllable(syllable_type) \ + HB_STMT_START { \ + if (0) fprintf (stderr, "syllable %d..%d %s\n", ts, te, #syllable_type); \ + for (unsigned int i = ts; i < te; i++) \ + info[i].syllable() = (syllable_serial << 4) | syllable_type; \ + syllable_serial++; \ + if (unlikely (syllable_serial == 16)) syllable_serial = 1; \ + } HB_STMT_END + +inline void +find_syllables_khmer (hb_buffer_t *buffer) +{ + unsigned int p, pe, eof, ts, te, act HB_UNUSED; + int cs; + hb_glyph_info_t *info = buffer->info; + +#line 298 "hb-ot-shaper-khmer-machine.hh" + { + cs = khmer_syllable_machine_start; + ts = 0; + te = 0; + act = 0; + } + +#line 122 "hb-ot-shaper-khmer-machine.rl" + + + p = 0; + pe = eof = buffer->len; + + unsigned int syllable_serial = 1; + +#line 314 "hb-ot-shaper-khmer-machine.hh" + { + int _slen; + int _trans; + const unsigned char *_keys; + const char *_inds; + if ( p == pe ) + goto _test_eof; +_resume: + switch ( _khmer_syllable_machine_from_state_actions[cs] ) { + case 7: +#line 1 "NONE" + {ts = p;} + break; +#line 328 "hb-ot-shaper-khmer-machine.hh" + } + + _keys = _khmer_syllable_machine_trans_keys + (cs<<1); + _inds = _khmer_syllable_machine_indicies + _khmer_syllable_machine_index_offsets[cs]; + + _slen = _khmer_syllable_machine_key_spans[cs]; + _trans = _inds[ _slen > 0 && _keys[0] <=( info[p].khmer_category()) && + ( info[p].khmer_category()) <= _keys[1] ? + ( info[p].khmer_category()) - _keys[0] : _slen ]; + +_eof_trans: + cs = _khmer_syllable_machine_trans_targs[_trans]; + + if ( _khmer_syllable_machine_trans_actions[_trans] == 0 ) + goto _again; + + switch ( _khmer_syllable_machine_trans_actions[_trans] ) { + case 2: +#line 1 "NONE" + {te = p+1;} + break; + case 8: +#line 98 "hb-ot-shaper-khmer-machine.rl" + {te = p+1;{ found_syllable (khmer_non_khmer_cluster); }} + break; + case 10: +#line 96 "hb-ot-shaper-khmer-machine.rl" + {te = p;p--;{ found_syllable (khmer_consonant_syllable); }} + break; + case 11: +#line 97 "hb-ot-shaper-khmer-machine.rl" + {te = p;p--;{ found_syllable (khmer_broken_cluster); buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_BROKEN_SYLLABLE; }} + break; + case 12: +#line 98 "hb-ot-shaper-khmer-machine.rl" + {te = p;p--;{ found_syllable (khmer_non_khmer_cluster); }} + break; + case 1: +#line 96 "hb-ot-shaper-khmer-machine.rl" + {{p = ((te))-1;}{ found_syllable (khmer_consonant_syllable); }} + break; + case 3: +#line 97 "hb-ot-shaper-khmer-machine.rl" + {{p = ((te))-1;}{ found_syllable (khmer_broken_cluster); buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_BROKEN_SYLLABLE; }} + break; + case 5: +#line 1 "NONE" + { switch( act ) { + case 2: + {{p = ((te))-1;} found_syllable (khmer_broken_cluster); buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_BROKEN_SYLLABLE; } + break; + case 3: + {{p = ((te))-1;} found_syllable (khmer_non_khmer_cluster); } + break; + } + } + break; + case 4: +#line 1 "NONE" + {te = p+1;} +#line 97 "hb-ot-shaper-khmer-machine.rl" + {act = 2;} + break; + case 9: +#line 1 "NONE" + {te = p+1;} +#line 98 "hb-ot-shaper-khmer-machine.rl" + {act = 3;} + break; +#line 398 "hb-ot-shaper-khmer-machine.hh" + } + +_again: + switch ( _khmer_syllable_machine_to_state_actions[cs] ) { + case 6: +#line 1 "NONE" + {ts = 0;} + break; +#line 407 "hb-ot-shaper-khmer-machine.hh" + } + + if ( ++p != pe ) + goto _resume; + _test_eof: {} + if ( p == eof ) + { + if ( _khmer_syllable_machine_eof_trans[cs] > 0 ) { + _trans = _khmer_syllable_machine_eof_trans[cs] - 1; + goto _eof_trans; + } + } + + } + +#line 130 "hb-ot-shaper-khmer-machine.rl" + +} + +#undef found_syllable + +#endif /* HB_OT_SHAPER_KHMER_MACHINE_HH */ diff --git a/libs/harfbuzz/src/hb-ot-shaper-khmer-machine.rl b/libs/harfbuzz/src/hb-ot-shaper-khmer-machine.rl new file mode 100644 index 00000000..f180471b --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-shaper-khmer-machine.rl @@ -0,0 +1,135 @@ +/* + * Copyright © 2011,2012 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 HB_OT_SHAPER_KHMER_MACHINE_HH +#define HB_OT_SHAPER_KHMER_MACHINE_HH + +#include "hb.hh" + +#include "hb-ot-layout.hh" +#include "hb-ot-shaper-indic.hh" + +/* buffer var allocations */ +#define khmer_category() ot_shaper_var_u8_category() /* khmer_category_t */ + +using khmer_category_t = unsigned; + +#define K_Cat(Cat) khmer_syllable_machine_ex_##Cat + +enum khmer_syllable_type_t { + khmer_consonant_syllable, + khmer_broken_cluster, + khmer_non_khmer_cluster, +}; + +%%{ + machine khmer_syllable_machine; + alphtype unsigned char; + write exports; + write data; +}%% + +%%{ + + +# We use category H for spec category Coeng + +export C = 1; +export V = 2; +export H = 4; +export ZWNJ = 5; +export ZWJ = 6; +export PLACEHOLDER = 10; +export DOTTEDCIRCLE = 11; +export Ra = 15; + +export VAbv = 20; +export VBlw = 21; +export VPre = 22; +export VPst = 23; + +export Robatic = 25; +export Xgroup = 26; +export Ygroup = 27; + + +c = (C | Ra | V); +cn = c.((ZWJ|ZWNJ)?.Robatic)?; +joiner = (ZWJ | ZWNJ); +xgroup = (joiner*.Xgroup)*; +ygroup = Ygroup*; + +# This grammar was experimentally extracted from what Uniscribe allows. + +matra_group = VPre? xgroup VBlw? xgroup (joiner?.VAbv)? xgroup VPst?; +syllable_tail = xgroup matra_group xgroup (H.c)? ygroup; + + +broken_cluster = Robatic? (H.cn)* (H | syllable_tail); +consonant_syllable = (cn|PLACEHOLDER|DOTTEDCIRCLE) broken_cluster; +other = any; + +main := |* + consonant_syllable => { found_syllable (khmer_consonant_syllable); }; + broken_cluster => { found_syllable (khmer_broken_cluster); buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_BROKEN_SYLLABLE; }; + other => { found_syllable (khmer_non_khmer_cluster); }; +*|; + + +}%% + +#define found_syllable(syllable_type) \ + HB_STMT_START { \ + if (0) fprintf (stderr, "syllable %d..%d %s\n", ts, te, #syllable_type); \ + for (unsigned int i = ts; i < te; i++) \ + info[i].syllable() = (syllable_serial << 4) | syllable_type; \ + syllable_serial++; \ + if (unlikely (syllable_serial == 16)) syllable_serial = 1; \ + } HB_STMT_END + +inline void +find_syllables_khmer (hb_buffer_t *buffer) +{ + unsigned int p, pe, eof, ts, te, act HB_UNUSED; + int cs; + hb_glyph_info_t *info = buffer->info; + %%{ + write init; + getkey info[p].khmer_category(); + }%% + + p = 0; + pe = eof = buffer->len; + + unsigned int syllable_serial = 1; + %%{ + write exec; + }%% +} + +#undef found_syllable + +#endif /* HB_OT_SHAPER_KHMER_MACHINE_HH */ diff --git a/libs/harfbuzz/src/hb-ot-shaper-khmer.cc b/libs/harfbuzz/src/hb-ot-shaper-khmer.cc new file mode 100644 index 00000000..d9795589 --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-shaper-khmer.cc @@ -0,0 +1,382 @@ +/* + * Copyright © 2011,2012 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 + */ + +#include "hb.hh" + +#ifndef HB_NO_OT_SHAPE + +#include "hb-ot-shaper-khmer-machine.hh" +#include "hb-ot-shaper-indic.hh" +#include "hb-ot-layout.hh" + + +/* + * Khmer shaper. + */ + + +static const hb_ot_map_feature_t +khmer_features[] = +{ + /* + * Basic features. + * These features are applied all at once, before reordering, constrained + * to the syllable. + */ + {HB_TAG('p','r','e','f'), F_MANUAL_JOINERS | F_PER_SYLLABLE}, + {HB_TAG('b','l','w','f'), F_MANUAL_JOINERS | F_PER_SYLLABLE}, + {HB_TAG('a','b','v','f'), F_MANUAL_JOINERS | F_PER_SYLLABLE}, + {HB_TAG('p','s','t','f'), F_MANUAL_JOINERS | F_PER_SYLLABLE}, + {HB_TAG('c','f','a','r'), F_MANUAL_JOINERS | F_PER_SYLLABLE}, + /* + * Other features. + * These features are applied all at once after clearing syllables. + */ + {HB_TAG('p','r','e','s'), F_GLOBAL_MANUAL_JOINERS}, + {HB_TAG('a','b','v','s'), F_GLOBAL_MANUAL_JOINERS}, + {HB_TAG('b','l','w','s'), F_GLOBAL_MANUAL_JOINERS}, + {HB_TAG('p','s','t','s'), F_GLOBAL_MANUAL_JOINERS}, +}; + +/* + * Must be in the same order as the khmer_features array. + */ +enum { + KHMER_PREF, + KHMER_BLWF, + KHMER_ABVF, + KHMER_PSTF, + KHMER_CFAR, + + _KHMER_PRES, + _KHMER_ABVS, + _KHMER_BLWS, + _KHMER_PSTS, + + KHMER_NUM_FEATURES, + KHMER_BASIC_FEATURES = _KHMER_PRES, /* Don't forget to update this! */ +}; + +static inline void +set_khmer_properties (hb_glyph_info_t &info) +{ + hb_codepoint_t u = info.codepoint; + unsigned int type = hb_indic_get_categories (u); + + info.khmer_category() = (khmer_category_t) (type & 0xFFu); +} + +static void +setup_syllables_khmer (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer); +static void +reorder_khmer (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer); + +static void +collect_features_khmer (hb_ot_shape_planner_t *plan) +{ + hb_ot_map_builder_t *map = &plan->map; + + /* Do this before any lookups have been applied. */ + map->add_gsub_pause (setup_syllables_khmer); + map->add_gsub_pause (reorder_khmer); + + /* Testing suggests that Uniscribe does NOT pause between basic + * features. Test with KhmerUI.ttf and the following three + * sequences: + * + * U+1789,U+17BC + * U+1789,U+17D2,U+1789 + * U+1789,U+17D2,U+1789,U+17BC + * + * https://github.com/harfbuzz/harfbuzz/issues/974 + */ + map->enable_feature (HB_TAG('l','o','c','l'), F_PER_SYLLABLE); + map->enable_feature (HB_TAG('c','c','m','p'), F_PER_SYLLABLE); + + unsigned int i = 0; + for (; i < KHMER_BASIC_FEATURES; i++) + map->add_feature (khmer_features[i]); + + /* https://github.com/harfbuzz/harfbuzz/issues/3531 */ + map->add_gsub_pause (hb_syllabic_clear_var); // Don't need syllables anymore, use stop to free buffer var + + for (; i < KHMER_NUM_FEATURES; i++) + map->add_feature (khmer_features[i]); +} + +static void +override_features_khmer (hb_ot_shape_planner_t *plan) +{ + hb_ot_map_builder_t *map = &plan->map; + + /* Khmer spec has 'clig' as part of required shaping features: + * "Apply feature 'clig' to form ligatures that are desired for + * typographical correctness.", hence in overrides... */ + map->enable_feature (HB_TAG('c','l','i','g')); + + /* Uniscribe does not apply 'kern' in Khmer. */ + if (hb_options ().uniscribe_bug_compatible) + { + map->disable_feature (HB_TAG('k','e','r','n')); + } + + map->disable_feature (HB_TAG('l','i','g','a')); +} + + +struct khmer_shape_plan_t +{ + hb_mask_t mask_array[KHMER_NUM_FEATURES]; +}; + +static void * +data_create_khmer (const hb_ot_shape_plan_t *plan) +{ + khmer_shape_plan_t *khmer_plan = (khmer_shape_plan_t *) hb_calloc (1, sizeof (khmer_shape_plan_t)); + if (unlikely (!khmer_plan)) + return nullptr; + + for (unsigned int i = 0; i < ARRAY_LENGTH (khmer_plan->mask_array); i++) + khmer_plan->mask_array[i] = (khmer_features[i].flags & F_GLOBAL) ? + 0 : plan->map.get_1_mask (khmer_features[i].tag); + + return khmer_plan; +} + +static void +data_destroy_khmer (void *data) +{ + hb_free (data); +} + +static void +setup_masks_khmer (const hb_ot_shape_plan_t *plan HB_UNUSED, + hb_buffer_t *buffer, + hb_font_t *font HB_UNUSED) +{ + HB_BUFFER_ALLOCATE_VAR (buffer, khmer_category); + + /* We cannot setup masks here. We save information about characters + * and setup masks later on in a pause-callback. */ + + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; + for (unsigned int i = 0; i < count; i++) + set_khmer_properties (info[i]); +} + +static void +setup_syllables_khmer (const hb_ot_shape_plan_t *plan HB_UNUSED, + hb_font_t *font HB_UNUSED, + hb_buffer_t *buffer) +{ + HB_BUFFER_ALLOCATE_VAR (buffer, syllable); + find_syllables_khmer (buffer); + foreach_syllable (buffer, start, end) + buffer->unsafe_to_break (start, end); +} + + +/* Rules from: + * https://docs.microsoft.com/en-us/typography/script-development/devanagari */ + +static void +reorder_consonant_syllable (const hb_ot_shape_plan_t *plan, + hb_face_t *face HB_UNUSED, + hb_buffer_t *buffer, + unsigned int start, unsigned int end) +{ + const khmer_shape_plan_t *khmer_plan = (const khmer_shape_plan_t *) plan->data; + hb_glyph_info_t *info = buffer->info; + + /* Setup masks. */ + { + /* Post-base */ + hb_mask_t mask = khmer_plan->mask_array[KHMER_BLWF] | + khmer_plan->mask_array[KHMER_ABVF] | + khmer_plan->mask_array[KHMER_PSTF]; + for (unsigned int i = start + 1; i < end; i++) + info[i].mask |= mask; + } + + unsigned int num_coengs = 0; + for (unsigned int i = start + 1; i < end; i++) + { + /* """ + * When a COENG + (Cons | IndV) combination are found (and subscript count + * is less than two) the character combination is handled according to the + * subscript type of the character following the COENG. + * + * ... + * + * Subscript Type 2 - The COENG + RO characters are reordered to immediately + * before the base glyph. Then the COENG + RO characters are assigned to have + * the 'pref' OpenType feature applied to them. + * """ + */ + if (info[i].khmer_category() == K_Cat(H) && num_coengs <= 2 && i + 1 < end) + { + num_coengs++; + + if (info[i + 1].khmer_category() == K_Cat(Ra)) + { + for (unsigned int j = 0; j < 2; j++) + info[i + j].mask |= khmer_plan->mask_array[KHMER_PREF]; + + /* Move the Coeng,Ro sequence to the start. */ + buffer->merge_clusters (start, i + 2); + hb_glyph_info_t t0 = info[i]; + hb_glyph_info_t t1 = info[i + 1]; + memmove (&info[start + 2], &info[start], (i - start) * sizeof (info[0])); + info[start] = t0; + info[start + 1] = t1; + + /* Mark the subsequent stuff with 'cfar'. Used in Khmer. + * Read the feature spec. + * This allows distinguishing the following cases with MS Khmer fonts: + * U+1784,U+17D2,U+179A,U+17D2,U+1782 + * U+1784,U+17D2,U+1782,U+17D2,U+179A + */ + if (khmer_plan->mask_array[KHMER_CFAR]) + for (unsigned int j = i + 2; j < end; j++) + info[j].mask |= khmer_plan->mask_array[KHMER_CFAR]; + + num_coengs = 2; /* Done. */ + } + } + + /* Reorder left matra piece. */ + else if (info[i].khmer_category() == K_Cat(VPre)) + { + /* Move to the start. */ + buffer->merge_clusters (start, i + 1); + hb_glyph_info_t t = info[i]; + memmove (&info[start + 1], &info[start], (i - start) * sizeof (info[0])); + info[start] = t; + } + } +} + +static void +reorder_syllable_khmer (const hb_ot_shape_plan_t *plan, + hb_face_t *face, + hb_buffer_t *buffer, + unsigned int start, unsigned int end) +{ + khmer_syllable_type_t syllable_type = (khmer_syllable_type_t) (buffer->info[start].syllable() & 0x0F); + switch (syllable_type) + { + case khmer_broken_cluster: /* We already inserted dotted-circles, so just call the consonant_syllable. */ + case khmer_consonant_syllable: + reorder_consonant_syllable (plan, face, buffer, start, end); + break; + + case khmer_non_khmer_cluster: + break; + } +} + +static void +reorder_khmer (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer) +{ + if (buffer->message (font, "start reordering khmer")) + { + hb_syllabic_insert_dotted_circles (font, buffer, + khmer_broken_cluster, + K_Cat(DOTTEDCIRCLE), + (unsigned) -1); + + foreach_syllable (buffer, start, end) + reorder_syllable_khmer (plan, font->face, buffer, start, end); + (void) buffer->message (font, "end reordering khmer"); + } + HB_BUFFER_DEALLOCATE_VAR (buffer, khmer_category); +} + + +static bool +decompose_khmer (const hb_ot_shape_normalize_context_t *c, + hb_codepoint_t ab, + hb_codepoint_t *a, + hb_codepoint_t *b) +{ + switch (ab) + { + /* + * Decompose split matras that don't have Unicode decompositions. + */ + + /* Khmer */ + case 0x17BEu : *a = 0x17C1u; *b= 0x17BEu; return true; + case 0x17BFu : *a = 0x17C1u; *b= 0x17BFu; return true; + case 0x17C0u : *a = 0x17C1u; *b= 0x17C0u; return true; + case 0x17C4u : *a = 0x17C1u; *b= 0x17C4u; return true; + case 0x17C5u : *a = 0x17C1u; *b= 0x17C5u; return true; + } + + return (bool) c->unicode->decompose (ab, a, b); +} + +static bool +compose_khmer (const hb_ot_shape_normalize_context_t *c, + hb_codepoint_t a, + hb_codepoint_t b, + hb_codepoint_t *ab) +{ + /* Avoid recomposing split matras. */ + if (HB_UNICODE_GENERAL_CATEGORY_IS_MARK (c->unicode->general_category (a))) + return false; + + return (bool) c->unicode->compose (a, b, ab); +} + + +const hb_ot_shaper_t _hb_ot_shaper_khmer = +{ + collect_features_khmer, + override_features_khmer, + data_create_khmer, + data_destroy_khmer, + nullptr, /* preprocess_text */ + nullptr, /* postprocess_glyphs */ + decompose_khmer, + compose_khmer, + setup_masks_khmer, + nullptr, /* reorder_marks */ + HB_TAG_NONE, /* gpos_tag */ + HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT, + HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE, + false, /* fallback_position */ +}; + + +#endif diff --git a/libs/harfbuzz/src/hb-ot-shaper-myanmar-machine.hh b/libs/harfbuzz/src/hb-ot-shaper-myanmar-machine.hh new file mode 100644 index 00000000..b1097089 --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-shaper-myanmar-machine.hh @@ -0,0 +1,553 @@ + +#line 1 "hb-ot-shaper-myanmar-machine.rl" +/* + * Copyright © 2011,2012 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 HB_OT_SHAPER_MYANMAR_MACHINE_HH +#define HB_OT_SHAPER_MYANMAR_MACHINE_HH + +#include "hb.hh" + +#include "hb-ot-layout.hh" +#include "hb-ot-shaper-indic.hh" + +/* buffer var allocations */ +#define myanmar_category() ot_shaper_var_u8_category() /* myanmar_category_t */ +#define myanmar_position() ot_shaper_var_u8_auxiliary() /* myanmar_position_t */ + +using myanmar_category_t = unsigned; +using myanmar_position_t = ot_position_t; + +#define M_Cat(Cat) myanmar_syllable_machine_ex_##Cat + +enum myanmar_syllable_type_t { + myanmar_consonant_syllable, + myanmar_broken_cluster, + myanmar_non_myanmar_cluster, +}; + + +#line 54 "hb-ot-shaper-myanmar-machine.hh" +#define myanmar_syllable_machine_ex_A 9u +#define myanmar_syllable_machine_ex_As 32u +#define myanmar_syllable_machine_ex_C 1u +#define myanmar_syllable_machine_ex_CS 18u +#define myanmar_syllable_machine_ex_DB 3u +#define myanmar_syllable_machine_ex_DOTTEDCIRCLE 11u +#define myanmar_syllable_machine_ex_GB 10u +#define myanmar_syllable_machine_ex_H 4u +#define myanmar_syllable_machine_ex_IV 2u +#define myanmar_syllable_machine_ex_MH 35u +#define myanmar_syllable_machine_ex_ML 41u +#define myanmar_syllable_machine_ex_MR 36u +#define myanmar_syllable_machine_ex_MW 37u +#define myanmar_syllable_machine_ex_MY 38u +#define myanmar_syllable_machine_ex_PT 39u +#define myanmar_syllable_machine_ex_Ra 15u +#define myanmar_syllable_machine_ex_SM 8u +#define myanmar_syllable_machine_ex_VAbv 20u +#define myanmar_syllable_machine_ex_VBlw 21u +#define myanmar_syllable_machine_ex_VPre 22u +#define myanmar_syllable_machine_ex_VPst 23u +#define myanmar_syllable_machine_ex_VS 40u +#define myanmar_syllable_machine_ex_ZWJ 6u +#define myanmar_syllable_machine_ex_ZWNJ 5u + + +#line 81 "hb-ot-shaper-myanmar-machine.hh" +static const unsigned char _myanmar_syllable_machine_trans_keys[] = { + 1u, 41u, 3u, 41u, 5u, 39u, 5u, 8u, 3u, 41u, 3u, 39u, 3u, 39u, 5u, 39u, + 5u, 39u, 3u, 39u, 3u, 39u, 3u, 41u, 5u, 39u, 1u, 15u, 3u, 39u, 3u, 39u, + 3u, 40u, 3u, 39u, 3u, 41u, 3u, 41u, 3u, 39u, 3u, 41u, 3u, 41u, 3u, 41u, + 3u, 41u, 3u, 41u, 5u, 39u, 5u, 8u, 3u, 41u, 3u, 39u, 3u, 39u, 5u, 39u, + 5u, 39u, 3u, 39u, 3u, 39u, 3u, 41u, 5u, 39u, 1u, 15u, 3u, 41u, 3u, 39u, + 3u, 39u, 3u, 40u, 3u, 39u, 3u, 41u, 3u, 41u, 3u, 39u, 3u, 41u, 3u, 41u, + 3u, 41u, 3u, 41u, 3u, 41u, 3u, 41u, 3u, 41u, 1u, 41u, 1u, 15u, 0 +}; + +static const char _myanmar_syllable_machine_key_spans[] = { + 41, 39, 35, 4, 39, 37, 37, 35, + 35, 37, 37, 39, 35, 15, 37, 37, + 38, 37, 39, 39, 37, 39, 39, 39, + 39, 39, 35, 4, 39, 37, 37, 35, + 35, 37, 37, 39, 35, 15, 39, 37, + 37, 38, 37, 39, 39, 37, 39, 39, + 39, 39, 39, 39, 39, 41, 15 +}; + +static const short _myanmar_syllable_machine_index_offsets[] = { + 0, 42, 82, 118, 123, 163, 201, 239, + 275, 311, 349, 387, 427, 463, 479, 517, + 555, 594, 632, 672, 712, 750, 790, 830, + 870, 910, 950, 986, 991, 1031, 1069, 1107, + 1143, 1179, 1217, 1255, 1295, 1331, 1347, 1387, + 1425, 1463, 1502, 1540, 1580, 1620, 1658, 1698, + 1738, 1778, 1818, 1858, 1898, 1938, 1980 +}; + +static const char _myanmar_syllable_machine_indicies[] = { + 1, 1, 2, 3, 4, 4, 0, 5, + 6, 1, 1, 0, 0, 0, 7, 0, + 0, 8, 0, 9, 10, 11, 12, 0, + 0, 0, 0, 0, 0, 0, 0, 13, + 0, 0, 14, 15, 16, 17, 18, 19, + 20, 0, 22, 23, 24, 24, 21, 25, + 26, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 27, 28, 29, 30, 21, + 21, 21, 21, 21, 21, 21, 21, 31, + 21, 21, 32, 33, 34, 35, 36, 37, + 38, 21, 24, 24, 21, 25, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 30, 21, 21, 21, + 21, 21, 21, 21, 21, 39, 21, 21, + 21, 21, 21, 21, 36, 21, 24, 24, + 21, 25, 21, 22, 21, 24, 24, 21, + 25, 26, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 40, 21, 21, 30, + 21, 21, 21, 21, 21, 21, 21, 21, + 41, 21, 21, 42, 21, 21, 21, 36, + 21, 41, 21, 22, 21, 24, 24, 21, + 25, 26, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 30, + 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 36, + 21, 43, 21, 24, 24, 21, 25, 36, + 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 44, 21, + 21, 21, 21, 21, 21, 36, 21, 24, + 24, 21, 25, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 44, 21, 21, 21, 21, 21, + 21, 36, 21, 24, 24, 21, 25, 21, + 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 36, 21, 22, + 21, 24, 24, 21, 25, 26, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, + 40, 21, 21, 30, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 36, 21, 22, 21, 24, + 24, 21, 25, 26, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 40, 21, + 21, 30, 21, 21, 21, 21, 21, 21, + 21, 21, 41, 21, 21, 21, 21, 21, + 21, 36, 21, 22, 21, 24, 24, 21, + 25, 26, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 40, 21, 21, 30, + 21, 21, 21, 21, 21, 21, 21, 21, + 41, 21, 21, 21, 21, 21, 21, 36, + 21, 41, 21, 24, 24, 21, 25, 21, + 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 30, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 36, 21, 1, + 1, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 1, 21, 22, + 21, 24, 24, 21, 25, 26, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, + 27, 28, 21, 30, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 36, 21, 22, 21, 24, + 24, 21, 25, 26, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 28, + 21, 30, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, + 21, 36, 21, 22, 21, 24, 24, 21, + 25, 26, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 27, 28, 29, 30, + 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 36, + 45, 21, 22, 21, 24, 24, 21, 25, + 26, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 27, 28, 29, 30, 21, + 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 36, 21, + 22, 21, 24, 24, 21, 25, 26, 21, + 21, 21, 21, 21, 21, 21, 21, 21, + 21, 27, 28, 29, 30, 21, 21, 21, + 21, 21, 21, 21, 21, 31, 21, 21, + 32, 33, 34, 35, 36, 21, 38, 21, + 22, 21, 24, 24, 21, 25, 26, 21, + 21, 21, 21, 21, 21, 21, 21, 21, + 21, 27, 28, 29, 30, 21, 21, 21, + 21, 21, 21, 21, 21, 45, 21, 21, + 21, 21, 21, 21, 36, 21, 38, 21, + 22, 21, 24, 24, 21, 25, 26, 21, + 21, 21, 21, 21, 21, 21, 21, 21, + 21, 27, 28, 29, 30, 21, 21, 21, + 21, 21, 21, 21, 21, 45, 21, 21, + 21, 21, 21, 21, 36, 21, 22, 21, + 24, 24, 21, 25, 26, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 27, + 28, 29, 30, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 32, 21, + 34, 21, 36, 21, 38, 21, 22, 21, + 24, 24, 21, 25, 26, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 27, + 28, 29, 30, 21, 21, 21, 21, 21, + 21, 21, 21, 45, 21, 21, 32, 21, + 21, 21, 36, 21, 38, 21, 22, 21, + 24, 24, 21, 25, 26, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 27, + 28, 29, 30, 21, 21, 21, 21, 21, + 21, 21, 21, 46, 21, 21, 32, 33, + 34, 21, 36, 21, 38, 21, 22, 21, + 24, 24, 21, 25, 26, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 27, + 28, 29, 30, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 32, 33, + 34, 21, 36, 21, 38, 21, 22, 23, + 24, 24, 21, 25, 26, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 27, + 28, 29, 30, 21, 21, 21, 21, 21, + 21, 21, 21, 31, 21, 21, 32, 33, + 34, 35, 36, 21, 38, 21, 48, 48, + 47, 5, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, + 12, 47, 47, 47, 47, 47, 47, 47, + 47, 49, 47, 47, 47, 47, 47, 47, + 18, 47, 48, 48, 47, 5, 47, 2, + 47, 48, 48, 47, 5, 6, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, + 50, 47, 47, 12, 47, 47, 47, 47, + 47, 47, 47, 47, 51, 47, 47, 52, + 47, 47, 47, 18, 47, 51, 47, 2, + 47, 48, 48, 47, 5, 6, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 12, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 18, 47, 53, 47, 48, + 48, 47, 5, 18, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 54, 47, 47, 47, 47, 47, + 47, 18, 47, 48, 48, 47, 5, 47, + 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 54, 47, + 47, 47, 47, 47, 47, 18, 47, 48, + 48, 47, 5, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, + 47, 18, 47, 2, 47, 48, 48, 47, + 5, 6, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 50, 47, 47, 12, + 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 18, + 47, 2, 47, 48, 48, 47, 5, 6, + 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 50, 47, 47, 12, 47, 47, + 47, 47, 47, 47, 47, 47, 51, 47, + 47, 47, 47, 47, 47, 18, 47, 2, + 47, 48, 48, 47, 5, 6, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, + 50, 47, 47, 12, 47, 47, 47, 47, + 47, 47, 47, 47, 51, 47, 47, 47, + 47, 47, 47, 18, 47, 51, 47, 48, + 48, 47, 5, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, + 47, 12, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, + 47, 18, 47, 55, 55, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, + 47, 55, 47, 2, 3, 48, 48, 47, + 5, 6, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 9, 10, 11, 12, + 47, 47, 47, 47, 47, 47, 47, 47, + 13, 47, 47, 14, 15, 16, 17, 18, + 19, 20, 47, 2, 47, 48, 48, 47, + 5, 6, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 9, 10, 47, 12, + 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 18, + 47, 2, 47, 48, 48, 47, 5, 6, + 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 10, 47, 12, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 18, 47, 2, + 47, 48, 48, 47, 5, 6, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, + 9, 10, 11, 12, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 18, 56, 47, 2, 47, + 48, 48, 47, 5, 6, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 9, + 10, 11, 12, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 18, 47, 2, 47, 48, 48, + 47, 5, 6, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 9, 10, 11, + 12, 47, 47, 47, 47, 47, 47, 47, + 47, 13, 47, 47, 14, 15, 16, 17, + 18, 47, 20, 47, 2, 47, 48, 48, + 47, 5, 6, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 9, 10, 11, + 12, 47, 47, 47, 47, 47, 47, 47, + 47, 56, 47, 47, 47, 47, 47, 47, + 18, 47, 20, 47, 2, 47, 48, 48, + 47, 5, 6, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 9, 10, 11, + 12, 47, 47, 47, 47, 47, 47, 47, + 47, 56, 47, 47, 47, 47, 47, 47, + 18, 47, 2, 47, 48, 48, 47, 5, + 6, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 9, 10, 11, 12, 47, + 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 14, 47, 16, 47, 18, 47, + 20, 47, 2, 47, 48, 48, 47, 5, + 6, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 9, 10, 11, 12, 47, + 47, 47, 47, 47, 47, 47, 47, 56, + 47, 47, 14, 47, 47, 47, 18, 47, + 20, 47, 2, 47, 48, 48, 47, 5, + 6, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 9, 10, 11, 12, 47, + 47, 47, 47, 47, 47, 47, 47, 57, + 47, 47, 14, 15, 16, 47, 18, 47, + 20, 47, 2, 47, 48, 48, 47, 5, + 6, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 9, 10, 11, 12, 47, + 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 14, 15, 16, 47, 18, 47, + 20, 47, 2, 3, 48, 48, 47, 5, + 6, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 9, 10, 11, 12, 47, + 47, 47, 47, 47, 47, 47, 47, 13, + 47, 47, 14, 15, 16, 17, 18, 47, + 20, 47, 22, 23, 24, 24, 21, 25, + 26, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 27, 28, 29, 30, 21, + 21, 21, 21, 21, 21, 21, 21, 58, + 21, 21, 32, 33, 34, 35, 36, 37, + 38, 21, 22, 59, 24, 24, 21, 25, + 26, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 27, 28, 29, 30, 21, + 21, 21, 21, 21, 21, 21, 21, 31, + 21, 21, 32, 33, 34, 35, 36, 21, + 38, 21, 1, 1, 2, 3, 48, 48, + 47, 5, 6, 1, 1, 47, 47, 47, + 1, 47, 47, 47, 47, 9, 10, 11, + 12, 47, 47, 47, 47, 47, 47, 47, + 47, 13, 47, 47, 14, 15, 16, 17, + 18, 19, 20, 47, 1, 1, 60, 60, + 60, 60, 60, 60, 60, 1, 1, 60, + 60, 60, 1, 60, 0 +}; + +static const char _myanmar_syllable_machine_trans_targs[] = { + 0, 1, 26, 37, 0, 27, 29, 51, + 54, 39, 40, 41, 28, 43, 44, 46, + 47, 48, 30, 50, 45, 0, 2, 13, + 0, 3, 5, 14, 15, 16, 4, 18, + 19, 21, 22, 23, 6, 25, 20, 12, + 9, 10, 11, 7, 8, 17, 24, 0, + 0, 36, 33, 34, 35, 31, 32, 38, + 42, 49, 52, 53, 0 +}; + +static const char _myanmar_syllable_machine_trans_actions[] = { + 3, 0, 0, 0, 4, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 5, 0, 0, + 6, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 7, + 8, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 9 +}; + +static const char _myanmar_syllable_machine_to_state_actions[] = { + 1, 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, 0, 0, 0, 0, 0, 0 +}; + +static const char _myanmar_syllable_machine_from_state_actions[] = { + 2, 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, 0, 0, 0, 0, 0, 0 +}; + +static const short _myanmar_syllable_machine_eof_trans[] = { + 0, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 22, 22, 48, 61 +}; + +static const int myanmar_syllable_machine_start = 0; +static const int myanmar_syllable_machine_first_final = 0; +static const int myanmar_syllable_machine_error = -1; + +static const int myanmar_syllable_machine_en_main = 0; + + +#line 55 "hb-ot-shaper-myanmar-machine.rl" + + + +#line 117 "hb-ot-shaper-myanmar-machine.rl" + + +#define found_syllable(syllable_type) \ + HB_STMT_START { \ + if (0) fprintf (stderr, "syllable %d..%d %s\n", ts, te, #syllable_type); \ + for (unsigned int i = ts; i < te; i++) \ + info[i].syllable() = (syllable_serial << 4) | syllable_type; \ + syllable_serial++; \ + if (unlikely (syllable_serial == 16)) syllable_serial = 1; \ + } HB_STMT_END + +inline void +find_syllables_myanmar (hb_buffer_t *buffer) +{ + unsigned int p, pe, eof, ts, te, act HB_UNUSED; + int cs; + hb_glyph_info_t *info = buffer->info; + +#line 447 "hb-ot-shaper-myanmar-machine.hh" + { + cs = myanmar_syllable_machine_start; + ts = 0; + te = 0; + act = 0; + } + +#line 137 "hb-ot-shaper-myanmar-machine.rl" + + + p = 0; + pe = eof = buffer->len; + + unsigned int syllable_serial = 1; + +#line 463 "hb-ot-shaper-myanmar-machine.hh" + { + int _slen; + int _trans; + const unsigned char *_keys; + const char *_inds; + if ( p == pe ) + goto _test_eof; +_resume: + switch ( _myanmar_syllable_machine_from_state_actions[cs] ) { + case 2: +#line 1 "NONE" + {ts = p;} + break; +#line 477 "hb-ot-shaper-myanmar-machine.hh" + } + + _keys = _myanmar_syllable_machine_trans_keys + (cs<<1); + _inds = _myanmar_syllable_machine_indicies + _myanmar_syllable_machine_index_offsets[cs]; + + _slen = _myanmar_syllable_machine_key_spans[cs]; + _trans = _inds[ _slen > 0 && _keys[0] <=( info[p].myanmar_category()) && + ( info[p].myanmar_category()) <= _keys[1] ? + ( info[p].myanmar_category()) - _keys[0] : _slen ]; + +_eof_trans: + cs = _myanmar_syllable_machine_trans_targs[_trans]; + + if ( _myanmar_syllable_machine_trans_actions[_trans] == 0 ) + goto _again; + + switch ( _myanmar_syllable_machine_trans_actions[_trans] ) { + case 6: +#line 110 "hb-ot-shaper-myanmar-machine.rl" + {te = p+1;{ found_syllable (myanmar_consonant_syllable); }} + break; + case 4: +#line 111 "hb-ot-shaper-myanmar-machine.rl" + {te = p+1;{ found_syllable (myanmar_non_myanmar_cluster); }} + break; + case 8: +#line 112 "hb-ot-shaper-myanmar-machine.rl" + {te = p+1;{ found_syllable (myanmar_broken_cluster); buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_BROKEN_SYLLABLE; }} + break; + case 3: +#line 113 "hb-ot-shaper-myanmar-machine.rl" + {te = p+1;{ found_syllable (myanmar_non_myanmar_cluster); }} + break; + case 5: +#line 110 "hb-ot-shaper-myanmar-machine.rl" + {te = p;p--;{ found_syllable (myanmar_consonant_syllable); }} + break; + case 7: +#line 112 "hb-ot-shaper-myanmar-machine.rl" + {te = p;p--;{ found_syllable (myanmar_broken_cluster); buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_BROKEN_SYLLABLE; }} + break; + case 9: +#line 113 "hb-ot-shaper-myanmar-machine.rl" + {te = p;p--;{ found_syllable (myanmar_non_myanmar_cluster); }} + break; +#line 523 "hb-ot-shaper-myanmar-machine.hh" + } + +_again: + switch ( _myanmar_syllable_machine_to_state_actions[cs] ) { + case 1: +#line 1 "NONE" + {ts = 0;} + break; +#line 532 "hb-ot-shaper-myanmar-machine.hh" + } + + if ( ++p != pe ) + goto _resume; + _test_eof: {} + if ( p == eof ) + { + if ( _myanmar_syllable_machine_eof_trans[cs] > 0 ) { + _trans = _myanmar_syllable_machine_eof_trans[cs] - 1; + goto _eof_trans; + } + } + + } + +#line 145 "hb-ot-shaper-myanmar-machine.rl" + +} + +#undef found_syllable + +#endif /* HB_OT_SHAPER_MYANMAR_MACHINE_HH */ diff --git a/libs/harfbuzz/src/hb-ot-shaper-myanmar-machine.rl b/libs/harfbuzz/src/hb-ot-shaper-myanmar-machine.rl new file mode 100644 index 00000000..2cb6e4e7 --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-shaper-myanmar-machine.rl @@ -0,0 +1,150 @@ +/* + * Copyright © 2011,2012 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 HB_OT_SHAPER_MYANMAR_MACHINE_HH +#define HB_OT_SHAPER_MYANMAR_MACHINE_HH + +#include "hb.hh" + +#include "hb-ot-layout.hh" +#include "hb-ot-shaper-indic.hh" + +/* buffer var allocations */ +#define myanmar_category() ot_shaper_var_u8_category() /* myanmar_category_t */ +#define myanmar_position() ot_shaper_var_u8_auxiliary() /* myanmar_position_t */ + +using myanmar_category_t = unsigned; +using myanmar_position_t = ot_position_t; + +#define M_Cat(Cat) myanmar_syllable_machine_ex_##Cat + +enum myanmar_syllable_type_t { + myanmar_consonant_syllable, + myanmar_broken_cluster, + myanmar_non_myanmar_cluster, +}; + +%%{ + machine myanmar_syllable_machine; + alphtype unsigned char; + write exports; + write data; +}%% + +%%{ + + +# Spec category D is folded into GB; D0 is not implemented by Uniscribe and as such folded into D +# Spec category P is folded into GB + +export C = 1; +export IV = 2; +export DB = 3; # Dot below = OT_N +export H = 4; +export ZWNJ = 5; +export ZWJ = 6; +export SM = 8; # Visarga and Shan tones +export GB = 10; # = OT_PLACEHOLDER +export DOTTEDCIRCLE = 11; +export A = 9; +export Ra = 15; +export CS = 18; + +export VAbv = 20; +export VBlw = 21; +export VPre = 22; +export VPst = 23; + +# 32+ are for Myanmar-specific values +export As = 32; # Asat +export MH = 35; # Medial Ha +export MR = 36; # Medial Ra +export MW = 37; # Medial Wa, Shan Wa +export MY = 38; # Medial Ya, Mon Na, Mon Ma +export PT = 39; # Pwo and other tones +export VS = 40; # Variation selectors +export ML = 41; # Medial Mon La + + +j = ZWJ|ZWNJ; # Joiners +k = (Ra As H); # Kinzi + +c = C|Ra; # is_consonant + +medial_group = MY? As? MR? ((MW MH? ML? | MH ML? | ML) As?)?; +main_vowel_group = (VPre.VS?)* VAbv* VBlw* A* (DB As?)?; +post_vowel_group = VPst MH? ML? As* VAbv* A* (DB As?)?; +pwo_tone_group = PT A* DB? As?; + +complex_syllable_tail = As* medial_group main_vowel_group post_vowel_group* pwo_tone_group* SM* j?; +syllable_tail = (H (c|IV).VS?)* (H | complex_syllable_tail); + +consonant_syllable = (k|CS)? (c|IV|GB|DOTTEDCIRCLE).VS? syllable_tail; +broken_cluster = k? VS? syllable_tail; +other = any; + +main := |* + consonant_syllable => { found_syllable (myanmar_consonant_syllable); }; + j => { found_syllable (myanmar_non_myanmar_cluster); }; + broken_cluster => { found_syllable (myanmar_broken_cluster); buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_BROKEN_SYLLABLE; }; + other => { found_syllable (myanmar_non_myanmar_cluster); }; +*|; + + +}%% + +#define found_syllable(syllable_type) \ + HB_STMT_START { \ + if (0) fprintf (stderr, "syllable %d..%d %s\n", ts, te, #syllable_type); \ + for (unsigned int i = ts; i < te; i++) \ + info[i].syllable() = (syllable_serial << 4) | syllable_type; \ + syllable_serial++; \ + if (unlikely (syllable_serial == 16)) syllable_serial = 1; \ + } HB_STMT_END + +inline void +find_syllables_myanmar (hb_buffer_t *buffer) +{ + unsigned int p, pe, eof, ts, te, act HB_UNUSED; + int cs; + hb_glyph_info_t *info = buffer->info; + %%{ + write init; + getkey info[p].myanmar_category(); + }%% + + p = 0; + pe = eof = buffer->len; + + unsigned int syllable_serial = 1; + %%{ + write exec; + }%% +} + +#undef found_syllable + +#endif /* HB_OT_SHAPER_MYANMAR_MACHINE_HH */ diff --git a/libs/harfbuzz/src/hb-ot-shaper-myanmar.cc b/libs/harfbuzz/src/hb-ot-shaper-myanmar.cc new file mode 100644 index 00000000..78bd8de5 --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-shaper-myanmar.cc @@ -0,0 +1,356 @@ +/* + * Copyright © 2011,2012,2013 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 + */ + +#include "hb.hh" + +#ifndef HB_NO_OT_SHAPE + +#include "hb-ot-shaper-myanmar-machine.hh" +#include "hb-ot-shaper-indic.hh" +#include "hb-ot-layout.hh" + + +/* + * Myanmar shaper. + */ + + +static const hb_tag_t +myanmar_basic_features[] = +{ + /* + * Basic features. + * These features are applied in order, one at a time, after reordering, + * constrained to the syllable. + */ + HB_TAG('r','p','h','f'), + HB_TAG('p','r','e','f'), + HB_TAG('b','l','w','f'), + HB_TAG('p','s','t','f'), +}; +static const hb_tag_t +myanmar_other_features[] = +{ + /* + * Other features. + * These features are applied all at once, after clearing syllables. + */ + HB_TAG('p','r','e','s'), + HB_TAG('a','b','v','s'), + HB_TAG('b','l','w','s'), + HB_TAG('p','s','t','s'), +}; + +static inline void +set_myanmar_properties (hb_glyph_info_t &info) +{ + hb_codepoint_t u = info.codepoint; + unsigned int type = hb_indic_get_categories (u); + + info.myanmar_category() = (myanmar_category_t) (type & 0xFFu); +} + + +static inline bool +is_one_of_myanmar (const hb_glyph_info_t &info, unsigned int flags) +{ + /* If it ligated, all bets are off. */ + if (_hb_glyph_info_ligated (&info)) return false; + return !!(FLAG_UNSAFE (info.myanmar_category()) & flags); +} + +/* Note: + * + * We treat Vowels and placeholders as if they were consonants. This is safe because Vowels + * cannot happen in a consonant syllable. The plus side however is, we can call the + * consonant syllable logic from the vowel syllable function and get it all right! + * + * Keep in sync with consonant_categories in the generator. */ +#define CONSONANT_FLAGS_MYANMAR (FLAG (M_Cat(C)) | FLAG (M_Cat(CS)) | FLAG (M_Cat(Ra)) | /* FLAG (M_Cat(CM)) | */ FLAG (M_Cat(IV)) | FLAG (M_Cat(GB)) | FLAG (M_Cat(DOTTEDCIRCLE))) + +static inline bool +is_consonant_myanmar (const hb_glyph_info_t &info) +{ + return is_one_of_myanmar (info, CONSONANT_FLAGS_MYANMAR); +} + + +static void +setup_syllables_myanmar (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer); +static void +reorder_myanmar (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer); + +static void +collect_features_myanmar (hb_ot_shape_planner_t *plan) +{ + hb_ot_map_builder_t *map = &plan->map; + + /* Do this before any lookups have been applied. */ + map->add_gsub_pause (setup_syllables_myanmar); + + map->enable_feature (HB_TAG('l','o','c','l'), F_PER_SYLLABLE); + /* The Indic specs do not require ccmp, but we apply it here since if + * there is a use of it, it's typically at the beginning. */ + map->enable_feature (HB_TAG('c','c','m','p'), F_PER_SYLLABLE); + + + map->add_gsub_pause (reorder_myanmar); + + for (unsigned int i = 0; i < ARRAY_LENGTH (myanmar_basic_features); i++) + { + map->enable_feature (myanmar_basic_features[i], F_MANUAL_ZWJ | F_PER_SYLLABLE); + map->add_gsub_pause (nullptr); + } + map->add_gsub_pause (hb_syllabic_clear_var); // Don't need syllables anymore, use stop to free buffer var + + for (unsigned int i = 0; i < ARRAY_LENGTH (myanmar_other_features); i++) + map->enable_feature (myanmar_other_features[i], F_MANUAL_ZWJ); +} + +static void +setup_masks_myanmar (const hb_ot_shape_plan_t *plan HB_UNUSED, + hb_buffer_t *buffer, + hb_font_t *font HB_UNUSED) +{ + HB_BUFFER_ALLOCATE_VAR (buffer, myanmar_category); + HB_BUFFER_ALLOCATE_VAR (buffer, myanmar_position); + + /* No masks, we just save information about characters. */ + + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; + for (unsigned int i = 0; i < count; i++) + set_myanmar_properties (info[i]); +} + +static void +setup_syllables_myanmar (const hb_ot_shape_plan_t *plan HB_UNUSED, + hb_font_t *font HB_UNUSED, + hb_buffer_t *buffer) +{ + HB_BUFFER_ALLOCATE_VAR (buffer, syllable); + find_syllables_myanmar (buffer); + foreach_syllable (buffer, start, end) + buffer->unsafe_to_break (start, end); +} + +static int +compare_myanmar_order (const hb_glyph_info_t *pa, const hb_glyph_info_t *pb) +{ + int a = pa->myanmar_position(); + int b = pb->myanmar_position(); + + return (int) a - (int) b; +} + + +/* Rules from: + * https://docs.microsoft.com/en-us/typography/script-development/myanmar */ + +static void +initial_reordering_consonant_syllable (hb_buffer_t *buffer, + unsigned int start, unsigned int end) +{ + hb_glyph_info_t *info = buffer->info; + + unsigned int base = end; + bool has_reph = false; + + { + unsigned int limit = start; + if (start + 3 <= end && + info[start ].myanmar_category() == M_Cat(Ra) && + info[start+1].myanmar_category() == M_Cat(As) && + info[start+2].myanmar_category() == M_Cat(H)) + { + limit += 3; + base = start; + has_reph = true; + } + + { + if (!has_reph) + base = limit; + + for (unsigned int i = limit; i < end; i++) + if (is_consonant_myanmar (info[i])) + { + base = i; + break; + } + } + } + + /* Reorder! */ + { + unsigned int i = start; + for (; i < start + (has_reph ? 3 : 0); i++) + info[i].myanmar_position() = POS_AFTER_MAIN; + for (; i < base; i++) + info[i].myanmar_position() = POS_PRE_C; + if (i < end) + { + info[i].myanmar_position() = POS_BASE_C; + i++; + } + myanmar_position_t pos = POS_AFTER_MAIN; + /* The following loop may be ugly, but it implements all of + * Myanmar reordering! */ + for (; i < end; i++) + { + if (info[i].myanmar_category() == M_Cat(MR)) /* Pre-base reordering */ + { + info[i].myanmar_position() = POS_PRE_C; + continue; + } + if (info[i].myanmar_category() == M_Cat(VPre)) /* Left matra */ + { + info[i].myanmar_position() = POS_PRE_M; + continue; + } + if (info[i].myanmar_category() == M_Cat(VS)) + { + info[i].myanmar_position() = info[i - 1].myanmar_position(); + continue; + } + + if (pos == POS_AFTER_MAIN && info[i].myanmar_category() == M_Cat(VBlw)) + { + pos = POS_BELOW_C; + info[i].myanmar_position() = pos; + continue; + } + + if (pos == POS_BELOW_C && info[i].myanmar_category() == M_Cat(A)) + { + info[i].myanmar_position() = POS_BEFORE_SUB; + continue; + } + if (pos == POS_BELOW_C && info[i].myanmar_category() == M_Cat(VBlw)) + { + info[i].myanmar_position() = pos; + continue; + } + if (pos == POS_BELOW_C && info[i].myanmar_category() != M_Cat(A)) + { + pos = POS_AFTER_SUB; + info[i].myanmar_position() = pos; + continue; + } + info[i].myanmar_position() = pos; + } + } + + /* Sit tight, rock 'n roll! */ + buffer->sort (start, end, compare_myanmar_order); +} + +static void +reorder_syllable_myanmar (const hb_ot_shape_plan_t *plan HB_UNUSED, + hb_face_t *face HB_UNUSED, + hb_buffer_t *buffer, + unsigned int start, unsigned int end) +{ + myanmar_syllable_type_t syllable_type = (myanmar_syllable_type_t) (buffer->info[start].syllable() & 0x0F); + switch (syllable_type) { + + case myanmar_broken_cluster: /* We already inserted dotted-circles, so just call the consonant_syllable. */ + case myanmar_consonant_syllable: + initial_reordering_consonant_syllable (buffer, start, end); + break; + + case myanmar_non_myanmar_cluster: + break; + } +} + +static void +reorder_myanmar (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer) +{ + if (buffer->message (font, "start reordering myanmar")) + { + hb_syllabic_insert_dotted_circles (font, buffer, + myanmar_broken_cluster, + M_Cat(DOTTEDCIRCLE)); + + foreach_syllable (buffer, start, end) + reorder_syllable_myanmar (plan, font->face, buffer, start, end); + (void) buffer->message (font, "end reordering myanmar"); + } + + HB_BUFFER_DEALLOCATE_VAR (buffer, myanmar_category); + HB_BUFFER_DEALLOCATE_VAR (buffer, myanmar_position); +} + + +const hb_ot_shaper_t _hb_ot_shaper_myanmar = +{ + collect_features_myanmar, + nullptr, /* override_features */ + nullptr, /* data_create */ + nullptr, /* data_destroy */ + nullptr, /* preprocess_text */ + nullptr, /* postprocess_glyphs */ + nullptr, /* decompose */ + nullptr, /* compose */ + setup_masks_myanmar, + nullptr, /* reorder_marks */ + HB_TAG_NONE, /* gpos_tag */ + HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT, + HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_EARLY, + false, /* fallback_position */ +}; + + +/* Ugly Zawgyi encoding. + * Disable all auto processing. + * https://github.com/harfbuzz/harfbuzz/issues/1162 */ +const hb_ot_shaper_t _hb_ot_shaper_myanmar_zawgyi = +{ + nullptr, /* collect_features */ + nullptr, /* override_features */ + nullptr, /* data_create */ + nullptr, /* data_destroy */ + nullptr, /* preprocess_text */ + nullptr, /* postprocess_glyphs */ + nullptr, /* decompose */ + nullptr, /* compose */ + nullptr, /* setup_masks */ + nullptr, /* reorder_marks */ + HB_TAG_NONE, /* gpos_tag */ + HB_OT_SHAPE_NORMALIZATION_MODE_NONE, + HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE, + false, /* fallback_position */ +}; + + +#endif diff --git a/libs/harfbuzz/src/hb-ot-shaper-syllabic.cc b/libs/harfbuzz/src/hb-ot-shaper-syllabic.cc new file mode 100644 index 00000000..a8e0d8e8 --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-shaper-syllabic.cc @@ -0,0 +1,98 @@ +/* + * Copyright © 2021 Behdad Esfahbod. + * + * 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. + */ + +#include "hb.hh" + +#ifndef HB_NO_OT_SHAPE + +#include "hb-ot-shaper-syllabic.hh" + + +void +hb_syllabic_insert_dotted_circles (hb_font_t *font, + hb_buffer_t *buffer, + unsigned int broken_syllable_type, + unsigned int dottedcircle_category, + int repha_category, + int dottedcircle_position) +{ + if (unlikely (buffer->flags & HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE)) + return; + if (likely (!(buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_BROKEN_SYLLABLE))) + return; + + hb_codepoint_t dottedcircle_glyph; + if (!font->get_nominal_glyph (0x25CCu, &dottedcircle_glyph)) + return; + + hb_glyph_info_t dottedcircle = {0}; + dottedcircle.codepoint = 0x25CCu; + dottedcircle.ot_shaper_var_u8_category() = dottedcircle_category; + if (dottedcircle_position != -1) + dottedcircle.ot_shaper_var_u8_auxiliary() = dottedcircle_position; + dottedcircle.codepoint = dottedcircle_glyph; + + buffer->clear_output (); + + buffer->idx = 0; + unsigned int last_syllable = 0; + while (buffer->idx < buffer->len && buffer->successful) + { + unsigned int syllable = buffer->cur().syllable(); + if (unlikely (last_syllable != syllable && (syllable & 0x0F) == broken_syllable_type)) + { + last_syllable = syllable; + + hb_glyph_info_t ginfo = dottedcircle; + ginfo.cluster = buffer->cur().cluster; + ginfo.mask = buffer->cur().mask; + ginfo.syllable() = buffer->cur().syllable(); + + /* Insert dottedcircle after possible Repha. */ + if (repha_category != -1) + { + while (buffer->idx < buffer->len && buffer->successful && + last_syllable == buffer->cur().syllable() && + buffer->cur().ot_shaper_var_u8_category() == (unsigned) repha_category) + (void) buffer->next_glyph (); + } + + (void) buffer->output_info (ginfo); + } + else + (void) buffer->next_glyph (); + } + buffer->sync (); +} + +HB_INTERNAL void +hb_syllabic_clear_var (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer) +{ + HB_BUFFER_DEALLOCATE_VAR (buffer, syllable); +} + + +#endif diff --git a/libs/harfbuzz/src/hb-ot-shaper-syllabic.hh b/libs/harfbuzz/src/hb-ot-shaper-syllabic.hh new file mode 100644 index 00000000..e8a15bb4 --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-shaper-syllabic.hh @@ -0,0 +1,47 @@ +/* + * Copyright © 2021 Behdad Esfahbod. + * + * 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. + */ + +#ifndef HB_OT_SHAPER_SYLLABIC_HH +#define HB_OT_SHAPER_SYLLABIC_HH + +#include "hb.hh" + +#include "hb-ot-shaper.hh" + + +HB_INTERNAL void +hb_syllabic_insert_dotted_circles (hb_font_t *font, + hb_buffer_t *buffer, + unsigned int broken_syllable_type, + unsigned int dottedcircle_category, + int repha_category = -1, + int dottedcircle_position = -1); + +HB_INTERNAL void +hb_syllabic_clear_var (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer); + + +#endif /* HB_OT_SHAPER_SYLLABIC_HH */ diff --git a/libs/harfbuzz/src/hb-ot-shaper-thai.cc b/libs/harfbuzz/src/hb-ot-shaper-thai.cc new file mode 100644 index 00000000..15349b1e --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-shaper-thai.cc @@ -0,0 +1,393 @@ +/* + * Copyright © 2010,2012 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 + */ + +#include "hb.hh" + +#ifndef HB_NO_OT_SHAPE + +#include "hb-ot-shaper.hh" + + +/* Thai / Lao shaper */ + + +/* PUA shaping */ + + +enum thai_consonant_type_t +{ + NC, + AC, + RC, + DC, + NOT_CONSONANT, + NUM_CONSONANT_TYPES = NOT_CONSONANT +}; + +static thai_consonant_type_t +get_consonant_type (hb_codepoint_t u) +{ + if (u == 0x0E1Bu || u == 0x0E1Du || u == 0x0E1Fu/* || u == 0x0E2Cu*/) + return AC; + if (u == 0x0E0Du || u == 0x0E10u) + return RC; + if (u == 0x0E0Eu || u == 0x0E0Fu) + return DC; + if (hb_in_range (u, 0x0E01u, 0x0E2Eu)) + return NC; + return NOT_CONSONANT; +} + + +enum thai_mark_type_t +{ + AV, + BV, + T, + NOT_MARK, + NUM_MARK_TYPES = NOT_MARK +}; + +static thai_mark_type_t +get_mark_type (hb_codepoint_t u) +{ + if (u == 0x0E31u || hb_in_range (u, 0x0E34u, 0x0E37u) || + u == 0x0E47u || hb_in_range (u, 0x0E4Du, 0x0E4Eu)) + return AV; + if (hb_in_range (u, 0x0E38u, 0x0E3Au)) + return BV; + if (hb_in_range (u, 0x0E48u, 0x0E4Cu)) + return T; + return NOT_MARK; +} + + +enum thai_action_t +{ + NOP, + SD, /* Shift combining-mark down */ + SL, /* Shift combining-mark left */ + SDL, /* Shift combining-mark down-left */ + RD /* Remove descender from base */ +}; + +static hb_codepoint_t +thai_pua_shape (hb_codepoint_t u, thai_action_t action, hb_font_t *font) +{ + struct thai_pua_mapping_t { + hb_codepoint_t u; + hb_codepoint_t win_pua; + hb_codepoint_t mac_pua; + } const *pua_mappings = nullptr; + static const thai_pua_mapping_t SD_mappings[] = { + {0x0E48u, 0xF70Au, 0xF88Bu}, /* MAI EK */ + {0x0E49u, 0xF70Bu, 0xF88Eu}, /* MAI THO */ + {0x0E4Au, 0xF70Cu, 0xF891u}, /* MAI TRI */ + {0x0E4Bu, 0xF70Du, 0xF894u}, /* MAI CHATTAWA */ + {0x0E4Cu, 0xF70Eu, 0xF897u}, /* THANTHAKHAT */ + {0x0E38u, 0xF718u, 0xF89Bu}, /* SARA U */ + {0x0E39u, 0xF719u, 0xF89Cu}, /* SARA UU */ + {0x0E3Au, 0xF71Au, 0xF89Du}, /* PHINTHU */ + {0x0000u, 0x0000u, 0x0000u} + }; + static const thai_pua_mapping_t SDL_mappings[] = { + {0x0E48u, 0xF705u, 0xF88Cu}, /* MAI EK */ + {0x0E49u, 0xF706u, 0xF88Fu}, /* MAI THO */ + {0x0E4Au, 0xF707u, 0xF892u}, /* MAI TRI */ + {0x0E4Bu, 0xF708u, 0xF895u}, /* MAI CHATTAWA */ + {0x0E4Cu, 0xF709u, 0xF898u}, /* THANTHAKHAT */ + {0x0000u, 0x0000u, 0x0000u} + }; + static const thai_pua_mapping_t SL_mappings[] = { + {0x0E48u, 0xF713u, 0xF88Au}, /* MAI EK */ + {0x0E49u, 0xF714u, 0xF88Du}, /* MAI THO */ + {0x0E4Au, 0xF715u, 0xF890u}, /* MAI TRI */ + {0x0E4Bu, 0xF716u, 0xF893u}, /* MAI CHATTAWA */ + {0x0E4Cu, 0xF717u, 0xF896u}, /* THANTHAKHAT */ + {0x0E31u, 0xF710u, 0xF884u}, /* MAI HAN-AKAT */ + {0x0E34u, 0xF701u, 0xF885u}, /* SARA I */ + {0x0E35u, 0xF702u, 0xF886u}, /* SARA II */ + {0x0E36u, 0xF703u, 0xF887u}, /* SARA UE */ + {0x0E37u, 0xF704u, 0xF888u}, /* SARA UEE */ + {0x0E47u, 0xF712u, 0xF889u}, /* MAITAIKHU */ + {0x0E4Du, 0xF711u, 0xF899u}, /* NIKHAHIT */ + {0x0000u, 0x0000u, 0x0000u} + }; + static const thai_pua_mapping_t RD_mappings[] = { + {0x0E0Du, 0xF70Fu, 0xF89Au}, /* YO YING */ + {0x0E10u, 0xF700u, 0xF89Eu}, /* THO THAN */ + {0x0000u, 0x0000u, 0x0000u} + }; + + switch (action) { + case NOP: return u; + case SD: pua_mappings = SD_mappings; break; + case SDL: pua_mappings = SDL_mappings; break; + case SL: pua_mappings = SL_mappings; break; + case RD: pua_mappings = RD_mappings; break; + } + for (; pua_mappings->u; pua_mappings++) + if (pua_mappings->u == u) + { + hb_codepoint_t glyph; + if (hb_font_get_glyph (font, pua_mappings->win_pua, 0, &glyph)) + return pua_mappings->win_pua; + if (hb_font_get_glyph (font, pua_mappings->mac_pua, 0, &glyph)) + return pua_mappings->mac_pua; + break; + } + return u; +} + + +static enum thai_above_state_t +{ /* Cluster above looks like: */ + T0, /* ⣤ */ + T1, /* ⣼ */ + T2, /* ⣾ */ + T3, /* ⣿ */ + NUM_ABOVE_STATES +} thai_above_start_state[NUM_CONSONANT_TYPES + 1/* For NOT_CONSONANT */] = +{ + T0, /* NC */ + T1, /* AC */ + T0, /* RC */ + T0, /* DC */ + T3, /* NOT_CONSONANT */ +}; + +static const struct thai_above_state_machine_edge_t { + thai_action_t action; + thai_above_state_t next_state; +} thai_above_state_machine[NUM_ABOVE_STATES][NUM_MARK_TYPES] = +{ /*AV*/ /*BV*/ /*T*/ +/*T0*/ {{NOP,T3}, {NOP,T0}, {SD, T3}}, +/*T1*/ {{SL, T2}, {NOP,T1}, {SDL,T2}}, +/*T2*/ {{NOP,T3}, {NOP,T2}, {SL, T3}}, +/*T3*/ {{NOP,T3}, {NOP,T3}, {NOP,T3}}, +}; + + +static enum thai_below_state_t +{ + B0, /* No descender */ + B1, /* Removable descender */ + B2, /* Strict descender */ + NUM_BELOW_STATES +} thai_below_start_state[NUM_CONSONANT_TYPES + 1/* For NOT_CONSONANT */] = +{ + B0, /* NC */ + B0, /* AC */ + B1, /* RC */ + B2, /* DC */ + B2, /* NOT_CONSONANT */ +}; + +static const struct thai_below_state_machine_edge_t { + thai_action_t action; + thai_below_state_t next_state; +} thai_below_state_machine[NUM_BELOW_STATES][NUM_MARK_TYPES] = +{ /*AV*/ /*BV*/ /*T*/ +/*B0*/ {{NOP,B0}, {NOP,B2}, {NOP, B0}}, +/*B1*/ {{NOP,B1}, {RD, B2}, {NOP, B1}}, +/*B2*/ {{NOP,B2}, {SD, B2}, {NOP, B2}}, +}; + + +static void +do_thai_pua_shaping (const hb_ot_shape_plan_t *plan HB_UNUSED, + hb_buffer_t *buffer, + hb_font_t *font) +{ +#ifdef HB_NO_OT_SHAPER_THAI_FALLBACK + return; +#endif + + thai_above_state_t above_state = thai_above_start_state[NOT_CONSONANT]; + thai_below_state_t below_state = thai_below_start_state[NOT_CONSONANT]; + unsigned int base = 0; + + hb_glyph_info_t *info = buffer->info; + unsigned int count = buffer->len; + for (unsigned int i = 0; i < count; i++) + { + thai_mark_type_t mt = get_mark_type (info[i].codepoint); + + if (mt == NOT_MARK) { + thai_consonant_type_t ct = get_consonant_type (info[i].codepoint); + above_state = thai_above_start_state[ct]; + below_state = thai_below_start_state[ct]; + base = i; + continue; + } + + const thai_above_state_machine_edge_t &above_edge = thai_above_state_machine[above_state][mt]; + const thai_below_state_machine_edge_t &below_edge = thai_below_state_machine[below_state][mt]; + above_state = above_edge.next_state; + below_state = below_edge.next_state; + + /* At least one of the above/below actions is NOP. */ + thai_action_t action = above_edge.action != NOP ? above_edge.action : below_edge.action; + + buffer->unsafe_to_break (base, i); + if (action == RD) + info[base].codepoint = thai_pua_shape (info[base].codepoint, action, font); + else + info[i].codepoint = thai_pua_shape (info[i].codepoint, action, font); + } +} + + +static void +preprocess_text_thai (const hb_ot_shape_plan_t *plan, + hb_buffer_t *buffer, + hb_font_t *font) +{ + /* This function implements the shaping logic documented here: + * + * https://linux.thai.net/~thep/th-otf/shaping.html + * + * The first shaping rule listed there is needed even if the font has Thai + * OpenType tables. The rest do fallback positioning based on PUA codepoints. + * We implement that only if there exist no Thai GSUB in the font. + */ + + /* The following is NOT specified in the MS OT Thai spec, however, it seems + * to be what Uniscribe and other engines implement. According to Eric Muller: + * + * When you have a SARA AM, decompose it in NIKHAHIT + SARA AA, *and* move the + * NIKHAHIT backwards over any above-base marks. + * + * <0E14, 0E4B, 0E33> -> <0E14, 0E4D, 0E4B, 0E32> + * + * This reordering is legit only when the NIKHAHIT comes from a SARA AM, not + * when it's there to start with. The string <0E14, 0E4B, 0E4D> is probably + * not what a user wanted, but the rendering is nevertheless nikhahit above + * chattawa. + * + * Same for Lao. + * + * Note: + * + * Uniscribe also does some below-marks reordering. Namely, it positions U+0E3A + * after U+0E38 and U+0E39. We do that by modifying the ccc for U+0E3A. + * See unicode->modified_combining_class (). Lao does NOT have a U+0E3A + * equivalent. + */ + + + /* + * Here are the characters of significance: + * + * Thai Lao + * SARA AM: U+0E33 U+0EB3 + * SARA AA: U+0E32 U+0EB2 + * Nikhahit: U+0E4D U+0ECD + * + * Testing shows that Uniscribe reorder the following marks: + * Thai: <0E31,0E34..0E37, 0E47..0E4E> + * Lao: <0EB1,0EB4..0EB7,0EBB,0EC8..0ECD> + * + * Note how the Lao versions are the same as Thai + 0x80. + */ + + /* We only get one script at a time, so a script-agnostic implementation + * is adequate here. */ +#define IS_SARA_AM(x) (((x) & ~0x0080u) == 0x0E33u) +#define NIKHAHIT_FROM_SARA_AM(x) ((x) - 0x0E33u + 0x0E4Du) +#define SARA_AA_FROM_SARA_AM(x) ((x) - 1) +#define IS_ABOVE_BASE_MARK(x) (hb_in_ranges ((x) & ~0x0080u, 0x0E34u, 0x0E37u, 0x0E47u, 0x0E4Eu, 0x0E31u, 0x0E31u, 0x0E3Bu, 0x0E3Bu)) + + buffer->clear_output (); + unsigned int count = buffer->len; + for (buffer->idx = 0; buffer->idx < count /* No need for: && buffer->successful */;) + { + hb_codepoint_t u = buffer->cur().codepoint; + if (likely (!IS_SARA_AM (u))) + { + if (unlikely (!buffer->next_glyph ())) break; + continue; + } + + /* Is SARA AM. Decompose and reorder. */ + (void) buffer->output_glyph (NIKHAHIT_FROM_SARA_AM (u)); + _hb_glyph_info_set_continuation (&buffer->prev()); + if (unlikely (!buffer->replace_glyph (SARA_AA_FROM_SARA_AM (u)))) break; + + /* Make Nikhahit be recognized as a ccc=0 mark when zeroing widths. */ + unsigned int end = buffer->out_len; + _hb_glyph_info_set_general_category (&buffer->out_info[end - 2], HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK); + + /* Ok, let's see... */ + unsigned int start = end - 2; + while (start > 0 && IS_ABOVE_BASE_MARK (buffer->out_info[start - 1].codepoint)) + start--; + + if (start + 2 < end) + { + /* Move Nikhahit (end-2) to the beginning */ + buffer->merge_out_clusters (start, end); + hb_glyph_info_t t = buffer->out_info[end - 2]; + memmove (buffer->out_info + start + 1, + buffer->out_info + start, + sizeof (buffer->out_info[0]) * (end - start - 2)); + buffer->out_info[start] = t; + } + else + { + /* Since we decomposed, and NIKHAHIT is combining, merge clusters with the + * previous cluster. */ + if (start && buffer->cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES) + buffer->merge_out_clusters (start - 1, end); + } + } + buffer->sync (); + + /* If font has Thai GSUB, we are done. */ + if (plan->props.script == HB_SCRIPT_THAI && !plan->map.found_script[0]) + do_thai_pua_shaping (plan, buffer, font); +} + +const hb_ot_shaper_t _hb_ot_shaper_thai = +{ + nullptr, /* collect_features */ + nullptr, /* override_features */ + nullptr, /* data_create */ + nullptr, /* data_destroy */ + preprocess_text_thai, + nullptr, /* postprocess_glyphs */ + nullptr, /* decompose */ + nullptr, /* compose */ + nullptr, /* setup_masks */ + nullptr, /* reorder_marks */ + HB_TAG_NONE, /* gpos_tag */ + HB_OT_SHAPE_NORMALIZATION_MODE_DEFAULT, + HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE, + false,/* fallback_position */ +}; + + +#endif diff --git a/libs/harfbuzz/src/hb-ot-shaper-use-machine.hh b/libs/harfbuzz/src/hb-ot-shaper-use-machine.hh new file mode 100644 index 00000000..41e8a34f --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-shaper-use-machine.hh @@ -0,0 +1,1080 @@ + +#line 1 "hb-ot-shaper-use-machine.rl" +/* + * Copyright © 2015 Mozilla Foundation. + * Copyright © 2015 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. + * + * Mozilla Author(s): Jonathan Kew + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_OT_SHAPER_USE_MACHINE_HH +#define HB_OT_SHAPER_USE_MACHINE_HH + +#include "hb.hh" + +#include "hb-ot-shaper-syllabic.hh" + +/* buffer var allocations */ +#define use_category() ot_shaper_var_u8_category() + +#define USE(Cat) use_syllable_machine_ex_##Cat + +enum use_syllable_type_t { + use_virama_terminated_cluster, + use_sakot_terminated_cluster, + use_standard_cluster, + use_number_joiner_terminated_cluster, + use_numeral_cluster, + use_symbol_cluster, + use_hieroglyph_cluster, + use_broken_cluster, + use_non_cluster, +}; + + +#line 57 "hb-ot-shaper-use-machine.hh" +#define use_syllable_machine_ex_B 1u +#define use_syllable_machine_ex_CGJ 6u +#define use_syllable_machine_ex_CMAbv 31u +#define use_syllable_machine_ex_CMBlw 32u +#define use_syllable_machine_ex_CS 43u +#define use_syllable_machine_ex_FAbv 24u +#define use_syllable_machine_ex_FBlw 25u +#define use_syllable_machine_ex_FMAbv 45u +#define use_syllable_machine_ex_FMBlw 46u +#define use_syllable_machine_ex_FMPst 47u +#define use_syllable_machine_ex_FPst 26u +#define use_syllable_machine_ex_G 49u +#define use_syllable_machine_ex_GB 5u +#define use_syllable_machine_ex_H 12u +#define use_syllable_machine_ex_HN 13u +#define use_syllable_machine_ex_HVM 53u +#define use_syllable_machine_ex_IS 44u +#define use_syllable_machine_ex_J 50u +#define use_syllable_machine_ex_MAbv 27u +#define use_syllable_machine_ex_MBlw 28u +#define use_syllable_machine_ex_MPre 30u +#define use_syllable_machine_ex_MPst 29u +#define use_syllable_machine_ex_N 4u +#define use_syllable_machine_ex_O 0u +#define use_syllable_machine_ex_R 18u +#define use_syllable_machine_ex_SB 51u +#define use_syllable_machine_ex_SE 52u +#define use_syllable_machine_ex_SMAbv 41u +#define use_syllable_machine_ex_SMBlw 42u +#define use_syllable_machine_ex_SUB 11u +#define use_syllable_machine_ex_Sk 48u +#define use_syllable_machine_ex_VAbv 33u +#define use_syllable_machine_ex_VBlw 34u +#define use_syllable_machine_ex_VMAbv 37u +#define use_syllable_machine_ex_VMBlw 38u +#define use_syllable_machine_ex_VMPre 23u +#define use_syllable_machine_ex_VMPst 39u +#define use_syllable_machine_ex_VPre 22u +#define use_syllable_machine_ex_VPst 35u +#define use_syllable_machine_ex_WJ 16u +#define use_syllable_machine_ex_ZWNJ 14u + + +#line 101 "hb-ot-shaper-use-machine.hh" +static const unsigned char _use_syllable_machine_trans_keys[] = { + 0u, 53u, 11u, 53u, 11u, 53u, 1u, 53u, 14u, 48u, 14u, 47u, 14u, 47u, 14u, 47u, + 14u, 46u, 14u, 46u, 14u, 14u, 14u, 48u, 14u, 48u, 14u, 48u, 1u, 14u, 14u, 48u, + 14u, 53u, 14u, 53u, 14u, 53u, 14u, 53u, 12u, 53u, 14u, 53u, 12u, 53u, 12u, 53u, + 12u, 53u, 11u, 53u, 1u, 14u, 1u, 48u, 11u, 53u, 14u, 42u, 14u, 42u, 11u, 53u, + 11u, 53u, 1u, 53u, 14u, 48u, 14u, 47u, 14u, 47u, 14u, 47u, 14u, 46u, 14u, 46u, + 14u, 14u, 14u, 48u, 14u, 48u, 14u, 48u, 1u, 14u, 14u, 48u, 14u, 53u, 14u, 53u, + 14u, 53u, 14u, 53u, 12u, 53u, 14u, 53u, 12u, 53u, 12u, 53u, 12u, 53u, 11u, 53u, + 1u, 14u, 1u, 14u, 1u, 48u, 13u, 14u, 4u, 14u, 11u, 53u, 11u, 53u, 1u, 53u, + 14u, 48u, 14u, 47u, 14u, 47u, 14u, 47u, 14u, 46u, 14u, 46u, 14u, 14u, 14u, 48u, + 14u, 48u, 14u, 48u, 1u, 14u, 14u, 48u, 14u, 53u, 14u, 53u, 14u, 53u, 14u, 53u, + 12u, 53u, 14u, 53u, 12u, 53u, 12u, 53u, 12u, 53u, 11u, 53u, 1u, 14u, 1u, 14u, + 1u, 48u, 11u, 53u, 11u, 53u, 1u, 53u, 14u, 48u, 14u, 47u, 14u, 47u, 14u, 47u, + 14u, 46u, 14u, 46u, 14u, 14u, 14u, 48u, 14u, 48u, 14u, 48u, 1u, 14u, 14u, 48u, + 14u, 53u, 14u, 53u, 14u, 53u, 14u, 53u, 12u, 53u, 14u, 53u, 12u, 53u, 12u, 53u, + 12u, 53u, 11u, 53u, 1u, 14u, 1u, 48u, 4u, 14u, 13u, 14u, 1u, 53u, 11u, 53u, + 14u, 42u, 14u, 42u, 1u, 5u, 14u, 52u, 14u, 52u, 14u, 51u, 0 +}; + +static const char _use_syllable_machine_key_spans[] = { + 54, 43, 43, 53, 35, 34, 34, 34, + 33, 33, 1, 35, 35, 35, 14, 35, + 40, 40, 40, 40, 42, 40, 42, 42, + 42, 43, 14, 48, 43, 29, 29, 43, + 43, 53, 35, 34, 34, 34, 33, 33, + 1, 35, 35, 35, 14, 35, 40, 40, + 40, 40, 42, 40, 42, 42, 42, 43, + 14, 14, 48, 2, 11, 43, 43, 53, + 35, 34, 34, 34, 33, 33, 1, 35, + 35, 35, 14, 35, 40, 40, 40, 40, + 42, 40, 42, 42, 42, 43, 14, 14, + 48, 43, 43, 53, 35, 34, 34, 34, + 33, 33, 1, 35, 35, 35, 14, 35, + 40, 40, 40, 40, 42, 40, 42, 42, + 42, 43, 14, 48, 11, 2, 53, 43, + 29, 29, 5, 39, 39, 38 +}; + +static const short _use_syllable_machine_index_offsets[] = { + 0, 55, 99, 143, 197, 233, 268, 303, + 338, 372, 406, 408, 444, 480, 516, 531, + 567, 608, 649, 690, 731, 774, 815, 858, + 901, 944, 988, 1003, 1052, 1096, 1126, 1156, + 1200, 1244, 1298, 1334, 1369, 1404, 1439, 1473, + 1507, 1509, 1545, 1581, 1617, 1632, 1668, 1709, + 1750, 1791, 1832, 1875, 1916, 1959, 2002, 2045, + 2089, 2104, 2119, 2168, 2171, 2183, 2227, 2271, + 2325, 2361, 2396, 2431, 2466, 2500, 2534, 2536, + 2572, 2608, 2644, 2659, 2695, 2736, 2777, 2818, + 2859, 2902, 2943, 2986, 3029, 3072, 3116, 3131, + 3146, 3195, 3239, 3283, 3337, 3373, 3408, 3443, + 3478, 3512, 3546, 3548, 3584, 3620, 3656, 3671, + 3707, 3748, 3789, 3830, 3871, 3914, 3955, 3998, + 4041, 4084, 4128, 4143, 4192, 4204, 4207, 4261, + 4305, 4335, 4365, 4371, 4411, 4451 +}; + +static const unsigned char _use_syllable_machine_indicies[] = { + 0, 1, 2, 2, 3, 4, 2, 2, + 2, 2, 2, 5, 6, 7, 8, 2, + 2, 2, 9, 2, 2, 2, 10, 11, + 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 2, 24, 25, 26, + 2, 27, 28, 29, 30, 31, 32, 33, + 30, 34, 2, 35, 2, 36, 2, 38, + 39, 37, 40, 37, 37, 37, 37, 37, + 37, 37, 41, 42, 43, 44, 45, 46, + 47, 48, 49, 50, 51, 52, 53, 54, + 37, 55, 56, 57, 37, 58, 59, 37, + 60, 61, 62, 63, 60, 37, 37, 37, + 37, 64, 37, 38, 39, 37, 40, 37, + 37, 37, 37, 37, 37, 37, 41, 42, + 43, 44, 45, 46, 47, 48, 49, 51, + 51, 52, 53, 54, 37, 55, 56, 57, + 37, 37, 37, 37, 60, 61, 62, 63, + 60, 37, 37, 37, 37, 64, 37, 38, + 37, 37, 37, 37, 37, 37, 37, 37, + 37, 37, 37, 37, 40, 37, 37, 37, + 37, 37, 37, 37, 37, 42, 43, 44, + 45, 37, 37, 37, 37, 37, 37, 37, + 37, 37, 37, 55, 56, 57, 37, 37, + 37, 37, 37, 61, 62, 63, 65, 37, + 37, 37, 37, 42, 37, 40, 37, 37, + 37, 37, 37, 37, 37, 37, 42, 43, + 44, 45, 37, 37, 37, 37, 37, 37, + 37, 37, 37, 37, 55, 56, 57, 37, + 37, 37, 37, 37, 61, 62, 63, 65, + 37, 40, 37, 37, 37, 37, 37, 37, + 37, 37, 37, 43, 44, 45, 37, 37, + 37, 37, 37, 37, 37, 37, 37, 37, + 37, 37, 37, 37, 37, 37, 37, 37, + 61, 62, 63, 37, 40, 37, 37, 37, + 37, 37, 37, 37, 37, 37, 37, 44, + 45, 37, 37, 37, 37, 37, 37, 37, + 37, 37, 37, 37, 37, 37, 37, 37, + 37, 37, 37, 61, 62, 63, 37, 40, + 37, 37, 37, 37, 37, 37, 37, 37, + 37, 37, 37, 45, 37, 37, 37, 37, + 37, 37, 37, 37, 37, 37, 37, 37, + 37, 37, 37, 37, 37, 37, 61, 62, + 63, 37, 40, 37, 37, 37, 37, 37, + 37, 37, 37, 37, 37, 37, 37, 37, + 37, 37, 37, 37, 37, 37, 37, 37, + 37, 37, 37, 37, 37, 37, 37, 37, + 37, 61, 62, 37, 40, 37, 37, 37, + 37, 37, 37, 37, 37, 37, 37, 37, + 37, 37, 37, 37, 37, 37, 37, 37, + 37, 37, 37, 37, 37, 37, 37, 37, + 37, 37, 37, 37, 62, 37, 40, 37, + 40, 37, 37, 37, 37, 37, 37, 37, + 37, 37, 43, 44, 45, 37, 37, 37, + 37, 37, 37, 37, 37, 37, 37, 55, + 56, 57, 37, 37, 37, 37, 37, 61, + 62, 63, 65, 37, 40, 37, 37, 37, + 37, 37, 37, 37, 37, 37, 43, 44, + 45, 37, 37, 37, 37, 37, 37, 37, + 37, 37, 37, 37, 56, 57, 37, 37, + 37, 37, 37, 61, 62, 63, 65, 37, + 40, 37, 37, 37, 37, 37, 37, 37, + 37, 37, 43, 44, 45, 37, 37, 37, + 37, 37, 37, 37, 37, 37, 37, 37, + 37, 57, 37, 37, 37, 37, 37, 61, + 62, 63, 65, 37, 66, 37, 37, 37, + 37, 37, 37, 37, 37, 37, 37, 37, + 37, 40, 37, 40, 37, 37, 37, 37, + 37, 37, 37, 37, 37, 43, 44, 45, + 37, 37, 37, 37, 37, 37, 37, 37, + 37, 37, 37, 37, 37, 37, 37, 37, + 37, 37, 61, 62, 63, 65, 37, 40, + 37, 37, 37, 37, 37, 37, 37, 41, + 42, 43, 44, 45, 37, 37, 37, 37, + 37, 37, 52, 53, 54, 37, 55, 56, + 57, 37, 37, 37, 37, 37, 61, 62, + 63, 65, 37, 37, 37, 37, 42, 37, + 40, 37, 37, 37, 37, 37, 37, 37, + 37, 42, 43, 44, 45, 37, 37, 37, + 37, 37, 37, 52, 53, 54, 37, 55, + 56, 57, 37, 37, 37, 37, 37, 61, + 62, 63, 65, 37, 37, 37, 37, 42, + 37, 40, 37, 37, 37, 37, 37, 37, + 37, 37, 42, 43, 44, 45, 37, 37, + 37, 37, 37, 37, 37, 53, 54, 37, + 55, 56, 57, 37, 37, 37, 37, 37, + 61, 62, 63, 65, 37, 37, 37, 37, + 42, 37, 40, 37, 37, 37, 37, 37, + 37, 37, 37, 42, 43, 44, 45, 37, + 37, 37, 37, 37, 37, 37, 37, 54, + 37, 55, 56, 57, 37, 37, 37, 37, + 37, 61, 62, 63, 65, 37, 37, 37, + 37, 42, 37, 67, 37, 40, 37, 37, + 37, 37, 37, 37, 37, 41, 42, 43, + 44, 45, 37, 47, 48, 37, 37, 37, + 52, 53, 54, 37, 55, 56, 57, 37, + 37, 37, 37, 37, 61, 62, 63, 65, + 37, 37, 37, 37, 42, 37, 40, 37, + 37, 37, 37, 37, 37, 37, 37, 42, + 43, 44, 45, 37, 37, 37, 37, 37, + 37, 37, 37, 37, 37, 55, 56, 57, + 37, 37, 37, 37, 37, 61, 62, 63, + 65, 37, 37, 37, 37, 42, 37, 67, + 37, 40, 37, 37, 37, 37, 37, 37, + 37, 41, 42, 43, 44, 45, 37, 37, + 48, 37, 37, 37, 52, 53, 54, 37, + 55, 56, 57, 37, 37, 37, 37, 37, + 61, 62, 63, 65, 37, 37, 37, 37, + 42, 37, 67, 37, 40, 37, 37, 37, + 37, 37, 37, 37, 41, 42, 43, 44, + 45, 37, 37, 37, 37, 37, 37, 52, + 53, 54, 37, 55, 56, 57, 37, 37, + 37, 37, 37, 61, 62, 63, 65, 37, + 37, 37, 37, 42, 37, 67, 37, 40, + 37, 37, 37, 37, 37, 37, 37, 41, + 42, 43, 44, 45, 46, 47, 48, 37, + 37, 37, 52, 53, 54, 37, 55, 56, + 57, 37, 37, 37, 37, 37, 61, 62, + 63, 65, 37, 37, 37, 37, 42, 37, + 38, 39, 37, 40, 37, 37, 37, 37, + 37, 37, 37, 41, 42, 43, 44, 45, + 46, 47, 48, 49, 37, 51, 52, 53, + 54, 37, 55, 56, 57, 37, 37, 37, + 37, 60, 61, 62, 63, 60, 37, 37, + 37, 37, 64, 37, 38, 37, 37, 37, + 37, 37, 37, 37, 37, 37, 37, 37, + 37, 40, 37, 38, 37, 37, 37, 37, + 37, 37, 37, 37, 37, 37, 37, 37, + 40, 37, 37, 37, 37, 37, 37, 37, + 37, 42, 43, 44, 45, 37, 37, 37, + 37, 37, 37, 37, 37, 37, 37, 55, + 56, 57, 37, 37, 37, 37, 37, 61, + 62, 63, 65, 37, 38, 39, 37, 40, + 37, 37, 37, 37, 37, 37, 37, 41, + 42, 43, 44, 45, 46, 47, 48, 49, + 50, 51, 52, 53, 54, 37, 55, 56, + 57, 37, 37, 37, 37, 60, 61, 62, + 63, 60, 37, 37, 37, 37, 64, 37, + 40, 37, 37, 37, 37, 37, 37, 37, + 37, 37, 37, 37, 37, 37, 37, 37, + 37, 37, 37, 37, 37, 37, 37, 37, + 37, 37, 37, 58, 59, 37, 40, 37, + 37, 37, 37, 37, 37, 37, 37, 37, + 37, 37, 37, 37, 37, 37, 37, 37, + 37, 37, 37, 37, 37, 37, 37, 37, + 37, 37, 59, 37, 69, 70, 68, 71, + 68, 68, 68, 68, 68, 68, 68, 72, + 73, 74, 75, 76, 77, 78, 79, 80, + 1, 81, 82, 83, 84, 68, 85, 86, + 87, 68, 68, 68, 68, 88, 89, 90, + 91, 92, 68, 68, 68, 68, 93, 68, + 69, 70, 68, 71, 68, 68, 68, 68, + 68, 68, 68, 72, 73, 74, 75, 76, + 77, 78, 79, 80, 81, 81, 82, 83, + 84, 68, 85, 86, 87, 68, 68, 68, + 68, 88, 89, 90, 91, 92, 68, 68, + 68, 68, 93, 68, 69, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, + 68, 71, 68, 68, 68, 68, 68, 68, + 68, 68, 73, 74, 75, 76, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, + 85, 86, 87, 68, 68, 68, 68, 68, + 89, 90, 91, 94, 68, 68, 68, 68, + 73, 68, 71, 68, 68, 68, 68, 68, + 68, 68, 68, 73, 74, 75, 76, 68, + 68, 68, 68, 68, 68, 68, 68, 68, + 68, 85, 86, 87, 68, 68, 68, 68, + 68, 89, 90, 91, 94, 68, 71, 68, + 68, 68, 68, 68, 68, 68, 68, 68, + 74, 75, 76, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 89, 90, 91, + 68, 71, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 75, 76, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, + 89, 90, 91, 68, 71, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, + 76, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 89, 90, 91, 68, 71, + 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 89, 90, + 68, 71, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, + 68, 90, 68, 71, 68, 71, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 74, + 75, 76, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 85, 86, 87, 68, + 68, 68, 68, 68, 89, 90, 91, 94, + 68, 71, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 74, 75, 76, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, + 68, 86, 87, 68, 68, 68, 68, 68, + 89, 90, 91, 94, 68, 71, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 74, + 75, 76, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 87, 68, + 68, 68, 68, 68, 89, 90, 91, 94, + 68, 96, 95, 95, 95, 95, 95, 95, + 95, 95, 95, 95, 95, 95, 97, 95, + 71, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 74, 75, 76, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 89, + 90, 91, 94, 68, 71, 68, 68, 68, + 68, 68, 68, 68, 72, 73, 74, 75, + 76, 68, 68, 68, 68, 68, 68, 82, + 83, 84, 68, 85, 86, 87, 68, 68, + 68, 68, 68, 89, 90, 91, 94, 68, + 68, 68, 68, 73, 68, 71, 68, 68, + 68, 68, 68, 68, 68, 68, 73, 74, + 75, 76, 68, 68, 68, 68, 68, 68, + 82, 83, 84, 68, 85, 86, 87, 68, + 68, 68, 68, 68, 89, 90, 91, 94, + 68, 68, 68, 68, 73, 68, 71, 68, + 68, 68, 68, 68, 68, 68, 68, 73, + 74, 75, 76, 68, 68, 68, 68, 68, + 68, 68, 83, 84, 68, 85, 86, 87, + 68, 68, 68, 68, 68, 89, 90, 91, + 94, 68, 68, 68, 68, 73, 68, 71, + 68, 68, 68, 68, 68, 68, 68, 68, + 73, 74, 75, 76, 68, 68, 68, 68, + 68, 68, 68, 68, 84, 68, 85, 86, + 87, 68, 68, 68, 68, 68, 89, 90, + 91, 94, 68, 68, 68, 68, 73, 68, + 98, 68, 71, 68, 68, 68, 68, 68, + 68, 68, 72, 73, 74, 75, 76, 68, + 78, 79, 68, 68, 68, 82, 83, 84, + 68, 85, 86, 87, 68, 68, 68, 68, + 68, 89, 90, 91, 94, 68, 68, 68, + 68, 73, 68, 71, 68, 68, 68, 68, + 68, 68, 68, 68, 73, 74, 75, 76, + 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 85, 86, 87, 68, 68, 68, + 68, 68, 89, 90, 91, 94, 68, 68, + 68, 68, 73, 68, 98, 68, 71, 68, + 68, 68, 68, 68, 68, 68, 72, 73, + 74, 75, 76, 68, 68, 79, 68, 68, + 68, 82, 83, 84, 68, 85, 86, 87, + 68, 68, 68, 68, 68, 89, 90, 91, + 94, 68, 68, 68, 68, 73, 68, 98, + 68, 71, 68, 68, 68, 68, 68, 68, + 68, 72, 73, 74, 75, 76, 68, 68, + 68, 68, 68, 68, 82, 83, 84, 68, + 85, 86, 87, 68, 68, 68, 68, 68, + 89, 90, 91, 94, 68, 68, 68, 68, + 73, 68, 98, 68, 71, 68, 68, 68, + 68, 68, 68, 68, 72, 73, 74, 75, + 76, 77, 78, 79, 68, 68, 68, 82, + 83, 84, 68, 85, 86, 87, 68, 68, + 68, 68, 68, 89, 90, 91, 94, 68, + 68, 68, 68, 73, 68, 69, 70, 68, + 71, 68, 68, 68, 68, 68, 68, 68, + 72, 73, 74, 75, 76, 77, 78, 79, + 80, 68, 81, 82, 83, 84, 68, 85, + 86, 87, 68, 68, 68, 68, 88, 89, + 90, 91, 92, 68, 68, 68, 68, 93, + 68, 69, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 100, 99, + 69, 95, 95, 95, 95, 95, 95, 95, + 95, 95, 95, 95, 95, 97, 95, 69, + 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 71, 68, 68, 68, + 68, 68, 68, 68, 68, 73, 74, 75, + 76, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 85, 86, 87, 68, 68, + 68, 68, 68, 89, 90, 91, 94, 68, + 102, 103, 101, 3, 104, 104, 104, 104, + 104, 104, 104, 104, 104, 105, 104, 106, + 107, 68, 71, 68, 68, 68, 68, 68, + 68, 68, 108, 109, 110, 111, 112, 113, + 114, 115, 116, 117, 118, 119, 120, 121, + 68, 122, 123, 124, 68, 58, 59, 68, + 125, 126, 127, 128, 129, 68, 68, 68, + 68, 130, 68, 106, 107, 68, 71, 68, + 68, 68, 68, 68, 68, 68, 108, 109, + 110, 111, 112, 113, 114, 115, 116, 118, + 118, 119, 120, 121, 68, 122, 123, 124, + 68, 68, 68, 68, 125, 126, 127, 128, + 129, 68, 68, 68, 68, 130, 68, 106, + 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 71, 68, 68, 68, + 68, 68, 68, 68, 68, 109, 110, 111, + 112, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 122, 123, 124, 68, 68, + 68, 68, 68, 126, 127, 128, 131, 68, + 68, 68, 68, 109, 68, 71, 68, 68, + 68, 68, 68, 68, 68, 68, 109, 110, + 111, 112, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 122, 123, 124, 68, + 68, 68, 68, 68, 126, 127, 128, 131, + 68, 71, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 110, 111, 112, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, + 126, 127, 128, 68, 71, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 111, + 112, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 126, 127, 128, 68, 71, + 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 112, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 126, 127, + 128, 68, 71, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, + 68, 126, 127, 68, 71, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 127, 68, 71, 68, + 71, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 110, 111, 112, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 122, + 123, 124, 68, 68, 68, 68, 68, 126, + 127, 128, 131, 68, 71, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 110, 111, + 112, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 123, 124, 68, 68, + 68, 68, 68, 126, 127, 128, 131, 68, + 71, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 110, 111, 112, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, + 68, 124, 68, 68, 68, 68, 68, 126, + 127, 128, 131, 68, 132, 95, 95, 95, + 95, 95, 95, 95, 95, 95, 95, 95, + 95, 97, 95, 71, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 110, 111, 112, + 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 126, 127, 128, 131, 68, 71, + 68, 68, 68, 68, 68, 68, 68, 108, + 109, 110, 111, 112, 68, 68, 68, 68, + 68, 68, 119, 120, 121, 68, 122, 123, + 124, 68, 68, 68, 68, 68, 126, 127, + 128, 131, 68, 68, 68, 68, 109, 68, + 71, 68, 68, 68, 68, 68, 68, 68, + 68, 109, 110, 111, 112, 68, 68, 68, + 68, 68, 68, 119, 120, 121, 68, 122, + 123, 124, 68, 68, 68, 68, 68, 126, + 127, 128, 131, 68, 68, 68, 68, 109, + 68, 71, 68, 68, 68, 68, 68, 68, + 68, 68, 109, 110, 111, 112, 68, 68, + 68, 68, 68, 68, 68, 120, 121, 68, + 122, 123, 124, 68, 68, 68, 68, 68, + 126, 127, 128, 131, 68, 68, 68, 68, + 109, 68, 71, 68, 68, 68, 68, 68, + 68, 68, 68, 109, 110, 111, 112, 68, + 68, 68, 68, 68, 68, 68, 68, 121, + 68, 122, 123, 124, 68, 68, 68, 68, + 68, 126, 127, 128, 131, 68, 68, 68, + 68, 109, 68, 133, 68, 71, 68, 68, + 68, 68, 68, 68, 68, 108, 109, 110, + 111, 112, 68, 114, 115, 68, 68, 68, + 119, 120, 121, 68, 122, 123, 124, 68, + 68, 68, 68, 68, 126, 127, 128, 131, + 68, 68, 68, 68, 109, 68, 71, 68, + 68, 68, 68, 68, 68, 68, 68, 109, + 110, 111, 112, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 122, 123, 124, + 68, 68, 68, 68, 68, 126, 127, 128, + 131, 68, 68, 68, 68, 109, 68, 133, + 68, 71, 68, 68, 68, 68, 68, 68, + 68, 108, 109, 110, 111, 112, 68, 68, + 115, 68, 68, 68, 119, 120, 121, 68, + 122, 123, 124, 68, 68, 68, 68, 68, + 126, 127, 128, 131, 68, 68, 68, 68, + 109, 68, 133, 68, 71, 68, 68, 68, + 68, 68, 68, 68, 108, 109, 110, 111, + 112, 68, 68, 68, 68, 68, 68, 119, + 120, 121, 68, 122, 123, 124, 68, 68, + 68, 68, 68, 126, 127, 128, 131, 68, + 68, 68, 68, 109, 68, 133, 68, 71, + 68, 68, 68, 68, 68, 68, 68, 108, + 109, 110, 111, 112, 113, 114, 115, 68, + 68, 68, 119, 120, 121, 68, 122, 123, + 124, 68, 68, 68, 68, 68, 126, 127, + 128, 131, 68, 68, 68, 68, 109, 68, + 106, 107, 68, 71, 68, 68, 68, 68, + 68, 68, 68, 108, 109, 110, 111, 112, + 113, 114, 115, 116, 68, 118, 119, 120, + 121, 68, 122, 123, 124, 68, 68, 68, + 68, 125, 126, 127, 128, 129, 68, 68, + 68, 68, 130, 68, 106, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 100, 99, 106, 95, 95, 95, 95, + 95, 95, 95, 95, 95, 95, 95, 95, + 97, 95, 106, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 71, + 68, 68, 68, 68, 68, 68, 68, 68, + 109, 110, 111, 112, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 122, 123, + 124, 68, 68, 68, 68, 68, 126, 127, + 128, 131, 68, 106, 107, 68, 71, 68, + 68, 68, 68, 68, 68, 68, 108, 109, + 110, 111, 112, 113, 114, 115, 116, 117, + 118, 119, 120, 121, 68, 122, 123, 124, + 68, 68, 68, 68, 125, 126, 127, 128, + 129, 68, 68, 68, 68, 130, 68, 5, + 6, 134, 8, 134, 134, 134, 134, 134, + 134, 134, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 20, 20, 21, 22, 23, + 134, 24, 25, 26, 134, 134, 134, 134, + 30, 31, 32, 33, 30, 134, 134, 134, + 134, 36, 134, 5, 134, 134, 134, 134, + 134, 134, 134, 134, 134, 134, 134, 134, + 8, 134, 134, 134, 134, 134, 134, 134, + 134, 11, 12, 13, 14, 134, 134, 134, + 134, 134, 134, 134, 134, 134, 134, 24, + 25, 26, 134, 134, 134, 134, 134, 31, + 32, 33, 135, 134, 134, 134, 134, 11, + 134, 8, 134, 134, 134, 134, 134, 134, + 134, 134, 11, 12, 13, 14, 134, 134, + 134, 134, 134, 134, 134, 134, 134, 134, + 24, 25, 26, 134, 134, 134, 134, 134, + 31, 32, 33, 135, 134, 8, 134, 134, + 134, 134, 134, 134, 134, 134, 134, 12, + 13, 14, 134, 134, 134, 134, 134, 134, + 134, 134, 134, 134, 134, 134, 134, 134, + 134, 134, 134, 134, 31, 32, 33, 134, + 8, 134, 134, 134, 134, 134, 134, 134, + 134, 134, 134, 13, 14, 134, 134, 134, + 134, 134, 134, 134, 134, 134, 134, 134, + 134, 134, 134, 134, 134, 134, 134, 31, + 32, 33, 134, 8, 134, 134, 134, 134, + 134, 134, 134, 134, 134, 134, 134, 14, + 134, 134, 134, 134, 134, 134, 134, 134, + 134, 134, 134, 134, 134, 134, 134, 134, + 134, 134, 31, 32, 33, 134, 8, 134, + 134, 134, 134, 134, 134, 134, 134, 134, + 134, 134, 134, 134, 134, 134, 134, 134, + 134, 134, 134, 134, 134, 134, 134, 134, + 134, 134, 134, 134, 134, 31, 32, 134, + 8, 134, 134, 134, 134, 134, 134, 134, + 134, 134, 134, 134, 134, 134, 134, 134, + 134, 134, 134, 134, 134, 134, 134, 134, + 134, 134, 134, 134, 134, 134, 134, 134, + 32, 134, 8, 134, 8, 134, 134, 134, + 134, 134, 134, 134, 134, 134, 12, 13, + 14, 134, 134, 134, 134, 134, 134, 134, + 134, 134, 134, 24, 25, 26, 134, 134, + 134, 134, 134, 31, 32, 33, 135, 134, + 8, 134, 134, 134, 134, 134, 134, 134, + 134, 134, 12, 13, 14, 134, 134, 134, + 134, 134, 134, 134, 134, 134, 134, 134, + 25, 26, 134, 134, 134, 134, 134, 31, + 32, 33, 135, 134, 8, 134, 134, 134, + 134, 134, 134, 134, 134, 134, 12, 13, + 14, 134, 134, 134, 134, 134, 134, 134, + 134, 134, 134, 134, 134, 26, 134, 134, + 134, 134, 134, 31, 32, 33, 135, 134, + 136, 134, 134, 134, 134, 134, 134, 134, + 134, 134, 134, 134, 134, 8, 134, 8, + 134, 134, 134, 134, 134, 134, 134, 134, + 134, 12, 13, 14, 134, 134, 134, 134, + 134, 134, 134, 134, 134, 134, 134, 134, + 134, 134, 134, 134, 134, 134, 31, 32, + 33, 135, 134, 8, 134, 134, 134, 134, + 134, 134, 134, 10, 11, 12, 13, 14, + 134, 134, 134, 134, 134, 134, 21, 22, + 23, 134, 24, 25, 26, 134, 134, 134, + 134, 134, 31, 32, 33, 135, 134, 134, + 134, 134, 11, 134, 8, 134, 134, 134, + 134, 134, 134, 134, 134, 11, 12, 13, + 14, 134, 134, 134, 134, 134, 134, 21, + 22, 23, 134, 24, 25, 26, 134, 134, + 134, 134, 134, 31, 32, 33, 135, 134, + 134, 134, 134, 11, 134, 8, 134, 134, + 134, 134, 134, 134, 134, 134, 11, 12, + 13, 14, 134, 134, 134, 134, 134, 134, + 134, 22, 23, 134, 24, 25, 26, 134, + 134, 134, 134, 134, 31, 32, 33, 135, + 134, 134, 134, 134, 11, 134, 8, 134, + 134, 134, 134, 134, 134, 134, 134, 11, + 12, 13, 14, 134, 134, 134, 134, 134, + 134, 134, 134, 23, 134, 24, 25, 26, + 134, 134, 134, 134, 134, 31, 32, 33, + 135, 134, 134, 134, 134, 11, 134, 137, + 134, 8, 134, 134, 134, 134, 134, 134, + 134, 10, 11, 12, 13, 14, 134, 16, + 17, 134, 134, 134, 21, 22, 23, 134, + 24, 25, 26, 134, 134, 134, 134, 134, + 31, 32, 33, 135, 134, 134, 134, 134, + 11, 134, 8, 134, 134, 134, 134, 134, + 134, 134, 134, 11, 12, 13, 14, 134, + 134, 134, 134, 134, 134, 134, 134, 134, + 134, 24, 25, 26, 134, 134, 134, 134, + 134, 31, 32, 33, 135, 134, 134, 134, + 134, 11, 134, 137, 134, 8, 134, 134, + 134, 134, 134, 134, 134, 10, 11, 12, + 13, 14, 134, 134, 17, 134, 134, 134, + 21, 22, 23, 134, 24, 25, 26, 134, + 134, 134, 134, 134, 31, 32, 33, 135, + 134, 134, 134, 134, 11, 134, 137, 134, + 8, 134, 134, 134, 134, 134, 134, 134, + 10, 11, 12, 13, 14, 134, 134, 134, + 134, 134, 134, 21, 22, 23, 134, 24, + 25, 26, 134, 134, 134, 134, 134, 31, + 32, 33, 135, 134, 134, 134, 134, 11, + 134, 137, 134, 8, 134, 134, 134, 134, + 134, 134, 134, 10, 11, 12, 13, 14, + 15, 16, 17, 134, 134, 134, 21, 22, + 23, 134, 24, 25, 26, 134, 134, 134, + 134, 134, 31, 32, 33, 135, 134, 134, + 134, 134, 11, 134, 5, 6, 134, 8, + 134, 134, 134, 134, 134, 134, 134, 10, + 11, 12, 13, 14, 15, 16, 17, 18, + 134, 20, 21, 22, 23, 134, 24, 25, + 26, 134, 134, 134, 134, 30, 31, 32, + 33, 30, 134, 134, 134, 134, 36, 134, + 5, 134, 134, 134, 134, 134, 134, 134, + 134, 134, 134, 134, 134, 8, 134, 5, + 134, 134, 134, 134, 134, 134, 134, 134, + 134, 134, 134, 134, 8, 134, 134, 134, + 134, 134, 134, 134, 134, 11, 12, 13, + 14, 134, 134, 134, 134, 134, 134, 134, + 134, 134, 134, 24, 25, 26, 134, 134, + 134, 134, 134, 31, 32, 33, 135, 134, + 138, 134, 134, 134, 134, 134, 134, 134, + 134, 134, 8, 134, 7, 8, 134, 1, + 134, 134, 134, 1, 134, 134, 134, 134, + 134, 5, 6, 7, 8, 134, 134, 134, + 134, 134, 134, 134, 10, 11, 12, 13, + 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 134, 24, 25, 26, 134, 27, + 28, 134, 30, 31, 32, 33, 30, 134, + 134, 134, 134, 36, 134, 5, 6, 134, + 8, 134, 134, 134, 134, 134, 134, 134, + 10, 11, 12, 13, 14, 15, 16, 17, + 18, 19, 20, 21, 22, 23, 134, 24, + 25, 26, 134, 134, 134, 134, 30, 31, + 32, 33, 30, 134, 134, 134, 134, 36, + 134, 8, 134, 134, 134, 134, 134, 134, + 134, 134, 134, 134, 134, 134, 134, 134, + 134, 134, 134, 134, 134, 134, 134, 134, + 134, 134, 134, 134, 27, 28, 134, 8, + 134, 134, 134, 134, 134, 134, 134, 134, + 134, 134, 134, 134, 134, 134, 134, 134, + 134, 134, 134, 134, 134, 134, 134, 134, + 134, 134, 134, 28, 134, 1, 139, 139, + 139, 1, 139, 141, 140, 140, 140, 140, + 140, 140, 140, 140, 140, 140, 140, 140, + 140, 140, 140, 140, 140, 140, 140, 140, + 140, 140, 140, 140, 140, 140, 140, 140, + 140, 140, 140, 140, 140, 140, 140, 142, + 140, 34, 140, 141, 140, 140, 140, 140, + 140, 140, 140, 140, 140, 140, 140, 140, + 140, 140, 140, 140, 140, 140, 140, 140, + 140, 140, 140, 140, 140, 140, 140, 140, + 140, 140, 140, 140, 140, 140, 34, 142, + 140, 142, 140, 141, 140, 140, 140, 140, + 140, 140, 140, 140, 140, 140, 140, 140, + 140, 140, 140, 140, 140, 140, 140, 140, + 140, 140, 140, 140, 140, 140, 140, 140, + 140, 140, 140, 140, 140, 140, 34, 140, + 35, 140, 0 +}; + +static const char _use_syllable_machine_trans_targs[] = { + 1, 31, 0, 59, 61, 90, 91, 116, + 0, 118, 104, 92, 93, 94, 95, 108, + 110, 111, 112, 119, 113, 105, 106, 107, + 99, 100, 101, 120, 121, 122, 114, 96, + 97, 98, 123, 125, 115, 0, 2, 3, + 0, 16, 4, 5, 6, 7, 20, 22, + 23, 24, 28, 25, 17, 18, 19, 11, + 12, 13, 29, 30, 26, 8, 9, 10, + 27, 14, 15, 21, 0, 32, 33, 0, + 46, 34, 35, 36, 37, 50, 52, 53, + 54, 55, 47, 48, 49, 41, 42, 43, + 56, 38, 39, 40, 57, 58, 44, 0, + 45, 0, 51, 0, 0, 0, 60, 0, + 0, 0, 62, 63, 76, 64, 65, 66, + 67, 80, 82, 83, 84, 89, 85, 77, + 78, 79, 71, 72, 73, 86, 68, 69, + 70, 87, 88, 74, 75, 81, 0, 102, + 103, 109, 117, 0, 0, 0, 124 +}; + +static const char _use_syllable_machine_trans_actions[] = { + 0, 0, 3, 0, 0, 0, 0, 0, + 4, 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, 5, 0, 0, + 6, 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, 7, 0, 0, 8, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 9, + 0, 10, 0, 11, 12, 13, 0, 14, + 15, 16, 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, 17, 0, + 0, 0, 0, 18, 19, 20, 0 +}; + +static const char _use_syllable_machine_to_state_actions[] = { + 1, 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, 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, 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 +}; + +static const char _use_syllable_machine_from_state_actions[] = { + 2, 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, 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, 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 +}; + +static const short _use_syllable_machine_eof_trans[] = { + 0, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 69, + 69, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 69, 69, 96, 69, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 69, + 100, 96, 69, 102, 105, 69, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 96, 69, 69, 69, 69, 69, + 69, 69, 69, 69, 69, 69, 100, 96, + 69, 69, 135, 135, 135, 135, 135, 135, + 135, 135, 135, 135, 135, 135, 135, 135, + 135, 135, 135, 135, 135, 135, 135, 135, + 135, 135, 135, 135, 135, 135, 135, 135, + 135, 135, 140, 141, 141, 141 +}; + +static const int use_syllable_machine_start = 0; +static const int use_syllable_machine_first_final = 0; +static const int use_syllable_machine_error = -1; + +static const int use_syllable_machine_en_main = 0; + + +#line 58 "hb-ot-shaper-use-machine.rl" + + + +#line 182 "hb-ot-shaper-use-machine.rl" + + +#define found_syllable(syllable_type) \ + HB_STMT_START { \ + if (0) fprintf (stderr, "syllable %d..%d %s\n", (*ts).second.first, (*te).second.first, #syllable_type); \ + for (unsigned i = (*ts).second.first; i < (*te).second.first; ++i) \ + info[i].syllable() = (syllable_serial << 4) | syllable_type; \ + syllable_serial++; \ + if (unlikely (syllable_serial == 16)) syllable_serial = 1; \ + } HB_STMT_END + + +template +struct machine_index_t : + hb_iter_with_fallback_t, + typename Iter::item_t> +{ + machine_index_t (const Iter& it) : it (it) {} + machine_index_t (const machine_index_t& o) : hb_iter_with_fallback_t, + typename Iter::item_t> (), + it (o.it), is_null (o.is_null) {} + + static constexpr bool is_random_access_iterator = Iter::is_random_access_iterator; + static constexpr bool is_sorted_iterator = Iter::is_sorted_iterator; + + typename Iter::item_t __item__ () const { return *it; } + typename Iter::item_t __item_at__ (unsigned i) const { return it[i]; } + unsigned __len__ () const { return it.len (); } + void __next__ () { ++it; } + void __forward__ (unsigned n) { it += n; } + void __prev__ () { --it; } + void __rewind__ (unsigned n) { it -= n; } + + void operator = (unsigned n) + { + assert (n == 0); + is_null = true; + } + explicit operator bool () { return !is_null; } + + void operator = (const machine_index_t& o) + { + is_null = o.is_null; + unsigned index = (*it).first; + unsigned n = (*o.it).first; + if (index < n) it += n - index; else if (index > n) it -= index - n; + } + bool operator == (const machine_index_t& o) const + { return is_null ? o.is_null : !o.is_null && (*it).first == (*o.it).first; } + bool operator != (const machine_index_t& o) const { return !(*this == o); } + + private: + Iter it; + bool is_null = false; +}; +struct +{ + template + machine_index_t> + operator () (Iter&& it) const + { return machine_index_t> (hb_iter (it)); } +} +HB_FUNCOBJ (machine_index); + + + +static bool +not_ccs_default_ignorable (const hb_glyph_info_t &i) +{ return i.use_category() != USE(CGJ); } + +static inline void +find_syllables_use (hb_buffer_t *buffer) +{ + hb_glyph_info_t *info = buffer->info; + auto p = + + hb_iter (info, buffer->len) + | hb_enumerate + | hb_filter ([] (const hb_glyph_info_t &i) { return not_ccs_default_ignorable (i); }, + hb_second) + | hb_filter ([&] (const hb_pair_t p) + { + if (p.second.use_category() == USE(ZWNJ)) + for (unsigned i = p.first + 1; i < buffer->len; ++i) + if (not_ccs_default_ignorable (info[i])) + return !_hb_glyph_info_is_unicode_mark (&info[i]); + return true; + }) + | hb_enumerate + | machine_index + ; + auto pe = p + p.len (); + auto eof = +pe; + auto ts = +p; + auto te = +p; + unsigned int act HB_UNUSED; + int cs; + +#line 933 "hb-ot-shaper-use-machine.hh" + { + cs = use_syllable_machine_start; + ts = 0; + te = 0; + act = 0; + } + +#line 282 "hb-ot-shaper-use-machine.rl" + + + unsigned int syllable_serial = 1; + +#line 946 "hb-ot-shaper-use-machine.hh" + { + int _slen; + int _trans; + const unsigned char *_keys; + const unsigned char *_inds; + if ( p == pe ) + goto _test_eof; +_resume: + switch ( _use_syllable_machine_from_state_actions[cs] ) { + case 2: +#line 1 "NONE" + {ts = p;} + break; +#line 960 "hb-ot-shaper-use-machine.hh" + } + + _keys = _use_syllable_machine_trans_keys + (cs<<1); + _inds = _use_syllable_machine_indicies + _use_syllable_machine_index_offsets[cs]; + + _slen = _use_syllable_machine_key_spans[cs]; + _trans = _inds[ _slen > 0 && _keys[0] <=( (*p).second.second.use_category()) && + ( (*p).second.second.use_category()) <= _keys[1] ? + ( (*p).second.second.use_category()) - _keys[0] : _slen ]; + +_eof_trans: + cs = _use_syllable_machine_trans_targs[_trans]; + + if ( _use_syllable_machine_trans_actions[_trans] == 0 ) + goto _again; + + switch ( _use_syllable_machine_trans_actions[_trans] ) { + case 12: +#line 170 "hb-ot-shaper-use-machine.rl" + {te = p+1;{ found_syllable (use_virama_terminated_cluster); }} + break; + case 10: +#line 171 "hb-ot-shaper-use-machine.rl" + {te = p+1;{ found_syllable (use_sakot_terminated_cluster); }} + break; + case 8: +#line 172 "hb-ot-shaper-use-machine.rl" + {te = p+1;{ found_syllable (use_standard_cluster); }} + break; + case 16: +#line 173 "hb-ot-shaper-use-machine.rl" + {te = p+1;{ found_syllable (use_number_joiner_terminated_cluster); }} + break; + case 14: +#line 174 "hb-ot-shaper-use-machine.rl" + {te = p+1;{ found_syllable (use_numeral_cluster); }} + break; + case 6: +#line 175 "hb-ot-shaper-use-machine.rl" + {te = p+1;{ found_syllable (use_symbol_cluster); }} + break; + case 20: +#line 176 "hb-ot-shaper-use-machine.rl" + {te = p+1;{ found_syllable (use_hieroglyph_cluster); }} + break; + case 4: +#line 177 "hb-ot-shaper-use-machine.rl" + {te = p+1;{ found_syllable (use_broken_cluster); buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_BROKEN_SYLLABLE; }} + break; + case 3: +#line 178 "hb-ot-shaper-use-machine.rl" + {te = p+1;{ found_syllable (use_non_cluster); }} + break; + case 11: +#line 170 "hb-ot-shaper-use-machine.rl" + {te = p;p--;{ found_syllable (use_virama_terminated_cluster); }} + break; + case 9: +#line 171 "hb-ot-shaper-use-machine.rl" + {te = p;p--;{ found_syllable (use_sakot_terminated_cluster); }} + break; + case 7: +#line 172 "hb-ot-shaper-use-machine.rl" + {te = p;p--;{ found_syllable (use_standard_cluster); }} + break; + case 15: +#line 173 "hb-ot-shaper-use-machine.rl" + {te = p;p--;{ found_syllable (use_number_joiner_terminated_cluster); }} + break; + case 13: +#line 174 "hb-ot-shaper-use-machine.rl" + {te = p;p--;{ found_syllable (use_numeral_cluster); }} + break; + case 5: +#line 175 "hb-ot-shaper-use-machine.rl" + {te = p;p--;{ found_syllable (use_symbol_cluster); }} + break; + case 19: +#line 176 "hb-ot-shaper-use-machine.rl" + {te = p;p--;{ found_syllable (use_hieroglyph_cluster); }} + break; + case 17: +#line 177 "hb-ot-shaper-use-machine.rl" + {te = p;p--;{ found_syllable (use_broken_cluster); buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_BROKEN_SYLLABLE; }} + break; + case 18: +#line 178 "hb-ot-shaper-use-machine.rl" + {te = p;p--;{ found_syllable (use_non_cluster); }} + break; +#line 1050 "hb-ot-shaper-use-machine.hh" + } + +_again: + switch ( _use_syllable_machine_to_state_actions[cs] ) { + case 1: +#line 1 "NONE" + {ts = 0;} + break; +#line 1059 "hb-ot-shaper-use-machine.hh" + } + + if ( ++p != pe ) + goto _resume; + _test_eof: {} + if ( p == eof ) + { + if ( _use_syllable_machine_eof_trans[cs] > 0 ) { + _trans = _use_syllable_machine_eof_trans[cs] - 1; + goto _eof_trans; + } + } + + } + +#line 287 "hb-ot-shaper-use-machine.rl" + +} + +#undef found_syllable + +#endif /* HB_OT_SHAPER_USE_MACHINE_HH */ diff --git a/libs/harfbuzz/src/hb-ot-shaper-use-machine.rl b/libs/harfbuzz/src/hb-ot-shaper-use-machine.rl new file mode 100644 index 00000000..9f22057e --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-shaper-use-machine.rl @@ -0,0 +1,292 @@ +/* + * Copyright © 2015 Mozilla Foundation. + * Copyright © 2015 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. + * + * Mozilla Author(s): Jonathan Kew + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_OT_SHAPER_USE_MACHINE_HH +#define HB_OT_SHAPER_USE_MACHINE_HH + +#include "hb.hh" + +#include "hb-ot-shaper-syllabic.hh" + +/* buffer var allocations */ +#define use_category() ot_shaper_var_u8_category() + +#define USE(Cat) use_syllable_machine_ex_##Cat + +enum use_syllable_type_t { + use_virama_terminated_cluster, + use_sakot_terminated_cluster, + use_standard_cluster, + use_number_joiner_terminated_cluster, + use_numeral_cluster, + use_symbol_cluster, + use_hieroglyph_cluster, + use_broken_cluster, + use_non_cluster, +}; + +%%{ + machine use_syllable_machine; + alphtype unsigned char; + write exports; + write data; +}%% + +%%{ + +# Categories used in the Universal Shaping Engine spec: +# https://docs.microsoft.com/en-us/typography/script-development/use + +export O = 0; # OTHER + +export B = 1; # BASE +export N = 4; # BASE_NUM +export GB = 5; # BASE_OTHER +export CGJ = 6; # CGJ +export SUB = 11; # CONS_SUB +export H = 12; # HALANT + +export HN = 13; # HALANT_NUM +export ZWNJ = 14; # Zero width non-joiner +export WJ = 16; # Word joiner +export R = 18; # REPHA +export CS = 43; # CONS_WITH_STACKER +export IS = 44; # INVISIBLE_STACKER +export Sk = 48; # SAKOT +export G = 49; # HIEROGLYPH +export J = 50; # HIEROGLYPH_JOINER +export SB = 51; # HIEROGLYPH_SEGMENT_BEGIN +export SE = 52; # HIEROGLYPH_SEGMENT_END +export HVM = 53; # HALANT_OR_VOWEL_MODIFIER + +export FAbv = 24; # CONS_FINAL_ABOVE +export FBlw = 25; # CONS_FINAL_BELOW +export FPst = 26; # CONS_FINAL_POST +export MAbv = 27; # CONS_MED_ABOVE +export MBlw = 28; # CONS_MED_BELOW +export MPst = 29; # CONS_MED_POST +export MPre = 30; # CONS_MED_PRE +export CMAbv = 31; # CONS_MOD_ABOVE +export CMBlw = 32; # CONS_MOD_BELOW +export VAbv = 33; # VOWEL_ABOVE / VOWEL_ABOVE_BELOW / VOWEL_ABOVE_BELOW_POST / VOWEL_ABOVE_POST +export VBlw = 34; # VOWEL_BELOW / VOWEL_BELOW_POST +export VPst = 35; # VOWEL_POST UIPC = Right +export VPre = 22; # VOWEL_PRE / VOWEL_PRE_ABOVE / VOWEL_PRE_ABOVE_POST / VOWEL_PRE_POST +export VMAbv = 37; # VOWEL_MOD_ABOVE +export VMBlw = 38; # VOWEL_MOD_BELOW +export VMPst = 39; # VOWEL_MOD_POST +export VMPre = 23; # VOWEL_MOD_PRE +export SMAbv = 41; # SYM_MOD_ABOVE +export SMBlw = 42; # SYM_MOD_BELOW +export FMAbv = 45; # CONS_FINAL_MOD UIPC = Top +export FMBlw = 46; # CONS_FINAL_MOD UIPC = Bottom +export FMPst = 47; # CONS_FINAL_MOD UIPC = Not_Applicable + + +h = H | HVM | IS | Sk; + +consonant_modifiers = CMAbv* CMBlw* ((h B | SUB) CMAbv? CMBlw*)*; +medial_consonants = MPre? MAbv? MBlw? MPst?; +dependent_vowels = VPre* VAbv* VBlw* VPst* | H; +vowel_modifiers = HVM? VMPre* VMAbv* VMBlw* VMPst*; +final_consonants = FAbv* FBlw* FPst*; +final_modifiers = FMAbv* FMBlw* | FMPst?; + +complex_syllable_start = (R | CS)? (B | GB); +complex_syllable_middle = + consonant_modifiers + medial_consonants + dependent_vowels + vowel_modifiers + (Sk B)* +; +complex_syllable_tail = + complex_syllable_middle + final_consonants + final_modifiers +; +number_joiner_terminated_cluster_tail = (HN N)* HN; +numeral_cluster_tail = (HN N)+; +symbol_cluster_tail = SMAbv+ SMBlw* | SMBlw+; + +virama_terminated_cluster_tail = + consonant_modifiers + IS +; +virama_terminated_cluster = + complex_syllable_start + virama_terminated_cluster_tail +; +sakot_terminated_cluster_tail = + complex_syllable_middle + Sk +; +sakot_terminated_cluster = + complex_syllable_start + sakot_terminated_cluster_tail +; +standard_cluster = + complex_syllable_start + complex_syllable_tail +; +tail = complex_syllable_tail | sakot_terminated_cluster_tail | symbol_cluster_tail | virama_terminated_cluster_tail; +broken_cluster = + R? + (tail | number_joiner_terminated_cluster_tail | numeral_cluster_tail) +; + +number_joiner_terminated_cluster = N number_joiner_terminated_cluster_tail; +numeral_cluster = N numeral_cluster_tail?; +symbol_cluster = (O | GB) tail?; +hieroglyph_cluster = SB+ | SB* G SE* (J SE* (G SE*)?)*; +other = any; + +main := |* + virama_terminated_cluster ZWNJ? => { found_syllable (use_virama_terminated_cluster); }; + sakot_terminated_cluster ZWNJ? => { found_syllable (use_sakot_terminated_cluster); }; + standard_cluster ZWNJ? => { found_syllable (use_standard_cluster); }; + number_joiner_terminated_cluster ZWNJ? => { found_syllable (use_number_joiner_terminated_cluster); }; + numeral_cluster ZWNJ? => { found_syllable (use_numeral_cluster); }; + symbol_cluster ZWNJ? => { found_syllable (use_symbol_cluster); }; + hieroglyph_cluster ZWNJ? => { found_syllable (use_hieroglyph_cluster); }; + broken_cluster ZWNJ? => { found_syllable (use_broken_cluster); buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_BROKEN_SYLLABLE; }; + other => { found_syllable (use_non_cluster); }; +*|; + + +}%% + +#define found_syllable(syllable_type) \ + HB_STMT_START { \ + if (0) fprintf (stderr, "syllable %d..%d %s\n", (*ts).second.first, (*te).second.first, #syllable_type); \ + for (unsigned i = (*ts).second.first; i < (*te).second.first; ++i) \ + info[i].syllable() = (syllable_serial << 4) | syllable_type; \ + syllable_serial++; \ + if (unlikely (syllable_serial == 16)) syllable_serial = 1; \ + } HB_STMT_END + + +template +struct machine_index_t : + hb_iter_with_fallback_t, + typename Iter::item_t> +{ + machine_index_t (const Iter& it) : it (it) {} + machine_index_t (const machine_index_t& o) : hb_iter_with_fallback_t, + typename Iter::item_t> (), + it (o.it), is_null (o.is_null) {} + + static constexpr bool is_random_access_iterator = Iter::is_random_access_iterator; + static constexpr bool is_sorted_iterator = Iter::is_sorted_iterator; + + typename Iter::item_t __item__ () const { return *it; } + typename Iter::item_t __item_at__ (unsigned i) const { return it[i]; } + unsigned __len__ () const { return it.len (); } + void __next__ () { ++it; } + void __forward__ (unsigned n) { it += n; } + void __prev__ () { --it; } + void __rewind__ (unsigned n) { it -= n; } + + void operator = (unsigned n) + { + assert (n == 0); + is_null = true; + } + explicit operator bool () { return !is_null; } + + void operator = (const machine_index_t& o) + { + is_null = o.is_null; + unsigned index = (*it).first; + unsigned n = (*o.it).first; + if (index < n) it += n - index; else if (index > n) it -= index - n; + } + bool operator == (const machine_index_t& o) const + { return is_null ? o.is_null : !o.is_null && (*it).first == (*o.it).first; } + bool operator != (const machine_index_t& o) const { return !(*this == o); } + + private: + Iter it; + bool is_null = false; +}; +struct +{ + template + machine_index_t> + operator () (Iter&& it) const + { return machine_index_t> (hb_iter (it)); } +} +HB_FUNCOBJ (machine_index); + + + +static bool +not_ccs_default_ignorable (const hb_glyph_info_t &i) +{ return i.use_category() != USE(CGJ); } + +static inline void +find_syllables_use (hb_buffer_t *buffer) +{ + hb_glyph_info_t *info = buffer->info; + auto p = + + hb_iter (info, buffer->len) + | hb_enumerate + | hb_filter ([] (const hb_glyph_info_t &i) { return not_ccs_default_ignorable (i); }, + hb_second) + | hb_filter ([&] (const hb_pair_t p) + { + if (p.second.use_category() == USE(ZWNJ)) + for (unsigned i = p.first + 1; i < buffer->len; ++i) + if (not_ccs_default_ignorable (info[i])) + return !_hb_glyph_info_is_unicode_mark (&info[i]); + return true; + }) + | hb_enumerate + | machine_index + ; + auto pe = p + p.len (); + auto eof = +pe; + auto ts = +p; + auto te = +p; + unsigned int act HB_UNUSED; + int cs; + %%{ + write init; + getkey (*p).second.second.use_category(); + }%% + + unsigned int syllable_serial = 1; + %%{ + write exec; + }%% +} + +#undef found_syllable + +#endif /* HB_OT_SHAPER_USE_MACHINE_HH */ diff --git a/libs/harfbuzz/src/hb-ot-shaper-use-table.hh b/libs/harfbuzz/src/hb-ot-shaper-use-table.hh new file mode 100644 index 00000000..9833fb55 --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-shaper-use-table.hh @@ -0,0 +1,404 @@ +/* == Start of generated table == */ +/* + * The following table is generated by running: + * + * ./gen-use-table.py IndicSyllabicCategory.txt IndicPositionalCategory.txt ArabicShaping.txt DerivedCoreProperties.txt UnicodeData.txt Blocks.txt Scripts.txt IndicSyllabicCategory-Additional.txt IndicPositionalCategory-Additional.txt + * + * on files with these headers: + * + * # IndicSyllabicCategory-15.0.0.txt + * # Date: 2022-05-26, 02:18:00 GMT [KW, RP] + * # IndicPositionalCategory-15.0.0.txt + * # Date: 2022-05-26, 02:18:00 GMT [KW, RP] + * # ArabicShaping-15.0.0.txt + * # Date: 2022-02-14, 18:50:00 GMT [KW, RP] + * # DerivedCoreProperties-15.0.0.txt + * # Date: 2022-08-05, 22:17:05 GMT + * # Blocks-15.0.0.txt + * # Date: 2022-01-28, 20:58:00 GMT [KW] + * # Scripts-15.0.0.txt + * # Date: 2022-04-26, 23:15:02 GMT + * # Override values For Indic_Syllabic_Category + * # Not derivable + * # Initial version based on Unicode 7.0 by Andrew Glass 2014-03-17 + * # Updated for Unicode 10.0 by Andrew Glass 2017-07-25 + * # Updated for Unicode 12.1 by Andrew Glass 2019-05-24 + * # Updated for Unicode 13.0 by Andrew Glass 2020-07-28 + * # Updated for Unicode 14.0 by Andrew Glass 2021-09-25 + * # Updated for Unicode 15.0 by Andrew Glass 2022-09-16 + * # Override values For Indic_Positional_Category + * # Not derivable + * # Initial version based on Unicode 7.0 by Andrew Glass 2014-03-17 + * # Updated for Unicode 10.0 by Andrew Glass 2017-07-25 + * # Ammended for Unicode 10.0 by Andrew Glass 2018-09-21 + * # Updated for L2/19-083 by Andrew Glass 2019-05-06 + * # Updated for Unicode 12.1 by Andrew Glass 2019-05-30 + * # Updated for Unicode 13.0 by Andrew Glass 2020-07-28 + * # Updated for Unicode 14.0 by Andrew Glass 2021-09-28 + * # Updated for Unicode 15.0 by Andrew Glass 2022-09-16 + * UnicodeData.txt does not have a header. + */ + +#ifndef HB_OT_SHAPER_USE_TABLE_HH +#define HB_OT_SHAPER_USE_TABLE_HH + +#include "hb.hh" + +#include "hb-ot-shaper-use-machine.hh" + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-macros" +#define B USE(B) /* BASE */ +#define CGJ USE(CGJ) /* CGJ */ +#define CS USE(CS) /* CONS_WITH_STACKER */ +#define G USE(G) /* HIEROGLYPH */ +#define GB USE(GB) /* BASE_OTHER */ +#define H USE(H) /* HALANT */ +#define HN USE(HN) /* HALANT_NUM */ +#define HVM USE(HVM) /* HALANT_OR_VOWEL_MODIFIER */ +#define IS USE(IS) /* INVISIBLE_STACKER */ +#define J USE(J) /* HIEROGLYPH_JOINER */ +#define N USE(N) /* BASE_NUM */ +#define O USE(O) /* OTHER */ +#define R USE(R) /* REPHA */ +#define SB USE(SB) /* HIEROGLYPH_SEGMENT_BEGIN */ +#define SE USE(SE) /* HIEROGLYPH_SEGMENT_END */ +#define SUB USE(SUB) /* CONS_SUB */ +#define Sk USE(Sk) /* SAKOT */ +#define WJ USE(WJ) /* Word_Joiner */ +#define ZWNJ USE(ZWNJ) /* ZWNJ */ +#define CMAbv USE(CMAbv) +#define CMBlw USE(CMBlw) +#define FAbv USE(FAbv) +#define FBlw USE(FBlw) +#define FPst USE(FPst) +#define FMAbv USE(FMAbv) +#define FMBlw USE(FMBlw) +#define FMPst USE(FMPst) +#define MAbv USE(MAbv) +#define MBlw USE(MBlw) +#define MPst USE(MPst) +#define MPre USE(MPre) +#define SMAbv USE(SMAbv) +#define SMBlw USE(SMBlw) +#define VAbv USE(VAbv) +#define VBlw USE(VBlw) +#define VPst USE(VPst) +#define VPre USE(VPre) +#define VMAbv USE(VMAbv) +#define VMBlw USE(VMBlw) +#define VMPst USE(VMPst) +#define VMPre USE(VMPre) +#pragma GCC diagnostic pop + +static const uint8_t +hb_use_u8[3141] = +{ + 16, 50, 51, 51, 51, 52, 51, 83, 118, 131, 51, 57, 58, 179, 195, 61, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 14, 0, 1, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 4, 2, 2, + 5, 6, 2, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 2, 2, 17, + 18, 19, 20, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 2, 33, 2, 2, 2, + 2, 34, 35, 2, 2, 2, 2, 2, 2, 2, 2, 2, 36, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 37, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 38, 39, 40, 41, 42, 43, 2, 44, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 45, 46, 2, + 47, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 48, 49, 2, 2, 2, + 2, 2, 2, 2, 2, 50, 51, 2, 52, 2, 2, 53, 2, 2, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63, 2, 64, 65, 2, 66, 67, 68, 69, + 2, 70, 2, 71, 72, 73, 74, 2, 2, 75, 76, 77, 78, 2, 79, 80, + 2, 81, 81, 81, 81, 81, 81, 81, 81, 82, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 83, 84, 2, 2, 2, 2, 2, 2, 2, 85, + 86, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 81, 81, 81, 87, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 88, 89, 2, 2, 2, 2, 2, + 2, 2, 2, 90, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 91, 2, 2, 92, 2, 2, 2, 93, 2, 2, 2, 2, 2, + 2, 2, 2, 94, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 95, 95, 96, 97, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, + 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, + 95, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + 0, 2, 2, 2, 2, 2, 0, 0, 0, 3, 0, 0, 0, 0, 0, 4, + 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 6, 7, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 8, 9, 9, 9, 9, 0, 0, 0, 7, 10, + 0, 2, 2, 2, 2, 11, 12, 0, 0, 9, 13, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 14, 15, 16, 17, 18, 19, 20, 14, 21, 22, + 23, 10, 24, 25, 18, 2, 2, 2, 2, 2, 18, 0, 2, 2, 2, 2, + 2, 0, 2, 2, 2, 2, 2, 2, 2, 26, 27, 28, 2, 2, 2, 7, + 28, 7, 28, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 7, 2, 2, + 2, 7, 7, 0, 2, 2, 0, 15, 16, 17, 18, 29, 30, 31, 30, 32, + 0, 0, 0, 0, 33, 0, 0, 2, 28, 2, 0, 0, 0, 0, 0, 7, + 34, 10, 13, 28, 2, 2, 7, 0, 28, 7, 2, 28, 7, 2, 0, 35, + 16, 17, 29, 0, 25, 36, 25, 37, 0, 38, 0, 0, 0, 28, 2, 7, + 7, 0, 0, 0, 2, 2, 2, 2, 2, 39, 40, 41, 0, 0, 0, 0, + 0, 10, 13, 28, 2, 2, 2, 2, 28, 2, 28, 2, 2, 2, 2, 2, + 2, 7, 2, 28, 2, 2, 0, 15, 16, 17, 18, 19, 25, 20, 33, 22, + 0, 0, 0, 0, 0, 28, 39, 39, 42, 10, 27, 28, 2, 2, 2, 7, + 28, 7, 2, 28, 2, 2, 0, 15, 43, 0, 0, 25, 20, 0, 0, 2, + 28, 28, 0, 0, 0, 0, 0, 0, 0, 0, 44, 28, 2, 2, 7, 0, + 2, 7, 2, 2, 0, 28, 7, 7, 2, 0, 28, 7, 0, 2, 7, 0, + 2, 2, 2, 2, 2, 2, 0, 0, 21, 14, 45, 0, 46, 31, 46, 32, + 0, 0, 0, 0, 33, 0, 0, 0, 0, 13, 27, 47, 2, 2, 2, 7, + 2, 7, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 15, + 20, 14, 21, 45, 20, 36, 20, 37, 0, 0, 0, 25, 29, 2, 7, 0, + 0, 8, 27, 28, 2, 2, 2, 7, 2, 2, 2, 28, 2, 2, 0, 15, + 43, 0, 0, 33, 45, 0, 0, 0, 7, 48, 49, 0, 0, 0, 0, 0, + 0, 9, 27, 2, 2, 2, 2, 7, 2, 2, 2, 2, 2, 2, 50, 51, + 21, 21, 17, 29, 46, 31, 46, 32, 52, 0, 0, 0, 33, 0, 0, 0, + 28, 10, 27, 28, 2, 2, 2, 2, 2, 2, 2, 2, 7, 0, 2, 2, + 2, 2, 28, 2, 2, 2, 2, 28, 0, 2, 2, 2, 7, 0, 53, 0, + 33, 21, 20, 29, 29, 16, 46, 46, 23, 0, 21, 0, 0, 0, 0, 0, + 0, 2, 0, 2, 7, 0, 0, 0, 0, 0, 0, 0, 0, 18, 0, 0, + 0, 2, 2, 54, 54, 55, 0, 0, 16, 2, 2, 2, 2, 28, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 7, 0, 56, 19, 57, 20, 20, 18, 18, + 44, 19, 9, 29, 9, 2, 2, 58, 59, 59, 59, 59, 59, 60, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 61, + 0, 0, 0, 0, 62, 0, 0, 0, 0, 2, 2, 2, 2, 2, 63, 43, + 57, 64, 20, 20, 65, 66, 67, 68, 69, 2, 2, 2, 2, 2, 1, 0, + 3, 2, 2, 2, 21, 18, 2, 2, 70, 69, 71, 72, 63, 71, 27, 27, + 2, 50, 20, 51, 2, 2, 2, 2, 2, 2, 73, 74, 75, 27, 27, 76, + 77, 2, 2, 2, 2, 2, 27, 43, 0, 2, 57, 78, 0, 0, 0, 0, + 28, 2, 57, 45, 0, 0, 0, 0, 0, 2, 57, 0, 0, 0, 0, 0, + 0, 2, 2, 2, 2, 2, 2, 7, 2, 7, 57, 0, 0, 0, 0, 0, + 0, 2, 2, 79, 43, 20, 57, 18, 46, 46, 46, 46, 13, 80, 81, 82, + 83, 84, 85, 0, 0, 0, 0, 86, 0, 7, 0, 0, 28, 0, 87, 79, + 88, 2, 2, 2, 2, 7, 0, 0, 0, 40, 40, 89, 90, 2, 2, 2, + 2, 2, 2, 2, 2, 11, 7, 0, 0, 91, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 7, 20, 78, 43, 20, 92, 59, 0, + 0, 93, 94, 93, 93, 95, 96, 0, 0, 2, 2, 2, 2, 2, 2, 2, + 0, 2, 2, 7, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 0, + 0, 2, 2, 2, 2, 27, 0, 0, 0, 2, 2, 2, 2, 2, 7, 0, + 0, 2, 2, 2, 50, 97, 43, 0, 0, 2, 2, 98, 99, 100, 101, 59, + 61, 102, 14, 43, 20, 57, 19, 78, 46, 46, 74, 9, 9, 9, 103, 44, + 38, 9, 104, 72, 2, 2, 2, 2, 2, 2, 2, 105, 20, 18, 18, 20, + 46, 46, 20, 106, 2, 2, 2, 7, 0, 0, 0, 0, 0, 0, 107, 108, + 109, 109, 109, 0, 0, 0, 0, 0, 0, 104, 72, 2, 2, 2, 2, 2, + 2, 58, 59, 57, 23, 20, 110, 59, 2, 2, 2, 2, 105, 20, 21, 43, + 43, 100, 12, 0, 0, 0, 0, 0, 0, 2, 2, 59, 16, 46, 21, 111, + 100, 100, 100, 112, 113, 0, 0, 0, 0, 2, 2, 2, 2, 2, 0, 28, + 2, 9, 44, 114, 114, 114, 9, 114, 114, 13, 114, 114, 114, 24, 0, 38, + 0, 0, 0, 115, 49, 9, 3, 0, 0, 0, 0, 0, 0, 0, 116, 0, + 0, 0, 0, 0, 0, 0, 4, 117, 118, 40, 40, 3, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 118, 118, 119, 118, 118, 118, 118, 118, 118, 118, + 118, 0, 0, 120, 0, 0, 0, 0, 0, 0, 5, 120, 0, 0, 0, 0, + 0, 44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, + 0, 2, 2, 2, 2, 0, 0, 0, 28, 0, 0, 0, 0, 0, 0, 0, + 121, 2, 51, 2, 106, 2, 8, 2, 2, 2, 63, 17, 14, 0, 0, 29, + 0, 2, 2, 0, 0, 0, 0, 0, 0, 27, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 122, 21, 21, 21, 21, 21, 21, 21, 123, 0, 0, 0, 0, + 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 2, 0, 0, 0, 0, 0, + 50, 2, 2, 2, 20, 20, 124, 114, 0, 2, 2, 2, 125, 18, 57, 18, + 111, 100, 126, 0, 0, 0, 0, 0, 0, 9, 127, 2, 2, 2, 2, 2, + 2, 2, 128, 21, 20, 18, 46, 129, 130, 131, 0, 0, 0, 0, 0, 0, + 0, 2, 2, 50, 28, 2, 2, 2, 2, 2, 2, 2, 2, 8, 20, 57, + 97, 74, 132, 133, 134, 0, 0, 0, 0, 2, 135, 2, 2, 2, 2, 136, + 0, 28, 2, 40, 3, 0, 77, 13, 2, 51, 20, 137, 50, 51, 2, 2, + 103, 8, 7, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 138, 19, + 23, 0, 0, 139, 140, 0, 0, 0, 0, 2, 63, 43, 21, 78, 45, 141, + 0, 79, 79, 79, 79, 79, 79, 79, 79, 0, 0, 0, 0, 0, 0, 0, + 4, 118, 118, 118, 118, 119, 0, 0, 0, 2, 2, 2, 2, 2, 7, 2, + 2, 2, 7, 2, 28, 2, 2, 2, 2, 2, 28, 2, 2, 2, 28, 7, + 0, 125, 18, 25, 29, 0, 0, 142, 143, 2, 2, 28, 2, 28, 2, 2, + 2, 2, 2, 2, 0, 12, 35, 0, 144, 2, 2, 11, 35, 0, 28, 2, + 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 2, 2, + 7, 2, 2, 9, 39, 0, 0, 0, 0, 2, 2, 2, 2, 2, 25, 36, + 0, 2, 2, 2, 114, 114, 114, 114, 114, 145, 2, 7, 0, 0, 0, 0, + 0, 2, 12, 12, 0, 0, 0, 0, 0, 7, 2, 2, 7, 2, 2, 2, + 2, 28, 2, 7, 0, 28, 2, 0, 0, 146, 147, 148, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 20, 20, 18, 18, 18, 20, 20, 131, 0, 0, 0, + 0, 0, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 2, 2, 2, 2, + 2, 51, 50, 51, 0, 0, 0, 0, 150, 9, 72, 2, 2, 2, 2, 2, + 2, 16, 17, 19, 14, 22, 35, 0, 0, 0, 29, 0, 0, 0, 0, 0, + 0, 9, 47, 2, 2, 2, 2, 2, 2, 2, 2, 2, 125, 18, 20, 151, + 20, 19, 152, 153, 2, 2, 2, 2, 2, 0, 0, 63, 154, 0, 0, 0, + 0, 2, 11, 0, 0, 0, 0, 0, 0, 2, 63, 23, 18, 18, 18, 20, + 20, 106, 155, 0, 0, 54, 156, 29, 157, 28, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 21, 17, 20, 20, 158, 42, 0, 0, 0, + 47, 125, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 7, 7, 2, 2, + 28, 2, 2, 2, 2, 2, 2, 2, 28, 2, 2, 2, 2, 2, 2, 2, + 8, 16, 17, 19, 20, 159, 29, 0, 0, 9, 9, 28, 2, 2, 2, 7, + 28, 7, 2, 28, 2, 2, 56, 15, 21, 14, 21, 45, 30, 31, 30, 32, + 0, 0, 0, 0, 33, 0, 0, 0, 2, 2, 21, 0, 9, 9, 9, 44, + 0, 9, 9, 44, 0, 0, 0, 0, 0, 2, 2, 63, 23, 18, 18, 18, + 20, 21, 123, 13, 15, 0, 0, 0, 0, 2, 2, 2, 2, 2, 0, 0, + 160, 161, 0, 0, 0, 0, 0, 0, 0, 16, 17, 18, 18, 64, 97, 23, + 157, 9, 162, 7, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, + 63, 23, 18, 18, 0, 46, 46, 9, 163, 35, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 2, 2, 18, 0, 21, 17, 18, 18, 19, 14, 80, + 163, 36, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 8, 164, + 23, 18, 20, 20, 162, 7, 0, 0, 0, 2, 2, 2, 2, 2, 7, 41, + 133, 21, 20, 18, 74, 19, 20, 0, 0, 2, 2, 2, 7, 0, 0, 0, + 0, 2, 2, 2, 2, 2, 2, 16, 17, 18, 19, 20, 103, 163, 35, 0, + 0, 2, 2, 2, 7, 28, 0, 2, 2, 2, 2, 28, 7, 2, 2, 2, + 2, 21, 21, 16, 30, 31, 10, 165, 166, 167, 168, 0, 0, 0, 0, 0, + 0, 2, 2, 2, 2, 0, 2, 2, 2, 63, 23, 18, 18, 0, 20, 21, + 27, 106, 0, 31, 0, 0, 0, 0, 0, 50, 18, 20, 20, 20, 137, 2, + 2, 2, 169, 170, 9, 13, 171, 70, 172, 0, 0, 1, 144, 0, 0, 0, + 0, 50, 18, 20, 14, 17, 18, 2, 2, 2, 2, 155, 155, 155, 173, 173, + 173, 173, 173, 173, 13, 174, 0, 28, 0, 20, 18, 18, 29, 20, 20, 9, + 163, 0, 59, 59, 59, 59, 59, 59, 59, 64, 19, 80, 44, 0, 0, 0, + 0, 2, 2, 2, 7, 2, 28, 2, 2, 50, 20, 20, 29, 0, 36, 20, + 25, 9, 156, 175, 171, 0, 0, 0, 0, 2, 2, 2, 28, 7, 2, 2, + 2, 2, 2, 2, 2, 2, 21, 21, 45, 20, 33, 80, 66, 0, 0, 0, + 0, 2, 176, 64, 45, 0, 0, 0, 0, 9, 177, 2, 2, 2, 2, 2, + 2, 2, 2, 21, 20, 18, 29, 0, 46, 14, 140, 0, 0, 0, 0, 0, + 0, 178, 178, 178, 106, 179, 178, 0, 0, 145, 2, 2, 180, 114, 114, 114, + 114, 114, 114, 114, 0, 0, 0, 0, 0, 9, 9, 9, 44, 0, 0, 0, + 0, 2, 2, 2, 2, 2, 7, 0, 56, 181, 18, 18, 18, 18, 18, 18, + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 0, 0, 0, + 38, 114, 24, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, + 0, 2, 2, 2, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 0, 56, + 35, 0, 4, 118, 118, 118, 119, 0, 0, 9, 9, 9, 47, 2, 2, 2, + 0, 2, 2, 2, 2, 2, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, + 44, 2, 2, 2, 2, 2, 2, 9, 9, 2, 2, 2, 2, 2, 2, 20, + 20, 2, 2, 42, 42, 42, 90, 0, 0, O, O, O, GB, B, B, GB, + O, O, WJ,FMPst,FMPst, O, CGJ, B, O, B,VMAbv,VMAbv,VMAbv, O,VMAbv, B, + CMBlw,CMBlw,CMBlw,VMAbv,VMPst, VAbv, VPst,CMBlw, B, VPst, VPre, VPst, VBlw, VBlw, VBlw, VBlw, + VAbv, VAbv, VAbv, VPst, VPst, VPst, H, VPre, VPst,VMBlw, O, O, VAbv, GB,VMAbv,VMPst, + VMPst, O, B, VBlw, O, O, VPre, VPre, O, VPre, H, O, VPst,FMAbv, O,CMBlw, + O, VAbv, O, VAbv, H, O,VMBlw,VMAbv,CMAbv, GB, GB, O, MBlw,CMAbv,CMAbv, VPst, + VAbv,VMAbv, O, VPst, O, VPre, VPre,VMAbv, B, O, CS, CS,VMPst, B, VAbv, VAbv, + B, R, O, HVM, O, O,FMBlw, O,CMAbv, O,CMBlw, VAbv, VBlw, B, SUB, SUB, + SUB, O, SUB, SUB, O,FMBlw, O, B, VPst, VBlw, VPre,VMAbv,VMBlw,VMPst, IS, VAbv, + MPst, MPre, MBlw, MBlw, B, MBlw, MBlw, VPst,VMPst,VMPst, B, MBlw, VPst, VPre, VAbv, VAbv, + VMPst,VMPst,VMBlw, B,VMPst, VBlw, VPst, CGJ, CGJ, VPst,VMAbv,VMAbv,FMAbv, FAbv,CMAbv,FMAbv, + VMAbv,FMAbv, VAbv, IS,FMAbv, B,FMAbv, B, CGJ, WJ, CGJ, GB,CMAbv,CMAbv, B, GB, + B, VAbv, SUB, FPst, FPst,VMBlw, FPst, FPst, FBlw,VMAbv,FMBlw, VAbv, VPre, B, MPre, MBlw, + SUB, FAbv, FAbv, MAbv, SUB, Sk, VPst, VAbv,VMAbv,VMAbv, FAbv,CMAbv, VPst, H, B, O, + SMAbv,SMBlw,SMAbv,SMAbv,SMAbv, VPst, IS, VBlw, FAbv,VMPre,VMPre,FMAbv,CMBlw,VMBlw,VMBlw,VMAbv, + CS, O,FMAbv, ZWNJ, CGJ, WJ, WJ, WJ, O,FMPst, O, O, H, MPst, VPst, H, + VMAbv, VAbv,VMBlw, B, VBlw, FPst, VPst, FAbv,VMPst, B,CMAbv, VAbv, MBlw, MPst, MBlw, H, + O, VBlw, MPst, MPre, MAbv, MBlw, O, B, FAbv, FAbv, FPst, VBlw, B, B, VPre, O, + VMPst, IS, O,VMPst, VBlw, VPst,VMBlw,VMBlw,VMAbv, O, IS,VMBlw, B,VMPst,VMAbv,VMPst, + CS, CS, B, N, N, O, HN, VPre, VBlw, VAbv, IS,CMAbv, O, VPst, B, R, + R,CMBlw, VAbv, VPre,VMAbv,VMAbv, H, VAbv,CMBlw,FMAbv, B, CS, CS, H,CMBlw,VMPst, + H,VMPst, VAbv,VMAbv, VPst, IS, R, MPst, R, MPst,CMBlw, B,FMBlw, VBlw,VMAbv, R, + MBlw, MBlw, GB, FBlw, FBlw,CMAbv, IS, VBlw, IS, GB, VAbv, R,VMPst, H, H, B, + H, B,VMBlw, O, VBlw, +}; +static const uint16_t +hb_use_u16[784] = +{ + 0, 0, 1, 2, 0, 0, 0, 0, 0, 0, 3, 4, 0, 5, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, + 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 8, 9, 10, 11, + 0, 0, 0, 0, 9, 12, 0, 0, 13, 9, 9, 14, 15, 16, 17, 18, + 19, 20, 21, 22, 23, 24, 17, 25, 26, 20, 21, 27, 28, 29, 30, 31, + 32, 33, 21, 34, 35, 0, 17, 36, 37, 20, 21, 38, 23, 39, 17, 40, + 41, 42, 43, 44, 45, 46, 30, 0, 47, 48, 21, 49, 50, 51, 17, 0, + 52, 48, 21, 53, 50, 54, 17, 55, 56, 48, 9, 57, 58, 59, 17, 0, + 60, 61, 9, 62, 63, 64, 30, 65, 66, 67, 9, 68, 69, 9, 70, 71, + 72, 73, 74, 75, 76, 0, 0, 0, 9, 9, 77, 78, 79, 80, 81, 82, + 83, 84, 0, 0, 0, 0, 0, 0, 9, 85, 9, 86, 9, 87, 88, 89, + 9, 9, 9, 90, 91, 92, 2, 0, 93, 0, 9, 9, 9, 9, 9, 94, + 95, 9, 96, 0, 0, 0, 0, 0, 97, 98, 99,100, 30, 9,101,102, + 9, 9,103, 9,104,105, 0, 0, 9,106, 9, 9, 9,107,108,109, + 2, 2, 0, 0, 0, 0, 0, 0,110, 9, 9,111,112, 2,113,114, + 115, 9,116, 9, 9, 9,117,118, 9, 9,119,120,121, 0, 0, 0, + 0, 0, 0, 0, 0,122,123,124, 0, 0, 0, 0, 0, 0, 0,125, + 126,127,128, 0, 0, 0,129,130,131, 0, 0, 0, 0, 0, 0,132, + 0, 0, 0, 0,133, 0, 0, 0, 0, 0, 0, 9, 9, 9,134,135, + 136, 9,137, 0, 9, 9, 9,138,139, 9, 9,140,141, 2,142,143, + 9, 9,144, 9,145,146, 0, 0,147, 9, 9,148,149, 2,150, 98, + 9, 9,151,152,153, 2, 9,154, 9, 9, 9,155,156, 0,157,158, + 0, 0, 0, 0, 9, 9,159, 2,160, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0,161, 0, 0, 0, 0, 0, 0, 0,162, + 0, 0, 0, 0, 0, 0, 0,163,163,164, 33,165, 0, 0, 0, 0, + 166,167, 9,168, 94, 0, 0, 0, 0, 0, 0, 0, 69, 9,169, 0, + 9,170,171, 0, 0, 0, 0, 0, 9, 9,172, 2, 0, 0, 0, 0, + 9, 9,173,170, 0, 0, 0, 0, 0, 0, 0, 9,174,175, 0, 9, + 176, 0, 0,177,178, 0, 0, 0,179, 9, 9,180,181,182,183,184, + 185, 9, 9,186,187, 0, 0, 0,188, 9,189,190,191, 9, 9,192, + 185, 9, 9,193,194,105,195,102, 9, 33,196,197,198, 0, 0, 0, + 199,200, 94, 9, 9,201,202, 2,203, 20, 21,204,205,206,207,208, + 9, 9, 9,209,210,211,212, 0,195, 9, 9,213,214, 2, 0, 0, + 9, 9,215,216,217,218, 0, 0, 9, 9, 9,219,220, 2, 0, 0, + 9, 9,221,222, 2, 0, 0, 0, 9,223,224,103,225, 0, 0, 0, + 9, 9,226,227, 0, 0, 0, 0,228,229, 9,230,231, 2, 0, 0, + 0, 0,232, 9, 9,233,234, 0,235, 9, 9,236,237,238, 9, 9, + 239,240, 0, 0, 0, 0, 0, 0, 21, 9,215,241, 7, 9, 70, 18, + 9,242, 73,243, 0, 0, 0, 0,244, 9, 9,245,246, 2,247, 9, + 248,249, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9,250, + 251, 48, 9,252,253, 2, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9,254,255,256, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, + 9, 9, 9,257, 0, 0, 0, 0, 9, 9, 9, 9,258,259,260,260, + 261,262, 0, 0, 0, 0,263, 0, 9, 9, 9, 9, 9,264, 0, 0, + 9, 9, 9, 9, 9, 9,105, 70, 94,265, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0,266, 9, 9, 70,267,268, 0, 0, 0, + 0, 9,269, 0, 9, 9,270, 2, 0, 0, 0, 0, 0, 9,271, 2, + 9, 9, 9, 9,272, 2, 0, 0,129,129,129,129,129,129,129,129, + 160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,129, +}; + +static inline unsigned +hb_use_b4 (const uint8_t* a, unsigned i) +{ + return (a[i>>1]>>((i&1u)<<2))&15u; +} +static inline uint_fast8_t +hb_use_get_category (unsigned u) +{ + return u<921600u?hb_use_u8[2777+(((hb_use_u8[593+(((hb_use_u16[((hb_use_u8[113+(((hb_use_b4(hb_use_u8,u>>1>>3>>3>>5))<<5)+((u>>1>>3>>3)&31u))])<<3)+((u>>1>>3)&7u)])<<3)+((u>>1)&7u))])<<1)+((u)&1u))]:O; +} + +#undef B +#undef CGJ +#undef CS +#undef G +#undef GB +#undef H +#undef HN +#undef HVM +#undef IS +#undef J +#undef N +#undef O +#undef R +#undef SB +#undef SE +#undef SUB +#undef Sk +#undef WJ +#undef ZWNJ +#undef CMAbv +#undef CMBlw +#undef FAbv +#undef FBlw +#undef FPst +#undef FMAbv +#undef FMBlw +#undef FMPst +#undef MAbv +#undef MBlw +#undef MPst +#undef MPre +#undef SMAbv +#undef SMBlw +#undef VAbv +#undef VBlw +#undef VPst +#undef VPre +#undef VMAbv +#undef VMBlw +#undef VMPst +#undef VMPre + + +#endif /* HB_OT_SHAPER_USE_TABLE_HH */ +/* == End of generated table == */ diff --git a/libs/harfbuzz/src/hb-ot-shaper-use.cc b/libs/harfbuzz/src/hb-ot-shaper-use.cc new file mode 100644 index 00000000..c40ec52f --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-shaper-use.cc @@ -0,0 +1,504 @@ +/* + * Copyright © 2015 Mozilla Foundation. + * Copyright © 2015 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. + * + * Mozilla Author(s): Jonathan Kew + * Google Author(s): Behdad Esfahbod + */ + +#include "hb.hh" + +#ifndef HB_NO_OT_SHAPE + +#include "hb-ot-shaper-use-machine.hh" +#include "hb-ot-shaper-use-table.hh" +#include "hb-ot-shaper-arabic.hh" +#include "hb-ot-shaper-arabic-joining-list.hh" +#include "hb-ot-shaper-vowel-constraints.hh" + + +/* + * Universal Shaping Engine. + * https://docs.microsoft.com/en-us/typography/script-development/use + */ + +static const hb_tag_t +use_basic_features[] = +{ + /* + * Basic features. + * These features are applied all at once, before reordering, constrained + * to the syllable. + */ + HB_TAG('r','k','r','f'), + HB_TAG('a','b','v','f'), + HB_TAG('b','l','w','f'), + HB_TAG('h','a','l','f'), + HB_TAG('p','s','t','f'), + HB_TAG('v','a','t','u'), + HB_TAG('c','j','c','t'), +}; +static const hb_tag_t +use_topographical_features[] = +{ + HB_TAG('i','s','o','l'), + HB_TAG('i','n','i','t'), + HB_TAG('m','e','d','i'), + HB_TAG('f','i','n','a'), +}; +/* Same order as use_topographical_features. */ +enum joining_form_t { + JOINING_FORM_ISOL, + JOINING_FORM_INIT, + JOINING_FORM_MEDI, + JOINING_FORM_FINA, + _JOINING_FORM_NONE +}; +static const hb_tag_t +use_other_features[] = +{ + /* + * Other features. + * These features are applied all at once, after reordering and + * clearing syllables. + */ + HB_TAG('a','b','v','s'), + HB_TAG('b','l','w','s'), + HB_TAG('h','a','l','n'), + HB_TAG('p','r','e','s'), + HB_TAG('p','s','t','s'), +}; + +static void +setup_syllables_use (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer); +static void +record_rphf_use (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer); +static void +record_pref_use (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer); +static void +reorder_use (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer); + +static void +collect_features_use (hb_ot_shape_planner_t *plan) +{ + hb_ot_map_builder_t *map = &plan->map; + + /* Do this before any lookups have been applied. */ + map->add_gsub_pause (setup_syllables_use); + + /* "Default glyph pre-processing group" */ + map->enable_feature (HB_TAG('l','o','c','l'), F_PER_SYLLABLE); + map->enable_feature (HB_TAG('c','c','m','p'), F_PER_SYLLABLE); + map->enable_feature (HB_TAG('n','u','k','t'), F_PER_SYLLABLE); + map->enable_feature (HB_TAG('a','k','h','n'), F_MANUAL_ZWJ | F_PER_SYLLABLE); + + /* "Reordering group" */ + map->add_gsub_pause (_hb_clear_substitution_flags); + map->add_feature (HB_TAG('r','p','h','f'), F_MANUAL_ZWJ | F_PER_SYLLABLE); + map->add_gsub_pause (record_rphf_use); + map->add_gsub_pause (_hb_clear_substitution_flags); + map->enable_feature (HB_TAG('p','r','e','f'), F_MANUAL_ZWJ | F_PER_SYLLABLE); + map->add_gsub_pause (record_pref_use); + + /* "Orthographic unit shaping group" */ + for (unsigned int i = 0; i < ARRAY_LENGTH (use_basic_features); i++) + map->enable_feature (use_basic_features[i], F_MANUAL_ZWJ | F_PER_SYLLABLE); + + map->add_gsub_pause (reorder_use); + map->add_gsub_pause (hb_syllabic_clear_var); // Don't need syllables anymore, use stop to free buffer var + + /* "Topographical features" */ + for (unsigned int i = 0; i < ARRAY_LENGTH (use_topographical_features); i++) + map->add_feature (use_topographical_features[i]); + map->add_gsub_pause (nullptr); + + /* "Standard typographic presentation" */ + for (unsigned int i = 0; i < ARRAY_LENGTH (use_other_features); i++) + map->enable_feature (use_other_features[i], F_MANUAL_ZWJ); +} + +struct use_shape_plan_t +{ + hb_mask_t rphf_mask; + + arabic_shape_plan_t *arabic_plan; +}; + +static void * +data_create_use (const hb_ot_shape_plan_t *plan) +{ + use_shape_plan_t *use_plan = (use_shape_plan_t *) hb_calloc (1, sizeof (use_shape_plan_t)); + if (unlikely (!use_plan)) + return nullptr; + + use_plan->rphf_mask = plan->map.get_1_mask (HB_TAG('r','p','h','f')); + + if (has_arabic_joining (plan->props.script)) + { + use_plan->arabic_plan = (arabic_shape_plan_t *) data_create_arabic (plan); + if (unlikely (!use_plan->arabic_plan)) + { + hb_free (use_plan); + return nullptr; + } + } + + return use_plan; +} + +static void +data_destroy_use (void *data) +{ + use_shape_plan_t *use_plan = (use_shape_plan_t *) data; + + if (use_plan->arabic_plan) + data_destroy_arabic (use_plan->arabic_plan); + + hb_free (data); +} + +static void +setup_masks_use (const hb_ot_shape_plan_t *plan, + hb_buffer_t *buffer, + hb_font_t *font HB_UNUSED) +{ + const use_shape_plan_t *use_plan = (const use_shape_plan_t *) plan->data; + + /* Do this before allocating use_category(). */ + if (use_plan->arabic_plan) + { + setup_masks_arabic_plan (use_plan->arabic_plan, buffer, plan->props.script); + } + + HB_BUFFER_ALLOCATE_VAR (buffer, use_category); + + /* We cannot setup masks here. We save information about characters + * and setup masks later on in a pause-callback. */ + + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; + for (unsigned int i = 0; i < count; i++) + info[i].use_category() = hb_use_get_category (info[i].codepoint); +} + +static void +setup_rphf_mask (const hb_ot_shape_plan_t *plan, + hb_buffer_t *buffer) +{ + const use_shape_plan_t *use_plan = (const use_shape_plan_t *) plan->data; + + hb_mask_t mask = use_plan->rphf_mask; + if (!mask) return; + + hb_glyph_info_t *info = buffer->info; + + foreach_syllable (buffer, start, end) + { + unsigned int limit = info[start].use_category() == USE(R) ? 1 : hb_min (3u, end - start); + for (unsigned int i = start; i < start + limit; i++) + info[i].mask |= mask; + } +} + +static void +setup_topographical_masks (const hb_ot_shape_plan_t *plan, + hb_buffer_t *buffer) +{ + const use_shape_plan_t *use_plan = (const use_shape_plan_t *) plan->data; + if (use_plan->arabic_plan) + return; + + static_assert ((JOINING_FORM_INIT < 4 && JOINING_FORM_ISOL < 4 && JOINING_FORM_MEDI < 4 && JOINING_FORM_FINA < 4), ""); + hb_mask_t masks[4], all_masks = 0; + for (unsigned int i = 0; i < 4; i++) + { + masks[i] = plan->map.get_1_mask (use_topographical_features[i]); + if (masks[i] == plan->map.get_global_mask ()) + masks[i] = 0; + all_masks |= masks[i]; + } + if (!all_masks) + return; + hb_mask_t other_masks = ~all_masks; + + unsigned int last_start = 0; + joining_form_t last_form = _JOINING_FORM_NONE; + hb_glyph_info_t *info = buffer->info; + foreach_syllable (buffer, start, end) + { + use_syllable_type_t syllable_type = (use_syllable_type_t) (info[start].syllable() & 0x0F); + switch (syllable_type) + { + case use_hieroglyph_cluster: + case use_non_cluster: + /* These don't join. Nothing to do. */ + last_form = _JOINING_FORM_NONE; + break; + + case use_virama_terminated_cluster: + case use_sakot_terminated_cluster: + case use_standard_cluster: + case use_number_joiner_terminated_cluster: + case use_numeral_cluster: + case use_symbol_cluster: + case use_broken_cluster: + + bool join = last_form == JOINING_FORM_FINA || last_form == JOINING_FORM_ISOL; + + if (join) + { + /* Fixup previous syllable's form. */ + last_form = last_form == JOINING_FORM_FINA ? JOINING_FORM_MEDI : JOINING_FORM_INIT; + for (unsigned int i = last_start; i < start; i++) + info[i].mask = (info[i].mask & other_masks) | masks[last_form]; + } + + /* Form for this syllable. */ + last_form = join ? JOINING_FORM_FINA : JOINING_FORM_ISOL; + for (unsigned int i = start; i < end; i++) + info[i].mask = (info[i].mask & other_masks) | masks[last_form]; + + break; + } + + last_start = start; + } +} + +static void +setup_syllables_use (const hb_ot_shape_plan_t *plan, + hb_font_t *font HB_UNUSED, + hb_buffer_t *buffer) +{ + HB_BUFFER_ALLOCATE_VAR (buffer, syllable); + find_syllables_use (buffer); + foreach_syllable (buffer, start, end) + buffer->unsafe_to_break (start, end); + setup_rphf_mask (plan, buffer); + setup_topographical_masks (plan, buffer); +} + +static void +record_rphf_use (const hb_ot_shape_plan_t *plan, + hb_font_t *font HB_UNUSED, + hb_buffer_t *buffer) +{ + const use_shape_plan_t *use_plan = (const use_shape_plan_t *) plan->data; + + hb_mask_t mask = use_plan->rphf_mask; + if (!mask) return; + hb_glyph_info_t *info = buffer->info; + + foreach_syllable (buffer, start, end) + { + /* Mark a substituted repha as USE(R). */ + for (unsigned int i = start; i < end && (info[i].mask & mask); i++) + if (_hb_glyph_info_substituted (&info[i])) + { + info[i].use_category() = USE(R); + break; + } + } +} + +static void +record_pref_use (const hb_ot_shape_plan_t *plan HB_UNUSED, + hb_font_t *font HB_UNUSED, + hb_buffer_t *buffer) +{ + hb_glyph_info_t *info = buffer->info; + + foreach_syllable (buffer, start, end) + { + /* Mark a substituted pref as VPre, as they behave the same way. */ + for (unsigned int i = start; i < end; i++) + if (_hb_glyph_info_substituted (&info[i])) + { + info[i].use_category() = USE(VPre); + break; + } + } +} + +static inline bool +is_halant_use (const hb_glyph_info_t &info) +{ + return (info.use_category() == USE(H) || info.use_category() == USE(HVM) || info.use_category() == USE(IS)) && + !_hb_glyph_info_ligated (&info); +} + +static void +reorder_syllable_use (hb_buffer_t *buffer, unsigned int start, unsigned int end) +{ + use_syllable_type_t syllable_type = (use_syllable_type_t) (buffer->info[start].syllable() & 0x0F); + /* Only a few syllable types need reordering. */ + if (unlikely (!(FLAG_UNSAFE (syllable_type) & + (FLAG (use_virama_terminated_cluster) | + FLAG (use_sakot_terminated_cluster) | + FLAG (use_standard_cluster) | + FLAG (use_symbol_cluster) | + FLAG (use_broken_cluster) | + 0)))) + return; + + hb_glyph_info_t *info = buffer->info; + +#define POST_BASE_FLAGS64 (FLAG64 (USE(FAbv)) | \ + FLAG64 (USE(FBlw)) | \ + FLAG64 (USE(FPst)) | \ + FLAG64 (USE(MAbv)) | \ + FLAG64 (USE(MBlw)) | \ + FLAG64 (USE(MPst)) | \ + FLAG64 (USE(MPre)) | \ + FLAG64 (USE(VAbv)) | \ + FLAG64 (USE(VBlw)) | \ + FLAG64 (USE(VPst)) | \ + FLAG64 (USE(VPre)) | \ + FLAG64 (USE(VMAbv)) | \ + FLAG64 (USE(VMBlw)) | \ + FLAG64 (USE(VMPst)) | \ + FLAG64 (USE(VMPre))) + + /* Move things forward. */ + if (info[start].use_category() == USE(R) && end - start > 1) + { + /* Got a repha. Reorder it towards the end, but before the first post-base + * glyph. */ + for (unsigned int i = start + 1; i < end; i++) + { + bool is_post_base_glyph = (FLAG64_UNSAFE (info[i].use_category()) & POST_BASE_FLAGS64) || + is_halant_use (info[i]); + if (is_post_base_glyph || i == end - 1) + { + /* If we hit a post-base glyph, move before it; otherwise move to the + * end. Shift things in between backward. */ + + if (is_post_base_glyph) + i--; + + buffer->merge_clusters (start, i + 1); + hb_glyph_info_t t = info[start]; + memmove (&info[start], &info[start + 1], (i - start) * sizeof (info[0])); + info[i] = t; + + break; + } + } + } + + /* Move things back. */ + unsigned int j = start; + for (unsigned int i = start; i < end; i++) + { + uint32_t flag = FLAG_UNSAFE (info[i].use_category()); + if (is_halant_use (info[i])) + { + /* If we hit a halant, move after it; otherwise move to the beginning, and + * shift things in between forward. */ + j = i + 1; + } + else if (((flag) & (FLAG (USE(VPre)) | FLAG (USE(VMPre)))) && + /* Only move the first component of a MultipleSubst. */ + 0 == _hb_glyph_info_get_lig_comp (&info[i]) && + j < i) + { + buffer->merge_clusters (j, i + 1); + hb_glyph_info_t t = info[i]; + memmove (&info[j + 1], &info[j], (i - j) * sizeof (info[0])); + info[j] = t; + } + } +} + +static void +reorder_use (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer) +{ + if (buffer->message (font, "start reordering USE")) + { + hb_syllabic_insert_dotted_circles (font, buffer, + use_broken_cluster, + USE(B), + USE(R)); + + foreach_syllable (buffer, start, end) + reorder_syllable_use (buffer, start, end); + + (void) buffer->message (font, "end reordering USE"); + } + + HB_BUFFER_DEALLOCATE_VAR (buffer, use_category); +} + + +static void +preprocess_text_use (const hb_ot_shape_plan_t *plan, + hb_buffer_t *buffer, + hb_font_t *font) +{ + _hb_preprocess_text_vowel_constraints (plan, buffer, font); +} + +static bool +compose_use (const hb_ot_shape_normalize_context_t *c, + hb_codepoint_t a, + hb_codepoint_t b, + hb_codepoint_t *ab) +{ + /* Avoid recomposing split matras. */ + if (HB_UNICODE_GENERAL_CATEGORY_IS_MARK (c->unicode->general_category (a))) + return false; + + return (bool)c->unicode->compose (a, b, ab); +} + + +const hb_ot_shaper_t _hb_ot_shaper_use = +{ + collect_features_use, + nullptr, /* override_features */ + data_create_use, + data_destroy_use, + preprocess_text_use, + nullptr, /* postprocess_glyphs */ + nullptr, /* decompose */ + compose_use, + setup_masks_use, + nullptr, /* reorder_marks */ + HB_TAG_NONE, /* gpos_tag */ + HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT, + HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_EARLY, + false, /* fallback_position */ +}; + + +#endif diff --git a/libs/harfbuzz/src/hb-ot-shaper-vowel-constraints.cc b/libs/harfbuzz/src/hb-ot-shaper-vowel-constraints.cc new file mode 100644 index 00000000..e76b554b --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-shaper-vowel-constraints.cc @@ -0,0 +1,477 @@ +/* == Start of generated functions == */ +/* + * The following functions are generated by running: + * + * ./gen-vowel-constraints.py ms-use/IndicShapingInvalidCluster.txt Scripts.txt + * + * on files with these headers: + * + * # IndicShapingInvalidCluster.txt + * # Date: 2015-03-12, 21:17:00 GMT [AG] + * # Date: 2019-11-08, 23:22:00 GMT [AG] + * + * # Scripts-15.0.0.txt + * # Date: 2022-04-26, 23:15:02 GMT + */ + +#include "hb.hh" + +#ifndef HB_NO_OT_SHAPE + +#include "hb-ot-shaper-vowel-constraints.hh" + +static void +_output_dotted_circle (hb_buffer_t *buffer) +{ + (void) buffer->output_glyph (0x25CCu); + _hb_glyph_info_reset_continuation (&buffer->prev()); +} + +static void +_output_with_dotted_circle (hb_buffer_t *buffer) +{ + _output_dotted_circle (buffer); + (void) buffer->next_glyph (); +} + +void +_hb_preprocess_text_vowel_constraints (const hb_ot_shape_plan_t *plan HB_UNUSED, + hb_buffer_t *buffer, + hb_font_t *font HB_UNUSED) +{ +#ifdef HB_NO_OT_SHAPER_VOWEL_CONSTRAINTS + return; +#endif + if (buffer->flags & HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE) + return; + + /* UGLY UGLY UGLY business of adding dotted-circle in the middle of + * vowel-sequences that look like another vowel. Data for each script + * collected from the USE script development spec. + * + * https://github.com/harfbuzz/harfbuzz/issues/1019 + */ + buffer->clear_output (); + unsigned int count = buffer->len; + switch ((unsigned) buffer->props.script) + { + case HB_SCRIPT_DEVANAGARI: + for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;) + { + bool matched = false; + switch (buffer->cur ().codepoint) + { + case 0x0905u: + switch (buffer->cur (1).codepoint) + { + case 0x093Au: case 0x093Bu: case 0x093Eu: case 0x0945u: + case 0x0946u: case 0x0949u: case 0x094Au: case 0x094Bu: + case 0x094Cu: case 0x094Fu: case 0x0956u: case 0x0957u: + matched = true; + break; + } + break; + case 0x0906u: + switch (buffer->cur (1).codepoint) + { + case 0x093Au: case 0x0945u: case 0x0946u: case 0x0947u: + case 0x0948u: + matched = true; + break; + } + break; + case 0x0909u: + matched = 0x0941u == buffer->cur (1).codepoint; + break; + case 0x090Fu: + switch (buffer->cur (1).codepoint) + { + case 0x0945u: case 0x0946u: case 0x0947u: + matched = true; + break; + } + break; + case 0x0930u: + if (0x094Du == buffer->cur (1).codepoint && + buffer->idx + 2 < count && + 0x0907u == buffer->cur (2).codepoint) + { + (void) buffer->next_glyph (); + matched = true; + } + break; + } + (void) buffer->next_glyph (); + if (matched) _output_with_dotted_circle (buffer); + } + break; + + case HB_SCRIPT_BENGALI: + for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;) + { + bool matched = false; + switch (buffer->cur ().codepoint) + { + case 0x0985u: + matched = 0x09BEu == buffer->cur (1).codepoint; + break; + case 0x098Bu: + matched = 0x09C3u == buffer->cur (1).codepoint; + break; + case 0x098Cu: + matched = 0x09E2u == buffer->cur (1).codepoint; + break; + } + (void) buffer->next_glyph (); + if (matched) _output_with_dotted_circle (buffer); + } + break; + + case HB_SCRIPT_GURMUKHI: + for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;) + { + bool matched = false; + switch (buffer->cur ().codepoint) + { + case 0x0A05u: + switch (buffer->cur (1).codepoint) + { + case 0x0A3Eu: case 0x0A48u: case 0x0A4Cu: + matched = true; + break; + } + break; + case 0x0A72u: + switch (buffer->cur (1).codepoint) + { + case 0x0A3Fu: case 0x0A40u: case 0x0A47u: + matched = true; + break; + } + break; + case 0x0A73u: + switch (buffer->cur (1).codepoint) + { + case 0x0A41u: case 0x0A42u: case 0x0A4Bu: + matched = true; + break; + } + break; + } + (void) buffer->next_glyph (); + if (matched) _output_with_dotted_circle (buffer); + } + break; + + case HB_SCRIPT_GUJARATI: + for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;) + { + bool matched = false; + switch (buffer->cur ().codepoint) + { + case 0x0A85u: + switch (buffer->cur (1).codepoint) + { + case 0x0ABEu: case 0x0AC5u: case 0x0AC7u: case 0x0AC8u: + case 0x0AC9u: case 0x0ACBu: case 0x0ACCu: + matched = true; + break; + } + break; + case 0x0AC5u: + matched = 0x0ABEu == buffer->cur (1).codepoint; + break; + } + (void) buffer->next_glyph (); + if (matched) _output_with_dotted_circle (buffer); + } + break; + + case HB_SCRIPT_ORIYA: + for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;) + { + bool matched = false; + switch (buffer->cur ().codepoint) + { + case 0x0B05u: + matched = 0x0B3Eu == buffer->cur (1).codepoint; + break; + case 0x0B0Fu: case 0x0B13u: + matched = 0x0B57u == buffer->cur (1).codepoint; + break; + } + (void) buffer->next_glyph (); + if (matched) _output_with_dotted_circle (buffer); + } + break; + + case HB_SCRIPT_TAMIL: + for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;) + { + bool matched = false; + if (0x0B85u == buffer->cur ().codepoint && + 0x0BC2u == buffer->cur (1).codepoint) + { + matched = true; + } + (void) buffer->next_glyph (); + if (matched) _output_with_dotted_circle (buffer); + } + break; + + case HB_SCRIPT_TELUGU: + for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;) + { + bool matched = false; + switch (buffer->cur ().codepoint) + { + case 0x0C12u: + switch (buffer->cur (1).codepoint) + { + case 0x0C4Cu: case 0x0C55u: + matched = true; + break; + } + break; + case 0x0C3Fu: case 0x0C46u: case 0x0C4Au: + matched = 0x0C55u == buffer->cur (1).codepoint; + break; + } + (void) buffer->next_glyph (); + if (matched) _output_with_dotted_circle (buffer); + } + break; + + case HB_SCRIPT_KANNADA: + for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;) + { + bool matched = false; + switch (buffer->cur ().codepoint) + { + case 0x0C89u: case 0x0C8Bu: + matched = 0x0CBEu == buffer->cur (1).codepoint; + break; + case 0x0C92u: + matched = 0x0CCCu == buffer->cur (1).codepoint; + break; + } + (void) buffer->next_glyph (); + if (matched) _output_with_dotted_circle (buffer); + } + break; + + case HB_SCRIPT_MALAYALAM: + for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;) + { + bool matched = false; + switch (buffer->cur ().codepoint) + { + case 0x0D07u: case 0x0D09u: + matched = 0x0D57u == buffer->cur (1).codepoint; + break; + case 0x0D0Eu: + matched = 0x0D46u == buffer->cur (1).codepoint; + break; + case 0x0D12u: + switch (buffer->cur (1).codepoint) + { + case 0x0D3Eu: case 0x0D57u: + matched = true; + break; + } + break; + } + (void) buffer->next_glyph (); + if (matched) _output_with_dotted_circle (buffer); + } + break; + + case HB_SCRIPT_SINHALA: + for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;) + { + bool matched = false; + switch (buffer->cur ().codepoint) + { + case 0x0D85u: + switch (buffer->cur (1).codepoint) + { + case 0x0DCFu: case 0x0DD0u: case 0x0DD1u: + matched = true; + break; + } + break; + case 0x0D8Bu: case 0x0D8Fu: case 0x0D94u: + matched = 0x0DDFu == buffer->cur (1).codepoint; + break; + case 0x0D8Du: + matched = 0x0DD8u == buffer->cur (1).codepoint; + break; + case 0x0D91u: + switch (buffer->cur (1).codepoint) + { + case 0x0DCAu: case 0x0DD9u: case 0x0DDAu: case 0x0DDCu: + case 0x0DDDu: case 0x0DDEu: + matched = true; + break; + } + break; + } + (void) buffer->next_glyph (); + if (matched) _output_with_dotted_circle (buffer); + } + break; + + case HB_SCRIPT_BRAHMI: + for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;) + { + bool matched = false; + switch (buffer->cur ().codepoint) + { + case 0x11005u: + matched = 0x11038u == buffer->cur (1).codepoint; + break; + case 0x1100Bu: + matched = 0x1103Eu == buffer->cur (1).codepoint; + break; + case 0x1100Fu: + matched = 0x11042u == buffer->cur (1).codepoint; + break; + } + (void) buffer->next_glyph (); + if (matched) _output_with_dotted_circle (buffer); + } + break; + + case HB_SCRIPT_KHOJKI: + for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;) + { + bool matched = false; + switch (buffer->cur ().codepoint) + { + case 0x11200u: + switch (buffer->cur (1).codepoint) + { + case 0x1122Cu: case 0x11231u: case 0x11233u: + matched = true; + break; + } + break; + case 0x11206u: + matched = 0x1122Cu == buffer->cur (1).codepoint; + break; + case 0x1122Cu: + switch (buffer->cur (1).codepoint) + { + case 0x11230u: case 0x11231u: + matched = true; + break; + } + break; + case 0x11240u: + matched = 0x1122Eu == buffer->cur (1).codepoint; + break; + } + (void) buffer->next_glyph (); + if (matched) _output_with_dotted_circle (buffer); + } + break; + + case HB_SCRIPT_KHUDAWADI: + for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;) + { + bool matched = false; + switch (buffer->cur ().codepoint) + { + case 0x112B0u: + switch (buffer->cur (1).codepoint) + { + case 0x112E0u: case 0x112E5u: case 0x112E6u: case 0x112E7u: + case 0x112E8u: + matched = true; + break; + } + break; + } + (void) buffer->next_glyph (); + if (matched) _output_with_dotted_circle (buffer); + } + break; + + case HB_SCRIPT_TIRHUTA: + for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;) + { + bool matched = false; + switch (buffer->cur ().codepoint) + { + case 0x11481u: + matched = 0x114B0u == buffer->cur (1).codepoint; + break; + case 0x1148Bu: case 0x1148Du: + matched = 0x114BAu == buffer->cur (1).codepoint; + break; + case 0x114AAu: + switch (buffer->cur (1).codepoint) + { + case 0x114B5u: case 0x114B6u: + matched = true; + break; + } + break; + } + (void) buffer->next_glyph (); + if (matched) _output_with_dotted_circle (buffer); + } + break; + + case HB_SCRIPT_MODI: + for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;) + { + bool matched = false; + switch (buffer->cur ().codepoint) + { + case 0x11600u: case 0x11601u: + switch (buffer->cur (1).codepoint) + { + case 0x11639u: case 0x1163Au: + matched = true; + break; + } + break; + } + (void) buffer->next_glyph (); + if (matched) _output_with_dotted_circle (buffer); + } + break; + + case HB_SCRIPT_TAKRI: + for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;) + { + bool matched = false; + switch (buffer->cur ().codepoint) + { + case 0x11680u: + switch (buffer->cur (1).codepoint) + { + case 0x116ADu: case 0x116B4u: case 0x116B5u: + matched = true; + break; + } + break; + case 0x11686u: + matched = 0x116B2u == buffer->cur (1).codepoint; + break; + } + (void) buffer->next_glyph (); + if (matched) _output_with_dotted_circle (buffer); + } + break; + + default: + break; + } + buffer->sync (); +} + + +#endif +/* == End of generated functions == */ diff --git a/libs/harfbuzz/src/hb-ot-shaper-vowel-constraints.hh b/libs/harfbuzz/src/hb-ot-shaper-vowel-constraints.hh new file mode 100644 index 00000000..5a7ee1b0 --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-shaper-vowel-constraints.hh @@ -0,0 +1,39 @@ +/* + * Copyright © 2018 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 HB_OT_SHAPER_VOWEL_CONSTRAINTS_HH +#define HB_OT_SHAPER_VOWEL_CONSTRAINTS_HH + +#include "hb.hh" + +#include "hb-ot-shaper.hh" + +HB_INTERNAL void +_hb_preprocess_text_vowel_constraints (const hb_ot_shape_plan_t *plan, + hb_buffer_t *buffer, + hb_font_t *font); + +#endif /* HB_OT_SHAPER_VOWEL_CONSTRAINTS_HH */ diff --git a/libs/harfbuzz/src/hb-ot-shaper.hh b/libs/harfbuzz/src/hb-ot-shaper.hh new file mode 100644 index 00000000..b2d1acb3 --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-shaper.hh @@ -0,0 +1,401 @@ +/* + * Copyright © 2010,2011,2012 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 HB_OT_SHAPER_HH +#define HB_OT_SHAPER_HH + +#include "hb.hh" + +#include "hb-ot-layout.hh" +#include "hb-ot-shape.hh" +#include "hb-ot-shape-normalize.hh" + + +/* buffer var allocations, used by all OT shapers */ +#define ot_shaper_var_u8_category() var2.u8[2] +#define ot_shaper_var_u8_auxiliary() var2.u8[3] + + +#define HB_OT_SHAPE_MAX_COMBINING_MARKS 32 + +enum hb_ot_shape_zero_width_marks_type_t { + HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE, + HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_EARLY, + HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE +}; + + +/* Master OT shaper list */ +#define HB_OT_SHAPERS_IMPLEMENT_SHAPERS \ + HB_OT_SHAPER_IMPLEMENT (arabic) \ + HB_OT_SHAPER_IMPLEMENT (default) \ + HB_OT_SHAPER_IMPLEMENT (dumber) \ + HB_OT_SHAPER_IMPLEMENT (hangul) \ + HB_OT_SHAPER_IMPLEMENT (hebrew) \ + HB_OT_SHAPER_IMPLEMENT (indic) \ + HB_OT_SHAPER_IMPLEMENT (khmer) \ + HB_OT_SHAPER_IMPLEMENT (myanmar) \ + HB_OT_SHAPER_IMPLEMENT (myanmar_zawgyi) \ + HB_OT_SHAPER_IMPLEMENT (thai) \ + HB_OT_SHAPER_IMPLEMENT (use) \ + /* ^--- Add new shapers here; keep sorted. */ + + +struct hb_ot_shaper_t +{ + /* collect_features() + * Called during shape_plan(). + * Shapers should use plan->map to add their features and callbacks. + * May be NULL. + */ + void (*collect_features) (hb_ot_shape_planner_t *plan); + + /* override_features() + * Called during shape_plan(). + * Shapers should use plan->map to override features and add callbacks after + * common features are added. + * May be NULL. + */ + void (*override_features) (hb_ot_shape_planner_t *plan); + + + /* data_create() + * Called at the end of shape_plan(). + * Whatever shapers return will be accessible through plan->data later. + * If nullptr is returned, means a plan failure. + */ + void *(*data_create) (const hb_ot_shape_plan_t *plan); + + /* data_destroy() + * Called when the shape_plan is being destroyed. + * plan->data is passed here for destruction. + * If nullptr is returned, means a plan failure. + * May be NULL. + */ + void (*data_destroy) (void *data); + + + /* preprocess_text() + * Called during shape(). + * Shapers can use to modify text before shaping starts. + * May be NULL. + */ + void (*preprocess_text) (const hb_ot_shape_plan_t *plan, + hb_buffer_t *buffer, + hb_font_t *font); + + /* postprocess_glyphs() + * Called during shape(). + * Shapers can use to modify glyphs after shaping ends. + * May be NULL. + */ + void (*postprocess_glyphs) (const hb_ot_shape_plan_t *plan, + hb_buffer_t *buffer, + hb_font_t *font); + + + /* decompose() + * Called during shape()'s normalization. + * May be NULL. + */ + bool (*decompose) (const hb_ot_shape_normalize_context_t *c, + hb_codepoint_t ab, + hb_codepoint_t *a, + hb_codepoint_t *b); + + /* compose() + * Called during shape()'s normalization. + * May be NULL. + */ + bool (*compose) (const hb_ot_shape_normalize_context_t *c, + hb_codepoint_t a, + hb_codepoint_t b, + hb_codepoint_t *ab); + + /* setup_masks() + * Called during shape(). + * Shapers should use map to get feature masks and set on buffer. + * Shapers may NOT modify characters. + * May be NULL. + */ + void (*setup_masks) (const hb_ot_shape_plan_t *plan, + hb_buffer_t *buffer, + hb_font_t *font); + + /* reorder_marks() + * Called during shape(). + * Shapers can use to modify ordering of combining marks. + * May be NULL. + */ + void (*reorder_marks) (const hb_ot_shape_plan_t *plan, + hb_buffer_t *buffer, + unsigned int start, + unsigned int end); + + /* gpos_tag() + * If not HB_TAG_NONE, then must match found GPOS script tag for + * GPOS to be applied. Otherwise, fallback positioning will be used. + */ + hb_tag_t gpos_tag; + + hb_ot_shape_normalization_mode_t normalization_preference; + + hb_ot_shape_zero_width_marks_type_t zero_width_marks; + + bool fallback_position; +}; + +#define HB_OT_SHAPER_IMPLEMENT(name) extern HB_INTERNAL const hb_ot_shaper_t _hb_ot_shaper_##name; +HB_OT_SHAPERS_IMPLEMENT_SHAPERS +#undef HB_OT_SHAPER_IMPLEMENT + + +static inline const hb_ot_shaper_t * +hb_ot_shaper_categorize (const hb_ot_shape_planner_t *planner) +{ + switch ((hb_tag_t) planner->props.script) + { + default: + return &_hb_ot_shaper_default; + + + /* Unicode-1.1 additions */ + case HB_SCRIPT_ARABIC: + + /* Unicode-3.0 additions */ + case HB_SCRIPT_SYRIAC: + + /* For Arabic script, use the Arabic shaper even if no OT script tag was found. + * This is because we do fallback shaping for Arabic script (and not others). + * But note that Arabic shaping is applicable only to horizontal layout; for + * vertical text, just use the generic shaper instead. */ + if ((planner->map.chosen_script[0] != HB_OT_TAG_DEFAULT_SCRIPT || + planner->props.script == HB_SCRIPT_ARABIC) && + HB_DIRECTION_IS_HORIZONTAL(planner->props.direction)) + return &_hb_ot_shaper_arabic; + else + return &_hb_ot_shaper_default; + + + /* Unicode-1.1 additions */ + case HB_SCRIPT_THAI: + case HB_SCRIPT_LAO: + + return &_hb_ot_shaper_thai; + + + /* Unicode-1.1 additions */ + case HB_SCRIPT_HANGUL: + + return &_hb_ot_shaper_hangul; + + + /* Unicode-1.1 additions */ + case HB_SCRIPT_HEBREW: + + return &_hb_ot_shaper_hebrew; + + + /* Unicode-1.1 additions */ + case HB_SCRIPT_BENGALI: + case HB_SCRIPT_DEVANAGARI: + case HB_SCRIPT_GUJARATI: + case HB_SCRIPT_GURMUKHI: + case HB_SCRIPT_KANNADA: + case HB_SCRIPT_MALAYALAM: + case HB_SCRIPT_ORIYA: + case HB_SCRIPT_TAMIL: + case HB_SCRIPT_TELUGU: + + /* If the designer designed the font for the 'DFLT' script, + * (or we ended up arbitrarily pick 'latn'), use the default shaper. + * Otherwise, use the specific shaper. + * + * If it's indy3 tag, send to USE. */ + if (planner->map.chosen_script[0] == HB_TAG ('D','F','L','T') || + planner->map.chosen_script[0] == HB_TAG ('l','a','t','n')) + return &_hb_ot_shaper_default; + else if ((planner->map.chosen_script[0] & 0x000000FF) == '3') + return &_hb_ot_shaper_use; + else + return &_hb_ot_shaper_indic; + + case HB_SCRIPT_KHMER: + return &_hb_ot_shaper_khmer; + + case HB_SCRIPT_MYANMAR: + /* If the designer designed the font for the 'DFLT' script, + * (or we ended up arbitrarily pick 'latn'), use the default shaper. + * Otherwise, use the specific shaper. + * + * If designer designed for 'mymr' tag, also send to default + * shaper. That's tag used from before Myanmar shaping spec + * was developed. The shaping spec uses 'mym2' tag. */ + if (planner->map.chosen_script[0] == HB_TAG ('D','F','L','T') || + planner->map.chosen_script[0] == HB_TAG ('l','a','t','n') || + planner->map.chosen_script[0] == HB_TAG ('m','y','m','r')) + return &_hb_ot_shaper_default; + else + return &_hb_ot_shaper_myanmar; + + +#define HB_SCRIPT_MYANMAR_ZAWGYI ((hb_script_t) HB_TAG ('Q','a','a','g')) + case HB_SCRIPT_MYANMAR_ZAWGYI: + /* https://github.com/harfbuzz/harfbuzz/issues/1162 */ + + return &_hb_ot_shaper_myanmar_zawgyi; + + + /* Unicode-2.0 additions */ + case HB_SCRIPT_TIBETAN: + + /* Unicode-3.0 additions */ + case HB_SCRIPT_MONGOLIAN: + case HB_SCRIPT_SINHALA: + + /* Unicode-3.2 additions */ + case HB_SCRIPT_BUHID: + case HB_SCRIPT_HANUNOO: + case HB_SCRIPT_TAGALOG: + case HB_SCRIPT_TAGBANWA: + + /* Unicode-4.0 additions */ + case HB_SCRIPT_LIMBU: + case HB_SCRIPT_TAI_LE: + + /* Unicode-4.1 additions */ + case HB_SCRIPT_BUGINESE: + case HB_SCRIPT_KHAROSHTHI: + case HB_SCRIPT_SYLOTI_NAGRI: + case HB_SCRIPT_TIFINAGH: + + /* Unicode-5.0 additions */ + case HB_SCRIPT_BALINESE: + case HB_SCRIPT_NKO: + case HB_SCRIPT_PHAGS_PA: + + /* Unicode-5.1 additions */ + case HB_SCRIPT_CHAM: + case HB_SCRIPT_KAYAH_LI: + case HB_SCRIPT_LEPCHA: + case HB_SCRIPT_REJANG: + case HB_SCRIPT_SAURASHTRA: + case HB_SCRIPT_SUNDANESE: + + /* Unicode-5.2 additions */ + case HB_SCRIPT_EGYPTIAN_HIEROGLYPHS: + case HB_SCRIPT_JAVANESE: + case HB_SCRIPT_KAITHI: + case HB_SCRIPT_MEETEI_MAYEK: + case HB_SCRIPT_TAI_THAM: + case HB_SCRIPT_TAI_VIET: + + /* Unicode-6.0 additions */ + case HB_SCRIPT_BATAK: + case HB_SCRIPT_BRAHMI: + case HB_SCRIPT_MANDAIC: + + /* Unicode-6.1 additions */ + case HB_SCRIPT_CHAKMA: + case HB_SCRIPT_MIAO: + case HB_SCRIPT_SHARADA: + case HB_SCRIPT_TAKRI: + + /* Unicode-7.0 additions */ + case HB_SCRIPT_DUPLOYAN: + case HB_SCRIPT_GRANTHA: + case HB_SCRIPT_KHOJKI: + case HB_SCRIPT_KHUDAWADI: + case HB_SCRIPT_MAHAJANI: + case HB_SCRIPT_MANICHAEAN: + case HB_SCRIPT_MODI: + case HB_SCRIPT_PAHAWH_HMONG: + case HB_SCRIPT_PSALTER_PAHLAVI: + case HB_SCRIPT_SIDDHAM: + case HB_SCRIPT_TIRHUTA: + + /* Unicode-8.0 additions */ + case HB_SCRIPT_AHOM: + case HB_SCRIPT_MULTANI: + + /* Unicode-9.0 additions */ + case HB_SCRIPT_ADLAM: + case HB_SCRIPT_BHAIKSUKI: + case HB_SCRIPT_MARCHEN: + case HB_SCRIPT_NEWA: + + /* Unicode-10.0 additions */ + case HB_SCRIPT_MASARAM_GONDI: + case HB_SCRIPT_SOYOMBO: + case HB_SCRIPT_ZANABAZAR_SQUARE: + + /* Unicode-11.0 additions */ + case HB_SCRIPT_DOGRA: + case HB_SCRIPT_GUNJALA_GONDI: + case HB_SCRIPT_HANIFI_ROHINGYA: + case HB_SCRIPT_MAKASAR: + case HB_SCRIPT_MEDEFAIDRIN: + case HB_SCRIPT_OLD_SOGDIAN: + case HB_SCRIPT_SOGDIAN: + + /* Unicode-12.0 additions */ + case HB_SCRIPT_ELYMAIC: + case HB_SCRIPT_NANDINAGARI: + case HB_SCRIPT_NYIAKENG_PUACHUE_HMONG: + case HB_SCRIPT_WANCHO: + + /* Unicode-13.0 additions */ + case HB_SCRIPT_CHORASMIAN: + case HB_SCRIPT_DIVES_AKURU: + case HB_SCRIPT_KHITAN_SMALL_SCRIPT: + case HB_SCRIPT_YEZIDI: + + /* Unicode-14.0 additions */ + case HB_SCRIPT_CYPRO_MINOAN: + case HB_SCRIPT_OLD_UYGHUR: + case HB_SCRIPT_TANGSA: + case HB_SCRIPT_TOTO: + case HB_SCRIPT_VITHKUQI: + + /* Unicode-15.0 additions */ + case HB_SCRIPT_KAWI: + case HB_SCRIPT_NAG_MUNDARI: + + /* If the designer designed the font for the 'DFLT' script, + * (or we ended up arbitrarily pick 'latn'), use the default shaper. + * Otherwise, use the specific shaper. + * Note that for some simple scripts, there may not be *any* + * GSUB/GPOS needed, so there may be no scripts found! */ + if (planner->map.chosen_script[0] == HB_TAG ('D','F','L','T') || + planner->map.chosen_script[0] == HB_TAG ('l','a','t','n')) + return &_hb_ot_shaper_default; + else + return &_hb_ot_shaper_use; + } +} + + +#endif /* HB_OT_SHAPER_HH */ diff --git a/libs/harfbuzz/src/hb-ot-stat-table.hh b/libs/harfbuzz/src/hb-ot-stat-table.hh new file mode 100644 index 00000000..af6d550c --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-stat-table.hh @@ -0,0 +1,618 @@ +/* + * Copyright © 2018 Ebrahim Byagowi + * + * 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. + */ + +#ifndef HB_OT_STAT_TABLE_HH +#define HB_OT_STAT_TABLE_HH + +#include "hb-open-type.hh" +#include "hb-ot-layout-common.hh" + +/* + * STAT -- Style Attributes + * https://docs.microsoft.com/en-us/typography/opentype/spec/stat + */ +#define HB_OT_TAG_STAT HB_TAG('S','T','A','T') + + +namespace OT { + +enum +{ + OLDER_SIBLING_FONT_ATTRIBUTE = 0x0001, /* If set, this axis value table + * provides axis value information + * that is applicable to other fonts + * within the same font family. This + * is used if the other fonts were + * released earlier and did not include + * information about values for some axis. + * If newer versions of the other + * fonts include the information + * themselves and are present, + * then this record is ignored. */ + ELIDABLE_AXIS_VALUE_NAME = 0x0002 /* If set, it indicates that the axis + * value represents the “normal” value + * for the axis and may be omitted when + * composing name strings. */ + // Reserved = 0xFFFC /* Reserved for future use — set to zero. */ +}; + +struct StatAxisRecord +{ + int cmp (hb_tag_t key) const { return tag.cmp (key); } + + hb_ot_name_id_t get_name_id () const { return nameID; } + + hb_tag_t get_axis_tag () const { return tag; } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this))); + } + + protected: + Tag tag; /* A tag identifying the axis of design variation. */ + NameID nameID; /* The name ID for entries in the 'name' table that + * provide a display string for this axis. */ + HBUINT16 ordering; /* A value that applications can use to determine + * primary sorting of face names, or for ordering + * of descriptors when composing family or face names. */ + public: + DEFINE_SIZE_STATIC (8); +}; + +struct AxisValueFormat1 +{ + unsigned int get_axis_index () const { return axisIndex; } + float get_value () const { return value.to_float (); } + + hb_ot_name_id_t get_value_name_id () const { return valueNameID; } + + hb_tag_t get_axis_tag (const hb_array_t axis_records) const + { + unsigned axis_idx = get_axis_index (); + return axis_records[axis_idx].get_axis_tag (); + } + + bool keep_axis_value (const hb_array_t axis_records, + const hb_hashmap_t *user_axes_location) const + { + hb_tag_t axis_tag = get_axis_tag (axis_records); + float axis_value = get_value (); + + if (!user_axes_location->has (axis_tag) || + fabsf(axis_value - user_axes_location->get (axis_tag)) < 0.001f) + return true; + + return false; + } + + bool subset (hb_subset_context_t *c, + const hb_array_t axis_records) const + { + TRACE_SUBSET (this); + const hb_hashmap_t* user_axes_location = c->plan->user_axes_location; + + if (keep_axis_value (axis_records, user_axes_location)) + return_trace (c->serializer->embed (this)); + + return_trace (false); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + protected: + HBUINT16 format; /* Format identifier — set to 1. */ + HBUINT16 axisIndex; /* Zero-base index into the axis record array + * identifying the axis of design variation + * to which the axis value record applies. + * Must be less than designAxisCount. */ + HBUINT16 flags; /* Flags — see below for details. */ + NameID valueNameID; /* The name ID for entries in the 'name' table + * that provide a display string for this + * attribute value. */ + F16DOT16 value; /* A numeric value for this attribute value. */ + public: + DEFINE_SIZE_STATIC (12); +}; + +struct AxisValueFormat2 +{ + unsigned int get_axis_index () const { return axisIndex; } + float get_value () const { return nominalValue.to_float (); } + + hb_ot_name_id_t get_value_name_id () const { return valueNameID; } + + hb_tag_t get_axis_tag (const hb_array_t axis_records) const + { + unsigned axis_idx = get_axis_index (); + return axis_records[axis_idx].get_axis_tag (); + } + + bool keep_axis_value (const hb_array_t axis_records, + const hb_hashmap_t *user_axes_location) const + { + hb_tag_t axis_tag = get_axis_tag (axis_records); + float axis_value = get_value (); + + if (!user_axes_location->has (axis_tag) || + fabsf(axis_value - user_axes_location->get (axis_tag)) < 0.001f) + return true; + + return false; + } + + bool subset (hb_subset_context_t *c, + const hb_array_t axis_records) const + { + TRACE_SUBSET (this); + const hb_hashmap_t* user_axes_location = c->plan->user_axes_location; + + if (keep_axis_value (axis_records, user_axes_location)) + return_trace (c->serializer->embed (this)); + + return_trace (false); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + protected: + HBUINT16 format; /* Format identifier — set to 2. */ + HBUINT16 axisIndex; /* Zero-base index into the axis record array + * identifying the axis of design variation + * to which the axis value record applies. + * Must be less than designAxisCount. */ + HBUINT16 flags; /* Flags — see below for details. */ + NameID valueNameID; /* The name ID for entries in the 'name' table + * that provide a display string for this + * attribute value. */ + F16DOT16 nominalValue; /* A numeric value for this attribute value. */ + F16DOT16 rangeMinValue; /* The minimum value for a range associated + * with the specified name ID. */ + F16DOT16 rangeMaxValue; /* The maximum value for a range associated + * with the specified name ID. */ + public: + DEFINE_SIZE_STATIC (20); +}; + +struct AxisValueFormat3 +{ + unsigned int get_axis_index () const { return axisIndex; } + float get_value () const { return value.to_float (); } + + hb_ot_name_id_t get_value_name_id () const { return valueNameID; } + + hb_tag_t get_axis_tag (const hb_array_t axis_records) const + { + unsigned axis_idx = get_axis_index (); + return axis_records[axis_idx].get_axis_tag (); + } + + bool keep_axis_value (const hb_array_t axis_records, + const hb_hashmap_t *user_axes_location) const + { + hb_tag_t axis_tag = get_axis_tag (axis_records); + float axis_value = get_value (); + + if (!user_axes_location->has (axis_tag) || + fabsf(axis_value - user_axes_location->get (axis_tag)) < 0.001f) + return true; + + return false; + } + + bool subset (hb_subset_context_t *c, + const hb_array_t axis_records) const + { + TRACE_SUBSET (this); + const hb_hashmap_t* user_axes_location = c->plan->user_axes_location; + + if (keep_axis_value (axis_records, user_axes_location)) + return_trace (c->serializer->embed (this)); + + return_trace (false); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + protected: + HBUINT16 format; /* Format identifier — set to 3. */ + HBUINT16 axisIndex; /* Zero-base index into the axis record array + * identifying the axis of design variation + * to which the axis value record applies. + * Must be less than designAxisCount. */ + HBUINT16 flags; /* Flags — see below for details. */ + NameID valueNameID; /* The name ID for entries in the 'name' table + * that provide a display string for this + * attribute value. */ + F16DOT16 value; /* A numeric value for this attribute value. */ + F16DOT16 linkedValue; /* The numeric value for a style-linked mapping + * from this value. */ + public: + DEFINE_SIZE_STATIC (16); +}; + +struct AxisValueRecord +{ + unsigned int get_axis_index () const { return axisIndex; } + float get_value () const { return value.to_float (); } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + protected: + HBUINT16 axisIndex; /* Zero-base index into the axis record array + * identifying the axis to which this value + * applies. Must be less than designAxisCount. */ + F16DOT16 value; /* A numeric value for this attribute value. */ + public: + DEFINE_SIZE_STATIC (6); +}; + +struct AxisValueFormat4 +{ + const AxisValueRecord &get_axis_record (unsigned int axis_index) const + { return axisValues.as_array (axisCount)[axis_index]; } + + bool keep_axis_value (const hb_array_t axis_records, + const hb_hashmap_t *user_axes_location) const + { + hb_array_t axis_value_records = axisValues.as_array (axisCount); + + for (const auto& rec : axis_value_records) + { + unsigned axis_idx = rec.get_axis_index (); + float axis_value = rec.get_value (); + hb_tag_t axis_tag = axis_records[axis_idx].get_axis_tag (); + + if (user_axes_location->has (axis_tag) && + fabsf(axis_value - user_axes_location->get (axis_tag)) > 0.001f) + return false; + } + + return true; + } + + bool subset (hb_subset_context_t *c, + const hb_array_t axis_records) const + { + TRACE_SUBSET (this); + const hb_hashmap_t *user_axes_location = c->plan->user_axes_location; + if (!keep_axis_value (axis_records, user_axes_location)) + return_trace (false); + + unsigned total_size = min_size + axisCount * AxisValueRecord::static_size; + auto *out = c->serializer->allocate_size (total_size); + if (unlikely (!out)) return_trace (false); + memcpy (out, this, total_size); + return_trace (true); + } + + hb_ot_name_id_t get_value_name_id () const { return valueNameID; } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + axisValues.sanitize (c, axisCount))); + } + + protected: + HBUINT16 format; /* Format identifier — set to 4. */ + HBUINT16 axisCount; /* The total number of axes contributing to + * this axis-values combination. */ + HBUINT16 flags; /* Flags — see below for details. */ + NameID valueNameID; /* The name ID for entries in the 'name' table + * that provide a display string for this + * attribute value. */ + UnsizedArrayOf + axisValues; /* Array of AxisValue records that provide the + * combination of axis values, one for each + * contributing axis. */ + public: + DEFINE_SIZE_ARRAY (8, axisValues); +}; + +struct AxisValue +{ + bool get_value (unsigned int axis_index) const + { + switch (u.format) + { + case 1: return u.format1.get_value (); + case 2: return u.format2.get_value (); + case 3: return u.format3.get_value (); + case 4: return u.format4.get_axis_record (axis_index).get_value (); + default:return 0; + } + } + + unsigned int get_axis_index () const + { + switch (u.format) + { + case 1: return u.format1.get_axis_index (); + case 2: return u.format2.get_axis_index (); + case 3: return u.format3.get_axis_index (); + /* case 4: Makes more sense for variable fonts which are handled by fvar in hb-style */ + default:return -1; + } + } + + hb_ot_name_id_t get_value_name_id () const + { + switch (u.format) + { + case 1: return u.format1.get_value_name_id (); + case 2: return u.format2.get_value_name_id (); + case 3: return u.format3.get_value_name_id (); + case 4: return u.format4.get_value_name_id (); + default:return HB_OT_NAME_ID_INVALID; + } + } + + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const + { + TRACE_DISPATCH (this, u.format); + if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ()); + switch (u.format) { + case 1: return_trace (c->dispatch (u.format1, std::forward (ds)...)); + case 2: return_trace (c->dispatch (u.format2, std::forward (ds)...)); + case 3: return_trace (c->dispatch (u.format3, std::forward (ds)...)); + case 4: return_trace (c->dispatch (u.format4, std::forward (ds)...)); + default:return_trace (c->default_return_value ()); + } + } + + bool keep_axis_value (const hb_array_t axis_records, + hb_hashmap_t *user_axes_location) const + { + switch (u.format) + { + case 1: return u.format1.keep_axis_value (axis_records, user_axes_location); + case 2: return u.format2.keep_axis_value (axis_records, user_axes_location); + case 3: return u.format3.keep_axis_value (axis_records, user_axes_location); + case 4: return u.format4.keep_axis_value (axis_records, user_axes_location); + default:return false; + } + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (unlikely (!c->check_struct (this))) + return_trace (false); + + switch (u.format) + { + case 1: return_trace (u.format1.sanitize (c)); + case 2: return_trace (u.format2.sanitize (c)); + case 3: return_trace (u.format3.sanitize (c)); + case 4: return_trace (u.format4.sanitize (c)); + default:return_trace (true); + } + } + + protected: + union + { + HBUINT16 format; + AxisValueFormat1 format1; + AxisValueFormat2 format2; + AxisValueFormat3 format3; + AxisValueFormat4 format4; + } u; + public: + DEFINE_SIZE_UNION (2, format); +}; + +struct AxisValueOffsetArray: UnsizedArrayOf> +{ + bool subset (hb_subset_context_t *c, + unsigned axisValueCount, + unsigned& count, + const hb_array_t axis_records) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->start_embed (this); + if (unlikely (!out)) return_trace (false); + + auto axisValueOffsets = as_array (axisValueCount); + count = 0; + for (const auto& offset : axisValueOffsets) + { + if (!offset) continue; + auto o_snap = c->serializer->snapshot (); + auto *o = c->serializer->embed (offset); + if (!o) return_trace (false); + if (!o->serialize_subset (c, offset, this, axis_records)) + { + c->serializer->revert (o_snap); + continue; + } + count++; + } + + return_trace (count); + } +}; + +struct STAT +{ + static constexpr hb_tag_t tableTag = HB_OT_TAG_STAT; + + bool has_data () const { return version.to_int (); } + + bool get_value (hb_tag_t tag, float *value) const + { + unsigned int axis_index; + if (!get_design_axes ().lfind (tag, &axis_index)) return false; + + hb_array_t> axis_values = get_axis_value_offsets (); + for (unsigned int i = 0; i < axis_values.length; i++) + { + const AxisValue& axis_value = this+axis_values[i]; + if (axis_value.get_axis_index () == axis_index) + { + if (value) + *value = axis_value.get_value (axis_index); + return true; + } + } + return false; + } + + unsigned get_design_axis_count () const { return designAxisCount; } + + hb_ot_name_id_t get_axis_record_name_id (unsigned axis_record_index) const + { + if (unlikely (axis_record_index >= designAxisCount)) return HB_OT_NAME_ID_INVALID; + const StatAxisRecord &axis_record = get_design_axes ()[axis_record_index]; + return axis_record.get_name_id (); + } + + unsigned get_axis_value_count () const { return axisValueCount; } + + hb_ot_name_id_t get_axis_value_name_id (unsigned axis_value_index) const + { + if (unlikely (axis_value_index >= axisValueCount)) return HB_OT_NAME_ID_INVALID; + const AxisValue &axis_value = (this + get_axis_value_offsets ()[axis_value_index]); + return axis_value.get_value_name_id (); + } + + void collect_name_ids (hb_hashmap_t *user_axes_location, + hb_set_t *nameids_to_retain /* OUT */) const + { + if (!has_data ()) return; + + + get_design_axes () + | hb_map (&StatAxisRecord::get_name_id) + | hb_sink (nameids_to_retain) + ; + + auto designAxes = get_design_axes (); + + + get_axis_value_offsets () + | hb_map (hb_add (&(this + offsetToAxisValueOffsets))) + | hb_filter ([&] (const AxisValue& _) + { return _.keep_axis_value (designAxes, user_axes_location); }) + | hb_map (&AxisValue::get_value_name_id) + | hb_sink (nameids_to_retain) + ; + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + STAT *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + + auto designAxes = get_design_axes (); + for (unsigned i = 0; i < (unsigned)designAxisCount; i++) + if (unlikely (!c->serializer->embed (designAxes[i]))) + return_trace (false); + + if (designAxisCount) + c->serializer->check_assign (out->designAxesOffset, this->get_size (), + HB_SERIALIZE_ERROR_INT_OVERFLOW); + + unsigned count = 0; + out->offsetToAxisValueOffsets.serialize_subset (c, offsetToAxisValueOffsets, this, + axisValueCount, count, designAxes); + return_trace (c->serializer->check_assign (out->axisValueCount, count, HB_SERIALIZE_ERROR_INT_OVERFLOW)); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + version.major == 1 && + version.minor > 0 && + designAxesOffset.sanitize (c, this, designAxisCount) && + offsetToAxisValueOffsets.sanitize (c, this, axisValueCount, &(this+offsetToAxisValueOffsets)))); + } + + protected: + hb_array_t const get_design_axes () const + { return (this+designAxesOffset).as_array (designAxisCount); } + + hb_array_t> const get_axis_value_offsets () const + { return (this+offsetToAxisValueOffsets).as_array (axisValueCount); } + + + protected: + FixedVersion<>version; /* Version of the stat table + * initially set to 0x00010002u */ + HBUINT16 designAxisSize; /* The size in bytes of each axis record. */ + HBUINT16 designAxisCount;/* The number of design axis records. In a + * font with an 'fvar' table, this value must be + * greater than or equal to the axisCount value + * in the 'fvar' table. In all fonts, must + * be greater than zero if axisValueCount + * is greater than zero. */ + NNOffset32To> + designAxesOffset; + /* Offset in bytes from the beginning of + * the STAT table to the start of the design + * axes array. If designAxisCount is zero, + * set to zero; if designAxisCount is greater + * than zero, must be greater than zero. */ + HBUINT16 axisValueCount; /* The number of axis value tables. */ + NNOffset32To + offsetToAxisValueOffsets; + /* Offset in bytes from the beginning of + * the STAT table to the start of the design + * axes value offsets array. If axisValueCount + * is zero, set to zero; if axisValueCount is + * greater than zero, must be greater than zero. */ + NameID elidedFallbackNameID; + /* Name ID used as fallback when projection of + * names into a particular font model produces + * a subfamily name containing only elidable + * elements. */ + public: + DEFINE_SIZE_STATIC (20); +}; + + +} /* namespace OT */ + + +#endif /* HB_OT_STAT_TABLE_HH */ diff --git a/libs/harfbuzz/src/hb-ot-tag-table.hh b/libs/harfbuzz/src/hb-ot-tag-table.hh new file mode 100644 index 00000000..9394b90e --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-tag-table.hh @@ -0,0 +1,2993 @@ +/* == Start of generated table == */ +/* + * The following table is generated by running: + * + * ./gen-tag-table.py languagetags language-subtag-registry + * + * on files with these headers: + * + * + * File-Date: 2022-03-02 + */ + +#ifndef HB_OT_TAG_TABLE_HH +#define HB_OT_TAG_TABLE_HH + +static const LangTag ot_languages2[] = { + {HB_TAG('a','a',' ',' '), HB_TAG('A','F','R',' ')}, /* Afar */ + {HB_TAG('a','b',' ',' '), HB_TAG('A','B','K',' ')}, /* Abkhazian */ + {HB_TAG('a','f',' ',' '), HB_TAG('A','F','K',' ')}, /* Afrikaans */ + {HB_TAG('a','k',' ',' '), HB_TAG('A','K','A',' ')}, /* Akan [macrolanguage] */ + {HB_TAG('a','m',' ',' '), HB_TAG('A','M','H',' ')}, /* Amharic */ + {HB_TAG('a','n',' ',' '), HB_TAG('A','R','G',' ')}, /* Aragonese */ + {HB_TAG('a','r',' ',' '), HB_TAG('A','R','A',' ')}, /* Arabic [macrolanguage] */ + {HB_TAG('a','s',' ',' '), HB_TAG('A','S','M',' ')}, /* Assamese */ + {HB_TAG('a','v',' ',' '), HB_TAG('A','V','R',' ')}, /* Avaric -> Avar */ + {HB_TAG('a','y',' ',' '), HB_TAG('A','Y','M',' ')}, /* Aymara [macrolanguage] */ + {HB_TAG('a','z',' ',' '), HB_TAG('A','Z','E',' ')}, /* Azerbaijani [macrolanguage] */ + {HB_TAG('b','a',' ',' '), HB_TAG('B','S','H',' ')}, /* Bashkir */ + {HB_TAG('b','e',' ',' '), HB_TAG('B','E','L',' ')}, /* Belarusian -> Belarussian */ + {HB_TAG('b','g',' ',' '), HB_TAG('B','G','R',' ')}, /* Bulgarian */ + {HB_TAG('b','i',' ',' '), HB_TAG('B','I','S',' ')}, /* Bislama */ + {HB_TAG('b','i',' ',' '), HB_TAG('C','P','P',' ')}, /* Bislama -> Creoles */ + {HB_TAG('b','m',' ',' '), HB_TAG('B','M','B',' ')}, /* Bambara (Bamanankan) */ + {HB_TAG('b','n',' ',' '), HB_TAG('B','E','N',' ')}, /* Bengali */ + {HB_TAG('b','o',' ',' '), HB_TAG('T','I','B',' ')}, /* Tibetan */ + {HB_TAG('b','r',' ',' '), HB_TAG('B','R','E',' ')}, /* Breton */ + {HB_TAG('b','s',' ',' '), HB_TAG('B','O','S',' ')}, /* Bosnian */ + {HB_TAG('c','a',' ',' '), HB_TAG('C','A','T',' ')}, /* Catalan */ + {HB_TAG('c','e',' ',' '), HB_TAG('C','H','E',' ')}, /* Chechen */ + {HB_TAG('c','h',' ',' '), HB_TAG('C','H','A',' ')}, /* Chamorro */ + {HB_TAG('c','o',' ',' '), HB_TAG('C','O','S',' ')}, /* Corsican */ + {HB_TAG('c','r',' ',' '), HB_TAG('C','R','E',' ')}, /* Cree [macrolanguage] */ + {HB_TAG('c','s',' ',' '), HB_TAG('C','S','Y',' ')}, /* Czech */ + {HB_TAG('c','u',' ',' '), HB_TAG('C','S','L',' ')}, /* Church Slavonic */ + {HB_TAG('c','v',' ',' '), HB_TAG('C','H','U',' ')}, /* Chuvash */ + {HB_TAG('c','y',' ',' '), HB_TAG('W','E','L',' ')}, /* Welsh */ + {HB_TAG('d','a',' ',' '), HB_TAG('D','A','N',' ')}, /* Danish */ + {HB_TAG('d','e',' ',' '), HB_TAG('D','E','U',' ')}, /* German */ + {HB_TAG('d','v',' ',' '), HB_TAG('D','I','V',' ')}, /* Divehi (Dhivehi, Maldivian) */ + {HB_TAG('d','v',' ',' '), HB_TAG('D','H','V',' ')}, /* Divehi (Dhivehi, Maldivian) (deprecated) */ + {HB_TAG('d','z',' ',' '), HB_TAG('D','Z','N',' ')}, /* Dzongkha */ + {HB_TAG('e','e',' ',' '), HB_TAG('E','W','E',' ')}, /* Ewe */ + {HB_TAG('e','l',' ',' '), HB_TAG('E','L','L',' ')}, /* Modern Greek (1453-) -> Greek */ + {HB_TAG('e','n',' ',' '), HB_TAG('E','N','G',' ')}, /* English */ + {HB_TAG('e','o',' ',' '), HB_TAG('N','T','O',' ')}, /* Esperanto */ + {HB_TAG('e','s',' ',' '), HB_TAG('E','S','P',' ')}, /* Spanish */ + {HB_TAG('e','t',' ',' '), HB_TAG('E','T','I',' ')}, /* Estonian [macrolanguage] */ + {HB_TAG('e','u',' ',' '), HB_TAG('E','U','Q',' ')}, /* Basque */ + {HB_TAG('f','a',' ',' '), HB_TAG('F','A','R',' ')}, /* Persian [macrolanguage] */ + {HB_TAG('f','f',' ',' '), HB_TAG('F','U','L',' ')}, /* Fulah [macrolanguage] */ + {HB_TAG('f','i',' ',' '), HB_TAG('F','I','N',' ')}, /* Finnish */ + {HB_TAG('f','j',' ',' '), HB_TAG('F','J','I',' ')}, /* Fijian */ + {HB_TAG('f','o',' ',' '), HB_TAG('F','O','S',' ')}, /* Faroese */ + {HB_TAG('f','r',' ',' '), HB_TAG('F','R','A',' ')}, /* French */ + {HB_TAG('f','y',' ',' '), HB_TAG('F','R','I',' ')}, /* Western Frisian -> Frisian */ + {HB_TAG('g','a',' ',' '), HB_TAG('I','R','I',' ')}, /* Irish */ + {HB_TAG('g','d',' ',' '), HB_TAG('G','A','E',' ')}, /* Scottish Gaelic (Gaelic) */ + {HB_TAG('g','l',' ',' '), HB_TAG('G','A','L',' ')}, /* Galician */ + {HB_TAG('g','n',' ',' '), HB_TAG('G','U','A',' ')}, /* Guarani [macrolanguage] */ + {HB_TAG('g','u',' ',' '), HB_TAG('G','U','J',' ')}, /* Gujarati */ + {HB_TAG('g','v',' ',' '), HB_TAG('M','N','X',' ')}, /* Manx */ + {HB_TAG('h','a',' ',' '), HB_TAG('H','A','U',' ')}, /* Hausa */ + {HB_TAG('h','e',' ',' '), HB_TAG('I','W','R',' ')}, /* Hebrew */ + {HB_TAG('h','i',' ',' '), HB_TAG('H','I','N',' ')}, /* Hindi */ + {HB_TAG('h','o',' ',' '), HB_TAG('H','M','O',' ')}, /* Hiri Motu */ + {HB_TAG('h','o',' ',' '), HB_TAG('C','P','P',' ')}, /* Hiri Motu -> Creoles */ + {HB_TAG('h','r',' ',' '), HB_TAG('H','R','V',' ')}, /* Croatian */ + {HB_TAG('h','t',' ',' '), HB_TAG('H','A','I',' ')}, /* Haitian (Haitian Creole) */ + {HB_TAG('h','t',' ',' '), HB_TAG('C','P','P',' ')}, /* Haitian -> Creoles */ + {HB_TAG('h','u',' ',' '), HB_TAG('H','U','N',' ')}, /* Hungarian */ + {HB_TAG('h','y',' ',' '), HB_TAG('H','Y','E','0')}, /* Armenian -> Armenian East */ + {HB_TAG('h','y',' ',' '), HB_TAG('H','Y','E',' ')}, /* Armenian */ + {HB_TAG('h','z',' ',' '), HB_TAG('H','E','R',' ')}, /* Herero */ + {HB_TAG('i','a',' ',' '), HB_TAG('I','N','A',' ')}, /* Interlingua (International Auxiliary Language Association) */ + {HB_TAG('i','d',' ',' '), HB_TAG('I','N','D',' ')}, /* Indonesian */ + {HB_TAG('i','d',' ',' '), HB_TAG('M','L','Y',' ')}, /* Indonesian -> Malay */ + {HB_TAG('i','e',' ',' '), HB_TAG('I','L','E',' ')}, /* Interlingue */ + {HB_TAG('i','g',' ',' '), HB_TAG('I','B','O',' ')}, /* Igbo */ + {HB_TAG('i','i',' ',' '), HB_TAG('Y','I','M',' ')}, /* Sichuan Yi -> Yi Modern */ + {HB_TAG('i','k',' ',' '), HB_TAG('I','P','K',' ')}, /* Inupiaq [macrolanguage] -> Inupiat */ + {HB_TAG('i','n',' ',' '), HB_TAG('I','N','D',' ')}, /* Indonesian (retired code) */ + {HB_TAG('i','n',' ',' '), HB_TAG('M','L','Y',' ')}, /* Indonesian (retired code) -> Malay */ + {HB_TAG('i','o',' ',' '), HB_TAG('I','D','O',' ')}, /* Ido */ + {HB_TAG('i','s',' ',' '), HB_TAG('I','S','L',' ')}, /* Icelandic */ + {HB_TAG('i','t',' ',' '), HB_TAG('I','T','A',' ')}, /* Italian */ + {HB_TAG('i','u',' ',' '), HB_TAG('I','N','U',' ')}, /* Inuktitut [macrolanguage] */ + {HB_TAG('i','u',' ',' '), HB_TAG('I','N','U','K')}, /* Inuktitut [macrolanguage] -> Nunavik Inuktitut */ + {HB_TAG('i','w',' ',' '), HB_TAG('I','W','R',' ')}, /* Hebrew (retired code) */ + {HB_TAG('j','a',' ',' '), HB_TAG('J','A','N',' ')}, /* Japanese */ + {HB_TAG('j','i',' ',' '), HB_TAG('J','I','I',' ')}, /* Yiddish (retired code) */ + {HB_TAG('j','v',' ',' '), HB_TAG('J','A','V',' ')}, /* Javanese */ + {HB_TAG('j','w',' ',' '), HB_TAG('J','A','V',' ')}, /* Javanese (retired code) */ + {HB_TAG('k','a',' ',' '), HB_TAG('K','A','T',' ')}, /* Georgian */ + {HB_TAG('k','g',' ',' '), HB_TAG('K','O','N','0')}, /* Kongo [macrolanguage] */ + {HB_TAG('k','i',' ',' '), HB_TAG('K','I','K',' ')}, /* Kikuyu (Gikuyu) */ + {HB_TAG('k','j',' ',' '), HB_TAG('K','U','A',' ')}, /* Kuanyama */ + {HB_TAG('k','k',' ',' '), HB_TAG('K','A','Z',' ')}, /* Kazakh */ + {HB_TAG('k','l',' ',' '), HB_TAG('G','R','N',' ')}, /* Greenlandic */ + {HB_TAG('k','m',' ',' '), HB_TAG('K','H','M',' ')}, /* Khmer */ + {HB_TAG('k','n',' ',' '), HB_TAG('K','A','N',' ')}, /* Kannada */ + {HB_TAG('k','o',' ',' '), HB_TAG('K','O','R',' ')}, /* Korean */ + {HB_TAG('k','o',' ',' '), HB_TAG('K','O','H',' ')}, /* Korean -> Korean Old Hangul */ + {HB_TAG('k','r',' ',' '), HB_TAG('K','N','R',' ')}, /* Kanuri [macrolanguage] */ + {HB_TAG('k','s',' ',' '), HB_TAG('K','S','H',' ')}, /* Kashmiri */ + {HB_TAG('k','u',' ',' '), HB_TAG('K','U','R',' ')}, /* Kurdish [macrolanguage] */ + {HB_TAG('k','v',' ',' '), HB_TAG('K','O','M',' ')}, /* Komi [macrolanguage] */ + {HB_TAG('k','w',' ',' '), HB_TAG('C','O','R',' ')}, /* Cornish */ + {HB_TAG('k','y',' ',' '), HB_TAG('K','I','R',' ')}, /* Kirghiz (Kyrgyz) */ + {HB_TAG('l','a',' ',' '), HB_TAG('L','A','T',' ')}, /* Latin */ + {HB_TAG('l','b',' ',' '), HB_TAG('L','T','Z',' ')}, /* Luxembourgish */ + {HB_TAG('l','g',' ',' '), HB_TAG('L','U','G',' ')}, /* Ganda */ + {HB_TAG('l','i',' ',' '), HB_TAG('L','I','M',' ')}, /* Limburgish */ + {HB_TAG('l','n',' ',' '), HB_TAG('L','I','N',' ')}, /* Lingala */ + {HB_TAG('l','o',' ',' '), HB_TAG('L','A','O',' ')}, /* Lao */ + {HB_TAG('l','t',' ',' '), HB_TAG('L','T','H',' ')}, /* Lithuanian */ + {HB_TAG('l','u',' ',' '), HB_TAG('L','U','B',' ')}, /* Luba-Katanga */ + {HB_TAG('l','v',' ',' '), HB_TAG('L','V','I',' ')}, /* Latvian [macrolanguage] */ + {HB_TAG('m','g',' ',' '), HB_TAG('M','L','G',' ')}, /* Malagasy [macrolanguage] */ + {HB_TAG('m','h',' ',' '), HB_TAG('M','A','H',' ')}, /* Marshallese */ + {HB_TAG('m','i',' ',' '), HB_TAG('M','R','I',' ')}, /* Maori */ + {HB_TAG('m','k',' ',' '), HB_TAG('M','K','D',' ')}, /* Macedonian */ + {HB_TAG('m','l',' ',' '), HB_TAG('M','A','L',' ')}, /* Malayalam -> Malayalam Traditional */ + {HB_TAG('m','l',' ',' '), HB_TAG('M','L','R',' ')}, /* Malayalam -> Malayalam Reformed */ + {HB_TAG('m','n',' ',' '), HB_TAG('M','N','G',' ')}, /* Mongolian [macrolanguage] */ + {HB_TAG('m','o',' ',' '), HB_TAG('M','O','L',' ')}, /* Moldavian (retired code) */ + {HB_TAG('m','o',' ',' '), HB_TAG('R','O','M',' ')}, /* Moldavian (retired code) -> Romanian */ + {HB_TAG('m','r',' ',' '), HB_TAG('M','A','R',' ')}, /* Marathi */ + {HB_TAG('m','s',' ',' '), HB_TAG('M','L','Y',' ')}, /* Malay [macrolanguage] */ + {HB_TAG('m','t',' ',' '), HB_TAG('M','T','S',' ')}, /* Maltese */ + {HB_TAG('m','y',' ',' '), HB_TAG('B','R','M',' ')}, /* Burmese */ + {HB_TAG('n','a',' ',' '), HB_TAG('N','A','U',' ')}, /* Nauru -> Nauruan */ + {HB_TAG('n','b',' ',' '), HB_TAG('N','O','R',' ')}, /* Norwegian Bokmål -> Norwegian */ + {HB_TAG('n','d',' ',' '), HB_TAG('N','D','B',' ')}, /* North Ndebele -> Ndebele */ + {HB_TAG('n','e',' ',' '), HB_TAG('N','E','P',' ')}, /* Nepali [macrolanguage] */ + {HB_TAG('n','g',' ',' '), HB_TAG('N','D','G',' ')}, /* Ndonga */ + {HB_TAG('n','l',' ',' '), HB_TAG('N','L','D',' ')}, /* Dutch */ + {HB_TAG('n','n',' ',' '), HB_TAG('N','Y','N',' ')}, /* Norwegian Nynorsk (Nynorsk, Norwegian) */ + {HB_TAG('n','o',' ',' '), HB_TAG('N','O','R',' ')}, /* Norwegian [macrolanguage] */ + {HB_TAG('n','r',' ',' '), HB_TAG('N','D','B',' ')}, /* South Ndebele -> Ndebele */ + {HB_TAG('n','v',' ',' '), HB_TAG('N','A','V',' ')}, /* Navajo */ + {HB_TAG('n','v',' ',' '), HB_TAG('A','T','H',' ')}, /* Navajo -> Athapaskan */ + {HB_TAG('n','y',' ',' '), HB_TAG('C','H','I',' ')}, /* Chichewa (Chewa, Nyanja) */ + {HB_TAG('o','c',' ',' '), HB_TAG('O','C','I',' ')}, /* Occitan (post 1500) */ + {HB_TAG('o','j',' ',' '), HB_TAG('O','J','B',' ')}, /* Ojibwa [macrolanguage] -> Ojibway */ + {HB_TAG('o','m',' ',' '), HB_TAG('O','R','O',' ')}, /* Oromo [macrolanguage] */ + {HB_TAG('o','r',' ',' '), HB_TAG('O','R','I',' ')}, /* Odia (formerly Oriya) [macrolanguage] */ + {HB_TAG('o','s',' ',' '), HB_TAG('O','S','S',' ')}, /* Ossetian */ + {HB_TAG('p','a',' ',' '), HB_TAG('P','A','N',' ')}, /* Punjabi */ + {HB_TAG('p','i',' ',' '), HB_TAG('P','A','L',' ')}, /* Pali */ + {HB_TAG('p','l',' ',' '), HB_TAG('P','L','K',' ')}, /* Polish */ + {HB_TAG('p','s',' ',' '), HB_TAG('P','A','S',' ')}, /* Pashto [macrolanguage] */ + {HB_TAG('p','t',' ',' '), HB_TAG('P','T','G',' ')}, /* Portuguese */ + {HB_TAG('q','u',' ',' '), HB_TAG('Q','U','Z',' ')}, /* Quechua [macrolanguage] */ + {HB_TAG('r','m',' ',' '), HB_TAG('R','M','S',' ')}, /* Romansh */ + {HB_TAG('r','n',' ',' '), HB_TAG('R','U','N',' ')}, /* Rundi */ + {HB_TAG('r','o',' ',' '), HB_TAG('R','O','M',' ')}, /* Romanian */ + {HB_TAG('r','u',' ',' '), HB_TAG('R','U','S',' ')}, /* Russian */ + {HB_TAG('r','w',' ',' '), HB_TAG('R','U','A',' ')}, /* Kinyarwanda */ + {HB_TAG('s','a',' ',' '), HB_TAG('S','A','N',' ')}, /* Sanskrit */ + {HB_TAG('s','c',' ',' '), HB_TAG('S','R','D',' ')}, /* Sardinian [macrolanguage] */ + {HB_TAG('s','d',' ',' '), HB_TAG('S','N','D',' ')}, /* Sindhi */ + {HB_TAG('s','e',' ',' '), HB_TAG('N','S','M',' ')}, /* Northern Sami */ + {HB_TAG('s','g',' ',' '), HB_TAG('S','G','O',' ')}, /* Sango */ + {HB_TAG('s','h',' ',' '), HB_TAG('B','O','S',' ')}, /* Serbo-Croatian [macrolanguage] -> Bosnian */ + {HB_TAG('s','h',' ',' '), HB_TAG('H','R','V',' ')}, /* Serbo-Croatian [macrolanguage] -> Croatian */ + {HB_TAG('s','h',' ',' '), HB_TAG('S','R','B',' ')}, /* Serbo-Croatian [macrolanguage] -> Serbian */ + {HB_TAG('s','i',' ',' '), HB_TAG('S','N','H',' ')}, /* Sinhala (Sinhalese) */ + {HB_TAG('s','k',' ',' '), HB_TAG('S','K','Y',' ')}, /* Slovak */ + {HB_TAG('s','l',' ',' '), HB_TAG('S','L','V',' ')}, /* Slovenian */ + {HB_TAG('s','m',' ',' '), HB_TAG('S','M','O',' ')}, /* Samoan */ + {HB_TAG('s','n',' ',' '), HB_TAG('S','N','A','0')}, /* Shona */ + {HB_TAG('s','o',' ',' '), HB_TAG('S','M','L',' ')}, /* Somali */ + {HB_TAG('s','q',' ',' '), HB_TAG('S','Q','I',' ')}, /* Albanian [macrolanguage] */ + {HB_TAG('s','r',' ',' '), HB_TAG('S','R','B',' ')}, /* Serbian */ + {HB_TAG('s','s',' ',' '), HB_TAG('S','W','Z',' ')}, /* Swati */ + {HB_TAG('s','t',' ',' '), HB_TAG('S','O','T',' ')}, /* Southern Sotho */ + {HB_TAG('s','u',' ',' '), HB_TAG('S','U','N',' ')}, /* Sundanese */ + {HB_TAG('s','v',' ',' '), HB_TAG('S','V','E',' ')}, /* Swedish */ + {HB_TAG('s','w',' ',' '), HB_TAG('S','W','K',' ')}, /* Swahili [macrolanguage] */ + {HB_TAG('t','a',' ',' '), HB_TAG('T','A','M',' ')}, /* Tamil */ + {HB_TAG('t','e',' ',' '), HB_TAG('T','E','L',' ')}, /* Telugu */ + {HB_TAG('t','g',' ',' '), HB_TAG('T','A','J',' ')}, /* Tajik -> Tajiki */ + {HB_TAG('t','h',' ',' '), HB_TAG('T','H','A',' ')}, /* Thai */ + {HB_TAG('t','i',' ',' '), HB_TAG('T','G','Y',' ')}, /* Tigrinya */ + {HB_TAG('t','k',' ',' '), HB_TAG('T','K','M',' ')}, /* Turkmen */ + {HB_TAG('t','l',' ',' '), HB_TAG('T','G','L',' ')}, /* Tagalog */ + {HB_TAG('t','n',' ',' '), HB_TAG('T','N','A',' ')}, /* Tswana */ + {HB_TAG('t','o',' ',' '), HB_TAG('T','G','N',' ')}, /* Tonga (Tonga Islands) -> Tongan */ + {HB_TAG('t','r',' ',' '), HB_TAG('T','R','K',' ')}, /* Turkish */ + {HB_TAG('t','s',' ',' '), HB_TAG('T','S','G',' ')}, /* Tsonga */ + {HB_TAG('t','t',' ',' '), HB_TAG('T','A','T',' ')}, /* Tatar */ + {HB_TAG('t','w',' ',' '), HB_TAG('T','W','I',' ')}, /* Twi */ + {HB_TAG('t','w',' ',' '), HB_TAG('A','K','A',' ')}, /* Twi -> Akan */ + {HB_TAG('t','y',' ',' '), HB_TAG('T','H','T',' ')}, /* Tahitian */ + {HB_TAG('u','g',' ',' '), HB_TAG('U','Y','G',' ')}, /* Uyghur */ + {HB_TAG('u','k',' ',' '), HB_TAG('U','K','R',' ')}, /* Ukrainian */ + {HB_TAG('u','r',' ',' '), HB_TAG('U','R','D',' ')}, /* Urdu */ + {HB_TAG('u','z',' ',' '), HB_TAG('U','Z','B',' ')}, /* Uzbek [macrolanguage] */ + {HB_TAG('v','e',' ',' '), HB_TAG('V','E','N',' ')}, /* Venda */ + {HB_TAG('v','i',' ',' '), HB_TAG('V','I','T',' ')}, /* Vietnamese */ + {HB_TAG('v','o',' ',' '), HB_TAG('V','O','L',' ')}, /* Volapük */ + {HB_TAG('w','a',' ',' '), HB_TAG('W','L','N',' ')}, /* Walloon */ + {HB_TAG('w','o',' ',' '), HB_TAG('W','L','F',' ')}, /* Wolof */ + {HB_TAG('x','h',' ',' '), HB_TAG('X','H','S',' ')}, /* Xhosa */ + {HB_TAG('y','i',' ',' '), HB_TAG('J','I','I',' ')}, /* Yiddish [macrolanguage] */ + {HB_TAG('y','o',' ',' '), HB_TAG('Y','B','A',' ')}, /* Yoruba */ + {HB_TAG('z','a',' ',' '), HB_TAG('Z','H','A',' ')}, /* Zhuang [macrolanguage] */ + {HB_TAG('z','h',' ',' '), HB_TAG('Z','H','S',' ')}, /* Chinese, Simplified [macrolanguage] */ + {HB_TAG('z','u',' ',' '), HB_TAG('Z','U','L',' ')}, /* Zulu */ +}; + +#ifndef HB_NO_LANGUAGE_LONG +static const LangTag ot_languages3[] = { + {HB_TAG('a','a','e',' '), HB_TAG('S','Q','I',' ')}, /* Arbëreshë Albanian -> Albanian */ + {HB_TAG('a','a','o',' '), HB_TAG('A','R','A',' ')}, /* Algerian Saharan Arabic -> Arabic */ + {HB_TAG('a','a','t',' '), HB_TAG('S','Q','I',' ')}, /* Arvanitika Albanian -> Albanian */ + {HB_TAG('a','b','a',' '), HB_TAG_NONE }, /* Abé != Abaza */ + {HB_TAG('a','b','h',' '), HB_TAG('A','R','A',' ')}, /* Tajiki Arabic -> Arabic */ + {HB_TAG('a','b','q',' '), HB_TAG('A','B','A',' ')}, /* Abaza */ + {HB_TAG('a','b','s',' '), HB_TAG('C','P','P',' ')}, /* Ambonese Malay -> Creoles */ + {HB_TAG('a','b','v',' '), HB_TAG('A','R','A',' ')}, /* Baharna Arabic -> Arabic */ + {HB_TAG('a','c','f',' '), HB_TAG('F','A','N',' ')}, /* Saint Lucian Creole French -> French Antillean */ + {HB_TAG('a','c','f',' '), HB_TAG('C','P','P',' ')}, /* Saint Lucian Creole French -> Creoles */ +/*{HB_TAG('a','c','h',' '), HB_TAG('A','C','H',' ')},*/ /* Acoli -> Acholi */ + {HB_TAG('a','c','m',' '), HB_TAG('A','R','A',' ')}, /* Mesopotamian Arabic -> Arabic */ + {HB_TAG('a','c','q',' '), HB_TAG('A','R','A',' ')}, /* Ta'izzi-Adeni Arabic -> Arabic */ + {HB_TAG('a','c','r',' '), HB_TAG('A','C','R',' ')}, /* Achi */ + {HB_TAG('a','c','r',' '), HB_TAG('M','Y','N',' ')}, /* Achi -> Mayan */ + {HB_TAG('a','c','w',' '), HB_TAG('A','R','A',' ')}, /* Hijazi Arabic -> Arabic */ + {HB_TAG('a','c','x',' '), HB_TAG('A','R','A',' ')}, /* Omani Arabic -> Arabic */ + {HB_TAG('a','c','y',' '), HB_TAG('A','R','A',' ')}, /* Cypriot Arabic -> Arabic */ + {HB_TAG('a','d','a',' '), HB_TAG('D','N','G',' ')}, /* Adangme -> Dangme */ + {HB_TAG('a','d','f',' '), HB_TAG('A','R','A',' ')}, /* Dhofari Arabic -> Arabic */ + {HB_TAG('a','d','p',' '), HB_TAG('D','Z','N',' ')}, /* Adap (retired code) -> Dzongkha */ +/*{HB_TAG('a','d','y',' '), HB_TAG('A','D','Y',' ')},*/ /* Adyghe */ + {HB_TAG('a','e','b',' '), HB_TAG('A','R','A',' ')}, /* Tunisian Arabic -> Arabic */ + {HB_TAG('a','e','c',' '), HB_TAG('A','R','A',' ')}, /* Saidi Arabic -> Arabic */ + {HB_TAG('a','f','b',' '), HB_TAG('A','R','A',' ')}, /* Gulf Arabic -> Arabic */ + {HB_TAG('a','f','k',' '), HB_TAG_NONE }, /* Nanubae != Afrikaans */ + {HB_TAG('a','f','s',' '), HB_TAG('C','P','P',' ')}, /* Afro-Seminole Creole -> Creoles */ + {HB_TAG('a','g','u',' '), HB_TAG('M','Y','N',' ')}, /* Aguacateco -> Mayan */ + {HB_TAG('a','g','w',' '), HB_TAG_NONE }, /* Kahua != Agaw */ + {HB_TAG('a','h','g',' '), HB_TAG('A','G','W',' ')}, /* Qimant -> Agaw */ + {HB_TAG('a','h','t',' '), HB_TAG('A','T','H',' ')}, /* Ahtena -> Athapaskan */ + {HB_TAG('a','i','g',' '), HB_TAG('C','P','P',' ')}, /* Antigua and Barbuda Creole English -> Creoles */ + {HB_TAG('a','i','i',' '), HB_TAG('S','W','A',' ')}, /* Assyrian Neo-Aramaic -> Swadaya Aramaic */ + {HB_TAG('a','i','i',' '), HB_TAG('S','Y','R',' ')}, /* Assyrian Neo-Aramaic -> Syriac */ +/*{HB_TAG('a','i','o',' '), HB_TAG('A','I','O',' ')},*/ /* Aiton */ + {HB_TAG('a','i','w',' '), HB_TAG('A','R','I',' ')}, /* Aari */ + {HB_TAG('a','j','p',' '), HB_TAG('A','R','A',' ')}, /* South Levantine Arabic -> Arabic */ + {HB_TAG('a','j','t',' '), HB_TAG('A','R','A',' ')}, /* Judeo-Tunisian Arabic (retired code) -> Arabic */ + {HB_TAG('a','k','b',' '), HB_TAG('A','K','B',' ')}, /* Batak Angkola */ + {HB_TAG('a','k','b',' '), HB_TAG('B','T','K',' ')}, /* Batak Angkola -> Batak */ + {HB_TAG('a','l','n',' '), HB_TAG('S','Q','I',' ')}, /* Gheg Albanian -> Albanian */ + {HB_TAG('a','l','s',' '), HB_TAG('S','Q','I',' ')}, /* Tosk Albanian -> Albanian */ +/*{HB_TAG('a','l','t',' '), HB_TAG('A','L','T',' ')},*/ /* Southern Altai -> Altai */ + {HB_TAG('a','m','f',' '), HB_TAG('H','B','N',' ')}, /* Hamer-Banna -> Hammer-Banna */ + {HB_TAG('a','m','w',' '), HB_TAG('S','Y','R',' ')}, /* Western Neo-Aramaic -> Syriac */ +/*{HB_TAG('a','n','g',' '), HB_TAG('A','N','G',' ')},*/ /* Old English (ca. 450-1100) -> Anglo-Saxon */ + {HB_TAG('a','o','a',' '), HB_TAG('C','P','P',' ')}, /* Angolar -> Creoles */ + {HB_TAG('a','p','a',' '), HB_TAG('A','T','H',' ')}, /* Apache [collection] -> Athapaskan */ + {HB_TAG('a','p','c',' '), HB_TAG('A','R','A',' ')}, /* North Levantine Arabic -> Arabic */ + {HB_TAG('a','p','d',' '), HB_TAG('A','R','A',' ')}, /* Sudanese Arabic -> Arabic */ + {HB_TAG('a','p','j',' '), HB_TAG('A','T','H',' ')}, /* Jicarilla Apache -> Athapaskan */ + {HB_TAG('a','p','k',' '), HB_TAG('A','T','H',' ')}, /* Kiowa Apache -> Athapaskan */ + {HB_TAG('a','p','l',' '), HB_TAG('A','T','H',' ')}, /* Lipan Apache -> Athapaskan */ + {HB_TAG('a','p','m',' '), HB_TAG('A','T','H',' ')}, /* Mescalero-Chiricahua Apache -> Athapaskan */ + {HB_TAG('a','p','w',' '), HB_TAG('A','T','H',' ')}, /* Western Apache -> Athapaskan */ + {HB_TAG('a','r','b',' '), HB_TAG('A','R','A',' ')}, /* Standard Arabic -> Arabic */ + {HB_TAG('a','r','i',' '), HB_TAG_NONE }, /* Arikara != Aari */ + {HB_TAG('a','r','k',' '), HB_TAG_NONE }, /* Arikapú != Rakhine */ + {HB_TAG('a','r','n',' '), HB_TAG('M','A','P',' ')}, /* Mapudungun */ + {HB_TAG('a','r','q',' '), HB_TAG('A','R','A',' ')}, /* Algerian Arabic -> Arabic */ + {HB_TAG('a','r','s',' '), HB_TAG('A','R','A',' ')}, /* Najdi Arabic -> Arabic */ + {HB_TAG('a','r','y',' '), HB_TAG('M','O','R',' ')}, /* Moroccan Arabic -> Moroccan */ + {HB_TAG('a','r','y',' '), HB_TAG('A','R','A',' ')}, /* Moroccan Arabic -> Arabic */ + {HB_TAG('a','r','z',' '), HB_TAG('A','R','A',' ')}, /* Egyptian Arabic -> Arabic */ +/*{HB_TAG('a','s','t',' '), HB_TAG('A','S','T',' ')},*/ /* Asturian */ +/*{HB_TAG('a','t','h',' '), HB_TAG('A','T','H',' ')},*/ /* Athapascan [collection] -> Athapaskan */ + {HB_TAG('a','t','j',' '), HB_TAG('R','C','R',' ')}, /* Atikamekw -> R-Cree */ + {HB_TAG('a','t','v',' '), HB_TAG('A','L','T',' ')}, /* Northern Altai -> Altai */ + {HB_TAG('a','u','j',' '), HB_TAG('B','B','R',' ')}, /* Awjilah -> Berber */ + {HB_TAG('a','u','z',' '), HB_TAG('A','R','A',' ')}, /* Uzbeki Arabic -> Arabic */ + {HB_TAG('a','v','l',' '), HB_TAG('A','R','A',' ')}, /* Eastern Egyptian Bedawi Arabic -> Arabic */ +/*{HB_TAG('a','v','n',' '), HB_TAG('A','V','N',' ')},*/ /* Avatime */ +/*{HB_TAG('a','w','a',' '), HB_TAG('A','W','A',' ')},*/ /* Awadhi */ + {HB_TAG('a','y','c',' '), HB_TAG('A','Y','M',' ')}, /* Southern Aymara -> Aymara */ + {HB_TAG('a','y','h',' '), HB_TAG('A','R','A',' ')}, /* Hadrami Arabic -> Arabic */ + {HB_TAG('a','y','l',' '), HB_TAG('A','R','A',' ')}, /* Libyan Arabic -> Arabic */ + {HB_TAG('a','y','n',' '), HB_TAG('A','R','A',' ')}, /* Sanaani Arabic -> Arabic */ + {HB_TAG('a','y','p',' '), HB_TAG('A','R','A',' ')}, /* North Mesopotamian Arabic -> Arabic */ + {HB_TAG('a','y','r',' '), HB_TAG('A','Y','M',' ')}, /* Central Aymara -> Aymara */ + {HB_TAG('a','z','b',' '), HB_TAG('A','Z','B',' ')}, /* South Azerbaijani -> Torki */ + {HB_TAG('a','z','b',' '), HB_TAG('A','Z','E',' ')}, /* South Azerbaijani -> Azerbaijani */ + {HB_TAG('a','z','d',' '), HB_TAG('N','A','H',' ')}, /* Eastern Durango Nahuatl -> Nahuatl */ + {HB_TAG('a','z','j',' '), HB_TAG('A','Z','E',' ')}, /* North Azerbaijani -> Azerbaijani */ + {HB_TAG('a','z','n',' '), HB_TAG('N','A','H',' ')}, /* Western Durango Nahuatl -> Nahuatl */ + {HB_TAG('a','z','z',' '), HB_TAG('N','A','H',' ')}, /* Highland Puebla Nahuatl -> Nahuatl */ + {HB_TAG('b','a','d',' '), HB_TAG('B','A','D','0')}, /* Banda [collection] */ + {HB_TAG('b','a','g',' '), HB_TAG_NONE }, /* Tuki != Baghelkhandi */ + {HB_TAG('b','a','h',' '), HB_TAG('C','P','P',' ')}, /* Bahamas Creole English -> Creoles */ + {HB_TAG('b','a','i',' '), HB_TAG('B','M','L',' ')}, /* Bamileke [collection] */ + {HB_TAG('b','a','l',' '), HB_TAG('B','L','I',' ')}, /* Baluchi [macrolanguage] */ +/*{HB_TAG('b','a','n',' '), HB_TAG('B','A','N',' ')},*/ /* Balinese */ +/*{HB_TAG('b','a','r',' '), HB_TAG('B','A','R',' ')},*/ /* Bavarian */ + {HB_TAG('b','a','u',' '), HB_TAG_NONE }, /* Bada (Nigeria) != Baulé */ + {HB_TAG('b','b','c',' '), HB_TAG('B','B','C',' ')}, /* Batak Toba */ + {HB_TAG('b','b','c',' '), HB_TAG('B','T','K',' ')}, /* Batak Toba -> Batak */ + {HB_TAG('b','b','j',' '), HB_TAG('B','M','L',' ')}, /* Ghomálá' -> Bamileke */ + {HB_TAG('b','b','p',' '), HB_TAG('B','A','D','0')}, /* West Central Banda -> Banda */ + {HB_TAG('b','b','r',' '), HB_TAG_NONE }, /* Girawa != Berber */ + {HB_TAG('b','b','z',' '), HB_TAG('A','R','A',' ')}, /* Babalia Creole Arabic (retired code) -> Arabic */ + {HB_TAG('b','c','c',' '), HB_TAG('B','L','I',' ')}, /* Southern Balochi -> Baluchi */ + {HB_TAG('b','c','h',' '), HB_TAG_NONE }, /* Bariai != Bench */ + {HB_TAG('b','c','i',' '), HB_TAG('B','A','U',' ')}, /* Baoulé -> Baulé */ + {HB_TAG('b','c','l',' '), HB_TAG('B','I','K',' ')}, /* Central Bikol -> Bikol */ + {HB_TAG('b','c','q',' '), HB_TAG('B','C','H',' ')}, /* Bench */ + {HB_TAG('b','c','r',' '), HB_TAG('A','T','H',' ')}, /* Babine -> Athapaskan */ +/*{HB_TAG('b','d','y',' '), HB_TAG('B','D','Y',' ')},*/ /* Bandjalang */ + {HB_TAG('b','e','a',' '), HB_TAG('A','T','H',' ')}, /* Beaver -> Athapaskan */ + {HB_TAG('b','e','b',' '), HB_TAG('B','T','I',' ')}, /* Bebele -> Beti */ +/*{HB_TAG('b','e','m',' '), HB_TAG('B','E','M',' ')},*/ /* Bemba (Zambia) */ + {HB_TAG('b','e','r',' '), HB_TAG('B','B','R',' ')}, /* Berber [collection] */ + {HB_TAG('b','e','w',' '), HB_TAG('C','P','P',' ')}, /* Betawi -> Creoles */ + {HB_TAG('b','f','l',' '), HB_TAG('B','A','D','0')}, /* Banda-Ndélé -> Banda */ + {HB_TAG('b','f','q',' '), HB_TAG('B','A','D',' ')}, /* Badaga */ + {HB_TAG('b','f','t',' '), HB_TAG('B','L','T',' ')}, /* Balti */ + {HB_TAG('b','f','u',' '), HB_TAG('L','A','H',' ')}, /* Gahri -> Lahuli */ + {HB_TAG('b','f','y',' '), HB_TAG('B','A','G',' ')}, /* Bagheli -> Baghelkhandi */ +/*{HB_TAG('b','g','c',' '), HB_TAG('B','G','C',' ')},*/ /* Haryanvi */ + {HB_TAG('b','g','n',' '), HB_TAG('B','L','I',' ')}, /* Western Balochi -> Baluchi */ + {HB_TAG('b','g','p',' '), HB_TAG('B','L','I',' ')}, /* Eastern Balochi -> Baluchi */ + {HB_TAG('b','g','q',' '), HB_TAG('B','G','Q',' ')}, /* Bagri */ + {HB_TAG('b','g','q',' '), HB_TAG('R','A','J',' ')}, /* Bagri -> Rajasthani */ + {HB_TAG('b','g','r',' '), HB_TAG('Q','I','N',' ')}, /* Bawm Chin -> Chin */ + {HB_TAG('b','h','b',' '), HB_TAG('B','H','I',' ')}, /* Bhili */ +/*{HB_TAG('b','h','i',' '), HB_TAG('B','H','I',' ')},*/ /* Bhilali -> Bhili */ + {HB_TAG('b','h','k',' '), HB_TAG('B','I','K',' ')}, /* Albay Bicolano (retired code) -> Bikol */ +/*{HB_TAG('b','h','o',' '), HB_TAG('B','H','O',' ')},*/ /* Bhojpuri */ + {HB_TAG('b','h','r',' '), HB_TAG('M','L','G',' ')}, /* Bara Malagasy -> Malagasy */ +/*{HB_TAG('b','i','k',' '), HB_TAG('B','I','K',' ')},*/ /* Bikol [macrolanguage] */ + {HB_TAG('b','i','l',' '), HB_TAG_NONE }, /* Bile != Bilen */ + {HB_TAG('b','i','n',' '), HB_TAG('E','D','O',' ')}, /* Edo */ + {HB_TAG('b','i','u',' '), HB_TAG('Q','I','N',' ')}, /* Biete -> Chin */ +/*{HB_TAG('b','j','j',' '), HB_TAG('B','J','J',' ')},*/ /* Kanauji */ + {HB_TAG('b','j','n',' '), HB_TAG('M','L','Y',' ')}, /* Banjar -> Malay */ + {HB_TAG('b','j','o',' '), HB_TAG('B','A','D','0')}, /* Mid-Southern Banda -> Banda */ + {HB_TAG('b','j','q',' '), HB_TAG('M','L','G',' ')}, /* Southern Betsimisaraka Malagasy (retired code) -> Malagasy */ + {HB_TAG('b','j','s',' '), HB_TAG('C','P','P',' ')}, /* Bajan -> Creoles */ + {HB_TAG('b','j','t',' '), HB_TAG('B','L','N',' ')}, /* Balanta-Ganja -> Balante */ + {HB_TAG('b','k','f',' '), HB_TAG_NONE }, /* Beeke != Blackfoot */ + {HB_TAG('b','k','o',' '), HB_TAG('B','M','L',' ')}, /* Kwa' -> Bamileke */ + {HB_TAG('b','l','a',' '), HB_TAG('B','K','F',' ')}, /* Siksika -> Blackfoot */ + {HB_TAG('b','l','e',' '), HB_TAG('B','L','N',' ')}, /* Balanta-Kentohe -> Balante */ + {HB_TAG('b','l','g',' '), HB_TAG('I','B','A',' ')}, /* Balau (retired code) -> Iban */ + {HB_TAG('b','l','i',' '), HB_TAG_NONE }, /* Bolia != Baluchi */ + {HB_TAG('b','l','k',' '), HB_TAG('B','L','K',' ')}, /* Pa’o Karen */ + {HB_TAG('b','l','k',' '), HB_TAG('K','R','N',' ')}, /* Pa'o Karen -> Karen */ + {HB_TAG('b','l','n',' '), HB_TAG('B','I','K',' ')}, /* Southern Catanduanes Bikol -> Bikol */ + {HB_TAG('b','l','t',' '), HB_TAG_NONE }, /* Tai Dam != Balti */ + {HB_TAG('b','m','b',' '), HB_TAG_NONE }, /* Bembe != Bambara (Bamanankan) */ + {HB_TAG('b','m','l',' '), HB_TAG_NONE }, /* Bomboli != Bamileke */ + {HB_TAG('b','m','m',' '), HB_TAG('M','L','G',' ')}, /* Northern Betsimisaraka Malagasy -> Malagasy */ + {HB_TAG('b','p','d',' '), HB_TAG('B','A','D','0')}, /* Banda-Banda -> Banda */ + {HB_TAG('b','p','l',' '), HB_TAG('C','P','P',' ')}, /* Broome Pearling Lugger Pidgin -> Creoles */ + {HB_TAG('b','p','q',' '), HB_TAG('C','P','P',' ')}, /* Banda Malay -> Creoles */ +/*{HB_TAG('b','p','y',' '), HB_TAG('B','P','Y',' ')},*/ /* Bishnupriya -> Bishnupriya Manipuri */ + {HB_TAG('b','q','i',' '), HB_TAG('L','R','C',' ')}, /* Bakhtiari -> Luri */ + {HB_TAG('b','q','k',' '), HB_TAG('B','A','D','0')}, /* Banda-Mbrès -> Banda */ + {HB_TAG('b','r','a',' '), HB_TAG('B','R','I',' ')}, /* Braj -> Braj Bhasha */ + {HB_TAG('b','r','c',' '), HB_TAG('C','P','P',' ')}, /* Berbice Creole Dutch -> Creoles */ +/*{HB_TAG('b','r','h',' '), HB_TAG('B','R','H',' ')},*/ /* Brahui */ + {HB_TAG('b','r','i',' '), HB_TAG_NONE }, /* Mokpwe != Braj Bhasha */ + {HB_TAG('b','r','m',' '), HB_TAG_NONE }, /* Barambu != Burmese */ +/*{HB_TAG('b','r','x',' '), HB_TAG('B','R','X',' ')},*/ /* Bodo (India) */ + {HB_TAG('b','s','h',' '), HB_TAG_NONE }, /* Kati != Bashkir */ +/*{HB_TAG('b','s','k',' '), HB_TAG('B','S','K',' ')},*/ /* Burushaski */ + {HB_TAG('b','t','b',' '), HB_TAG('B','T','I',' ')}, /* Beti (Cameroon) (retired code) */ + {HB_TAG('b','t','d',' '), HB_TAG('B','T','D',' ')}, /* Batak Dairi (Pakpak) */ + {HB_TAG('b','t','d',' '), HB_TAG('B','T','K',' ')}, /* Batak Dairi -> Batak */ + {HB_TAG('b','t','i',' '), HB_TAG_NONE }, /* Burate != Beti */ + {HB_TAG('b','t','j',' '), HB_TAG('M','L','Y',' ')}, /* Bacanese Malay -> Malay */ +/*{HB_TAG('b','t','k',' '), HB_TAG('B','T','K',' ')},*/ /* Batak [collection] */ + {HB_TAG('b','t','m',' '), HB_TAG('B','T','M',' ')}, /* Batak Mandailing */ + {HB_TAG('b','t','m',' '), HB_TAG('B','T','K',' ')}, /* Batak Mandailing -> Batak */ + {HB_TAG('b','t','o',' '), HB_TAG('B','I','K',' ')}, /* Rinconada Bikol -> Bikol */ + {HB_TAG('b','t','s',' '), HB_TAG('B','T','S',' ')}, /* Batak Simalungun */ + {HB_TAG('b','t','s',' '), HB_TAG('B','T','K',' ')}, /* Batak Simalungun -> Batak */ + {HB_TAG('b','t','x',' '), HB_TAG('B','T','X',' ')}, /* Batak Karo */ + {HB_TAG('b','t','x',' '), HB_TAG('B','T','K',' ')}, /* Batak Karo -> Batak */ + {HB_TAG('b','t','z',' '), HB_TAG('B','T','Z',' ')}, /* Batak Alas-Kluet */ + {HB_TAG('b','t','z',' '), HB_TAG('B','T','K',' ')}, /* Batak Alas-Kluet -> Batak */ +/*{HB_TAG('b','u','g',' '), HB_TAG('B','U','G',' ')},*/ /* Buginese -> Bugis */ + {HB_TAG('b','u','m',' '), HB_TAG('B','T','I',' ')}, /* Bulu (Cameroon) -> Beti */ + {HB_TAG('b','v','e',' '), HB_TAG('M','L','Y',' ')}, /* Berau Malay -> Malay */ + {HB_TAG('b','v','u',' '), HB_TAG('M','L','Y',' ')}, /* Bukit Malay -> Malay */ + {HB_TAG('b','w','e',' '), HB_TAG('K','R','N',' ')}, /* Bwe Karen -> Karen */ + {HB_TAG('b','x','k',' '), HB_TAG('L','U','H',' ')}, /* Bukusu -> Luyia */ + {HB_TAG('b','x','o',' '), HB_TAG('C','P','P',' ')}, /* Barikanchi -> Creoles */ + {HB_TAG('b','x','p',' '), HB_TAG('B','T','I',' ')}, /* Bebil -> Beti */ + {HB_TAG('b','x','r',' '), HB_TAG('R','B','U',' ')}, /* Russia Buriat -> Russian Buriat */ + {HB_TAG('b','y','n',' '), HB_TAG('B','I','L',' ')}, /* Bilin -> Bilen */ + {HB_TAG('b','y','v',' '), HB_TAG('B','Y','V',' ')}, /* Medumba */ + {HB_TAG('b','y','v',' '), HB_TAG('B','M','L',' ')}, /* Medumba -> Bamileke */ + {HB_TAG('b','z','c',' '), HB_TAG('M','L','G',' ')}, /* Southern Betsimisaraka Malagasy -> Malagasy */ + {HB_TAG('b','z','j',' '), HB_TAG('C','P','P',' ')}, /* Belize Kriol English -> Creoles */ + {HB_TAG('b','z','k',' '), HB_TAG('C','P','P',' ')}, /* Nicaragua Creole English -> Creoles */ + {HB_TAG('c','a','a',' '), HB_TAG('M','Y','N',' ')}, /* Chortí -> Mayan */ + {HB_TAG('c','a','c',' '), HB_TAG('M','Y','N',' ')}, /* Chuj -> Mayan */ + {HB_TAG('c','a','f',' '), HB_TAG('C','R','R',' ')}, /* Southern Carrier -> Carrier */ + {HB_TAG('c','a','f',' '), HB_TAG('A','T','H',' ')}, /* Southern Carrier -> Athapaskan */ + {HB_TAG('c','a','k',' '), HB_TAG('C','A','K',' ')}, /* Kaqchikel */ + {HB_TAG('c','a','k',' '), HB_TAG('M','Y','N',' ')}, /* Kaqchikel -> Mayan */ + {HB_TAG('c','b','k',' '), HB_TAG('C','B','K',' ')}, /* Chavacano -> Zamboanga Chavacano */ + {HB_TAG('c','b','k',' '), HB_TAG('C','P','P',' ')}, /* Chavacano -> Creoles */ + {HB_TAG('c','b','l',' '), HB_TAG('Q','I','N',' ')}, /* Bualkhaw Chin -> Chin */ + {HB_TAG('c','c','l',' '), HB_TAG('C','P','P',' ')}, /* Cutchi-Swahili -> Creoles */ + {HB_TAG('c','c','m',' '), HB_TAG('C','P','P',' ')}, /* Malaccan Creole Malay -> Creoles */ + {HB_TAG('c','c','o',' '), HB_TAG('C','C','H','N')}, /* Comaltepec Chinantec -> Chinantec */ + {HB_TAG('c','c','q',' '), HB_TAG('A','R','K',' ')}, /* Chaungtha (retired code) -> Rakhine */ + {HB_TAG('c','d','o',' '), HB_TAG('Z','H','S',' ')}, /* Min Dong Chinese -> Chinese, Simplified */ +/*{HB_TAG('c','e','b',' '), HB_TAG('C','E','B',' ')},*/ /* Cebuano */ + {HB_TAG('c','e','k',' '), HB_TAG('Q','I','N',' ')}, /* Eastern Khumi Chin -> Chin */ + {HB_TAG('c','e','y',' '), HB_TAG('Q','I','N',' ')}, /* Ekai Chin -> Chin */ + {HB_TAG('c','f','m',' '), HB_TAG('H','A','L',' ')}, /* Halam (Falam Chin) */ + {HB_TAG('c','f','m',' '), HB_TAG('Q','I','N',' ')}, /* Falam Chin -> Chin */ +/*{HB_TAG('c','g','g',' '), HB_TAG('C','G','G',' ')},*/ /* Chiga */ + {HB_TAG('c','h','f',' '), HB_TAG('M','Y','N',' ')}, /* Tabasco Chontal -> Mayan */ + {HB_TAG('c','h','g',' '), HB_TAG_NONE }, /* Chagatai != Chaha Gurage */ + {HB_TAG('c','h','h',' '), HB_TAG_NONE }, /* Chinook != Chattisgarhi */ + {HB_TAG('c','h','j',' '), HB_TAG('C','C','H','N')}, /* Ojitlán Chinantec -> Chinantec */ + {HB_TAG('c','h','k',' '), HB_TAG('C','H','K','0')}, /* Chuukese */ + {HB_TAG('c','h','m',' '), HB_TAG('H','M','A',' ')}, /* Mari (Russia) [macrolanguage] -> High Mari */ + {HB_TAG('c','h','m',' '), HB_TAG('L','M','A',' ')}, /* Mari (Russia) [macrolanguage] -> Low Mari */ + {HB_TAG('c','h','n',' '), HB_TAG('C','P','P',' ')}, /* Chinook jargon -> Creoles */ +/*{HB_TAG('c','h','o',' '), HB_TAG('C','H','O',' ')},*/ /* Choctaw */ + {HB_TAG('c','h','p',' '), HB_TAG('C','H','P',' ')}, /* Chipewyan */ + {HB_TAG('c','h','p',' '), HB_TAG('S','A','Y',' ')}, /* Chipewyan -> Sayisi */ + {HB_TAG('c','h','p',' '), HB_TAG('A','T','H',' ')}, /* Chipewyan -> Athapaskan */ + {HB_TAG('c','h','q',' '), HB_TAG('C','C','H','N')}, /* Quiotepec Chinantec -> Chinantec */ +/*{HB_TAG('c','h','r',' '), HB_TAG('C','H','R',' ')},*/ /* Cherokee */ +/*{HB_TAG('c','h','y',' '), HB_TAG('C','H','Y',' ')},*/ /* Cheyenne */ + {HB_TAG('c','h','z',' '), HB_TAG('C','C','H','N')}, /* Ozumacín Chinantec -> Chinantec */ + {HB_TAG('c','i','w',' '), HB_TAG('O','J','B',' ')}, /* Chippewa -> Ojibway */ +/*{HB_TAG('c','j','a',' '), HB_TAG('C','J','A',' ')},*/ /* Western Cham */ +/*{HB_TAG('c','j','m',' '), HB_TAG('C','J','M',' ')},*/ /* Eastern Cham */ + {HB_TAG('c','j','y',' '), HB_TAG('Z','H','S',' ')}, /* Jinyu Chinese -> Chinese, Simplified */ + {HB_TAG('c','k','a',' '), HB_TAG('Q','I','N',' ')}, /* Khumi Awa Chin (retired code) -> Chin */ + {HB_TAG('c','k','b',' '), HB_TAG('K','U','R',' ')}, /* Central Kurdish -> Kurdish */ + {HB_TAG('c','k','n',' '), HB_TAG('Q','I','N',' ')}, /* Kaang Chin -> Chin */ + {HB_TAG('c','k','s',' '), HB_TAG('C','P','P',' ')}, /* Tayo -> Creoles */ + {HB_TAG('c','k','t',' '), HB_TAG('C','H','K',' ')}, /* Chukot -> Chukchi */ + {HB_TAG('c','k','z',' '), HB_TAG('M','Y','N',' ')}, /* Cakchiquel-Quiché Mixed Language -> Mayan */ + {HB_TAG('c','l','c',' '), HB_TAG('A','T','H',' ')}, /* Chilcotin -> Athapaskan */ + {HB_TAG('c','l','d',' '), HB_TAG('S','Y','R',' ')}, /* Chaldean Neo-Aramaic -> Syriac */ + {HB_TAG('c','l','e',' '), HB_TAG('C','C','H','N')}, /* Lealao Chinantec -> Chinantec */ + {HB_TAG('c','l','j',' '), HB_TAG('Q','I','N',' ')}, /* Laitu Chin -> Chin */ + {HB_TAG('c','l','t',' '), HB_TAG('Q','I','N',' ')}, /* Lautu Chin -> Chin */ + {HB_TAG('c','m','n',' '), HB_TAG('Z','H','S',' ')}, /* Mandarin Chinese -> Chinese, Simplified */ + {HB_TAG('c','m','r',' '), HB_TAG('Q','I','N',' ')}, /* Mro-Khimi Chin -> Chin */ + {HB_TAG('c','n','b',' '), HB_TAG('Q','I','N',' ')}, /* Chinbon Chin -> Chin */ + {HB_TAG('c','n','h',' '), HB_TAG('Q','I','N',' ')}, /* Hakha Chin -> Chin */ + {HB_TAG('c','n','k',' '), HB_TAG('Q','I','N',' ')}, /* Khumi Chin -> Chin */ + {HB_TAG('c','n','l',' '), HB_TAG('C','C','H','N')}, /* Lalana Chinantec -> Chinantec */ + {HB_TAG('c','n','p',' '), HB_TAG('Z','H','S',' ')}, /* Northern Ping Chinese -> Chinese, Simplified */ + {HB_TAG('c','n','r',' '), HB_TAG('S','R','B',' ')}, /* Montenegrin -> Serbian */ + {HB_TAG('c','n','t',' '), HB_TAG('C','C','H','N')}, /* Tepetotutla Chinantec -> Chinantec */ + {HB_TAG('c','n','u',' '), HB_TAG('B','B','R',' ')}, /* Chenoua -> Berber */ + {HB_TAG('c','n','w',' '), HB_TAG('Q','I','N',' ')}, /* Ngawn Chin -> Chin */ + {HB_TAG('c','o','a',' '), HB_TAG('M','L','Y',' ')}, /* Cocos Islands Malay -> Malay */ + {HB_TAG('c','o','b',' '), HB_TAG('M','Y','N',' ')}, /* Chicomuceltec -> Mayan */ +/*{HB_TAG('c','o','p',' '), HB_TAG('C','O','P',' ')},*/ /* Coptic */ + {HB_TAG('c','o','q',' '), HB_TAG('A','T','H',' ')}, /* Coquille -> Athapaskan */ + {HB_TAG('c','p','a',' '), HB_TAG('C','C','H','N')}, /* Palantla Chinantec -> Chinantec */ + {HB_TAG('c','p','e',' '), HB_TAG('C','P','P',' ')}, /* English-based creoles and pidgins [collection] -> Creoles */ + {HB_TAG('c','p','f',' '), HB_TAG('C','P','P',' ')}, /* French-based creoles and pidgins [collection] -> Creoles */ + {HB_TAG('c','p','i',' '), HB_TAG('C','P','P',' ')}, /* Chinese Pidgin English -> Creoles */ +/*{HB_TAG('c','p','p',' '), HB_TAG('C','P','P',' ')},*/ /* Portuguese-based creoles and pidgins [collection] -> Creoles */ + {HB_TAG('c','p','x',' '), HB_TAG('Z','H','S',' ')}, /* Pu-Xian Chinese -> Chinese, Simplified */ + {HB_TAG('c','q','d',' '), HB_TAG('H','M','N',' ')}, /* Chuanqiandian Cluster Miao -> Hmong */ + {HB_TAG('c','q','u',' '), HB_TAG('Q','U','H',' ')}, /* Chilean Quechua (retired code) -> Quechua (Bolivia) */ + {HB_TAG('c','q','u',' '), HB_TAG('Q','U','Z',' ')}, /* Chilean Quechua (retired code) -> Quechua */ + {HB_TAG('c','r','h',' '), HB_TAG('C','R','T',' ')}, /* Crimean Tatar */ + {HB_TAG('c','r','i',' '), HB_TAG('C','P','P',' ')}, /* Sãotomense -> Creoles */ + {HB_TAG('c','r','j',' '), HB_TAG('E','C','R',' ')}, /* Southern East Cree -> Eastern Cree */ + {HB_TAG('c','r','j',' '), HB_TAG('Y','C','R',' ')}, /* Southern East Cree -> Y-Cree */ + {HB_TAG('c','r','j',' '), HB_TAG('C','R','E',' ')}, /* Southern East Cree -> Cree */ + {HB_TAG('c','r','k',' '), HB_TAG('W','C','R',' ')}, /* Plains Cree -> West-Cree */ + {HB_TAG('c','r','k',' '), HB_TAG('Y','C','R',' ')}, /* Plains Cree -> Y-Cree */ + {HB_TAG('c','r','k',' '), HB_TAG('C','R','E',' ')}, /* Plains Cree -> Cree */ + {HB_TAG('c','r','l',' '), HB_TAG('E','C','R',' ')}, /* Northern East Cree -> Eastern Cree */ + {HB_TAG('c','r','l',' '), HB_TAG('Y','C','R',' ')}, /* Northern East Cree -> Y-Cree */ + {HB_TAG('c','r','l',' '), HB_TAG('C','R','E',' ')}, /* Northern East Cree -> Cree */ + {HB_TAG('c','r','m',' '), HB_TAG('M','C','R',' ')}, /* Moose Cree */ + {HB_TAG('c','r','m',' '), HB_TAG('L','C','R',' ')}, /* Moose Cree -> L-Cree */ + {HB_TAG('c','r','m',' '), HB_TAG('C','R','E',' ')}, /* Moose Cree -> Cree */ + {HB_TAG('c','r','p',' '), HB_TAG('C','P','P',' ')}, /* Creoles and pidgins [collection] -> Creoles */ + {HB_TAG('c','r','r',' '), HB_TAG_NONE }, /* Carolina Algonquian != Carrier */ + {HB_TAG('c','r','s',' '), HB_TAG('C','P','P',' ')}, /* Seselwa Creole French -> Creoles */ + {HB_TAG('c','r','t',' '), HB_TAG_NONE }, /* Iyojwa'ja Chorote != Crimean Tatar */ + {HB_TAG('c','r','x',' '), HB_TAG('C','R','R',' ')}, /* Carrier */ + {HB_TAG('c','r','x',' '), HB_TAG('A','T','H',' ')}, /* Carrier -> Athapaskan */ + {HB_TAG('c','s','a',' '), HB_TAG('C','C','H','N')}, /* Chiltepec Chinantec -> Chinantec */ +/*{HB_TAG('c','s','b',' '), HB_TAG('C','S','B',' ')},*/ /* Kashubian */ + {HB_TAG('c','s','h',' '), HB_TAG('Q','I','N',' ')}, /* Asho Chin -> Chin */ + {HB_TAG('c','s','j',' '), HB_TAG('Q','I','N',' ')}, /* Songlai Chin -> Chin */ + {HB_TAG('c','s','l',' '), HB_TAG_NONE }, /* Chinese Sign Language != Church Slavonic */ + {HB_TAG('c','s','o',' '), HB_TAG('C','C','H','N')}, /* Sochiapam Chinantec -> Chinantec */ + {HB_TAG('c','s','p',' '), HB_TAG('Z','H','S',' ')}, /* Southern Ping Chinese -> Chinese, Simplified */ + {HB_TAG('c','s','v',' '), HB_TAG('Q','I','N',' ')}, /* Sumtu Chin -> Chin */ + {HB_TAG('c','s','w',' '), HB_TAG('N','C','R',' ')}, /* Swampy Cree -> N-Cree */ + {HB_TAG('c','s','w',' '), HB_TAG('N','H','C',' ')}, /* Swampy Cree -> Norway House Cree */ + {HB_TAG('c','s','w',' '), HB_TAG('C','R','E',' ')}, /* Swampy Cree -> Cree */ + {HB_TAG('c','s','y',' '), HB_TAG('Q','I','N',' ')}, /* Siyin Chin -> Chin */ + {HB_TAG('c','t','c',' '), HB_TAG('A','T','H',' ')}, /* Chetco -> Athapaskan */ + {HB_TAG('c','t','d',' '), HB_TAG('Q','I','N',' ')}, /* Tedim Chin -> Chin */ + {HB_TAG('c','t','e',' '), HB_TAG('C','C','H','N')}, /* Tepinapa Chinantec -> Chinantec */ +/*{HB_TAG('c','t','g',' '), HB_TAG('C','T','G',' ')},*/ /* Chittagonian */ + {HB_TAG('c','t','h',' '), HB_TAG('Q','I','N',' ')}, /* Thaiphum Chin -> Chin */ + {HB_TAG('c','t','l',' '), HB_TAG('C','C','H','N')}, /* Tlacoatzintepec Chinantec -> Chinantec */ + {HB_TAG('c','t','s',' '), HB_TAG('B','I','K',' ')}, /* Northern Catanduanes Bikol -> Bikol */ +/*{HB_TAG('c','t','t',' '), HB_TAG('C','T','T',' ')},*/ /* Wayanad Chetti */ + {HB_TAG('c','t','u',' '), HB_TAG('M','Y','N',' ')}, /* Chol -> Mayan */ + {HB_TAG('c','u','c',' '), HB_TAG('C','C','H','N')}, /* Usila Chinantec -> Chinantec */ +/*{HB_TAG('c','u','k',' '), HB_TAG('C','U','K',' ')},*/ /* San Blas Kuna */ + {HB_TAG('c','v','n',' '), HB_TAG('C','C','H','N')}, /* Valle Nacional Chinantec -> Chinantec */ + {HB_TAG('c','w','d',' '), HB_TAG('D','C','R',' ')}, /* Woods Cree */ + {HB_TAG('c','w','d',' '), HB_TAG('T','C','R',' ')}, /* Woods Cree -> TH-Cree */ + {HB_TAG('c','w','d',' '), HB_TAG('C','R','E',' ')}, /* Woods Cree -> Cree */ + {HB_TAG('c','z','h',' '), HB_TAG('Z','H','S',' ')}, /* Huizhou Chinese -> Chinese, Simplified */ + {HB_TAG('c','z','o',' '), HB_TAG('Z','H','S',' ')}, /* Min Zhong Chinese -> Chinese, Simplified */ + {HB_TAG('c','z','t',' '), HB_TAG('Q','I','N',' ')}, /* Zotung Chin -> Chin */ +/*{HB_TAG('d','a','g',' '), HB_TAG('D','A','G',' ')},*/ /* Dagbani */ + {HB_TAG('d','a','o',' '), HB_TAG('Q','I','N',' ')}, /* Daai Chin -> Chin */ + {HB_TAG('d','a','p',' '), HB_TAG('N','I','S',' ')}, /* Nisi (India) (retired code) */ +/*{HB_TAG('d','a','r',' '), HB_TAG('D','A','R',' ')},*/ /* Dargwa */ +/*{HB_TAG('d','a','x',' '), HB_TAG('D','A','X',' ')},*/ /* Dayi */ + {HB_TAG('d','c','r',' '), HB_TAG('C','P','P',' ')}, /* Negerhollands -> Creoles */ + {HB_TAG('d','e','n',' '), HB_TAG('S','L','A',' ')}, /* Slave (Athapascan) [macrolanguage] -> Slavey */ + {HB_TAG('d','e','n',' '), HB_TAG('A','T','H',' ')}, /* Slave (Athapascan) [macrolanguage] -> Athapaskan */ + {HB_TAG('d','e','p',' '), HB_TAG('C','P','P',' ')}, /* Pidgin Delaware -> Creoles */ + {HB_TAG('d','g','o',' '), HB_TAG('D','G','O',' ')}, /* Dogri (individual language) */ + {HB_TAG('d','g','o',' '), HB_TAG('D','G','R',' ')}, /* Dogri (macrolanguage) */ + {HB_TAG('d','g','r',' '), HB_TAG('A','T','H',' ')}, /* Dogrib -> Athapaskan */ + {HB_TAG('d','h','d',' '), HB_TAG('M','A','W',' ')}, /* Dhundari -> Marwari */ +/*{HB_TAG('d','h','g',' '), HB_TAG('D','H','G',' ')},*/ /* Dhangu */ + {HB_TAG('d','h','v',' '), HB_TAG_NONE }, /* Dehu != Divehi (Dhivehi, Maldivian) (deprecated) */ + {HB_TAG('d','i','b',' '), HB_TAG('D','N','K',' ')}, /* South Central Dinka -> Dinka */ + {HB_TAG('d','i','k',' '), HB_TAG('D','N','K',' ')}, /* Southwestern Dinka -> Dinka */ + {HB_TAG('d','i','n',' '), HB_TAG('D','N','K',' ')}, /* Dinka [macrolanguage] */ + {HB_TAG('d','i','p',' '), HB_TAG('D','N','K',' ')}, /* Northeastern Dinka -> Dinka */ + {HB_TAG('d','i','q',' '), HB_TAG('D','I','Q',' ')}, /* Dimli */ + {HB_TAG('d','i','q',' '), HB_TAG('Z','Z','A',' ')}, /* Dimli -> Zazaki */ + {HB_TAG('d','i','w',' '), HB_TAG('D','N','K',' ')}, /* Northwestern Dinka -> Dinka */ + {HB_TAG('d','j','e',' '), HB_TAG('D','J','R',' ')}, /* Zarma */ + {HB_TAG('d','j','k',' '), HB_TAG('C','P','P',' ')}, /* Eastern Maroon Creole -> Creoles */ + {HB_TAG('d','j','r',' '), HB_TAG('D','J','R','0')}, /* Djambarrpuyngu */ + {HB_TAG('d','k','s',' '), HB_TAG('D','N','K',' ')}, /* Southeastern Dinka -> Dinka */ + {HB_TAG('d','n','g',' '), HB_TAG('D','U','N',' ')}, /* Dungan */ +/*{HB_TAG('d','n','j',' '), HB_TAG('D','N','J',' ')},*/ /* Dan */ + {HB_TAG('d','n','k',' '), HB_TAG_NONE }, /* Dengka != Dinka */ + {HB_TAG('d','o','i',' '), HB_TAG('D','G','R',' ')}, /* Dogri (macrolanguage) [macrolanguage] */ + {HB_TAG('d','r','h',' '), HB_TAG('M','N','G',' ')}, /* Darkhat (retired code) -> Mongolian */ + {HB_TAG('d','r','i',' '), HB_TAG_NONE }, /* C'Lela != Dari */ + {HB_TAG('d','r','w',' '), HB_TAG('D','R','I',' ')}, /* Darwazi (retired code) -> Dari */ + {HB_TAG('d','r','w',' '), HB_TAG('F','A','R',' ')}, /* Darwazi (retired code) -> Persian */ + {HB_TAG('d','s','b',' '), HB_TAG('L','S','B',' ')}, /* Lower Sorbian */ + {HB_TAG('d','t','y',' '), HB_TAG('N','E','P',' ')}, /* Dotyali -> Nepali */ +/*{HB_TAG('d','u','j',' '), HB_TAG('D','U','J',' ')},*/ /* Dhuwal (retired code) */ + {HB_TAG('d','u','n',' '), HB_TAG_NONE }, /* Dusun Deyah != Dungan */ + {HB_TAG('d','u','p',' '), HB_TAG('M','L','Y',' ')}, /* Duano -> Malay */ + {HB_TAG('d','w','k',' '), HB_TAG('K','U','I',' ')}, /* Dawik Kui -> Kui */ + {HB_TAG('d','w','u',' '), HB_TAG('D','U','J',' ')}, /* Dhuwal */ + {HB_TAG('d','w','y',' '), HB_TAG('D','U','J',' ')}, /* Dhuwaya -> Dhuwal */ + {HB_TAG('d','y','u',' '), HB_TAG('J','U','L',' ')}, /* Dyula -> Jula */ + {HB_TAG('d','z','n',' '), HB_TAG_NONE }, /* Dzando != Dzongkha */ + {HB_TAG('e','c','r',' '), HB_TAG_NONE }, /* Eteocretan != Eastern Cree */ +/*{HB_TAG('e','f','i',' '), HB_TAG('E','F','I',' ')},*/ /* Efik */ + {HB_TAG('e','k','k',' '), HB_TAG('E','T','I',' ')}, /* Standard Estonian -> Estonian */ + {HB_TAG('e','k','y',' '), HB_TAG('K','R','N',' ')}, /* Eastern Kayah -> Karen */ + {HB_TAG('e','m','k',' '), HB_TAG('E','M','K',' ')}, /* Eastern Maninkakan */ + {HB_TAG('e','m','k',' '), HB_TAG('M','N','K',' ')}, /* Eastern Maninkakan -> Maninka */ + {HB_TAG('e','m','y',' '), HB_TAG('M','Y','N',' ')}, /* Epigraphic Mayan -> Mayan */ + {HB_TAG('e','n','b',' '), HB_TAG('K','A','L',' ')}, /* Markweeta -> Kalenjin */ + {HB_TAG('e','n','f',' '), HB_TAG('F','N','E',' ')}, /* Forest Enets */ + {HB_TAG('e','n','h',' '), HB_TAG('T','N','E',' ')}, /* Tundra Enets */ + {HB_TAG('e','s','g',' '), HB_TAG('G','O','N',' ')}, /* Aheri Gondi -> Gondi */ + {HB_TAG('e','s','i',' '), HB_TAG('I','P','K',' ')}, /* North Alaskan Inupiatun -> Inupiat */ + {HB_TAG('e','s','k',' '), HB_TAG('I','P','K',' ')}, /* Northwest Alaska Inupiatun -> Inupiat */ +/*{HB_TAG('e','s','u',' '), HB_TAG('E','S','U',' ')},*/ /* Central Yupik */ + {HB_TAG('e','t','o',' '), HB_TAG('B','T','I',' ')}, /* Eton (Cameroon) -> Beti */ + {HB_TAG('e','u','q',' '), HB_TAG_NONE }, /* Basque [collection] != Basque */ + {HB_TAG('e','v','e',' '), HB_TAG('E','V','N',' ')}, /* Even */ + {HB_TAG('e','v','n',' '), HB_TAG('E','V','K',' ')}, /* Evenki */ + {HB_TAG('e','w','o',' '), HB_TAG('B','T','I',' ')}, /* Ewondo -> Beti */ + {HB_TAG('e','y','o',' '), HB_TAG('K','A','L',' ')}, /* Keiyo -> Kalenjin */ + {HB_TAG('f','a','b',' '), HB_TAG('C','P','P',' ')}, /* Fa d'Ambu -> Creoles */ + {HB_TAG('f','a','n',' '), HB_TAG('F','A','N','0')}, /* Fang (Equatorial Guinea) */ + {HB_TAG('f','a','n',' '), HB_TAG('B','T','I',' ')}, /* Fang (Equatorial Guinea) -> Beti */ + {HB_TAG('f','a','r',' '), HB_TAG_NONE }, /* Fataleka != Persian */ + {HB_TAG('f','a','t',' '), HB_TAG('F','A','T',' ')}, /* Fanti */ + {HB_TAG('f','a','t',' '), HB_TAG('A','K','A',' ')}, /* Fanti -> Akan */ + {HB_TAG('f','b','l',' '), HB_TAG('B','I','K',' ')}, /* West Albay Bikol -> Bikol */ + {HB_TAG('f','f','m',' '), HB_TAG('F','U','L',' ')}, /* Maasina Fulfulde -> Fulah */ + {HB_TAG('f','i','l',' '), HB_TAG('P','I','L',' ')}, /* Filipino */ + {HB_TAG('f','l','m',' '), HB_TAG('H','A','L',' ')}, /* Halam (Falam Chin) (retired code) */ + {HB_TAG('f','l','m',' '), HB_TAG('Q','I','N',' ')}, /* Falam Chin (retired code) -> Chin */ + {HB_TAG('f','m','p',' '), HB_TAG('F','M','P',' ')}, /* Fe’fe’ */ + {HB_TAG('f','m','p',' '), HB_TAG('B','M','L',' ')}, /* Fe'fe' -> Bamileke */ + {HB_TAG('f','n','g',' '), HB_TAG('C','P','P',' ')}, /* Fanagalo -> Creoles */ +/*{HB_TAG('f','o','n',' '), HB_TAG('F','O','N',' ')},*/ /* Fon */ + {HB_TAG('f','o','s',' '), HB_TAG_NONE }, /* Siraya != Faroese */ + {HB_TAG('f','p','e',' '), HB_TAG('C','P','P',' ')}, /* Fernando Po Creole English -> Creoles */ +/*{HB_TAG('f','r','c',' '), HB_TAG('F','R','C',' ')},*/ /* Cajun French */ +/*{HB_TAG('f','r','p',' '), HB_TAG('F','R','P',' ')},*/ /* Arpitan */ + {HB_TAG('f','u','b',' '), HB_TAG('F','U','L',' ')}, /* Adamawa Fulfulde -> Fulah */ + {HB_TAG('f','u','c',' '), HB_TAG('F','U','L',' ')}, /* Pulaar -> Fulah */ + {HB_TAG('f','u','e',' '), HB_TAG('F','U','L',' ')}, /* Borgu Fulfulde -> Fulah */ + {HB_TAG('f','u','f',' '), HB_TAG('F','T','A',' ')}, /* Pular -> Futa */ + {HB_TAG('f','u','f',' '), HB_TAG('F','U','L',' ')}, /* Pular -> Fulah */ + {HB_TAG('f','u','h',' '), HB_TAG('F','U','L',' ')}, /* Western Niger Fulfulde -> Fulah */ + {HB_TAG('f','u','i',' '), HB_TAG('F','U','L',' ')}, /* Bagirmi Fulfulde -> Fulah */ + {HB_TAG('f','u','q',' '), HB_TAG('F','U','L',' ')}, /* Central-Eastern Niger Fulfulde -> Fulah */ + {HB_TAG('f','u','r',' '), HB_TAG('F','R','L',' ')}, /* Friulian */ + {HB_TAG('f','u','v',' '), HB_TAG('F','U','V',' ')}, /* Nigerian Fulfulde */ + {HB_TAG('f','u','v',' '), HB_TAG('F','U','L',' ')}, /* Nigerian Fulfulde -> Fulah */ + {HB_TAG('g','a','a',' '), HB_TAG('G','A','D',' ')}, /* Ga */ + {HB_TAG('g','a','c',' '), HB_TAG('C','P','P',' ')}, /* Mixed Great Andamanese -> Creoles */ + {HB_TAG('g','a','d',' '), HB_TAG_NONE }, /* Gaddang != Ga */ + {HB_TAG('g','a','e',' '), HB_TAG_NONE }, /* Guarequena != Scottish Gaelic (Gaelic) */ +/*{HB_TAG('g','a','g',' '), HB_TAG('G','A','G',' ')},*/ /* Gagauz */ + {HB_TAG('g','a','l',' '), HB_TAG_NONE }, /* Galolen != Galician */ + {HB_TAG('g','a','n',' '), HB_TAG('Z','H','S',' ')}, /* Gan Chinese -> Chinese, Simplified */ + {HB_TAG('g','a','r',' '), HB_TAG_NONE }, /* Galeya != Garshuni */ + {HB_TAG('g','a','w',' '), HB_TAG_NONE }, /* Nobonob != Garhwali */ + {HB_TAG('g','a','x',' '), HB_TAG('O','R','O',' ')}, /* Borana-Arsi-Guji Oromo -> Oromo */ + {HB_TAG('g','a','z',' '), HB_TAG('O','R','O',' ')}, /* West Central Oromo -> Oromo */ + {HB_TAG('g','b','m',' '), HB_TAG('G','A','W',' ')}, /* Garhwali */ + {HB_TAG('g','c','e',' '), HB_TAG('A','T','H',' ')}, /* Galice -> Athapaskan */ + {HB_TAG('g','c','f',' '), HB_TAG('C','P','P',' ')}, /* Guadeloupean Creole French -> Creoles */ + {HB_TAG('g','c','l',' '), HB_TAG('C','P','P',' ')}, /* Grenadian Creole English -> Creoles */ + {HB_TAG('g','c','r',' '), HB_TAG('C','P','P',' ')}, /* Guianese Creole French -> Creoles */ + {HB_TAG('g','d','a',' '), HB_TAG('R','A','J',' ')}, /* Gade Lohar -> Rajasthani */ +/*{HB_TAG('g','e','z',' '), HB_TAG('G','E','Z',' ')},*/ /* Geez */ + {HB_TAG('g','g','o',' '), HB_TAG('G','O','N',' ')}, /* Southern Gondi (retired code) -> Gondi */ + {HB_TAG('g','h','a',' '), HB_TAG('B','B','R',' ')}, /* Ghadamès -> Berber */ + {HB_TAG('g','h','k',' '), HB_TAG('K','R','N',' ')}, /* Geko Karen -> Karen */ + {HB_TAG('g','h','o',' '), HB_TAG('B','B','R',' ')}, /* Ghomara -> Berber */ + {HB_TAG('g','i','b',' '), HB_TAG('C','P','P',' ')}, /* Gibanawa -> Creoles */ +/*{HB_TAG('g','i','h',' '), HB_TAG('G','I','H',' ')},*/ /* Githabul */ + {HB_TAG('g','i','l',' '), HB_TAG('G','I','L','0')}, /* Kiribati (Gilbertese) */ + {HB_TAG('g','j','u',' '), HB_TAG('R','A','J',' ')}, /* Gujari -> Rajasthani */ + {HB_TAG('g','k','p',' '), HB_TAG('G','K','P',' ')}, /* Guinea Kpelle -> Kpelle (Guinea) */ + {HB_TAG('g','k','p',' '), HB_TAG('K','P','L',' ')}, /* Guinea Kpelle -> Kpelle */ + {HB_TAG('g','l','d',' '), HB_TAG('N','A','N',' ')}, /* Nanai */ +/*{HB_TAG('g','l','k',' '), HB_TAG('G','L','K',' ')},*/ /* Gilaki */ + {HB_TAG('g','m','z',' '), HB_TAG_NONE }, /* Mgbolizhia != Gumuz */ + {HB_TAG('g','n','b',' '), HB_TAG('Q','I','N',' ')}, /* Gangte -> Chin */ +/*{HB_TAG('g','n','n',' '), HB_TAG('G','N','N',' ')},*/ /* Gumatj */ + {HB_TAG('g','n','o',' '), HB_TAG('G','O','N',' ')}, /* Northern Gondi -> Gondi */ + {HB_TAG('g','n','w',' '), HB_TAG('G','U','A',' ')}, /* Western Bolivian Guaraní -> Guarani */ +/*{HB_TAG('g','o','g',' '), HB_TAG('G','O','G',' ')},*/ /* Gogo */ + {HB_TAG('g','o','m',' '), HB_TAG('K','O','K',' ')}, /* Goan Konkani -> Konkani */ +/*{HB_TAG('g','o','n',' '), HB_TAG('G','O','N',' ')},*/ /* Gondi [macrolanguage] */ + {HB_TAG('g','o','q',' '), HB_TAG('C','P','P',' ')}, /* Gorap -> Creoles */ + {HB_TAG('g','o','x',' '), HB_TAG('B','A','D','0')}, /* Gobu -> Banda */ + {HB_TAG('g','p','e',' '), HB_TAG('C','P','P',' ')}, /* Ghanaian Pidgin English -> Creoles */ + {HB_TAG('g','r','o',' '), HB_TAG_NONE }, /* Groma != Garo */ + {HB_TAG('g','r','r',' '), HB_TAG('B','B','R',' ')}, /* Taznatit -> Berber */ + {HB_TAG('g','r','t',' '), HB_TAG('G','R','O',' ')}, /* Garo */ + {HB_TAG('g','r','u',' '), HB_TAG('S','O','G',' ')}, /* Kistane -> Sodo Gurage */ + {HB_TAG('g','s','w',' '), HB_TAG('A','L','S',' ')}, /* Alsatian */ + {HB_TAG('g','u','a',' '), HB_TAG_NONE }, /* Shiki != Guarani */ +/*{HB_TAG('g','u','c',' '), HB_TAG('G','U','C',' ')},*/ /* Wayuu */ +/*{HB_TAG('g','u','f',' '), HB_TAG('G','U','F',' ')},*/ /* Gupapuyngu */ + {HB_TAG('g','u','g',' '), HB_TAG('G','U','A',' ')}, /* Paraguayan Guaraní -> Guarani */ + {HB_TAG('g','u','i',' '), HB_TAG('G','U','A',' ')}, /* Eastern Bolivian Guaraní -> Guarani */ + {HB_TAG('g','u','k',' '), HB_TAG('G','M','Z',' ')}, /* Gumuz */ + {HB_TAG('g','u','l',' '), HB_TAG('C','P','P',' ')}, /* Sea Island Creole English -> Creoles */ + {HB_TAG('g','u','n',' '), HB_TAG('G','U','A',' ')}, /* Mbyá Guaraní -> Guarani */ +/*{HB_TAG('g','u','z',' '), HB_TAG('G','U','Z',' ')},*/ /* Gusii */ + {HB_TAG('g','w','i',' '), HB_TAG('A','T','H',' ')}, /* Gwichʼin -> Athapaskan */ + {HB_TAG('g','y','n',' '), HB_TAG('C','P','P',' ')}, /* Guyanese Creole English -> Creoles */ + {HB_TAG('h','a','a',' '), HB_TAG('A','T','H',' ')}, /* Han -> Athapaskan */ + {HB_TAG('h','a','e',' '), HB_TAG('O','R','O',' ')}, /* Eastern Oromo -> Oromo */ + {HB_TAG('h','a','i',' '), HB_TAG('H','A','I','0')}, /* Haida [macrolanguage] */ + {HB_TAG('h','a','k',' '), HB_TAG('Z','H','S',' ')}, /* Hakka Chinese -> Chinese, Simplified */ + {HB_TAG('h','a','l',' '), HB_TAG_NONE }, /* Halang != Halam (Falam Chin) */ + {HB_TAG('h','a','r',' '), HB_TAG('H','R','I',' ')}, /* Harari */ +/*{HB_TAG('h','a','w',' '), HB_TAG('H','A','W',' ')},*/ /* Hawaiian */ + {HB_TAG('h','a','x',' '), HB_TAG('H','A','I','0')}, /* Southern Haida -> Haida */ +/*{HB_TAG('h','a','y',' '), HB_TAG('H','A','Y',' ')},*/ /* Haya */ +/*{HB_TAG('h','a','z',' '), HB_TAG('H','A','Z',' ')},*/ /* Hazaragi */ + {HB_TAG('h','b','n',' '), HB_TAG_NONE }, /* Heiban != Hammer-Banna */ + {HB_TAG('h','c','a',' '), HB_TAG('C','P','P',' ')}, /* Andaman Creole Hindi -> Creoles */ + {HB_TAG('h','d','n',' '), HB_TAG('H','A','I','0')}, /* Northern Haida -> Haida */ + {HB_TAG('h','e','a',' '), HB_TAG('H','M','N',' ')}, /* Northern Qiandong Miao -> Hmong */ +/*{HB_TAG('h','e','i',' '), HB_TAG('H','E','I',' ')},*/ /* Heiltsuk */ +/*{HB_TAG('h','i','l',' '), HB_TAG('H','I','L',' ')},*/ /* Hiligaynon */ + {HB_TAG('h','j','i',' '), HB_TAG('M','L','Y',' ')}, /* Haji -> Malay */ + {HB_TAG('h','l','t',' '), HB_TAG('Q','I','N',' ')}, /* Matu Chin -> Chin */ + {HB_TAG('h','m','a',' '), HB_TAG('H','M','N',' ')}, /* Southern Mashan Hmong -> Hmong */ + {HB_TAG('h','m','c',' '), HB_TAG('H','M','N',' ')}, /* Central Huishui Hmong -> Hmong */ + {HB_TAG('h','m','d',' '), HB_TAG('H','M','D',' ')}, /* Large Flowery Miao -> A-Hmao */ + {HB_TAG('h','m','d',' '), HB_TAG('H','M','N',' ')}, /* Large Flowery Miao -> Hmong */ + {HB_TAG('h','m','e',' '), HB_TAG('H','M','N',' ')}, /* Eastern Huishui Hmong -> Hmong */ + {HB_TAG('h','m','g',' '), HB_TAG('H','M','N',' ')}, /* Southwestern Guiyang Hmong -> Hmong */ + {HB_TAG('h','m','h',' '), HB_TAG('H','M','N',' ')}, /* Southwestern Huishui Hmong -> Hmong */ + {HB_TAG('h','m','i',' '), HB_TAG('H','M','N',' ')}, /* Northern Huishui Hmong -> Hmong */ + {HB_TAG('h','m','j',' '), HB_TAG('H','M','N',' ')}, /* Ge -> Hmong */ + {HB_TAG('h','m','l',' '), HB_TAG('H','M','N',' ')}, /* Luopohe Hmong -> Hmong */ + {HB_TAG('h','m','m',' '), HB_TAG('H','M','N',' ')}, /* Central Mashan Hmong -> Hmong */ +/*{HB_TAG('h','m','n',' '), HB_TAG('H','M','N',' ')},*/ /* Hmong [macrolanguage] */ + {HB_TAG('h','m','p',' '), HB_TAG('H','M','N',' ')}, /* Northern Mashan Hmong -> Hmong */ + {HB_TAG('h','m','q',' '), HB_TAG('H','M','N',' ')}, /* Eastern Qiandong Miao -> Hmong */ + {HB_TAG('h','m','r',' '), HB_TAG('Q','I','N',' ')}, /* Hmar -> Chin */ + {HB_TAG('h','m','s',' '), HB_TAG('H','M','N',' ')}, /* Southern Qiandong Miao -> Hmong */ + {HB_TAG('h','m','w',' '), HB_TAG('H','M','N',' ')}, /* Western Mashan Hmong -> Hmong */ + {HB_TAG('h','m','y',' '), HB_TAG('H','M','N',' ')}, /* Southern Guiyang Hmong -> Hmong */ + {HB_TAG('h','m','z',' '), HB_TAG('H','M','Z',' ')}, /* Hmong Shua -> Hmong Shuat */ + {HB_TAG('h','m','z',' '), HB_TAG('H','M','N',' ')}, /* Hmong Shua -> Hmong */ +/*{HB_TAG('h','n','d',' '), HB_TAG('H','N','D',' ')},*/ /* Southern Hindko -> Hindko */ + {HB_TAG('h','n','e',' '), HB_TAG('C','H','H',' ')}, /* Chhattisgarhi -> Chattisgarhi */ + {HB_TAG('h','n','j',' '), HB_TAG('H','M','N',' ')}, /* Hmong Njua -> Hmong */ + {HB_TAG('h','n','o',' '), HB_TAG('H','N','D',' ')}, /* Northern Hindko -> Hindko */ + {HB_TAG('h','o','c',' '), HB_TAG('H','O',' ',' ')}, /* Ho */ + {HB_TAG('h','o','i',' '), HB_TAG('A','T','H',' ')}, /* Holikachuk -> Athapaskan */ + {HB_TAG('h','o','j',' '), HB_TAG('H','A','R',' ')}, /* Hadothi -> Harauti */ + {HB_TAG('h','o','j',' '), HB_TAG('R','A','J',' ')}, /* Hadothi -> Rajasthani */ + {HB_TAG('h','r','a',' '), HB_TAG('Q','I','N',' ')}, /* Hrangkhol -> Chin */ + {HB_TAG('h','r','m',' '), HB_TAG('H','M','N',' ')}, /* Horned Miao -> Hmong */ + {HB_TAG('h','s','b',' '), HB_TAG('U','S','B',' ')}, /* Upper Sorbian */ + {HB_TAG('h','s','n',' '), HB_TAG('Z','H','S',' ')}, /* Xiang Chinese -> Chinese, Simplified */ + {HB_TAG('h','u','j',' '), HB_TAG('H','M','N',' ')}, /* Northern Guiyang Hmong -> Hmong */ + {HB_TAG('h','u','p',' '), HB_TAG('A','T','H',' ')}, /* Hupa -> Athapaskan */ + {HB_TAG('h','u','s',' '), HB_TAG('M','Y','N',' ')}, /* Huastec -> Mayan */ + {HB_TAG('h','w','c',' '), HB_TAG('C','P','P',' ')}, /* Hawai'i Creole English -> Creoles */ + {HB_TAG('h','y','w',' '), HB_TAG('H','Y','E',' ')}, /* Western Armenian -> Armenian */ +/*{HB_TAG('i','b','a',' '), HB_TAG('I','B','A',' ')},*/ /* Iban */ +/*{HB_TAG('i','b','b',' '), HB_TAG('I','B','B',' ')},*/ /* Ibibio */ + {HB_TAG('i','b','y',' '), HB_TAG('I','J','O',' ')}, /* Ibani -> Ijo */ + {HB_TAG('i','c','r',' '), HB_TAG('C','P','P',' ')}, /* Islander Creole English -> Creoles */ + {HB_TAG('i','d','a',' '), HB_TAG('L','U','H',' ')}, /* Idakho-Isukha-Tiriki -> Luyia */ + {HB_TAG('i','d','b',' '), HB_TAG('C','P','P',' ')}, /* Indo-Portuguese -> Creoles */ + {HB_TAG('i','g','b',' '), HB_TAG('E','B','I',' ')}, /* Ebira */ + {HB_TAG('i','h','b',' '), HB_TAG('C','P','P',' ')}, /* Iha Based Pidgin -> Creoles */ + {HB_TAG('i','j','c',' '), HB_TAG('I','J','O',' ')}, /* Izon -> Ijo */ + {HB_TAG('i','j','e',' '), HB_TAG('I','J','O',' ')}, /* Biseni -> Ijo */ + {HB_TAG('i','j','n',' '), HB_TAG('I','J','O',' ')}, /* Kalabari -> Ijo */ +/*{HB_TAG('i','j','o',' '), HB_TAG('I','J','O',' ')},*/ /* Ijo [collection] */ + {HB_TAG('i','j','s',' '), HB_TAG('I','J','O',' ')}, /* Southeast Ijo -> Ijo */ + {HB_TAG('i','k','e',' '), HB_TAG('I','N','U',' ')}, /* Eastern Canadian Inuktitut -> Inuktitut */ + {HB_TAG('i','k','e',' '), HB_TAG('I','N','U','K')}, /* Eastern Canadian Inuktitut -> Nunavik Inuktitut */ + {HB_TAG('i','k','t',' '), HB_TAG('I','N','U',' ')}, /* Inuinnaqtun -> Inuktitut */ +/*{HB_TAG('i','l','o',' '), HB_TAG('I','L','O',' ')},*/ /* Iloko -> Ilokano */ + {HB_TAG('i','n','g',' '), HB_TAG('A','T','H',' ')}, /* Degexit'an -> Athapaskan */ + {HB_TAG('i','n','h',' '), HB_TAG('I','N','G',' ')}, /* Ingush */ + {HB_TAG('i','r','i',' '), HB_TAG_NONE }, /* Rigwe != Irish */ +/*{HB_TAG('i','r','u',' '), HB_TAG('I','R','U',' ')},*/ /* Irula */ + {HB_TAG('i','s','m',' '), HB_TAG_NONE }, /* Masimasi != Inari Sami */ + {HB_TAG('i','t','z',' '), HB_TAG('M','Y','N',' ')}, /* Itzá -> Mayan */ + {HB_TAG('i','x','l',' '), HB_TAG('M','Y','N',' ')}, /* Ixil -> Mayan */ + {HB_TAG('j','a','c',' '), HB_TAG('M','Y','N',' ')}, /* Popti' -> Mayan */ + {HB_TAG('j','a','k',' '), HB_TAG('M','L','Y',' ')}, /* Jakun -> Malay */ + {HB_TAG('j','a','m',' '), HB_TAG('J','A','M',' ')}, /* Jamaican Creole English -> Jamaican Creole */ + {HB_TAG('j','a','m',' '), HB_TAG('C','P','P',' ')}, /* Jamaican Creole English -> Creoles */ + {HB_TAG('j','a','n',' '), HB_TAG_NONE }, /* Jandai != Japanese */ + {HB_TAG('j','a','x',' '), HB_TAG('M','L','Y',' ')}, /* Jambi Malay -> Malay */ + {HB_TAG('j','b','e',' '), HB_TAG('B','B','R',' ')}, /* Judeo-Berber -> Berber */ + {HB_TAG('j','b','n',' '), HB_TAG('B','B','R',' ')}, /* Nafusi -> Berber */ +/*{HB_TAG('j','b','o',' '), HB_TAG('J','B','O',' ')},*/ /* Lojban */ +/*{HB_TAG('j','c','t',' '), HB_TAG('J','C','T',' ')},*/ /* Krymchak */ + {HB_TAG('j','g','o',' '), HB_TAG('B','M','L',' ')}, /* Ngomba -> Bamileke */ + {HB_TAG('j','i','i',' '), HB_TAG_NONE }, /* Jiiddu != Yiddish */ + {HB_TAG('j','k','m',' '), HB_TAG('K','R','N',' ')}, /* Mobwa Karen -> Karen */ + {HB_TAG('j','k','p',' '), HB_TAG('K','R','N',' ')}, /* Paku Karen -> Karen */ + {HB_TAG('j','u','d',' '), HB_TAG_NONE }, /* Worodougou != Ladino */ + {HB_TAG('j','u','l',' '), HB_TAG_NONE }, /* Jirel != Jula */ + {HB_TAG('j','v','d',' '), HB_TAG('C','P','P',' ')}, /* Javindo -> Creoles */ + {HB_TAG('k','a','a',' '), HB_TAG('K','R','K',' ')}, /* Karakalpak */ + {HB_TAG('k','a','b',' '), HB_TAG('K','A','B','0')}, /* Kabyle */ + {HB_TAG('k','a','b',' '), HB_TAG('B','B','R',' ')}, /* Kabyle -> Berber */ + {HB_TAG('k','a','c',' '), HB_TAG_NONE }, /* Kachin != Kachchi */ + {HB_TAG('k','a','m',' '), HB_TAG('K','M','B',' ')}, /* Kamba (Kenya) */ + {HB_TAG('k','a','r',' '), HB_TAG('K','R','N',' ')}, /* Karen [collection] */ +/*{HB_TAG('k','a','w',' '), HB_TAG('K','A','W',' ')},*/ /* Kawi (Old Javanese) */ + {HB_TAG('k','b','d',' '), HB_TAG('K','A','B',' ')}, /* Kabardian */ + {HB_TAG('k','b','y',' '), HB_TAG('K','N','R',' ')}, /* Manga Kanuri -> Kanuri */ + {HB_TAG('k','c','a',' '), HB_TAG('K','H','K',' ')}, /* Khanty -> Khanty-Kazim */ + {HB_TAG('k','c','a',' '), HB_TAG('K','H','S',' ')}, /* Khanty -> Khanty-Shurishkar */ + {HB_TAG('k','c','a',' '), HB_TAG('K','H','V',' ')}, /* Khanty -> Khanty-Vakhi */ + {HB_TAG('k','c','n',' '), HB_TAG('C','P','P',' ')}, /* Nubi -> Creoles */ +/*{HB_TAG('k','d','e',' '), HB_TAG('K','D','E',' ')},*/ /* Makonde */ + {HB_TAG('k','d','r',' '), HB_TAG('K','R','M',' ')}, /* Karaim */ + {HB_TAG('k','d','t',' '), HB_TAG('K','U','Y',' ')}, /* Kuy */ + {HB_TAG('k','e','a',' '), HB_TAG('K','E','A',' ')}, /* Kabuverdianu (Crioulo) */ + {HB_TAG('k','e','a',' '), HB_TAG('C','P','P',' ')}, /* Kabuverdianu -> Creoles */ + {HB_TAG('k','e','b',' '), HB_TAG_NONE }, /* Kélé != Kebena */ + {HB_TAG('k','e','k',' '), HB_TAG('K','E','K',' ')}, /* Kekchi */ + {HB_TAG('k','e','k',' '), HB_TAG('M','Y','N',' ')}, /* Kekchí -> Mayan */ + {HB_TAG('k','e','x',' '), HB_TAG('K','K','N',' ')}, /* Kukna -> Kokni */ + {HB_TAG('k','f','a',' '), HB_TAG('K','O','D',' ')}, /* Kodava -> Kodagu */ + {HB_TAG('k','f','r',' '), HB_TAG('K','A','C',' ')}, /* Kachhi -> Kachchi */ + {HB_TAG('k','f','x',' '), HB_TAG('K','U','L',' ')}, /* Kullu Pahari -> Kulvi */ + {HB_TAG('k','f','y',' '), HB_TAG('K','M','N',' ')}, /* Kumaoni */ + {HB_TAG('k','g','e',' '), HB_TAG_NONE }, /* Komering != Khutsuri Georgian */ + {HB_TAG('k','h','a',' '), HB_TAG('K','S','I',' ')}, /* Khasi */ + {HB_TAG('k','h','b',' '), HB_TAG('X','B','D',' ')}, /* Lü */ + {HB_TAG('k','h','k',' '), HB_TAG('M','N','G',' ')}, /* Halh Mongolian -> Mongolian */ + {HB_TAG('k','h','n',' '), HB_TAG_NONE }, /* Khandesi != Khamti Shan (Microsoft fonts) */ + {HB_TAG('k','h','s',' '), HB_TAG_NONE }, /* Kasua != Khanty-Shurishkar */ + {HB_TAG('k','h','t',' '), HB_TAG('K','H','T',' ')}, /* Khamti -> Khamti Shan */ + {HB_TAG('k','h','t',' '), HB_TAG('K','H','N',' ')}, /* Khamti -> Khamti Shan (Microsoft fonts) */ + {HB_TAG('k','h','v',' '), HB_TAG_NONE }, /* Khvarshi != Khanty-Vakhi */ +/*{HB_TAG('k','h','w',' '), HB_TAG('K','H','W',' ')},*/ /* Khowar */ + {HB_TAG('k','i','s',' '), HB_TAG_NONE }, /* Kis != Kisii */ + {HB_TAG('k','i','u',' '), HB_TAG('K','I','U',' ')}, /* Kirmanjki */ + {HB_TAG('k','i','u',' '), HB_TAG('Z','Z','A',' ')}, /* Kirmanjki -> Zazaki */ + {HB_TAG('k','j','b',' '), HB_TAG('M','Y','N',' ')}, /* Q'anjob'al -> Mayan */ +/*{HB_TAG('k','j','d',' '), HB_TAG('K','J','D',' ')},*/ /* Southern Kiwai */ + {HB_TAG('k','j','h',' '), HB_TAG('K','H','A',' ')}, /* Khakas -> Khakass */ + {HB_TAG('k','j','p',' '), HB_TAG('K','J','P',' ')}, /* Pwo Eastern Karen -> Eastern Pwo Karen */ + {HB_TAG('k','j','p',' '), HB_TAG('K','R','N',' ')}, /* Pwo Eastern Karen -> Karen */ + {HB_TAG('k','j','t',' '), HB_TAG('K','R','N',' ')}, /* Phrae Pwo Karen -> Karen */ +/*{HB_TAG('k','j','z',' '), HB_TAG('K','J','Z',' ')},*/ /* Bumthangkha */ + {HB_TAG('k','k','n',' '), HB_TAG_NONE }, /* Kon Keu != Kokni */ + {HB_TAG('k','k','z',' '), HB_TAG('A','T','H',' ')}, /* Kaska -> Athapaskan */ + {HB_TAG('k','l','m',' '), HB_TAG_NONE }, /* Migum != Kalmyk */ + {HB_TAG('k','l','n',' '), HB_TAG('K','A','L',' ')}, /* Kalenjin [macrolanguage] */ + {HB_TAG('k','m','b',' '), HB_TAG('M','B','N',' ')}, /* Kimbundu -> Mbundu */ + {HB_TAG('k','m','n',' '), HB_TAG_NONE }, /* Awtuw != Kumaoni */ + {HB_TAG('k','m','o',' '), HB_TAG_NONE }, /* Kwoma != Komo */ + {HB_TAG('k','m','r',' '), HB_TAG('K','U','R',' ')}, /* Northern Kurdish -> Kurdish */ + {HB_TAG('k','m','s',' '), HB_TAG_NONE }, /* Kamasau != Komso */ + {HB_TAG('k','m','v',' '), HB_TAG('C','P','P',' ')}, /* Karipúna Creole French -> Creoles */ + {HB_TAG('k','m','w',' '), HB_TAG('K','M','O',' ')}, /* Komo (Democratic Republic of Congo) */ +/*{HB_TAG('k','m','z',' '), HB_TAG('K','M','Z',' ')},*/ /* Khorasani Turkish -> Khorasani Turkic */ + {HB_TAG('k','n','c',' '), HB_TAG('K','N','R',' ')}, /* Central Kanuri -> Kanuri */ + {HB_TAG('k','n','g',' '), HB_TAG('K','O','N','0')}, /* Koongo -> Kongo */ + {HB_TAG('k','n','j',' '), HB_TAG('M','Y','N',' ')}, /* Western Kanjobal -> Mayan */ + {HB_TAG('k','n','n',' '), HB_TAG('K','O','K',' ')}, /* Konkani */ + {HB_TAG('k','n','r',' '), HB_TAG_NONE }, /* Kaningra != Kanuri */ + {HB_TAG('k','o','d',' '), HB_TAG_NONE }, /* Kodi != Kodagu */ + {HB_TAG('k','o','h',' '), HB_TAG_NONE }, /* Koyo != Korean Old Hangul */ + {HB_TAG('k','o','i',' '), HB_TAG('K','O','P',' ')}, /* Komi-Permyak */ + {HB_TAG('k','o','i',' '), HB_TAG('K','O','M',' ')}, /* Komi-Permyak -> Komi */ +/*{HB_TAG('k','o','k',' '), HB_TAG('K','O','K',' ')},*/ /* Konkani [macrolanguage] */ + {HB_TAG('k','o','p',' '), HB_TAG_NONE }, /* Waube != Komi-Permyak */ +/*{HB_TAG('k','o','s',' '), HB_TAG('K','O','S',' ')},*/ /* Kosraean */ + {HB_TAG('k','o','y',' '), HB_TAG('A','T','H',' ')}, /* Koyukon -> Athapaskan */ + {HB_TAG('k','o','z',' '), HB_TAG_NONE }, /* Korak != Komi-Zyrian */ + {HB_TAG('k','p','e',' '), HB_TAG('K','P','L',' ')}, /* Kpelle [macrolanguage] */ + {HB_TAG('k','p','l',' '), HB_TAG_NONE }, /* Kpala != Kpelle */ + {HB_TAG('k','p','p',' '), HB_TAG('K','R','N',' ')}, /* Paku Karen (retired code) -> Karen */ + {HB_TAG('k','p','v',' '), HB_TAG('K','O','Z',' ')}, /* Komi-Zyrian */ + {HB_TAG('k','p','v',' '), HB_TAG('K','O','M',' ')}, /* Komi-Zyrian -> Komi */ + {HB_TAG('k','p','y',' '), HB_TAG('K','Y','K',' ')}, /* Koryak */ + {HB_TAG('k','q','s',' '), HB_TAG('K','I','S',' ')}, /* Northern Kissi -> Kisii */ + {HB_TAG('k','q','y',' '), HB_TAG('K','R','T',' ')}, /* Koorete */ + {HB_TAG('k','r','c',' '), HB_TAG('K','A','R',' ')}, /* Karachay-Balkar -> Karachay */ + {HB_TAG('k','r','c',' '), HB_TAG('B','A','L',' ')}, /* Karachay-Balkar -> Balkar */ + {HB_TAG('k','r','i',' '), HB_TAG('K','R','I',' ')}, /* Krio */ + {HB_TAG('k','r','i',' '), HB_TAG('C','P','P',' ')}, /* Krio -> Creoles */ + {HB_TAG('k','r','k',' '), HB_TAG_NONE }, /* Kerek != Karakalpak */ +/*{HB_TAG('k','r','l',' '), HB_TAG('K','R','L',' ')},*/ /* Karelian */ + {HB_TAG('k','r','m',' '), HB_TAG_NONE }, /* Krim (retired code) != Karaim */ + {HB_TAG('k','r','n',' '), HB_TAG_NONE }, /* Sapo != Karen */ + {HB_TAG('k','r','t',' '), HB_TAG('K','N','R',' ')}, /* Tumari Kanuri -> Kanuri */ + {HB_TAG('k','r','u',' '), HB_TAG('K','U','U',' ')}, /* Kurukh */ + {HB_TAG('k','s','h',' '), HB_TAG('K','S','H','0')}, /* Kölsch -> Ripuarian */ + {HB_TAG('k','s','i',' '), HB_TAG_NONE }, /* Krisa != Khasi */ + {HB_TAG('k','s','m',' '), HB_TAG_NONE }, /* Kumba != Kildin Sami */ + {HB_TAG('k','s','s',' '), HB_TAG('K','I','S',' ')}, /* Southern Kisi -> Kisii */ + {HB_TAG('k','s','w',' '), HB_TAG('K','S','W',' ')}, /* S’gaw Karen */ + {HB_TAG('k','s','w',' '), HB_TAG('K','R','N',' ')}, /* S'gaw Karen -> Karen */ + {HB_TAG('k','t','b',' '), HB_TAG('K','E','B',' ')}, /* Kambaata -> Kebena */ + {HB_TAG('k','t','u',' '), HB_TAG('K','O','N',' ')}, /* Kituba (Democratic Republic of Congo) -> Kikongo */ + {HB_TAG('k','t','w',' '), HB_TAG('A','T','H',' ')}, /* Kato -> Athapaskan */ + {HB_TAG('k','u','i',' '), HB_TAG_NONE }, /* Kuikúro-Kalapálo != Kui */ + {HB_TAG('k','u','l',' '), HB_TAG_NONE }, /* Kulere != Kulvi */ +/*{HB_TAG('k','u','m',' '), HB_TAG('K','U','M',' ')},*/ /* Kumyk */ + {HB_TAG('k','u','u',' '), HB_TAG('A','T','H',' ')}, /* Upper Kuskokwim -> Athapaskan */ + {HB_TAG('k','u','w',' '), HB_TAG('B','A','D','0')}, /* Kpagua -> Banda */ + {HB_TAG('k','u','y',' '), HB_TAG_NONE }, /* Kuuku-Ya'u != Kuy */ + {HB_TAG('k','v','b',' '), HB_TAG('M','L','Y',' ')}, /* Kubu -> Malay */ + {HB_TAG('k','v','l',' '), HB_TAG('K','R','N',' ')}, /* Kayaw -> Karen */ + {HB_TAG('k','v','q',' '), HB_TAG('K','R','N',' ')}, /* Geba Karen -> Karen */ + {HB_TAG('k','v','r',' '), HB_TAG('M','L','Y',' ')}, /* Kerinci -> Malay */ + {HB_TAG('k','v','t',' '), HB_TAG('K','R','N',' ')}, /* Lahta Karen -> Karen */ + {HB_TAG('k','v','u',' '), HB_TAG('K','R','N',' ')}, /* Yinbaw Karen -> Karen */ + {HB_TAG('k','v','y',' '), HB_TAG('K','R','N',' ')}, /* Yintale Karen -> Karen */ +/*{HB_TAG('k','w','k',' '), HB_TAG('K','W','K',' ')},*/ /* Kwakiutl -> Kwakʼwala */ + {HB_TAG('k','w','w',' '), HB_TAG('C','P','P',' ')}, /* Kwinti -> Creoles */ + {HB_TAG('k','w','y',' '), HB_TAG('K','O','N','0')}, /* San Salvador Kongo -> Kongo */ + {HB_TAG('k','x','c',' '), HB_TAG('K','M','S',' ')}, /* Konso -> Komso */ + {HB_TAG('k','x','d',' '), HB_TAG('M','L','Y',' ')}, /* Brunei -> Malay */ + {HB_TAG('k','x','f',' '), HB_TAG('K','R','N',' ')}, /* Manumanaw Karen -> Karen */ + {HB_TAG('k','x','k',' '), HB_TAG('K','R','N',' ')}, /* Zayein Karen -> Karen */ + {HB_TAG('k','x','l',' '), HB_TAG('K','U','U',' ')}, /* Nepali Kurux (retired code) -> Kurukh */ + {HB_TAG('k','x','u',' '), HB_TAG('K','U','I',' ')}, /* Kui (India) (retired code) */ + {HB_TAG('k','y','k',' '), HB_TAG_NONE }, /* Kamayo != Koryak */ + {HB_TAG('k','y','u',' '), HB_TAG('K','Y','U',' ')}, /* Western Kayah */ + {HB_TAG('k','y','u',' '), HB_TAG('K','R','N',' ')}, /* Western Kayah -> Karen */ + {HB_TAG('l','a','c',' '), HB_TAG('M','Y','N',' ')}, /* Lacandon -> Mayan */ + {HB_TAG('l','a','d',' '), HB_TAG('J','U','D',' ')}, /* Ladino */ + {HB_TAG('l','a','h',' '), HB_TAG_NONE }, /* Lahnda [macrolanguage] != Lahuli */ + {HB_TAG('l','a','k',' '), HB_TAG_NONE }, /* Laka (Nigeria) (retired code) != Lak */ + {HB_TAG('l','a','m',' '), HB_TAG_NONE }, /* Lamba != Lambani */ + {HB_TAG('l','a','z',' '), HB_TAG_NONE }, /* Aribwatsa != Laz */ + {HB_TAG('l','b','e',' '), HB_TAG('L','A','K',' ')}, /* Lak */ + {HB_TAG('l','b','j',' '), HB_TAG('L','D','K',' ')}, /* Ladakhi */ + {HB_TAG('l','b','l',' '), HB_TAG('B','I','K',' ')}, /* Libon Bikol -> Bikol */ + {HB_TAG('l','c','e',' '), HB_TAG('M','L','Y',' ')}, /* Loncong -> Malay */ + {HB_TAG('l','c','f',' '), HB_TAG('M','L','Y',' ')}, /* Lubu -> Malay */ + {HB_TAG('l','d','i',' '), HB_TAG('K','O','N','0')}, /* Laari -> Kongo */ + {HB_TAG('l','d','k',' '), HB_TAG_NONE }, /* Leelau != Ladakhi */ +/*{HB_TAG('l','e','f',' '), HB_TAG('L','E','F',' ')},*/ /* Lelemi */ +/*{HB_TAG('l','e','z',' '), HB_TAG('L','E','Z',' ')},*/ /* Lezghian -> Lezgi */ + {HB_TAG('l','i','f',' '), HB_TAG('L','M','B',' ')}, /* Limbu */ +/*{HB_TAG('l','i','j',' '), HB_TAG('L','I','J',' ')},*/ /* Ligurian */ + {HB_TAG('l','i','r',' '), HB_TAG('C','P','P',' ')}, /* Liberian English -> Creoles */ +/*{HB_TAG('l','i','s',' '), HB_TAG('L','I','S',' ')},*/ /* Lisu */ + {HB_TAG('l','i','w',' '), HB_TAG('M','L','Y',' ')}, /* Col -> Malay */ + {HB_TAG('l','i','y',' '), HB_TAG('B','A','D','0')}, /* Banda-Bambari -> Banda */ +/*{HB_TAG('l','j','p',' '), HB_TAG('L','J','P',' ')},*/ /* Lampung Api -> Lampung */ + {HB_TAG('l','k','b',' '), HB_TAG('L','U','H',' ')}, /* Kabras -> Luyia */ +/*{HB_TAG('l','k','i',' '), HB_TAG('L','K','I',' ')},*/ /* Laki */ + {HB_TAG('l','k','o',' '), HB_TAG('L','U','H',' ')}, /* Khayo -> Luyia */ + {HB_TAG('l','k','s',' '), HB_TAG('L','U','H',' ')}, /* Kisa -> Luyia */ + {HB_TAG('l','l','d',' '), HB_TAG('L','A','D',' ')}, /* Ladin */ + {HB_TAG('l','m','a',' '), HB_TAG_NONE }, /* East Limba != Low Mari */ + {HB_TAG('l','m','b',' '), HB_TAG_NONE }, /* Merei != Limbu */ + {HB_TAG('l','m','n',' '), HB_TAG('L','A','M',' ')}, /* Lambadi -> Lambani */ +/*{HB_TAG('l','m','o',' '), HB_TAG('L','M','O',' ')},*/ /* Lombard */ + {HB_TAG('l','m','w',' '), HB_TAG_NONE }, /* Lake Miwok != Lomwe */ + {HB_TAG('l','n','a',' '), HB_TAG('B','A','D','0')}, /* Langbashe -> Banda */ + {HB_TAG('l','n','l',' '), HB_TAG('B','A','D','0')}, /* South Central Banda -> Banda */ +/*{HB_TAG('l','o','m',' '), HB_TAG('L','O','M',' ')},*/ /* Loma (Liberia) */ + {HB_TAG('l','o','u',' '), HB_TAG('C','P','P',' ')}, /* Louisiana Creole -> Creoles */ +/*{HB_TAG('l','p','o',' '), HB_TAG('L','P','O',' ')},*/ /* Lipo */ +/*{HB_TAG('l','r','c',' '), HB_TAG('L','R','C',' ')},*/ /* Northern Luri -> Luri */ + {HB_TAG('l','r','i',' '), HB_TAG('L','U','H',' ')}, /* Marachi -> Luyia */ + {HB_TAG('l','r','m',' '), HB_TAG('L','U','H',' ')}, /* Marama -> Luyia */ + {HB_TAG('l','r','t',' '), HB_TAG('C','P','P',' ')}, /* Larantuka Malay -> Creoles */ + {HB_TAG('l','s','b',' '), HB_TAG_NONE }, /* Burundian Sign Language != Lower Sorbian */ + {HB_TAG('l','s','m',' '), HB_TAG('L','U','H',' ')}, /* Saamia -> Luyia */ + {HB_TAG('l','t','g',' '), HB_TAG('L','V','I',' ')}, /* Latgalian -> Latvian */ + {HB_TAG('l','t','h',' '), HB_TAG_NONE }, /* Thur != Lithuanian */ + {HB_TAG('l','t','o',' '), HB_TAG('L','U','H',' ')}, /* Tsotso -> Luyia */ + {HB_TAG('l','t','s',' '), HB_TAG('L','U','H',' ')}, /* Tachoni -> Luyia */ +/*{HB_TAG('l','u','a',' '), HB_TAG('L','U','A',' ')},*/ /* Luba-Lulua */ +/*{HB_TAG('l','u','o',' '), HB_TAG('L','U','O',' ')},*/ /* Luo (Kenya and Tanzania) */ + {HB_TAG('l','u','s',' '), HB_TAG('M','I','Z',' ')}, /* Lushai -> Mizo */ + {HB_TAG('l','u','s',' '), HB_TAG('Q','I','N',' ')}, /* Lushai -> Chin */ + {HB_TAG('l','u','y',' '), HB_TAG('L','U','H',' ')}, /* Luyia [macrolanguage] */ + {HB_TAG('l','u','z',' '), HB_TAG('L','R','C',' ')}, /* Southern Luri -> Luri */ + {HB_TAG('l','v','i',' '), HB_TAG_NONE }, /* Lavi != Latvian */ + {HB_TAG('l','v','s',' '), HB_TAG('L','V','I',' ')}, /* Standard Latvian -> Latvian */ + {HB_TAG('l','w','g',' '), HB_TAG('L','U','H',' ')}, /* Wanga -> Luyia */ + {HB_TAG('l','z','h',' '), HB_TAG('Z','H','T',' ')}, /* Literary Chinese -> Chinese, Traditional */ + {HB_TAG('l','z','z',' '), HB_TAG('L','A','Z',' ')}, /* Laz */ +/*{HB_TAG('m','a','d',' '), HB_TAG('M','A','D',' ')},*/ /* Madurese -> Madura */ +/*{HB_TAG('m','a','g',' '), HB_TAG('M','A','G',' ')},*/ /* Magahi */ + {HB_TAG('m','a','i',' '), HB_TAG('M','T','H',' ')}, /* Maithili */ + {HB_TAG('m','a','j',' '), HB_TAG_NONE }, /* Jalapa De Díaz Mazatec != Majang */ + {HB_TAG('m','a','k',' '), HB_TAG('M','K','R',' ')}, /* Makasar */ + {HB_TAG('m','a','m',' '), HB_TAG('M','A','M',' ')}, /* Mam */ + {HB_TAG('m','a','m',' '), HB_TAG('M','Y','N',' ')}, /* Mam -> Mayan */ + {HB_TAG('m','a','n',' '), HB_TAG('M','N','K',' ')}, /* Mandingo [macrolanguage] -> Maninka */ + {HB_TAG('m','a','p',' '), HB_TAG_NONE }, /* Austronesian [collection] != Mapudungun */ + {HB_TAG('m','a','w',' '), HB_TAG_NONE }, /* Mampruli != Marwari */ + {HB_TAG('m','a','x',' '), HB_TAG('M','L','Y',' ')}, /* North Moluccan Malay -> Malay */ + {HB_TAG('m','a','x',' '), HB_TAG('C','P','P',' ')}, /* North Moluccan Malay -> Creoles */ + {HB_TAG('m','b','f',' '), HB_TAG('C','P','P',' ')}, /* Baba Malay -> Creoles */ + {HB_TAG('m','b','n',' '), HB_TAG_NONE }, /* Macaguán != Mbundu */ +/*{HB_TAG('m','b','o',' '), HB_TAG('M','B','O',' ')},*/ /* Mbo (Cameroon) */ + {HB_TAG('m','c','h',' '), HB_TAG_NONE }, /* Maquiritari != Manchu */ + {HB_TAG('m','c','m',' '), HB_TAG('C','P','P',' ')}, /* Malaccan Creole Portuguese -> Creoles */ + {HB_TAG('m','c','r',' '), HB_TAG_NONE }, /* Menya != Moose Cree */ + {HB_TAG('m','c','t',' '), HB_TAG('B','T','I',' ')}, /* Mengisa -> Beti */ + {HB_TAG('m','d','e',' '), HB_TAG_NONE }, /* Maba (Chad) != Mende */ + {HB_TAG('m','d','f',' '), HB_TAG('M','O','K',' ')}, /* Moksha */ +/*{HB_TAG('m','d','r',' '), HB_TAG('M','D','R',' ')},*/ /* Mandar */ + {HB_TAG('m','d','y',' '), HB_TAG('M','L','E',' ')}, /* Male (Ethiopia) */ + {HB_TAG('m','e','n',' '), HB_TAG('M','D','E',' ')}, /* Mende (Sierra Leone) */ + {HB_TAG('m','e','o',' '), HB_TAG('M','L','Y',' ')}, /* Kedah Malay -> Malay */ +/*{HB_TAG('m','e','r',' '), HB_TAG('M','E','R',' ')},*/ /* Meru */ + {HB_TAG('m','f','a',' '), HB_TAG('M','F','A',' ')}, /* Pattani Malay */ + {HB_TAG('m','f','a',' '), HB_TAG('M','L','Y',' ')}, /* Pattani Malay -> Malay */ + {HB_TAG('m','f','b',' '), HB_TAG('M','L','Y',' ')}, /* Bangka -> Malay */ + {HB_TAG('m','f','e',' '), HB_TAG('M','F','E',' ')}, /* Morisyen */ + {HB_TAG('m','f','e',' '), HB_TAG('C','P','P',' ')}, /* Morisyen -> Creoles */ + {HB_TAG('m','f','p',' '), HB_TAG('C','P','P',' ')}, /* Makassar Malay -> Creoles */ + {HB_TAG('m','h','c',' '), HB_TAG('M','Y','N',' ')}, /* Mocho -> Mayan */ + {HB_TAG('m','h','r',' '), HB_TAG('L','M','A',' ')}, /* Eastern Mari -> Low Mari */ + {HB_TAG('m','h','v',' '), HB_TAG('A','R','K',' ')}, /* Arakanese (retired code) -> Rakhine */ + {HB_TAG('m','i','n',' '), HB_TAG('M','I','N',' ')}, /* Minangkabau */ + {HB_TAG('m','i','n',' '), HB_TAG('M','L','Y',' ')}, /* Minangkabau -> Malay */ + {HB_TAG('m','i','z',' '), HB_TAG_NONE }, /* Coatzospan Mixtec != Mizo */ + {HB_TAG('m','k','n',' '), HB_TAG('C','P','P',' ')}, /* Kupang Malay -> Creoles */ + {HB_TAG('m','k','r',' '), HB_TAG_NONE }, /* Malas != Makasar */ + {HB_TAG('m','k','u',' '), HB_TAG('M','N','K',' ')}, /* Konyanka Maninka -> Maninka */ +/*{HB_TAG('m','k','w',' '), HB_TAG('M','K','W',' ')},*/ /* Kituba (Congo) */ + {HB_TAG('m','l','e',' '), HB_TAG_NONE }, /* Manambu != Male */ + {HB_TAG('m','l','n',' '), HB_TAG_NONE }, /* Malango != Malinke */ + {HB_TAG('m','l','q',' '), HB_TAG('M','L','N',' ')}, /* Western Maninkakan -> Malinke */ + {HB_TAG('m','l','q',' '), HB_TAG('M','N','K',' ')}, /* Western Maninkakan -> Maninka */ + {HB_TAG('m','l','r',' '), HB_TAG_NONE }, /* Vame != Malayalam Reformed */ + {HB_TAG('m','m','r',' '), HB_TAG('H','M','N',' ')}, /* Western Xiangxi Miao -> Hmong */ + {HB_TAG('m','n','c',' '), HB_TAG('M','C','H',' ')}, /* Manchu */ + {HB_TAG('m','n','d',' '), HB_TAG_NONE }, /* Mondé != Mandinka */ + {HB_TAG('m','n','g',' '), HB_TAG_NONE }, /* Eastern Mnong != Mongolian */ + {HB_TAG('m','n','h',' '), HB_TAG('B','A','D','0')}, /* Mono (Democratic Republic of Congo) -> Banda */ +/*{HB_TAG('m','n','i',' '), HB_TAG('M','N','I',' ')},*/ /* Manipuri */ + {HB_TAG('m','n','k',' '), HB_TAG('M','N','D',' ')}, /* Mandinka */ + {HB_TAG('m','n','k',' '), HB_TAG('M','N','K',' ')}, /* Mandinka -> Maninka */ + {HB_TAG('m','n','p',' '), HB_TAG('Z','H','S',' ')}, /* Min Bei Chinese -> Chinese, Simplified */ + {HB_TAG('m','n','s',' '), HB_TAG('M','A','N',' ')}, /* Mansi */ + {HB_TAG('m','n','w',' '), HB_TAG('M','O','N',' ')}, /* Mon */ + {HB_TAG('m','n','w',' '), HB_TAG('M','O','N','T')}, /* Mon -> Thailand Mon */ + {HB_TAG('m','n','x',' '), HB_TAG_NONE }, /* Manikion != Manx */ + {HB_TAG('m','o','d',' '), HB_TAG('C','P','P',' ')}, /* Mobilian -> Creoles */ +/*{HB_TAG('m','o','h',' '), HB_TAG('M','O','H',' ')},*/ /* Mohawk */ + {HB_TAG('m','o','k',' '), HB_TAG_NONE }, /* Morori != Moksha */ + {HB_TAG('m','o','p',' '), HB_TAG('M','Y','N',' ')}, /* Mopán Maya -> Mayan */ + {HB_TAG('m','o','r',' '), HB_TAG_NONE }, /* Moro != Moroccan */ +/*{HB_TAG('m','o','s',' '), HB_TAG('M','O','S',' ')},*/ /* Mossi */ + {HB_TAG('m','p','e',' '), HB_TAG('M','A','J',' ')}, /* Majang */ + {HB_TAG('m','q','g',' '), HB_TAG('M','L','Y',' ')}, /* Kota Bangun Kutai Malay -> Malay */ + {HB_TAG('m','r','h',' '), HB_TAG('Q','I','N',' ')}, /* Mara Chin -> Chin */ + {HB_TAG('m','r','j',' '), HB_TAG('H','M','A',' ')}, /* Western Mari -> High Mari */ + {HB_TAG('m','s','c',' '), HB_TAG('M','N','K',' ')}, /* Sankaran Maninka -> Maninka */ + {HB_TAG('m','s','h',' '), HB_TAG('M','L','G',' ')}, /* Masikoro Malagasy -> Malagasy */ + {HB_TAG('m','s','i',' '), HB_TAG('M','L','Y',' ')}, /* Sabah Malay -> Malay */ + {HB_TAG('m','s','i',' '), HB_TAG('C','P','P',' ')}, /* Sabah Malay -> Creoles */ + {HB_TAG('m','t','h',' '), HB_TAG_NONE }, /* Munggui != Maithili */ + {HB_TAG('m','t','r',' '), HB_TAG('M','A','W',' ')}, /* Mewari -> Marwari */ + {HB_TAG('m','t','s',' '), HB_TAG_NONE }, /* Yora != Maltese */ + {HB_TAG('m','u','d',' '), HB_TAG('C','P','P',' ')}, /* Mednyj Aleut -> Creoles */ + {HB_TAG('m','u','i',' '), HB_TAG('M','L','Y',' ')}, /* Musi -> Malay */ + {HB_TAG('m','u','n',' '), HB_TAG_NONE }, /* Munda [collection] != Mundari */ + {HB_TAG('m','u','p',' '), HB_TAG('R','A','J',' ')}, /* Malvi -> Rajasthani */ + {HB_TAG('m','u','q',' '), HB_TAG('H','M','N',' ')}, /* Eastern Xiangxi Miao -> Hmong */ +/*{HB_TAG('m','u','s',' '), HB_TAG('M','U','S',' ')},*/ /* Creek -> Muscogee */ + {HB_TAG('m','v','b',' '), HB_TAG('A','T','H',' ')}, /* Mattole -> Athapaskan */ + {HB_TAG('m','v','e',' '), HB_TAG('M','A','W',' ')}, /* Marwari (Pakistan) */ + {HB_TAG('m','v','f',' '), HB_TAG('M','N','G',' ')}, /* Peripheral Mongolian -> Mongolian */ + {HB_TAG('m','w','k',' '), HB_TAG('M','N','K',' ')}, /* Kita Maninkakan -> Maninka */ +/*{HB_TAG('m','w','l',' '), HB_TAG('M','W','L',' ')},*/ /* Mirandese */ + {HB_TAG('m','w','q',' '), HB_TAG('Q','I','N',' ')}, /* Mün Chin -> Chin */ + {HB_TAG('m','w','r',' '), HB_TAG('M','A','W',' ')}, /* Marwari [macrolanguage] */ + {HB_TAG('m','w','w',' '), HB_TAG('M','W','W',' ')}, /* Hmong Daw */ + {HB_TAG('m','w','w',' '), HB_TAG('H','M','N',' ')}, /* Hmong Daw -> Hmong */ + {HB_TAG('m','y','m',' '), HB_TAG('M','E','N',' ')}, /* Me’en */ +/*{HB_TAG('m','y','n',' '), HB_TAG('M','Y','N',' ')},*/ /* Mayan [collection] */ + {HB_TAG('m','y','q',' '), HB_TAG('M','N','K',' ')}, /* Forest Maninka (retired code) -> Maninka */ + {HB_TAG('m','y','v',' '), HB_TAG('E','R','Z',' ')}, /* Erzya */ + {HB_TAG('m','z','b',' '), HB_TAG('B','B','R',' ')}, /* Tumzabt -> Berber */ +/*{HB_TAG('m','z','n',' '), HB_TAG('M','Z','N',' ')},*/ /* Mazanderani */ + {HB_TAG('m','z','s',' '), HB_TAG('C','P','P',' ')}, /* Macanese -> Creoles */ + {HB_TAG('n','a','g',' '), HB_TAG('N','A','G',' ')}, /* Naga Pidgin -> Naga-Assamese */ + {HB_TAG('n','a','g',' '), HB_TAG('C','P','P',' ')}, /* Naga Pidgin -> Creoles */ +/*{HB_TAG('n','a','h',' '), HB_TAG('N','A','H',' ')},*/ /* Nahuatl [collection] */ + {HB_TAG('n','a','n',' '), HB_TAG('Z','H','S',' ')}, /* Min Nan Chinese -> Chinese, Simplified */ +/*{HB_TAG('n','a','p',' '), HB_TAG('N','A','P',' ')},*/ /* Neapolitan */ + {HB_TAG('n','a','s',' '), HB_TAG_NONE }, /* Naasioi != Naskapi */ + {HB_TAG('n','a','z',' '), HB_TAG('N','A','H',' ')}, /* Coatepec Nahuatl -> Nahuatl */ + {HB_TAG('n','c','h',' '), HB_TAG('N','A','H',' ')}, /* Central Huasteca Nahuatl -> Nahuatl */ + {HB_TAG('n','c','i',' '), HB_TAG('N','A','H',' ')}, /* Classical Nahuatl -> Nahuatl */ + {HB_TAG('n','c','j',' '), HB_TAG('N','A','H',' ')}, /* Northern Puebla Nahuatl -> Nahuatl */ + {HB_TAG('n','c','l',' '), HB_TAG('N','A','H',' ')}, /* Michoacán Nahuatl -> Nahuatl */ + {HB_TAG('n','c','r',' '), HB_TAG_NONE }, /* Ncane != N-Cree */ + {HB_TAG('n','c','x',' '), HB_TAG('N','A','H',' ')}, /* Central Puebla Nahuatl -> Nahuatl */ + {HB_TAG('n','d','b',' '), HB_TAG_NONE }, /* Kenswei Nsei != Ndebele */ +/*{HB_TAG('n','d','c',' '), HB_TAG('N','D','C',' ')},*/ /* Ndau */ + {HB_TAG('n','d','g',' '), HB_TAG_NONE }, /* Ndengereko != Ndonga */ +/*{HB_TAG('n','d','s',' '), HB_TAG('N','D','S',' ')},*/ /* Low Saxon */ + {HB_TAG('n','e','f',' '), HB_TAG('C','P','P',' ')}, /* Nefamese -> Creoles */ +/*{HB_TAG('n','e','w',' '), HB_TAG('N','E','W',' ')},*/ /* Newari */ +/*{HB_TAG('n','g','a',' '), HB_TAG('N','G','A',' ')},*/ /* Ngbaka */ + {HB_TAG('n','g','l',' '), HB_TAG('L','M','W',' ')}, /* Lomwe */ + {HB_TAG('n','g','m',' '), HB_TAG('C','P','P',' ')}, /* Ngatik Men's Creole -> Creoles */ + {HB_TAG('n','g','o',' '), HB_TAG('S','X','T',' ')}, /* Ngoni (retired code) -> Sutu */ + {HB_TAG('n','g','r',' '), HB_TAG_NONE }, /* Engdewu != Nagari */ + {HB_TAG('n','g','u',' '), HB_TAG('N','A','H',' ')}, /* Guerrero Nahuatl -> Nahuatl */ + {HB_TAG('n','h','c',' '), HB_TAG('N','A','H',' ')}, /* Tabasco Nahuatl -> Nahuatl */ + {HB_TAG('n','h','d',' '), HB_TAG('G','U','A',' ')}, /* Chiripá -> Guarani */ + {HB_TAG('n','h','e',' '), HB_TAG('N','A','H',' ')}, /* Eastern Huasteca Nahuatl -> Nahuatl */ + {HB_TAG('n','h','g',' '), HB_TAG('N','A','H',' ')}, /* Tetelcingo Nahuatl -> Nahuatl */ + {HB_TAG('n','h','i',' '), HB_TAG('N','A','H',' ')}, /* Zacatlán-Ahuacatlán-Tepetzintla Nahuatl -> Nahuatl */ + {HB_TAG('n','h','k',' '), HB_TAG('N','A','H',' ')}, /* Isthmus-Cosoleacaque Nahuatl -> Nahuatl */ + {HB_TAG('n','h','m',' '), HB_TAG('N','A','H',' ')}, /* Morelos Nahuatl -> Nahuatl */ + {HB_TAG('n','h','n',' '), HB_TAG('N','A','H',' ')}, /* Central Nahuatl -> Nahuatl */ + {HB_TAG('n','h','p',' '), HB_TAG('N','A','H',' ')}, /* Isthmus-Pajapan Nahuatl -> Nahuatl */ + {HB_TAG('n','h','q',' '), HB_TAG('N','A','H',' ')}, /* Huaxcaleca Nahuatl -> Nahuatl */ + {HB_TAG('n','h','t',' '), HB_TAG('N','A','H',' ')}, /* Ometepec Nahuatl -> Nahuatl */ + {HB_TAG('n','h','v',' '), HB_TAG('N','A','H',' ')}, /* Temascaltepec Nahuatl -> Nahuatl */ + {HB_TAG('n','h','w',' '), HB_TAG('N','A','H',' ')}, /* Western Huasteca Nahuatl -> Nahuatl */ + {HB_TAG('n','h','x',' '), HB_TAG('N','A','H',' ')}, /* Isthmus-Mecayapan Nahuatl -> Nahuatl */ + {HB_TAG('n','h','y',' '), HB_TAG('N','A','H',' ')}, /* Northern Oaxaca Nahuatl -> Nahuatl */ + {HB_TAG('n','h','z',' '), HB_TAG('N','A','H',' ')}, /* Santa María La Alta Nahuatl -> Nahuatl */ + {HB_TAG('n','i','q',' '), HB_TAG('K','A','L',' ')}, /* Nandi -> Kalenjin */ + {HB_TAG('n','i','s',' '), HB_TAG_NONE }, /* Nimi != Nisi */ +/*{HB_TAG('n','i','u',' '), HB_TAG('N','I','U',' ')},*/ /* Niuean */ + {HB_TAG('n','i','v',' '), HB_TAG('G','I','L',' ')}, /* Gilyak */ + {HB_TAG('n','j','t',' '), HB_TAG('C','P','P',' ')}, /* Ndyuka-Trio Pidgin -> Creoles */ + {HB_TAG('n','j','z',' '), HB_TAG('N','I','S',' ')}, /* Nyishi -> Nisi */ + {HB_TAG('n','k','o',' '), HB_TAG_NONE }, /* Nkonya != N’Ko */ + {HB_TAG('n','k','x',' '), HB_TAG('I','J','O',' ')}, /* Nkoroo -> Ijo */ + {HB_TAG('n','l','a',' '), HB_TAG('B','M','L',' ')}, /* Ngombale -> Bamileke */ + {HB_TAG('n','l','e',' '), HB_TAG('L','U','H',' ')}, /* East Nyala -> Luyia */ + {HB_TAG('n','l','n',' '), HB_TAG('N','A','H',' ')}, /* Durango Nahuatl (retired code) -> Nahuatl */ + {HB_TAG('n','l','v',' '), HB_TAG('N','A','H',' ')}, /* Orizaba Nahuatl -> Nahuatl */ + {HB_TAG('n','n','h',' '), HB_TAG('B','M','L',' ')}, /* Ngiemboon -> Bamileke */ + {HB_TAG('n','n','z',' '), HB_TAG('B','M','L',' ')}, /* Nda'nda' -> Bamileke */ + {HB_TAG('n','o','d',' '), HB_TAG('N','T','A',' ')}, /* Northern Thai -> Northern Tai */ +/*{HB_TAG('n','o','e',' '), HB_TAG('N','O','E',' ')},*/ /* Nimadi */ +/*{HB_TAG('n','o','g',' '), HB_TAG('N','O','G',' ')},*/ /* Nogai */ +/*{HB_TAG('n','o','v',' '), HB_TAG('N','O','V',' ')},*/ /* Novial */ + {HB_TAG('n','p','i',' '), HB_TAG('N','E','P',' ')}, /* Nepali */ + {HB_TAG('n','p','l',' '), HB_TAG('N','A','H',' ')}, /* Southeastern Puebla Nahuatl -> Nahuatl */ + {HB_TAG('n','q','o',' '), HB_TAG('N','K','O',' ')}, /* N’Ko */ + {HB_TAG('n','s','k',' '), HB_TAG('N','A','S',' ')}, /* Naskapi */ + {HB_TAG('n','s','m',' '), HB_TAG_NONE }, /* Sumi Naga != Northern Sami */ +/*{HB_TAG('n','s','o',' '), HB_TAG('N','S','O',' ')},*/ /* Northern Sotho */ + {HB_TAG('n','s','u',' '), HB_TAG('N','A','H',' ')}, /* Sierra Negra Nahuatl -> Nahuatl */ + {HB_TAG('n','t','o',' '), HB_TAG_NONE }, /* Ntomba != Esperanto */ + {HB_TAG('n','u','e',' '), HB_TAG('B','A','D','0')}, /* Ngundu -> Banda */ + {HB_TAG('n','u','u',' '), HB_TAG('B','A','D','0')}, /* Ngbundu -> Banda */ + {HB_TAG('n','u','z',' '), HB_TAG('N','A','H',' ')}, /* Tlamacazapa Nahuatl -> Nahuatl */ + {HB_TAG('n','w','e',' '), HB_TAG('B','M','L',' ')}, /* Ngwe -> Bamileke */ + {HB_TAG('n','y','d',' '), HB_TAG('L','U','H',' ')}, /* Nyore -> Luyia */ +/*{HB_TAG('n','y','m',' '), HB_TAG('N','Y','M',' ')},*/ /* Nyamwezi */ + {HB_TAG('n','y','n',' '), HB_TAG('N','K','L',' ')}, /* Nyankole */ +/*{HB_TAG('n','z','a',' '), HB_TAG('N','Z','A',' ')},*/ /* Tigon Mbembe -> Mbembe Tigon */ +/*{HB_TAG('o','j','b',' '), HB_TAG('O','J','B',' ')},*/ /* Northwestern Ojibwa -> Ojibway */ + {HB_TAG('o','j','c',' '), HB_TAG('O','J','B',' ')}, /* Central Ojibwa -> Ojibway */ + {HB_TAG('o','j','g',' '), HB_TAG('O','J','B',' ')}, /* Eastern Ojibwa -> Ojibway */ + {HB_TAG('o','j','s',' '), HB_TAG('O','C','R',' ')}, /* Severn Ojibwa -> Oji-Cree */ + {HB_TAG('o','j','s',' '), HB_TAG('O','J','B',' ')}, /* Severn Ojibwa -> Ojibway */ + {HB_TAG('o','j','w',' '), HB_TAG('O','J','B',' ')}, /* Western Ojibwa -> Ojibway */ + {HB_TAG('o','k','d',' '), HB_TAG('I','J','O',' ')}, /* Okodia -> Ijo */ + {HB_TAG('o','k','i',' '), HB_TAG('K','A','L',' ')}, /* Okiek -> Kalenjin */ + {HB_TAG('o','k','m',' '), HB_TAG('K','O','H',' ')}, /* Middle Korean (10th-16th cent.) -> Korean Old Hangul */ + {HB_TAG('o','k','r',' '), HB_TAG('I','J','O',' ')}, /* Kirike -> Ijo */ + {HB_TAG('o','n','x',' '), HB_TAG('C','P','P',' ')}, /* Onin Based Pidgin -> Creoles */ + {HB_TAG('o','o','r',' '), HB_TAG('C','P','P',' ')}, /* Oorlams -> Creoles */ + {HB_TAG('o','r','c',' '), HB_TAG('O','R','O',' ')}, /* Orma -> Oromo */ + {HB_TAG('o','r','n',' '), HB_TAG('M','L','Y',' ')}, /* Orang Kanaq -> Malay */ + {HB_TAG('o','r','o',' '), HB_TAG_NONE }, /* Orokolo != Oromo */ + {HB_TAG('o','r','r',' '), HB_TAG('I','J','O',' ')}, /* Oruma -> Ijo */ + {HB_TAG('o','r','s',' '), HB_TAG('M','L','Y',' ')}, /* Orang Seletar -> Malay */ + {HB_TAG('o','r','y',' '), HB_TAG('O','R','I',' ')}, /* Odia (formerly Oriya) */ + {HB_TAG('o','t','w',' '), HB_TAG('O','J','B',' ')}, /* Ottawa -> Ojibway */ + {HB_TAG('o','u','a',' '), HB_TAG('B','B','R',' ')}, /* Tagargrent -> Berber */ + {HB_TAG('p','a','a',' '), HB_TAG_NONE }, /* Papuan [collection] != Palestinian Aramaic */ +/*{HB_TAG('p','a','g',' '), HB_TAG('P','A','G',' ')},*/ /* Pangasinan */ + {HB_TAG('p','a','l',' '), HB_TAG_NONE }, /* Pahlavi != Pali */ +/*{HB_TAG('p','a','m',' '), HB_TAG('P','A','M',' ')},*/ /* Pampanga -> Pampangan */ + {HB_TAG('p','a','p',' '), HB_TAG('P','A','P','0')}, /* Papiamento -> Papiamentu */ + {HB_TAG('p','a','p',' '), HB_TAG('C','P','P',' ')}, /* Papiamento -> Creoles */ + {HB_TAG('p','a','s',' '), HB_TAG_NONE }, /* Papasena != Pashto */ +/*{HB_TAG('p','a','u',' '), HB_TAG('P','A','U',' ')},*/ /* Palauan */ + {HB_TAG('p','b','t',' '), HB_TAG('P','A','S',' ')}, /* Southern Pashto -> Pashto */ + {HB_TAG('p','b','u',' '), HB_TAG('P','A','S',' ')}, /* Northern Pashto -> Pashto */ +/*{HB_TAG('p','c','c',' '), HB_TAG('P','C','C',' ')},*/ /* Bouyei */ +/*{HB_TAG('p','c','d',' '), HB_TAG('P','C','D',' ')},*/ /* Picard */ + {HB_TAG('p','c','e',' '), HB_TAG('P','L','G',' ')}, /* Ruching Palaung -> Palaung */ + {HB_TAG('p','c','k',' '), HB_TAG('Q','I','N',' ')}, /* Paite Chin -> Chin */ + {HB_TAG('p','c','m',' '), HB_TAG('C','P','P',' ')}, /* Nigerian Pidgin -> Creoles */ +/*{HB_TAG('p','d','c',' '), HB_TAG('P','D','C',' ')},*/ /* Pennsylvania German */ + {HB_TAG('p','d','u',' '), HB_TAG('K','R','N',' ')}, /* Kayan -> Karen */ + {HB_TAG('p','e','a',' '), HB_TAG('C','P','P',' ')}, /* Peranakan Indonesian -> Creoles */ + {HB_TAG('p','e','l',' '), HB_TAG('M','L','Y',' ')}, /* Pekal -> Malay */ + {HB_TAG('p','e','s',' '), HB_TAG('F','A','R',' ')}, /* Iranian Persian -> Persian */ + {HB_TAG('p','e','y',' '), HB_TAG('C','P','P',' ')}, /* Petjo -> Creoles */ + {HB_TAG('p','g','a',' '), HB_TAG('A','R','A',' ')}, /* Sudanese Creole Arabic -> Arabic */ + {HB_TAG('p','g','a',' '), HB_TAG('C','P','P',' ')}, /* Sudanese Creole Arabic -> Creoles */ +/*{HB_TAG('p','h','k',' '), HB_TAG('P','H','K',' ')},*/ /* Phake */ + {HB_TAG('p','i','h',' '), HB_TAG('P','I','H',' ')}, /* Pitcairn-Norfolk -> Norfolk */ + {HB_TAG('p','i','h',' '), HB_TAG('C','P','P',' ')}, /* Pitcairn-Norfolk -> Creoles */ + {HB_TAG('p','i','l',' '), HB_TAG_NONE }, /* Yom != Filipino */ + {HB_TAG('p','i','s',' '), HB_TAG('C','P','P',' ')}, /* Pijin -> Creoles */ + {HB_TAG('p','k','h',' '), HB_TAG('Q','I','N',' ')}, /* Pankhu -> Chin */ + {HB_TAG('p','k','o',' '), HB_TAG('K','A','L',' ')}, /* Pökoot -> Kalenjin */ + {HB_TAG('p','l','g',' '), HB_TAG_NONE }, /* Pilagá != Palaung */ + {HB_TAG('p','l','k',' '), HB_TAG_NONE }, /* Kohistani Shina != Polish */ + {HB_TAG('p','l','l',' '), HB_TAG('P','L','G',' ')}, /* Shwe Palaung -> Palaung */ + {HB_TAG('p','l','n',' '), HB_TAG('C','P','P',' ')}, /* Palenquero -> Creoles */ + {HB_TAG('p','l','p',' '), HB_TAG('P','A','P',' ')}, /* Palpa (retired code) */ + {HB_TAG('p','l','t',' '), HB_TAG('M','L','G',' ')}, /* Plateau Malagasy -> Malagasy */ + {HB_TAG('p','m','l',' '), HB_TAG('C','P','P',' ')}, /* Lingua Franca -> Creoles */ +/*{HB_TAG('p','m','s',' '), HB_TAG('P','M','S',' ')},*/ /* Piemontese */ + {HB_TAG('p','m','y',' '), HB_TAG('C','P','P',' ')}, /* Papuan Malay -> Creoles */ +/*{HB_TAG('p','n','b',' '), HB_TAG('P','N','B',' ')},*/ /* Western Panjabi */ + {HB_TAG('p','o','c',' '), HB_TAG('M','Y','N',' ')}, /* Poqomam -> Mayan */ + {HB_TAG('p','o','h',' '), HB_TAG('P','O','H',' ')}, /* Poqomchi' -> Pocomchi */ + {HB_TAG('p','o','h',' '), HB_TAG('M','Y','N',' ')}, /* Poqomchi' -> Mayan */ +/*{HB_TAG('p','o','n',' '), HB_TAG('P','O','N',' ')},*/ /* Pohnpeian */ + {HB_TAG('p','o','v',' '), HB_TAG('C','P','P',' ')}, /* Upper Guinea Crioulo -> Creoles */ + {HB_TAG('p','p','a',' '), HB_TAG('B','A','G',' ')}, /* Pao (retired code) -> Baghelkhandi */ + {HB_TAG('p','r','e',' '), HB_TAG('C','P','P',' ')}, /* Principense -> Creoles */ +/*{HB_TAG('p','r','o',' '), HB_TAG('P','R','O',' ')},*/ /* Old Provençal (to 1500) -> Provençal / Old Provençal */ + {HB_TAG('p','r','s',' '), HB_TAG('D','R','I',' ')}, /* Dari */ + {HB_TAG('p','r','s',' '), HB_TAG('F','A','R',' ')}, /* Dari -> Persian */ + {HB_TAG('p','s','e',' '), HB_TAG('M','L','Y',' ')}, /* Central Malay -> Malay */ + {HB_TAG('p','s','t',' '), HB_TAG('P','A','S',' ')}, /* Central Pashto -> Pashto */ + {HB_TAG('p','u','b',' '), HB_TAG('Q','I','N',' ')}, /* Purum -> Chin */ + {HB_TAG('p','u','z',' '), HB_TAG('Q','I','N',' ')}, /* Purum Naga (retired code) -> Chin */ + {HB_TAG('p','w','o',' '), HB_TAG('P','W','O',' ')}, /* Pwo Western Karen -> Western Pwo Karen */ + {HB_TAG('p','w','o',' '), HB_TAG('K','R','N',' ')}, /* Pwo Western Karen -> Karen */ + {HB_TAG('p','w','w',' '), HB_TAG('K','R','N',' ')}, /* Pwo Northern Karen -> Karen */ + {HB_TAG('q','u','b',' '), HB_TAG('Q','W','H',' ')}, /* Huallaga Huánuco Quechua -> Quechua (Peru) */ + {HB_TAG('q','u','b',' '), HB_TAG('Q','U','Z',' ')}, /* Huallaga Huánuco Quechua -> Quechua */ + {HB_TAG('q','u','c',' '), HB_TAG('Q','U','C',' ')}, /* K’iche’ */ + {HB_TAG('q','u','c',' '), HB_TAG('M','Y','N',' ')}, /* K'iche' -> Mayan */ + {HB_TAG('q','u','d',' '), HB_TAG('Q','V','I',' ')}, /* Calderón Highland Quichua -> Quechua (Ecuador) */ + {HB_TAG('q','u','d',' '), HB_TAG('Q','U','Z',' ')}, /* Calderón Highland Quichua -> Quechua */ + {HB_TAG('q','u','f',' '), HB_TAG('Q','U','Z',' ')}, /* Lambayeque Quechua -> Quechua */ + {HB_TAG('q','u','g',' '), HB_TAG('Q','V','I',' ')}, /* Chimborazo Highland Quichua -> Quechua (Ecuador) */ + {HB_TAG('q','u','g',' '), HB_TAG('Q','U','Z',' ')}, /* Chimborazo Highland Quichua -> Quechua */ + {HB_TAG('q','u','h',' '), HB_TAG('Q','U','H',' ')}, /* South Bolivian Quechua -> Quechua (Bolivia) */ + {HB_TAG('q','u','h',' '), HB_TAG('Q','U','Z',' ')}, /* South Bolivian Quechua -> Quechua */ + {HB_TAG('q','u','k',' '), HB_TAG('Q','U','Z',' ')}, /* Chachapoyas Quechua -> Quechua */ + {HB_TAG('q','u','l',' '), HB_TAG('Q','U','H',' ')}, /* North Bolivian Quechua -> Quechua (Bolivia) */ + {HB_TAG('q','u','l',' '), HB_TAG('Q','U','Z',' ')}, /* North Bolivian Quechua -> Quechua */ + {HB_TAG('q','u','m',' '), HB_TAG('M','Y','N',' ')}, /* Sipacapense -> Mayan */ + {HB_TAG('q','u','p',' '), HB_TAG('Q','V','I',' ')}, /* Southern Pastaza Quechua -> Quechua (Ecuador) */ + {HB_TAG('q','u','p',' '), HB_TAG('Q','U','Z',' ')}, /* Southern Pastaza Quechua -> Quechua */ + {HB_TAG('q','u','r',' '), HB_TAG('Q','W','H',' ')}, /* Yanahuanca Pasco Quechua -> Quechua (Peru) */ + {HB_TAG('q','u','r',' '), HB_TAG('Q','U','Z',' ')}, /* Yanahuanca Pasco Quechua -> Quechua */ + {HB_TAG('q','u','s',' '), HB_TAG('Q','U','H',' ')}, /* Santiago del Estero Quichua -> Quechua (Bolivia) */ + {HB_TAG('q','u','s',' '), HB_TAG('Q','U','Z',' ')}, /* Santiago del Estero Quichua -> Quechua */ + {HB_TAG('q','u','v',' '), HB_TAG('M','Y','N',' ')}, /* Sacapulteco -> Mayan */ + {HB_TAG('q','u','w',' '), HB_TAG('Q','V','I',' ')}, /* Tena Lowland Quichua -> Quechua (Ecuador) */ + {HB_TAG('q','u','w',' '), HB_TAG('Q','U','Z',' ')}, /* Tena Lowland Quichua -> Quechua */ + {HB_TAG('q','u','x',' '), HB_TAG('Q','W','H',' ')}, /* Yauyos Quechua -> Quechua (Peru) */ + {HB_TAG('q','u','x',' '), HB_TAG('Q','U','Z',' ')}, /* Yauyos Quechua -> Quechua */ + {HB_TAG('q','u','y',' '), HB_TAG('Q','U','Z',' ')}, /* Ayacucho Quechua -> Quechua */ +/*{HB_TAG('q','u','z',' '), HB_TAG('Q','U','Z',' ')},*/ /* Cusco Quechua -> Quechua */ + {HB_TAG('q','v','a',' '), HB_TAG('Q','W','H',' ')}, /* Ambo-Pasco Quechua -> Quechua (Peru) */ + {HB_TAG('q','v','a',' '), HB_TAG('Q','U','Z',' ')}, /* Ambo-Pasco Quechua -> Quechua */ + {HB_TAG('q','v','c',' '), HB_TAG('Q','U','Z',' ')}, /* Cajamarca Quechua -> Quechua */ + {HB_TAG('q','v','e',' '), HB_TAG('Q','U','Z',' ')}, /* Eastern Apurímac Quechua -> Quechua */ + {HB_TAG('q','v','h',' '), HB_TAG('Q','W','H',' ')}, /* Huamalíes-Dos de Mayo Huánuco Quechua -> Quechua (Peru) */ + {HB_TAG('q','v','h',' '), HB_TAG('Q','U','Z',' ')}, /* Huamalíes-Dos de Mayo Huánuco Quechua -> Quechua */ + {HB_TAG('q','v','i',' '), HB_TAG('Q','V','I',' ')}, /* Imbabura Highland Quichua -> Quechua (Ecuador) */ + {HB_TAG('q','v','i',' '), HB_TAG('Q','U','Z',' ')}, /* Imbabura Highland Quichua -> Quechua */ + {HB_TAG('q','v','j',' '), HB_TAG('Q','V','I',' ')}, /* Loja Highland Quichua -> Quechua (Ecuador) */ + {HB_TAG('q','v','j',' '), HB_TAG('Q','U','Z',' ')}, /* Loja Highland Quichua -> Quechua */ + {HB_TAG('q','v','l',' '), HB_TAG('Q','W','H',' ')}, /* Cajatambo North Lima Quechua -> Quechua (Peru) */ + {HB_TAG('q','v','l',' '), HB_TAG('Q','U','Z',' ')}, /* Cajatambo North Lima Quechua -> Quechua */ + {HB_TAG('q','v','m',' '), HB_TAG('Q','W','H',' ')}, /* Margos-Yarowilca-Lauricocha Quechua -> Quechua (Peru) */ + {HB_TAG('q','v','m',' '), HB_TAG('Q','U','Z',' ')}, /* Margos-Yarowilca-Lauricocha Quechua -> Quechua */ + {HB_TAG('q','v','n',' '), HB_TAG('Q','W','H',' ')}, /* North Junín Quechua -> Quechua (Peru) */ + {HB_TAG('q','v','n',' '), HB_TAG('Q','U','Z',' ')}, /* North Junín Quechua -> Quechua */ + {HB_TAG('q','v','o',' '), HB_TAG('Q','V','I',' ')}, /* Napo Lowland Quechua -> Quechua (Ecuador) */ + {HB_TAG('q','v','o',' '), HB_TAG('Q','U','Z',' ')}, /* Napo Lowland Quechua -> Quechua */ + {HB_TAG('q','v','p',' '), HB_TAG('Q','W','H',' ')}, /* Pacaraos Quechua -> Quechua (Peru) */ + {HB_TAG('q','v','p',' '), HB_TAG('Q','U','Z',' ')}, /* Pacaraos Quechua -> Quechua */ + {HB_TAG('q','v','s',' '), HB_TAG('Q','U','Z',' ')}, /* San Martín Quechua -> Quechua */ + {HB_TAG('q','v','w',' '), HB_TAG('Q','W','H',' ')}, /* Huaylla Wanca Quechua -> Quechua (Peru) */ + {HB_TAG('q','v','w',' '), HB_TAG('Q','U','Z',' ')}, /* Huaylla Wanca Quechua -> Quechua */ + {HB_TAG('q','v','z',' '), HB_TAG('Q','V','I',' ')}, /* Northern Pastaza Quichua -> Quechua (Ecuador) */ + {HB_TAG('q','v','z',' '), HB_TAG('Q','U','Z',' ')}, /* Northern Pastaza Quichua -> Quechua */ + {HB_TAG('q','w','a',' '), HB_TAG('Q','W','H',' ')}, /* Corongo Ancash Quechua -> Quechua (Peru) */ + {HB_TAG('q','w','a',' '), HB_TAG('Q','U','Z',' ')}, /* Corongo Ancash Quechua -> Quechua */ + {HB_TAG('q','w','c',' '), HB_TAG('Q','U','Z',' ')}, /* Classical Quechua -> Quechua */ + {HB_TAG('q','w','h',' '), HB_TAG('Q','W','H',' ')}, /* Huaylas Ancash Quechua -> Quechua (Peru) */ + {HB_TAG('q','w','h',' '), HB_TAG('Q','U','Z',' ')}, /* Huaylas Ancash Quechua -> Quechua */ + {HB_TAG('q','w','s',' '), HB_TAG('Q','W','H',' ')}, /* Sihuas Ancash Quechua -> Quechua (Peru) */ + {HB_TAG('q','w','s',' '), HB_TAG('Q','U','Z',' ')}, /* Sihuas Ancash Quechua -> Quechua */ + {HB_TAG('q','w','t',' '), HB_TAG('A','T','H',' ')}, /* Kwalhioqua-Tlatskanai -> Athapaskan */ + {HB_TAG('q','x','a',' '), HB_TAG('Q','W','H',' ')}, /* Chiquián Ancash Quechua -> Quechua (Peru) */ + {HB_TAG('q','x','a',' '), HB_TAG('Q','U','Z',' ')}, /* Chiquián Ancash Quechua -> Quechua */ + {HB_TAG('q','x','c',' '), HB_TAG('Q','W','H',' ')}, /* Chincha Quechua -> Quechua (Peru) */ + {HB_TAG('q','x','c',' '), HB_TAG('Q','U','Z',' ')}, /* Chincha Quechua -> Quechua */ + {HB_TAG('q','x','h',' '), HB_TAG('Q','W','H',' ')}, /* Panao Huánuco Quechua -> Quechua (Peru) */ + {HB_TAG('q','x','h',' '), HB_TAG('Q','U','Z',' ')}, /* Panao Huánuco Quechua -> Quechua */ + {HB_TAG('q','x','l',' '), HB_TAG('Q','V','I',' ')}, /* Salasaca Highland Quichua -> Quechua (Ecuador) */ + {HB_TAG('q','x','l',' '), HB_TAG('Q','U','Z',' ')}, /* Salasaca Highland Quichua -> Quechua */ + {HB_TAG('q','x','n',' '), HB_TAG('Q','W','H',' ')}, /* Northern Conchucos Ancash Quechua -> Quechua (Peru) */ + {HB_TAG('q','x','n',' '), HB_TAG('Q','U','Z',' ')}, /* Northern Conchucos Ancash Quechua -> Quechua */ + {HB_TAG('q','x','o',' '), HB_TAG('Q','W','H',' ')}, /* Southern Conchucos Ancash Quechua -> Quechua (Peru) */ + {HB_TAG('q','x','o',' '), HB_TAG('Q','U','Z',' ')}, /* Southern Conchucos Ancash Quechua -> Quechua */ + {HB_TAG('q','x','p',' '), HB_TAG('Q','U','Z',' ')}, /* Puno Quechua -> Quechua */ + {HB_TAG('q','x','r',' '), HB_TAG('Q','V','I',' ')}, /* Cañar Highland Quichua -> Quechua (Ecuador) */ + {HB_TAG('q','x','r',' '), HB_TAG('Q','U','Z',' ')}, /* Cañar Highland Quichua -> Quechua */ + {HB_TAG('q','x','t',' '), HB_TAG('Q','W','H',' ')}, /* Santa Ana de Tusi Pasco Quechua -> Quechua (Peru) */ + {HB_TAG('q','x','t',' '), HB_TAG('Q','U','Z',' ')}, /* Santa Ana de Tusi Pasco Quechua -> Quechua */ + {HB_TAG('q','x','u',' '), HB_TAG('Q','U','Z',' ')}, /* Arequipa-La Unión Quechua -> Quechua */ + {HB_TAG('q','x','w',' '), HB_TAG('Q','W','H',' ')}, /* Jauja Wanca Quechua -> Quechua (Peru) */ + {HB_TAG('q','x','w',' '), HB_TAG('Q','U','Z',' ')}, /* Jauja Wanca Quechua -> Quechua */ + {HB_TAG('r','a','g',' '), HB_TAG('L','U','H',' ')}, /* Logooli -> Luyia */ +/*{HB_TAG('r','a','j',' '), HB_TAG('R','A','J',' ')},*/ /* Rajasthani [macrolanguage] */ + {HB_TAG('r','a','l',' '), HB_TAG('Q','I','N',' ')}, /* Ralte -> Chin */ +/*{HB_TAG('r','a','r',' '), HB_TAG('R','A','R',' ')},*/ /* Rarotongan */ + {HB_TAG('r','b','b',' '), HB_TAG('P','L','G',' ')}, /* Rumai Palaung -> Palaung */ + {HB_TAG('r','b','l',' '), HB_TAG('B','I','K',' ')}, /* Miraya Bikol -> Bikol */ + {HB_TAG('r','c','f',' '), HB_TAG('C','P','P',' ')}, /* Réunion Creole French -> Creoles */ +/*{HB_TAG('r','e','j',' '), HB_TAG('R','E','J',' ')},*/ /* Rejang */ +/*{HB_TAG('r','h','g',' '), HB_TAG('R','H','G',' ')},*/ /* Rohingya */ +/*{HB_TAG('r','i','a',' '), HB_TAG('R','I','A',' ')},*/ /* Riang (India) */ + {HB_TAG('r','i','f',' '), HB_TAG('R','I','F',' ')}, /* Tarifit */ + {HB_TAG('r','i','f',' '), HB_TAG('B','B','R',' ')}, /* Tarifit -> Berber */ +/*{HB_TAG('r','i','t',' '), HB_TAG('R','I','T',' ')},*/ /* Ritharrngu -> Ritarungo */ + {HB_TAG('r','k','i',' '), HB_TAG('A','R','K',' ')}, /* Rakhine */ +/*{HB_TAG('r','k','w',' '), HB_TAG('R','K','W',' ')},*/ /* Arakwal */ + {HB_TAG('r','m','c',' '), HB_TAG('R','O','Y',' ')}, /* Carpathian Romani -> Romany */ + {HB_TAG('r','m','f',' '), HB_TAG('R','O','Y',' ')}, /* Kalo Finnish Romani -> Romany */ + {HB_TAG('r','m','l',' '), HB_TAG('R','O','Y',' ')}, /* Baltic Romani -> Romany */ + {HB_TAG('r','m','n',' '), HB_TAG('R','O','Y',' ')}, /* Balkan Romani -> Romany */ + {HB_TAG('r','m','o',' '), HB_TAG('R','O','Y',' ')}, /* Sinte Romani -> Romany */ + {HB_TAG('r','m','s',' '), HB_TAG_NONE }, /* Romanian Sign Language != Romansh */ + {HB_TAG('r','m','w',' '), HB_TAG('R','O','Y',' ')}, /* Welsh Romani -> Romany */ + {HB_TAG('r','m','y',' '), HB_TAG('R','M','Y',' ')}, /* Vlax Romani */ + {HB_TAG('r','m','y',' '), HB_TAG('R','O','Y',' ')}, /* Vlax Romani -> Romany */ + {HB_TAG('r','m','z',' '), HB_TAG('A','R','K',' ')}, /* Marma -> Rakhine */ + {HB_TAG('r','o','m',' '), HB_TAG('R','O','Y',' ')}, /* Romany [macrolanguage] */ + {HB_TAG('r','o','p',' '), HB_TAG('C','P','P',' ')}, /* Kriol -> Creoles */ + {HB_TAG('r','t','c',' '), HB_TAG('Q','I','N',' ')}, /* Rungtu Chin -> Chin */ +/*{HB_TAG('r','t','m',' '), HB_TAG('R','T','M',' ')},*/ /* Rotuman */ + {HB_TAG('r','u','e',' '), HB_TAG('R','S','Y',' ')}, /* Rusyn */ +/*{HB_TAG('r','u','p',' '), HB_TAG('R','U','P',' ')},*/ /* Aromanian */ + {HB_TAG('r','w','r',' '), HB_TAG('M','A','W',' ')}, /* Marwari (India) */ + {HB_TAG('s','a','d',' '), HB_TAG_NONE }, /* Sandawe != Sadri */ + {HB_TAG('s','a','h',' '), HB_TAG('Y','A','K',' ')}, /* Yakut -> Sakha */ + {HB_TAG('s','a','m',' '), HB_TAG('P','A','A',' ')}, /* Samaritan Aramaic -> Palestinian Aramaic */ +/*{HB_TAG('s','a','s',' '), HB_TAG('S','A','S',' ')},*/ /* Sasak */ +/*{HB_TAG('s','a','t',' '), HB_TAG('S','A','T',' ')},*/ /* Santali */ + {HB_TAG('s','a','y',' '), HB_TAG_NONE }, /* Saya != Sayisi */ + {HB_TAG('s','c','f',' '), HB_TAG('C','P','P',' ')}, /* San Miguel Creole French -> Creoles */ + {HB_TAG('s','c','h',' '), HB_TAG('Q','I','N',' ')}, /* Sakachep -> Chin */ + {HB_TAG('s','c','i',' '), HB_TAG('C','P','P',' ')}, /* Sri Lankan Creole Malay -> Creoles */ + {HB_TAG('s','c','k',' '), HB_TAG('S','A','D',' ')}, /* Sadri */ +/*{HB_TAG('s','c','n',' '), HB_TAG('S','C','N',' ')},*/ /* Sicilian */ +/*{HB_TAG('s','c','o',' '), HB_TAG('S','C','O',' ')},*/ /* Scots */ + {HB_TAG('s','c','s',' '), HB_TAG('S','C','S',' ')}, /* North Slavey */ + {HB_TAG('s','c','s',' '), HB_TAG('S','L','A',' ')}, /* North Slavey -> Slavey */ + {HB_TAG('s','c','s',' '), HB_TAG('A','T','H',' ')}, /* North Slavey -> Athapaskan */ + {HB_TAG('s','d','c',' '), HB_TAG('S','R','D',' ')}, /* Sassarese Sardinian -> Sardinian */ + {HB_TAG('s','d','h',' '), HB_TAG('K','U','R',' ')}, /* Southern Kurdish -> Kurdish */ + {HB_TAG('s','d','n',' '), HB_TAG('S','R','D',' ')}, /* Gallurese Sardinian -> Sardinian */ + {HB_TAG('s','d','s',' '), HB_TAG('B','B','R',' ')}, /* Sened -> Berber */ + {HB_TAG('s','e','h',' '), HB_TAG('S','N','A',' ')}, /* Sena */ + {HB_TAG('s','e','k',' '), HB_TAG('A','T','H',' ')}, /* Sekani -> Athapaskan */ +/*{HB_TAG('s','e','l',' '), HB_TAG('S','E','L',' ')},*/ /* Selkup */ + {HB_TAG('s','e','z',' '), HB_TAG('Q','I','N',' ')}, /* Senthang Chin -> Chin */ + {HB_TAG('s','f','m',' '), HB_TAG('S','F','M',' ')}, /* Small Flowery Miao */ + {HB_TAG('s','f','m',' '), HB_TAG('H','M','N',' ')}, /* Small Flowery Miao -> Hmong */ +/*{HB_TAG('s','g','a',' '), HB_TAG('S','G','A',' ')},*/ /* Old Irish (to 900) */ + {HB_TAG('s','g','c',' '), HB_TAG('K','A','L',' ')}, /* Kipsigis -> Kalenjin */ + {HB_TAG('s','g','o',' '), HB_TAG_NONE }, /* Songa (retired code) != Sango */ +/*{HB_TAG('s','g','s',' '), HB_TAG('S','G','S',' ')},*/ /* Samogitian */ + {HB_TAG('s','g','w',' '), HB_TAG('C','H','G',' ')}, /* Sebat Bet Gurage -> Chaha Gurage */ + {HB_TAG('s','h','i',' '), HB_TAG('S','H','I',' ')}, /* Tachelhit */ + {HB_TAG('s','h','i',' '), HB_TAG('B','B','R',' ')}, /* Tachelhit -> Berber */ + {HB_TAG('s','h','l',' '), HB_TAG('Q','I','N',' ')}, /* Shendu -> Chin */ +/*{HB_TAG('s','h','n',' '), HB_TAG('S','H','N',' ')},*/ /* Shan */ + {HB_TAG('s','h','u',' '), HB_TAG('A','R','A',' ')}, /* Chadian Arabic -> Arabic */ + {HB_TAG('s','h','y',' '), HB_TAG('B','B','R',' ')}, /* Tachawit -> Berber */ + {HB_TAG('s','i','b',' '), HB_TAG_NONE }, /* Sebop != Sibe */ +/*{HB_TAG('s','i','d',' '), HB_TAG('S','I','D',' ')},*/ /* Sidamo */ + {HB_TAG('s','i','g',' '), HB_TAG_NONE }, /* Paasaal != Silte Gurage */ + {HB_TAG('s','i','z',' '), HB_TAG('B','B','R',' ')}, /* Siwi -> Berber */ + {HB_TAG('s','j','d',' '), HB_TAG('K','S','M',' ')}, /* Kildin Sami */ + {HB_TAG('s','j','o',' '), HB_TAG('S','I','B',' ')}, /* Xibe -> Sibe */ + {HB_TAG('s','j','s',' '), HB_TAG('B','B','R',' ')}, /* Senhaja De Srair -> Berber */ + {HB_TAG('s','k','g',' '), HB_TAG('M','L','G',' ')}, /* Sakalava Malagasy -> Malagasy */ + {HB_TAG('s','k','r',' '), HB_TAG('S','R','K',' ')}, /* Saraiki */ + {HB_TAG('s','k','s',' '), HB_TAG_NONE }, /* Maia != Skolt Sami */ + {HB_TAG('s','k','w',' '), HB_TAG('C','P','P',' ')}, /* Skepi Creole Dutch -> Creoles */ + {HB_TAG('s','k','y',' '), HB_TAG_NONE }, /* Sikaiana != Slovak */ + {HB_TAG('s','l','a',' '), HB_TAG_NONE }, /* Slavic [collection] != Slavey */ + {HB_TAG('s','m','a',' '), HB_TAG('S','S','M',' ')}, /* Southern Sami */ + {HB_TAG('s','m','d',' '), HB_TAG('M','B','N',' ')}, /* Sama (retired code) -> Mbundu */ + {HB_TAG('s','m','j',' '), HB_TAG('L','S','M',' ')}, /* Lule Sami */ + {HB_TAG('s','m','l',' '), HB_TAG_NONE }, /* Central Sama != Somali */ + {HB_TAG('s','m','n',' '), HB_TAG('I','S','M',' ')}, /* Inari Sami */ + {HB_TAG('s','m','s',' '), HB_TAG('S','K','S',' ')}, /* Skolt Sami */ + {HB_TAG('s','m','t',' '), HB_TAG('Q','I','N',' ')}, /* Simte -> Chin */ + {HB_TAG('s','n','b',' '), HB_TAG('I','B','A',' ')}, /* Sebuyau (retired code) -> Iban */ + {HB_TAG('s','n','h',' '), HB_TAG_NONE }, /* Shinabo (retired code) != Sinhala (Sinhalese) */ +/*{HB_TAG('s','n','k',' '), HB_TAG('S','N','K',' ')},*/ /* Soninke */ + {HB_TAG('s','o','g',' '), HB_TAG_NONE }, /* Sogdian != Sodo Gurage */ +/*{HB_TAG('s','o','p',' '), HB_TAG('S','O','P',' ')},*/ /* Songe */ + {HB_TAG('s','p','v',' '), HB_TAG('O','R','I',' ')}, /* Sambalpuri -> Odia (formerly Oriya) */ + {HB_TAG('s','p','y',' '), HB_TAG('K','A','L',' ')}, /* Sabaot -> Kalenjin */ + {HB_TAG('s','r','b',' '), HB_TAG_NONE }, /* Sora != Serbian */ + {HB_TAG('s','r','c',' '), HB_TAG('S','R','D',' ')}, /* Logudorese Sardinian -> Sardinian */ + {HB_TAG('s','r','k',' '), HB_TAG_NONE }, /* Serudung Murut != Saraiki */ + {HB_TAG('s','r','m',' '), HB_TAG('C','P','P',' ')}, /* Saramaccan -> Creoles */ + {HB_TAG('s','r','n',' '), HB_TAG('C','P','P',' ')}, /* Sranan Tongo -> Creoles */ + {HB_TAG('s','r','o',' '), HB_TAG('S','R','D',' ')}, /* Campidanese Sardinian -> Sardinian */ +/*{HB_TAG('s','r','r',' '), HB_TAG('S','R','R',' ')},*/ /* Serer */ + {HB_TAG('s','r','s',' '), HB_TAG('A','T','H',' ')}, /* Sarsi -> Athapaskan */ + {HB_TAG('s','s','h',' '), HB_TAG('A','R','A',' ')}, /* Shihhi Arabic -> Arabic */ + {HB_TAG('s','s','l',' '), HB_TAG_NONE }, /* Western Sisaala != South Slavey */ + {HB_TAG('s','s','m',' '), HB_TAG_NONE }, /* Semnam != Southern Sami */ + {HB_TAG('s','t','a',' '), HB_TAG('C','P','P',' ')}, /* Settla -> Creoles */ +/*{HB_TAG('s','t','q',' '), HB_TAG('S','T','Q',' ')},*/ /* Saterfriesisch -> Saterland Frisian */ + {HB_TAG('s','t','v',' '), HB_TAG('S','I','G',' ')}, /* Silt'e -> Silte Gurage */ +/*{HB_TAG('s','u','k',' '), HB_TAG('S','U','K',' ')},*/ /* Sukuma */ + {HB_TAG('s','u','q',' '), HB_TAG('S','U','R',' ')}, /* Suri */ + {HB_TAG('s','u','r',' '), HB_TAG_NONE }, /* Mwaghavul != Suri */ +/*{HB_TAG('s','v','a',' '), HB_TAG('S','V','A',' ')},*/ /* Svan */ + {HB_TAG('s','v','c',' '), HB_TAG('C','P','P',' ')}, /* Vincentian Creole English -> Creoles */ + {HB_TAG('s','v','e',' '), HB_TAG_NONE }, /* Serili != Swedish */ + {HB_TAG('s','w','b',' '), HB_TAG('C','M','R',' ')}, /* Maore Comorian -> Comorian */ + {HB_TAG('s','w','c',' '), HB_TAG('S','W','K',' ')}, /* Congo Swahili -> Swahili */ + {HB_TAG('s','w','h',' '), HB_TAG('S','W','K',' ')}, /* Swahili */ + {HB_TAG('s','w','k',' '), HB_TAG_NONE }, /* Malawi Sena != Swahili */ + {HB_TAG('s','w','n',' '), HB_TAG('B','B','R',' ')}, /* Sawknah -> Berber */ + {HB_TAG('s','w','v',' '), HB_TAG('M','A','W',' ')}, /* Shekhawati -> Marwari */ +/*{HB_TAG('s','x','u',' '), HB_TAG('S','X','U',' ')},*/ /* Upper Saxon */ + {HB_TAG('s','y','c',' '), HB_TAG('S','Y','R',' ')}, /* Classical Syriac -> Syriac */ +/*{HB_TAG('s','y','l',' '), HB_TAG('S','Y','L',' ')},*/ /* Sylheti */ +/*{HB_TAG('s','y','r',' '), HB_TAG('S','Y','R',' ')},*/ /* Syriac [macrolanguage] */ +/*{HB_TAG('s','z','l',' '), HB_TAG('S','Z','L',' ')},*/ /* Silesian */ + {HB_TAG('t','a','a',' '), HB_TAG('A','T','H',' ')}, /* Lower Tanana -> Athapaskan */ +/*{HB_TAG('t','a','b',' '), HB_TAG('T','A','B',' ')},*/ /* Tabassaran -> Tabasaran */ + {HB_TAG('t','a','j',' '), HB_TAG_NONE }, /* Eastern Tamang != Tajiki */ + {HB_TAG('t','a','q',' '), HB_TAG('T','M','H',' ')}, /* Tamasheq -> Tamashek */ + {HB_TAG('t','a','q',' '), HB_TAG('B','B','R',' ')}, /* Tamasheq -> Berber */ + {HB_TAG('t','a','s',' '), HB_TAG('C','P','P',' ')}, /* Tay Boi -> Creoles */ + {HB_TAG('t','a','u',' '), HB_TAG('A','T','H',' ')}, /* Upper Tanana -> Athapaskan */ + {HB_TAG('t','c','b',' '), HB_TAG('A','T','H',' ')}, /* Tanacross -> Athapaskan */ + {HB_TAG('t','c','e',' '), HB_TAG('A','T','H',' ')}, /* Southern Tutchone -> Athapaskan */ + {HB_TAG('t','c','h',' '), HB_TAG('C','P','P',' ')}, /* Turks And Caicos Creole English -> Creoles */ + {HB_TAG('t','c','p',' '), HB_TAG('Q','I','N',' ')}, /* Tawr Chin -> Chin */ + {HB_TAG('t','c','s',' '), HB_TAG('C','P','P',' ')}, /* Torres Strait Creole -> Creoles */ + {HB_TAG('t','c','y',' '), HB_TAG('T','U','L',' ')}, /* Tulu -> Tumbuka */ + {HB_TAG('t','c','z',' '), HB_TAG('Q','I','N',' ')}, /* Thado Chin -> Chin */ +/*{HB_TAG('t','d','d',' '), HB_TAG('T','D','D',' ')},*/ /* Tai Nüa -> Dehong Dai */ + {HB_TAG('t','d','x',' '), HB_TAG('M','L','G',' ')}, /* Tandroy-Mahafaly Malagasy -> Malagasy */ + {HB_TAG('t','e','c',' '), HB_TAG('K','A','L',' ')}, /* Terik -> Kalenjin */ + {HB_TAG('t','e','m',' '), HB_TAG('T','M','N',' ')}, /* Timne -> Temne */ +/*{HB_TAG('t','e','t',' '), HB_TAG('T','E','T',' ')},*/ /* Tetum */ + {HB_TAG('t','e','z',' '), HB_TAG('B','B','R',' ')}, /* Tetserret -> Berber */ + {HB_TAG('t','f','n',' '), HB_TAG('A','T','H',' ')}, /* Tanaina -> Athapaskan */ + {HB_TAG('t','g','h',' '), HB_TAG('C','P','P',' ')}, /* Tobagonian Creole English -> Creoles */ + {HB_TAG('t','g','j',' '), HB_TAG('N','I','S',' ')}, /* Tagin -> Nisi */ + {HB_TAG('t','g','n',' '), HB_TAG_NONE }, /* Tandaganon != Tongan */ + {HB_TAG('t','g','r',' '), HB_TAG_NONE }, /* Tareng != Tigre */ + {HB_TAG('t','g','x',' '), HB_TAG('A','T','H',' ')}, /* Tagish -> Athapaskan */ + {HB_TAG('t','g','y',' '), HB_TAG_NONE }, /* Togoyo != Tigrinya */ + {HB_TAG('t','h','t',' '), HB_TAG('A','T','H',' ')}, /* Tahltan -> Athapaskan */ + {HB_TAG('t','h','v',' '), HB_TAG('T','M','H',' ')}, /* Tahaggart Tamahaq -> Tamashek */ + {HB_TAG('t','h','v',' '), HB_TAG('B','B','R',' ')}, /* Tahaggart Tamahaq -> Berber */ + {HB_TAG('t','h','z',' '), HB_TAG('T','M','H',' ')}, /* Tayart Tamajeq -> Tamashek */ + {HB_TAG('t','h','z',' '), HB_TAG('B','B','R',' ')}, /* Tayart Tamajeq -> Berber */ + {HB_TAG('t','i','a',' '), HB_TAG('B','B','R',' ')}, /* Tidikelt Tamazight -> Berber */ + {HB_TAG('t','i','g',' '), HB_TAG('T','G','R',' ')}, /* Tigre */ +/*{HB_TAG('t','i','v',' '), HB_TAG('T','I','V',' ')},*/ /* Tiv */ +/*{HB_TAG('t','j','l',' '), HB_TAG('T','J','L',' ')},*/ /* Tai Laing */ + {HB_TAG('t','j','o',' '), HB_TAG('B','B','R',' ')}, /* Temacine Tamazight -> Berber */ + {HB_TAG('t','k','g',' '), HB_TAG('M','L','G',' ')}, /* Tesaka Malagasy -> Malagasy */ + {HB_TAG('t','k','m',' '), HB_TAG_NONE }, /* Takelma != Turkmen */ +/*{HB_TAG('t','l','i',' '), HB_TAG('T','L','I',' ')},*/ /* Tlingit */ + {HB_TAG('t','m','g',' '), HB_TAG('C','P','P',' ')}, /* Ternateño -> Creoles */ + {HB_TAG('t','m','h',' '), HB_TAG('T','M','H',' ')}, /* Tamashek [macrolanguage] */ + {HB_TAG('t','m','h',' '), HB_TAG('B','B','R',' ')}, /* Tamashek [macrolanguage] -> Berber */ + {HB_TAG('t','m','n',' '), HB_TAG_NONE }, /* Taman (Indonesia) != Temne */ + {HB_TAG('t','m','w',' '), HB_TAG('M','L','Y',' ')}, /* Temuan -> Malay */ + {HB_TAG('t','n','a',' '), HB_TAG_NONE }, /* Tacana != Tswana */ + {HB_TAG('t','n','e',' '), HB_TAG_NONE }, /* Tinoc Kallahan (retired code) != Tundra Enets */ + {HB_TAG('t','n','f',' '), HB_TAG('D','R','I',' ')}, /* Tangshewi (retired code) -> Dari */ + {HB_TAG('t','n','f',' '), HB_TAG('F','A','R',' ')}, /* Tangshewi (retired code) -> Persian */ + {HB_TAG('t','n','g',' '), HB_TAG_NONE }, /* Tobanga != Tonga */ + {HB_TAG('t','o','d',' '), HB_TAG('T','O','D','0')}, /* Toma */ + {HB_TAG('t','o','i',' '), HB_TAG('T','N','G',' ')}, /* Tonga (Zambia) */ + {HB_TAG('t','o','j',' '), HB_TAG('M','Y','N',' ')}, /* Tojolabal -> Mayan */ + {HB_TAG('t','o','l',' '), HB_TAG('A','T','H',' ')}, /* Tolowa -> Athapaskan */ + {HB_TAG('t','o','r',' '), HB_TAG('B','A','D','0')}, /* Togbo-Vara Banda -> Banda */ + {HB_TAG('t','p','i',' '), HB_TAG('T','P','I',' ')}, /* Tok Pisin */ + {HB_TAG('t','p','i',' '), HB_TAG('C','P','P',' ')}, /* Tok Pisin -> Creoles */ + {HB_TAG('t','r','f',' '), HB_TAG('C','P','P',' ')}, /* Trinidadian Creole English -> Creoles */ + {HB_TAG('t','r','k',' '), HB_TAG_NONE }, /* Turkic [collection] != Turkish */ + {HB_TAG('t','r','u',' '), HB_TAG('T','U','A',' ')}, /* Turoyo -> Turoyo Aramaic */ + {HB_TAG('t','r','u',' '), HB_TAG('S','Y','R',' ')}, /* Turoyo -> Syriac */ + {HB_TAG('t','s','g',' '), HB_TAG_NONE }, /* Tausug != Tsonga */ +/*{HB_TAG('t','s','j',' '), HB_TAG('T','S','J',' ')},*/ /* Tshangla */ + {HB_TAG('t','t','c',' '), HB_TAG('M','Y','N',' ')}, /* Tektiteko -> Mayan */ + {HB_TAG('t','t','m',' '), HB_TAG('A','T','H',' ')}, /* Northern Tutchone -> Athapaskan */ + {HB_TAG('t','t','q',' '), HB_TAG('T','M','H',' ')}, /* Tawallammat Tamajaq -> Tamashek */ + {HB_TAG('t','t','q',' '), HB_TAG('B','B','R',' ')}, /* Tawallammat Tamajaq -> Berber */ + {HB_TAG('t','u','a',' '), HB_TAG_NONE }, /* Wiarumus != Turoyo Aramaic */ + {HB_TAG('t','u','l',' '), HB_TAG_NONE }, /* Tula != Tumbuka */ +/*{HB_TAG('t','u','m',' '), HB_TAG('T','U','M',' ')},*/ /* Tumbuka -> Tulu */ + {HB_TAG('t','u','u',' '), HB_TAG('A','T','H',' ')}, /* Tututni -> Athapaskan */ + {HB_TAG('t','u','v',' '), HB_TAG_NONE }, /* Turkana != Tuvin */ + {HB_TAG('t','u','y',' '), HB_TAG('K','A','L',' ')}, /* Tugen -> Kalenjin */ +/*{HB_TAG('t','v','l',' '), HB_TAG('T','V','L',' ')},*/ /* Tuvalu */ + {HB_TAG('t','v','y',' '), HB_TAG('C','P','P',' ')}, /* Timor Pidgin -> Creoles */ + {HB_TAG('t','x','c',' '), HB_TAG('A','T','H',' ')}, /* Tsetsaut -> Athapaskan */ + {HB_TAG('t','x','y',' '), HB_TAG('M','L','G',' ')}, /* Tanosy Malagasy -> Malagasy */ + {HB_TAG('t','y','v',' '), HB_TAG('T','U','V',' ')}, /* Tuvinian -> Tuvin */ +/*{HB_TAG('t','y','z',' '), HB_TAG('T','Y','Z',' ')},*/ /* Tày */ + {HB_TAG('t','z','h',' '), HB_TAG('M','Y','N',' ')}, /* Tzeltal -> Mayan */ + {HB_TAG('t','z','j',' '), HB_TAG('M','Y','N',' ')}, /* Tz'utujil -> Mayan */ + {HB_TAG('t','z','m',' '), HB_TAG('T','Z','M',' ')}, /* Central Atlas Tamazight -> Tamazight */ + {HB_TAG('t','z','m',' '), HB_TAG('B','B','R',' ')}, /* Central Atlas Tamazight -> Berber */ + {HB_TAG('t','z','o',' '), HB_TAG('T','Z','O',' ')}, /* Tzotzil */ + {HB_TAG('t','z','o',' '), HB_TAG('M','Y','N',' ')}, /* Tzotzil -> Mayan */ + {HB_TAG('u','b','l',' '), HB_TAG('B','I','K',' ')}, /* Buhi'non Bikol -> Bikol */ +/*{HB_TAG('u','d','m',' '), HB_TAG('U','D','M',' ')},*/ /* Udmurt */ + {HB_TAG('u','k','i',' '), HB_TAG('K','U','I',' ')}, /* Kui (India) */ + {HB_TAG('u','l','n',' '), HB_TAG('C','P','P',' ')}, /* Unserdeutsch -> Creoles */ +/*{HB_TAG('u','m','b',' '), HB_TAG('U','M','B',' ')},*/ /* Umbundu */ + {HB_TAG('u','n','r',' '), HB_TAG('M','U','N',' ')}, /* Mundari */ + {HB_TAG('u','r','k',' '), HB_TAG('M','L','Y',' ')}, /* Urak Lawoi' -> Malay */ + {HB_TAG('u','s','p',' '), HB_TAG('M','Y','N',' ')}, /* Uspanteco -> Mayan */ + {HB_TAG('u','z','n',' '), HB_TAG('U','Z','B',' ')}, /* Northern Uzbek -> Uzbek */ + {HB_TAG('u','z','s',' '), HB_TAG('U','Z','B',' ')}, /* Southern Uzbek -> Uzbek */ + {HB_TAG('v','a','p',' '), HB_TAG('Q','I','N',' ')}, /* Vaiphei -> Chin */ +/*{HB_TAG('v','e','c',' '), HB_TAG('V','E','C',' ')},*/ /* Venetian */ + {HB_TAG('v','i','c',' '), HB_TAG('C','P','P',' ')}, /* Virgin Islands Creole English -> Creoles */ + {HB_TAG('v','i','t',' '), HB_TAG_NONE }, /* Viti != Vietnamese */ + {HB_TAG('v','k','k',' '), HB_TAG('M','L','Y',' ')}, /* Kaur -> Malay */ + {HB_TAG('v','k','p',' '), HB_TAG('C','P','P',' ')}, /* Korlai Creole Portuguese -> Creoles */ + {HB_TAG('v','k','t',' '), HB_TAG('M','L','Y',' ')}, /* Tenggarong Kutai Malay -> Malay */ + {HB_TAG('v','l','s',' '), HB_TAG('F','L','E',' ')}, /* Vlaams -> Dutch (Flemish) */ + {HB_TAG('v','m','w',' '), HB_TAG('M','A','K',' ')}, /* Makhuwa */ +/*{HB_TAG('v','r','o',' '), HB_TAG('V','R','O',' ')},*/ /* Võro */ + {HB_TAG('w','a','g',' '), HB_TAG_NONE }, /* Wa'ema != Wagdi */ +/*{HB_TAG('w','a','r',' '), HB_TAG('W','A','R',' ')},*/ /* Waray (Philippines) -> Waray-Waray */ + {HB_TAG('w','b','m',' '), HB_TAG('W','A',' ',' ')}, /* Wa */ + {HB_TAG('w','b','r',' '), HB_TAG('W','A','G',' ')}, /* Wagdi */ + {HB_TAG('w','b','r',' '), HB_TAG('R','A','J',' ')}, /* Wagdi -> Rajasthani */ +/*{HB_TAG('w','c','i',' '), HB_TAG('W','C','I',' ')},*/ /* Waci Gbe */ + {HB_TAG('w','e','a',' '), HB_TAG('K','R','N',' ')}, /* Wewaw -> Karen */ + {HB_TAG('w','e','s',' '), HB_TAG('C','P','P',' ')}, /* Cameroon Pidgin -> Creoles */ + {HB_TAG('w','e','u',' '), HB_TAG('Q','I','N',' ')}, /* Rawngtu Chin -> Chin */ + {HB_TAG('w','l','c',' '), HB_TAG('C','M','R',' ')}, /* Mwali Comorian -> Comorian */ + {HB_TAG('w','l','e',' '), HB_TAG('S','I','G',' ')}, /* Wolane -> Silte Gurage */ + {HB_TAG('w','l','k',' '), HB_TAG('A','T','H',' ')}, /* Wailaki -> Athapaskan */ + {HB_TAG('w','n','i',' '), HB_TAG('C','M','R',' ')}, /* Ndzwani Comorian -> Comorian */ + {HB_TAG('w','r','y',' '), HB_TAG('M','A','W',' ')}, /* Merwari -> Marwari */ + {HB_TAG('w','s','g',' '), HB_TAG('G','O','N',' ')}, /* Adilabad Gondi -> Gondi */ +/*{HB_TAG('w','t','m',' '), HB_TAG('W','T','M',' ')},*/ /* Mewati */ + {HB_TAG('w','u','u',' '), HB_TAG('Z','H','S',' ')}, /* Wu Chinese -> Chinese, Simplified */ + {HB_TAG('x','a','l',' '), HB_TAG('K','L','M',' ')}, /* Kalmyk */ + {HB_TAG('x','a','l',' '), HB_TAG('T','O','D',' ')}, /* Kalmyk -> Todo */ + {HB_TAG('x','a','n',' '), HB_TAG('S','E','K',' ')}, /* Xamtanga -> Sekota */ + {HB_TAG('x','b','d',' '), HB_TAG_NONE }, /* Bindal != Lü */ +/*{HB_TAG('x','j','b',' '), HB_TAG('X','J','B',' ')},*/ /* Minjungbal -> Minjangbal */ +/*{HB_TAG('x','k','f',' '), HB_TAG('X','K','F',' ')},*/ /* Khengkha */ + {HB_TAG('x','m','g',' '), HB_TAG('B','M','L',' ')}, /* Mengaka -> Bamileke */ + {HB_TAG('x','m','m',' '), HB_TAG('M','L','Y',' ')}, /* Manado Malay -> Malay */ + {HB_TAG('x','m','m',' '), HB_TAG('C','P','P',' ')}, /* Manado Malay -> Creoles */ + {HB_TAG('x','m','v',' '), HB_TAG('M','L','G',' ')}, /* Antankarana Malagasy -> Malagasy */ + {HB_TAG('x','m','w',' '), HB_TAG('M','L','G',' ')}, /* Tsimihety Malagasy -> Malagasy */ + {HB_TAG('x','n','j',' '), HB_TAG('S','X','T',' ')}, /* Ngoni (Tanzania) -> Sutu */ + {HB_TAG('x','n','q',' '), HB_TAG('S','X','T',' ')}, /* Ngoni (Mozambique) -> Sutu */ + {HB_TAG('x','n','r',' '), HB_TAG('D','G','R',' ')}, /* Kangri -> Dogri (macrolanguage) */ +/*{HB_TAG('x','o','g',' '), HB_TAG('X','O','G',' ')},*/ /* Soga */ + {HB_TAG('x','p','e',' '), HB_TAG('X','P','E',' ')}, /* Liberia Kpelle -> Kpelle (Liberia) */ + {HB_TAG('x','p','e',' '), HB_TAG('K','P','L',' ')}, /* Liberia Kpelle -> Kpelle */ + {HB_TAG('x','s','l',' '), HB_TAG('S','S','L',' ')}, /* South Slavey */ + {HB_TAG('x','s','l',' '), HB_TAG('S','L','A',' ')}, /* South Slavey -> Slavey */ + {HB_TAG('x','s','l',' '), HB_TAG('A','T','H',' ')}, /* South Slavey -> Athapaskan */ + {HB_TAG('x','s','t',' '), HB_TAG('S','I','G',' ')}, /* Silt'e (retired code) -> Silte Gurage */ +/*{HB_TAG('x','u','b',' '), HB_TAG('X','U','B',' ')},*/ /* Betta Kurumba -> Bette Kuruma */ +/*{HB_TAG('x','u','j',' '), HB_TAG('X','U','J',' ')},*/ /* Jennu Kurumba -> Jennu Kuruma */ + {HB_TAG('x','u','p',' '), HB_TAG('A','T','H',' ')}, /* Upper Umpqua -> Athapaskan */ + {HB_TAG('x','w','o',' '), HB_TAG('T','O','D',' ')}, /* Written Oirat -> Todo */ + {HB_TAG('y','a','j',' '), HB_TAG('B','A','D','0')}, /* Banda-Yangere -> Banda */ + {HB_TAG('y','a','k',' '), HB_TAG_NONE }, /* Yakama != Sakha */ +/*{HB_TAG('y','a','o',' '), HB_TAG('Y','A','O',' ')},*/ /* Yao */ +/*{HB_TAG('y','a','p',' '), HB_TAG('Y','A','P',' ')},*/ /* Yapese */ + {HB_TAG('y','b','a',' '), HB_TAG_NONE }, /* Yala != Yoruba */ + {HB_TAG('y','b','b',' '), HB_TAG('B','M','L',' ')}, /* Yemba -> Bamileke */ + {HB_TAG('y','b','d',' '), HB_TAG('A','R','K',' ')}, /* Yangbye (retired code) -> Rakhine */ + {HB_TAG('y','d','d',' '), HB_TAG('J','I','I',' ')}, /* Eastern Yiddish -> Yiddish */ +/*{HB_TAG('y','g','p',' '), HB_TAG('Y','G','P',' ')},*/ /* Gepo */ + {HB_TAG('y','i','h',' '), HB_TAG('J','I','I',' ')}, /* Western Yiddish -> Yiddish */ + {HB_TAG('y','i','m',' '), HB_TAG_NONE }, /* Yimchungru Naga != Yi Modern */ +/*{HB_TAG('y','n','a',' '), HB_TAG('Y','N','A',' ')},*/ /* Aluo */ + {HB_TAG('y','o','s',' '), HB_TAG('Q','I','N',' ')}, /* Yos (retired code) -> Chin */ + {HB_TAG('y','u','a',' '), HB_TAG('M','Y','N',' ')}, /* Yucateco -> Mayan */ + {HB_TAG('y','u','e',' '), HB_TAG('Z','H','H',' ')}, /* Yue Chinese -> Chinese, Traditional, Hong Kong SAR */ +/*{HB_TAG('y','w','q',' '), HB_TAG('Y','W','Q',' ')},*/ /* Wuding-Luquan Yi */ + {HB_TAG('z','c','h',' '), HB_TAG('Z','H','A',' ')}, /* Central Hongshuihe Zhuang -> Zhuang */ + {HB_TAG('z','d','j',' '), HB_TAG('C','M','R',' ')}, /* Ngazidja Comorian -> Comorian */ +/*{HB_TAG('z','e','a',' '), HB_TAG('Z','E','A',' ')},*/ /* Zeeuws -> Zealandic */ + {HB_TAG('z','e','h',' '), HB_TAG('Z','H','A',' ')}, /* Eastern Hongshuihe Zhuang -> Zhuang */ + {HB_TAG('z','e','n',' '), HB_TAG('B','B','R',' ')}, /* Zenaga -> Berber */ + {HB_TAG('z','g','b',' '), HB_TAG('Z','H','A',' ')}, /* Guibei Zhuang -> Zhuang */ + {HB_TAG('z','g','h',' '), HB_TAG('Z','G','H',' ')}, /* Standard Moroccan Tamazight */ + {HB_TAG('z','g','h',' '), HB_TAG('B','B','R',' ')}, /* Standard Moroccan Tamazight -> Berber */ + {HB_TAG('z','g','m',' '), HB_TAG('Z','H','A',' ')}, /* Minz Zhuang -> Zhuang */ + {HB_TAG('z','g','n',' '), HB_TAG('Z','H','A',' ')}, /* Guibian Zhuang -> Zhuang */ + {HB_TAG('z','h','d',' '), HB_TAG('Z','H','A',' ')}, /* Dai Zhuang -> Zhuang */ + {HB_TAG('z','h','n',' '), HB_TAG('Z','H','A',' ')}, /* Nong Zhuang -> Zhuang */ + {HB_TAG('z','l','j',' '), HB_TAG('Z','H','A',' ')}, /* Liujiang Zhuang -> Zhuang */ + {HB_TAG('z','l','m',' '), HB_TAG('M','L','Y',' ')}, /* Malay */ + {HB_TAG('z','l','n',' '), HB_TAG('Z','H','A',' ')}, /* Lianshan Zhuang -> Zhuang */ + {HB_TAG('z','l','q',' '), HB_TAG('Z','H','A',' ')}, /* Liuqian Zhuang -> Zhuang */ + {HB_TAG('z','m','i',' '), HB_TAG('M','L','Y',' ')}, /* Negeri Sembilan Malay -> Malay */ + {HB_TAG('z','m','z',' '), HB_TAG('B','A','D','0')}, /* Mbandja -> Banda */ + {HB_TAG('z','n','d',' '), HB_TAG_NONE }, /* Zande [collection] != Zande */ + {HB_TAG('z','n','e',' '), HB_TAG('Z','N','D',' ')}, /* Zande */ + {HB_TAG('z','o','m',' '), HB_TAG('Q','I','N',' ')}, /* Zou -> Chin */ + {HB_TAG('z','q','e',' '), HB_TAG('Z','H','A',' ')}, /* Qiubei Zhuang -> Zhuang */ + {HB_TAG('z','s','m',' '), HB_TAG('M','L','Y',' ')}, /* Standard Malay -> Malay */ + {HB_TAG('z','u','m',' '), HB_TAG('L','R','C',' ')}, /* Kumzari -> Luri */ + {HB_TAG('z','y','b',' '), HB_TAG('Z','H','A',' ')}, /* Yongbei Zhuang -> Zhuang */ + {HB_TAG('z','y','g',' '), HB_TAG('Z','H','A',' ')}, /* Yang Zhuang -> Zhuang */ + {HB_TAG('z','y','j',' '), HB_TAG('Z','H','A',' ')}, /* Youjiang Zhuang -> Zhuang */ + {HB_TAG('z','y','n',' '), HB_TAG('Z','H','A',' ')}, /* Yongnan Zhuang -> Zhuang */ + {HB_TAG('z','y','p',' '), HB_TAG('Q','I','N',' ')}, /* Zyphe Chin -> Chin */ +/*{HB_TAG('z','z','a',' '), HB_TAG('Z','Z','A',' ')},*/ /* Zazaki [macrolanguage] */ + {HB_TAG('z','z','j',' '), HB_TAG('Z','H','A',' ')}, /* Zuojiang Zhuang -> Zhuang */ +}; +#endif + +/** + * hb_ot_tags_from_complex_language: + * @lang_str: a BCP 47 language tag to convert. + * @limit: a pointer to the end of the substring of @lang_str to consider for + * conversion. + * @count: maximum number of language tags to retrieve (IN) and actual number of + * language tags retrieved (OUT). If no tags are retrieved, it is not modified. + * @tags: array of size at least @language_count to store the language tag + * results + * + * Converts a multi-subtag BCP 47 language tag to language tags. + * + * Return value: Whether any language systems were retrieved. + **/ +static inline bool +hb_ot_tags_from_complex_language (const char *lang_str, + const char *limit, + unsigned int *count /* IN/OUT */, + hb_tag_t *tags /* OUT */) +{ + if (limit - lang_str >= 7) + { + const char *p = strchr (lang_str, '-'); + if (!p || p >= limit || limit - p < 5) goto out; + if (subtag_matches (p, limit, "-fonnapa", 8)) + { + /* Undetermined; North American Phonetic Alphabet */ + tags[0] = HB_TAG('A','P','P','H'); /* Phonetic transcription—Americanist conventions */ + *count = 1; + return true; + } + if (subtag_matches (p, limit, "-polyton", 8)) + { + /* Modern Greek (1453-); Polytonic Greek */ + tags[0] = HB_TAG('P','G','R',' '); /* Polytonic Greek */ + *count = 1; + return true; + } + if (subtag_matches (p, limit, "-arevmda", 8)) + { + /* Armenian; Western Armenian (retired code) */ + tags[0] = HB_TAG('H','Y','E',' '); /* Armenian */ + *count = 1; + return true; + } + if (subtag_matches (p, limit, "-provenc", 8)) + { + /* Occitan (post 1500); Provençal */ + tags[0] = HB_TAG('P','R','O',' '); /* Provençal / Old Provençal */ + *count = 1; + return true; + } + if (subtag_matches (p, limit, "-fonipa", 7)) + { + /* Undetermined; International Phonetic Alphabet */ + tags[0] = HB_TAG('I','P','P','H'); /* Phonetic transcription—IPA conventions */ + *count = 1; + return true; + } + if (subtag_matches (p, limit, "-geok", 5)) + { + /* Undetermined; Khutsuri (Asomtavruli and Nuskhuri) */ + tags[0] = HB_TAG('K','G','E',' '); /* Khutsuri Georgian */ + *count = 1; + return true; + } + if (subtag_matches (p, limit, "-syre", 5)) + { + /* Undetermined; Syriac (Estrangelo variant) */ + tags[0] = HB_TAG('S','Y','R','E'); /* Syriac, Estrangela script-variant (equivalent to ISO 15924 'Syre') */ + *count = 1; + return true; + } + if (subtag_matches (p, limit, "-syrj", 5)) + { + /* Undetermined; Syriac (Western variant) */ + tags[0] = HB_TAG('S','Y','R','J'); /* Syriac, Western script-variant (equivalent to ISO 15924 'Syrj') */ + *count = 1; + return true; + } + if (subtag_matches (p, limit, "-syrn", 5)) + { + /* Undetermined; Syriac (Eastern variant) */ + tags[0] = HB_TAG('S','Y','R','N'); /* Syriac, Eastern script-variant (equivalent to ISO 15924 'Syrn') */ + *count = 1; + return true; + } + } +out: + switch (lang_str[0]) + { + case 'a': + if (0 == strcmp (&lang_str[1], "rt-lojban")) + { + /* Lojban (retired code) */ + tags[0] = HB_TAG('J','B','O',' '); /* Lojban */ + *count = 1; + return true; + } + break; + case 'c': + if (lang_matches (&lang_str[1], limit, "do-hant-hk", 10)) + { + /* Min Dong Chinese; Han (Traditional variant); Hong Kong */ + tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Traditional, Hong Kong SAR */ + *count = 1; + return true; + } + if (lang_matches (&lang_str[1], limit, "do-hant-mo", 10)) + { + /* Min Dong Chinese; Han (Traditional variant); Macao */ + unsigned int i; + hb_tag_t possible_tags[] = { + HB_TAG('Z','H','T','M'), /* Chinese, Traditional, Macao SAR */ + HB_TAG('Z','H','H',' '), /* Chinese, Traditional, Hong Kong SAR */ + }; + for (i = 0; i < 2 && i < *count; i++) + tags[i] = possible_tags[i]; + *count = i; + return true; + } + if (lang_matches (&lang_str[1], limit, "jy-hant-hk", 10)) + { + /* Jinyu Chinese; Han (Traditional variant); Hong Kong */ + tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Traditional, Hong Kong SAR */ + *count = 1; + return true; + } + if (lang_matches (&lang_str[1], limit, "jy-hant-mo", 10)) + { + /* Jinyu Chinese; Han (Traditional variant); Macao */ + unsigned int i; + hb_tag_t possible_tags[] = { + HB_TAG('Z','H','T','M'), /* Chinese, Traditional, Macao SAR */ + HB_TAG('Z','H','H',' '), /* Chinese, Traditional, Hong Kong SAR */ + }; + for (i = 0; i < 2 && i < *count; i++) + tags[i] = possible_tags[i]; + *count = i; + return true; + } + if (lang_matches (&lang_str[1], limit, "mn-hant-hk", 10)) + { + /* Mandarin Chinese; Han (Traditional variant); Hong Kong */ + tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Traditional, Hong Kong SAR */ + *count = 1; + return true; + } + if (lang_matches (&lang_str[1], limit, "mn-hant-mo", 10)) + { + /* Mandarin Chinese; Han (Traditional variant); Macao */ + unsigned int i; + hb_tag_t possible_tags[] = { + HB_TAG('Z','H','T','M'), /* Chinese, Traditional, Macao SAR */ + HB_TAG('Z','H','H',' '), /* Chinese, Traditional, Hong Kong SAR */ + }; + for (i = 0; i < 2 && i < *count; i++) + tags[i] = possible_tags[i]; + *count = i; + return true; + } + if (lang_matches (&lang_str[1], limit, "np-hant-hk", 10)) + { + /* Northern Ping Chinese; Han (Traditional variant); Hong Kong */ + tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Traditional, Hong Kong SAR */ + *count = 1; + return true; + } + if (lang_matches (&lang_str[1], limit, "np-hant-mo", 10)) + { + /* Northern Ping Chinese; Han (Traditional variant); Macao */ + unsigned int i; + hb_tag_t possible_tags[] = { + HB_TAG('Z','H','T','M'), /* Chinese, Traditional, Macao SAR */ + HB_TAG('Z','H','H',' '), /* Chinese, Traditional, Hong Kong SAR */ + }; + for (i = 0; i < 2 && i < *count; i++) + tags[i] = possible_tags[i]; + *count = i; + return true; + } + if (lang_matches (&lang_str[1], limit, "px-hant-hk", 10)) + { + /* Pu-Xian Chinese; Han (Traditional variant); Hong Kong */ + tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Traditional, Hong Kong SAR */ + *count = 1; + return true; + } + if (lang_matches (&lang_str[1], limit, "px-hant-mo", 10)) + { + /* Pu-Xian Chinese; Han (Traditional variant); Macao */ + unsigned int i; + hb_tag_t possible_tags[] = { + HB_TAG('Z','H','T','M'), /* Chinese, Traditional, Macao SAR */ + HB_TAG('Z','H','H',' '), /* Chinese, Traditional, Hong Kong SAR */ + }; + for (i = 0; i < 2 && i < *count; i++) + tags[i] = possible_tags[i]; + *count = i; + return true; + } + if (lang_matches (&lang_str[1], limit, "sp-hant-hk", 10)) + { + /* Southern Ping Chinese; Han (Traditional variant); Hong Kong */ + tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Traditional, Hong Kong SAR */ + *count = 1; + return true; + } + if (lang_matches (&lang_str[1], limit, "sp-hant-mo", 10)) + { + /* Southern Ping Chinese; Han (Traditional variant); Macao */ + unsigned int i; + hb_tag_t possible_tags[] = { + HB_TAG('Z','H','T','M'), /* Chinese, Traditional, Macao SAR */ + HB_TAG('Z','H','H',' '), /* Chinese, Traditional, Hong Kong SAR */ + }; + for (i = 0; i < 2 && i < *count; i++) + tags[i] = possible_tags[i]; + *count = i; + return true; + } + if (lang_matches (&lang_str[1], limit, "zh-hant-hk", 10)) + { + /* Huizhou Chinese; Han (Traditional variant); Hong Kong */ + tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Traditional, Hong Kong SAR */ + *count = 1; + return true; + } + if (lang_matches (&lang_str[1], limit, "zh-hant-mo", 10)) + { + /* Huizhou Chinese; Han (Traditional variant); Macao */ + unsigned int i; + hb_tag_t possible_tags[] = { + HB_TAG('Z','H','T','M'), /* Chinese, Traditional, Macao SAR */ + HB_TAG('Z','H','H',' '), /* Chinese, Traditional, Hong Kong SAR */ + }; + for (i = 0; i < 2 && i < *count; i++) + tags[i] = possible_tags[i]; + *count = i; + return true; + } + if (lang_matches (&lang_str[1], limit, "zo-hant-hk", 10)) + { + /* Min Zhong Chinese; Han (Traditional variant); Hong Kong */ + tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Traditional, Hong Kong SAR */ + *count = 1; + return true; + } + if (lang_matches (&lang_str[1], limit, "zo-hant-mo", 10)) + { + /* Min Zhong Chinese; Han (Traditional variant); Macao */ + unsigned int i; + hb_tag_t possible_tags[] = { + HB_TAG('Z','H','T','M'), /* Chinese, Traditional, Macao SAR */ + HB_TAG('Z','H','H',' '), /* Chinese, Traditional, Hong Kong SAR */ + }; + for (i = 0; i < 2 && i < *count; i++) + tags[i] = possible_tags[i]; + *count = i; + return true; + } + if (lang_matches (&lang_str[1], limit, "do-hans", 7)) + { + /* Min Dong Chinese; Han (Simplified variant) */ + tags[0] = HB_TAG('Z','H','S',' '); /* Chinese, Simplified */ + *count = 1; + return true; + } + if (lang_matches (&lang_str[1], limit, "do-hant", 7)) + { + /* Min Dong Chinese; Han (Traditional variant) */ + tags[0] = HB_TAG('Z','H','T',' '); /* Chinese, Traditional */ + *count = 1; + return true; + } + if (lang_matches (&lang_str[1], limit, "jy-hans", 7)) + { + /* Jinyu Chinese; Han (Simplified variant) */ + tags[0] = HB_TAG('Z','H','S',' '); /* Chinese, Simplified */ + *count = 1; + return true; + } + if (lang_matches (&lang_str[1], limit, "jy-hant", 7)) + { + /* Jinyu Chinese; Han (Traditional variant) */ + tags[0] = HB_TAG('Z','H','T',' '); /* Chinese, Traditional */ + *count = 1; + return true; + } + if (lang_matches (&lang_str[1], limit, "mn-hans", 7)) + { + /* Mandarin Chinese; Han (Simplified variant) */ + tags[0] = HB_TAG('Z','H','S',' '); /* Chinese, Simplified */ + *count = 1; + return true; + } + if (lang_matches (&lang_str[1], limit, "mn-hant", 7)) + { + /* Mandarin Chinese; Han (Traditional variant) */ + tags[0] = HB_TAG('Z','H','T',' '); /* Chinese, Traditional */ + *count = 1; + return true; + } + if (lang_matches (&lang_str[1], limit, "np-hans", 7)) + { + /* Northern Ping Chinese; Han (Simplified variant) */ + tags[0] = HB_TAG('Z','H','S',' '); /* Chinese, Simplified */ + *count = 1; + return true; + } + if (lang_matches (&lang_str[1], limit, "np-hant", 7)) + { + /* Northern Ping Chinese; Han (Traditional variant) */ + tags[0] = HB_TAG('Z','H','T',' '); /* Chinese, Traditional */ + *count = 1; + return true; + } + if (lang_matches (&lang_str[1], limit, "px-hans", 7)) + { + /* Pu-Xian Chinese; Han (Simplified variant) */ + tags[0] = HB_TAG('Z','H','S',' '); /* Chinese, Simplified */ + *count = 1; + return true; + } + if (lang_matches (&lang_str[1], limit, "px-hant", 7)) + { + /* Pu-Xian Chinese; Han (Traditional variant) */ + tags[0] = HB_TAG('Z','H','T',' '); /* Chinese, Traditional */ + *count = 1; + return true; + } + if (lang_matches (&lang_str[1], limit, "sp-hans", 7)) + { + /* Southern Ping Chinese; Han (Simplified variant) */ + tags[0] = HB_TAG('Z','H','S',' '); /* Chinese, Simplified */ + *count = 1; + return true; + } + if (lang_matches (&lang_str[1], limit, "sp-hant", 7)) + { + /* Southern Ping Chinese; Han (Traditional variant) */ + tags[0] = HB_TAG('Z','H','T',' '); /* Chinese, Traditional */ + *count = 1; + return true; + } + if (lang_matches (&lang_str[1], limit, "zh-hans", 7)) + { + /* Huizhou Chinese; Han (Simplified variant) */ + tags[0] = HB_TAG('Z','H','S',' '); /* Chinese, Simplified */ + *count = 1; + return true; + } + if (lang_matches (&lang_str[1], limit, "zh-hant", 7)) + { + /* Huizhou Chinese; Han (Traditional variant) */ + tags[0] = HB_TAG('Z','H','T',' '); /* Chinese, Traditional */ + *count = 1; + return true; + } + if (lang_matches (&lang_str[1], limit, "zo-hans", 7)) + { + /* Min Zhong Chinese; Han (Simplified variant) */ + tags[0] = HB_TAG('Z','H','S',' '); /* Chinese, Simplified */ + *count = 1; + return true; + } + if (lang_matches (&lang_str[1], limit, "zo-hant", 7)) + { + /* Min Zhong Chinese; Han (Traditional variant) */ + tags[0] = HB_TAG('Z','H','T',' '); /* Chinese, Traditional */ + *count = 1; + return true; + } + if (0 == strncmp (&lang_str[1], "do-", 3) + && subtag_matches (lang_str, limit, "-hk", 3)) + { + /* Min Dong Chinese; Hong Kong */ + tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Traditional, Hong Kong SAR */ + *count = 1; + return true; + } + if (0 == strncmp (&lang_str[1], "do-", 3) + && subtag_matches (lang_str, limit, "-mo", 3)) + { + /* Min Dong Chinese; Macao */ + unsigned int i; + hb_tag_t possible_tags[] = { + HB_TAG('Z','H','T','M'), /* Chinese, Traditional, Macao SAR */ + HB_TAG('Z','H','H',' '), /* Chinese, Traditional, Hong Kong SAR */ + }; + for (i = 0; i < 2 && i < *count; i++) + tags[i] = possible_tags[i]; + *count = i; + return true; + } + if (0 == strncmp (&lang_str[1], "do-", 3) + && subtag_matches (lang_str, limit, "-tw", 3)) + { + /* Min Dong Chinese; Taiwan, Province of China */ + tags[0] = HB_TAG('Z','H','T',' '); /* Chinese, Traditional */ + *count = 1; + return true; + } + if (0 == strncmp (&lang_str[1], "jy-", 3) + && subtag_matches (lang_str, limit, "-hk", 3)) + { + /* Jinyu Chinese; Hong Kong */ + tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Traditional, Hong Kong SAR */ + *count = 1; + return true; + } + if (0 == strncmp (&lang_str[1], "jy-", 3) + && subtag_matches (lang_str, limit, "-mo", 3)) + { + /* Jinyu Chinese; Macao */ + unsigned int i; + hb_tag_t possible_tags[] = { + HB_TAG('Z','H','T','M'), /* Chinese, Traditional, Macao SAR */ + HB_TAG('Z','H','H',' '), /* Chinese, Traditional, Hong Kong SAR */ + }; + for (i = 0; i < 2 && i < *count; i++) + tags[i] = possible_tags[i]; + *count = i; + return true; + } + if (0 == strncmp (&lang_str[1], "jy-", 3) + && subtag_matches (lang_str, limit, "-tw", 3)) + { + /* Jinyu Chinese; Taiwan, Province of China */ + tags[0] = HB_TAG('Z','H','T',' '); /* Chinese, Traditional */ + *count = 1; + return true; + } + if (0 == strncmp (&lang_str[1], "mn-", 3) + && subtag_matches (lang_str, limit, "-hk", 3)) + { + /* Mandarin Chinese; Hong Kong */ + tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Traditional, Hong Kong SAR */ + *count = 1; + return true; + } + if (0 == strncmp (&lang_str[1], "mn-", 3) + && subtag_matches (lang_str, limit, "-mo", 3)) + { + /* Mandarin Chinese; Macao */ + unsigned int i; + hb_tag_t possible_tags[] = { + HB_TAG('Z','H','T','M'), /* Chinese, Traditional, Macao SAR */ + HB_TAG('Z','H','H',' '), /* Chinese, Traditional, Hong Kong SAR */ + }; + for (i = 0; i < 2 && i < *count; i++) + tags[i] = possible_tags[i]; + *count = i; + return true; + } + if (0 == strncmp (&lang_str[1], "mn-", 3) + && subtag_matches (lang_str, limit, "-tw", 3)) + { + /* Mandarin Chinese; Taiwan, Province of China */ + tags[0] = HB_TAG('Z','H','T',' '); /* Chinese, Traditional */ + *count = 1; + return true; + } + if (0 == strncmp (&lang_str[1], "np-", 3) + && subtag_matches (lang_str, limit, "-hk", 3)) + { + /* Northern Ping Chinese; Hong Kong */ + tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Traditional, Hong Kong SAR */ + *count = 1; + return true; + } + if (0 == strncmp (&lang_str[1], "np-", 3) + && subtag_matches (lang_str, limit, "-mo", 3)) + { + /* Northern Ping Chinese; Macao */ + unsigned int i; + hb_tag_t possible_tags[] = { + HB_TAG('Z','H','T','M'), /* Chinese, Traditional, Macao SAR */ + HB_TAG('Z','H','H',' '), /* Chinese, Traditional, Hong Kong SAR */ + }; + for (i = 0; i < 2 && i < *count; i++) + tags[i] = possible_tags[i]; + *count = i; + return true; + } + if (0 == strncmp (&lang_str[1], "np-", 3) + && subtag_matches (lang_str, limit, "-tw", 3)) + { + /* Northern Ping Chinese; Taiwan, Province of China */ + tags[0] = HB_TAG('Z','H','T',' '); /* Chinese, Traditional */ + *count = 1; + return true; + } + if (0 == strncmp (&lang_str[1], "px-", 3) + && subtag_matches (lang_str, limit, "-hk", 3)) + { + /* Pu-Xian Chinese; Hong Kong */ + tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Traditional, Hong Kong SAR */ + *count = 1; + return true; + } + if (0 == strncmp (&lang_str[1], "px-", 3) + && subtag_matches (lang_str, limit, "-mo", 3)) + { + /* Pu-Xian Chinese; Macao */ + unsigned int i; + hb_tag_t possible_tags[] = { + HB_TAG('Z','H','T','M'), /* Chinese, Traditional, Macao SAR */ + HB_TAG('Z','H','H',' '), /* Chinese, Traditional, Hong Kong SAR */ + }; + for (i = 0; i < 2 && i < *count; i++) + tags[i] = possible_tags[i]; + *count = i; + return true; + } + if (0 == strncmp (&lang_str[1], "px-", 3) + && subtag_matches (lang_str, limit, "-tw", 3)) + { + /* Pu-Xian Chinese; Taiwan, Province of China */ + tags[0] = HB_TAG('Z','H','T',' '); /* Chinese, Traditional */ + *count = 1; + return true; + } + if (0 == strncmp (&lang_str[1], "sp-", 3) + && subtag_matches (lang_str, limit, "-hk", 3)) + { + /* Southern Ping Chinese; Hong Kong */ + tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Traditional, Hong Kong SAR */ + *count = 1; + return true; + } + if (0 == strncmp (&lang_str[1], "sp-", 3) + && subtag_matches (lang_str, limit, "-mo", 3)) + { + /* Southern Ping Chinese; Macao */ + unsigned int i; + hb_tag_t possible_tags[] = { + HB_TAG('Z','H','T','M'), /* Chinese, Traditional, Macao SAR */ + HB_TAG('Z','H','H',' '), /* Chinese, Traditional, Hong Kong SAR */ + }; + for (i = 0; i < 2 && i < *count; i++) + tags[i] = possible_tags[i]; + *count = i; + return true; + } + if (0 == strncmp (&lang_str[1], "sp-", 3) + && subtag_matches (lang_str, limit, "-tw", 3)) + { + /* Southern Ping Chinese; Taiwan, Province of China */ + tags[0] = HB_TAG('Z','H','T',' '); /* Chinese, Traditional */ + *count = 1; + return true; + } + if (0 == strncmp (&lang_str[1], "zh-", 3) + && subtag_matches (lang_str, limit, "-hk", 3)) + { + /* Huizhou Chinese; Hong Kong */ + tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Traditional, Hong Kong SAR */ + *count = 1; + return true; + } + if (0 == strncmp (&lang_str[1], "zh-", 3) + && subtag_matches (lang_str, limit, "-mo", 3)) + { + /* Huizhou Chinese; Macao */ + unsigned int i; + hb_tag_t possible_tags[] = { + HB_TAG('Z','H','T','M'), /* Chinese, Traditional, Macao SAR */ + HB_TAG('Z','H','H',' '), /* Chinese, Traditional, Hong Kong SAR */ + }; + for (i = 0; i < 2 && i < *count; i++) + tags[i] = possible_tags[i]; + *count = i; + return true; + } + if (0 == strncmp (&lang_str[1], "zh-", 3) + && subtag_matches (lang_str, limit, "-tw", 3)) + { + /* Huizhou Chinese; Taiwan, Province of China */ + tags[0] = HB_TAG('Z','H','T',' '); /* Chinese, Traditional */ + *count = 1; + return true; + } + if (0 == strncmp (&lang_str[1], "zo-", 3) + && subtag_matches (lang_str, limit, "-hk", 3)) + { + /* Min Zhong Chinese; Hong Kong */ + tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Traditional, Hong Kong SAR */ + *count = 1; + return true; + } + if (0 == strncmp (&lang_str[1], "zo-", 3) + && subtag_matches (lang_str, limit, "-mo", 3)) + { + /* Min Zhong Chinese; Macao */ + unsigned int i; + hb_tag_t possible_tags[] = { + HB_TAG('Z','H','T','M'), /* Chinese, Traditional, Macao SAR */ + HB_TAG('Z','H','H',' '), /* Chinese, Traditional, Hong Kong SAR */ + }; + for (i = 0; i < 2 && i < *count; i++) + tags[i] = possible_tags[i]; + *count = i; + return true; + } + if (0 == strncmp (&lang_str[1], "zo-", 3) + && subtag_matches (lang_str, limit, "-tw", 3)) + { + /* Min Zhong Chinese; Taiwan, Province of China */ + tags[0] = HB_TAG('Z','H','T',' '); /* Chinese, Traditional */ + *count = 1; + return true; + } + break; + case 'g': + if (lang_matches (&lang_str[1], limit, "an-hant-hk", 10)) + { + /* Gan Chinese; Han (Traditional variant); Hong Kong */ + tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Traditional, Hong Kong SAR */ + *count = 1; + return true; + } + if (lang_matches (&lang_str[1], limit, "an-hant-mo", 10)) + { + /* Gan Chinese; Han (Traditional variant); Macao */ + unsigned int i; + hb_tag_t possible_tags[] = { + HB_TAG('Z','H','T','M'), /* Chinese, Traditional, Macao SAR */ + HB_TAG('Z','H','H',' '), /* Chinese, Traditional, Hong Kong SAR */ + }; + for (i = 0; i < 2 && i < *count; i++) + tags[i] = possible_tags[i]; + *count = i; + return true; + } + if (lang_matches (&lang_str[1], limit, "an-hans", 7)) + { + /* Gan Chinese; Han (Simplified variant) */ + tags[0] = HB_TAG('Z','H','S',' '); /* Chinese, Simplified */ + *count = 1; + return true; + } + if (lang_matches (&lang_str[1], limit, "an-hant", 7)) + { + /* Gan Chinese; Han (Traditional variant) */ + tags[0] = HB_TAG('Z','H','T',' '); /* Chinese, Traditional */ + *count = 1; + return true; + } + if (lang_matches (&lang_str[1], limit, "a-latg", 6)) + { + /* Irish; Latin (Gaelic variant) */ + tags[0] = HB_TAG('I','R','T',' '); /* Irish Traditional */ + *count = 1; + return true; + } + if (0 == strncmp (&lang_str[1], "an-", 3) + && subtag_matches (lang_str, limit, "-hk", 3)) + { + /* Gan Chinese; Hong Kong */ + tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Traditional, Hong Kong SAR */ + *count = 1; + return true; + } + if (0 == strncmp (&lang_str[1], "an-", 3) + && subtag_matches (lang_str, limit, "-mo", 3)) + { + /* Gan Chinese; Macao */ + unsigned int i; + hb_tag_t possible_tags[] = { + HB_TAG('Z','H','T','M'), /* Chinese, Traditional, Macao SAR */ + HB_TAG('Z','H','H',' '), /* Chinese, Traditional, Hong Kong SAR */ + }; + for (i = 0; i < 2 && i < *count; i++) + tags[i] = possible_tags[i]; + *count = i; + return true; + } + if (0 == strncmp (&lang_str[1], "an-", 3) + && subtag_matches (lang_str, limit, "-tw", 3)) + { + /* Gan Chinese; Taiwan, Province of China */ + tags[0] = HB_TAG('Z','H','T',' '); /* Chinese, Traditional */ + *count = 1; + return true; + } + break; + case 'h': + if (lang_matches (&lang_str[1], limit, "ak-hant-hk", 10)) + { + /* Hakka Chinese; Han (Traditional variant); Hong Kong */ + tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Traditional, Hong Kong SAR */ + *count = 1; + return true; + } + if (lang_matches (&lang_str[1], limit, "ak-hant-mo", 10)) + { + /* Hakka Chinese; Han (Traditional variant); Macao */ + unsigned int i; + hb_tag_t possible_tags[] = { + HB_TAG('Z','H','T','M'), /* Chinese, Traditional, Macao SAR */ + HB_TAG('Z','H','H',' '), /* Chinese, Traditional, Hong Kong SAR */ + }; + for (i = 0; i < 2 && i < *count; i++) + tags[i] = possible_tags[i]; + *count = i; + return true; + } + if (lang_matches (&lang_str[1], limit, "sn-hant-hk", 10)) + { + /* Xiang Chinese; Han (Traditional variant); Hong Kong */ + tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Traditional, Hong Kong SAR */ + *count = 1; + return true; + } + if (lang_matches (&lang_str[1], limit, "sn-hant-mo", 10)) + { + /* Xiang Chinese; Han (Traditional variant); Macao */ + unsigned int i; + hb_tag_t possible_tags[] = { + HB_TAG('Z','H','T','M'), /* Chinese, Traditional, Macao SAR */ + HB_TAG('Z','H','H',' '), /* Chinese, Traditional, Hong Kong SAR */ + }; + for (i = 0; i < 2 && i < *count; i++) + tags[i] = possible_tags[i]; + *count = i; + return true; + } + if (lang_matches (&lang_str[1], limit, "ak-hans", 7)) + { + /* Hakka Chinese; Han (Simplified variant) */ + tags[0] = HB_TAG('Z','H','S',' '); /* Chinese, Simplified */ + *count = 1; + return true; + } + if (lang_matches (&lang_str[1], limit, "ak-hant", 7)) + { + /* Hakka Chinese; Han (Traditional variant) */ + tags[0] = HB_TAG('Z','H','T',' '); /* Chinese, Traditional */ + *count = 1; + return true; + } + if (lang_matches (&lang_str[1], limit, "sn-hans", 7)) + { + /* Xiang Chinese; Han (Simplified variant) */ + tags[0] = HB_TAG('Z','H','S',' '); /* Chinese, Simplified */ + *count = 1; + return true; + } + if (lang_matches (&lang_str[1], limit, "sn-hant", 7)) + { + /* Xiang Chinese; Han (Traditional variant) */ + tags[0] = HB_TAG('Z','H','T',' '); /* Chinese, Traditional */ + *count = 1; + return true; + } + if (0 == strncmp (&lang_str[1], "ak-", 3) + && subtag_matches (lang_str, limit, "-hk", 3)) + { + /* Hakka Chinese; Hong Kong */ + tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Traditional, Hong Kong SAR */ + *count = 1; + return true; + } + if (0 == strncmp (&lang_str[1], "ak-", 3) + && subtag_matches (lang_str, limit, "-mo", 3)) + { + /* Hakka Chinese; Macao */ + unsigned int i; + hb_tag_t possible_tags[] = { + HB_TAG('Z','H','T','M'), /* Chinese, Traditional, Macao SAR */ + HB_TAG('Z','H','H',' '), /* Chinese, Traditional, Hong Kong SAR */ + }; + for (i = 0; i < 2 && i < *count; i++) + tags[i] = possible_tags[i]; + *count = i; + return true; + } + if (0 == strncmp (&lang_str[1], "ak-", 3) + && subtag_matches (lang_str, limit, "-tw", 3)) + { + /* Hakka Chinese; Taiwan, Province of China */ + tags[0] = HB_TAG('Z','H','T',' '); /* Chinese, Traditional */ + *count = 1; + return true; + } + if (0 == strncmp (&lang_str[1], "sn-", 3) + && subtag_matches (lang_str, limit, "-hk", 3)) + { + /* Xiang Chinese; Hong Kong */ + tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Traditional, Hong Kong SAR */ + *count = 1; + return true; + } + if (0 == strncmp (&lang_str[1], "sn-", 3) + && subtag_matches (lang_str, limit, "-mo", 3)) + { + /* Xiang Chinese; Macao */ + unsigned int i; + hb_tag_t possible_tags[] = { + HB_TAG('Z','H','T','M'), /* Chinese, Traditional, Macao SAR */ + HB_TAG('Z','H','H',' '), /* Chinese, Traditional, Hong Kong SAR */ + }; + for (i = 0; i < 2 && i < *count; i++) + tags[i] = possible_tags[i]; + *count = i; + return true; + } + if (0 == strncmp (&lang_str[1], "sn-", 3) + && subtag_matches (lang_str, limit, "-tw", 3)) + { + /* Xiang Chinese; Taiwan, Province of China */ + tags[0] = HB_TAG('Z','H','T',' '); /* Chinese, Traditional */ + *count = 1; + return true; + } + break; + case 'i': + if (0 == strcmp (&lang_str[1], "-navajo")) + { + /* Navajo (retired code) */ + unsigned int i; + hb_tag_t possible_tags[] = { + HB_TAG('N','A','V',' '), /* Navajo */ + HB_TAG('A','T','H',' '), /* Athapaskan */ + }; + for (i = 0; i < 2 && i < *count; i++) + tags[i] = possible_tags[i]; + *count = i; + return true; + } + if (0 == strcmp (&lang_str[1], "-hak")) + { + /* Hakka (retired code) */ + tags[0] = HB_TAG('Z','H','S',' '); /* Chinese, Simplified */ + *count = 1; + return true; + } + if (0 == strcmp (&lang_str[1], "-lux")) + { + /* Luxembourgish (retired code) */ + tags[0] = HB_TAG('L','T','Z',' '); /* Luxembourgish */ + *count = 1; + return true; + } + break; + case 'l': + if (lang_matches (&lang_str[1], limit, "zh-hans", 7)) + { + /* Literary Chinese; Han (Simplified variant) */ + tags[0] = HB_TAG('Z','H','S',' '); /* Chinese, Simplified */ + *count = 1; + return true; + } + break; + case 'm': + if (lang_matches (&lang_str[1], limit, "np-hant-hk", 10)) + { + /* Min Bei Chinese; Han (Traditional variant); Hong Kong */ + tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Traditional, Hong Kong SAR */ + *count = 1; + return true; + } + if (lang_matches (&lang_str[1], limit, "np-hant-mo", 10)) + { + /* Min Bei Chinese; Han (Traditional variant); Macao */ + unsigned int i; + hb_tag_t possible_tags[] = { + HB_TAG('Z','H','T','M'), /* Chinese, Traditional, Macao SAR */ + HB_TAG('Z','H','H',' '), /* Chinese, Traditional, Hong Kong SAR */ + }; + for (i = 0; i < 2 && i < *count; i++) + tags[i] = possible_tags[i]; + *count = i; + return true; + } + if (lang_matches (&lang_str[1], limit, "np-hans", 7)) + { + /* Min Bei Chinese; Han (Simplified variant) */ + tags[0] = HB_TAG('Z','H','S',' '); /* Chinese, Simplified */ + *count = 1; + return true; + } + if (lang_matches (&lang_str[1], limit, "np-hant", 7)) + { + /* Min Bei Chinese; Han (Traditional variant) */ + tags[0] = HB_TAG('Z','H','T',' '); /* Chinese, Traditional */ + *count = 1; + return true; + } + if (0 == strncmp (&lang_str[1], "np-", 3) + && subtag_matches (lang_str, limit, "-hk", 3)) + { + /* Min Bei Chinese; Hong Kong */ + tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Traditional, Hong Kong SAR */ + *count = 1; + return true; + } + if (0 == strncmp (&lang_str[1], "np-", 3) + && subtag_matches (lang_str, limit, "-mo", 3)) + { + /* Min Bei Chinese; Macao */ + unsigned int i; + hb_tag_t possible_tags[] = { + HB_TAG('Z','H','T','M'), /* Chinese, Traditional, Macao SAR */ + HB_TAG('Z','H','H',' '), /* Chinese, Traditional, Hong Kong SAR */ + }; + for (i = 0; i < 2 && i < *count; i++) + tags[i] = possible_tags[i]; + *count = i; + return true; + } + if (0 == strncmp (&lang_str[1], "np-", 3) + && subtag_matches (lang_str, limit, "-tw", 3)) + { + /* Min Bei Chinese; Taiwan, Province of China */ + tags[0] = HB_TAG('Z','H','T',' '); /* Chinese, Traditional */ + *count = 1; + return true; + } + if (0 == strncmp (&lang_str[1], "nw-", 3) + && subtag_matches (lang_str, limit, "-th", 3)) + { + /* Mon; Thailand */ + tags[0] = HB_TAG('M','O','N','T'); /* Thailand Mon */ + *count = 1; + return true; + } + break; + case 'n': + if (lang_matches (&lang_str[1], limit, "an-hant-hk", 10)) + { + /* Min Nan Chinese; Han (Traditional variant); Hong Kong */ + tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Traditional, Hong Kong SAR */ + *count = 1; + return true; + } + if (lang_matches (&lang_str[1], limit, "an-hant-mo", 10)) + { + /* Min Nan Chinese; Han (Traditional variant); Macao */ + unsigned int i; + hb_tag_t possible_tags[] = { + HB_TAG('Z','H','T','M'), /* Chinese, Traditional, Macao SAR */ + HB_TAG('Z','H','H',' '), /* Chinese, Traditional, Hong Kong SAR */ + }; + for (i = 0; i < 2 && i < *count; i++) + tags[i] = possible_tags[i]; + *count = i; + return true; + } + if (lang_matches (&lang_str[1], limit, "an-hans", 7)) + { + /* Min Nan Chinese; Han (Simplified variant) */ + tags[0] = HB_TAG('Z','H','S',' '); /* Chinese, Simplified */ + *count = 1; + return true; + } + if (lang_matches (&lang_str[1], limit, "an-hant", 7)) + { + /* Min Nan Chinese; Han (Traditional variant) */ + tags[0] = HB_TAG('Z','H','T',' '); /* Chinese, Traditional */ + *count = 1; + return true; + } + if (0 == strncmp (&lang_str[1], "an-", 3) + && subtag_matches (lang_str, limit, "-hk", 3)) + { + /* Min Nan Chinese; Hong Kong */ + tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Traditional, Hong Kong SAR */ + *count = 1; + return true; + } + if (0 == strncmp (&lang_str[1], "an-", 3) + && subtag_matches (lang_str, limit, "-mo", 3)) + { + /* Min Nan Chinese; Macao */ + unsigned int i; + hb_tag_t possible_tags[] = { + HB_TAG('Z','H','T','M'), /* Chinese, Traditional, Macao SAR */ + HB_TAG('Z','H','H',' '), /* Chinese, Traditional, Hong Kong SAR */ + }; + for (i = 0; i < 2 && i < *count; i++) + tags[i] = possible_tags[i]; + *count = i; + return true; + } + if (0 == strncmp (&lang_str[1], "an-", 3) + && subtag_matches (lang_str, limit, "-tw", 3)) + { + /* Min Nan Chinese; Taiwan, Province of China */ + tags[0] = HB_TAG('Z','H','T',' '); /* Chinese, Traditional */ + *count = 1; + return true; + } + if (0 == strcmp (&lang_str[1], "o-bok")) + { + /* Norwegian Bokmal (retired code) */ + tags[0] = HB_TAG('N','O','R',' '); /* Norwegian */ + *count = 1; + return true; + } + if (0 == strcmp (&lang_str[1], "o-nyn")) + { + /* Norwegian Nynorsk (retired code) */ + tags[0] = HB_TAG('N','Y','N',' '); /* Norwegian Nynorsk (Nynorsk, Norwegian) */ + *count = 1; + return true; + } + break; + case 'r': + if (0 == strncmp (&lang_str[1], "o-", 2) + && subtag_matches (lang_str, limit, "-md", 3)) + { + /* Romanian; Moldova */ + unsigned int i; + hb_tag_t possible_tags[] = { + HB_TAG('M','O','L',' '), /* Moldavian */ + HB_TAG('R','O','M',' '), /* Romanian */ + }; + for (i = 0; i < 2 && i < *count; i++) + tags[i] = possible_tags[i]; + *count = i; + return true; + } + break; + case 'w': + if (lang_matches (&lang_str[1], limit, "uu-hant-hk", 10)) + { + /* Wu Chinese; Han (Traditional variant); Hong Kong */ + tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Traditional, Hong Kong SAR */ + *count = 1; + return true; + } + if (lang_matches (&lang_str[1], limit, "uu-hant-mo", 10)) + { + /* Wu Chinese; Han (Traditional variant); Macao */ + unsigned int i; + hb_tag_t possible_tags[] = { + HB_TAG('Z','H','T','M'), /* Chinese, Traditional, Macao SAR */ + HB_TAG('Z','H','H',' '), /* Chinese, Traditional, Hong Kong SAR */ + }; + for (i = 0; i < 2 && i < *count; i++) + tags[i] = possible_tags[i]; + *count = i; + return true; + } + if (lang_matches (&lang_str[1], limit, "uu-hans", 7)) + { + /* Wu Chinese; Han (Simplified variant) */ + tags[0] = HB_TAG('Z','H','S',' '); /* Chinese, Simplified */ + *count = 1; + return true; + } + if (lang_matches (&lang_str[1], limit, "uu-hant", 7)) + { + /* Wu Chinese; Han (Traditional variant) */ + tags[0] = HB_TAG('Z','H','T',' '); /* Chinese, Traditional */ + *count = 1; + return true; + } + if (0 == strncmp (&lang_str[1], "uu-", 3) + && subtag_matches (lang_str, limit, "-hk", 3)) + { + /* Wu Chinese; Hong Kong */ + tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Traditional, Hong Kong SAR */ + *count = 1; + return true; + } + if (0 == strncmp (&lang_str[1], "uu-", 3) + && subtag_matches (lang_str, limit, "-mo", 3)) + { + /* Wu Chinese; Macao */ + unsigned int i; + hb_tag_t possible_tags[] = { + HB_TAG('Z','H','T','M'), /* Chinese, Traditional, Macao SAR */ + HB_TAG('Z','H','H',' '), /* Chinese, Traditional, Hong Kong SAR */ + }; + for (i = 0; i < 2 && i < *count; i++) + tags[i] = possible_tags[i]; + *count = i; + return true; + } + if (0 == strncmp (&lang_str[1], "uu-", 3) + && subtag_matches (lang_str, limit, "-tw", 3)) + { + /* Wu Chinese; Taiwan, Province of China */ + tags[0] = HB_TAG('Z','H','T',' '); /* Chinese, Traditional */ + *count = 1; + return true; + } + break; + case 'y': + if (lang_matches (&lang_str[1], limit, "ue-hans", 7)) + { + /* Yue Chinese; Han (Simplified variant) */ + tags[0] = HB_TAG('Z','H','S',' '); /* Chinese, Simplified */ + *count = 1; + return true; + } + break; + case 'z': + if (lang_matches (&lang_str[1], limit, "h-hant-hk", 9)) + { + /* Chinese [macrolanguage]; Han (Traditional variant); Hong Kong */ + tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Traditional, Hong Kong SAR */ + *count = 1; + return true; + } + if (lang_matches (&lang_str[1], limit, "h-hant-mo", 9)) + { + /* Chinese [macrolanguage]; Han (Traditional variant); Macao */ + unsigned int i; + hb_tag_t possible_tags[] = { + HB_TAG('Z','H','T','M'), /* Chinese, Traditional, Macao SAR */ + HB_TAG('Z','H','H',' '), /* Chinese, Traditional, Hong Kong SAR */ + }; + for (i = 0; i < 2 && i < *count; i++) + tags[i] = possible_tags[i]; + *count = i; + return true; + } + if (0 == strcmp (&lang_str[1], "h-min-nan")) + { + /* Minnan, Hokkien, Amoy, Taiwanese, Southern Min, Southern Fujian, Hoklo, Southern Fukien, Ho-lo (retired code) */ + tags[0] = HB_TAG('Z','H','S',' '); /* Chinese, Simplified */ + *count = 1; + return true; + } + if (lang_matches (&lang_str[1], limit, "h-hans", 6)) + { + /* Chinese [macrolanguage]; Han (Simplified variant) */ + tags[0] = HB_TAG('Z','H','S',' '); /* Chinese, Simplified */ + *count = 1; + return true; + } + if (lang_matches (&lang_str[1], limit, "h-hant", 6)) + { + /* Chinese [macrolanguage]; Han (Traditional variant) */ + tags[0] = HB_TAG('Z','H','T',' '); /* Chinese, Traditional */ + *count = 1; + return true; + } + if (0 == strcmp (&lang_str[1], "h-min")) + { + /* Min, Fuzhou, Hokkien, Amoy, or Taiwanese (retired code) */ + tags[0] = HB_TAG('Z','H','S',' '); /* Chinese, Simplified */ + *count = 1; + return true; + } + if (0 == strncmp (&lang_str[1], "h-", 2) + && subtag_matches (lang_str, limit, "-hk", 3)) + { + /* Chinese [macrolanguage]; Hong Kong */ + tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Traditional, Hong Kong SAR */ + *count = 1; + return true; + } + if (0 == strncmp (&lang_str[1], "h-", 2) + && subtag_matches (lang_str, limit, "-mo", 3)) + { + /* Chinese [macrolanguage]; Macao */ + unsigned int i; + hb_tag_t possible_tags[] = { + HB_TAG('Z','H','T','M'), /* Chinese, Traditional, Macao SAR */ + HB_TAG('Z','H','H',' '), /* Chinese, Traditional, Hong Kong SAR */ + }; + for (i = 0; i < 2 && i < *count; i++) + tags[i] = possible_tags[i]; + *count = i; + return true; + } + if (0 == strncmp (&lang_str[1], "h-", 2) + && subtag_matches (lang_str, limit, "-tw", 3)) + { + /* Chinese [macrolanguage]; Taiwan, Province of China */ + tags[0] = HB_TAG('Z','H','T',' '); /* Chinese, Traditional */ + *count = 1; + return true; + } + break; + } + return false; +} + +/** + * hb_ot_ambiguous_tag_to_language + * @tag: A language tag. + * + * Converts @tag to a BCP 47 language tag if it is ambiguous (it corresponds to + * many language tags) and the best tag is not the alphabetically first, or if + * the best tag consists of multiple subtags, or if the best tag does not appear + * in #ot_languages. + * + * Return value: The #hb_language_t corresponding to the BCP 47 language tag, + * or #HB_LANGUAGE_INVALID if @tag is not ambiguous. + **/ +static inline hb_language_t +hb_ot_ambiguous_tag_to_language (hb_tag_t tag) +{ + switch (tag) + { + case HB_TAG('A','L','T',' '): /* Altai */ + return hb_language_from_string ("alt", -1); /* Southern Altai */ + case HB_TAG('A','P','P','H'): /* Phonetic transcription—Americanist conventions */ + return hb_language_from_string ("und-fonnapa", -1); /* Undetermined; North American Phonetic Alphabet */ + case HB_TAG('A','R','A',' '): /* Arabic */ + return hb_language_from_string ("ar", -1); /* Arabic [macrolanguage] */ + case HB_TAG('A','R','K',' '): /* Rakhine */ + return hb_language_from_string ("rki", -1); /* Rakhine */ + case HB_TAG('A','T','H',' '): /* Athapaskan */ + return hb_language_from_string ("ath", -1); /* Athapascan [collection] */ + case HB_TAG('B','B','R',' '): /* Berber */ + return hb_language_from_string ("ber", -1); /* Berber [collection] */ + case HB_TAG('B','I','K',' '): /* Bikol */ + return hb_language_from_string ("bik", -1); /* Bikol [macrolanguage] */ + case HB_TAG('B','T','K',' '): /* Batak */ + return hb_language_from_string ("btk", -1); /* Batak [collection] */ + case HB_TAG('C','P','P',' '): /* Creoles */ + return hb_language_from_string ("crp", -1); /* Creoles and pidgins [collection] */ + case HB_TAG('C','R','R',' '): /* Carrier */ + return hb_language_from_string ("crx", -1); /* Carrier */ + case HB_TAG('D','G','R',' '): /* Dogri (macrolanguage) */ + return hb_language_from_string ("doi", -1); /* Dogri [macrolanguage] */ + case HB_TAG('D','N','K',' '): /* Dinka */ + return hb_language_from_string ("din", -1); /* Dinka [macrolanguage] */ + case HB_TAG('D','R','I',' '): /* Dari */ + return hb_language_from_string ("prs", -1); /* Dari */ + case HB_TAG('D','Z','N',' '): /* Dzongkha */ + return hb_language_from_string ("dz", -1); /* Dzongkha */ + case HB_TAG('E','T','I',' '): /* Estonian */ + return hb_language_from_string ("et", -1); /* Estonian [macrolanguage] */ + case HB_TAG('F','A','R',' '): /* Persian */ + return hb_language_from_string ("fa", -1); /* Persian [macrolanguage] */ + case HB_TAG('G','O','N',' '): /* Gondi */ + return hb_language_from_string ("gon", -1); /* Gondi [macrolanguage] */ + case HB_TAG('H','M','A',' '): /* High Mari */ + return hb_language_from_string ("mrj", -1); /* Western Mari */ + case HB_TAG('H','M','N',' '): /* Hmong */ + return hb_language_from_string ("hmn", -1); /* Hmong [macrolanguage] */ + case HB_TAG('H','N','D',' '): /* Hindko */ + return hb_language_from_string ("hnd", -1); /* Southern Hindko */ + case HB_TAG('H','Y','E',' '): /* Armenian */ + return hb_language_from_string ("hyw", -1); /* Western Armenian */ + case HB_TAG('I','B','A',' '): /* Iban */ + return hb_language_from_string ("iba", -1); /* Iban */ + case HB_TAG('I','J','O',' '): /* Ijo */ + return hb_language_from_string ("ijo", -1); /* Ijo [collection] */ + case HB_TAG('I','N','U',' '): /* Inuktitut */ + return hb_language_from_string ("iu", -1); /* Inuktitut [macrolanguage] */ + case HB_TAG('I','P','K',' '): /* Inupiat */ + return hb_language_from_string ("ik", -1); /* Inupiaq [macrolanguage] */ + case HB_TAG('I','P','P','H'): /* Phonetic transcription—IPA conventions */ + return hb_language_from_string ("und-fonipa", -1); /* Undetermined; International Phonetic Alphabet */ + case HB_TAG('I','R','T',' '): /* Irish Traditional */ + return hb_language_from_string ("ga-Latg", -1); /* Irish; Latin (Gaelic variant) */ + case HB_TAG('J','I','I',' '): /* Yiddish */ + return hb_language_from_string ("yi", -1); /* Yiddish [macrolanguage] */ + case HB_TAG('K','A','L',' '): /* Kalenjin */ + return hb_language_from_string ("kln", -1); /* Kalenjin [macrolanguage] */ + case HB_TAG('K','G','E',' '): /* Khutsuri Georgian */ + return hb_language_from_string ("und-Geok", -1); /* Undetermined; Khutsuri (Asomtavruli and Nuskhuri) */ + case HB_TAG('K','N','R',' '): /* Kanuri */ + return hb_language_from_string ("kr", -1); /* Kanuri [macrolanguage] */ + case HB_TAG('K','O','H',' '): /* Korean Old Hangul */ + return hb_language_from_string ("okm", -1); /* Middle Korean (10th-16th cent.) */ + case HB_TAG('K','O','K',' '): /* Konkani */ + return hb_language_from_string ("kok", -1); /* Konkani [macrolanguage] */ + case HB_TAG('K','O','M',' '): /* Komi */ + return hb_language_from_string ("kv", -1); /* Komi [macrolanguage] */ + case HB_TAG('K','P','L',' '): /* Kpelle */ + return hb_language_from_string ("kpe", -1); /* Kpelle [macrolanguage] */ + case HB_TAG('K','R','N',' '): /* Karen */ + return hb_language_from_string ("kar", -1); /* Karen [collection] */ + case HB_TAG('K','U','I',' '): /* Kui */ + return hb_language_from_string ("uki", -1); /* Kui (India) */ + case HB_TAG('K','U','R',' '): /* Kurdish */ + return hb_language_from_string ("ku", -1); /* Kurdish [macrolanguage] */ + case HB_TAG('L','M','A',' '): /* Low Mari */ + return hb_language_from_string ("mhr", -1); /* Eastern Mari */ + case HB_TAG('L','U','H',' '): /* Luyia */ + return hb_language_from_string ("luy", -1); /* Luyia [macrolanguage] */ + case HB_TAG('L','V','I',' '): /* Latvian */ + return hb_language_from_string ("lv", -1); /* Latvian [macrolanguage] */ + case HB_TAG('M','A','W',' '): /* Marwari */ + return hb_language_from_string ("mwr", -1); /* Marwari [macrolanguage] */ + case HB_TAG('M','L','G',' '): /* Malagasy */ + return hb_language_from_string ("mg", -1); /* Malagasy [macrolanguage] */ + case HB_TAG('M','L','Y',' '): /* Malay */ + return hb_language_from_string ("ms", -1); /* Malay [macrolanguage] */ + case HB_TAG('M','N','G',' '): /* Mongolian */ + return hb_language_from_string ("mn", -1); /* Mongolian [macrolanguage] */ + case HB_TAG('M','N','K',' '): /* Maninka */ + return hb_language_from_string ("man", -1); /* Mandingo [macrolanguage] */ + case HB_TAG('M','O','L',' '): /* Moldavian */ + return hb_language_from_string ("ro-MD", -1); /* Romanian; Moldova */ + case HB_TAG('M','O','N','T'): /* Thailand Mon */ + return hb_language_from_string ("mnw-TH", -1); /* Mon; Thailand */ + case HB_TAG('M','Y','N',' '): /* Mayan */ + return hb_language_from_string ("myn", -1); /* Mayan [collection] */ + case HB_TAG('N','A','H',' '): /* Nahuatl */ + return hb_language_from_string ("nah", -1); /* Nahuatl [collection] */ + case HB_TAG('N','E','P',' '): /* Nepali */ + return hb_language_from_string ("ne", -1); /* Nepali [macrolanguage] */ + case HB_TAG('N','I','S',' '): /* Nisi */ + return hb_language_from_string ("njz", -1); /* Nyishi */ + case HB_TAG('N','O','R',' '): /* Norwegian */ + return hb_language_from_string ("no", -1); /* Norwegian [macrolanguage] */ + case HB_TAG('O','J','B',' '): /* Ojibway */ + return hb_language_from_string ("oj", -1); /* Ojibwa [macrolanguage] */ + case HB_TAG('O','R','O',' '): /* Oromo */ + return hb_language_from_string ("om", -1); /* Oromo [macrolanguage] */ + case HB_TAG('P','A','S',' '): /* Pashto */ + return hb_language_from_string ("ps", -1); /* Pashto [macrolanguage] */ + case HB_TAG('P','G','R',' '): /* Polytonic Greek */ + return hb_language_from_string ("el-polyton", -1); /* Modern Greek (1453-); Polytonic Greek */ + case HB_TAG('P','R','O',' '): /* Provençal / Old Provençal */ + return hb_language_from_string ("pro", -1); /* Old Provençal (to 1500) */ + case HB_TAG('Q','U','H',' '): /* Quechua (Bolivia) */ + return hb_language_from_string ("quh", -1); /* South Bolivian Quechua */ + case HB_TAG('Q','U','Z',' '): /* Quechua */ + return hb_language_from_string ("qu", -1); /* Quechua [macrolanguage] */ + case HB_TAG('Q','V','I',' '): /* Quechua (Ecuador) */ + return hb_language_from_string ("qvi", -1); /* Imbabura Highland Quichua */ + case HB_TAG('Q','W','H',' '): /* Quechua (Peru) */ + return hb_language_from_string ("qwh", -1); /* Huaylas Ancash Quechua */ + case HB_TAG('R','A','J',' '): /* Rajasthani */ + return hb_language_from_string ("raj", -1); /* Rajasthani [macrolanguage] */ + case HB_TAG('R','O','M',' '): /* Romanian */ + return hb_language_from_string ("ro", -1); /* Romanian */ + case HB_TAG('R','O','Y',' '): /* Romany */ + return hb_language_from_string ("rom", -1); /* Romany [macrolanguage] */ + case HB_TAG('S','Q','I',' '): /* Albanian */ + return hb_language_from_string ("sq", -1); /* Albanian [macrolanguage] */ + case HB_TAG('S','R','B',' '): /* Serbian */ + return hb_language_from_string ("sr", -1); /* Serbian */ + case HB_TAG('S','X','T',' '): /* Sutu */ + return hb_language_from_string ("xnj", -1); /* Ngoni (Tanzania) */ + case HB_TAG('S','Y','R',' '): /* Syriac */ + return hb_language_from_string ("syr", -1); /* Syriac [macrolanguage] */ + case HB_TAG('S','Y','R','E'): /* Syriac, Estrangela script-variant (equivalent to ISO 15924 'Syre') */ + return hb_language_from_string ("und-Syre", -1); /* Undetermined; Syriac (Estrangelo variant) */ + case HB_TAG('S','Y','R','J'): /* Syriac, Western script-variant (equivalent to ISO 15924 'Syrj') */ + return hb_language_from_string ("und-Syrj", -1); /* Undetermined; Syriac (Western variant) */ + case HB_TAG('S','Y','R','N'): /* Syriac, Eastern script-variant (equivalent to ISO 15924 'Syrn') */ + return hb_language_from_string ("und-Syrn", -1); /* Undetermined; Syriac (Eastern variant) */ + case HB_TAG('T','M','H',' '): /* Tamashek */ + return hb_language_from_string ("tmh", -1); /* Tamashek [macrolanguage] */ + case HB_TAG('T','O','D',' '): /* Todo */ + return hb_language_from_string ("xwo", -1); /* Written Oirat */ + case HB_TAG('Z','H','H',' '): /* Chinese, Traditional, Hong Kong SAR */ + return hb_language_from_string ("zh-HK", -1); /* Chinese [macrolanguage]; Hong Kong */ + case HB_TAG('Z','H','S',' '): /* Chinese, Simplified */ + return hb_language_from_string ("zh-Hans", -1); /* Chinese [macrolanguage]; Han (Simplified variant) */ + case HB_TAG('Z','H','T',' '): /* Chinese, Traditional */ + return hb_language_from_string ("zh-Hant", -1); /* Chinese [macrolanguage]; Han (Traditional variant) */ + case HB_TAG('Z','H','T','M'): /* Chinese, Traditional, Macao SAR */ + return hb_language_from_string ("zh-MO", -1); /* Chinese [macrolanguage]; Macao */ + case HB_TAG('Z','Z','A',' '): /* Zazaki */ + return hb_language_from_string ("zza", -1); /* Zazaki [macrolanguage] */ + default: + return HB_LANGUAGE_INVALID; + } +} + +#endif /* HB_OT_TAG_TABLE_HH */ + +/* == End of generated table == */ diff --git a/libs/harfbuzz/src/hb-ot-tag.cc b/libs/harfbuzz/src/hb-ot-tag.cc new file mode 100644 index 00000000..1b18270c --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-tag.cc @@ -0,0 +1,638 @@ +/* + * Copyright © 2009 Red Hat, Inc. + * 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. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod, Roozbeh Pournader + */ + +#include "hb.hh" + +#ifndef HB_NO_OT_TAG + + +/* hb_script_t */ + +static hb_tag_t +hb_ot_old_tag_from_script (hb_script_t script) +{ + /* This seems to be accurate as of end of 2012. */ + + switch ((hb_tag_t) script) + { + case HB_SCRIPT_INVALID: return HB_OT_TAG_DEFAULT_SCRIPT; + case HB_SCRIPT_MATH: return HB_OT_TAG_MATH_SCRIPT; + + /* KATAKANA and HIRAGANA both map to 'kana' */ + case HB_SCRIPT_HIRAGANA: return HB_TAG('k','a','n','a'); + + /* Spaces at the end are preserved, unlike ISO 15924 */ + case HB_SCRIPT_LAO: return HB_TAG('l','a','o',' '); + case HB_SCRIPT_YI: return HB_TAG('y','i',' ',' '); + /* Unicode-5.0 additions */ + case HB_SCRIPT_NKO: return HB_TAG('n','k','o',' '); + /* Unicode-5.1 additions */ + case HB_SCRIPT_VAI: return HB_TAG('v','a','i',' '); + } + + /* Else, just change first char to lowercase and return */ + return ((hb_tag_t) script) | 0x20000000u; +} + +static hb_script_t +hb_ot_old_tag_to_script (hb_tag_t tag) +{ + if (unlikely (tag == HB_OT_TAG_DEFAULT_SCRIPT)) + return HB_SCRIPT_INVALID; + if (unlikely (tag == HB_OT_TAG_MATH_SCRIPT)) + return HB_SCRIPT_MATH; + + /* This side of the conversion is fully algorithmic. */ + + /* Any spaces at the end of the tag are replaced by repeating the last + * letter. Eg 'nko ' -> 'Nkoo' */ + if (unlikely ((tag & 0x0000FF00u) == 0x00002000u)) + tag |= (tag >> 8) & 0x0000FF00u; /* Copy second letter to third */ + if (unlikely ((tag & 0x000000FFu) == 0x00000020u)) + tag |= (tag >> 8) & 0x000000FFu; /* Copy third letter to fourth */ + + /* Change first char to uppercase and return */ + return (hb_script_t) (tag & ~0x20000000u); +} + +static hb_tag_t +hb_ot_new_tag_from_script (hb_script_t script) +{ + switch ((hb_tag_t) script) { + case HB_SCRIPT_BENGALI: return HB_TAG('b','n','g','2'); + case HB_SCRIPT_DEVANAGARI: return HB_TAG('d','e','v','2'); + case HB_SCRIPT_GUJARATI: return HB_TAG('g','j','r','2'); + case HB_SCRIPT_GURMUKHI: return HB_TAG('g','u','r','2'); + case HB_SCRIPT_KANNADA: return HB_TAG('k','n','d','2'); + case HB_SCRIPT_MALAYALAM: return HB_TAG('m','l','m','2'); + case HB_SCRIPT_ORIYA: return HB_TAG('o','r','y','2'); + case HB_SCRIPT_TAMIL: return HB_TAG('t','m','l','2'); + case HB_SCRIPT_TELUGU: return HB_TAG('t','e','l','2'); + case HB_SCRIPT_MYANMAR: return HB_TAG('m','y','m','2'); + } + + return HB_OT_TAG_DEFAULT_SCRIPT; +} + +static hb_script_t +hb_ot_new_tag_to_script (hb_tag_t tag) +{ + switch (tag) { + case HB_TAG('b','n','g','2'): return HB_SCRIPT_BENGALI; + case HB_TAG('d','e','v','2'): return HB_SCRIPT_DEVANAGARI; + case HB_TAG('g','j','r','2'): return HB_SCRIPT_GUJARATI; + case HB_TAG('g','u','r','2'): return HB_SCRIPT_GURMUKHI; + case HB_TAG('k','n','d','2'): return HB_SCRIPT_KANNADA; + case HB_TAG('m','l','m','2'): return HB_SCRIPT_MALAYALAM; + case HB_TAG('o','r','y','2'): return HB_SCRIPT_ORIYA; + case HB_TAG('t','m','l','2'): return HB_SCRIPT_TAMIL; + case HB_TAG('t','e','l','2'): return HB_SCRIPT_TELUGU; + case HB_TAG('m','y','m','2'): return HB_SCRIPT_MYANMAR; + } + + return HB_SCRIPT_UNKNOWN; +} + +#ifndef HB_DISABLE_DEPRECATED +void +hb_ot_tags_from_script (hb_script_t script, + hb_tag_t *script_tag_1, + hb_tag_t *script_tag_2) +{ + unsigned int count = 2; + hb_tag_t tags[2]; + hb_ot_tags_from_script_and_language (script, HB_LANGUAGE_INVALID, &count, tags, nullptr, nullptr); + *script_tag_1 = count > 0 ? tags[0] : HB_OT_TAG_DEFAULT_SCRIPT; + *script_tag_2 = count > 1 ? tags[1] : HB_OT_TAG_DEFAULT_SCRIPT; +} +#endif + +/* + * Complete list at: + * https://docs.microsoft.com/en-us/typography/opentype/spec/scripttags + * + * Most of the script tags are the same as the ISO 15924 tag but lowercased. + * So we just do that, and handle the exceptional cases in a switch. + */ + +static void +hb_ot_all_tags_from_script (hb_script_t script, + unsigned int *count /* IN/OUT */, + hb_tag_t *tags /* OUT */) +{ + unsigned int i = 0; + + hb_tag_t new_tag = hb_ot_new_tag_from_script (script); + if (unlikely (new_tag != HB_OT_TAG_DEFAULT_SCRIPT)) + { + /* HB_SCRIPT_MYANMAR maps to 'mym2', but there is no 'mym3'. */ + if (new_tag != HB_TAG('m','y','m','2')) + tags[i++] = new_tag | '3'; + if (*count > i) + tags[i++] = new_tag; + } + + if (*count > i) + { + hb_tag_t old_tag = hb_ot_old_tag_from_script (script); + if (old_tag != HB_OT_TAG_DEFAULT_SCRIPT) + tags[i++] = old_tag; + } + + *count = i; +} + +/** + * hb_ot_tag_to_script: + * @tag: a script tag + * + * Converts a script tag to an #hb_script_t. + * + * Return value: The #hb_script_t corresponding to @tag. + * + **/ +hb_script_t +hb_ot_tag_to_script (hb_tag_t tag) +{ + unsigned char digit = tag & 0x000000FFu; + if (unlikely (digit == '2' || digit == '3')) + return hb_ot_new_tag_to_script (tag & 0xFFFFFF32); + + return hb_ot_old_tag_to_script (tag); +} + + +/* hb_language_t */ + +static inline bool +subtag_matches (const char *lang_str, + const char *limit, + const char *subtag, + unsigned subtag_len) +{ + if (likely ((unsigned) (limit - lang_str) < subtag_len)) + return false; + + do { + const char *s = strstr (lang_str, subtag); + if (!s || s >= limit) + return false; + if (!ISALNUM (s[subtag_len])) + return true; + lang_str = s + subtag_len; + } while (true); +} + +static bool +lang_matches (const char *lang_str, + const char *limit, + const char *spec, + unsigned spec_len) +{ + /* Same as hb_language_matches(); duplicated. */ + + if (likely ((unsigned) (limit - lang_str) < spec_len)) + return false; + + return strncmp (lang_str, spec, spec_len) == 0 && + (lang_str[spec_len] == '\0' || lang_str[spec_len] == '-'); +} + +struct LangTag +{ + hb_tag_t language; + hb_tag_t tag; + + int cmp (hb_tag_t a) const + { + return a < this->language ? -1 : a > this->language ? +1 : 0; + } + int cmp (const LangTag *that) const + { return cmp (that->language); } +}; + +#include "hb-ot-tag-table.hh" + +/* The corresponding languages IDs for the following IDs are unclear, + * overlap, or are architecturally weird. Needs more research. */ + +/*{"??", {HB_TAG('B','C','R',' ')}},*/ /* Bible Cree */ +/*{"zh?", {HB_TAG('C','H','N',' ')}},*/ /* Chinese (seen in Microsoft fonts) */ +/*{"ar-Syrc?", {HB_TAG('G','A','R',' ')}},*/ /* Garshuni */ +/*{"??", {HB_TAG('N','G','R',' ')}},*/ /* Nagari */ +/*{"??", {HB_TAG('Y','I','C',' ')}},*/ /* Yi Classic */ +/*{"zh?", {HB_TAG('Z','H','P',' ')}},*/ /* Chinese Phonetic */ + +#ifndef HB_DISABLE_DEPRECATED +hb_tag_t +hb_ot_tag_from_language (hb_language_t language) +{ + unsigned int count = 1; + hb_tag_t tags[1]; + hb_ot_tags_from_script_and_language (HB_SCRIPT_UNKNOWN, language, nullptr, nullptr, &count, tags); + return count > 0 ? tags[0] : HB_OT_TAG_DEFAULT_LANGUAGE; +} +#endif + +static void +hb_ot_tags_from_language (const char *lang_str, + const char *limit, + unsigned int *count, + hb_tag_t *tags) +{ + +#ifndef HB_NO_LANGUAGE_LONG + /* Check for matches of multiple subtags. */ + if (hb_ot_tags_from_complex_language (lang_str, limit, count, tags)) + return; +#endif + + /* Find a language matching in the first component. */ +#ifndef HB_NO_LANGUAGE_LONG + const char *s; s = strchr (lang_str, '-'); +#endif + { +#ifndef HB_NO_LANGUAGE_LONG + if (s && limit - lang_str >= 6) + { + const char *extlang_end = strchr (s + 1, '-'); + /* If there is an extended language tag, use it. */ + if (3 == (extlang_end ? extlang_end - s - 1 : strlen (s + 1)) && + ISALPHA (s[1])) + lang_str = s + 1; + } +#endif + const LangTag *ot_languages = nullptr; + unsigned ot_languages_len = 0; + const char *dash = strchr (lang_str, '-'); + unsigned first_len = dash ? dash - lang_str : limit - lang_str; + if (first_len == 2) + { + ot_languages = ot_languages2; + ot_languages_len = ARRAY_LENGTH (ot_languages2); + } +#ifndef HB_NO_LANGUAGE_LONG + else if (first_len == 3) + { + ot_languages = ot_languages3; + ot_languages_len = ARRAY_LENGTH (ot_languages3); + } +#endif + + hb_tag_t lang_tag = hb_tag_from_string (lang_str, first_len); + + static hb_atomic_int_t last_tag_idx; /* Poor man's cache. */ + unsigned tag_idx = last_tag_idx; + + if (likely (tag_idx < ot_languages_len && ot_languages[tag_idx].language == lang_tag) || + hb_sorted_array (ot_languages, ot_languages_len).bfind (lang_tag, &tag_idx)) + { + last_tag_idx = tag_idx; + unsigned int i; + while (tag_idx != 0 && + ot_languages[tag_idx].language == ot_languages[tag_idx - 1].language) + tag_idx--; + for (i = 0; + i < *count && + tag_idx + i < ot_languages_len && + ot_languages[tag_idx + i].tag != HB_TAG_NONE && + ot_languages[tag_idx + i].language == ot_languages[tag_idx].language; + i++) + tags[i] = ot_languages[tag_idx + i].tag; + *count = i; + return; + } + } + +#ifndef HB_NO_LANGUAGE_LONG + if (!s) + s = lang_str + strlen (lang_str); + if (s - lang_str == 3) { + /* Assume it's ISO-639-3 and upper-case and use it. */ + tags[0] = hb_tag_from_string (lang_str, s - lang_str) & ~0x20202000u; + *count = 1; + return; + } +#endif + + *count = 0; +} + +static bool +parse_private_use_subtag (const char *private_use_subtag, + unsigned int *count, + hb_tag_t *tags, + const char *prefix, + unsigned char (*normalize) (unsigned char)) +{ +#ifdef HB_NO_LANGUAGE_PRIVATE_SUBTAG + return false; +#endif + + if (!(private_use_subtag && count && tags && *count)) return false; + + const char *s = strstr (private_use_subtag, prefix); + if (!s) return false; + + char tag[4]; + int i; + s += strlen (prefix); + if (s[0] == '-') { + s += 1; + char c; + for (i = 0; i < 8 && ISHEX (s[i]); i++) + { + c = FROMHEX (s[i]); + if (i % 2 == 0) + tag[i / 2] = c << 4; + else + tag[i / 2] += c; + } + if (i != 8) return false; + } else { + for (i = 0; i < 4 && ISALNUM (s[i]); i++) + tag[i] = normalize (s[i]); + if (!i) return false; + + for (; i < 4; i++) + tag[i] = ' '; + } + tags[0] = HB_TAG (tag[0], tag[1], tag[2], tag[3]); + if ((tags[0] & 0xDFDFDFDF) == HB_OT_TAG_DEFAULT_SCRIPT) + tags[0] ^= ~0xDFDFDFDF; + *count = 1; + return true; +} + +/** + * hb_ot_tags_from_script_and_language: + * @script: an #hb_script_t to convert. + * @language: an #hb_language_t to convert. + * @script_count: (inout) (optional): maximum number of script tags to retrieve (IN) + * and actual number of script tags retrieved (OUT) + * @script_tags: (out) (optional): array of size at least @script_count to store the + * script tag results + * @language_count: (inout) (optional): maximum number of language tags to retrieve + * (IN) and actual number of language tags retrieved (OUT) + * @language_tags: (out) (optional): array of size at least @language_count to store + * the language tag results + * + * Converts an #hb_script_t and an #hb_language_t to script and language tags. + * + * Since: 2.0.0 + **/ +void +hb_ot_tags_from_script_and_language (hb_script_t script, + hb_language_t language, + unsigned int *script_count /* IN/OUT */, + hb_tag_t *script_tags /* OUT */, + unsigned int *language_count /* IN/OUT */, + hb_tag_t *language_tags /* OUT */) +{ + bool needs_script = true; + + if (language == HB_LANGUAGE_INVALID) + { + if (language_count && language_tags && *language_count) + *language_count = 0; + } + else + { + const char *lang_str, *s, *limit, *private_use_subtag; + bool needs_language; + + lang_str = hb_language_to_string (language); + limit = nullptr; + private_use_subtag = nullptr; + if (lang_str[0] == 'x' && lang_str[1] == '-') + { + private_use_subtag = lang_str; + } else { + for (s = lang_str + 1; *s; s++) + { + if (s[-1] == '-' && s[1] == '-') + { + if (s[0] == 'x') + { + private_use_subtag = s; + if (!limit) + limit = s - 1; + break; + } else if (!limit) + { + limit = s - 1; + } + } + } + if (!limit) + limit = s; + } + + needs_script = !parse_private_use_subtag (private_use_subtag, script_count, script_tags, "-hbsc", TOLOWER); + needs_language = !parse_private_use_subtag (private_use_subtag, language_count, language_tags, "-hbot", TOUPPER); + + if (needs_language && language_count && language_tags && *language_count) + hb_ot_tags_from_language (lang_str, limit, language_count, language_tags); + } + + if (needs_script && script_count && script_tags && *script_count) + hb_ot_all_tags_from_script (script, script_count, script_tags); +} + +/** + * hb_ot_tag_to_language: + * @tag: an language tag + * + * Converts a language tag to an #hb_language_t. + * + * Return value: (transfer none) (nullable): + * The #hb_language_t corresponding to @tag. + * + * Since: 0.9.2 + **/ +hb_language_t +hb_ot_tag_to_language (hb_tag_t tag) +{ + unsigned int i; + + if (tag == HB_OT_TAG_DEFAULT_LANGUAGE) + return nullptr; + +#ifndef HB_NO_LANGUAGE_LONG + { + hb_language_t disambiguated_tag = hb_ot_ambiguous_tag_to_language (tag); + if (disambiguated_tag != HB_LANGUAGE_INVALID) + return disambiguated_tag; + } +#endif + + char buf[4]; + for (i = 0; i < ARRAY_LENGTH (ot_languages2); i++) + if (ot_languages2[i].tag == tag) + { + hb_tag_to_string (ot_languages2[i].language, buf); + return hb_language_from_string (buf, 2); + } +#ifndef HB_NO_LANGUAGE_LONG + for (i = 0; i < ARRAY_LENGTH (ot_languages3); i++) + if (ot_languages3[i].tag == tag) + { + hb_tag_to_string (ot_languages3[i].language, buf); + return hb_language_from_string (buf, 3); + } +#endif + + /* Return a custom language in the form of "x-hbot-AABBCCDD". + * If it's three letters long, also guess it's ISO 639-3 and lower-case and + * prepend it (if it's not a registered tag, the private use subtags will + * ensure that calling hb_ot_tag_from_language on the result will still return + * the same tag as the original tag). + */ + { + char buf[20]; + char *str = buf; + if (ISALPHA (tag >> 24) + && ISALPHA ((tag >> 16) & 0xFF) + && ISALPHA ((tag >> 8) & 0xFF) + && (tag & 0xFF) == ' ') + { + buf[0] = TOLOWER (tag >> 24); + buf[1] = TOLOWER ((tag >> 16) & 0xFF); + buf[2] = TOLOWER ((tag >> 8) & 0xFF); + buf[3] = '-'; + str += 4; + } + snprintf (str, 16, "x-hbot-%08x", tag); + return hb_language_from_string (&*buf, -1); + } +} + +/** + * hb_ot_tags_to_script_and_language: + * @script_tag: a script tag + * @language_tag: a language tag + * @script: (out) (optional): the #hb_script_t corresponding to @script_tag. + * @language: (out) (optional): the #hb_language_t corresponding to @script_tag and + * @language_tag. + * + * Converts a script tag and a language tag to an #hb_script_t and an + * #hb_language_t. + * + * Since: 2.0.0 + **/ +void +hb_ot_tags_to_script_and_language (hb_tag_t script_tag, + hb_tag_t language_tag, + hb_script_t *script /* OUT */, + hb_language_t *language /* OUT */) +{ + hb_script_t script_out = hb_ot_tag_to_script (script_tag); + if (script) + *script = script_out; + if (language) + { + unsigned int script_count = 1; + hb_tag_t primary_script_tag[1]; + hb_ot_tags_from_script_and_language (script_out, + HB_LANGUAGE_INVALID, + &script_count, + primary_script_tag, + nullptr, nullptr); + *language = hb_ot_tag_to_language (language_tag); + if (script_count == 0 || primary_script_tag[0] != script_tag) + { + unsigned char *buf; + const char *lang_str = hb_language_to_string (*language); + size_t len = strlen (lang_str); + buf = (unsigned char *) hb_malloc (len + 16); + if (unlikely (!buf)) + { + *language = nullptr; + } + else + { + int shift; + memcpy (buf, lang_str, len); + if (lang_str[0] != 'x' || lang_str[1] != '-') { + buf[len++] = '-'; + buf[len++] = 'x'; + } + buf[len++] = '-'; + buf[len++] = 'h'; + buf[len++] = 'b'; + buf[len++] = 's'; + buf[len++] = 'c'; + buf[len++] = '-'; + for (shift = 28; shift >= 0; shift -= 4) + buf[len++] = TOHEX (script_tag >> shift); + *language = hb_language_from_string ((char *) buf, len); + hb_free (buf); + } + } + } +} + +#ifdef MAIN +static inline void +test_langs_sorted () +{ + for (unsigned int i = 1; i < ARRAY_LENGTH (ot_languages2); i++) + { + int c = ot_languages2[i].cmp (&ot_languages2[i - 1]); + if (c > 0) + { + fprintf (stderr, "ot_languages2 not sorted at index %d: %08x %d %08x\n", + i, ot_languages2[i-1].language, c, ot_languages2[i].language); + abort(); + } + } +#ifndef HB_NO_LANGUAGE_LONG + for (unsigned int i = 1; i < ARRAY_LENGTH (ot_languages3); i++) + { + int c = ot_languages3[i].cmp (&ot_languages3[i - 1]); + if (c > 0) + { + fprintf (stderr, "ot_languages3 not sorted at index %d: %08x %d %08x\n", + i, ot_languages3[i-1].language, c, ot_languages3[i].language); + abort(); + } + } +#endif +} + +int +main () +{ + test_langs_sorted (); + return 0; +} + +#endif + + +#endif diff --git a/libs/harfbuzz/src/hb-ot-var-avar-table.hh b/libs/harfbuzz/src/hb-ot-var-avar-table.hh new file mode 100644 index 00000000..f60bc4a3 --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-var-avar-table.hh @@ -0,0 +1,244 @@ +/* + * Copyright © 2017 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 HB_OT_VAR_AVAR_TABLE_HH +#define HB_OT_VAR_AVAR_TABLE_HH + +#include "hb-open-type.hh" +#include "hb-ot-var-common.hh" + + +/* + * avar -- Axis Variations + * https://docs.microsoft.com/en-us/typography/opentype/spec/avar + */ + +#define HB_OT_TAG_avar HB_TAG('a','v','a','r') + + +namespace OT { + + +/* "Spec": https://github.com/be-fonts/boring-expansion-spec/issues/14 */ +struct avarV2Tail +{ + friend struct avar; + + bool sanitize (hb_sanitize_context_t *c, + const void *base) const + { + TRACE_SANITIZE (this); + return_trace (varIdxMap.sanitize (c, base) && + varStore.sanitize (c, base)); + } + + protected: + Offset32To varIdxMap; /* Offset from the beginning of 'avar' table. */ + Offset32To varStore; /* Offset from the beginning of 'avar' table. */ + + public: + DEFINE_SIZE_STATIC (8); +}; + + +struct AxisValueMap +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + public: + F2DOT14 coords[2]; +// F2DOT14 fromCoord; /* A normalized coordinate value obtained using +// * default normalization. */ +// F2DOT14 toCoord; /* The modified, normalized coordinate value. */ + + public: + DEFINE_SIZE_STATIC (4); +}; + +struct SegmentMaps : Array16Of +{ + int map (int value, unsigned int from_offset = 0, unsigned int to_offset = 1) const + { +#define fromCoord coords[from_offset] +#define toCoord coords[to_offset] + /* The following special-cases are not part of OpenType, which requires + * that at least -1, 0, and +1 must be mapped. But we include these as + * part of a better error recovery scheme. */ + if (len < 2) + { + if (!len) + return value; + else /* len == 1*/ + return value - arrayZ[0].fromCoord + arrayZ[0].toCoord; + } + + if (value <= arrayZ[0].fromCoord) + return value - arrayZ[0].fromCoord + arrayZ[0].toCoord; + + unsigned int i; + unsigned int count = len - 1; + for (i = 1; i < count && value > arrayZ[i].fromCoord; i++) + ; + + if (value >= arrayZ[i].fromCoord) + return value - arrayZ[i].fromCoord + arrayZ[i].toCoord; + + if (unlikely (arrayZ[i-1].fromCoord == arrayZ[i].fromCoord)) + return arrayZ[i-1].toCoord; + + int denom = arrayZ[i].fromCoord - arrayZ[i-1].fromCoord; + return roundf (arrayZ[i-1].toCoord + ((float) (arrayZ[i].toCoord - arrayZ[i-1].toCoord) * + (value - arrayZ[i-1].fromCoord)) / denom); +#undef toCoord +#undef fromCoord + } + + int unmap (int value) const { return map (value, 1, 0); } + + public: + DEFINE_SIZE_ARRAY (2, *this); +}; + +struct avar +{ + static constexpr hb_tag_t tableTag = HB_OT_TAG_avar; + + bool has_data () const { return version.to_int (); } + + const SegmentMaps* get_segment_maps () const + { return &firstAxisSegmentMaps; } + + unsigned get_axis_count () const + { return axisCount; } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (!(version.sanitize (c) && + (version.major == 1 +#ifndef HB_NO_VARIATIONS2 + || version.major == 2 +#endif + ) && + c->check_struct (this))) + return_trace (false); + + const SegmentMaps *map = &firstAxisSegmentMaps; + unsigned int count = axisCount; + for (unsigned int i = 0; i < count; i++) + { + if (unlikely (!map->sanitize (c))) + return_trace (false); + map = &StructAfter (*map); + } + +#ifndef HB_NO_VARIATIONS2 + if (version.major < 2) + return_trace (true); + + const auto &v2 = * (const avarV2Tail *) map; + if (unlikely (!v2.sanitize (c, this))) + return_trace (false); +#endif + + return_trace (true); + } + + void map_coords (int *coords, unsigned int coords_length) const + { + unsigned int count = hb_min (coords_length, axisCount); + + const SegmentMaps *map = &firstAxisSegmentMaps; + for (unsigned int i = 0; i < count; i++) + { + coords[i] = map->map (coords[i]); + map = &StructAfter (*map); + } + +#ifndef HB_NO_VARIATIONS2 + if (version.major < 2) + return; + + for (; count < axisCount; count++) + map = &StructAfter (*map); + + const auto &v2 = * (const avarV2Tail *) map; + + const auto &varidx_map = this+v2.varIdxMap; + const auto &var_store = this+v2.varStore; + auto *var_store_cache = var_store.create_cache (); + + hb_vector_t out; + out.alloc (coords_length); + for (unsigned i = 0; i < coords_length; i++) + { + int v = coords[i]; + uint32_t varidx = varidx_map.map (i); + float delta = var_store.get_delta (varidx, coords, coords_length, var_store_cache); + v += roundf (delta); + v = hb_clamp (v, -(1<<14), +(1<<14)); + out.push (v); + } + for (unsigned i = 0; i < coords_length; i++) + coords[i] = out[i]; + + OT::VariationStore::destroy_cache (var_store_cache); +#endif + } + + void unmap_coords (int *coords, unsigned int coords_length) const + { + unsigned int count = hb_min (coords_length, axisCount); + + const SegmentMaps *map = &firstAxisSegmentMaps; + for (unsigned int i = 0; i < count; i++) + { + coords[i] = map->unmap (coords[i]); + map = &StructAfter (*map); + } + } + + protected: + FixedVersion<>version; /* Version of the avar table + * initially set to 0x00010000u */ + HBUINT16 reserved; /* This field is permanently reserved. Set to 0. */ + HBUINT16 axisCount; /* The number of variation axes in the font. This + * must be the same number as axisCount in the + * 'fvar' table. */ + SegmentMaps firstAxisSegmentMaps; + + public: + DEFINE_SIZE_MIN (8); +}; + +} /* namespace OT */ + + +#endif /* HB_OT_VAR_AVAR_TABLE_HH */ diff --git a/libs/harfbuzz/src/hb-ot-var-common.hh b/libs/harfbuzz/src/hb-ot-var-common.hh new file mode 100644 index 00000000..1d29e3e4 --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-var-common.hh @@ -0,0 +1,225 @@ +/* + * Copyright © 2021 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. + * + */ + +#ifndef HB_OT_VAR_COMMON_HH +#define HB_OT_VAR_COMMON_HH + +#include "hb-ot-layout-common.hh" + + +namespace OT { + +template +struct DeltaSetIndexMapFormat01 +{ + friend struct DeltaSetIndexMap; + + private: + DeltaSetIndexMapFormat01* copy (hb_serialize_context_t *c) const + { + TRACE_SERIALIZE (this); + auto *out = c->start_embed (this); + if (unlikely (!out)) return_trace (nullptr); + + unsigned total_size = min_size + mapCount * get_width (); + HBUINT8 *p = c->allocate_size (total_size); + if (unlikely (!p)) return_trace (nullptr); + + memcpy (p, this, HBUINT8::static_size * total_size); + return_trace (out); + } + + template + bool serialize (hb_serialize_context_t *c, const T &plan) + { + unsigned int width = plan.get_width (); + unsigned int inner_bit_count = plan.get_inner_bit_count (); + const hb_array_t output_map = plan.get_output_map (); + + TRACE_SERIALIZE (this); + if (unlikely (output_map.length && ((((inner_bit_count-1)&~0xF)!=0) || (((width-1)&~0x3)!=0)))) + return_trace (false); + if (unlikely (!c->extend_min (this))) return_trace (false); + + entryFormat = ((width-1)<<4)|(inner_bit_count-1); + mapCount = output_map.length; + HBUINT8 *p = c->allocate_size (width * output_map.length); + if (unlikely (!p)) return_trace (false); + for (unsigned int i = 0; i < output_map.length; i++) + { + unsigned int v = output_map[i]; + unsigned int outer = v >> 16; + unsigned int inner = v & 0xFFFF; + unsigned int u = (outer << inner_bit_count) | inner; + for (unsigned int w = width; w > 0;) + { + p[--w] = u; + u >>= 8; + } + p += width; + } + return_trace (true); + } + + uint32_t map (unsigned int v) const /* Returns 16.16 outer.inner. */ + { + /* If count is zero, pass value unchanged. This takes + * care of direct mapping for advance map. */ + if (!mapCount) + return v; + + if (v >= mapCount) + v = mapCount - 1; + + unsigned int u = 0; + { /* Fetch it. */ + unsigned int w = get_width (); + const HBUINT8 *p = mapDataZ.arrayZ + w * v; + for (; w; w--) + u = (u << 8) + *p++; + } + + { /* Repack it. */ + unsigned int n = get_inner_bit_count (); + unsigned int outer = u >> n; + unsigned int inner = u & ((1 << n) - 1); + u = (outer<<16) | inner; + } + + return u; + } + + unsigned get_map_count () const { return mapCount; } + unsigned get_width () const { return ((entryFormat >> 4) & 3) + 1; } + unsigned get_inner_bit_count () const { return (entryFormat & 0xF) + 1; } + + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + c->check_range (mapDataZ.arrayZ, + mapCount, + get_width ())); + } + + protected: + HBUINT8 format; /* Format identifier--format = 0 */ + HBUINT8 entryFormat; /* A packed field that describes the compressed + * representation of delta-set indices. */ + MapCountT mapCount; /* The number of mapping entries. */ + UnsizedArrayOf + mapDataZ; /* The delta-set index mapping data. */ + + public: + DEFINE_SIZE_ARRAY (2+MapCountT::static_size, mapDataZ); +}; + +struct DeltaSetIndexMap +{ + template + bool serialize (hb_serialize_context_t *c, const T &plan) + { + TRACE_SERIALIZE (this); + unsigned length = plan.get_output_map ().length; + u.format = length <= 0xFFFF ? 0 : 1; + switch (u.format) { + case 0: return_trace (u.format0.serialize (c, plan)); + case 1: return_trace (u.format1.serialize (c, plan)); + default:return_trace (false); + } + } + + uint32_t map (unsigned v) const + { + switch (u.format) { + case 0: return (u.format0.map (v)); + case 1: return (u.format1.map (v)); + default:return v; + } + } + + unsigned get_map_count () const + { + switch (u.format) { + case 0: return u.format0.get_map_count (); + case 1: return u.format1.get_map_count (); + default:return 0; + } + } + + unsigned get_width () const + { + switch (u.format) { + case 0: return u.format0.get_width (); + case 1: return u.format1.get_width (); + default:return 0; + } + } + + unsigned get_inner_bit_count () const + { + switch (u.format) { + case 0: return u.format0.get_inner_bit_count (); + case 1: return u.format1.get_inner_bit_count (); + default:return 0; + } + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (!u.format.sanitize (c)) return_trace (false); + switch (u.format) { + case 0: return_trace (u.format0.sanitize (c)); + case 1: return_trace (u.format1.sanitize (c)); + default:return_trace (true); + } + } + + DeltaSetIndexMap* copy (hb_serialize_context_t *c) const + { + TRACE_SERIALIZE (this); + switch (u.format) { + case 0: return_trace (reinterpret_cast (u.format0.copy (c))); + case 1: return_trace (reinterpret_cast (u.format1.copy (c))); + default:return_trace (nullptr); + } + } + + protected: + union { + HBUINT8 format; /* Format identifier */ + DeltaSetIndexMapFormat01 format0; + DeltaSetIndexMapFormat01 format1; + } u; + public: + DEFINE_SIZE_UNION (1, format); +}; + +} /* namespace OT */ + + +#endif /* HB_OT_VAR_COMMON_HH */ diff --git a/libs/harfbuzz/src/hb-ot-var-fvar-table.hh b/libs/harfbuzz/src/hb-ot-var-fvar-table.hh new file mode 100644 index 00000000..af238628 --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-var-fvar-table.hh @@ -0,0 +1,439 @@ +/* + * Copyright © 2017 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 HB_OT_VAR_FVAR_TABLE_HH +#define HB_OT_VAR_FVAR_TABLE_HH + +#include "hb-open-type.hh" + +/* + * fvar -- Font Variations + * https://docs.microsoft.com/en-us/typography/opentype/spec/fvar + */ + +#define HB_OT_TAG_fvar HB_TAG('f','v','a','r') + + +namespace OT { + + +struct InstanceRecord +{ + friend struct fvar; + + hb_array_t get_coordinates (unsigned int axis_count) const + { return coordinatesZ.as_array (axis_count); } + + bool subset (hb_subset_context_t *c, + unsigned axis_count, + bool has_postscript_nameid) const + { + TRACE_SUBSET (this); + if (unlikely (!c->serializer->embed (subfamilyNameID))) return_trace (false); + if (unlikely (!c->serializer->embed (flags))) return_trace (false); + + const hb_array_t coords = get_coordinates (axis_count); + const hb_hashmap_t *axes_location = c->plan->user_axes_location; + for (unsigned i = 0 ; i < axis_count; i++) + { + unsigned *axis_tag; + // only keep instances whose coordinates == pinned axis location + if (!c->plan->axes_old_index_tag_map->has (i, &axis_tag)) continue; + + if (axes_location->has (*axis_tag) && + fabsf (axes_location->get (*axis_tag) - coords[i].to_float ()) > 0.001f) + return_trace (false); + + if (!c->plan->axes_index_map->has (i)) + continue; + + if (!c->serializer->embed (coords[i])) + return_trace (false); + } + + if (has_postscript_nameid) + { + NameID name_id; + name_id = StructAfter (coords); + if (!c->serializer->embed (name_id)) + return_trace (false); + } + + return_trace (true); + } + + bool sanitize (hb_sanitize_context_t *c, unsigned int axis_count) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + c->check_array (coordinatesZ.arrayZ, axis_count)); + } + + protected: + NameID subfamilyNameID;/* The name ID for entries in the 'name' table + * that provide subfamily names for this instance. */ + HBUINT16 flags; /* Reserved for future use — set to 0. */ + UnsizedArrayOf + coordinatesZ; /* The coordinates array for this instance. */ + //NameID postScriptNameIDX;/*Optional. The name ID for entries in the 'name' + // * table that provide PostScript names for this + // * instance. */ + + public: + DEFINE_SIZE_UNBOUNDED (4); +}; + +struct AxisRecord +{ + int cmp (hb_tag_t key) const { return axisTag.cmp (key); } + + enum + { + AXIS_FLAG_HIDDEN = 0x0001, + }; + +#ifndef HB_DISABLE_DEPRECATED + void get_axis_deprecated (hb_ot_var_axis_t *info) const + { + info->tag = axisTag; + info->name_id = axisNameID; + get_coordinates (info->min_value, info->default_value, info->max_value); + } +#endif + + void get_axis_info (unsigned axis_index, hb_ot_var_axis_info_t *info) const + { + info->axis_index = axis_index; + info->tag = axisTag; + info->name_id = axisNameID; + info->flags = (hb_ot_var_axis_flags_t) (unsigned int) flags; + get_coordinates (info->min_value, info->default_value, info->max_value); + info->reserved = 0; + } + + hb_tag_t get_axis_tag () const { return axisTag; } + + int normalize_axis_value (float v) const + { + float min_value, default_value, max_value; + get_coordinates (min_value, default_value, max_value); + + v = hb_clamp (v, min_value, max_value); + + if (v == default_value) + return 0; + else if (v < default_value) + v = (v - default_value) / (default_value - min_value); + else + v = (v - default_value) / (max_value - default_value); + return roundf (v * 16384.f); + } + + float unnormalize_axis_value (int v) const + { + float min_value, default_value, max_value; + get_coordinates (min_value, default_value, max_value); + + if (v == 0) + return default_value; + else if (v < 0) + return v * (default_value - min_value) / 16384.f + default_value; + else + return v * (max_value - default_value) / 16384.f + default_value; + } + + hb_ot_name_id_t get_name_id () const { return axisNameID; } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + void get_coordinates (float &min, float &default_, float &max) const + { + default_ = defaultValue / 65536.f; + /* Ensure order, to simplify client math. */ + min = hb_min (default_, minValue / 65536.f); + max = hb_max (default_, maxValue / 65536.f); + } + + float get_default () const + { + return defaultValue / 65536.f; + } + + public: + Tag axisTag; /* Tag identifying the design variation for the axis. */ + protected: + F16DOT16 minValue; /* The minimum coordinate value for the axis. */ + F16DOT16 defaultValue; /* The default coordinate value for the axis. */ + F16DOT16 maxValue; /* The maximum coordinate value for the axis. */ + public: + HBUINT16 flags; /* Axis flags. */ + NameID axisNameID; /* The name ID for entries in the 'name' table that + * provide a display name for this axis. */ + + public: + DEFINE_SIZE_STATIC (20); +}; + +struct fvar +{ + static constexpr hb_tag_t tableTag = HB_OT_TAG_fvar; + + bool has_data () const { return version.to_int (); } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (version.sanitize (c) && + likely (version.major == 1) && + c->check_struct (this) && + axisSize == 20 && /* Assumed in our code. */ + instanceSize >= axisCount * 4 + 4 && + get_axes ().sanitize (c) && + c->check_range (get_instance (0), instanceCount, instanceSize)); + } + + unsigned int get_axis_count () const { return axisCount; } + +#ifndef HB_DISABLE_DEPRECATED + unsigned int get_axes_deprecated (unsigned int start_offset, + unsigned int *axes_count /* IN/OUT */, + hb_ot_var_axis_t *axes_array /* OUT */) const + { + if (axes_count) + { + hb_array_t arr = get_axes ().sub_array (start_offset, axes_count); + for (unsigned i = 0; i < arr.length; ++i) + arr[i].get_axis_deprecated (&axes_array[i]); + } + return axisCount; + } +#endif + + unsigned int get_axis_infos (unsigned int start_offset, + unsigned int *axes_count /* IN/OUT */, + hb_ot_var_axis_info_t *axes_array /* OUT */) const + { + if (axes_count) + { + hb_array_t arr = get_axes ().sub_array (start_offset, axes_count); + for (unsigned i = 0; i < arr.length; ++i) + arr[i].get_axis_info (start_offset + i, &axes_array[i]); + } + return axisCount; + } + +#ifndef HB_DISABLE_DEPRECATED + bool + find_axis_deprecated (hb_tag_t tag, unsigned *axis_index, hb_ot_var_axis_t *info) const + { + unsigned i; + if (!axis_index) axis_index = &i; + *axis_index = HB_OT_VAR_NO_AXIS_INDEX; + auto axes = get_axes (); + return axes.lfind (tag, axis_index) && ((void) axes[*axis_index].get_axis_deprecated (info), true); + } +#endif + bool + find_axis_info (hb_tag_t tag, hb_ot_var_axis_info_t *info) const + { + unsigned i; + auto axes = get_axes (); + return axes.lfind (tag, &i) && ((void) axes[i].get_axis_info (i, info), true); + } + + int normalize_axis_value (unsigned int axis_index, float v) const + { return get_axes ()[axis_index].normalize_axis_value (v); } + + float unnormalize_axis_value (unsigned int axis_index, int v) const + { return get_axes ()[axis_index].unnormalize_axis_value (v); } + + unsigned int get_instance_count () const { return instanceCount; } + + hb_ot_name_id_t get_instance_subfamily_name_id (unsigned int instance_index) const + { + const InstanceRecord *instance = get_instance (instance_index); + if (unlikely (!instance)) return HB_OT_NAME_ID_INVALID; + return instance->subfamilyNameID; + } + + hb_ot_name_id_t get_instance_postscript_name_id (unsigned int instance_index) const + { + const InstanceRecord *instance = get_instance (instance_index); + if (unlikely (!instance)) return HB_OT_NAME_ID_INVALID; + if (instanceSize >= axisCount * 4 + 6) + return StructAfter (instance->get_coordinates (axisCount)); + return HB_OT_NAME_ID_INVALID; + } + + unsigned int get_instance_coords (unsigned int instance_index, + unsigned int *coords_length, /* IN/OUT */ + float *coords /* OUT */) const + { + const InstanceRecord *instance = get_instance (instance_index); + if (unlikely (!instance)) + { + if (coords_length) + *coords_length = 0; + return 0; + } + + if (coords_length && *coords_length) + { + hb_array_t instanceCoords = instance->get_coordinates (axisCount) + .sub_array (0, coords_length); + for (unsigned int i = 0; i < instanceCoords.length; i++) + coords[i] = instanceCoords.arrayZ[i].to_float (); + } + return axisCount; + } + + void collect_name_ids (hb_hashmap_t *user_axes_location, + hb_set_t *nameids /* IN/OUT */) const + { + if (!has_data ()) return; + hb_map_t pinned_axes; + + auto axis_records = get_axes (); + for (unsigned i = 0 ; i < (unsigned)axisCount; i++) + { + hb_tag_t axis_tag = axis_records[i].get_axis_tag (); + if (user_axes_location->has (axis_tag)) + { + pinned_axes.set (i, axis_tag); + continue; + } + + nameids->add (axis_records[i].get_name_id ()); + } + + for (unsigned i = 0 ; i < (unsigned)instanceCount; i++) + { + const InstanceRecord *instance = get_instance (i); + + if (hb_any (+ hb_zip (instance->get_coordinates (axisCount), hb_range ((unsigned)axisCount)) + | hb_filter (pinned_axes, hb_second) + | hb_map ([&] (const hb_pair_t& _) + { + hb_tag_t axis_tag = pinned_axes.get (_.second); + float location = user_axes_location->get (axis_tag); + if (fabs ((double)location - (double)_.first.to_float ()) > 0.001) return true; + return false; + }) + )) + continue; + + nameids->add (instance->subfamilyNameID); + + if (instanceSize >= axisCount * 4 + 6) + { + unsigned post_script_name_id = StructAfter (instance->get_coordinates (axisCount)); + if (post_script_name_id != HB_OT_NAME_ID_INVALID) nameids->add (post_script_name_id); + } + } + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + unsigned retained_axis_count = c->plan->axes_index_map->get_population (); + if (!retained_axis_count) //all axes are pinned + return_trace (false); + + fvar *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + + if (!c->serializer->check_assign (out->axisCount, retained_axis_count, HB_SERIALIZE_ERROR_INT_OVERFLOW)) + return_trace (false); + + bool has_postscript_nameid = false; + if (instanceSize >= axisCount * 4 + 6) + has_postscript_nameid = true; + + if (!c->serializer->check_assign (out->instanceSize, retained_axis_count * 4 + (has_postscript_nameid ? 6 : 4), + HB_SERIALIZE_ERROR_INT_OVERFLOW)) + return_trace (false); + + auto axes_records = get_axes (); + for (unsigned i = 0 ; i < (unsigned)axisCount; i++) + { + if (!c->plan->axes_index_map->has (i)) continue; + if (unlikely (!c->serializer->embed (axes_records[i]))) + return_trace (false); + } + + if (!c->serializer->check_assign (out->firstAxis, get_size (), HB_SERIALIZE_ERROR_INT_OVERFLOW)) + return_trace (false); + + for (unsigned i = 0 ; i < (unsigned)instanceCount; i++) + { + const InstanceRecord *instance = get_instance (i); + auto snap = c->serializer->snapshot (); + if (!instance->subset (c, axisCount, has_postscript_nameid)) + c->serializer->revert (snap); + } + return_trace (true); + } + + public: + hb_array_t get_axes () const + { return hb_array (&(this+firstAxis), axisCount); } + + const InstanceRecord *get_instance (unsigned int i) const + { + if (unlikely (i >= instanceCount)) return nullptr; + return &StructAtOffset (&StructAfter (get_axes ()), + i * instanceSize); + } + + protected: + FixedVersion<>version; /* Version of the fvar table + * initially set to 0x00010000u */ + Offset16To + firstAxis; /* Offset in bytes from the beginning of the table + * to the start of the AxisRecord array. */ + HBUINT16 reserved; /* This field is permanently reserved. Set to 2. */ + HBUINT16 axisCount; /* The number of variation axes in the font (the + * number of records in the axes array). */ + HBUINT16 axisSize; /* The size in bytes of each VariationAxisRecord — + * set to 20 (0x0014) for this version. */ + HBUINT16 instanceCount; /* The number of named instances defined in the font + * (the number of records in the instances array). */ + HBUINT16 instanceSize; /* The size in bytes of each InstanceRecord — set + * to either axisCount * sizeof(F16DOT16) + 4, or to + * axisCount * sizeof(F16DOT16) + 6. */ + + public: + DEFINE_SIZE_STATIC (16); +}; + +} /* namespace OT */ + + +#endif /* HB_OT_VAR_FVAR_TABLE_HH */ diff --git a/libs/harfbuzz/src/hb-ot-var-gvar-table.hh b/libs/harfbuzz/src/hb-ot-var-gvar-table.hh new file mode 100644 index 00000000..bf1039d1 --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-var-gvar-table.hh @@ -0,0 +1,718 @@ +/* + * Copyright © 2019 Adobe Inc. + * Copyright © 2019 Ebrahim Byagowi + * + * 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. + * + * Adobe Author(s): Michiharu Ariza + */ + +#ifndef HB_OT_VAR_GVAR_TABLE_HH +#define HB_OT_VAR_GVAR_TABLE_HH + +#include "hb-open-type.hh" + +/* + * gvar -- Glyph Variation Table + * https://docs.microsoft.com/en-us/typography/opentype/spec/gvar + */ +#define HB_OT_TAG_gvar HB_TAG('g','v','a','r') + +namespace OT { + +struct contour_point_t +{ + void init (float x_ = 0.f, float y_ = 0.f, bool is_end_point_ = false) + { flag = 0; x = x_; y = y_; is_end_point = is_end_point_; } + + void translate (const contour_point_t &p) { x += p.x; y += p.y; } + + float x = 0.f; + float y = 0.f; + uint8_t flag = 0; + bool is_end_point = false; +}; + +struct contour_point_vector_t : hb_vector_t +{ + void extend (const hb_array_t &a) + { + unsigned int old_len = length; + if (unlikely (!resize (old_len + a.length))) + return; + auto arrayZ = this->arrayZ + old_len; + unsigned count = a.length; + for (unsigned int i = 0; i < count; i++) + arrayZ[i] = a.arrayZ[i]; + } + + void transform (const float (&matrix)[4]) + { + if (matrix[0] == 1.f && matrix[1] == 0.f && + matrix[2] == 0.f && matrix[3] == 1.f) + return; + auto arrayZ = this->arrayZ; + unsigned count = length; + for (unsigned i = 0; i < count; i++) + { + contour_point_t &p = arrayZ[i]; + float x_ = p.x * matrix[0] + p.y * matrix[2]; + p.y = p.x * matrix[1] + p.y * matrix[3]; + p.x = x_; + } + } + + void translate (const contour_point_t& delta) + { + if (delta.x == 0.f && delta.y == 0.f) + return; + auto arrayZ = this->arrayZ; + unsigned count = length; + for (unsigned i = 0; i < count; i++) + arrayZ[i].translate (delta); + } +}; + +/* https://docs.microsoft.com/en-us/typography/opentype/spec/otvarcommonformats#tuplevariationheader */ +struct TupleVariationHeader +{ + unsigned get_size (unsigned axis_count) const + { return min_size + get_all_tuples (axis_count).get_size (); } + + unsigned get_data_size () const { return varDataSize; } + + const TupleVariationHeader &get_next (unsigned axis_count) const + { return StructAtOffset (this, get_size (axis_count)); } + + float calculate_scalar (hb_array_t coords, unsigned int coord_count, + const hb_array_t shared_tuples) const + { + hb_array_t peak_tuple; + + if (has_peak ()) + peak_tuple = get_peak_tuple (coord_count); + else + { + unsigned int index = get_index (); + if (unlikely (index * coord_count >= shared_tuples.length)) + return 0.f; + peak_tuple = shared_tuples.sub_array (coord_count * index, coord_count); + } + + hb_array_t start_tuple; + hb_array_t end_tuple; + if (has_intermediate ()) + { + start_tuple = get_start_tuple (coord_count); + end_tuple = get_end_tuple (coord_count); + } + + float scalar = 1.f; + for (unsigned int i = 0; i < coord_count; i++) + { + int v = coords[i]; + int peak = peak_tuple[i]; + if (!peak || v == peak) continue; + + if (has_intermediate ()) + { + int start = start_tuple[i]; + int end = end_tuple[i]; + if (unlikely (start > peak || peak > end || + (start < 0 && end > 0 && peak))) continue; + if (v < start || v > end) return 0.f; + if (v < peak) + { if (peak != start) scalar *= (float) (v - start) / (peak - start); } + else + { if (peak != end) scalar *= (float) (end - v) / (end - peak); } + } + else if (!v || v < hb_min (0, peak) || v > hb_max (0, peak)) return 0.f; + else + scalar *= (float) v / peak; + } + return scalar; + } + + bool has_peak () const { return tupleIndex & TuppleIndex::EmbeddedPeakTuple; } + bool has_intermediate () const { return tupleIndex & TuppleIndex::IntermediateRegion; } + bool has_private_points () const { return tupleIndex & TuppleIndex::PrivatePointNumbers; } + unsigned get_index () const { return tupleIndex & TuppleIndex::TupleIndexMask; } + + protected: + struct TuppleIndex : HBUINT16 + { + enum Flags { + EmbeddedPeakTuple = 0x8000u, + IntermediateRegion = 0x4000u, + PrivatePointNumbers = 0x2000u, + TupleIndexMask = 0x0FFFu + }; + + DEFINE_SIZE_STATIC (2); + }; + + hb_array_t get_all_tuples (unsigned axis_count) const + { return StructAfter> (tupleIndex).as_array ((has_peak () + has_intermediate () * 2) * axis_count); } + hb_array_t get_peak_tuple (unsigned axis_count) const + { return get_all_tuples (axis_count).sub_array (0, axis_count); } + hb_array_t get_start_tuple (unsigned axis_count) const + { return get_all_tuples (axis_count).sub_array (has_peak () * axis_count, axis_count); } + hb_array_t get_end_tuple (unsigned axis_count) const + { return get_all_tuples (axis_count).sub_array (has_peak () * axis_count + axis_count, axis_count); } + + HBUINT16 varDataSize; /* The size in bytes of the serialized + * data for this tuple variation table. */ + TuppleIndex tupleIndex; /* A packed field. The high 4 bits are flags (see below). + The low 12 bits are an index into a shared tuple + records array. */ + /* UnsizedArrayOf peakTuple - optional */ + /* Peak tuple record for this tuple variation table — optional, + * determined by flags in the tupleIndex value. + * + * Note that this must always be included in the 'cvar' table. */ + /* UnsizedArrayOf intermediateStartTuple - optional */ + /* Intermediate start tuple record for this tuple variation table — optional, + determined by flags in the tupleIndex value. */ + /* UnsizedArrayOf intermediateEndTuple - optional */ + /* Intermediate end tuple record for this tuple variation table — optional, + * determined by flags in the tupleIndex value. */ + public: + DEFINE_SIZE_MIN (4); +}; + +struct GlyphVariationData +{ + const TupleVariationHeader &get_tuple_var_header (void) const + { return StructAfter (data); } + + struct tuple_iterator_t + { + void init (hb_bytes_t var_data_bytes_, unsigned int axis_count_) + { + var_data_bytes = var_data_bytes_; + var_data = var_data_bytes_.as (); + index = 0; + axis_count = axis_count_; + current_tuple = &var_data->get_tuple_var_header (); + data_offset = 0; + } + + bool get_shared_indices (hb_vector_t &shared_indices /* OUT */) + { + if (var_data->has_shared_point_numbers ()) + { + const HBUINT8 *base = &(var_data+var_data->data); + const HBUINT8 *p = base; + if (!unpack_points (p, shared_indices, (const HBUINT8 *) (var_data_bytes.arrayZ + var_data_bytes.length))) return false; + data_offset = p - base; + } + return true; + } + + bool is_valid () const + { + return (index < var_data->tupleVarCount.get_count ()) && + var_data_bytes.check_range (current_tuple, TupleVariationHeader::min_size) && + var_data_bytes.check_range (current_tuple, hb_max (current_tuple->get_data_size (), current_tuple->get_size (axis_count))) && + current_tuple->get_size (axis_count); + } + + bool move_to_next () + { + data_offset += current_tuple->get_data_size (); + current_tuple = ¤t_tuple->get_next (axis_count); + index++; + return is_valid (); + } + + const HBUINT8 *get_serialized_data () const + { return &(var_data+var_data->data) + data_offset; } + + private: + const GlyphVariationData *var_data; + unsigned int index; + unsigned int axis_count; + unsigned int data_offset; + + public: + hb_bytes_t var_data_bytes; + const TupleVariationHeader *current_tuple; + }; + + static bool get_tuple_iterator (hb_bytes_t var_data_bytes, unsigned axis_count, + hb_vector_t &shared_indices /* OUT */, + tuple_iterator_t *iterator /* OUT */) + { + iterator->init (var_data_bytes, axis_count); + if (!iterator->get_shared_indices (shared_indices)) + return false; + return iterator->is_valid (); + } + + bool has_shared_point_numbers () const { return tupleVarCount.has_shared_point_numbers (); } + + static bool unpack_points (const HBUINT8 *&p /* IN/OUT */, + hb_vector_t &points /* OUT */, + const HBUINT8 *end) + { + enum packed_point_flag_t + { + POINTS_ARE_WORDS = 0x80, + POINT_RUN_COUNT_MASK = 0x7F + }; + + if (unlikely (p + 1 > end)) return false; + + uint16_t count = *p++; + if (count & POINTS_ARE_WORDS) + { + if (unlikely (p + 1 > end)) return false; + count = ((count & POINT_RUN_COUNT_MASK) << 8) | *p++; + } + if (unlikely (!points.resize (count))) return false; + + unsigned int n = 0; + uint16_t i = 0; + while (i < count) + { + if (unlikely (p + 1 > end)) return false; + uint16_t j; + uint8_t control = *p++; + uint16_t run_count = (control & POINT_RUN_COUNT_MASK) + 1; + if (control & POINTS_ARE_WORDS) + { + for (j = 0; j < run_count && i < count; j++, i++) + { + if (unlikely (p + HBUINT16::static_size > end)) return false; + n += *(const HBUINT16 *)p; + points[i] = n; + p += HBUINT16::static_size; + } + } + else + { + for (j = 0; j < run_count && i < count; j++, i++) + { + if (unlikely (p + 1 > end)) return false; + n += *p++; + points[i] = n; + } + } + if (j < run_count) return false; + } + return true; + } + + static bool unpack_deltas (const HBUINT8 *&p /* IN/OUT */, + hb_vector_t &deltas /* IN/OUT */, + const HBUINT8 *end) + { + enum packed_delta_flag_t + { + DELTAS_ARE_ZERO = 0x80, + DELTAS_ARE_WORDS = 0x40, + DELTA_RUN_COUNT_MASK = 0x3F + }; + + unsigned int i = 0; + unsigned int count = deltas.length; + while (i < count) + { + if (unlikely (p + 1 > end)) return false; + uint8_t control = *p++; + unsigned int run_count = (control & DELTA_RUN_COUNT_MASK) + 1; + unsigned int j; + if (control & DELTAS_ARE_ZERO) + for (j = 0; j < run_count && i < count; j++, i++) + deltas[i] = 0; + else if (control & DELTAS_ARE_WORDS) + for (j = 0; j < run_count && i < count; j++, i++) + { + if (unlikely (p + HBUINT16::static_size > end)) return false; + deltas[i] = *(const HBINT16 *) p; + p += HBUINT16::static_size; + } + else + for (j = 0; j < run_count && i < count; j++, i++) + { + if (unlikely (p + 1 > end)) return false; + deltas[i] = *(const HBINT8 *) p++; + } + if (j < run_count) + return false; + } + return true; + } + + bool has_data () const { return tupleVarCount; } + + protected: + struct TupleVarCount : HBUINT16 + { + bool has_shared_point_numbers () const { return ((*this) & SharedPointNumbers); } + unsigned int get_count () const { return (*this) & CountMask; } + + protected: + enum Flags + { + SharedPointNumbers= 0x8000u, + CountMask = 0x0FFFu + }; + public: + DEFINE_SIZE_STATIC (2); + }; + + TupleVarCount tupleVarCount; /* A packed field. The high 4 bits are flags, and the + * low 12 bits are the number of tuple variation tables + * for this glyph. The number of tuple variation tables + * can be any number between 1 and 4095. */ + Offset16To + data; /* Offset from the start of the GlyphVariationData table + * to the serialized data. */ + /* TupleVariationHeader tupleVariationHeaders[] *//* Array of tuple variation headers. */ + public: + DEFINE_SIZE_MIN (4); +}; + +struct gvar +{ + static constexpr hb_tag_t tableTag = HB_OT_TAG_gvar; + + bool sanitize_shallow (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && (version.major == 1) && + sharedTuples.sanitize (c, this, axisCount * sharedTupleCount) && + (is_long_offset () ? + c->check_array (get_long_offset_array (), glyphCount+1) : + c->check_array (get_short_offset_array (), glyphCount+1))); + } + + /* GlyphVariationData not sanitized here; must be checked while accessing each glyph variation data */ + bool sanitize (hb_sanitize_context_t *c) const + { return sanitize_shallow (c); } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + + gvar *out = c->serializer->allocate_min (); + if (unlikely (!out)) return_trace (false); + + out->version.major = 1; + out->version.minor = 0; + out->axisCount = axisCount; + out->sharedTupleCount = sharedTupleCount; + + unsigned int num_glyphs = c->plan->num_output_glyphs (); + out->glyphCount = num_glyphs; + + unsigned int subset_data_size = 0; + for (hb_codepoint_t gid = (c->plan->flags & HB_SUBSET_FLAGS_NOTDEF_OUTLINE) ? 0 : 1; + gid < num_glyphs; + gid++) + { + hb_codepoint_t old_gid; + if (!c->plan->old_gid_for_new_gid (gid, &old_gid)) continue; + subset_data_size += get_glyph_var_data_bytes (c->source_blob, old_gid).length; + } + + bool long_offset = subset_data_size & ~0xFFFFu; + out->flags = long_offset ? 1 : 0; + + HBUINT8 *subset_offsets = c->serializer->allocate_size ((long_offset ? 4 : 2) * (num_glyphs + 1)); + if (!subset_offsets) return_trace (false); + + /* shared tuples */ + if (!sharedTupleCount || !sharedTuples) + out->sharedTuples = 0; + else + { + unsigned int shared_tuple_size = F2DOT14::static_size * axisCount * sharedTupleCount; + F2DOT14 *tuples = c->serializer->allocate_size (shared_tuple_size); + if (!tuples) return_trace (false); + out->sharedTuples = (char *) tuples - (char *) out; + memcpy (tuples, this+sharedTuples, shared_tuple_size); + } + + char *subset_data = c->serializer->allocate_size (subset_data_size); + if (!subset_data) return_trace (false); + out->dataZ = subset_data - (char *) out; + + unsigned int glyph_offset = 0; + for (hb_codepoint_t gid = (c->plan->flags & HB_SUBSET_FLAGS_NOTDEF_OUTLINE) ? 0 : 1; + gid < num_glyphs; + gid++) + { + hb_codepoint_t old_gid; + hb_bytes_t var_data_bytes = c->plan->old_gid_for_new_gid (gid, &old_gid) + ? get_glyph_var_data_bytes (c->source_blob, old_gid) + : hb_bytes_t (); + + if (long_offset) + ((HBUINT32 *) subset_offsets)[gid] = glyph_offset; + else + ((HBUINT16 *) subset_offsets)[gid] = glyph_offset / 2; + + if (var_data_bytes.length > 0) + memcpy (subset_data, var_data_bytes.arrayZ, var_data_bytes.length); + subset_data += var_data_bytes.length; + glyph_offset += var_data_bytes.length; + } + if (long_offset) + ((HBUINT32 *) subset_offsets)[num_glyphs] = glyph_offset; + else + ((HBUINT16 *) subset_offsets)[num_glyphs] = glyph_offset / 2; + + return_trace (true); + } + + protected: + const hb_bytes_t get_glyph_var_data_bytes (hb_blob_t *blob, hb_codepoint_t glyph) const + { + unsigned start_offset = get_offset (glyph); + unsigned end_offset = get_offset (glyph+1); + if (unlikely (end_offset < start_offset)) return hb_bytes_t (); + unsigned length = end_offset - start_offset; + hb_bytes_t var_data = blob->as_bytes ().sub_array (((unsigned) dataZ) + start_offset, length); + return likely (var_data.length >= GlyphVariationData::min_size) ? var_data : hb_bytes_t (); + } + + bool is_long_offset () const { return flags & 1; } + + unsigned get_offset (unsigned i) const + { + if (unlikely (i > glyphCount)) return 0; + return is_long_offset () ? get_long_offset_array ()[i] : get_short_offset_array ()[i] * 2; + } + + const HBUINT32 * get_long_offset_array () const { return (const HBUINT32 *) &offsetZ; } + const HBUINT16 *get_short_offset_array () const { return (const HBUINT16 *) &offsetZ; } + + public: + struct accelerator_t + { + accelerator_t (hb_face_t *face) + { table = hb_sanitize_context_t ().reference_table (face); } + ~accelerator_t () { table.destroy (); } + + private: + + static float infer_delta (const hb_array_t points, + const hb_array_t deltas, + unsigned int target, unsigned int prev, unsigned int next, + float contour_point_t::*m) + { + float target_val = points[target].*m; + float prev_val = points[prev].*m; + float next_val = points[next].*m; + float prev_delta = deltas[prev].*m; + float next_delta = deltas[next].*m; + + if (prev_val == next_val) + return (prev_delta == next_delta) ? prev_delta : 0.f; + else if (target_val <= hb_min (prev_val, next_val)) + return (prev_val < next_val) ? prev_delta : next_delta; + else if (target_val >= hb_max (prev_val, next_val)) + return (prev_val > next_val) ? prev_delta : next_delta; + + /* linear interpolation */ + float r = (target_val - prev_val) / (next_val - prev_val); + return prev_delta + r * (next_delta - prev_delta); + } + + static unsigned int next_index (unsigned int i, unsigned int start, unsigned int end) + { return (i >= end) ? start : (i + 1); } + + public: + bool apply_deltas_to_points (hb_codepoint_t glyph, hb_font_t *font, + const hb_array_t points) const + { + if (!font->num_coords) return true; + + if (unlikely (glyph >= table->glyphCount)) return true; + + hb_bytes_t var_data_bytes = table->get_glyph_var_data_bytes (table.get_blob (), glyph); + if (!var_data_bytes.as ()->has_data ()) return true; + hb_vector_t shared_indices; + GlyphVariationData::tuple_iterator_t iterator; + if (!GlyphVariationData::get_tuple_iterator (var_data_bytes, table->axisCount, + shared_indices, &iterator)) + return true; /* so isn't applied at all */ + + /* Save original points for inferred delta calculation */ + contour_point_vector_t orig_points; + if (unlikely (!orig_points.resize (points.length))) return false; + for (unsigned int i = 0; i < orig_points.length; i++) + orig_points.arrayZ[i] = points.arrayZ[i]; + + contour_point_vector_t deltas; /* flag is used to indicate referenced point */ + if (unlikely (!deltas.resize (points.length))) return false; + + hb_vector_t end_points; + for (unsigned i = 0; i < points.length; ++i) + if (points[i].is_end_point) + end_points.push (i); + + auto coords = hb_array (font->coords, font->num_coords); + unsigned num_coords = table->axisCount; + hb_array_t shared_tuples = (table+table->sharedTuples).as_array (table->sharedTupleCount * table->axisCount); + + hb_vector_t private_indices; + hb_vector_t x_deltas; + hb_vector_t y_deltas; + do + { + float scalar = iterator.current_tuple->calculate_scalar (coords, num_coords, shared_tuples); + if (scalar == 0.f) continue; + const HBUINT8 *p = iterator.get_serialized_data (); + unsigned int length = iterator.current_tuple->get_data_size (); + if (unlikely (!iterator.var_data_bytes.check_range (p, length))) + return false; + + const HBUINT8 *end = p + length; + + bool has_private_points = iterator.current_tuple->has_private_points (); + if (has_private_points && + !GlyphVariationData::unpack_points (p, private_indices, end)) + return false; + const hb_array_t &indices = has_private_points ? private_indices : shared_indices; + + bool apply_to_all = (indices.length == 0); + unsigned int num_deltas = apply_to_all ? points.length : indices.length; + if (unlikely (!x_deltas.resize (num_deltas))) return false; + if (unlikely (!GlyphVariationData::unpack_deltas (p, x_deltas, end))) return false; + if (unlikely (!y_deltas.resize (num_deltas))) return false; + if (unlikely (!GlyphVariationData::unpack_deltas (p, y_deltas, end))) return false; + + for (unsigned int i = 0; i < deltas.length; i++) + deltas[i].init (); + for (unsigned int i = 0; i < num_deltas; i++) + { + unsigned int pt_index = apply_to_all ? i : indices[i]; + if (unlikely (pt_index >= deltas.length)) continue; + deltas.arrayZ[pt_index].flag = 1; /* this point is referenced, i.e., explicit deltas specified */ + deltas.arrayZ[pt_index].x += x_deltas.arrayZ[i] * scalar; + deltas.arrayZ[pt_index].y += y_deltas.arrayZ[i] * scalar; + } + + /* infer deltas for unreferenced points */ + unsigned start_point = 0; + for (unsigned c = 0; c < end_points.length; c++) + { + unsigned end_point = end_points[c]; + + /* Check the number of unreferenced points in a contour. If no unref points or no ref points, nothing to do. */ + unsigned unref_count = 0; + for (unsigned i = start_point; i <= end_point; i++) + if (!deltas[i].flag) unref_count++; + + unsigned j = start_point; + if (unref_count == 0 || unref_count > end_point - start_point) + goto no_more_gaps; + + for (;;) + { + /* Locate the next gap of unreferenced points between two referenced points prev and next. + * Note that a gap may wrap around at left (start_point) and/or at right (end_point). + */ + unsigned int prev, next, i; + for (;;) + { + i = j; + j = next_index (i, start_point, end_point); + if (deltas[i].flag && !deltas[j].flag) break; + } + prev = j = i; + for (;;) + { + i = j; + j = next_index (i, start_point, end_point); + if (!deltas[i].flag && deltas[j].flag) break; + } + next = j; + /* Infer deltas for all unref points in the gap between prev and next */ + i = prev; + for (;;) + { + i = next_index (i, start_point, end_point); + if (i == next) break; + deltas[i].x = infer_delta (orig_points.as_array (), deltas.as_array (), i, prev, next, &contour_point_t::x); + deltas[i].y = infer_delta (orig_points.as_array (), deltas.as_array (), i, prev, next, &contour_point_t::y); + if (--unref_count == 0) goto no_more_gaps; + } + } + no_more_gaps: + start_point = end_point + 1; + } + + /* apply specified / inferred deltas to points */ + for (unsigned int i = 0; i < points.length; i++) + { + points.arrayZ[i].x += deltas.arrayZ[i].x; + points.arrayZ[i].y += deltas.arrayZ[i].y; + } + } while (iterator.move_to_next ()); + + return true; + } + + unsigned int get_axis_count () const { return table->axisCount; } + + private: + hb_blob_ptr_t table; + }; + + protected: + FixedVersion<>version; /* Version number of the glyph variations table + * Set to 0x00010000u. */ + HBUINT16 axisCount; /* The number of variation axes for this font. This must be + * the same number as axisCount in the 'fvar' table. */ + HBUINT16 sharedTupleCount; + /* The number of shared tuple records. Shared tuple records + * can be referenced within glyph variation data tables for + * multiple glyphs, as opposed to other tuple records stored + * directly within a glyph variation data table. */ + NNOffset32To> + sharedTuples; /* Offset from the start of this table to the shared tuple records. + * Array of tuple records shared across all glyph variation data tables. */ + HBUINT16 glyphCount; /* The number of glyphs in this font. This must match the number of + * glyphs stored elsewhere in the font. */ + HBUINT16 flags; /* Bit-field that gives the format of the offset array that follows. + * If bit 0 is clear, the offsets are uint16; if bit 0 is set, the + * offsets are uint32. */ + Offset32To + dataZ; /* Offset from the start of this table to the array of + * GlyphVariationData tables. */ + UnsizedArrayOf + offsetZ; /* Offsets from the start of the GlyphVariationData array + * to each GlyphVariationData table. */ + public: + DEFINE_SIZE_ARRAY (20, offsetZ); +}; + +struct gvar_accelerator_t : gvar::accelerator_t { + gvar_accelerator_t (hb_face_t *face) : gvar::accelerator_t (face) {} +}; + +} /* namespace OT */ + +#endif /* HB_OT_VAR_GVAR_TABLE_HH */ diff --git a/libs/harfbuzz/src/hb-ot-var-hvar-table.hh b/libs/harfbuzz/src/hb-ot-var-hvar-table.hh new file mode 100644 index 00000000..53355c50 --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-var-hvar-table.hh @@ -0,0 +1,415 @@ +/* + * Copyright © 2017 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 HB_OT_VAR_HVAR_TABLE_HH +#define HB_OT_VAR_HVAR_TABLE_HH + +#include "hb-ot-layout-common.hh" +#include "hb-ot-var-common.hh" + +namespace OT { + + +struct index_map_subset_plan_t +{ + enum index_map_index_t { + ADV_INDEX, + LSB_INDEX, /* dual as TSB */ + RSB_INDEX, /* dual as BSB */ + VORG_INDEX + }; + + void init (const DeltaSetIndexMap &index_map, + hb_inc_bimap_t &outer_map, + hb_vector_t &inner_sets, + const hb_subset_plan_t *plan) + { + map_count = 0; + outer_bit_count = 0; + inner_bit_count = 1; + max_inners.init (); + output_map.init (); + + if (&index_map == &Null (DeltaSetIndexMap)) return; + + unsigned int last_val = (unsigned int)-1; + hb_codepoint_t last_gid = (hb_codepoint_t)-1; + hb_codepoint_t gid = (hb_codepoint_t) hb_min (index_map.get_map_count (), plan->num_output_glyphs ()); + + outer_bit_count = (index_map.get_width () * 8) - index_map.get_inner_bit_count (); + max_inners.resize (inner_sets.length); + for (unsigned i = 0; i < inner_sets.length; i++) max_inners[i] = 0; + + /* Search backwards for a map value different from the last map value */ + for (; gid > 0; gid--) + { + hb_codepoint_t old_gid; + if (!plan->old_gid_for_new_gid (gid - 1, &old_gid)) + { + if (last_gid == (hb_codepoint_t) -1) + continue; + else + break; + } + + unsigned int v = index_map.map (old_gid); + if (last_gid == (hb_codepoint_t) -1) + { + last_val = v; + last_gid = gid; + continue; + } + if (v != last_val) break; + + last_gid = gid; + } + + if (unlikely (last_gid == (hb_codepoint_t)-1)) return; + map_count = last_gid; + for (gid = 0; gid < map_count; gid++) + { + hb_codepoint_t old_gid; + if (plan->old_gid_for_new_gid (gid, &old_gid)) + { + unsigned int v = index_map.map (old_gid); + unsigned int outer = v >> 16; + unsigned int inner = v & 0xFFFF; + outer_map.add (outer); + if (inner > max_inners[outer]) max_inners[outer] = inner; + if (outer >= inner_sets.length) return; + inner_sets[outer]->add (inner); + } + } + } + + void fini () + { + max_inners.fini (); + output_map.fini (); + } + + void remap (const DeltaSetIndexMap *input_map, + const hb_inc_bimap_t &outer_map, + const hb_vector_t &inner_maps, + const hb_subset_plan_t *plan) + { + if (input_map == &Null (DeltaSetIndexMap)) return; + + for (unsigned int i = 0; i < max_inners.length; i++) + { + if (inner_maps[i].get_population () == 0) continue; + unsigned int bit_count = (max_inners[i]==0)? 1: hb_bit_storage (inner_maps[i][max_inners[i]]); + if (bit_count > inner_bit_count) inner_bit_count = bit_count; + } + + output_map.resize (map_count); + for (hb_codepoint_t gid = 0; gid < output_map.length; gid++) + { + hb_codepoint_t old_gid; + if (plan->old_gid_for_new_gid (gid, &old_gid)) + { + uint32_t v = input_map->map (old_gid); + unsigned int outer = v >> 16; + output_map[gid] = (outer_map[outer] << 16) | (inner_maps[outer][v & 0xFFFF]); + } + else + output_map[gid] = 0; /* Map unused glyph to outer/inner=0/0 */ + } + } + + unsigned int get_inner_bit_count () const { return inner_bit_count; } + unsigned int get_width () const { return ((outer_bit_count + inner_bit_count + 7) / 8); } + unsigned int get_map_count () const { return map_count; } + + unsigned int get_size () const + { return (map_count? (DeltaSetIndexMap::min_size + get_width () * map_count): 0); } + + bool is_identity () const { return get_output_map ().length == 0; } + hb_array_t get_output_map () const { return output_map.as_array (); } + + protected: + unsigned int map_count; + hb_vector_t max_inners; + unsigned int outer_bit_count; + unsigned int inner_bit_count; + hb_vector_t output_map; +}; + +struct hvarvvar_subset_plan_t +{ + hvarvvar_subset_plan_t() : inner_maps (), index_map_plans () {} + ~hvarvvar_subset_plan_t() { fini (); } + + void init (const hb_array_t &index_maps, + const VariationStore &_var_store, + const hb_subset_plan_t *plan) + { + index_map_plans.resize (index_maps.length); + + var_store = &_var_store; + inner_sets.resize (var_store->get_sub_table_count ()); + for (unsigned int i = 0; i < inner_sets.length; i++) + inner_sets[i] = hb_set_create (); + adv_set = hb_set_create (); + + inner_maps.resize (var_store->get_sub_table_count ()); + + if (unlikely (!index_map_plans.length || !inner_sets.length || !inner_maps.length)) return; + + bool retain_adv_map = false; + index_map_plans[0].init (*index_maps[0], outer_map, inner_sets, plan); + if (index_maps[0] == &Null (DeltaSetIndexMap)) + { + retain_adv_map = plan->flags & HB_SUBSET_FLAGS_RETAIN_GIDS; + outer_map.add (0); + for (hb_codepoint_t gid = 0; gid < plan->num_output_glyphs (); gid++) + { + hb_codepoint_t old_gid; + if (plan->old_gid_for_new_gid (gid, &old_gid)) + inner_sets[0]->add (old_gid); + } + hb_set_union (adv_set, inner_sets[0]); + } + + for (unsigned int i = 1; i < index_maps.length; i++) + index_map_plans[i].init (*index_maps[i], outer_map, inner_sets, plan); + + outer_map.sort (); + + if (retain_adv_map) + { + for (hb_codepoint_t gid = 0; gid < plan->num_output_glyphs (); gid++) + if (inner_sets[0]->has (gid)) + inner_maps[0].add (gid); + else + inner_maps[0].skip (); + } + else + { + inner_maps[0].add_set (adv_set); + hb_set_subtract (inner_sets[0], adv_set); + inner_maps[0].add_set (inner_sets[0]); + } + + for (unsigned int i = 1; i < inner_maps.length; i++) + inner_maps[i].add_set (inner_sets[i]); + + for (unsigned int i = 0; i < index_maps.length; i++) + index_map_plans[i].remap (index_maps[i], outer_map, inner_maps, plan); + } + + void fini () + { + for (unsigned int i = 0; i < inner_sets.length; i++) + hb_set_destroy (inner_sets[i]); + hb_set_destroy (adv_set); + inner_maps.fini (); + index_map_plans.fini (); + } + + hb_inc_bimap_t outer_map; + hb_vector_t inner_maps; + hb_vector_t index_map_plans; + const VariationStore *var_store; + + protected: + hb_vector_t inner_sets; + hb_set_t *adv_set; +}; + +/* + * HVAR -- Horizontal Metrics Variations + * https://docs.microsoft.com/en-us/typography/opentype/spec/hvar + * VVAR -- Vertical Metrics Variations + * https://docs.microsoft.com/en-us/typography/opentype/spec/vvar + */ +#define HB_OT_TAG_HVAR HB_TAG('H','V','A','R') +#define HB_OT_TAG_VVAR HB_TAG('V','V','A','R') + +struct HVARVVAR +{ + static constexpr hb_tag_t HVARTag = HB_OT_TAG_HVAR; + static constexpr hb_tag_t VVARTag = HB_OT_TAG_VVAR; + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (version.sanitize (c) && + likely (version.major == 1) && + varStore.sanitize (c, this) && + advMap.sanitize (c, this) && + lsbMap.sanitize (c, this) && + rsbMap.sanitize (c, this)); + } + + void listup_index_maps (hb_vector_t &index_maps) const + { + index_maps.push (&(this+advMap)); + index_maps.push (&(this+lsbMap)); + index_maps.push (&(this+rsbMap)); + } + + bool serialize_index_maps (hb_serialize_context_t *c, + const hb_array_t &im_plans) + { + TRACE_SERIALIZE (this); + if (im_plans[index_map_subset_plan_t::ADV_INDEX].is_identity ()) + advMap = 0; + else if (unlikely (!advMap.serialize_serialize (c, im_plans[index_map_subset_plan_t::ADV_INDEX]))) + return_trace (false); + if (im_plans[index_map_subset_plan_t::LSB_INDEX].is_identity ()) + lsbMap = 0; + else if (unlikely (!lsbMap.serialize_serialize (c, im_plans[index_map_subset_plan_t::LSB_INDEX]))) + return_trace (false); + if (im_plans[index_map_subset_plan_t::RSB_INDEX].is_identity ()) + rsbMap = 0; + else if (unlikely (!rsbMap.serialize_serialize (c, im_plans[index_map_subset_plan_t::RSB_INDEX]))) + return_trace (false); + + return_trace (true); + } + + template + bool _subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + hvarvvar_subset_plan_t hvar_plan; + hb_vector_t + index_maps; + + ((T*)this)->listup_index_maps (index_maps); + hvar_plan.init (index_maps.as_array (), this+varStore, c->plan); + + T *out = c->serializer->allocate_min (); + if (unlikely (!out)) return_trace (false); + + out->version.major = 1; + out->version.minor = 0; + + if (unlikely (!out->varStore + .serialize_serialize (c->serializer, + hvar_plan.var_store, + hvar_plan.inner_maps.as_array ()))) + return_trace (false); + + return_trace (out->T::serialize_index_maps (c->serializer, + hvar_plan.index_map_plans.as_array ())); + } + + float get_advance_delta_unscaled (hb_codepoint_t glyph, + const int *coords, unsigned int coord_count, + VariationStore::cache_t *store_cache = nullptr) const + { + uint32_t varidx = (this+advMap).map (glyph); + return (this+varStore).get_delta (varidx, + coords, coord_count, + store_cache); + } + + bool get_lsb_delta_unscaled (hb_codepoint_t glyph, + const int *coords, unsigned int coord_count, + float *lsb) const + { + if (!lsbMap) return false; + uint32_t varidx = (this+lsbMap).map (glyph); + *lsb = (this+varStore).get_delta (varidx, coords, coord_count); + return true; + } + + public: + FixedVersion<>version; /* Version of the metrics variation table + * initially set to 0x00010000u */ + Offset32To + varStore; /* Offset to item variation store table. */ + Offset32To + advMap; /* Offset to advance var-idx mapping. */ + Offset32To + lsbMap; /* Offset to lsb/tsb var-idx mapping. */ + Offset32To + rsbMap; /* Offset to rsb/bsb var-idx mapping. */ + + public: + DEFINE_SIZE_STATIC (20); +}; + +struct HVAR : HVARVVAR { + static constexpr hb_tag_t tableTag = HB_OT_TAG_HVAR; + bool subset (hb_subset_context_t *c) const { return HVARVVAR::_subset (c); } +}; +struct VVAR : HVARVVAR { + static constexpr hb_tag_t tableTag = HB_OT_TAG_VVAR; + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (static_cast (this)->sanitize (c) && + vorgMap.sanitize (c, this)); + } + + void listup_index_maps (hb_vector_t &index_maps) const + { + HVARVVAR::listup_index_maps (index_maps); + index_maps.push (&(this+vorgMap)); + } + + bool serialize_index_maps (hb_serialize_context_t *c, + const hb_array_t &im_plans) + { + TRACE_SERIALIZE (this); + if (unlikely (!HVARVVAR::serialize_index_maps (c, im_plans))) + return_trace (false); + if (!im_plans[index_map_subset_plan_t::VORG_INDEX].get_map_count ()) + vorgMap = 0; + else if (unlikely (!vorgMap.serialize_serialize (c, im_plans[index_map_subset_plan_t::VORG_INDEX]))) + return_trace (false); + + return_trace (true); + } + + bool subset (hb_subset_context_t *c) const { return HVARVVAR::_subset (c); } + + bool get_vorg_delta_unscaled (hb_codepoint_t glyph, + const int *coords, unsigned int coord_count, + float *delta) const + { + if (!vorgMap) return false; + uint32_t varidx = (this+vorgMap).map (glyph); + *delta = (this+varStore).get_delta (varidx, coords, coord_count); + return true; + } + + protected: + Offset32To + vorgMap; /* Offset to vertical-origin var-idx mapping. */ + + public: + DEFINE_SIZE_STATIC (24); +}; + +} /* namespace OT */ + + +#endif /* HB_OT_VAR_HVAR_TABLE_HH */ diff --git a/libs/harfbuzz/src/hb-ot-var-mvar-table.hh b/libs/harfbuzz/src/hb-ot-var-mvar-table.hh new file mode 100644 index 00000000..420366fb --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-var-mvar-table.hh @@ -0,0 +1,119 @@ +/* + * Copyright © 2017 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 HB_OT_VAR_MVAR_TABLE_HH +#define HB_OT_VAR_MVAR_TABLE_HH + +#include "hb-ot-layout-common.hh" + + +namespace OT { + + +struct VariationValueRecord +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + public: + Tag valueTag; /* Four-byte tag identifying a font-wide measure. */ + VarIdx varIdx; /* Outer/inner index into VariationStore item. */ + + public: + DEFINE_SIZE_STATIC (8); +}; + + +/* + * MVAR -- Metrics Variations + * https://docs.microsoft.com/en-us/typography/opentype/spec/mvar + */ +#define HB_OT_TAG_MVAR HB_TAG('M','V','A','R') + +struct MVAR +{ + static constexpr hb_tag_t tableTag = HB_OT_TAG_MVAR; + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (version.sanitize (c) && + likely (version.major == 1) && + c->check_struct (this) && + valueRecordSize >= VariationValueRecord::static_size && + varStore.sanitize (c, this) && + c->check_range (valuesZ.arrayZ, + valueRecordCount, + valueRecordSize)); + } + + float get_var (hb_tag_t tag, + const int *coords, unsigned int coord_count) const + { + const VariationValueRecord *record; + record = (VariationValueRecord *) hb_bsearch (tag, + (const VariationValueRecord *) + (const HBUINT8 *) valuesZ, + valueRecordCount, valueRecordSize, + tag_compare); + if (!record) + return 0.; + + return (this+varStore).get_delta (record->varIdx, coords, coord_count); + } + +protected: + static int tag_compare (const void *pa, const void *pb) + { + const hb_tag_t *a = (const hb_tag_t *) pa; + const Tag *b = (const Tag *) pb; + return b->cmp (*a); + } + + protected: + FixedVersion<>version; /* Version of the metrics variation table + * initially set to 0x00010000u */ + HBUINT16 reserved; /* Not used; set to 0. */ + HBUINT16 valueRecordSize;/* The size in bytes of each value record — + * must be greater than zero. */ + HBUINT16 valueRecordCount;/* The number of value records — may be zero. */ + Offset16To + varStore; /* Offset to item variation store table. */ + UnsizedArrayOf + valuesZ; /* Array of value records. The records must be + * in binary order of their valueTag field. */ + + public: + DEFINE_SIZE_ARRAY (12, valuesZ); +}; + +} /* namespace OT */ + + +#endif /* HB_OT_VAR_MVAR_TABLE_HH */ diff --git a/libs/harfbuzz/src/hb-ot-var.cc b/libs/harfbuzz/src/hb-ot-var.cc new file mode 100644 index 00000000..f000f272 --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-var.cc @@ -0,0 +1,328 @@ +/* + * Copyright © 2017 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 + */ + +#include "hb.hh" + +#ifndef HB_NO_VAR + +#include "hb-ot-var.h" + +#include "hb-ot-var-avar-table.hh" +#include "hb-ot-var-fvar-table.hh" +#include "hb-ot-var-mvar-table.hh" + + +/** + * SECTION:hb-ot-var + * @title: hb-ot-var + * @short_description: OpenType Font Variations + * @include: hb-ot.h + * + * Functions for fetching information about OpenType Variable Fonts. + **/ + + +/* + * fvar/avar + */ + + +/** + * hb_ot_var_has_data: + * @face: The #hb_face_t to work on + * + * Tests whether a face includes any OpenType variation data in the `fvar` table. + * + * Return value: `true` if data found, `false` otherwise + * + * Since: 1.4.2 + **/ +hb_bool_t +hb_ot_var_has_data (hb_face_t *face) +{ + return face->table.fvar->has_data (); +} + +/** + * hb_ot_var_get_axis_count: + * @face: The #hb_face_t to work on + * + * Fetches the number of OpenType variation axes included in the face. + * + * Return value: the number of variation axes defined + * + * Since: 1.4.2 + **/ +unsigned int +hb_ot_var_get_axis_count (hb_face_t *face) +{ + return face->table.fvar->get_axis_count (); +} + +#ifndef HB_DISABLE_DEPRECATED +/** + * hb_ot_var_get_axes: + * @face: #hb_face_t to work upon + * @start_offset: offset of the first lookup to retrieve + * @axes_count: (inout) (optional): Input = the maximum number of variation axes to return; + * Output = the actual number of variation axes returned (may be zero) + * @axes_array: (out caller-allocates) (array length=axes_count): The array of variation axes found + * + * Fetches a list of all variation axes in the specified face. The list returned will begin + * at the offset provided. + * + * Since: 1.4.2 + * Deprecated: 2.2.0: use hb_ot_var_get_axis_infos() instead + **/ +unsigned int +hb_ot_var_get_axes (hb_face_t *face, + unsigned int start_offset, + unsigned int *axes_count /* IN/OUT */, + hb_ot_var_axis_t *axes_array /* OUT */) +{ + return face->table.fvar->get_axes_deprecated (start_offset, axes_count, axes_array); +} + +/** + * hb_ot_var_find_axis: + * @face: #hb_face_t to work upon + * @axis_tag: The #hb_tag_t of the variation axis to query + * @axis_index: The index of the variation axis + * @axis_info: (out): The #hb_ot_var_axis_info_t of the axis tag queried + * + * Fetches the variation-axis information corresponding to the specified axis tag + * in the specified face. + * + * Since: 1.4.2 + * Deprecated: 2.2.0 - use hb_ot_var_find_axis_info() instead + **/ +hb_bool_t +hb_ot_var_find_axis (hb_face_t *face, + hb_tag_t axis_tag, + unsigned int *axis_index, + hb_ot_var_axis_t *axis_info) +{ + return face->table.fvar->find_axis_deprecated (axis_tag, axis_index, axis_info); +} +#endif + +/** + * hb_ot_var_get_axis_infos: + * @face: #hb_face_t to work upon + * @start_offset: offset of the first lookup to retrieve + * @axes_count: (inout) (optional): Input = the maximum number of variation axes to return; + * Output = the actual number of variation axes returned (may be zero) + * @axes_array: (out caller-allocates) (array length=axes_count): The array of variation axes found + * + * Fetches a list of all variation axes in the specified face. The list returned will begin + * at the offset provided. + * + * Return value: the number of variation axes in the face + * + * Since: 2.2.0 + **/ +HB_EXTERN unsigned int +hb_ot_var_get_axis_infos (hb_face_t *face, + unsigned int start_offset, + unsigned int *axes_count /* IN/OUT */, + hb_ot_var_axis_info_t *axes_array /* OUT */) +{ + return face->table.fvar->get_axis_infos (start_offset, axes_count, axes_array); +} + +/** + * hb_ot_var_find_axis_info: + * @face: #hb_face_t to work upon + * @axis_tag: The #hb_tag_t of the variation axis to query + * @axis_info: (out): The #hb_ot_var_axis_info_t of the axis tag queried + * + * Fetches the variation-axis information corresponding to the specified axis tag + * in the specified face. + * + * Return value: `true` if data found, `false` otherwise + * + * Since: 2.2.0 + **/ +HB_EXTERN hb_bool_t +hb_ot_var_find_axis_info (hb_face_t *face, + hb_tag_t axis_tag, + hb_ot_var_axis_info_t *axis_info) +{ + return face->table.fvar->find_axis_info (axis_tag, axis_info); +} + + +/* + * Named instances. + */ + +/** + * hb_ot_var_get_named_instance_count: + * @face: The #hb_face_t to work on + * + * Fetches the number of named instances included in the face. + * + * Return value: the number of named instances defined + * + * Since: 2.2.0 + **/ +unsigned int +hb_ot_var_get_named_instance_count (hb_face_t *face) +{ + return face->table.fvar->get_instance_count (); +} + +/** + * hb_ot_var_named_instance_get_subfamily_name_id: + * @face: The #hb_face_t to work on + * @instance_index: The index of the named instance to query + * + * Fetches the `name` table Name ID that provides display names for + * the "Subfamily name" defined for the given named instance in the face. + * + * Return value: the Name ID found for the Subfamily name + * + * Since: 2.2.0 + **/ +hb_ot_name_id_t +hb_ot_var_named_instance_get_subfamily_name_id (hb_face_t *face, + unsigned int instance_index) +{ + return face->table.fvar->get_instance_subfamily_name_id (instance_index); +} + +/** + * hb_ot_var_named_instance_get_postscript_name_id: + * @face: The #hb_face_t to work on + * @instance_index: The index of the named instance to query + * + * Fetches the `name` table Name ID that provides display names for + * the "PostScript name" defined for the given named instance in the face. + * + * Return value: the Name ID found for the PostScript name + * + * Since: 2.2.0 + **/ +hb_ot_name_id_t +hb_ot_var_named_instance_get_postscript_name_id (hb_face_t *face, + unsigned int instance_index) +{ + return face->table.fvar->get_instance_postscript_name_id (instance_index); +} + +/** + * hb_ot_var_named_instance_get_design_coords: + * @face: The #hb_face_t to work on + * @instance_index: The index of the named instance to query + * @coords_length: (inout) (optional): Input = the maximum number of coordinates to return; + * Output = the actual number of coordinates returned (may be zero) + * @coords: (out) (array length=coords_length): The array of coordinates found for the query + * + * Fetches the design-space coordinates corresponding to the given + * named instance in the face. + * + * Return value: the number of variation axes in the face + * + * Since: 2.2.0 + **/ +unsigned int +hb_ot_var_named_instance_get_design_coords (hb_face_t *face, + unsigned int instance_index, + unsigned int *coords_length, /* IN/OUT */ + float *coords /* OUT */) +{ + return face->table.fvar->get_instance_coords (instance_index, coords_length, coords); +} + + +/** + * hb_ot_var_normalize_variations: + * @face: The #hb_face_t to work on + * @variations: The array of variations to normalize + * @variations_length: The number of variations to normalize + * @coords: (out) (array length=coords_length): The array of normalized coordinates + * @coords_length: The length of the coordinate array + * + * Normalizes all of the coordinates in the given list of variation axes. + * + * Since: 1.4.2 + **/ +void +hb_ot_var_normalize_variations (hb_face_t *face, + const hb_variation_t *variations, /* IN */ + unsigned int variations_length, + int *coords, /* OUT */ + unsigned int coords_length) +{ + for (unsigned int i = 0; i < coords_length; i++) + coords[i] = 0; + + const OT::fvar &fvar = *face->table.fvar; + for (unsigned int i = 0; i < variations_length; i++) + { + hb_ot_var_axis_info_t info; + if (hb_ot_var_find_axis_info (face, variations[i].tag, &info) && + info.axis_index < coords_length) + coords[info.axis_index] = fvar.normalize_axis_value (info.axis_index, variations[i].value); + } + + face->table.avar->map_coords (coords, coords_length); +} + +/** + * hb_ot_var_normalize_coords: + * @face: The #hb_face_t to work on + * @coords_length: The length of the coordinate array + * @design_coords: The design-space coordinates to normalize + * @normalized_coords: (out): The normalized coordinates + * + * Normalizes the given design-space coordinates. The minimum and maximum + * values for the axis are mapped to the interval [-1,1], with the default + * axis value mapped to 0. + * + * The normalized values have 14 bits of fixed-point sub-integer precision as per + * OpenType specification. + * + * Any additional scaling defined in the face's `avar` table is also + * applied, as described at https://docs.microsoft.com/en-us/typography/opentype/spec/avar + * + * Since: 1.4.2 + **/ +void +hb_ot_var_normalize_coords (hb_face_t *face, + unsigned int coords_length, + const float *design_coords, /* IN */ + int *normalized_coords /* OUT */) +{ + const OT::fvar &fvar = *face->table.fvar; + for (unsigned int i = 0; i < coords_length; i++) + normalized_coords[i] = fvar.normalize_axis_value (i, design_coords[i]); + + face->table.avar->map_coords (normalized_coords, coords_length); +} + + +#endif diff --git a/libs/harfbuzz/src/hb-ot-var.h b/libs/harfbuzz/src/hb-ot-var.h new file mode 100644 index 00000000..05147cc2 --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-var.h @@ -0,0 +1,191 @@ +/* + * Copyright © 2017 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. + * + * Red Hat Author(s): Behdad Esfahbod + */ + +#if !defined(HB_OT_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR) +#error "Include instead." +#endif + +#ifndef HB_OT_VAR_H +#define HB_OT_VAR_H + +#include "hb.h" + +HB_BEGIN_DECLS + +/** + * HB_OT_TAG_VAR_AXIS_ITALIC: + * + * Registered tag for the roman/italic axis. + */ +#define HB_OT_TAG_VAR_AXIS_ITALIC HB_TAG('i','t','a','l') + +/** + * HB_OT_TAG_VAR_AXIS_OPTICAL_SIZE: + * + * Registered tag for the optical-size axis. + * Note: The optical-size axis supersedes the OpenType `size` feature. + */ +#define HB_OT_TAG_VAR_AXIS_OPTICAL_SIZE HB_TAG('o','p','s','z') + +/** + * HB_OT_TAG_VAR_AXIS_SLANT: + * + * Registered tag for the slant axis + */ +#define HB_OT_TAG_VAR_AXIS_SLANT HB_TAG('s','l','n','t') + +/** + * HB_OT_TAG_VAR_AXIS_WIDTH: + * + * Registered tag for the width axis. + */ +#define HB_OT_TAG_VAR_AXIS_WIDTH HB_TAG('w','d','t','h') + +/** + * HB_OT_TAG_VAR_AXIS_WEIGHT: + * + * Registered tag for the weight axis. + */ +#define HB_OT_TAG_VAR_AXIS_WEIGHT HB_TAG('w','g','h','t') + + +/* + * fvar / avar + */ + +HB_EXTERN hb_bool_t +hb_ot_var_has_data (hb_face_t *face); + + +/* + * Variation axes. + */ + + +HB_EXTERN unsigned int +hb_ot_var_get_axis_count (hb_face_t *face); + +/** + * hb_ot_var_axis_flags_t: + * @HB_OT_VAR_AXIS_FLAG_HIDDEN: The axis should not be exposed directly in user interfaces. + * + * Flags for #hb_ot_var_axis_info_t. + * + * Since: 2.2.0 + */ +typedef enum { /*< flags >*/ + HB_OT_VAR_AXIS_FLAG_HIDDEN = 0x00000001u, + + /*< private >*/ + _HB_OT_VAR_AXIS_FLAG_MAX_VALUE= HB_TAG_MAX_SIGNED /*< skip >*/ +} hb_ot_var_axis_flags_t; + +/** + * hb_ot_var_axis_info_t: + * @axis_index: Index of the axis in the variation-axis array + * @tag: The #hb_tag_t tag identifying the design variation of the axis + * @name_id: The `name` table Name ID that provides display names for the axis + * @flags: The #hb_ot_var_axis_flags_t flags for the axis + * @min_value: The minimum value on the variation axis that the font covers + * @default_value: The position on the variation axis corresponding to the font's defaults + * @max_value: The maximum value on the variation axis that the font covers + * + * Data type for holding variation-axis values. + * + * The minimum, default, and maximum values are in un-normalized, user scales. + * + * Note: at present, the only flag defined for @flags is + * #HB_OT_VAR_AXIS_FLAG_HIDDEN. + * + * Since: 2.2.0 + */ +typedef struct hb_ot_var_axis_info_t { + unsigned int axis_index; + hb_tag_t tag; + hb_ot_name_id_t name_id; + hb_ot_var_axis_flags_t flags; + float min_value; + float default_value; + float max_value; + /*< private >*/ + unsigned int reserved; +} hb_ot_var_axis_info_t; + +HB_EXTERN unsigned int +hb_ot_var_get_axis_infos (hb_face_t *face, + unsigned int start_offset, + unsigned int *axes_count /* IN/OUT */, + hb_ot_var_axis_info_t *axes_array /* OUT */); + +HB_EXTERN hb_bool_t +hb_ot_var_find_axis_info (hb_face_t *face, + hb_tag_t axis_tag, + hb_ot_var_axis_info_t *axis_info); + + +/* + * Named instances. + */ + +HB_EXTERN unsigned int +hb_ot_var_get_named_instance_count (hb_face_t *face); + +HB_EXTERN hb_ot_name_id_t +hb_ot_var_named_instance_get_subfamily_name_id (hb_face_t *face, + unsigned int instance_index); + +HB_EXTERN hb_ot_name_id_t +hb_ot_var_named_instance_get_postscript_name_id (hb_face_t *face, + unsigned int instance_index); + +HB_EXTERN unsigned int +hb_ot_var_named_instance_get_design_coords (hb_face_t *face, + unsigned int instance_index, + unsigned int *coords_length, /* IN/OUT */ + float *coords /* OUT */); + + +/* + * Conversions. + */ + +HB_EXTERN void +hb_ot_var_normalize_variations (hb_face_t *face, + const hb_variation_t *variations, /* IN */ + unsigned int variations_length, + int *coords, /* OUT */ + unsigned int coords_length); + +HB_EXTERN void +hb_ot_var_normalize_coords (hb_face_t *face, + unsigned int coords_length, + const float *design_coords, /* IN */ + int *normalized_coords /* OUT */); + + +HB_END_DECLS + +#endif /* HB_OT_VAR_H */ diff --git a/libs/harfbuzz/src/hb-ot-vorg-table.hh b/libs/harfbuzz/src/hb-ot-vorg-table.hh new file mode 100644 index 00000000..811e1391 --- /dev/null +++ b/libs/harfbuzz/src/hb-ot-vorg-table.hh @@ -0,0 +1,136 @@ +/* + * Copyright © 2018 Adobe 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. + * + * Adobe Author(s): Michiharu Ariza + */ + +#ifndef HB_OT_VORG_TABLE_HH +#define HB_OT_VORG_TABLE_HH + +#include "hb-open-type.hh" + +/* + * VORG -- Vertical Origin Table + * https://docs.microsoft.com/en-us/typography/opentype/spec/vorg + */ +#define HB_OT_TAG_VORG HB_TAG('V','O','R','G') + +namespace OT { + +struct VertOriginMetric +{ + int cmp (hb_codepoint_t g) const { return glyph.cmp (g); } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + public: + HBGlyphID16 glyph; + FWORD vertOriginY; + + public: + DEFINE_SIZE_STATIC (4); +}; + +struct VORG +{ + static constexpr hb_tag_t tableTag = HB_OT_TAG_VORG; + + bool has_data () const { return version.to_int (); } + + int get_y_origin (hb_codepoint_t glyph) const + { + unsigned int i; + if (!vertYOrigins.bfind (glyph, &i)) + return defaultVertOriginY; + return vertYOrigins[i].vertOriginY; + } + + template + void serialize (hb_serialize_context_t *c, + Iterator it, + FWORD defaultVertOriginY) + { + + if (unlikely (!c->extend_min ((*this)))) return; + + this->version.major = 1; + this->version.minor = 0; + + this->defaultVertOriginY = defaultVertOriginY; + this->vertYOrigins.len = it.len (); + + c->copy_all (it); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + VORG *vorg_prime = c->serializer->start_embed (); + if (unlikely (!c->serializer->check_success (vorg_prime))) return_trace (false); + + auto it = + + vertYOrigins.as_array () + | hb_filter (c->plan->glyphset (), &VertOriginMetric::glyph) + | hb_map ([&] (const VertOriginMetric& _) + { + hb_codepoint_t new_glyph = HB_SET_VALUE_INVALID; + c->plan->new_gid_for_old_gid (_.glyph, &new_glyph); + + VertOriginMetric metric; + metric.glyph = new_glyph; + metric.vertOriginY = _.vertOriginY; + return metric; + }) + ; + + /* serialize the new table */ + vorg_prime->serialize (c->serializer, it, defaultVertOriginY); + return_trace (true); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + version.major == 1 && + vertYOrigins.sanitize (c)); + } + + protected: + FixedVersion<>version; /* Version of VORG table. Set to 0x00010000u. */ + FWORD defaultVertOriginY; + /* The default vertical origin. */ + SortedArray16Of + vertYOrigins; /* The array of vertical origins. */ + + public: + DEFINE_SIZE_ARRAY(8, vertYOrigins); +}; +} /* namespace OT */ + +#endif /* HB_OT_VORG_TABLE_HH */ diff --git a/libs/harfbuzz/src/hb-ot.h b/libs/harfbuzz/src/hb-ot.h new file mode 100644 index 00000000..f2dbaa1b --- /dev/null +++ b/libs/harfbuzz/src/hb-ot.h @@ -0,0 +1,49 @@ +/* + * Copyright © 2009 Red Hat, 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. + * + * Red Hat Author(s): Behdad Esfahbod + */ + +#ifndef HB_OT_H +#define HB_OT_H +#define HB_OT_H_IN + +#include "hb.h" + +#include "hb-ot-color.h" +#include "hb-ot-deprecated.h" +#include "hb-ot-font.h" +#include "hb-ot-layout.h" +#include "hb-ot-math.h" +#include "hb-ot-meta.h" +#include "hb-ot-metrics.h" +#include "hb-ot-name.h" +#include "hb-ot-shape.h" +#include "hb-ot-var.h" + +HB_BEGIN_DECLS + +HB_END_DECLS + +#undef HB_OT_H_IN +#endif /* HB_OT_H */ diff --git a/libs/harfbuzz/src/hb-pool.hh b/libs/harfbuzz/src/hb-pool.hh new file mode 100644 index 00000000..dcf8f662 --- /dev/null +++ b/libs/harfbuzz/src/hb-pool.hh @@ -0,0 +1,100 @@ +/* + * Copyright © 2019 Facebook, 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. + * + * Facebook Author(s): Behdad Esfahbod + */ + +#ifndef HB_POOL_HH +#define HB_POOL_HH + +#include "hb.hh" + +/* Memory pool for persistent allocation of small objects. */ + +template +struct hb_pool_t +{ + hb_pool_t () : next (nullptr) {} + ~hb_pool_t () { fini (); } + + void fini () + { + next = nullptr; + + for (chunk_t *_ : chunks) hb_free (_); + + chunks.fini (); + } + + T* alloc () + { + if (unlikely (!next)) + { + if (unlikely (!chunks.alloc (chunks.length + 1))) return nullptr; + chunk_t *chunk = (chunk_t *) hb_calloc (1, sizeof (chunk_t)); + if (unlikely (!chunk)) return nullptr; + chunks.push (chunk); + next = chunk->thread (); + } + + T* obj = next; + next = * ((T**) next); + + memset (obj, 0, sizeof (T)); + + return obj; + } + + void release (T* obj) + { + * (T**) obj = next; + next = obj; + } + + private: + + static_assert (ChunkLen > 1, ""); + static_assert (sizeof (T) >= sizeof (void *), ""); + static_assert (alignof (T) % alignof (void *) == 0, ""); + + struct chunk_t + { + T* thread () + { + for (unsigned i = 0; i < ARRAY_LENGTH (arrayZ) - 1; i++) + * (T**) &arrayZ[i] = &arrayZ[i + 1]; + + * (T**) &arrayZ[ARRAY_LENGTH (arrayZ) - 1] = nullptr; + + return arrayZ; + } + + T arrayZ[ChunkLen]; + }; + + T* next; + hb_vector_t chunks; +}; + + +#endif /* HB_POOL_HH */ diff --git a/libs/harfbuzz/src/hb-priority-queue.hh b/libs/harfbuzz/src/hb-priority-queue.hh new file mode 100644 index 00000000..ffb86e30 --- /dev/null +++ b/libs/harfbuzz/src/hb-priority-queue.hh @@ -0,0 +1,151 @@ +/* + * Copyright © 2020 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): Garret Rieger + */ + +#ifndef HB_PRIORITY_QUEUE_HH +#define HB_PRIORITY_QUEUE_HH + +#include "hb.hh" +#include "hb-vector.hh" + +/* + * hb_priority_queue_t + * + * Priority queue implemented as a binary heap. Supports extract minimum + * and insert operations. + */ +struct hb_priority_queue_t +{ + private: + typedef hb_pair_t item_t; + hb_vector_t heap; + + public: + + void reset () { heap.resize (0); } + + bool in_error () const { return heap.in_error (); } + + void insert (int64_t priority, unsigned value) + { + heap.push (item_t (priority, value)); + if (unlikely (heap.in_error ())) return; + bubble_up (heap.length - 1); + } + + item_t pop_minimum () + { + assert (!is_empty ()); + + item_t result = heap.arrayZ[0]; + + heap.arrayZ[0] = heap.arrayZ[heap.length - 1]; + heap.shrink (heap.length - 1); + bubble_down (0); + + return result; + } + + const item_t& minimum () + { + return heap[0]; + } + + bool is_empty () const { return heap.length == 0; } + explicit operator bool () const { return !is_empty (); } + unsigned int get_population () const { return heap.length; } + + /* Sink interface. */ + hb_priority_queue_t& operator << (item_t item) + { insert (item.first, item.second); return *this; } + + private: + + static constexpr unsigned parent (unsigned index) + { + return (index - 1) / 2; + } + + static constexpr unsigned left_child (unsigned index) + { + return 2 * index + 1; + } + + static constexpr unsigned right_child (unsigned index) + { + return 2 * index + 2; + } + + void bubble_down (unsigned index) + { + assert (index <= heap.length); + + unsigned left = left_child (index); + unsigned right = right_child (index); + + bool has_left = left < heap.length; + if (!has_left) + // If there's no left, then there's also no right. + return; + + bool has_right = right < heap.length; + if (heap.arrayZ[index].first <= heap.arrayZ[left].first + && (!has_right || heap[index].first <= heap.arrayZ[right].first)) + return; + + if (!has_right || heap.arrayZ[left].first < heap.arrayZ[right].first) + { + swap (index, left); + bubble_down (left); + return; + } + + swap (index, right); + bubble_down (right); + } + + void bubble_up (unsigned index) + { + assert (index <= heap.length); + + if (index == 0) return; + + unsigned parent_index = parent (index); + if (heap.arrayZ[parent_index].first <= heap.arrayZ[index].first) + return; + + swap (index, parent_index); + bubble_up (parent_index); + } + + void swap (unsigned a, unsigned b) + { + assert (a <= heap.length); + assert (b <= heap.length); + hb_swap (heap.arrayZ[a], heap.arrayZ[b]); + } +}; + +#endif /* HB_PRIORITY_QUEUE_HH */ diff --git a/libs/harfbuzz/src/hb-repacker.hh b/libs/harfbuzz/src/hb-repacker.hh new file mode 100644 index 00000000..c97ce6cc --- /dev/null +++ b/libs/harfbuzz/src/hb-repacker.hh @@ -0,0 +1,385 @@ +/* + * Copyright © 2020 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): Garret Rieger + */ + +#ifndef HB_REPACKER_HH +#define HB_REPACKER_HH + +#include "hb-open-type.hh" +#include "hb-map.hh" +#include "hb-vector.hh" +#include "graph/graph.hh" +#include "graph/gsubgpos-graph.hh" +#include "graph/serialize.hh" + +using graph::graph_t; + +/* + * For a detailed writeup on the overflow resolution algorithm see: + * docs/repacker.md + */ + +struct lookup_size_t +{ + unsigned lookup_index; + size_t size; + unsigned num_subtables; + + static int cmp (const void* a, const void* b) + { + return cmp ((const lookup_size_t*) a, + (const lookup_size_t*) b); + } + + static int cmp (const lookup_size_t* a, const lookup_size_t* b) + { + double subtables_per_byte_a = (double) a->num_subtables / (double) a->size; + double subtables_per_byte_b = (double) b->num_subtables / (double) b->size; + if (subtables_per_byte_a == subtables_per_byte_b) { + return b->lookup_index - a->lookup_index; + } + + double cmp = subtables_per_byte_b - subtables_per_byte_a; + if (cmp < 0) return -1; + if (cmp > 0) return 1; + return 0; + } +}; + +static inline +bool _presplit_subtables_if_needed (graph::gsubgpos_graph_context_t& ext_context) +{ + // For each lookup this will check the size of subtables and split them as needed + // so that no subtable is at risk of overflowing. (where we support splitting for + // that subtable type). + // + // TODO(grieger): de-dup newly added nodes as necessary. Probably just want a full de-dup + // pass after this processing is done. Not super necessary as splits are + // only done where overflow is likely, so de-dup probably will get undone + // later anyways. + for (unsigned lookup_index : ext_context.lookups.keys ()) + { + graph::Lookup* lookup = ext_context.lookups.get(lookup_index); + if (!lookup->split_subtables_if_needed (ext_context, lookup_index)) + return false; + } + + return true; +} + +/* + * Analyze the lookups in a GSUB/GPOS table and decide if any should be promoted + * to extension lookups. + */ +static inline +bool _promote_extensions_if_needed (graph::gsubgpos_graph_context_t& ext_context) +{ + // Simple Algorithm (v1, current): + // 1. Calculate how many bytes each non-extension lookup consumes. + // 2. Select up to 64k of those to remain as non-extension (greedy, highest subtables per byte first) + // 3. Promote the rest. + // + // Advanced Algorithm (v2, not implemented): + // 1. Perform connected component analysis using lookups as roots. + // 2. Compute size of each connected component. + // 3. Select up to 64k worth of connected components to remain as non-extensions. + // (greedy, highest subtables per byte first) + // 4. Promote the rest. + + // TODO(garretrieger): support extension demotion, then consider all lookups. Requires advanced algo. + // TODO(garretrieger): also support extension promotion during iterative resolution phase, then + // we can use a less conservative threshold here. + // TODO(grieger): skip this for the 24 bit case. + if (!ext_context.lookups) return true; + + hb_vector_t lookup_sizes; + lookup_sizes.alloc (ext_context.lookups.get_population ()); + + for (unsigned lookup_index : ext_context.lookups.keys ()) + { + const graph::Lookup* lookup = ext_context.lookups.get(lookup_index); + hb_set_t visited; + lookup_sizes.push (lookup_size_t { + lookup_index, + ext_context.graph.find_subgraph_size (lookup_index, visited), + lookup->number_of_subtables (), + }); + } + + lookup_sizes.qsort (); + + size_t lookup_list_size = ext_context.graph.vertices_[ext_context.lookup_list_index].table_size (); + size_t l2_l3_size = lookup_list_size; // Lookup List + Lookups + size_t l3_l4_size = 0; // Lookups + SubTables + size_t l4_plus_size = 0; // SubTables + their descendants + + // Start by assuming all lookups are using extension subtables, this size will be removed later + // if it's decided to not make a lookup extension. + for (auto p : lookup_sizes) + { + unsigned subtables_size = p.num_subtables * 8; + l3_l4_size += subtables_size; + l4_plus_size += subtables_size; + } + + bool layers_full = false; + for (auto p : lookup_sizes) + { + const graph::Lookup* lookup = ext_context.lookups.get(p.lookup_index); + if (lookup->is_extension (ext_context.table_tag)) + // already an extension so size is counted by the loop above. + continue; + + if (!layers_full) + { + size_t lookup_size = ext_context.graph.vertices_[p.lookup_index].table_size (); + hb_set_t visited; + size_t subtables_size = ext_context.graph.find_subgraph_size (p.lookup_index, visited, 1) - lookup_size; + size_t remaining_size = p.size - subtables_size - lookup_size; + + l2_l3_size += lookup_size; + l3_l4_size += lookup_size + subtables_size; + l3_l4_size -= p.num_subtables * 8; + l4_plus_size += subtables_size + remaining_size; + + if (l2_l3_size < (1 << 16) + && l3_l4_size < (1 << 16) + && l4_plus_size < (1 << 16)) continue; // this lookup fits within all layers groups + + layers_full = true; + } + + if (!ext_context.lookups.get(p.lookup_index)->make_extension (ext_context, p.lookup_index)) + return false; + } + + return true; +} + +static inline +bool _try_isolating_subgraphs (const hb_vector_t& overflows, + graph_t& sorted_graph) +{ + unsigned space = 0; + hb_set_t roots_to_isolate; + + for (int i = overflows.length - 1; i >= 0; i--) + { + const graph::overflow_record_t& r = overflows[i]; + + unsigned root; + unsigned overflow_space = sorted_graph.space_for (r.parent, &root); + if (!overflow_space) continue; + if (sorted_graph.num_roots_for_space (overflow_space) <= 1) continue; + + if (!space) { + space = overflow_space; + } + + if (space == overflow_space) + roots_to_isolate.add(root); + } + + if (!roots_to_isolate) return false; + + unsigned maximum_to_move = hb_max ((sorted_graph.num_roots_for_space (space) / 2u), 1u); + if (roots_to_isolate.get_population () > maximum_to_move) { + // Only move at most half of the roots in a space at a time. + unsigned extra = roots_to_isolate.get_population () - maximum_to_move; + while (extra--) { + unsigned root = HB_SET_VALUE_INVALID; + roots_to_isolate.previous (&root); + roots_to_isolate.del (root); + } + } + + DEBUG_MSG (SUBSET_REPACK, nullptr, + "Overflow in space %d (%d roots). Moving %d roots to space %d.", + space, + sorted_graph.num_roots_for_space (space), + roots_to_isolate.get_population (), + sorted_graph.next_space ()); + + sorted_graph.isolate_subgraph (roots_to_isolate); + sorted_graph.move_to_new_space (roots_to_isolate); + + return true; +} + +static inline +bool _process_overflows (const hb_vector_t& overflows, + hb_set_t& priority_bumped_parents, + graph_t& sorted_graph) +{ + bool resolution_attempted = false; + + // Try resolving the furthest overflows first. + for (int i = overflows.length - 1; i >= 0; i--) + { + const graph::overflow_record_t& r = overflows[i]; + const auto& child = sorted_graph.vertices_[r.child]; + if (child.is_shared ()) + { + // The child object is shared, we may be able to eliminate the overflow + // by duplicating it. + if (sorted_graph.duplicate (r.parent, r.child) == (unsigned) -1) continue; + return true; + } + + if (child.is_leaf () && !priority_bumped_parents.has (r.parent)) + { + // This object is too far from it's parent, attempt to move it closer. + // + // TODO(garretrieger): initially limiting this to leaf's since they can be + // moved closer with fewer consequences. However, this can + // likely can be used for non-leafs as well. + // TODO(garretrieger): also try lowering priority of the parent. Make it + // get placed further up in the ordering, closer to it's children. + // this is probably preferable if the total size of the parent object + // is < then the total size of the children (and the parent can be moved). + // Since in that case moving the parent will cause a smaller increase in + // the length of other offsets. + if (sorted_graph.raise_childrens_priority (r.parent)) { + priority_bumped_parents.add (r.parent); + resolution_attempted = true; + } + continue; + } + + // TODO(garretrieger): add additional offset resolution strategies + // - Promotion to extension lookups. + // - Table splitting. + } + + return resolution_attempted; +} + +inline bool +hb_resolve_graph_overflows (hb_tag_t table_tag, + unsigned max_rounds , + bool recalculate_extensions, + graph_t& sorted_graph /* IN/OUT */) +{ + sorted_graph.sort_shortest_distance (); + + bool will_overflow = graph::will_overflow (sorted_graph); + if (!will_overflow) + return true; + + graph::gsubgpos_graph_context_t ext_context (table_tag, sorted_graph); + if ((table_tag == HB_OT_TAG_GPOS + || table_tag == HB_OT_TAG_GSUB) + && will_overflow) + { + if (recalculate_extensions) + { + DEBUG_MSG (SUBSET_REPACK, nullptr, "Splitting subtables if needed."); + if (!_presplit_subtables_if_needed (ext_context)) { + DEBUG_MSG (SUBSET_REPACK, nullptr, "Subtable splitting failed."); + return false; + } + + DEBUG_MSG (SUBSET_REPACK, nullptr, "Promoting lookups to extensions if needed."); + if (!_promote_extensions_if_needed (ext_context)) { + DEBUG_MSG (SUBSET_REPACK, nullptr, "Extensions promotion failed."); + return false; + } + } + + DEBUG_MSG (SUBSET_REPACK, nullptr, "Assigning spaces to 32 bit subgraphs."); + if (sorted_graph.assign_spaces ()) + sorted_graph.sort_shortest_distance (); + else + sorted_graph.sort_shortest_distance_if_needed (); + } + + unsigned round = 0; + hb_vector_t overflows; + // TODO(garretrieger): select a good limit for max rounds. + while (!sorted_graph.in_error () + && graph::will_overflow (sorted_graph, &overflows) + && round < max_rounds) { + DEBUG_MSG (SUBSET_REPACK, nullptr, "=== Overflow resolution round %d ===", round); + print_overflows (sorted_graph, overflows); + + hb_set_t priority_bumped_parents; + + if (!_try_isolating_subgraphs (overflows, sorted_graph)) + { + // Don't count space isolation towards round limit. Only increment + // round counter if space isolation made no changes. + round++; + if (!_process_overflows (overflows, priority_bumped_parents, sorted_graph)) + { + DEBUG_MSG (SUBSET_REPACK, nullptr, "No resolution available :("); + break; + } + } + + sorted_graph.sort_shortest_distance (); + } + + if (sorted_graph.in_error ()) + { + DEBUG_MSG (SUBSET_REPACK, nullptr, "Sorted graph in error state."); + return false; + } + + if (graph::will_overflow (sorted_graph)) + { + DEBUG_MSG (SUBSET_REPACK, nullptr, "Offset overflow resolution failed."); + return false; + } + + return true; +} + +/* + * Attempts to modify the topological sorting of the provided object graph to + * eliminate offset overflows in the links between objects of the graph. If a + * non-overflowing ordering is found the updated graph is serialized it into the + * provided serialization context. + * + * If necessary the structure of the graph may be modified in ways that do not + * affect the functionality of the graph. For example shared objects may be + * duplicated. + * + * For a detailed writeup describing how the algorithm operates see: + * docs/repacker.md + */ +template +inline hb_blob_t* +hb_resolve_overflows (const T& packed, + hb_tag_t table_tag, + unsigned max_rounds = 20, + bool recalculate_extensions = false) { + graph_t sorted_graph (packed); + if (!hb_resolve_graph_overflows (table_tag, max_rounds, recalculate_extensions, sorted_graph)) + return nullptr; + + return graph::serialize (sorted_graph); +} + +#endif /* HB_REPACKER_HH */ diff --git a/libs/harfbuzz/src/hb-sanitize.hh b/libs/harfbuzz/src/hb-sanitize.hh new file mode 100644 index 00000000..65c27722 --- /dev/null +++ b/libs/harfbuzz/src/hb-sanitize.hh @@ -0,0 +1,427 @@ +/* + * Copyright © 2007,2008,2009,2010 Red Hat, Inc. + * Copyright © 2012,2018 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. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_SANITIZE_HH +#define HB_SANITIZE_HH + +#include "hb.hh" +#include "hb-blob.hh" +#include "hb-dispatch.hh" + + +/* + * Sanitize + * + * + * === Introduction === + * + * The sanitize machinery is at the core of our zero-cost font loading. We + * mmap() font file into memory and create a blob out of it. Font subtables + * are returned as a readonly sub-blob of the main font blob. These table + * blobs are then sanitized before use, to ensure invalid memory access does + * not happen. The toplevel sanitize API use is like, eg. to load the 'head' + * table: + * + * hb_blob_t *head_blob = hb_sanitize_context_t ().reference_table (face); + * + * The blob then can be converted to a head table struct with: + * + * const head *head_table = head_blob->as (); + * + * What the reference_table does is, to call hb_face_reference_table() to load + * the table blob, sanitize it and return either the sanitized blob, or empty + * blob if sanitization failed. The blob->as() function returns the null + * object of its template type argument if the blob is empty. Otherwise, it + * just casts the blob contents to the desired type. + * + * Sanitizing a blob of data with a type T works as follows (with minor + * simplification): + * + * - Cast blob content to T*, call sanitize() method of it, + * - If sanitize succeeded, return blob. + * - Otherwise, if blob is not writable, try making it writable, + * or copy if cannot be made writable in-place, + * - Call sanitize() again. Return blob if sanitize succeeded. + * - Return empty blob otherwise. + * + * + * === The sanitize() contract === + * + * The sanitize() method of each object type shall return true if it's safe to + * call other methods of the object, and %false otherwise. + * + * Note that what sanitize() checks for might align with what the specification + * describes as valid table data, but does not have to be. In particular, we + * do NOT want to be pedantic and concern ourselves with validity checks that + * are irrelevant to our use of the table. On the contrary, we want to be + * lenient with error handling and accept invalid data to the extent that it + * does not impose extra burden on us. + * + * Based on the sanitize contract, one can see that what we check for depends + * on how we use the data in other table methods. Ie. if other table methods + * assume that offsets do NOT point out of the table data block, then that's + * something sanitize() must check for (GSUB/GPOS/GDEF/etc work this way). On + * the other hand, if other methods do such checks themselves, then sanitize() + * does not have to bother with them (glyf/local work this way). The choice + * depends on the table structure and sanitize() performance. For example, to + * check glyf/loca offsets in sanitize() would cost O(num-glyphs). We try hard + * to avoid such costs during font loading. By postponing such checks to the + * actual glyph loading, we reduce the sanitize cost to O(1) and total runtime + * cost to O(used-glyphs). As such, this is preferred. + * + * The same argument can be made re GSUB/GPOS/GDEF, but there, the table + * structure is so complicated that by checking all offsets at sanitize() time, + * we make the code much simpler in other methods, as offsets and referenced + * objects do not need to be validated at each use site. + */ + +/* This limits sanitizing time on really broken fonts. */ +#ifndef HB_SANITIZE_MAX_EDITS +#define HB_SANITIZE_MAX_EDITS 32 +#endif +#ifndef HB_SANITIZE_MAX_OPS_FACTOR +#define HB_SANITIZE_MAX_OPS_FACTOR 64 +#endif +#ifndef HB_SANITIZE_MAX_OPS_MIN +#define HB_SANITIZE_MAX_OPS_MIN 16384 +#endif +#ifndef HB_SANITIZE_MAX_OPS_MAX +#define HB_SANITIZE_MAX_OPS_MAX 0x3FFFFFFF +#endif +#ifndef HB_SANITIZE_MAX_SUBTABLES +#define HB_SANITIZE_MAX_SUBTABLES 0x4000 +#endif + +struct hb_sanitize_context_t : + hb_dispatch_context_t +{ + hb_sanitize_context_t () : + start (nullptr), end (nullptr), + max_ops (0), max_subtables (0), + recursion_depth (0), + writable (false), edit_count (0), + blob (nullptr), + num_glyphs (65536), + num_glyphs_set (false) {} + + const char *get_name () { return "SANITIZE"; } + template + bool may_dispatch (const T *obj HB_UNUSED, const F *format) + { return format->sanitize (this); } + static return_t default_return_value () { return true; } + static return_t no_dispatch_return_value () { return false; } + bool stop_sublookup_iteration (const return_t r) const { return !r; } + + bool visit_subtables (unsigned count) + { + max_subtables += count; + return max_subtables < HB_SANITIZE_MAX_SUBTABLES; + } + + private: + template auto + _dispatch (const T &obj, hb_priority<1>, Ts&&... ds) HB_AUTO_RETURN + ( obj.sanitize (this, std::forward (ds)...) ) + template auto + _dispatch (const T &obj, hb_priority<0>, Ts&&... ds) HB_AUTO_RETURN + ( obj.dispatch (this, std::forward (ds)...) ) + public: + template auto + dispatch (const T &obj, Ts&&... ds) HB_AUTO_RETURN + ( _dispatch (obj, hb_prioritize, std::forward (ds)...) ) + + + void init (hb_blob_t *b) + { + this->blob = hb_blob_reference (b); + this->writable = false; + } + + void set_num_glyphs (unsigned int num_glyphs_) + { + num_glyphs = num_glyphs_; + num_glyphs_set = true; + } + unsigned int get_num_glyphs () { return num_glyphs; } + + void set_max_ops (int max_ops_) { max_ops = max_ops_; } + + template + void set_object (const T *obj) + { + reset_object (); + + if (!obj) return; + + const char *obj_start = (const char *) obj; + if (unlikely (obj_start < this->start || this->end <= obj_start)) + this->start = this->end = nullptr; + else + { + this->start = obj_start; + this->end = obj_start + hb_min (size_t (this->end - obj_start), obj->get_size ()); + } + } + + void reset_object () + { + this->start = this->blob->data; + this->end = this->start + this->blob->length; + assert (this->start <= this->end); /* Must not overflow. */ + } + + void start_processing () + { + reset_object (); + if (unlikely (hb_unsigned_mul_overflows (this->end - this->start, HB_SANITIZE_MAX_OPS_FACTOR))) + this->max_ops = HB_SANITIZE_MAX_OPS_MAX; + else + this->max_ops = hb_clamp ((unsigned) (this->end - this->start) * HB_SANITIZE_MAX_OPS_FACTOR, + (unsigned) HB_SANITIZE_MAX_OPS_MIN, + (unsigned) HB_SANITIZE_MAX_OPS_MAX); + this->edit_count = 0; + this->debug_depth = 0; + this->recursion_depth = 0; + + DEBUG_MSG_LEVEL (SANITIZE, start, 0, +1, + "start [%p..%p] (%lu bytes)", + this->start, this->end, + (unsigned long) (this->end - this->start)); + } + + void end_processing () + { + DEBUG_MSG_LEVEL (SANITIZE, this->start, 0, -1, + "end [%p..%p] %u edit requests", + this->start, this->end, this->edit_count); + + hb_blob_destroy (this->blob); + this->blob = nullptr; + this->start = this->end = nullptr; + } + + unsigned get_edit_count () { return edit_count; } + + bool check_range (const void *base, + unsigned int len) const + { + const char *p = (const char *) base; + bool ok = !len || + (this->start <= p && + p <= this->end && + (unsigned int) (this->end - p) >= len && + (this->max_ops -= len) > 0); + + DEBUG_MSG_LEVEL (SANITIZE, p, this->debug_depth+1, 0, + "check_range [%p..%p]" + " (%d bytes) in [%p..%p] -> %s", + p, p + len, len, + this->start, this->end, + ok ? "OK" : "OUT-OF-RANGE"); + + return likely (ok); + } + + template + bool check_range (const T *base, + unsigned int a, + unsigned int b) const + { + return !hb_unsigned_mul_overflows (a, b) && + this->check_range (base, a * b); + } + + template + bool check_range (const T *base, + unsigned int a, + unsigned int b, + unsigned int c) const + { + return !hb_unsigned_mul_overflows (a, b) && + this->check_range (base, a * b, c); + } + + template + bool check_array (const T *base, unsigned int len) const + { + return this->check_range (base, len, hb_static_size (T)); + } + + template + bool check_array (const T *base, + unsigned int a, + unsigned int b) const + { + return this->check_range (base, a, b, hb_static_size (T)); + } + + bool check_start_recursion (int max_depth) + { + if (unlikely (recursion_depth >= max_depth)) return false; + return ++recursion_depth; + } + + bool end_recursion (bool result) + { + recursion_depth--; + return result; + } + + template + bool check_struct (const Type *obj) const + { return likely (this->check_range (obj, obj->min_size)); } + + bool may_edit (const void *base, unsigned int len) + { + if (this->edit_count >= HB_SANITIZE_MAX_EDITS) + return false; + + const char *p = (const char *) base; + this->edit_count++; + + DEBUG_MSG_LEVEL (SANITIZE, p, this->debug_depth+1, 0, + "may_edit(%u) [%p..%p] (%d bytes) in [%p..%p] -> %s", + this->edit_count, + p, p + len, len, + this->start, this->end, + this->writable ? "GRANTED" : "DENIED"); + + return this->writable; + } + + template + bool try_set (const Type *obj, const ValueType &v) + { + if (this->may_edit (obj, hb_static_size (Type))) + { + * const_cast (obj) = v; + return true; + } + return false; + } + + template + hb_blob_t *sanitize_blob (hb_blob_t *blob) + { + bool sane; + + init (blob); + + retry: + DEBUG_MSG_FUNC (SANITIZE, start, "start"); + + start_processing (); + + if (unlikely (!start)) + { + end_processing (); + return blob; + } + + Type *t = reinterpret_cast (const_cast (start)); + + sane = t->sanitize (this); + if (sane) + { + if (edit_count) + { + DEBUG_MSG_FUNC (SANITIZE, start, "passed first round with %d edits; going for second round", edit_count); + + /* sanitize again to ensure no toe-stepping */ + edit_count = 0; + sane = t->sanitize (this); + if (edit_count) { + DEBUG_MSG_FUNC (SANITIZE, start, "requested %d edits in second round; FAILLING", edit_count); + sane = false; + } + } + } + else + { + if (edit_count && !writable) { + start = hb_blob_get_data_writable (blob, nullptr); + end = start + blob->length; + + if (start) + { + writable = true; + /* ok, we made it writable by relocating. try again */ + DEBUG_MSG_FUNC (SANITIZE, start, "retry"); + goto retry; + } + } + } + + end_processing (); + + DEBUG_MSG_FUNC (SANITIZE, start, sane ? "PASSED" : "FAILED"); + if (sane) + { + hb_blob_make_immutable (blob); + return blob; + } + else + { + hb_blob_destroy (blob); + return hb_blob_get_empty (); + } + } + + template + hb_blob_t *reference_table (const hb_face_t *face, hb_tag_t tableTag = Type::tableTag) + { + if (!num_glyphs_set) + set_num_glyphs (hb_face_get_glyph_count (face)); + return sanitize_blob (hb_face_reference_table (face, tableTag)); + } + + const char *start, *end; + mutable int max_ops, max_subtables; + private: + int recursion_depth; + bool writable; + unsigned int edit_count; + hb_blob_t *blob; + unsigned int num_glyphs; + bool num_glyphs_set; +}; + +struct hb_sanitize_with_object_t +{ + template + hb_sanitize_with_object_t (hb_sanitize_context_t *c, const T& obj) : c (c) + { c->set_object (obj); } + ~hb_sanitize_with_object_t () + { c->reset_object (); } + + private: + hb_sanitize_context_t *c; +}; + + +#endif /* HB_SANITIZE_HH */ diff --git a/libs/harfbuzz/src/hb-serialize.hh b/libs/harfbuzz/src/hb-serialize.hh new file mode 100644 index 00000000..f47cde5e --- /dev/null +++ b/libs/harfbuzz/src/hb-serialize.hh @@ -0,0 +1,733 @@ +/* + * Copyright © 2007,2008,2009,2010 Red Hat, Inc. + * Copyright © 2012,2018 Google, Inc. + * Copyright © 2019 Facebook, 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. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + * Facebook Author(s): Behdad Esfahbod + */ + +#ifndef HB_SERIALIZE_HH +#define HB_SERIALIZE_HH + +#include "hb.hh" +#include "hb-blob.hh" +#include "hb-map.hh" +#include "hb-pool.hh" + +#ifdef HB_EXPERIMENTAL_API +#include "hb-subset-repacker.h" +#endif + +/* + * Serialize + */ + +enum hb_serialize_error_t { + HB_SERIALIZE_ERROR_NONE = 0x00000000u, + HB_SERIALIZE_ERROR_OTHER = 0x00000001u, + HB_SERIALIZE_ERROR_OFFSET_OVERFLOW = 0x00000002u, + HB_SERIALIZE_ERROR_OUT_OF_ROOM = 0x00000004u, + HB_SERIALIZE_ERROR_INT_OVERFLOW = 0x00000008u, + HB_SERIALIZE_ERROR_ARRAY_OVERFLOW = 0x00000010u +}; +HB_MARK_AS_FLAG_T (hb_serialize_error_t); + +struct hb_serialize_context_t +{ + typedef unsigned objidx_t; + + enum whence_t { + Head, /* Relative to the current object head (default). */ + Tail, /* Relative to the current object tail after packed. */ + Absolute /* Absolute: from the start of the serialize buffer. */ + }; + + + + struct object_t + { + void fini () { + real_links.fini (); + virtual_links.fini (); + } + + object_t () = default; + +#ifdef HB_EXPERIMENTAL_API + object_t (const hb_object_t &o) + { + head = o.head; + tail = o.tail; + next = nullptr; + real_links.alloc (o.num_real_links); + for (unsigned i = 0 ; i < o.num_real_links; i++) + real_links.push (o.real_links[i]); + + virtual_links.alloc (o.num_virtual_links); + for (unsigned i = 0; i < o.num_virtual_links; i++) + virtual_links.push (o.virtual_links[i]); + } +#endif + + friend void swap (object_t& a, object_t& b) + { + hb_swap (a.head, b.head); + hb_swap (a.tail, b.tail); + hb_swap (a.next, b.next); + hb_swap (a.real_links, b.real_links); + hb_swap (a.virtual_links, b.virtual_links); + } + + bool operator == (const object_t &o) const + { + // Virtual links aren't considered for equality since they don't affect the functionality + // of the object. + return (tail - head == o.tail - o.head) + && (real_links.length == o.real_links.length) + && 0 == hb_memcmp (head, o.head, tail - head) + && real_links.as_bytes () == o.real_links.as_bytes (); + } + uint32_t hash () const + { + // Virtual links aren't considered for equality since they don't affect the functionality + // of the object. + return hb_bytes_t (head, tail - head).hash () ^ + real_links.as_bytes ().hash (); + } + + struct link_t + { + unsigned width: 3; + unsigned is_signed: 1; + unsigned whence: 2; + unsigned bias : 26; + unsigned position; + objidx_t objidx; + + link_t () = default; + +#ifdef HB_EXPERIMENTAL_API + link_t (const hb_link_t &o) + { + width = o.width; + is_signed = 0; + whence = 0; + position = o.position; + bias = 0; + objidx = o.objidx; + } +#endif + + HB_INTERNAL static int cmp (const void* a, const void* b) + { + int cmp = ((const link_t*)a)->position - ((const link_t*)b)->position; + if (cmp) return cmp; + + return ((const link_t*)a)->objidx - ((const link_t*)b)->objidx; + } + }; + + char *head; + char *tail; + hb_vector_t real_links; + hb_vector_t virtual_links; + object_t *next; + + auto all_links () const HB_AUTO_RETURN + (( hb_concat (this->real_links, this->virtual_links) )); + auto all_links_writer () HB_AUTO_RETURN + (( hb_concat (this->real_links.writer (), this->virtual_links.writer ()) )); + }; + + struct snapshot_t + { + char *head; + char *tail; + object_t *current; // Just for sanity check + unsigned num_real_links; + unsigned num_virtual_links; + hb_serialize_error_t errors; + }; + + snapshot_t snapshot () + { return snapshot_t { + head, tail, current, current->real_links.length, current->virtual_links.length, errors }; } + + hb_serialize_context_t (void *start_, unsigned int size) : + start ((char *) start_), + end (start + size), + current (nullptr) + { reset (); } + ~hb_serialize_context_t () { fini (); } + + void fini () + { + for (object_t *_ : ++hb_iter (packed)) _->fini (); + packed.fini (); + this->packed_map.fini (); + + while (current) + { + auto *_ = current; + current = current->next; + _->fini (); + } + object_pool.fini (); + } + + bool in_error () const { return bool (errors); } + + bool successful () const { return !bool (errors); } + + HB_NODISCARD bool ran_out_of_room () const { return errors & HB_SERIALIZE_ERROR_OUT_OF_ROOM; } + HB_NODISCARD bool offset_overflow () const { return errors & HB_SERIALIZE_ERROR_OFFSET_OVERFLOW; } + HB_NODISCARD bool only_offset_overflow () const { return errors == HB_SERIALIZE_ERROR_OFFSET_OVERFLOW; } + HB_NODISCARD bool only_overflow () const + { + return errors == HB_SERIALIZE_ERROR_OFFSET_OVERFLOW + || errors == HB_SERIALIZE_ERROR_INT_OVERFLOW + || errors == HB_SERIALIZE_ERROR_ARRAY_OVERFLOW; + } + + void reset (void *start_, unsigned int size) + { + start = (char*) start_; + end = start + size; + reset (); + current = nullptr; + } + + void reset () + { + this->errors = HB_SERIALIZE_ERROR_NONE; + this->head = this->start; + this->tail = this->end; + this->debug_depth = 0; + + fini (); + this->packed.push (nullptr); + this->packed_map.init (); + } + + bool check_success (bool success, + hb_serialize_error_t err_type = HB_SERIALIZE_ERROR_OTHER) + { + return successful () + && (success || err (err_type)); + } + + template + bool check_equal (T1 &&v1, T2 &&v2, hb_serialize_error_t err_type) + { + if ((long long) v1 != (long long) v2) + { + return err (err_type); + } + return true; + } + + template + bool check_assign (T1 &v1, T2 &&v2, hb_serialize_error_t err_type) + { return check_equal (v1 = v2, v2, err_type); } + + template bool propagate_error (T &&obj) + { return check_success (!hb_deref (obj).in_error ()); } + + template bool propagate_error (T1 &&o1, Ts&&... os) + { return propagate_error (std::forward (o1)) && + propagate_error (std::forward (os)...); } + + /* To be called around main operation. */ + template + Type *start_serialize () + { + DEBUG_MSG_LEVEL (SERIALIZE, this->start, 0, +1, + "start [%p..%p] (%lu bytes)", + this->start, this->end, + (unsigned long) (this->end - this->start)); + + assert (!current); + return push (); + } + void end_serialize () + { + DEBUG_MSG_LEVEL (SERIALIZE, this->start, 0, -1, + "end [%p..%p] serialized %u bytes; %s", + this->start, this->end, + (unsigned) (this->head - this->start), + successful () ? "successful" : "UNSUCCESSFUL"); + + propagate_error (packed, packed_map); + + if (unlikely (!current)) return; + if (unlikely (in_error())) + { + // Offset overflows that occur before link resolution cannot be handled + // by repacking, so set a more general error. + if (offset_overflow ()) err (HB_SERIALIZE_ERROR_OTHER); + return; + } + + assert (!current->next); + + /* Only "pack" if there exist other objects... Otherwise, don't bother. + * Saves a move. */ + if (packed.length <= 1) + return; + + pop_pack (false); + + resolve_links (); + } + + template + Type *push () + { + if (unlikely (in_error ())) return start_embed (); + + object_t *obj = object_pool.alloc (); + if (unlikely (!obj)) + check_success (false); + else + { + obj->head = head; + obj->tail = tail; + obj->next = current; + current = obj; + } + return start_embed (); + } + void pop_discard () + { + object_t *obj = current; + if (unlikely (!obj)) return; + if (unlikely (in_error() && !only_overflow ())) return; + + current = current->next; + revert (obj->head, obj->tail); + obj->fini (); + object_pool.release (obj); + } + + /* Set share to false when an object is unlikely shareable with others + * so not worth an attempt, or a contiguous table is serialized as + * multiple consecutive objects in the reverse order so can't be shared. + */ + objidx_t pop_pack (bool share=true) + { + object_t *obj = current; + if (unlikely (!obj)) return 0; + if (unlikely (in_error())) return 0; + + current = current->next; + obj->tail = head; + obj->next = nullptr; + unsigned len = obj->tail - obj->head; + head = obj->head; /* Rewind head. */ + + if (!len) + { + assert (!obj->real_links.length); + assert (!obj->virtual_links.length); + return 0; + } + + objidx_t objidx; + if (share) + { + objidx = packed_map.get (obj); + if (objidx) + { + merge_virtual_links (obj, objidx); + obj->fini (); + return objidx; + } + } + + tail -= len; + memmove (tail, obj->head, len); + + obj->head = tail; + obj->tail = tail + len; + + packed.push (obj); + + if (unlikely (!propagate_error (packed))) + { + /* Obj wasn't successfully added to packed, so clean it up otherwise its + * links will be leaked. When we use constructor/destructors properly, we + * can remove these. */ + obj->fini (); + return 0; + } + + objidx = packed.length - 1; + + if (share) packed_map.set (obj, objidx); + propagate_error (packed_map); + + return objidx; + } + + void revert (snapshot_t snap) + { + // Overflows that happened after the snapshot will be erased by the revert. + if (unlikely (in_error () && !only_overflow ())) return; + assert (snap.current == current); + current->real_links.shrink (snap.num_real_links); + current->virtual_links.shrink (snap.num_virtual_links); + errors = snap.errors; + revert (snap.head, snap.tail); + } + + void revert (char *snap_head, + char *snap_tail) + { + if (unlikely (in_error ())) return; + assert (snap_head <= head); + assert (tail <= snap_tail); + head = snap_head; + tail = snap_tail; + discard_stale_objects (); + } + + void discard_stale_objects () + { + if (unlikely (in_error ())) return; + while (packed.length > 1 && + packed.tail ()->head < tail) + { + packed_map.del (packed.tail ()); + assert (!packed.tail ()->next); + packed.tail ()->fini (); + packed.pop (); + } + if (packed.length > 1) + assert (packed.tail ()->head == tail); + } + + // Adds a virtual link from the current object to objidx. A virtual link is not associated with + // an actual offset field. They are solely used to enforce ordering constraints between objects. + // Adding a virtual link from object a to object b will ensure that object b is always packed after + // object a in the final serialized order. + // + // This is useful in certain situations where there needs to be a specific ordering in the + // final serialization. Such as when platform bugs require certain orderings, or to provide + // guidance to the repacker for better offset overflow resolution. + void add_virtual_link (objidx_t objidx) + { + if (unlikely (in_error ())) return; + + if (!objidx) + return; + + assert (current); + + auto& link = *current->virtual_links.push (); + if (current->virtual_links.in_error ()) + err (HB_SERIALIZE_ERROR_OTHER); + + link.width = 0; + link.objidx = objidx; + link.is_signed = 0; + link.whence = 0; + link.position = 0; + link.bias = 0; + } + + template + void add_link (T &ofs, objidx_t objidx, + whence_t whence = Head, + unsigned bias = 0) + { + if (unlikely (in_error ())) return; + + if (!objidx) + return; + + assert (current); + assert (current->head <= (const char *) &ofs); + + auto& link = *current->real_links.push (); + if (current->real_links.in_error ()) + err (HB_SERIALIZE_ERROR_OTHER); + + link.width = sizeof (T); + link.objidx = objidx; + if (unlikely (!sizeof (T))) + { + // This link is not associated with an actual offset and exists merely to enforce + // an ordering constraint. + link.is_signed = 0; + link.whence = 0; + link.position = 0; + link.bias = 0; + return; + } + + link.is_signed = std::is_signed::value; + link.whence = (unsigned) whence; + link.position = (const char *) &ofs - current->head; + link.bias = bias; + } + + unsigned to_bias (const void *base) const + { + if (unlikely (in_error ())) return 0; + if (!base) return 0; + assert (current); + assert (current->head <= (const char *) base); + return (const char *) base - current->head; + } + + void resolve_links () + { + if (unlikely (in_error ())) return; + + assert (!current); + assert (packed.length > 1); + + for (const object_t* parent : ++hb_iter (packed)) + for (const object_t::link_t &link : parent->real_links) + { + const object_t* child = packed[link.objidx]; + if (unlikely (!child)) { err (HB_SERIALIZE_ERROR_OTHER); return; } + unsigned offset = 0; + switch ((whence_t) link.whence) { + case Head: offset = child->head - parent->head; break; + case Tail: offset = child->head - parent->tail; break; + case Absolute: offset = (head - start) + (child->head - tail); break; + } + + assert (offset >= link.bias); + offset -= link.bias; + if (link.is_signed) + { + assert (link.width == 2 || link.width == 4); + if (link.width == 4) + assign_offset (parent, link, offset); + else + assign_offset (parent, link, offset); + } + else + { + assert (link.width == 2 || link.width == 3 || link.width == 4); + if (link.width == 4) + assign_offset (parent, link, offset); + else if (link.width == 3) + assign_offset (parent, link, offset); + else + assign_offset (parent, link, offset); + } + } + } + + unsigned int length () const + { + if (unlikely (!current)) return 0; + return this->head - current->head; + } + + void align (unsigned int alignment) + { + unsigned int l = length () % alignment; + if (l) + allocate_size (alignment - l); + } + + template + Type *start_embed (const Type *obj HB_UNUSED = nullptr) const + { return reinterpret_cast (this->head); } + template + Type *start_embed (const Type &obj) const + { return start_embed (std::addressof (obj)); } + + bool err (hb_serialize_error_t err_type) + { + return !bool ((errors = (errors | err_type))); + } + + template + Type *allocate_size (size_t size) + { + if (unlikely (in_error ())) return nullptr; + + if (unlikely (size > INT_MAX || this->tail - this->head < ptrdiff_t (size))) + { + err (HB_SERIALIZE_ERROR_OUT_OF_ROOM); + return nullptr; + } + hb_memset (this->head, 0, size); + char *ret = this->head; + this->head += size; + return reinterpret_cast (ret); + } + + template + Type *allocate_min () + { return this->allocate_size (Type::min_size); } + + template + Type *embed (const Type *obj) + { + unsigned int size = obj->get_size (); + Type *ret = this->allocate_size (size); + if (unlikely (!ret)) return nullptr; + memcpy (ret, obj, size); + return ret; + } + template + Type *embed (const Type &obj) + { return embed (std::addressof (obj)); } + + template auto + _copy (const Type &src, hb_priority<1>, Ts&&... ds) HB_RETURN + (Type *, src.copy (this, std::forward (ds)...)) + + template auto + _copy (const Type &src, hb_priority<0>) -> decltype (&(hb_declval () = src)) + { + Type *ret = this->allocate_size (sizeof (Type)); + if (unlikely (!ret)) return nullptr; + *ret = src; + return ret; + } + + /* Like embed, but active: calls obj.operator=() or obj.copy() to transfer data + * instead of memcpy(). */ + template + Type *copy (const Type &src, Ts&&... ds) + { return _copy (src, hb_prioritize, std::forward (ds)...); } + template + Type *copy (const Type *src, Ts&&... ds) + { return copy (*src, std::forward (ds)...); } + + template + void copy_all (Iterator it, Ts&&... ds) + { for (decltype (*it) _ : it) copy (_, std::forward (ds)...); } + + template + hb_serialize_context_t& operator << (const Type &obj) & { embed (obj); return *this; } + + template + Type *extend_size (Type *obj, size_t size) + { + if (unlikely (in_error ())) return nullptr; + + assert (this->start <= (char *) obj); + assert ((char *) obj <= this->head); + assert ((size_t) (this->head - (char *) obj) <= size); + if (unlikely (((char *) obj + size < (char *) obj) || + !this->allocate_size (((char *) obj) + size - this->head))) return nullptr; + return reinterpret_cast (obj); + } + template + Type *extend_size (Type &obj, size_t size) + { return extend_size (std::addressof (obj), size); } + + template + Type *extend_min (Type *obj) { return extend_size (obj, obj->min_size); } + template + Type *extend_min (Type &obj) { return extend_min (std::addressof (obj)); } + + template + Type *extend (Type *obj, Ts&&... ds) + { return extend_size (obj, obj->get_size (std::forward (ds)...)); } + template + Type *extend (Type &obj, Ts&&... ds) + { return extend (std::addressof (obj), std::forward (ds)...); } + + /* Output routines. */ + hb_bytes_t copy_bytes () const + { + assert (successful ()); + /* Copy both items from head side and tail side... */ + unsigned int len = (this->head - this->start) + + (this->end - this->tail); + + // If len is zero don't hb_malloc as the memory won't get properly + // cleaned up later. + if (!len) return hb_bytes_t (); + + char *p = (char *) hb_malloc (len); + if (unlikely (!p)) return hb_bytes_t (); + + memcpy (p, this->start, this->head - this->start); + memcpy (p + (this->head - this->start), this->tail, this->end - this->tail); + return hb_bytes_t (p, len); + } + template + Type *copy () const + { return reinterpret_cast ((char *) copy_bytes ().arrayZ); } + hb_blob_t *copy_blob () const + { + hb_bytes_t b = copy_bytes (); + return hb_blob_create (b.arrayZ, b.length, + HB_MEMORY_MODE_WRITABLE, + (char *) b.arrayZ, hb_free); + } + + const hb_vector_t& object_graph() const + { return packed; } + + private: + template + void assign_offset (const object_t* parent, const object_t::link_t &link, unsigned offset) + { + auto &off = * ((BEInt *) (parent->head + link.position)); + assert (0 == off); + check_assign (off, offset, HB_SERIALIZE_ERROR_OFFSET_OVERFLOW); + } + + public: + char *start, *head, *tail, *end; + unsigned int debug_depth; + hb_serialize_error_t errors; + + private: + + void merge_virtual_links (const object_t* from, objidx_t to_idx) { + object_t* to = packed[to_idx]; + for (const auto& l : from->virtual_links) { + to->virtual_links.push (l); + } + } + + /* Object memory pool. */ + hb_pool_t object_pool; + + /* Stack of currently under construction objects. */ + object_t *current; + + /* Stack of packed objects. Object 0 is always nil object. */ + hb_vector_t packed; + + /* Map view of packed objects. */ + hb_hashmap_t packed_map; +}; + +#endif /* HB_SERIALIZE_HH */ diff --git a/libs/harfbuzz/src/hb-set-digest.hh b/libs/harfbuzz/src/hb-set-digest.hh new file mode 100644 index 00000000..fab36216 --- /dev/null +++ b/libs/harfbuzz/src/hb-set-digest.hh @@ -0,0 +1,196 @@ +/* + * Copyright © 2012 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 HB_SET_DIGEST_HH +#define HB_SET_DIGEST_HH + +#include "hb.hh" + +/* + * The set-digests here implement various "filters" that support + * "approximate member query". Conceptually these are like Bloom + * Filter and Quotient Filter, however, much smaller, faster, and + * designed to fit the requirements of our uses for glyph coverage + * queries. + * + * Our filters are highly accurate if the lookup covers fairly local + * set of glyphs, but fully flooded and ineffective if coverage is + * all over the place. + * + * The way these are used is that the filter is first populated by + * a lookup's or subtable's Coverage table(s), and then when we + * want to apply the lookup or subtable to a glyph, before trying + * to apply, we ask the filter if the glyph may be covered. If it's + * not, we return early. + * + * We use these filters both at the lookup-level, and then again, + * at the subtable-level. Both have performance win. + * + * The main filter we use is a combination of three bits-pattern + * filters. A bits-pattern filter checks a number of bits (5 or 6) + * of the input number (glyph-id in this case) and checks whether + * its pattern is amongst the patterns of any of the accepted values. + * The accepted patterns are represented as a "long" integer. The + * check is done using four bitwise operations only. + */ + +template +struct hb_set_digest_bits_pattern_t +{ + static constexpr unsigned mask_bytes = sizeof (mask_t); + static constexpr unsigned mask_bits = sizeof (mask_t) * 8; + static constexpr unsigned num_bits = 0 + + (mask_bytes >= 1 ? 3 : 0) + + (mask_bytes >= 2 ? 1 : 0) + + (mask_bytes >= 4 ? 1 : 0) + + (mask_bytes >= 8 ? 1 : 0) + + (mask_bytes >= 16? 1 : 0) + + 0; + + static_assert ((shift < sizeof (hb_codepoint_t) * 8), ""); + static_assert ((shift + num_bits <= sizeof (hb_codepoint_t) * 8), ""); + + void init () { mask = 0; } + + void add (hb_codepoint_t g) { mask |= mask_for (g); } + + bool add_range (hb_codepoint_t a, hb_codepoint_t b) + { + if ((b >> shift) - (a >> shift) >= mask_bits - 1) + mask = (mask_t) -1; + else { + mask_t ma = mask_for (a); + mask_t mb = mask_for (b); + mask |= mb + (mb - ma) - (mb < ma); + } + return true; + } + + template + void add_array (const T *array, unsigned int count, unsigned int stride=sizeof(T)) + { + for (unsigned int i = 0; i < count; i++) + { + add (*array); + array = (const T *) (stride + (const char *) array); + } + } + template + void add_array (const hb_array_t& arr) { add_array (&arr, arr.len ()); } + template + bool add_sorted_array (const T *array, unsigned int count, unsigned int stride=sizeof(T)) + { + for (unsigned int i = 0; i < count; i++) + { + add (*array); + array = (const T *) (stride + (const char *) array); + } + return true; + } + template + bool add_sorted_array (const hb_sorted_array_t& arr) { return add_sorted_array (&arr, arr.len ()); } + + bool may_have (hb_codepoint_t g) const + { return mask & mask_for (g); } + + private: + + static mask_t mask_for (hb_codepoint_t g) + { return ((mask_t) 1) << ((g >> shift) & (mask_bits - 1)); } + mask_t mask; +}; + +template +struct hb_set_digest_combiner_t +{ + void init () + { + head.init (); + tail.init (); + } + + void add (hb_codepoint_t g) + { + head.add (g); + tail.add (g); + } + + bool add_range (hb_codepoint_t a, hb_codepoint_t b) + { + head.add_range (a, b); + tail.add_range (a, b); + return true; + } + template + void add_array (const T *array, unsigned int count, unsigned int stride=sizeof(T)) + { + head.add_array (array, count, stride); + tail.add_array (array, count, stride); + } + template + void add_array (const hb_array_t& arr) { add_array (&arr, arr.len ()); } + template + bool add_sorted_array (const T *array, unsigned int count, unsigned int stride=sizeof(T)) + { + head.add_sorted_array (array, count, stride); + tail.add_sorted_array (array, count, stride); + return true; + } + template + bool add_sorted_array (const hb_sorted_array_t& arr) { return add_sorted_array (&arr, arr.len ()); } + + bool may_have (hb_codepoint_t g) const + { + return head.may_have (g) && tail.may_have (g); + } + + private: + head_t head; + tail_t tail; +}; + + +/* + * hb_set_digest_t + * + * This is a combination of digests that performs "best". + * There is not much science to this: it's a result of intuition + * and testing. + */ +using hb_set_digest_t = + hb_set_digest_combiner_t + < + hb_set_digest_bits_pattern_t, + hb_set_digest_combiner_t + < + hb_set_digest_bits_pattern_t, + hb_set_digest_bits_pattern_t + > + > +; + + +#endif /* HB_SET_DIGEST_HH */ diff --git a/libs/harfbuzz/src/hb-set.cc b/libs/harfbuzz/src/hb-set.cc new file mode 100644 index 00000000..0270b219 --- /dev/null +++ b/libs/harfbuzz/src/hb-set.cc @@ -0,0 +1,655 @@ +/* + * Copyright © 2012 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 + */ + +#include "hb-set.hh" + + +/** + * SECTION:hb-set + * @title: hb-set + * @short_description: Objects representing a set of integers + * @include: hb.h + * + * Set objects represent a mathematical set of integer values. They are + * used in non-shaping APIs to query certain sets of characters or glyphs, + * or other integer values. + **/ + + +/** + * hb_set_create: + * + * Creates a new, initially empty set. + * + * Return value: (transfer full): The new #hb_set_t + * + * Since: 0.9.2 + **/ +hb_set_t * +hb_set_create () +{ + hb_set_t *set; + + if (!(set = hb_object_create ())) + return hb_set_get_empty (); + + return set; +} + +/** + * hb_set_get_empty: + * + * Fetches the singleton empty #hb_set_t. + * + * Return value: (transfer full): The empty #hb_set_t + * + * Since: 0.9.2 + **/ +hb_set_t * +hb_set_get_empty () +{ + return const_cast (&Null (hb_set_t)); +} + +/** + * hb_set_reference: (skip) + * @set: A set + * + * Increases the reference count on a set. + * + * Return value: (transfer full): The set + * + * Since: 0.9.2 + **/ +hb_set_t * +hb_set_reference (hb_set_t *set) +{ + return hb_object_reference (set); +} + +/** + * hb_set_destroy: (skip) + * @set: A set + * + * Decreases the reference count on a set. When + * the reference count reaches zero, the set is + * destroyed, freeing all memory. + * + * Since: 0.9.2 + **/ +void +hb_set_destroy (hb_set_t *set) +{ + if (!hb_object_destroy (set)) return; + + hb_free (set); +} + +/** + * hb_set_set_user_data: (skip) + * @set: A set + * @key: The user-data key to set + * @data: A pointer to the user data to set + * @destroy: (nullable): A callback to call when @data is not needed anymore + * @replace: Whether to replace an existing data with the same key + * + * Attaches a user-data key/data pair to the specified set. + * + * Return value: `true` if success, `false` otherwise + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_set_set_user_data (hb_set_t *set, + hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy, + hb_bool_t replace) +{ + return hb_object_set_user_data (set, key, data, destroy, replace); +} + +/** + * hb_set_get_user_data: (skip) + * @set: A set + * @key: The user-data key to query + * + * Fetches the user data associated with the specified key, + * attached to the specified set. + * + * Return value: (transfer none): A pointer to the user data + * + * Since: 0.9.2 + **/ +void * +hb_set_get_user_data (const hb_set_t *set, + hb_user_data_key_t *key) +{ + return hb_object_get_user_data (set, key); +} + + +/** + * hb_set_allocation_successful: + * @set: A set + * + * Tests whether memory allocation for a set was successful. + * + * Return value: `true` if allocation succeeded, `false` otherwise + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_set_allocation_successful (const hb_set_t *set) +{ + return !set->in_error (); +} + +/** + * hb_set_copy: + * @set: A set + * + * Allocate a copy of @set. + * + * Return value: Newly-allocated set. + * + * Since: 2.8.2 + **/ +hb_set_t * +hb_set_copy (const hb_set_t *set) +{ + hb_set_t *copy = hb_set_create (); + if (unlikely (!copy)) return nullptr; + copy->set (*set); + return copy; +} + +/** + * hb_set_clear: + * @set: A set + * + * Clears out the contents of a set. + * + * Since: 0.9.2 + **/ +void +hb_set_clear (hb_set_t *set) +{ + /* Immutible-safe. */ + set->clear (); +} + +/** + * hb_set_is_empty: + * @set: a set. + * + * Tests whether a set is empty (contains no elements). + * + * Return value: `true` if @set is empty + * + * Since: 0.9.7 + **/ +hb_bool_t +hb_set_is_empty (const hb_set_t *set) +{ + return set->is_empty (); +} + +/** + * hb_set_has: + * @set: A set + * @codepoint: The element to query + * + * Tests whether @codepoint belongs to @set. + * + * Return value: `true` if @codepoint is in @set, `false` otherwise + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_set_has (const hb_set_t *set, + hb_codepoint_t codepoint) +{ + return set->has (codepoint); +} + +/** + * hb_set_add: + * @set: A set + * @codepoint: The element to add to @set + * + * Adds @codepoint to @set. + * + * Since: 0.9.2 + **/ +void +hb_set_add (hb_set_t *set, + hb_codepoint_t codepoint) +{ + /* Immutible-safe. */ + set->add (codepoint); +} + +/** + * hb_set_add_sorted_array: + * @set: A set + * @sorted_codepoints: (array length=num_codepoints): Array of codepoints to add + * @num_codepoints: Length of @sorted_codepoints + * + * Adds @num_codepoints codepoints to a set at once. + * The codepoints array must be in increasing order, + * with size at least @num_codepoints. + * + * Since: 4.1.0 + */ +HB_EXTERN void +hb_set_add_sorted_array (hb_set_t *set, + const hb_codepoint_t *sorted_codepoints, + unsigned int num_codepoints) +{ + /* Immutible-safe. */ + set->add_sorted_array (sorted_codepoints, + num_codepoints, + sizeof(hb_codepoint_t)); +} + +/** + * hb_set_add_range: + * @set: A set + * @first: The first element to add to @set + * @last: The final element to add to @set + * + * Adds all of the elements from @first to @last + * (inclusive) to @set. + * + * Since: 0.9.7 + **/ +void +hb_set_add_range (hb_set_t *set, + hb_codepoint_t first, + hb_codepoint_t last) +{ + /* Immutible-safe. */ + set->add_range (first, last); +} + +/** + * hb_set_del: + * @set: A set + * @codepoint: Removes @codepoint from @set + * + * Removes @codepoint from @set. + * + * Since: 0.9.2 + **/ +void +hb_set_del (hb_set_t *set, + hb_codepoint_t codepoint) +{ + /* Immutible-safe. */ + set->del (codepoint); +} + +/** + * hb_set_del_range: + * @set: A set + * @first: The first element to remove from @set + * @last: The final element to remove from @set + * + * Removes all of the elements from @first to @last + * (inclusive) from @set. + * + * If @last is #HB_SET_VALUE_INVALID, then all values + * greater than or equal to @first are removed. + * + * Since: 0.9.7 + **/ +void +hb_set_del_range (hb_set_t *set, + hb_codepoint_t first, + hb_codepoint_t last) +{ + /* Immutible-safe. */ + set->del_range (first, last); +} + +/** + * hb_set_is_equal: + * @set: A set + * @other: Another set + * + * Tests whether @set and @other are equal (contain the same + * elements). + * + * Return value: `true` if the two sets are equal, `false` otherwise. + * + * Since: 0.9.7 + **/ +hb_bool_t +hb_set_is_equal (const hb_set_t *set, + const hb_set_t *other) +{ + return set->is_equal (*other); +} + +/** + * hb_set_hash: + * @set: A set + * + * Creates a hash representing @set. + * + * Return value: + * A hash of @set. + * + * Since: 4.4.0 + **/ +HB_EXTERN unsigned int +hb_set_hash (const hb_set_t *set) +{ + return set->hash (); +} + +/** + * hb_set_is_subset: + * @set: A set + * @larger_set: Another set + * + * Tests whether @set is a subset of @larger_set. + * + * Return value: `true` if the @set is a subset of (or equal to) @larger_set, `false` otherwise. + * + * Since: 1.8.1 + **/ +hb_bool_t +hb_set_is_subset (const hb_set_t *set, + const hb_set_t *larger_set) +{ + return set->is_subset (*larger_set); +} + +/** + * hb_set_set: + * @set: A set + * @other: Another set + * + * Makes the contents of @set equal to the contents of @other. + * + * Since: 0.9.2 + **/ +void +hb_set_set (hb_set_t *set, + const hb_set_t *other) +{ + /* Immutible-safe. */ + set->set (*other); +} + +/** + * hb_set_union: + * @set: A set + * @other: Another set + * + * Makes @set the union of @set and @other. + * + * Since: 0.9.2 + **/ +void +hb_set_union (hb_set_t *set, + const hb_set_t *other) +{ + /* Immutible-safe. */ + set->union_ (*other); +} + +/** + * hb_set_intersect: + * @set: A set + * @other: Another set + * + * Makes @set the intersection of @set and @other. + * + * Since: 0.9.2 + **/ +void +hb_set_intersect (hb_set_t *set, + const hb_set_t *other) +{ + /* Immutible-safe. */ + set->intersect (*other); +} + +/** + * hb_set_subtract: + * @set: A set + * @other: Another set + * + * Subtracts the contents of @other from @set. + * + * Since: 0.9.2 + **/ +void +hb_set_subtract (hb_set_t *set, + const hb_set_t *other) +{ + /* Immutible-safe. */ + set->subtract (*other); +} + +/** + * hb_set_symmetric_difference: + * @set: A set + * @other: Another set + * + * Makes @set the symmetric difference of @set + * and @other. + * + * Since: 0.9.2 + **/ +void +hb_set_symmetric_difference (hb_set_t *set, + const hb_set_t *other) +{ + /* Immutible-safe. */ + set->symmetric_difference (*other); +} + +/** + * hb_set_invert: + * @set: A set + * + * Inverts the contents of @set. + * + * Since: 3.0.0 + **/ +void +hb_set_invert (hb_set_t *set) +{ + /* Immutible-safe. */ + set->invert (); +} + +/** + * hb_set_get_population: + * @set: A set + * + * Returns the number of elements in the set. + * + * Return value: The population of @set + * + * Since: 0.9.7 + **/ +unsigned int +hb_set_get_population (const hb_set_t *set) +{ + return set->get_population (); +} + +/** + * hb_set_get_min: + * @set: A set + * + * Finds the smallest element in the set. + * + * Return value: minimum of @set, or #HB_SET_VALUE_INVALID if @set is empty. + * + * Since: 0.9.7 + **/ +hb_codepoint_t +hb_set_get_min (const hb_set_t *set) +{ + return set->get_min (); +} + +/** + * hb_set_get_max: + * @set: A set + * + * Finds the largest element in the set. + * + * Return value: maximum of @set, or #HB_SET_VALUE_INVALID if @set is empty. + * + * Since: 0.9.7 + **/ +hb_codepoint_t +hb_set_get_max (const hb_set_t *set) +{ + return set->get_max (); +} + +/** + * hb_set_next: + * @set: A set + * @codepoint: (inout): Input = Code point to query + * Output = Code point retrieved + * + * Fetches the next element in @set that is greater than current value of @codepoint. + * + * Set @codepoint to #HB_SET_VALUE_INVALID to get started. + * + * Return value: `true` if there was a next value, `false` otherwise + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_set_next (const hb_set_t *set, + hb_codepoint_t *codepoint) +{ + return set->next (codepoint); +} + +/** + * hb_set_previous: + * @set: A set + * @codepoint: (inout): Input = Code point to query + * Output = Code point retrieved + * + * Fetches the previous element in @set that is lower than current value of @codepoint. + * + * Set @codepoint to #HB_SET_VALUE_INVALID to get started. + * + * Return value: `true` if there was a previous value, `false` otherwise + * + * Since: 1.8.0 + **/ +hb_bool_t +hb_set_previous (const hb_set_t *set, + hb_codepoint_t *codepoint) +{ + return set->previous (codepoint); +} + +/** + * hb_set_next_range: + * @set: A set + * @first: (out): The first code point in the range + * @last: (inout): Input = The current last code point in the range + * Output = The last code point in the range + * + * Fetches the next consecutive range of elements in @set that + * are greater than current value of @last. + * + * Set @last to #HB_SET_VALUE_INVALID to get started. + * + * Return value: `true` if there was a next range, `false` otherwise + * + * Since: 0.9.7 + **/ +hb_bool_t +hb_set_next_range (const hb_set_t *set, + hb_codepoint_t *first, + hb_codepoint_t *last) +{ + return set->next_range (first, last); +} + +/** + * hb_set_previous_range: + * @set: A set + * @first: (inout): Input = The current first code point in the range + * Output = The first code point in the range + * @last: (out): The last code point in the range + * + * Fetches the previous consecutive range of elements in @set that + * are greater than current value of @last. + * + * Set @first to #HB_SET_VALUE_INVALID to get started. + * + * Return value: `true` if there was a previous range, `false` otherwise + * + * Since: 1.8.0 + **/ +hb_bool_t +hb_set_previous_range (const hb_set_t *set, + hb_codepoint_t *first, + hb_codepoint_t *last) +{ + return set->previous_range (first, last); +} + +/** + * hb_set_next_many: + * @set: A set + * @codepoint: Outputting codepoints starting after this one. + * Use #HB_SET_VALUE_INVALID to get started. + * @out: (array length=size): An array of codepoints to write to. + * @size: The maximum number of codepoints to write out. + * + * Finds the next element in @set that is greater than @codepoint. Writes out + * codepoints to @out, until either the set runs out of elements, or @size + * codepoints are written, whichever comes first. + * + * Return value: the number of values written. + * + * Since: 4.2.0 + **/ +unsigned int +hb_set_next_many (const hb_set_t *set, + hb_codepoint_t codepoint, + hb_codepoint_t *out, + unsigned int size) +{ + return set->next_many (codepoint, out, size); +} diff --git a/libs/harfbuzz/src/hb-set.h b/libs/harfbuzz/src/hb-set.h new file mode 100644 index 00000000..93636ab5 --- /dev/null +++ b/libs/harfbuzz/src/hb-set.h @@ -0,0 +1,200 @@ +/* + * Copyright © 2012 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 + */ + +#if !defined(HB_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR) +#error "Include instead." +#endif + +#ifndef HB_SET_H +#define HB_SET_H + +#include "hb-common.h" + +HB_BEGIN_DECLS + + +/** + * HB_SET_VALUE_INVALID: + * + * Unset #hb_set_t value. + * + * Since: 0.9.21 + */ +#define HB_SET_VALUE_INVALID ((hb_codepoint_t) -1) + +/** + * hb_set_t: + * + * Data type for holding a set of integers. #hb_set_t's are + * used to gather and contain glyph IDs, Unicode code + * points, and various other collections of discrete + * values. + * + **/ +typedef struct hb_set_t hb_set_t; + + +HB_EXTERN hb_set_t * +hb_set_create (void); + +HB_EXTERN hb_set_t * +hb_set_get_empty (void); + +HB_EXTERN hb_set_t * +hb_set_reference (hb_set_t *set); + +HB_EXTERN void +hb_set_destroy (hb_set_t *set); + +HB_EXTERN hb_bool_t +hb_set_set_user_data (hb_set_t *set, + hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy, + hb_bool_t replace); + +HB_EXTERN void * +hb_set_get_user_data (const hb_set_t *set, + hb_user_data_key_t *key); + + +/* Returns false if allocation has failed before */ +HB_EXTERN hb_bool_t +hb_set_allocation_successful (const hb_set_t *set); + +HB_EXTERN hb_set_t * +hb_set_copy (const hb_set_t *set); + +HB_EXTERN void +hb_set_clear (hb_set_t *set); + +HB_EXTERN hb_bool_t +hb_set_is_empty (const hb_set_t *set); + +HB_EXTERN void +hb_set_invert (hb_set_t *set); + +HB_EXTERN hb_bool_t +hb_set_has (const hb_set_t *set, + hb_codepoint_t codepoint); + +HB_EXTERN void +hb_set_add (hb_set_t *set, + hb_codepoint_t codepoint); + +HB_EXTERN void +hb_set_add_range (hb_set_t *set, + hb_codepoint_t first, + hb_codepoint_t last); + +HB_EXTERN void +hb_set_add_sorted_array (hb_set_t *set, + const hb_codepoint_t *sorted_codepoints, + unsigned int num_codepoints); + +HB_EXTERN void +hb_set_del (hb_set_t *set, + hb_codepoint_t codepoint); + +HB_EXTERN void +hb_set_del_range (hb_set_t *set, + hb_codepoint_t first, + hb_codepoint_t last); + +HB_EXTERN hb_bool_t +hb_set_is_equal (const hb_set_t *set, + const hb_set_t *other); + +HB_EXTERN unsigned int +hb_set_hash (const hb_set_t *set); + +HB_EXTERN hb_bool_t +hb_set_is_subset (const hb_set_t *set, + const hb_set_t *larger_set); + +HB_EXTERN void +hb_set_set (hb_set_t *set, + const hb_set_t *other); + +HB_EXTERN void +hb_set_union (hb_set_t *set, + const hb_set_t *other); + +HB_EXTERN void +hb_set_intersect (hb_set_t *set, + const hb_set_t *other); + +HB_EXTERN void +hb_set_subtract (hb_set_t *set, + const hb_set_t *other); + +HB_EXTERN void +hb_set_symmetric_difference (hb_set_t *set, + const hb_set_t *other); + +HB_EXTERN unsigned int +hb_set_get_population (const hb_set_t *set); + +/* Returns HB_SET_VALUE_INVALID if set empty. */ +HB_EXTERN hb_codepoint_t +hb_set_get_min (const hb_set_t *set); + +/* Returns HB_SET_VALUE_INVALID if set empty. */ +HB_EXTERN hb_codepoint_t +hb_set_get_max (const hb_set_t *set); + +/* Pass HB_SET_VALUE_INVALID in to get started. */ +HB_EXTERN hb_bool_t +hb_set_next (const hb_set_t *set, + hb_codepoint_t *codepoint); + +/* Pass HB_SET_VALUE_INVALID in to get started. */ +HB_EXTERN hb_bool_t +hb_set_previous (const hb_set_t *set, + hb_codepoint_t *codepoint); + +/* Pass HB_SET_VALUE_INVALID for first and last to get started. */ +HB_EXTERN hb_bool_t +hb_set_next_range (const hb_set_t *set, + hb_codepoint_t *first, + hb_codepoint_t *last); + +/* Pass HB_SET_VALUE_INVALID for first and last to get started. */ +HB_EXTERN hb_bool_t +hb_set_previous_range (const hb_set_t *set, + hb_codepoint_t *first, + hb_codepoint_t *last); + +/* Pass HB_SET_VALUE_INVALID in to get started. */ +HB_EXTERN unsigned int +hb_set_next_many (const hb_set_t *set, + hb_codepoint_t codepoint, + hb_codepoint_t *out, + unsigned int size); + +HB_END_DECLS + +#endif /* HB_SET_H */ diff --git a/libs/harfbuzz/src/hb-set.hh b/libs/harfbuzz/src/hb-set.hh new file mode 100644 index 00000000..5d5576cb --- /dev/null +++ b/libs/harfbuzz/src/hb-set.hh @@ -0,0 +1,180 @@ +/* + * Copyright © 2012,2017 Google, Inc. + * Copyright © 2021 Behdad Esfahbod + * + * 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 HB_SET_HH +#define HB_SET_HH + +#include "hb.hh" +#include "hb-bit-set-invertible.hh" + + +template +struct hb_sparseset_t +{ + hb_object_header_t header; + impl_t s; + + hb_sparseset_t () { init (); } + ~hb_sparseset_t () { fini (); } + + hb_sparseset_t (const hb_sparseset_t& other) : hb_sparseset_t () { set (other); } + hb_sparseset_t (hb_sparseset_t&& other) : hb_sparseset_t () { s = std::move (other.s); } + hb_sparseset_t& operator = (const hb_sparseset_t& other) { set (other); return *this; } + hb_sparseset_t& operator = (hb_sparseset_t&& other) { s = std::move (other.s); return *this; } + friend void swap (hb_sparseset_t& a, hb_sparseset_t& b) { hb_swap (a.s, b.s); } + + hb_sparseset_t (std::initializer_list lst) : hb_sparseset_t () + { + for (auto&& item : lst) + add (item); + } + template + hb_sparseset_t (const Iterable &o) : hb_sparseset_t () + { + hb_copy (o, *this); + } + + void init () + { + hb_object_init (this); + s.init (); + } + void fini () + { + hb_object_fini (this); + s.fini (); + } + + explicit operator bool () const { return !is_empty (); } + + void err () { s.err (); } + bool in_error () const { return s.in_error (); } + + void alloc (unsigned sz) { s.alloc (sz); } + void reset () { s.reset (); } + void clear () { s.clear (); } + void invert () { s.invert (); } + bool is_empty () const { return s.is_empty (); } + uint32_t hash () const { return s.hash (); } + + void add (hb_codepoint_t g) { s.add (g); } + bool add_range (hb_codepoint_t a, hb_codepoint_t b) { return s.add_range (a, b); } + + template + void add_array (const T *array, unsigned int count, unsigned int stride=sizeof(T)) + { s.add_array (array, count, stride); } + template + void add_array (const hb_array_t& arr) { add_array (&arr, arr.len ()); } + + /* Might return false if array looks unsorted. + * Used for faster rejection of corrupt data. */ + template + bool add_sorted_array (const T *array, unsigned int count, unsigned int stride=sizeof(T)) + { return s.add_sorted_array (array, count, stride); } + template + bool add_sorted_array (const hb_sorted_array_t& arr) { return add_sorted_array (&arr, arr.len ()); } + + void del (hb_codepoint_t g) { s.del (g); } + void del_range (hb_codepoint_t a, hb_codepoint_t b) { s.del_range (a, b); } + + bool get (hb_codepoint_t g) const { return s.get (g); } + + /* Has interface. */ + static constexpr bool SENTINEL = false; + typedef bool value_t; + value_t operator [] (hb_codepoint_t k) const { return get (k); } + bool has (hb_codepoint_t k) const { return (*this)[k] != SENTINEL; } + + /* Predicate. */ + bool operator () (hb_codepoint_t k) const { return has (k); } + + /* Sink interface. */ + hb_sparseset_t& operator << (hb_codepoint_t v) + { add (v); return *this; } + hb_sparseset_t& operator << (const hb_pair_t& range) + { add_range (range.first, range.second); return *this; } + + bool intersects (hb_codepoint_t first, hb_codepoint_t last) const + { return s.intersects (first, last); } + + void set (const hb_sparseset_t &other) { s.set (other.s); } + + bool is_equal (const hb_sparseset_t &other) const { return s.is_equal (other.s); } + bool operator == (const hb_set_t &other) const { return is_equal (other); } + bool operator != (const hb_set_t &other) const { return !is_equal (other); } + + bool is_subset (const hb_sparseset_t &larger_set) const { return s.is_subset (larger_set.s); } + + void union_ (const hb_sparseset_t &other) { s.union_ (other.s); } + void intersect (const hb_sparseset_t &other) { s.intersect (other.s); } + void subtract (const hb_sparseset_t &other) { s.subtract (other.s); } + void symmetric_difference (const hb_sparseset_t &other) { s.symmetric_difference (other.s); } + + bool next (hb_codepoint_t *codepoint) const { return s.next (codepoint); } + bool previous (hb_codepoint_t *codepoint) const { return s.previous (codepoint); } + bool next_range (hb_codepoint_t *first, hb_codepoint_t *last) const + { return s.next_range (first, last); } + bool previous_range (hb_codepoint_t *first, hb_codepoint_t *last) const + { return s.previous_range (first, last); } + unsigned int next_many (hb_codepoint_t codepoint, hb_codepoint_t *out, unsigned int size) const + { return s.next_many (codepoint, out, size); } + + unsigned int get_population () const { return s.get_population (); } + hb_codepoint_t get_min () const { return s.get_min (); } + hb_codepoint_t get_max () const { return s.get_max (); } + + static constexpr hb_codepoint_t INVALID = impl_t::INVALID; + + /* + * Iterator implementation. + */ + using iter_t = typename impl_t::iter_t; + iter_t iter () const { return iter_t (this->s); } + operator iter_t () const { return iter (); } +}; + +struct hb_set_t : hb_sparseset_t +{ + using sparseset = hb_sparseset_t; + + ~hb_set_t () = default; + hb_set_t () : sparseset () {}; + hb_set_t (const hb_set_t &o) : sparseset ((sparseset &) o) {}; + hb_set_t (hb_set_t&& o) : sparseset (std::move ((sparseset &) o)) {} + hb_set_t& operator = (const hb_set_t&) = default; + hb_set_t& operator = (hb_set_t&&) = default; + hb_set_t (std::initializer_list lst) : sparseset (lst) {} + template + hb_set_t (const Iterable &o) : sparseset (o) {} +}; + +static_assert (hb_set_t::INVALID == HB_SET_VALUE_INVALID, ""); + + +#endif /* HB_SET_HH */ diff --git a/libs/harfbuzz/src/hb-shape-plan.cc b/libs/harfbuzz/src/hb-shape-plan.cc new file mode 100644 index 00000000..9e513c04 --- /dev/null +++ b/libs/harfbuzz/src/hb-shape-plan.cc @@ -0,0 +1,576 @@ +/* + * Copyright © 2012 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 + */ + +#include "hb.hh" +#include "hb-shape-plan.hh" +#include "hb-shaper.hh" +#include "hb-font.hh" +#include "hb-buffer.hh" + + +/** + * SECTION:hb-shape-plan + * @title: hb-shape-plan + * @short_description: Object representing a shaping plan + * @include: hb.h + * + * Shape plans are an internal mechanism. Each plan contains state + * describing how HarfBuzz will shape a particular text segment, based on + * the combination of segment properties and the capabilities in the + * font face in use. + * + * Shape plans are not used for shaping directly, but can be queried to + * access certain information about how shaping will perform, given a set + * of specific input parameters (script, language, direction, features, + * etc.). + * + * Most client programs will not need to deal with shape plans directly. + **/ + + +/* + * hb_shape_plan_key_t + */ + +bool +hb_shape_plan_key_t::init (bool copy, + hb_face_t *face, + const hb_segment_properties_t *props, + const hb_feature_t *user_features, + unsigned int num_user_features, + const int *coords, + unsigned int num_coords, + const char * const *shaper_list) +{ + hb_feature_t *features = nullptr; + if (copy && num_user_features && !(features = (hb_feature_t *) hb_calloc (num_user_features, sizeof (hb_feature_t)))) + goto bail; + + this->props = *props; + this->num_user_features = num_user_features; + this->user_features = copy ? features : user_features; + if (copy && num_user_features) + { + memcpy (features, user_features, num_user_features * sizeof (hb_feature_t)); + /* Make start/end uniform to easier catch bugs. */ + for (unsigned int i = 0; i < num_user_features; i++) + { + if (features[0].start != HB_FEATURE_GLOBAL_START) + features[0].start = 1; + if (features[0].end != HB_FEATURE_GLOBAL_END) + features[0].end = 2; + } + } + this->shaper_func = nullptr; + this->shaper_name = nullptr; +#ifndef HB_NO_OT_SHAPE + this->ot.init (face, coords, num_coords); +#endif + + /* + * Choose shaper. + */ + +#define HB_SHAPER_PLAN(shaper) \ + HB_STMT_START { \ + if (face->data.shaper) \ + { \ + this->shaper_func = _hb_##shaper##_shape; \ + this->shaper_name = #shaper; \ + return true; \ + } \ + } HB_STMT_END + + if (unlikely (shaper_list)) + { + for (; *shaper_list; shaper_list++) + if (false) + ; +#define HB_SHAPER_IMPLEMENT(shaper) \ + else if (0 == strcmp (*shaper_list, #shaper)) \ + HB_SHAPER_PLAN (shaper); +#include "hb-shaper-list.hh" +#undef HB_SHAPER_IMPLEMENT + } + else + { + const HB_UNUSED hb_shaper_entry_t *shapers = _hb_shapers_get (); + for (unsigned int i = 0; i < HB_SHAPERS_COUNT; i++) + if (false) + ; +#define HB_SHAPER_IMPLEMENT(shaper) \ + else if (shapers[i].func == _hb_##shaper##_shape) \ + HB_SHAPER_PLAN (shaper); +#include "hb-shaper-list.hh" +#undef HB_SHAPER_IMPLEMENT + } +#undef HB_SHAPER_PLAN + +bail: + ::hb_free (features); + return false; +} + +bool +hb_shape_plan_key_t::user_features_match (const hb_shape_plan_key_t *other) +{ + if (this->num_user_features != other->num_user_features) + return false; + for (unsigned int i = 0; i < num_user_features; i++) + { + if (this->user_features[i].tag != other->user_features[i].tag || + this->user_features[i].value != other->user_features[i].value || + (this->user_features[i].start == HB_FEATURE_GLOBAL_START && + this->user_features[i].end == HB_FEATURE_GLOBAL_END) != + (other->user_features[i].start == HB_FEATURE_GLOBAL_START && + other->user_features[i].end == HB_FEATURE_GLOBAL_END)) + return false; + } + return true; +} + +bool +hb_shape_plan_key_t::equal (const hb_shape_plan_key_t *other) +{ + return hb_segment_properties_equal (&this->props, &other->props) && + this->user_features_match (other) && +#ifndef HB_NO_OT_SHAPE + this->ot.equal (&other->ot) && +#endif + this->shaper_func == other->shaper_func; +} + + +/* + * hb_shape_plan_t + */ + + +/** + * hb_shape_plan_create: + * @face: #hb_face_t to use + * @props: The #hb_segment_properties_t of the segment + * @user_features: (array length=num_user_features): The list of user-selected features + * @num_user_features: The number of user-selected features + * @shaper_list: (array zero-terminated=1): List of shapers to try + * + * Constructs a shaping plan for a combination of @face, @user_features, @props, + * and @shaper_list. + * + * Return value: (transfer full): The shaping plan + * + * Since: 0.9.7 + **/ +hb_shape_plan_t * +hb_shape_plan_create (hb_face_t *face, + const hb_segment_properties_t *props, + const hb_feature_t *user_features, + unsigned int num_user_features, + const char * const *shaper_list) +{ + return hb_shape_plan_create2 (face, props, + user_features, num_user_features, + nullptr, 0, + shaper_list); +} + +/** + * hb_shape_plan_create2: + * @face: #hb_face_t to use + * @props: The #hb_segment_properties_t of the segment + * @user_features: (array length=num_user_features): The list of user-selected features + * @num_user_features: The number of user-selected features + * @coords: (array length=num_coords): The list of variation-space coordinates + * @num_coords: The number of variation-space coordinates + * @shaper_list: (array zero-terminated=1): List of shapers to try + * + * The variable-font version of #hb_shape_plan_create. + * Constructs a shaping plan for a combination of @face, @user_features, @props, + * and @shaper_list, plus the variation-space coordinates @coords. + * + * Return value: (transfer full): The shaping plan + * + * Since: 1.4.0 + **/ +hb_shape_plan_t * +hb_shape_plan_create2 (hb_face_t *face, + const hb_segment_properties_t *props, + const hb_feature_t *user_features, + unsigned int num_user_features, + const int *coords, + unsigned int num_coords, + const char * const *shaper_list) +{ + DEBUG_MSG_FUNC (SHAPE_PLAN, nullptr, + "face=%p num_features=%d num_coords=%d shaper_list=%p", + face, + num_user_features, + num_coords, + shaper_list); + + if (unlikely (props->direction == HB_DIRECTION_INVALID)) + return hb_shape_plan_get_empty (); + + hb_shape_plan_t *shape_plan; + + if (unlikely (!props)) + goto bail; + if (!(shape_plan = hb_object_create ())) + goto bail; + + if (unlikely (!face)) + face = hb_face_get_empty (); + hb_face_make_immutable (face); + shape_plan->face_unsafe = face; + + if (unlikely (!shape_plan->key.init (true, + face, + props, + user_features, + num_user_features, + coords, + num_coords, + shaper_list))) + goto bail2; +#ifndef HB_NO_OT_SHAPE + if (unlikely (!shape_plan->ot.init0 (face, &shape_plan->key))) + goto bail3; +#endif + + return shape_plan; + +#ifndef HB_NO_OT_SHAPE +bail3: +#endif + shape_plan->key.fini (); +bail2: + hb_free (shape_plan); +bail: + return hb_shape_plan_get_empty (); +} + +/** + * hb_shape_plan_get_empty: + * + * Fetches the singleton empty shaping plan. + * + * Return value: (transfer full): The empty shaping plan + * + * Since: 0.9.7 + **/ +hb_shape_plan_t * +hb_shape_plan_get_empty () +{ + return const_cast (&Null (hb_shape_plan_t)); +} + +/** + * hb_shape_plan_reference: (skip) + * @shape_plan: A shaping plan + * + * Increases the reference count on the given shaping plan. + * + * Return value: (transfer full): @shape_plan + * + * Since: 0.9.7 + **/ +hb_shape_plan_t * +hb_shape_plan_reference (hb_shape_plan_t *shape_plan) +{ + return hb_object_reference (shape_plan); +} + +/** + * hb_shape_plan_destroy: (skip) + * @shape_plan: A shaping plan + * + * Decreases the reference count on the given shaping plan. When the + * reference count reaches zero, the shaping plan is destroyed, + * freeing all memory. + * + * Since: 0.9.7 + **/ +void +hb_shape_plan_destroy (hb_shape_plan_t *shape_plan) +{ + if (!hb_object_destroy (shape_plan)) return; + + hb_free (shape_plan); +} + +/** + * hb_shape_plan_set_user_data: (skip) + * @shape_plan: A shaping plan + * @key: The user-data key to set + * @data: A pointer to the user data + * @destroy: (nullable): A callback to call when @data is not needed anymore + * @replace: Whether to replace an existing data with the same key + * + * Attaches a user-data key/data pair to the given shaping plan. + * + * Return value: `true` if success, `false` otherwise. + * + * Since: 0.9.7 + **/ +hb_bool_t +hb_shape_plan_set_user_data (hb_shape_plan_t *shape_plan, + hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy, + hb_bool_t replace) +{ + return hb_object_set_user_data (shape_plan, key, data, destroy, replace); +} + +/** + * hb_shape_plan_get_user_data: (skip) + * @shape_plan: A shaping plan + * @key: The user-data key to query + * + * Fetches the user data associated with the specified key, + * attached to the specified shaping plan. + * + * Return value: (transfer none): A pointer to the user data + * + * Since: 0.9.7 + **/ +void * +hb_shape_plan_get_user_data (const hb_shape_plan_t *shape_plan, + hb_user_data_key_t *key) +{ + return hb_object_get_user_data (shape_plan, key); +} + +/** + * hb_shape_plan_get_shaper: + * @shape_plan: A shaping plan + * + * Fetches the shaper from a given shaping plan. + * + * Return value: (transfer none): The shaper + * + * Since: 0.9.7 + **/ +const char * +hb_shape_plan_get_shaper (hb_shape_plan_t *shape_plan) +{ + return shape_plan->key.shaper_name; +} + + +static bool +_hb_shape_plan_execute_internal (hb_shape_plan_t *shape_plan, + hb_font_t *font, + hb_buffer_t *buffer, + const hb_feature_t *features, + unsigned int num_features) +{ + DEBUG_MSG_FUNC (SHAPE_PLAN, shape_plan, + "num_features=%d shaper_func=%p, shaper_name=%s", + num_features, + shape_plan->key.shaper_func, + shape_plan->key.shaper_name); + + if (unlikely (!buffer->len)) + return true; + + assert (!hb_object_is_immutable (buffer)); + + buffer->assert_unicode (); + + if (unlikely (!hb_object_is_valid (shape_plan))) + return false; + + assert (shape_plan->face_unsafe == font->face); + assert (hb_segment_properties_equal (&shape_plan->key.props, &buffer->props)); + +#define HB_SHAPER_EXECUTE(shaper) \ + HB_STMT_START { \ + return font->data.shaper && \ + _hb_##shaper##_shape (shape_plan, font, buffer, features, num_features); \ + } HB_STMT_END + + if (false) + ; +#define HB_SHAPER_IMPLEMENT(shaper) \ + else if (shape_plan->key.shaper_func == _hb_##shaper##_shape) \ + HB_SHAPER_EXECUTE (shaper); +#include "hb-shaper-list.hh" +#undef HB_SHAPER_IMPLEMENT + +#undef HB_SHAPER_EXECUTE + + return false; +} +/** + * hb_shape_plan_execute: + * @shape_plan: A shaping plan + * @font: The #hb_font_t to use + * @buffer: The #hb_buffer_t to work upon + * @features: (array length=num_features): Features to enable + * @num_features: The number of features to enable + * + * Executes the given shaping plan on the specified buffer, using + * the given @font and @features. + * + * Return value: `true` if success, `false` otherwise. + * + * Since: 0.9.7 + **/ +hb_bool_t +hb_shape_plan_execute (hb_shape_plan_t *shape_plan, + hb_font_t *font, + hb_buffer_t *buffer, + const hb_feature_t *features, + unsigned int num_features) +{ + bool ret = _hb_shape_plan_execute_internal (shape_plan, font, buffer, + features, num_features); + + if (ret && buffer->content_type == HB_BUFFER_CONTENT_TYPE_UNICODE) + buffer->content_type = HB_BUFFER_CONTENT_TYPE_GLYPHS; + + return ret; +} + + +/* + * Caching + */ + +/** + * hb_shape_plan_create_cached: + * @face: #hb_face_t to use + * @props: The #hb_segment_properties_t of the segment + * @user_features: (array length=num_user_features): The list of user-selected features + * @num_user_features: The number of user-selected features + * @shaper_list: (array zero-terminated=1): List of shapers to try + * + * Creates a cached shaping plan suitable for reuse, for a combination + * of @face, @user_features, @props, and @shaper_list. + * + * Return value: (transfer full): The shaping plan + * + * Since: 0.9.7 + **/ +hb_shape_plan_t * +hb_shape_plan_create_cached (hb_face_t *face, + const hb_segment_properties_t *props, + const hb_feature_t *user_features, + unsigned int num_user_features, + const char * const *shaper_list) +{ + return hb_shape_plan_create_cached2 (face, props, + user_features, num_user_features, + nullptr, 0, + shaper_list); +} + +/** + * hb_shape_plan_create_cached2: + * @face: #hb_face_t to use + * @props: The #hb_segment_properties_t of the segment + * @user_features: (array length=num_user_features): The list of user-selected features + * @num_user_features: The number of user-selected features + * @coords: (array length=num_coords): The list of variation-space coordinates + * @num_coords: The number of variation-space coordinates + * @shaper_list: (array zero-terminated=1): List of shapers to try + * + * The variable-font version of #hb_shape_plan_create_cached. + * Creates a cached shaping plan suitable for reuse, for a combination + * of @face, @user_features, @props, and @shaper_list, plus the + * variation-space coordinates @coords. + * + * Return value: (transfer full): The shaping plan + * + * Since: 1.4.0 + **/ +hb_shape_plan_t * +hb_shape_plan_create_cached2 (hb_face_t *face, + const hb_segment_properties_t *props, + const hb_feature_t *user_features, + unsigned int num_user_features, + const int *coords, + unsigned int num_coords, + const char * const *shaper_list) +{ + DEBUG_MSG_FUNC (SHAPE_PLAN, nullptr, + "face=%p num_features=%d shaper_list=%p", + face, + num_user_features, + shaper_list); + +retry: + hb_face_t::plan_node_t *cached_plan_nodes = face->shape_plans; + + bool dont_cache = !hb_object_is_valid (face); + + if (likely (!dont_cache)) + { + hb_shape_plan_key_t key; + if (!key.init (false, + face, + props, + user_features, + num_user_features, + coords, + num_coords, + shaper_list)) + return hb_shape_plan_get_empty (); + + for (hb_face_t::plan_node_t *node = cached_plan_nodes; node; node = node->next) + if (node->shape_plan->key.equal (&key)) + { + DEBUG_MSG_FUNC (SHAPE_PLAN, node->shape_plan, "fulfilled from cache"); + return hb_shape_plan_reference (node->shape_plan); + } + } + + hb_shape_plan_t *shape_plan = hb_shape_plan_create2 (face, props, + user_features, num_user_features, + coords, num_coords, + shaper_list); + + if (unlikely (dont_cache)) + return shape_plan; + + hb_face_t::plan_node_t *node = (hb_face_t::plan_node_t *) hb_calloc (1, sizeof (hb_face_t::plan_node_t)); + if (unlikely (!node)) + return shape_plan; + + node->shape_plan = shape_plan; + node->next = cached_plan_nodes; + + if (unlikely (!face->shape_plans.cmpexch (cached_plan_nodes, node))) + { + hb_shape_plan_destroy (shape_plan); + hb_free (node); + goto retry; + } + DEBUG_MSG_FUNC (SHAPE_PLAN, shape_plan, "inserted into cache"); + + return hb_shape_plan_reference (shape_plan); +} diff --git a/libs/harfbuzz/src/hb-shape-plan.h b/libs/harfbuzz/src/hb-shape-plan.h new file mode 100644 index 00000000..aaf5cf9c --- /dev/null +++ b/libs/harfbuzz/src/hb-shape-plan.h @@ -0,0 +1,122 @@ +/* + * Copyright © 2012 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 + */ + +#if !defined(HB_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR) +#error "Include instead." +#endif + +#ifndef HB_SHAPE_PLAN_H +#define HB_SHAPE_PLAN_H + +#include "hb-common.h" +#include "hb-font.h" + +HB_BEGIN_DECLS + +/** + * hb_shape_plan_t: + * + * Data type for holding a shaping plan. + * + * Shape plans contain information about how HarfBuzz will shape a + * particular text segment, based on the segment's properties and the + * capabilities in the font face in use. + * + * Shape plans can be queried about how shaping will perform, given a set + * of specific input parameters (script, language, direction, features, + * etc.). + * + **/ +typedef struct hb_shape_plan_t hb_shape_plan_t; + +HB_EXTERN hb_shape_plan_t * +hb_shape_plan_create (hb_face_t *face, + const hb_segment_properties_t *props, + const hb_feature_t *user_features, + unsigned int num_user_features, + const char * const *shaper_list); + +HB_EXTERN hb_shape_plan_t * +hb_shape_plan_create_cached (hb_face_t *face, + const hb_segment_properties_t *props, + const hb_feature_t *user_features, + unsigned int num_user_features, + const char * const *shaper_list); + +HB_EXTERN hb_shape_plan_t * +hb_shape_plan_create2 (hb_face_t *face, + const hb_segment_properties_t *props, + const hb_feature_t *user_features, + unsigned int num_user_features, + const int *coords, + unsigned int num_coords, + const char * const *shaper_list); + +HB_EXTERN hb_shape_plan_t * +hb_shape_plan_create_cached2 (hb_face_t *face, + const hb_segment_properties_t *props, + const hb_feature_t *user_features, + unsigned int num_user_features, + const int *coords, + unsigned int num_coords, + const char * const *shaper_list); + + +HB_EXTERN hb_shape_plan_t * +hb_shape_plan_get_empty (void); + +HB_EXTERN hb_shape_plan_t * +hb_shape_plan_reference (hb_shape_plan_t *shape_plan); + +HB_EXTERN void +hb_shape_plan_destroy (hb_shape_plan_t *shape_plan); + +HB_EXTERN hb_bool_t +hb_shape_plan_set_user_data (hb_shape_plan_t *shape_plan, + hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy, + hb_bool_t replace); + +HB_EXTERN void * +hb_shape_plan_get_user_data (const hb_shape_plan_t *shape_plan, + hb_user_data_key_t *key); + + +HB_EXTERN hb_bool_t +hb_shape_plan_execute (hb_shape_plan_t *shape_plan, + hb_font_t *font, + hb_buffer_t *buffer, + const hb_feature_t *features, + unsigned int num_features); + +HB_EXTERN const char * +hb_shape_plan_get_shaper (hb_shape_plan_t *shape_plan); + + +HB_END_DECLS + +#endif /* HB_SHAPE_PLAN_H */ diff --git a/libs/harfbuzz/src/hb-shape-plan.hh b/libs/harfbuzz/src/hb-shape-plan.hh new file mode 100644 index 00000000..6fc73939 --- /dev/null +++ b/libs/harfbuzz/src/hb-shape-plan.hh @@ -0,0 +1,77 @@ +/* + * Copyright © 2012,2018 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 HB_SHAPE_PLAN_HH +#define HB_SHAPE_PLAN_HH + +#include "hb.hh" +#include "hb-shaper.hh" +#include "hb-ot-shape.hh" + + +struct hb_shape_plan_key_t +{ + hb_segment_properties_t props; + + const hb_feature_t *user_features; + unsigned int num_user_features; + +#ifndef HB_NO_OT_SHAPE + hb_ot_shape_plan_key_t ot; +#endif + + hb_shape_func_t *shaper_func; + const char *shaper_name; + + HB_INTERNAL bool init (bool copy, + hb_face_t *face, + const hb_segment_properties_t *props, + const hb_feature_t *user_features, + unsigned int num_user_features, + const int *coords, + unsigned int num_coords, + const char * const *shaper_list); + + HB_INTERNAL void fini () { hb_free ((void *) user_features); user_features = nullptr; } + + HB_INTERNAL bool user_features_match (const hb_shape_plan_key_t *other); + + HB_INTERNAL bool equal (const hb_shape_plan_key_t *other); +}; + +struct hb_shape_plan_t +{ + ~hb_shape_plan_t () { key.fini (); } + hb_object_header_t header; + hb_face_t *face_unsafe; /* We don't carry a reference to face. */ + hb_shape_plan_key_t key; +#ifndef HB_NO_OT_SHAPE + hb_ot_shape_plan_t ot; +#endif +}; + + +#endif /* HB_SHAPE_PLAN_HH */ diff --git a/libs/harfbuzz/src/hb-shape.cc b/libs/harfbuzz/src/hb-shape.cc new file mode 100644 index 00000000..547d0afc --- /dev/null +++ b/libs/harfbuzz/src/hb-shape.cc @@ -0,0 +1,194 @@ +/* + * Copyright © 2009 Red Hat, Inc. + * Copyright © 2012 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. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#include "hb.hh" + +#include "hb-shaper.hh" +#include "hb-shape-plan.hh" +#include "hb-buffer.hh" +#include "hb-font.hh" +#include "hb-machinery.hh" + + +/** + * SECTION:hb-shape + * @title: hb-shape + * @short_description: Conversion of text strings into positioned glyphs + * @include: hb.h + * + * Shaping is the central operation of HarfBuzz. Shaping operates on buffers, + * which are sequences of Unicode characters that use the same font and have + * the same text direction, script, and language. After shaping the buffer + * contains the output glyphs and their positions. + **/ + + +static inline void free_static_shaper_list (); + +static const char * const nil_shaper_list[] = {nullptr}; + +static struct hb_shaper_list_lazy_loader_t : hb_lazy_loader_t +{ + static const char ** create () + { + const char **shaper_list = (const char **) hb_calloc (1 + HB_SHAPERS_COUNT, sizeof (const char *)); + if (unlikely (!shaper_list)) + return nullptr; + + const hb_shaper_entry_t *shapers = _hb_shapers_get (); + unsigned int i; + for (i = 0; i < HB_SHAPERS_COUNT; i++) + shaper_list[i] = shapers[i].name; + shaper_list[i] = nullptr; + + hb_atexit (free_static_shaper_list); + + return shaper_list; + } + static void destroy (const char **l) + { hb_free (l); } + static const char * const * get_null () + { return nil_shaper_list; } +} static_shaper_list; + +static inline +void free_static_shaper_list () +{ + static_shaper_list.free_instance (); +} + + +/** + * hb_shape_list_shapers: + * + * Retrieves the list of shapers supported by HarfBuzz. + * + * Return value: (transfer none) (array zero-terminated=1): an array of + * constant strings + * + * Since: 0.9.2 + **/ +const char ** +hb_shape_list_shapers () +{ + return static_shaper_list.get_unconst (); +} + + +/** + * hb_shape_full: + * @font: an #hb_font_t to use for shaping + * @buffer: an #hb_buffer_t to shape + * @features: (array length=num_features) (nullable): an array of user + * specified #hb_feature_t or `NULL` + * @num_features: the length of @features array + * @shaper_list: (array zero-terminated=1) (nullable): a `NULL`-terminated + * array of shapers to use or `NULL` + * + * See hb_shape() for details. If @shaper_list is not `NULL`, the specified + * shapers will be used in the given order, otherwise the default shapers list + * will be used. + * + * Return value: false if all shapers failed, true otherwise + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_shape_full (hb_font_t *font, + hb_buffer_t *buffer, + const hb_feature_t *features, + unsigned int num_features, + const char * const *shaper_list) +{ + if (unlikely (!buffer->len)) + return true; + + buffer->enter (); + + hb_buffer_t *text_buffer = nullptr; + if (buffer->flags & HB_BUFFER_FLAG_VERIFY) + { + text_buffer = hb_buffer_create (); + hb_buffer_append (text_buffer, buffer, 0, -1); + } + + hb_shape_plan_t *shape_plan = hb_shape_plan_create_cached2 (font->face, &buffer->props, + features, num_features, + font->coords, font->num_coords, + shaper_list); + + hb_bool_t res = hb_shape_plan_execute (shape_plan, font, buffer, features, num_features); + + if (buffer->max_ops <= 0) + buffer->shaping_failed = true; + + hb_shape_plan_destroy (shape_plan); + + if (text_buffer) + { + if (res && buffer->successful && !buffer->shaping_failed + && text_buffer->successful + && !buffer->verify (text_buffer, + font, + features, + num_features, + shaper_list)) + res = false; + hb_buffer_destroy (text_buffer); + } + + buffer->leave (); + + return res; +} + +/** + * hb_shape: + * @font: an #hb_font_t to use for shaping + * @buffer: an #hb_buffer_t to shape + * @features: (array length=num_features) (nullable): an array of user + * specified #hb_feature_t or `NULL` + * @num_features: the length of @features array + * + * Shapes @buffer using @font turning its Unicode characters content to + * positioned glyphs. If @features is not `NULL`, it will be used to control the + * features applied during shaping. If two @features have the same tag but + * overlapping ranges the value of the feature with the higher index takes + * precedence. + * + * Since: 0.9.2 + **/ +void +hb_shape (hb_font_t *font, + hb_buffer_t *buffer, + const hb_feature_t *features, + unsigned int num_features) +{ + hb_shape_full (font, buffer, features, num_features, nullptr); +} diff --git a/libs/harfbuzz/src/hb-shape.h b/libs/harfbuzz/src/hb-shape.h new file mode 100644 index 00000000..922f8c01 --- /dev/null +++ b/libs/harfbuzz/src/hb-shape.h @@ -0,0 +1,62 @@ +/* + * Copyright © 2009 Red Hat, Inc. + * Copyright © 2012 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. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#if !defined(HB_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR) +#error "Include instead." +#endif + +#ifndef HB_SHAPE_H +#define HB_SHAPE_H + +#include "hb-common.h" +#include "hb-buffer.h" +#include "hb-font.h" + +HB_BEGIN_DECLS + + +HB_EXTERN void +hb_shape (hb_font_t *font, + hb_buffer_t *buffer, + const hb_feature_t *features, + unsigned int num_features); + +HB_EXTERN hb_bool_t +hb_shape_full (hb_font_t *font, + hb_buffer_t *buffer, + const hb_feature_t *features, + unsigned int num_features, + const char * const *shaper_list); + +HB_EXTERN const char ** +hb_shape_list_shapers (void); + + +HB_END_DECLS + +#endif /* HB_SHAPE_H */ diff --git a/libs/harfbuzz/src/hb-shaper-impl.hh b/libs/harfbuzz/src/hb-shaper-impl.hh new file mode 100644 index 00000000..b674fceb --- /dev/null +++ b/libs/harfbuzz/src/hb-shaper-impl.hh @@ -0,0 +1,38 @@ +/* + * Copyright © 2012 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 HB_SHAPER_IMPL_HH +#define HB_SHAPER_IMPL_HH + +#include "hb.hh" + +#include "hb-shaper.hh" +#include "hb-face.hh" +#include "hb-font.hh" +#include "hb-shape-plan.hh" +#include "hb-buffer.hh" + +#endif /* HB_SHAPER_IMPL_HH */ diff --git a/libs/harfbuzz/src/hb-shaper-list.hh b/libs/harfbuzz/src/hb-shaper-list.hh new file mode 100644 index 00000000..0d63933a --- /dev/null +++ b/libs/harfbuzz/src/hb-shaper-list.hh @@ -0,0 +1,60 @@ +/* + * Copyright © 2012 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 HB_SHAPER_LIST_HH +#define HB_SHAPER_LIST_HH +#endif /* HB_SHAPER_LIST_HH */ /* Dummy header guards */ + +#ifndef HB_NO_SHAPER + + +/* v--- Add new shapers in the right place here. */ + +#ifdef HAVE_GRAPHITE2 +/* Only picks up fonts that have a "Silf" table. */ +HB_SHAPER_IMPLEMENT (graphite2) +#endif + +#ifndef HB_NO_OT_SHAPE +HB_SHAPER_IMPLEMENT (ot) /* <--- This is our main OpenType shaper. */ +#endif + +#ifdef HAVE_UNISCRIBE +HB_SHAPER_IMPLEMENT (uniscribe) +#endif +#ifdef HAVE_DIRECTWRITE +HB_SHAPER_IMPLEMENT (directwrite) +#endif +#ifdef HAVE_CORETEXT +HB_SHAPER_IMPLEMENT (coretext) +#endif + +#ifndef HB_NO_FALLBACK_SHAPE +HB_SHAPER_IMPLEMENT (fallback) /* <--- This should be last. */ +#endif + + +#endif diff --git a/libs/harfbuzz/src/hb-shaper.cc b/libs/harfbuzz/src/hb-shaper.cc new file mode 100644 index 00000000..a900ac69 --- /dev/null +++ b/libs/harfbuzz/src/hb-shaper.cc @@ -0,0 +1,102 @@ +/* + * Copyright © 2012 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 + */ + +#include "hb.hh" +#include "hb-shaper.hh" +#include "hb-machinery.hh" + + +static const hb_shaper_entry_t _hb_all_shapers[] = { +#define HB_SHAPER_IMPLEMENT(name) {#name, _hb_##name##_shape}, +#include "hb-shaper-list.hh" +#undef HB_SHAPER_IMPLEMENT +}; +#ifndef HB_NO_SHAPER +static_assert (0 != ARRAY_LENGTH_CONST (_hb_all_shapers), "No shaper enabled."); +#endif + +static inline void free_static_shapers (); + +static struct hb_shapers_lazy_loader_t : hb_lazy_loader_t +{ + static hb_shaper_entry_t *create () + { + char *env = getenv ("HB_SHAPER_LIST"); + if (!env || !*env) + return nullptr; + + hb_shaper_entry_t *shapers = (hb_shaper_entry_t *) hb_calloc (1, sizeof (_hb_all_shapers)); + if (unlikely (!shapers)) + return nullptr; + + memcpy (shapers, _hb_all_shapers, sizeof (_hb_all_shapers)); + + /* Reorder shaper list to prefer requested shapers. */ + unsigned int i = 0; + char *end, *p = env; + for (;;) + { + end = strchr (p, ','); + if (!end) + end = p + strlen (p); + + for (unsigned int j = i; j < ARRAY_LENGTH_CONST (_hb_all_shapers); j++) + if (end - p == (int) strlen (shapers[j].name) && + 0 == strncmp (shapers[j].name, p, end - p)) + { + /* Reorder this shaper to position i */ + struct hb_shaper_entry_t t = shapers[j]; + memmove (&shapers[i + 1], &shapers[i], sizeof (shapers[i]) * (j - i)); + shapers[i] = t; + i++; + } + + if (!*end) + break; + else + p = end + 1; + } + + hb_atexit (free_static_shapers); + + return shapers; + } + static void destroy (hb_shaper_entry_t *p) { hb_free (p); } + static const hb_shaper_entry_t *get_null () { return _hb_all_shapers; } +} static_shapers; + +static inline +void free_static_shapers () +{ + static_shapers.free_instance (); +} + +const hb_shaper_entry_t * +_hb_shapers_get () +{ + return static_shapers.get_unconst (); +} diff --git a/libs/harfbuzz/src/hb-shaper.hh b/libs/harfbuzz/src/hb-shaper.hh new file mode 100644 index 00000000..b4138a32 --- /dev/null +++ b/libs/harfbuzz/src/hb-shaper.hh @@ -0,0 +1,134 @@ +/* + * Copyright © 2012 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 HB_SHAPER_HH +#define HB_SHAPER_HH + +#include "hb.hh" +#include "hb-machinery.hh" + +typedef hb_bool_t hb_shape_func_t (hb_shape_plan_t *shape_plan, + hb_font_t *font, + hb_buffer_t *buffer, + const hb_feature_t *features, + unsigned int num_features); + +#define HB_SHAPER_IMPLEMENT(name) \ + extern "C" HB_INTERNAL hb_shape_func_t _hb_##name##_shape; +#include "hb-shaper-list.hh" +#undef HB_SHAPER_IMPLEMENT + +struct hb_shaper_entry_t { + char name[16]; + hb_shape_func_t *func; +}; + +HB_INTERNAL const hb_shaper_entry_t * +_hb_shapers_get (); + + +template +struct hb_shaper_lazy_loader_t; + +#define HB_SHAPER_ORDER(Shaper) \ + HB_PASTE (HB_SHAPER_ORDER_, Shaper) +enum hb_shaper_order_t +{ + _HB_SHAPER_ORDER_ORDER_ZERO, +#define HB_SHAPER_IMPLEMENT(Shaper) \ + HB_SHAPER_ORDER (Shaper), +#include "hb-shaper-list.hh" +#undef HB_SHAPER_IMPLEMENT + _HB_SHAPERS_COUNT_PLUS_ONE, + HB_SHAPERS_COUNT = _HB_SHAPERS_COUNT_PLUS_ONE - 1, +}; + +template struct hb_shaper_object_data_type_t; + +#define HB_SHAPER_DATA_SUCCEEDED ((void *) +1) +#define HB_SHAPER_DATA_TYPE(shaper, object) hb_##shaper##_##object##_data_t +#define HB_SHAPER_DATA_CREATE_FUNC(shaper, object) _hb_##shaper##_shaper_##object##_data_create +#define HB_SHAPER_DATA_DESTROY_FUNC(shaper, object) _hb_##shaper##_shaper_##object##_data_destroy + +#define HB_SHAPER_DATA_INSTANTIATE_SHAPERS(shaper, object) \ + \ + struct HB_SHAPER_DATA_TYPE (shaper, object); /* Type forward declaration. */ \ + extern "C" HB_INTERNAL HB_SHAPER_DATA_TYPE (shaper, object) * \ + HB_SHAPER_DATA_CREATE_FUNC (shaper, object) (hb_##object##_t *object); \ + extern "C" HB_INTERNAL void \ + HB_SHAPER_DATA_DESTROY_FUNC (shaper, object) (HB_SHAPER_DATA_TYPE (shaper, object) *shaper##_##object); \ + \ + template <> \ + struct hb_shaper_object_data_type_t \ + { \ + typedef HB_SHAPER_DATA_TYPE(shaper, object) value; \ + }; \ + \ + template \ + struct hb_shaper_lazy_loader_t \ + : hb_lazy_loader_t, \ + hb_##object##_t, WheresData> \ + { \ + typedef HB_SHAPER_DATA_TYPE(shaper, object) Type; \ + static Type* create (hb_##object##_t *data) \ + { return HB_SHAPER_DATA_CREATE_FUNC (shaper, object) (data); } \ + static Type *get_null () { return nullptr; } \ + static void destroy (Type *p) { HB_SHAPER_DATA_DESTROY_FUNC (shaper, object) (p); } \ + }; \ + \ + static_assert (true, "") /* Require semicolon after. */ + + +template +struct hb_shaper_object_dataset_t +{ + void init0 (Object *parent_data) + { + this->parent_data = parent_data; +#define HB_SHAPER_IMPLEMENT(shaper) shaper.init0 (); +#include "hb-shaper-list.hh" +#undef HB_SHAPER_IMPLEMENT + } + void fini () + { +#define HB_SHAPER_IMPLEMENT(shaper) shaper.fini (); +#include "hb-shaper-list.hh" +#undef HB_SHAPER_IMPLEMENT + } + + Object *parent_data; /* MUST be JUST before the lazy loaders. */ +#define HB_SHAPER_IMPLEMENT(shaper) \ + hb_shaper_lazy_loader_t::value \ + > shaper; +#include "hb-shaper-list.hh" +#undef HB_SHAPER_IMPLEMENT +}; + +#endif /* HB_SHAPER_HH */ diff --git a/libs/harfbuzz/src/hb-static.cc b/libs/harfbuzz/src/hb-static.cc new file mode 100644 index 00000000..5d4c7cda --- /dev/null +++ b/libs/harfbuzz/src/hb-static.cc @@ -0,0 +1,142 @@ +/* + * Copyright © 2018 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 + */ + +#include "hb.hh" + +#include "hb-open-type.hh" +#include "hb-face.hh" + +#include "hb-aat-layout-common.hh" +#include "hb-aat-layout-feat-table.hh" +#include "hb-ot-layout-common.hh" +#include "hb-ot-cmap-table.hh" +#include "hb-ot-glyf-table.hh" +#include "hb-ot-head-table.hh" +#include "hb-ot-maxp-table.hh" + +#ifndef HB_NO_VISIBILITY +#include "hb-ot-name-language-static.hh" + +uint64_t const _hb_NullPool[(HB_NULL_POOL_SIZE + sizeof (uint64_t) - 1) / sizeof (uint64_t)] = {}; +/*thread_local*/ uint64_t _hb_CrapPool[(HB_NULL_POOL_SIZE + sizeof (uint64_t) - 1) / sizeof (uint64_t)] = {}; + +DEFINE_NULL_NAMESPACE_BYTES (OT, Index) = {0xFF,0xFF}; +DEFINE_NULL_NAMESPACE_BYTES (OT, VarIdx) = {0xFF,0xFF,0xFF,0xFF}; +DEFINE_NULL_NAMESPACE_BYTES (OT, LangSys) = {0x00,0x00, 0xFF,0xFF, 0x00,0x00}; +DEFINE_NULL_NAMESPACE_BYTES (OT, RangeRecord) = {0x01}; +DEFINE_NULL_NAMESPACE_BYTES (OT, CmapSubtableLongGroup) = {0x00,0x00,0x00,0x01, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00}; +DEFINE_NULL_NAMESPACE_BYTES (AAT, SettingName) = {0xFF,0xFF, 0xFF,0xFF}; +DEFINE_NULL_NAMESPACE_BYTES (AAT, Lookup) = {0xFF,0xFF}; + + +/* hb_map_t */ + +const hb_codepoint_t minus_1 = -1; + +/* hb_face_t */ + +#ifndef HB_NO_BEYOND_64K +static inline unsigned +load_num_glyphs_from_loca (const hb_face_t *face) +{ + unsigned ret = 0; + + unsigned indexToLocFormat = face->table.head->indexToLocFormat; + + if (indexToLocFormat <= 1) + { + bool short_offset = 0 == indexToLocFormat; + hb_blob_t *loca_blob = face->table.loca.get_blob (); + ret = hb_max (1u, loca_blob->length / (short_offset ? 2 : 4)) - 1; + } + + return ret; +} +#endif + +static inline unsigned +load_num_glyphs_from_maxp (const hb_face_t *face) +{ + return face->table.maxp->get_num_glyphs (); +} + +unsigned int +hb_face_t::load_num_glyphs () const +{ + unsigned ret = 0; + +#ifndef HB_NO_BEYOND_64K + ret = hb_max (ret, load_num_glyphs_from_loca (this)); +#endif + + ret = hb_max (ret, load_num_glyphs_from_maxp (this)); + + num_glyphs = ret; + return ret; +} + +unsigned int +hb_face_t::load_upem () const +{ + unsigned int ret = table.head->get_upem (); + upem = ret; + return ret; +} + + +/* hb_user_data_array_t */ + +bool +hb_user_data_array_t::set (hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy, + hb_bool_t replace) +{ + if (!key) + return false; + + if (replace) { + if (!data && !destroy) { + items.remove (key, lock); + return true; + } + } + hb_user_data_item_t item = {key, data, destroy}; + bool ret = !!items.replace_or_insert (item, lock, (bool) replace); + + return ret; +} + +void * +hb_user_data_array_t::get (hb_user_data_key_t *key) +{ + hb_user_data_item_t item = {nullptr, nullptr, nullptr}; + + return items.find (key, &item, lock) ? item.data : nullptr; +} + + +#endif diff --git a/libs/harfbuzz/src/hb-string-array.hh b/libs/harfbuzz/src/hb-string-array.hh new file mode 100644 index 00000000..e7ac1192 --- /dev/null +++ b/libs/harfbuzz/src/hb-string-array.hh @@ -0,0 +1,85 @@ +/* + * Copyright © 2017 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 HB_STRING_ARRAY_HH +#if 0 /* Make checks happy. */ +#define HB_STRING_ARRAY_HH +#endif + +#include "hb.hh" + +/* Based on Bruno Haible's code in Appendix B of Ulrich Drepper's dsohowto.pdf: + * https://software.intel.com/sites/default/files/m/a/1/e/dsohowto.pdf */ + +#define HB_STRING_ARRAY_TYPE_NAME HB_PASTE(HB_STRING_ARRAY_NAME, _msgstr_t) +#define HB_STRING_ARRAY_POOL_NAME HB_PASTE(HB_STRING_ARRAY_NAME, _msgstr) +#define HB_STRING_ARRAY_OFFS_NAME HB_PASTE(HB_STRING_ARRAY_NAME, _msgidx) +#define HB_STRING_ARRAY_LENG_NAME HB_PASTE(HB_STRING_ARRAY_NAME, _length) + +static const union HB_STRING_ARRAY_TYPE_NAME { + struct { +/* I like to avoid storing the nul-termination byte since we don't need it, + * but C++ does not allow that. + * https://stackoverflow.com/q/28433862 + */ +#define _S(s) char HB_PASTE (str, __LINE__)[sizeof (s)]; +#include HB_STRING_ARRAY_LIST +#undef _S + } st; + char str[HB_VAR_ARRAY]; +} +HB_STRING_ARRAY_POOL_NAME = +{ + { +#define _S(s) s, +#include HB_STRING_ARRAY_LIST +#undef _S + } +}; +static const unsigned int HB_STRING_ARRAY_OFFS_NAME[] = +{ +#define _S(s) offsetof (union HB_STRING_ARRAY_TYPE_NAME, st.HB_PASTE(str, __LINE__)), +#include HB_STRING_ARRAY_LIST +#undef _S + sizeof (HB_STRING_ARRAY_TYPE_NAME) +}; + +static const unsigned int HB_STRING_ARRAY_LENG_NAME = ARRAY_LENGTH_CONST (HB_STRING_ARRAY_OFFS_NAME) - 1; + +static inline hb_bytes_t +HB_STRING_ARRAY_NAME (unsigned int i) +{ + assert (i < ARRAY_LENGTH (HB_STRING_ARRAY_OFFS_NAME) - 1); + return hb_bytes_t (HB_STRING_ARRAY_POOL_NAME.str + HB_STRING_ARRAY_OFFS_NAME[i], + HB_STRING_ARRAY_OFFS_NAME[i + 1] - HB_STRING_ARRAY_OFFS_NAME[i] - 1); +} + +#undef HB_STRING_ARRAY_TYPE_NAME +#undef HB_STRING_ARRAY_POOL_NAME +#undef HB_STRING_ARRAY_OFFS_NAME +#undef HB_STRING_ARRAY_LENG_NAME + +#endif /* HB_STRING_ARRAY_HH */ diff --git a/libs/harfbuzz/src/hb-style.cc b/libs/harfbuzz/src/hb-style.cc new file mode 100644 index 00000000..c7d7d713 --- /dev/null +++ b/libs/harfbuzz/src/hb-style.cc @@ -0,0 +1,134 @@ +/* + * Copyright © 2019 Ebrahim Byagowi + * + * 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. + */ + +#include "hb.hh" + +#ifndef HB_NO_STYLE + +#include "hb-ot-var-avar-table.hh" +#include "hb-ot-var-fvar-table.hh" +#include "hb-ot-stat-table.hh" +#include "hb-ot-os2-table.hh" +#include "hb-ot-head-table.hh" +#include "hb-ot-post-table.hh" +#include "hb-ot-face.hh" + +/** + * SECTION:hb-style + * @title: hb-style + * @short_description: Font Styles + * @include: hb.h + * + * Functions for fetching style information from fonts. + **/ + +static inline float +_hb_angle_to_ratio (float a) +{ + return tanf (a * float (-M_PI / 180.)); +} + +static inline float +_hb_ratio_to_angle (float r) +{ + return atanf (r) * float (-180. / M_PI); +} + +/** + * hb_style_get_value: + * @font: a #hb_font_t object. + * @style_tag: a style tag. + * + * Searches variation axes of a #hb_font_t object for a specific axis first, + * if not set, then tries to get default style values from different + * tables of the font. + * + * Returns: Corresponding axis or default value to a style tag. + * + * Since: 3.0.0 + **/ +float +hb_style_get_value (hb_font_t *font, hb_style_tag_t style_tag) +{ + if (unlikely (style_tag == HB_STYLE_TAG_SLANT_RATIO)) + return _hb_angle_to_ratio (hb_style_get_value (font, HB_STYLE_TAG_SLANT_ANGLE)); + + hb_face_t *face = font->face; + +#ifndef HB_NO_VAR + hb_ot_var_axis_info_t axis; + if (hb_ot_var_find_axis_info (face, style_tag, &axis)) + { + if (axis.axis_index < font->num_coords) return font->design_coords[axis.axis_index]; + /* If a face is variable, fvar's default_value is better than STAT records */ + return axis.default_value; + } +#endif + + if (style_tag == HB_STYLE_TAG_OPTICAL_SIZE && font->ptem) + return font->ptem; + + /* STAT */ + float value; + if (face->table.STAT->get_value (style_tag, &value)) + return value; + + switch ((unsigned) style_tag) + { + case HB_STYLE_TAG_ITALIC: + return face->table.OS2->is_italic () || face->table.head->is_italic () ? 1 : 0; + case HB_STYLE_TAG_OPTICAL_SIZE: + { + unsigned int lower, design, upper; + return face->table.OS2->v5 ().get_optical_size (&lower, &upper) + ? (float) (lower + upper) / 2.f + : hb_ot_layout_get_size_params (face, &design, nullptr, nullptr, nullptr, nullptr) + ? design / 10.f + : 12.f; + } + case HB_STYLE_TAG_SLANT_ANGLE: + { + float angle = face->table.post->table->italicAngle.to_float (); + + if (font->slant) + angle = _hb_ratio_to_angle (font->slant + _hb_angle_to_ratio (angle)); + + return angle; + } + case HB_STYLE_TAG_WIDTH: + return face->table.OS2->has_data () + ? face->table.OS2->get_width () + : (face->table.head->is_condensed () ? 75 : + face->table.head->is_expanded () ? 125 : + 100); + case HB_STYLE_TAG_WEIGHT: + return face->table.OS2->has_data () + ? face->table.OS2->usWeightClass + : (face->table.head->is_bold () ? 700 : 400); + default: + return 0; + } +} + +#endif diff --git a/libs/harfbuzz/src/hb-style.h b/libs/harfbuzz/src/hb-style.h new file mode 100644 index 00000000..d17d2daa --- /dev/null +++ b/libs/harfbuzz/src/hb-style.h @@ -0,0 +1,81 @@ +/* + * Copyright © 2019 Ebrahim Byagowi + * + * 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. + */ + +#if !defined(HB_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR) +#error "Include instead." +#endif + +#ifndef HB_STYLE_H +#define HB_STYLE_H + +#include "hb.h" + +HB_BEGIN_DECLS + +/** + * hb_style_tag_t: + * @HB_STYLE_TAG_ITALIC: Used to vary between non-italic and italic. + * A value of 0 can be interpreted as "Roman" (non-italic); a value of 1 can + * be interpreted as (fully) italic. + * @HB_STYLE_TAG_OPTICAL_SIZE: Used to vary design to suit different text sizes. + * Non-zero. Values can be interpreted as text size, in points. + * @HB_STYLE_TAG_SLANT_ANGLE: Used to vary between upright and slanted text. Values + * must be greater than -90 and less than +90. Values can be interpreted as + * the angle, in counter-clockwise degrees, of oblique slant from whatever the + * designer considers to be upright for that font design. Typical right-leaning + * Italic fonts have a negative slant angle (typically around -12) + * @HB_STYLE_TAG_SLANT_RATIO: same as @HB_STYLE_TAG_SLANT_ANGLE expression as ratio. + * Typical right-leaning Italic fonts have a positive slant ratio (typically around 0.2) + * @HB_STYLE_TAG_WIDTH: Used to vary width of text from narrower to wider. + * Non-zero. Values can be interpreted as a percentage of whatever the font + * designer considers “normal width” for that font design. + * @HB_STYLE_TAG_WEIGHT: Used to vary stroke thicknesses or other design details + * to give variation from lighter to blacker. Values can be interpreted in direct + * comparison to values for usWeightClass in the OS/2 table, + * or the CSS font-weight property. + * + * Defined by [OpenType Design-Variation Axis Tag Registry](https://docs.microsoft.com/en-us/typography/opentype/spec/dvaraxisreg). + * + * Since: 3.0.0 + **/ +typedef enum +{ + HB_STYLE_TAG_ITALIC = HB_TAG ('i','t','a','l'), + HB_STYLE_TAG_OPTICAL_SIZE = HB_TAG ('o','p','s','z'), + HB_STYLE_TAG_SLANT_ANGLE = HB_TAG ('s','l','n','t'), + HB_STYLE_TAG_SLANT_RATIO = HB_TAG ('S','l','n','t'), + HB_STYLE_TAG_WIDTH = HB_TAG ('w','d','t','h'), + HB_STYLE_TAG_WEIGHT = HB_TAG ('w','g','h','t'), + + /*< private >*/ + _HB_STYLE_TAG_MAX_VALUE = HB_TAG_MAX_SIGNED /*< skip >*/ +} hb_style_tag_t; + + +HB_EXTERN float +hb_style_get_value (hb_font_t *font, hb_style_tag_t style_tag); + +HB_END_DECLS + +#endif /* HB_STYLE_H */ diff --git a/libs/harfbuzz/src/hb-subset-accelerator.hh b/libs/harfbuzz/src/hb-subset-accelerator.hh new file mode 100644 index 00000000..34bd0534 --- /dev/null +++ b/libs/harfbuzz/src/hb-subset-accelerator.hh @@ -0,0 +1,76 @@ +/* + * Copyright © 2022 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): Garret Rieger + */ + +#ifndef HB_SUBSET_ACCELERATOR_HH +#define HB_SUBSET_ACCELERATOR_HH + + +#include "hb.hh" + +#include "hb-map.hh" +#include "hb-set.hh" + +struct hb_subset_accelerator_t +{ + static hb_user_data_key_t* user_data_key() + { + static hb_user_data_key_t key; + return &key; + } + + static hb_subset_accelerator_t* create(const hb_map_t& unicode_to_gid_, + const hb_set_t& unicodes_) { + hb_subset_accelerator_t* accel = + (hb_subset_accelerator_t*) hb_malloc (sizeof(hb_subset_accelerator_t)); + new (accel) hb_subset_accelerator_t (unicode_to_gid_, unicodes_); + return accel; + } + + static void destroy(void* value) { + if (!value) return; + + hb_subset_accelerator_t* accel = (hb_subset_accelerator_t*) value; + accel->~hb_subset_accelerator_t (); + hb_free (accel); + } + + hb_subset_accelerator_t(const hb_map_t& unicode_to_gid_, + const hb_set_t& unicodes_) + : unicode_to_gid(unicode_to_gid_), unicodes(unicodes_) {} + + const hb_map_t unicode_to_gid; + const hb_set_t unicodes; + // TODO(garretrieger): cumulative glyf checksum map + // TODO(garretrieger): sanitized table cache. + + bool in_error () const + { + return unicode_to_gid.in_error() || unicodes.in_error (); + } +}; + + +#endif /* HB_SUBSET_ACCELERATOR_HH */ diff --git a/libs/harfbuzz/src/hb-subset-cff-common.cc b/libs/harfbuzz/src/hb-subset-cff-common.cc new file mode 100644 index 00000000..711b2236 --- /dev/null +++ b/libs/harfbuzz/src/hb-subset-cff-common.cc @@ -0,0 +1,223 @@ +/* + * Copyright © 2018 Adobe 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. + * + * Adobe Author(s): Michiharu Ariza + */ + +#include "hb.hh" + +#ifndef HB_NO_SUBSET_CFF + +#include "hb-ot-cff-common.hh" +#include "hb-ot-cff2-table.hh" +#include "hb-subset-cff-common.hh" + +/* Disable FDSelect format 0 for compatibility with fonttools which doesn't seem choose it. + * Rarely any/much smaller than format 3 anyway. */ +#define CFF_SERIALIZE_FDSELECT_0 0 + +using namespace CFF; + + +/* Determine an optimal FDSelect format according to a provided plan. + * + * Return value: FDSelect format, size, and ranges for the most compact subset FDSelect + * along with a font index remapping table + */ + +bool +hb_plan_subset_cff_fdselect (const hb_subset_plan_t *plan, + unsigned int fdCount, + const FDSelect &src, /* IN */ + unsigned int &subset_fd_count /* OUT */, + unsigned int &subset_fdselect_size /* OUT */, + unsigned int &subset_fdselect_format /* OUT */, + hb_vector_t &fdselect_ranges /* OUT */, + hb_inc_bimap_t &fdmap /* OUT */) +{ + subset_fd_count = 0; + subset_fdselect_size = 0; + subset_fdselect_format = 0; + unsigned int num_ranges = 0; + + unsigned int subset_num_glyphs = plan->num_output_glyphs (); + if (subset_num_glyphs == 0) + return true; + + { + /* use hb_set to determine the subset of font dicts */ + hb_set_t *set = hb_set_create (); + if (unlikely (set == &Null (hb_set_t))) return false; + hb_codepoint_t prev_fd = CFF_UNDEF_CODE; + for (hb_codepoint_t i = 0; i < subset_num_glyphs; i++) + { + hb_codepoint_t glyph; + hb_codepoint_t fd; + if (!plan->old_gid_for_new_gid (i, &glyph)) + { + /* fonttools retains FDSelect & font dicts for missing glyphs. do the same */ + glyph = i; + } + fd = src.get_fd (glyph); + set->add (fd); + + if (fd != prev_fd) + { + num_ranges++; + prev_fd = fd; + code_pair_t pair = { fd, i }; + fdselect_ranges.push (pair); + } + } + + subset_fd_count = set->get_population (); + if (subset_fd_count == fdCount) + { + /* all font dicts belong to the subset. no need to subset FDSelect & FDArray */ + fdmap.identity (fdCount); + hb_set_destroy (set); + } + else + { + /* create a fdmap */ + fdmap.reset (); + + hb_codepoint_t fd = CFF_UNDEF_CODE; + while (set->next (&fd)) + fdmap.add (fd); + hb_set_destroy (set); + if (unlikely (fdmap.get_population () != subset_fd_count)) + return false; + } + + /* update each font dict index stored as "code" in fdselect_ranges */ + for (unsigned int i = 0; i < fdselect_ranges.length; i++) + fdselect_ranges[i].code = fdmap[fdselect_ranges[i].code]; + } + + /* determine which FDSelect format is most compact */ + if (subset_fd_count > 0xFF) + { + if (unlikely (src.format != 4)) + return false; + subset_fdselect_format = 4; + subset_fdselect_size = FDSelect::min_size + FDSelect4::min_size + FDSelect4_Range::static_size * num_ranges + HBUINT32::static_size; + } + else + { +#if CFF_SERIALIZE_FDSELECT_0 + unsigned int format0_size = FDSelect::min_size + FDSelect0::min_size + HBUINT8::static_size * subset_num_glyphs; +#endif + unsigned int format3_size = FDSelect::min_size + FDSelect3::min_size + FDSelect3_Range::static_size * num_ranges + HBUINT16::static_size; + +#if CFF_SERIALIZE_FDSELECT_0 + if (format0_size <= format3_size) + { + // subset_fdselect_format = 0; + subset_fdselect_size = format0_size; + } + else +#endif + { + subset_fdselect_format = 3; + subset_fdselect_size = format3_size; + } + } + + return true; +} + +template +static inline bool +serialize_fdselect_3_4 (hb_serialize_context_t *c, + const unsigned int num_glyphs, + const FDSelect &src, + unsigned int size, + const hb_vector_t &fdselect_ranges) +{ + TRACE_SERIALIZE (this); + FDSELECT3_4 *p = c->allocate_size (size); + if (unlikely (!p)) return_trace (false); + p->nRanges () = fdselect_ranges.length; + for (unsigned int i = 0; i < fdselect_ranges.length; i++) + { + p->ranges[i].first = fdselect_ranges[i].glyph; + p->ranges[i].fd = fdselect_ranges[i].code; + } + p->sentinel () = num_glyphs; + return_trace (true); +} + +/* Serialize a subset FDSelect format planned above. */ +bool +hb_serialize_cff_fdselect (hb_serialize_context_t *c, + const unsigned int num_glyphs, + const FDSelect &src, + unsigned int fd_count, + unsigned int fdselect_format, + unsigned int size, + const hb_vector_t &fdselect_ranges) +{ + TRACE_SERIALIZE (this); + FDSelect *p = c->allocate_min (); + if (unlikely (!p)) return_trace (false); + p->format = fdselect_format; + size -= FDSelect::min_size; + + switch (fdselect_format) + { +#if CFF_SERIALIZE_FDSELECT_0 + case 0: + { + FDSelect0 *p = c->allocate_size (size); + if (unlikely (!p)) return_trace (false); + unsigned int range_index = 0; + unsigned int fd = fdselect_ranges[range_index++].code; + for (unsigned int i = 0; i < num_glyphs; i++) + { + if ((range_index < fdselect_ranges.len) && + (i >= fdselect_ranges[range_index].glyph)) + { + fd = fdselect_ranges[range_index++].code; + } + p->fds[i] = fd; + } + return_trace (true); + } +#endif /* CFF_SERIALIZE_FDSELECT_0 */ + + case 3: + return serialize_fdselect_3_4 (c, num_glyphs, src, + size, fdselect_ranges); + + case 4: + return serialize_fdselect_3_4 (c, num_glyphs, src, + size, fdselect_ranges); + + default: + return_trace (false); + } +} + + +#endif diff --git a/libs/harfbuzz/src/hb-subset-cff-common.hh b/libs/harfbuzz/src/hb-subset-cff-common.hh new file mode 100644 index 00000000..bb9f27ee --- /dev/null +++ b/libs/harfbuzz/src/hb-subset-cff-common.hh @@ -0,0 +1,931 @@ +/* + * Copyright © 2018 Adobe 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. + * + * Adobe Author(s): Michiharu Ariza + */ + +#ifndef HB_SUBSET_CFF_COMMON_HH +#define HB_SUBSET_CFF_COMMON_HH + +#include "hb.hh" + +#include "hb-subset-plan.hh" +#include "hb-cff-interp-cs-common.hh" + +namespace CFF { + +/* Used for writing a temporary charstring */ +struct str_encoder_t +{ + str_encoder_t (str_buff_t &buff_) + : buff (buff_), error (false) {} + + void reset () { buff.reset (); } + + void encode_byte (unsigned char b) + { + buff.push (b); + if (unlikely (buff.in_error ())) + set_error (); + } + + void encode_int (int v) + { + if ((-1131 <= v) && (v <= 1131)) + { + if ((-107 <= v) && (v <= 107)) + encode_byte (v + 139); + else if (v > 0) + { + v -= 108; + encode_byte ((v >> 8) + OpCode_TwoBytePosInt0); + encode_byte (v & 0xFF); + } + else + { + v = -v - 108; + encode_byte ((v >> 8) + OpCode_TwoByteNegInt0); + encode_byte (v & 0xFF); + } + } + else + { + if (unlikely (v < -32768)) + v = -32768; + else if (unlikely (v > 32767)) + v = 32767; + encode_byte (OpCode_shortint); + encode_byte ((v >> 8) & 0xFF); + encode_byte (v & 0xFF); + } + } + + void encode_num (const number_t& n) + { + if (n.in_int_range ()) + { + encode_int (n.to_int ()); + } + else + { + int32_t v = n.to_fixed (); + encode_byte (OpCode_fixedcs); + encode_byte ((v >> 24) & 0xFF); + encode_byte ((v >> 16) & 0xFF); + encode_byte ((v >> 8) & 0xFF); + encode_byte (v & 0xFF); + } + } + + void encode_op (op_code_t op) + { + if (Is_OpCode_ESC (op)) + { + encode_byte (OpCode_escape); + encode_byte (Unmake_OpCode_ESC (op)); + } + else + encode_byte (op); + } + + void copy_str (const hb_ubytes_t &str) + { + unsigned int offset = buff.length; + /* Manually resize buffer since faster. */ + if ((signed) (buff.length + str.length) <= buff.allocated) + buff.length += str.length; + else if (unlikely (!buff.resize (offset + str.length))) + { + set_error (); + return; + } + memcpy (buff.arrayZ + offset, &str[0], str.length); + } + + bool is_error () const { return error; } + + protected: + void set_error () { error = true; } + + str_buff_t &buff; + bool error; +}; + +struct cff_sub_table_info_t { + cff_sub_table_info_t () + : fd_array_link (0), + char_strings_link (0) + { + fd_select.init (); + } + + table_info_t fd_select; + objidx_t fd_array_link; + objidx_t char_strings_link; +}; + +template +struct cff_top_dict_op_serializer_t : op_serializer_t +{ + bool serialize (hb_serialize_context_t *c, + const OPSTR &opstr, + const cff_sub_table_info_t &info) const + { + TRACE_SERIALIZE (this); + + switch (opstr.op) + { + case OpCode_CharStrings: + return_trace (FontDict::serialize_link4_op(c, opstr.op, info.char_strings_link, whence_t::Absolute)); + + case OpCode_FDArray: + return_trace (FontDict::serialize_link4_op(c, opstr.op, info.fd_array_link, whence_t::Absolute)); + + case OpCode_FDSelect: + return_trace (FontDict::serialize_link4_op(c, opstr.op, info.fd_select.link, whence_t::Absolute)); + + default: + return_trace (copy_opstr (c, opstr)); + } + return_trace (true); + } +}; + +struct cff_font_dict_op_serializer_t : op_serializer_t +{ + bool serialize (hb_serialize_context_t *c, + const op_str_t &opstr, + const table_info_t &privateDictInfo) const + { + TRACE_SERIALIZE (this); + + if (opstr.op == OpCode_Private) + { + /* serialize the private dict size & offset as 2-byte & 4-byte integers */ + return_trace (UnsizedByteStr::serialize_int2 (c, privateDictInfo.size) && + Dict::serialize_link4_op (c, opstr.op, privateDictInfo.link, whence_t::Absolute)); + } + else + { + HBUINT8 *d = c->allocate_size (opstr.str.length); + if (unlikely (!d)) return_trace (false); + memcpy (d, &opstr.str[0], opstr.str.length); + } + return_trace (true); + } +}; + +struct cff_private_dict_op_serializer_t : op_serializer_t +{ + cff_private_dict_op_serializer_t (bool desubroutinize_, bool drop_hints_) + : desubroutinize (desubroutinize_), drop_hints (drop_hints_) {} + + bool serialize (hb_serialize_context_t *c, + const op_str_t &opstr, + objidx_t subrs_link) const + { + TRACE_SERIALIZE (this); + + if (drop_hints && dict_opset_t::is_hint_op (opstr.op)) + return true; + if (opstr.op == OpCode_Subrs) + { + if (desubroutinize || !subrs_link) + return_trace (true); + else + return_trace (FontDict::serialize_link2_op (c, opstr.op, subrs_link)); + } + else + return_trace (copy_opstr (c, opstr)); + } + + protected: + const bool desubroutinize; + const bool drop_hints; +}; + +struct flatten_param_t +{ + str_buff_t &flatStr; + bool drop_hints; +}; + +template +struct subr_flattener_t +{ + subr_flattener_t (const ACC &acc_, + const hb_subset_plan_t *plan_) + : acc (acc_), plan (plan_) {} + + bool flatten (str_buff_vec_t &flat_charstrings) + { + if (!flat_charstrings.resize (plan->num_output_glyphs ())) + return false; + for (unsigned int i = 0; i < plan->num_output_glyphs (); i++) + flat_charstrings[i].init (); + for (unsigned int i = 0; i < plan->num_output_glyphs (); i++) + { + hb_codepoint_t glyph; + if (!plan->old_gid_for_new_gid (i, &glyph)) + { + /* add an endchar only charstring for a missing glyph if CFF1 */ + if (endchar_op != OpCode_Invalid) flat_charstrings[i].push (endchar_op); + continue; + } + const hb_ubytes_t str = (*acc.charStrings)[glyph]; + unsigned int fd = acc.fdSelect->get_fd (glyph); + if (unlikely (fd >= acc.fdCount)) + return false; + ENV env (str, acc, fd); + cs_interpreter_t interp (env); + flatten_param_t param = { + flat_charstrings[i], + (bool) (plan->flags & HB_SUBSET_FLAGS_NO_HINTING) + }; + if (unlikely (!interp.interpret (param))) + return false; + } + return true; + } + + const ACC &acc; + const hb_subset_plan_t *plan; +}; + +struct subr_closures_t +{ + subr_closures_t (unsigned int fd_count) : valid (false), global_closure (), local_closures () + { + valid = true; + if (!local_closures.resize (fd_count)) + valid = false; + } + + void reset () + { + global_closure.clear(); + for (unsigned int i = 0; i < local_closures.length; i++) + local_closures[i].clear(); + } + + bool is_valid () const { return valid; } + bool valid; + hb_set_t global_closure; + hb_vector_t local_closures; +}; + +struct parsed_cs_op_t : op_str_t +{ + void init (unsigned int subr_num_ = 0) + { + subr_num = subr_num_; + drop_flag = false; + keep_flag = false; + skip_flag = false; + } + + bool for_drop () const { return drop_flag; } + void set_drop () { if (!for_keep ()) drop_flag = true; } + + bool for_keep () const { return keep_flag; } + void set_keep () { keep_flag = true; } + + bool for_skip () const { return skip_flag; } + void set_skip () { skip_flag = true; } + + unsigned int subr_num; + + protected: + bool drop_flag; + bool keep_flag; + bool skip_flag; +}; + +struct parsed_cs_str_t : parsed_values_t +{ + void init () + { + SUPER::init (); + parsed = false; + hint_dropped = false; + has_prefix_ = false; + } + + void add_op (op_code_t op, const byte_str_ref_t& str_ref) + { + if (!is_parsed ()) + SUPER::add_op (op, str_ref); + } + + void add_call_op (op_code_t op, const byte_str_ref_t& str_ref, unsigned int subr_num) + { + if (!is_parsed ()) + { + unsigned int parsed_len = get_count (); + if (likely (parsed_len > 0)) + values[parsed_len-1].set_skip (); + + parsed_cs_op_t val; + val.init (subr_num); + SUPER::add_op (op, str_ref, val); + } + } + + void set_prefix (const number_t &num, op_code_t op = OpCode_Invalid) + { + has_prefix_ = true; + prefix_op_ = op; + prefix_num_ = num; + } + + bool at_end (unsigned int pos) const + { + return ((pos + 1 >= values.length) /* CFF2 */ + || (values[pos + 1].op == OpCode_return)); + } + + bool is_parsed () const { return parsed; } + void set_parsed () { parsed = true; } + + bool is_hint_dropped () const { return hint_dropped; } + void set_hint_dropped () { hint_dropped = true; } + + bool is_vsindex_dropped () const { return vsindex_dropped; } + void set_vsindex_dropped () { vsindex_dropped = true; } + + bool has_prefix () const { return has_prefix_; } + op_code_t prefix_op () const { return prefix_op_; } + const number_t &prefix_num () const { return prefix_num_; } + + protected: + bool parsed; + bool hint_dropped; + bool vsindex_dropped; + bool has_prefix_; + op_code_t prefix_op_; + number_t prefix_num_; + + private: + typedef parsed_values_t SUPER; +}; + +struct parsed_cs_str_vec_t : hb_vector_t +{ + private: + typedef hb_vector_t SUPER; +}; + +struct subr_subset_param_t +{ + subr_subset_param_t (parsed_cs_str_t *parsed_charstring_, + parsed_cs_str_vec_t *parsed_global_subrs_, + parsed_cs_str_vec_t *parsed_local_subrs_, + hb_set_t *global_closure_, + hb_set_t *local_closure_, + bool drop_hints_) : + current_parsed_str (parsed_charstring_), + parsed_charstring (parsed_charstring_), + parsed_global_subrs (parsed_global_subrs_), + parsed_local_subrs (parsed_local_subrs_), + global_closure (global_closure_), + local_closure (local_closure_), + drop_hints (drop_hints_) {} + + parsed_cs_str_t *get_parsed_str_for_context (call_context_t &context) + { + switch (context.type) + { + case CSType_CharString: + return parsed_charstring; + + case CSType_LocalSubr: + if (likely (context.subr_num < parsed_local_subrs->length)) + return &(*parsed_local_subrs)[context.subr_num]; + break; + + case CSType_GlobalSubr: + if (likely (context.subr_num < parsed_global_subrs->length)) + return &(*parsed_global_subrs)[context.subr_num]; + break; + } + return nullptr; + } + + template + void set_current_str (ENV &env, bool calling) + { + parsed_cs_str_t *parsed_str = get_parsed_str_for_context (env.context); + if (unlikely (!parsed_str)) + { + env.set_error (); + return; + } + /* If the called subroutine is parsed partially but not completely yet, + * it must be because we are calling it recursively. + * Handle it as an error. */ + if (unlikely (calling && !parsed_str->is_parsed () && (parsed_str->values.length > 0))) + env.set_error (); + else + current_parsed_str = parsed_str; + } + + parsed_cs_str_t *current_parsed_str; + + parsed_cs_str_t *parsed_charstring; + parsed_cs_str_vec_t *parsed_global_subrs; + parsed_cs_str_vec_t *parsed_local_subrs; + hb_set_t *global_closure; + hb_set_t *local_closure; + bool drop_hints; +}; + +struct subr_remap_t : hb_inc_bimap_t +{ + void create (const hb_set_t *closure) + { + /* create a remapping of subroutine numbers from old to new. + * no optimization based on usage counts. fonttools doesn't appear doing that either. + */ + + resize (closure->get_population ()); + hb_codepoint_t old_num = HB_SET_VALUE_INVALID; + while (hb_set_next (closure, &old_num)) + add (old_num); + + if (get_population () < 1240) + bias = 107; + else if (get_population () < 33900) + bias = 1131; + else + bias = 32768; + } + + int biased_num (unsigned int old_num) const + { + hb_codepoint_t new_num = get (old_num); + return (int)new_num - bias; + } + + protected: + int bias; +}; + +struct subr_remaps_t +{ + subr_remaps_t (unsigned int fdCount) + { + local_remaps.resize (fdCount); + } + + bool in_error() + { + return local_remaps.in_error (); + } + + void create (subr_closures_t& closures) + { + global_remap.create (&closures.global_closure); + for (unsigned int i = 0; i < local_remaps.length; i++) + local_remaps[i].create (&closures.local_closures[i]); + } + + subr_remap_t global_remap; + hb_vector_t local_remaps; +}; + +template +struct subr_subsetter_t +{ + subr_subsetter_t (ACC &acc_, const hb_subset_plan_t *plan_) + : acc (acc_), plan (plan_), closures(acc_.fdCount), remaps(acc_.fdCount) + {} + + /* Subroutine subsetting with --no-desubroutinize runs in phases: + * + * 1. execute charstrings/subroutines to determine subroutine closures + * 2. parse out all operators and numbers + * 3. mark hint operators and operands for removal if --no-hinting + * 4. re-encode all charstrings and subroutines with new subroutine numbers + * + * Phases #1 and #2 are done at the same time in collect_subrs (). + * Phase #3 walks charstrings/subroutines forward then backward (hence parsing required), + * because we can't tell if a number belongs to a hint op until we see the first moveto. + * + * Assumption: a callsubr/callgsubr operator must immediately follow a (biased) subroutine number + * within the same charstring/subroutine, e.g., not split across a charstring and a subroutine. + */ + bool subset (void) + { + parsed_charstrings.resize (plan->num_output_glyphs ()); + parsed_global_subrs.resize (acc.globalSubrs->count); + + if (unlikely (remaps.in_error() + || parsed_charstrings.in_error () + || parsed_global_subrs.in_error ())) { + return false; + } + + if (unlikely (!parsed_local_subrs.resize (acc.fdCount))) return false; + + for (unsigned int i = 0; i < acc.fdCount; i++) + { + parsed_local_subrs[i].resize (acc.privateDicts[i].localSubrs->count); + if (unlikely (parsed_local_subrs[i].in_error ())) return false; + } + if (unlikely (!closures.valid)) + return false; + + /* phase 1 & 2 */ + for (unsigned int i = 0; i < plan->num_output_glyphs (); i++) + { + hb_codepoint_t glyph; + if (!plan->old_gid_for_new_gid (i, &glyph)) + continue; + const hb_ubytes_t str = (*acc.charStrings)[glyph]; + unsigned int fd = acc.fdSelect->get_fd (glyph); + if (unlikely (fd >= acc.fdCount)) + return false; + + ENV env (str, acc, fd); + cs_interpreter_t interp (env); + + parsed_charstrings[i].alloc (str.length); + subr_subset_param_t param (&parsed_charstrings[i], + &parsed_global_subrs, + &parsed_local_subrs[fd], + &closures.global_closure, + &closures.local_closures[fd], + plan->flags & HB_SUBSET_FLAGS_NO_HINTING); + + if (unlikely (!interp.interpret (param))) + return false; + + /* complete parsed string esp. copy CFF1 width or CFF2 vsindex to the parsed charstring for encoding */ + SUBSETTER::complete_parsed_str (interp.env, param, parsed_charstrings[i]); + } + + if (plan->flags & HB_SUBSET_FLAGS_NO_HINTING) + { + /* mark hint ops and arguments for drop */ + for (unsigned int i = 0; i < plan->num_output_glyphs (); i++) + { + hb_codepoint_t glyph; + if (!plan->old_gid_for_new_gid (i, &glyph)) + continue; + unsigned int fd = acc.fdSelect->get_fd (glyph); + if (unlikely (fd >= acc.fdCount)) + return false; + subr_subset_param_t param (&parsed_charstrings[i], + &parsed_global_subrs, + &parsed_local_subrs[fd], + &closures.global_closure, + &closures.local_closures[fd], + plan->flags & HB_SUBSET_FLAGS_NO_HINTING); + + drop_hints_param_t drop; + if (drop_hints_in_str (parsed_charstrings[i], param, drop)) + { + parsed_charstrings[i].set_hint_dropped (); + if (drop.vsindex_dropped) + parsed_charstrings[i].set_vsindex_dropped (); + } + } + + /* after dropping hints recreate closures of actually used subrs */ + closures.reset (); + for (unsigned int i = 0; i < plan->num_output_glyphs (); i++) + { + hb_codepoint_t glyph; + if (!plan->old_gid_for_new_gid (i, &glyph)) + continue; + unsigned int fd = acc.fdSelect->get_fd (glyph); + if (unlikely (fd >= acc.fdCount)) + return false; + subr_subset_param_t param (&parsed_charstrings[i], + &parsed_global_subrs, + &parsed_local_subrs[fd], + &closures.global_closure, + &closures.local_closures[fd], + plan->flags & HB_SUBSET_FLAGS_NO_HINTING); + collect_subr_refs_in_str (parsed_charstrings[i], param); + } + } + + remaps.create (closures); + + return true; + } + + bool encode_charstrings (str_buff_vec_t &buffArray) const + { + if (unlikely (!buffArray.resize (plan->num_output_glyphs ()))) + return false; + for (unsigned int i = 0; i < plan->num_output_glyphs (); i++) + { + hb_codepoint_t glyph; + if (!plan->old_gid_for_new_gid (i, &glyph)) + { + /* add an endchar only charstring for a missing glyph if CFF1 */ + if (endchar_op != OpCode_Invalid) buffArray[i].push (endchar_op); + continue; + } + unsigned int fd = acc.fdSelect->get_fd (glyph); + if (unlikely (fd >= acc.fdCount)) + return false; + if (unlikely (!encode_str (parsed_charstrings[i], fd, buffArray[i]))) + return false; + } + return true; + } + + bool encode_subrs (const parsed_cs_str_vec_t &subrs, const subr_remap_t& remap, unsigned int fd, str_buff_vec_t &buffArray) const + { + unsigned int count = remap.get_population (); + + if (unlikely (!buffArray.resize (count))) + return false; + for (unsigned int old_num = 0; old_num < subrs.length; old_num++) + { + hb_codepoint_t new_num = remap[old_num]; + if (new_num != CFF_UNDEF_CODE) + { + if (unlikely (!encode_str (subrs[old_num], fd, buffArray[new_num]))) + return false; + } + } + return true; + } + + bool encode_globalsubrs (str_buff_vec_t &buffArray) + { + return encode_subrs (parsed_global_subrs, remaps.global_remap, 0, buffArray); + } + + bool encode_localsubrs (unsigned int fd, str_buff_vec_t &buffArray) const + { + return encode_subrs (parsed_local_subrs[fd], remaps.local_remaps[fd], fd, buffArray); + } + + protected: + struct drop_hints_param_t + { + drop_hints_param_t () + : seen_moveto (false), + ends_in_hint (false), + all_dropped (false), + vsindex_dropped (false) {} + + bool seen_moveto; + bool ends_in_hint; + bool all_dropped; + bool vsindex_dropped; + }; + + bool drop_hints_in_subr (parsed_cs_str_t &str, unsigned int pos, + parsed_cs_str_vec_t &subrs, unsigned int subr_num, + const subr_subset_param_t ¶m, drop_hints_param_t &drop) + { + drop.ends_in_hint = false; + bool has_hint = drop_hints_in_str (subrs[subr_num], param, drop); + + /* if this subr ends with a stem hint (i.e., not a number; potential argument for moveto), + * then this entire subroutine must be a hint. drop its call. */ + if (drop.ends_in_hint) + { + str.values[pos].set_drop (); + /* if this subr call is at the end of the parent subr, propagate the flag + * otherwise reset the flag */ + if (!str.at_end (pos)) + drop.ends_in_hint = false; + } + else if (drop.all_dropped) + { + str.values[pos].set_drop (); + } + + return has_hint; + } + + /* returns true if it sees a hint op before the first moveto */ + bool drop_hints_in_str (parsed_cs_str_t &str, const subr_subset_param_t ¶m, drop_hints_param_t &drop) + { + bool seen_hint = false; + + for (unsigned int pos = 0; pos < str.values.length; pos++) + { + bool has_hint = false; + switch (str.values[pos].op) + { + case OpCode_callsubr: + has_hint = drop_hints_in_subr (str, pos, + *param.parsed_local_subrs, str.values[pos].subr_num, + param, drop); + break; + + case OpCode_callgsubr: + has_hint = drop_hints_in_subr (str, pos, + *param.parsed_global_subrs, str.values[pos].subr_num, + param, drop); + break; + + case OpCode_rmoveto: + case OpCode_hmoveto: + case OpCode_vmoveto: + drop.seen_moveto = true; + break; + + case OpCode_hintmask: + case OpCode_cntrmask: + if (drop.seen_moveto) + { + str.values[pos].set_drop (); + break; + } + HB_FALLTHROUGH; + + case OpCode_hstemhm: + case OpCode_vstemhm: + case OpCode_hstem: + case OpCode_vstem: + has_hint = true; + str.values[pos].set_drop (); + if (str.at_end (pos)) + drop.ends_in_hint = true; + break; + + case OpCode_dotsection: + str.values[pos].set_drop (); + break; + + default: + /* NONE */ + break; + } + if (has_hint) + { + for (int i = pos - 1; i >= 0; i--) + { + parsed_cs_op_t &csop = str.values[(unsigned)i]; + if (csop.for_drop ()) + break; + csop.set_drop (); + if (csop.op == OpCode_vsindexcs) + drop.vsindex_dropped = true; + } + seen_hint |= has_hint; + } + } + + /* Raise all_dropped flag if all operators except return are dropped from a subr. + * It may happen even after seeing the first moveto if a subr contains + * only (usually one) hintmask operator, then calls to this subr can be dropped. + */ + drop.all_dropped = true; + for (unsigned int pos = 0; pos < str.values.length; pos++) + { + parsed_cs_op_t &csop = str.values[pos]; + if (csop.op == OpCode_return) + break; + if (!csop.for_drop ()) + { + drop.all_dropped = false; + break; + } + } + + return seen_hint; + } + + void collect_subr_refs_in_subr (parsed_cs_str_t &str, unsigned int pos, + unsigned int subr_num, parsed_cs_str_vec_t &subrs, + hb_set_t *closure, + const subr_subset_param_t ¶m) + { + closure->add (subr_num); + collect_subr_refs_in_str (subrs[subr_num], param); + } + + void collect_subr_refs_in_str (parsed_cs_str_t &str, const subr_subset_param_t ¶m) + { + for (unsigned int pos = 0; pos < str.values.length; pos++) + { + if (!str.values[pos].for_drop ()) + { + switch (str.values[pos].op) + { + case OpCode_callsubr: + collect_subr_refs_in_subr (str, pos, + str.values[pos].subr_num, *param.parsed_local_subrs, + param.local_closure, param); + break; + + case OpCode_callgsubr: + collect_subr_refs_in_subr (str, pos, + str.values[pos].subr_num, *param.parsed_global_subrs, + param.global_closure, param); + break; + + default: break; + } + } + } + } + + bool encode_str (const parsed_cs_str_t &str, const unsigned int fd, str_buff_t &buff) const + { + unsigned count = str.get_count (); + str_encoder_t encoder (buff); + encoder.reset (); + buff.alloc (count * 3); + /* if a prefix (CFF1 width or CFF2 vsindex) has been removed along with hints, + * re-insert it at the beginning of charstreing */ + if (str.has_prefix () && str.is_hint_dropped ()) + { + encoder.encode_num (str.prefix_num ()); + if (str.prefix_op () != OpCode_Invalid) + encoder.encode_op (str.prefix_op ()); + } + for (unsigned int i = 0; i < count; i++) + { + const parsed_cs_op_t &opstr = str.values[i]; + if (!opstr.for_drop () && !opstr.for_skip ()) + { + switch (opstr.op) + { + case OpCode_callsubr: + encoder.encode_int (remaps.local_remaps[fd].biased_num (opstr.subr_num)); + encoder.encode_op (OpCode_callsubr); + break; + + case OpCode_callgsubr: + encoder.encode_int (remaps.global_remap.biased_num (opstr.subr_num)); + encoder.encode_op (OpCode_callgsubr); + break; + + default: + encoder.copy_str (opstr.str); + break; + } + } + } + return !encoder.is_error (); + } + + protected: + const ACC &acc; + const hb_subset_plan_t *plan; + + subr_closures_t closures; + + parsed_cs_str_vec_t parsed_charstrings; + parsed_cs_str_vec_t parsed_global_subrs; + hb_vector_t parsed_local_subrs; + + subr_remaps_t remaps; + + private: + typedef typename SUBRS::count_type subr_count_type; +}; + +} /* namespace CFF */ + +HB_INTERNAL bool +hb_plan_subset_cff_fdselect (const hb_subset_plan_t *plan, + unsigned int fdCount, + const CFF::FDSelect &src, /* IN */ + unsigned int &subset_fd_count /* OUT */, + unsigned int &subset_fdselect_size /* OUT */, + unsigned int &subset_fdselect_format /* OUT */, + hb_vector_t &fdselect_ranges /* OUT */, + hb_inc_bimap_t &fdmap /* OUT */); + +HB_INTERNAL bool +hb_serialize_cff_fdselect (hb_serialize_context_t *c, + unsigned int num_glyphs, + const CFF::FDSelect &src, + unsigned int fd_count, + unsigned int fdselect_format, + unsigned int size, + const hb_vector_t &fdselect_ranges); + +#endif /* HB_SUBSET_CFF_COMMON_HH */ diff --git a/libs/harfbuzz/src/hb-subset-cff1.cc b/libs/harfbuzz/src/hb-subset-cff1.cc new file mode 100644 index 00000000..52bb13d3 --- /dev/null +++ b/libs/harfbuzz/src/hb-subset-cff1.cc @@ -0,0 +1,906 @@ +/* + * Copyright © 2018 Adobe 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. + * + * Adobe Author(s): Michiharu Ariza + */ + +#include "hb.hh" + +#ifndef HB_NO_SUBSET_CFF + +#include "hb-open-type.hh" +#include "hb-ot-cff1-table.hh" +#include "hb-set.h" +#include "hb-bimap.hh" +#include "hb-subset-cff1.hh" +#include "hb-subset-plan.hh" +#include "hb-subset-cff-common.hh" +#include "hb-cff1-interp-cs.hh" + +using namespace CFF; + +struct remap_sid_t : hb_inc_bimap_t +{ + unsigned int add (unsigned int sid) + { + if ((sid != CFF_UNDEF_SID) && !is_std_std (sid)) + return offset_sid (hb_inc_bimap_t::add (unoffset_sid (sid))); + else + return sid; + } + + unsigned int operator[] (unsigned int sid) const + { + if (is_std_std (sid) || (sid == CFF_UNDEF_SID)) + return sid; + else + return offset_sid (get (unoffset_sid (sid))); + } + + static const unsigned int num_std_strings = 391; + + static bool is_std_std (unsigned int sid) { return sid < num_std_strings; } + static unsigned int offset_sid (unsigned int sid) { return sid + num_std_strings; } + static unsigned int unoffset_sid (unsigned int sid) { return sid - num_std_strings; } +}; + +struct cff1_sub_table_info_t : cff_sub_table_info_t +{ + cff1_sub_table_info_t () + : cff_sub_table_info_t (), + encoding_link (0), + charset_link (0) + { + privateDictInfo.init (); + } + + objidx_t encoding_link; + objidx_t charset_link; + table_info_t privateDictInfo; +}; + +/* a copy of a parsed out cff1_top_dict_values_t augmented with additional operators */ +struct cff1_top_dict_values_mod_t : cff1_top_dict_values_t +{ + void init (const cff1_top_dict_values_t *base_= &Null (cff1_top_dict_values_t)) + { + SUPER::init (); + base = base_; + } + + void fini () { SUPER::fini (); } + + unsigned get_count () const { return base->get_count () + SUPER::get_count (); } + const cff1_top_dict_val_t &get_value (unsigned int i) const + { + if (i < base->get_count ()) + return (*base)[i]; + else + return SUPER::values[i - base->get_count ()]; + } + const cff1_top_dict_val_t &operator [] (unsigned int i) const { return get_value (i); } + + void reassignSIDs (const remap_sid_t& sidmap) + { + for (unsigned int i = 0; i < name_dict_values_t::ValCount; i++) + nameSIDs[i] = sidmap[base->nameSIDs[i]]; + } + + protected: + typedef cff1_top_dict_values_t SUPER; + const cff1_top_dict_values_t *base; +}; + +struct top_dict_modifiers_t +{ + top_dict_modifiers_t (const cff1_sub_table_info_t &info_, + const unsigned int (&nameSIDs_)[name_dict_values_t::ValCount]) + : info (info_), + nameSIDs (nameSIDs_) + {} + + const cff1_sub_table_info_t &info; + const unsigned int (&nameSIDs)[name_dict_values_t::ValCount]; +}; + +struct cff1_top_dict_op_serializer_t : cff_top_dict_op_serializer_t +{ + bool serialize (hb_serialize_context_t *c, + const cff1_top_dict_val_t &opstr, + const top_dict_modifiers_t &mod) const + { + TRACE_SERIALIZE (this); + + op_code_t op = opstr.op; + switch (op) + { + case OpCode_charset: + if (mod.info.charset_link) + return_trace (FontDict::serialize_link4_op(c, op, mod.info.charset_link, whence_t::Absolute)); + else + goto fall_back; + + case OpCode_Encoding: + if (mod.info.encoding_link) + return_trace (FontDict::serialize_link4_op(c, op, mod.info.encoding_link, whence_t::Absolute)); + else + goto fall_back; + + case OpCode_Private: + return_trace (UnsizedByteStr::serialize_int2 (c, mod.info.privateDictInfo.size) && + Dict::serialize_link4_op (c, op, mod.info.privateDictInfo.link, whence_t::Absolute)); + + case OpCode_version: + case OpCode_Notice: + case OpCode_Copyright: + case OpCode_FullName: + case OpCode_FamilyName: + case OpCode_Weight: + case OpCode_PostScript: + case OpCode_BaseFontName: + case OpCode_FontName: + return_trace (FontDict::serialize_int2_op (c, op, mod.nameSIDs[name_dict_values_t::name_op_to_index (op)])); + + case OpCode_ROS: + { + /* for registry & ordering, reassigned SIDs are serialized + * for supplement, the original byte string is copied along with the op code */ + op_str_t supp_op; + supp_op.op = op; + if ( unlikely (!(opstr.str.length >= opstr.last_arg_offset + 3))) + return_trace (false); + supp_op.str = hb_ubytes_t (&opstr.str + opstr.last_arg_offset, opstr.str.length - opstr.last_arg_offset); + return_trace (UnsizedByteStr::serialize_int2 (c, mod.nameSIDs[name_dict_values_t::registry]) && + UnsizedByteStr::serialize_int2 (c, mod.nameSIDs[name_dict_values_t::ordering]) && + copy_opstr (c, supp_op)); + } + fall_back: + default: + return_trace (cff_top_dict_op_serializer_t::serialize (c, opstr, mod.info)); + } + return_trace (true); + } + +}; + +struct cff1_font_dict_op_serializer_t : cff_font_dict_op_serializer_t +{ + bool serialize (hb_serialize_context_t *c, + const op_str_t &opstr, + const cff1_font_dict_values_mod_t &mod) const + { + TRACE_SERIALIZE (this); + + if (opstr.op == OpCode_FontName) + return_trace (FontDict::serialize_int2_op (c, opstr.op, mod.fontName)); + else + return_trace (SUPER::serialize (c, opstr, mod.privateDictInfo)); + } + + private: + typedef cff_font_dict_op_serializer_t SUPER; +}; + +struct cff1_cs_opset_flatten_t : cff1_cs_opset_t +{ + static void flush_args_and_op (op_code_t op, cff1_cs_interp_env_t &env, flatten_param_t& param) + { + if (env.arg_start > 0) + flush_width (env, param); + + switch (op) + { + case OpCode_hstem: + case OpCode_hstemhm: + case OpCode_vstem: + case OpCode_vstemhm: + case OpCode_hintmask: + case OpCode_cntrmask: + case OpCode_dotsection: + if (param.drop_hints) + { + env.clear_args (); + return; + } + HB_FALLTHROUGH; + + default: + SUPER::flush_args_and_op (op, env, param); + break; + } + } + static void flush_args (cff1_cs_interp_env_t &env, flatten_param_t& param) + { + str_encoder_t encoder (param.flatStr); + for (unsigned int i = env.arg_start; i < env.argStack.get_count (); i++) + encoder.encode_num (env.eval_arg (i)); + SUPER::flush_args (env, param); + } + + static void flush_op (op_code_t op, cff1_cs_interp_env_t &env, flatten_param_t& param) + { + str_encoder_t encoder (param.flatStr); + encoder.encode_op (op); + } + + static void flush_width (cff1_cs_interp_env_t &env, flatten_param_t& param) + { + assert (env.has_width); + str_encoder_t encoder (param.flatStr); + encoder.encode_num (env.width); + } + + static void flush_hintmask (op_code_t op, cff1_cs_interp_env_t &env, flatten_param_t& param) + { + SUPER::flush_hintmask (op, env, param); + if (!param.drop_hints) + { + str_encoder_t encoder (param.flatStr); + for (unsigned int i = 0; i < env.hintmask_size; i++) + encoder.encode_byte (env.str_ref[i]); + } + } + + private: + typedef cff1_cs_opset_t SUPER; +}; + +struct range_list_t : hb_vector_t +{ + /* replace the first glyph ID in the "glyph" field each range with a nLeft value */ + bool complete (unsigned int last_glyph) + { + bool two_byte = false; + unsigned count = this->length; + for (unsigned int i = count; i; i--) + { + code_pair_t &pair = arrayZ[i - 1]; + unsigned int nLeft = last_glyph - pair.glyph - 1; + two_byte |= nLeft >= 0x100; + last_glyph = pair.glyph; + pair.glyph = nLeft; + } + return two_byte; + } +}; + +struct cff1_cs_opset_subr_subset_t : cff1_cs_opset_t +{ + static void process_op (op_code_t op, cff1_cs_interp_env_t &env, subr_subset_param_t& param) + { + switch (op) { + + case OpCode_return: + param.current_parsed_str->add_op (op, env.str_ref); + param.current_parsed_str->set_parsed (); + env.return_from_subr (); + param.set_current_str (env, false); + break; + + case OpCode_endchar: + param.current_parsed_str->add_op (op, env.str_ref); + param.current_parsed_str->set_parsed (); + SUPER::process_op (op, env, param); + break; + + case OpCode_callsubr: + process_call_subr (op, CSType_LocalSubr, env, param, env.localSubrs, param.local_closure); + break; + + case OpCode_callgsubr: + process_call_subr (op, CSType_GlobalSubr, env, param, env.globalSubrs, param.global_closure); + break; + + default: + SUPER::process_op (op, env, param); + param.current_parsed_str->add_op (op, env.str_ref); + break; + } + } + + protected: + static void process_call_subr (op_code_t op, cs_type_t type, + cff1_cs_interp_env_t &env, subr_subset_param_t& param, + cff1_biased_subrs_t& subrs, hb_set_t *closure) + { + byte_str_ref_t str_ref = env.str_ref; + env.call_subr (subrs, type); + param.current_parsed_str->add_call_op (op, str_ref, env.context.subr_num); + closure->add (env.context.subr_num); + param.set_current_str (env, true); + } + + private: + typedef cff1_cs_opset_t SUPER; +}; + +struct cff1_subr_subsetter_t : subr_subsetter_t +{ + cff1_subr_subsetter_t (const OT::cff1::accelerator_subset_t &acc_, const hb_subset_plan_t *plan_) + : subr_subsetter_t (acc_, plan_) {} + + static void complete_parsed_str (cff1_cs_interp_env_t &env, subr_subset_param_t& param, parsed_cs_str_t &charstring) + { + /* insert width at the beginning of the charstring as necessary */ + if (env.has_width) + charstring.set_prefix (env.width); + + /* subroutines/charstring left on the call stack are legally left unmarked + * unmarked when a subroutine terminates with endchar. mark them. + */ + param.current_parsed_str->set_parsed (); + for (unsigned int i = 0; i < env.callStack.get_count (); i++) + { + parsed_cs_str_t *parsed_str = param.get_parsed_str_for_context (env.callStack[i]); + if (likely (parsed_str)) + parsed_str->set_parsed (); + else + env.set_error (); + } + } +}; + +struct cff_subset_plan { + cff_subset_plan () + { + for (unsigned int i = 0; i < name_dict_values_t::ValCount; i++) + topDictModSIDs[i] = CFF_UNDEF_SID; + } + + void plan_subset_encoding (const OT::cff1::accelerator_subset_t &acc, hb_subset_plan_t *plan) + { + const Encoding *encoding = acc.encoding; + unsigned int size0, size1; + hb_codepoint_t code, last_code = CFF_UNDEF_CODE; + hb_vector_t supp_codes; + + if (unlikely (!subset_enc_code_ranges.resize (0))) + { + plan->check_success (false); + return; + } + + supp_codes.init (); + + subset_enc_num_codes = plan->num_output_glyphs () - 1; + unsigned int glyph; + for (glyph = 1; glyph < plan->num_output_glyphs (); glyph++) + { + hb_codepoint_t old_glyph; + if (!plan->old_gid_for_new_gid (glyph, &old_glyph)) + { + /* Retain the code for the old missing glyph ID */ + old_glyph = glyph; + } + code = acc.glyph_to_code (old_glyph); + if (code == CFF_UNDEF_CODE) + { + subset_enc_num_codes = glyph - 1; + break; + } + + if ((last_code == CFF_UNDEF_CODE) || (code != last_code + 1)) + { + code_pair_t pair = { code, glyph }; + subset_enc_code_ranges.push (pair); + } + last_code = code; + + if (encoding != &Null (Encoding)) + { + hb_codepoint_t sid = acc.glyph_to_sid (old_glyph); + encoding->get_supplement_codes (sid, supp_codes); + for (unsigned int i = 0; i < supp_codes.length; i++) + { + code_pair_t pair = { supp_codes[i], sid }; + subset_enc_supp_codes.push (pair); + } + } + } + supp_codes.fini (); + + subset_enc_code_ranges.complete (glyph); + + assert (subset_enc_num_codes <= 0xFF); + size0 = Encoding0::min_size + HBUINT8::static_size * subset_enc_num_codes; + size1 = Encoding1::min_size + Encoding1_Range::static_size * subset_enc_code_ranges.length; + + if (size0 < size1) + subset_enc_format = 0; + else + subset_enc_format = 1; + } + + void plan_subset_charset (const OT::cff1::accelerator_subset_t &acc, hb_subset_plan_t *plan) + { + unsigned int size0, size_ranges; + hb_codepoint_t sid, last_sid = CFF_UNDEF_CODE; + + if (unlikely (!subset_charset_ranges.resize (0))) + { + plan->check_success (false); + return; + } + + bool use_glyph_to_sid_map = plan->num_output_glyphs () > plan->source->get_num_glyphs () / 8.; + hb_map_t *glyph_to_sid_map = use_glyph_to_sid_map ? acc.create_glyph_to_sid_map () : nullptr; + + unsigned int glyph; + for (glyph = 1; glyph < plan->num_output_glyphs (); glyph++) + { + hb_codepoint_t old_glyph; + if (!plan->old_gid_for_new_gid (glyph, &old_glyph)) + { + /* Retain the SID for the old missing glyph ID */ + old_glyph = glyph; + } + sid = glyph_to_sid_map ? glyph_to_sid_map->get (old_glyph) : acc.glyph_to_sid (old_glyph); + + if (!acc.is_CID ()) + sid = sidmap.add (sid); + + if ((last_sid == CFF_UNDEF_CODE) || (sid != last_sid + 1)) + { + code_pair_t pair = { sid, glyph }; + subset_charset_ranges.push (pair); + } + last_sid = sid; + } + + if (glyph_to_sid_map) + hb_map_destroy (glyph_to_sid_map); + + bool two_byte = subset_charset_ranges.complete (glyph); + + size0 = Charset0::min_size + HBUINT16::static_size * (plan->num_output_glyphs () - 1); + if (!two_byte) + size_ranges = Charset1::min_size + Charset1_Range::static_size * subset_charset_ranges.length; + else + size_ranges = Charset2::min_size + Charset2_Range::static_size * subset_charset_ranges.length; + + if (size0 < size_ranges) + subset_charset_format = 0; + else if (!two_byte) + subset_charset_format = 1; + else + subset_charset_format = 2; + } + + bool collect_sids_in_dicts (const OT::cff1::accelerator_subset_t &acc) + { + sidmap.reset (); + + for (unsigned int i = 0; i < name_dict_values_t::ValCount; i++) + { + unsigned int sid = acc.topDict.nameSIDs[i]; + if (sid != CFF_UNDEF_SID) + { + (void)sidmap.add (sid); + topDictModSIDs[i] = sidmap[sid]; + } + } + + if (acc.fdArray != &Null (CFF1FDArray)) + for (unsigned int i = 0; i < orig_fdcount; i++) + if (fdmap.has (i)) + (void)sidmap.add (acc.fontDicts[i].fontName); + + return true; + } + + bool create (const OT::cff1::accelerator_subset_t &acc, + hb_subset_plan_t *plan) + { + /* make sure notdef is first */ + hb_codepoint_t old_glyph; + if (!plan->old_gid_for_new_gid (0, &old_glyph) || (old_glyph != 0)) return false; + + num_glyphs = plan->num_output_glyphs (); + orig_fdcount = acc.fdCount; + drop_hints = plan->flags & HB_SUBSET_FLAGS_NO_HINTING; + desubroutinize = plan->flags & HB_SUBSET_FLAGS_DESUBROUTINIZE; + + /* check whether the subset renumbers any glyph IDs */ + gid_renum = false; + for (hb_codepoint_t new_glyph = 0; new_glyph < plan->num_output_glyphs (); new_glyph++) + { + if (!plan->old_gid_for_new_gid(new_glyph, &old_glyph)) + continue; + if (new_glyph != old_glyph) { + gid_renum = true; + break; + } + } + + subset_charset = gid_renum || !acc.is_predef_charset (); + subset_encoding = !acc.is_CID() && !acc.is_predef_encoding (); + + /* top dict INDEX */ + { + /* Add encoding/charset to a (copy of) top dict as necessary */ + topdict_mod.init (&acc.topDict); + bool need_to_add_enc = (subset_encoding && !acc.topDict.has_op (OpCode_Encoding)); + bool need_to_add_set = (subset_charset && !acc.topDict.has_op (OpCode_charset)); + if (need_to_add_enc || need_to_add_set) + { + if (need_to_add_enc) + topdict_mod.add_op (OpCode_Encoding); + if (need_to_add_set) + topdict_mod.add_op (OpCode_charset); + } + } + + /* Determine re-mapping of font index as fdmap among other info */ + if (acc.fdSelect != &Null (CFF1FDSelect)) + { + if (unlikely (!hb_plan_subset_cff_fdselect (plan, + orig_fdcount, + *acc.fdSelect, + subset_fdcount, + info.fd_select.size, + subset_fdselect_format, + subset_fdselect_ranges, + fdmap))) + return false; + } + else + fdmap.identity (1); + + /* remove unused SIDs & reassign SIDs */ + { + /* SIDs for name strings in dicts are added before glyph names so they fit in 16-bit int range */ + if (unlikely (!collect_sids_in_dicts (acc))) + return false; + if (unlikely (sidmap.get_population () > 0x8000)) /* assumption: a dict won't reference that many strings */ + return false; + + if (subset_charset) plan_subset_charset (acc, plan); + + topdict_mod.reassignSIDs (sidmap); + } + + if (desubroutinize) + { + /* Flatten global & local subrs */ + subr_flattener_t + flattener(acc, plan); + if (!flattener.flatten (subset_charstrings)) + return false; + } + else + { + cff1_subr_subsetter_t subr_subsetter (acc, plan); + + /* Subset subrs: collect used subroutines, leaving all unused ones behind */ + if (!subr_subsetter.subset ()) + return false; + + /* encode charstrings, global subrs, local subrs with new subroutine numbers */ + if (!subr_subsetter.encode_charstrings (subset_charstrings)) + return false; + + if (!subr_subsetter.encode_globalsubrs (subset_globalsubrs)) + return false; + + /* local subrs */ + if (!subset_localsubrs.resize (orig_fdcount)) + return false; + for (unsigned int fd = 0; fd < orig_fdcount; fd++) + { + subset_localsubrs[fd].init (); + if (fdmap.has (fd)) + { + if (!subr_subsetter.encode_localsubrs (fd, subset_localsubrs[fd])) + return false; + } + } + } + + /* Encoding */ + if (subset_encoding) + plan_subset_encoding (acc, plan); + + /* private dicts & local subrs */ + if (!acc.is_CID ()) + fontdicts_mod.push (cff1_font_dict_values_mod_t ()); + else + { + + hb_iter (acc.fontDicts) + | hb_filter ([&] (const cff1_font_dict_values_t &_) + { return fdmap.has (&_ - &acc.fontDicts[0]); } ) + | hb_map ([&] (const cff1_font_dict_values_t &_) + { + cff1_font_dict_values_mod_t mod; + mod.init (&_, sidmap[_.fontName]); + return mod; + }) + | hb_sink (fontdicts_mod) + ; + } + + return ((subset_charstrings.length == plan->num_output_glyphs ()) + && (fontdicts_mod.length == subset_fdcount)); + } + + cff1_top_dict_values_mod_t topdict_mod; + cff1_sub_table_info_t info; + + unsigned int num_glyphs; + unsigned int orig_fdcount = 0; + unsigned int subset_fdcount = 1; + unsigned int subset_fdselect_format = 0; + hb_vector_t subset_fdselect_ranges; + + /* font dict index remap table from fullset FDArray to subset FDArray. + * set to CFF_UNDEF_CODE if excluded from subset */ + hb_inc_bimap_t fdmap; + + str_buff_vec_t subset_charstrings; + str_buff_vec_t subset_globalsubrs; + hb_vector_t subset_localsubrs; + hb_vector_t fontdicts_mod; + + bool drop_hints = false; + + bool gid_renum; + bool subset_encoding; + uint8_t subset_enc_format; + unsigned int subset_enc_num_codes; + range_list_t subset_enc_code_ranges; + hb_vector_t subset_enc_supp_codes; + + uint8_t subset_charset_format; + range_list_t subset_charset_ranges; + bool subset_charset; + + remap_sid_t sidmap; + unsigned int topDictModSIDs[name_dict_values_t::ValCount]; + + bool desubroutinize = false; +}; + +static bool _serialize_cff1 (hb_serialize_context_t *c, + cff_subset_plan &plan, + const OT::cff1::accelerator_subset_t &acc, + unsigned int num_glyphs) +{ + /* private dicts & local subrs */ + for (int i = (int)acc.privateDicts.length; --i >= 0 ;) + { + if (plan.fdmap.has (i)) + { + objidx_t subrs_link = 0; + if (plan.subset_localsubrs[i].length > 0) + { + CFF1Subrs *dest = c->start_embed (); + if (unlikely (!dest)) return false; + c->push (); + if (likely (dest && dest->serialize (c, plan.subset_localsubrs[i]))) + subrs_link = c->pop_pack (); + else + { + c->pop_discard (); + return false; + } + } + + PrivateDict *pd = c->start_embed (); + if (unlikely (!pd)) return false; + c->push (); + cff_private_dict_op_serializer_t privSzr (plan.desubroutinize, plan.drop_hints); + /* N.B. local subrs immediately follows its corresponding private dict. i.e., subr offset == private dict size */ + if (likely (pd->serialize (c, acc.privateDicts[i], privSzr, subrs_link))) + { + unsigned fd = plan.fdmap[i]; + plan.fontdicts_mod[fd].privateDictInfo.size = c->length (); + plan.fontdicts_mod[fd].privateDictInfo.link = c->pop_pack (); + } + else + { + c->pop_discard (); + return false; + } + } + } + + if (!acc.is_CID ()) + plan.info.privateDictInfo = plan.fontdicts_mod[0].privateDictInfo; + + /* CharStrings */ + { + CFF1CharStrings *cs = c->start_embed (); + if (unlikely (!cs)) return false; + c->push (); + if (likely (cs->serialize (c, plan.subset_charstrings))) + plan.info.char_strings_link = c->pop_pack (); + else + { + c->pop_discard (); + return false; + } + } + + /* FDArray (FD Index) */ + if (acc.fdArray != &Null (CFF1FDArray)) + { + CFF1FDArray *fda = c->start_embed (); + if (unlikely (!fda)) return false; + c->push (); + cff1_font_dict_op_serializer_t fontSzr; + auto it = + hb_zip (+ hb_iter (plan.fontdicts_mod), + hb_iter (plan.fontdicts_mod)); + if (likely (fda->serialize (c, it, fontSzr))) + plan.info.fd_array_link = c->pop_pack (false); + else + { + c->pop_discard (); + return false; + } + } + + /* FDSelect */ + if (acc.fdSelect != &Null (CFF1FDSelect)) + { + c->push (); + if (likely (hb_serialize_cff_fdselect (c, num_glyphs, *acc.fdSelect, acc.fdCount, + plan.subset_fdselect_format, plan.info.fd_select.size, + plan.subset_fdselect_ranges))) + plan.info.fd_select.link = c->pop_pack (); + else + { + c->pop_discard (); + return false; + } + } + + /* Charset */ + if (plan.subset_charset) + { + Charset *dest = c->start_embed (); + if (unlikely (!dest)) return false; + c->push (); + if (likely (dest->serialize (c, + plan.subset_charset_format, + plan.num_glyphs, + plan.subset_charset_ranges))) + plan.info.charset_link = c->pop_pack (); + else + { + c->pop_discard (); + return false; + } + } + + /* Encoding */ + if (plan.subset_encoding) + { + Encoding *dest = c->start_embed (); + if (unlikely (!dest)) return false; + c->push (); + if (likely (dest->serialize (c, + plan.subset_enc_format, + plan.subset_enc_num_codes, + plan.subset_enc_code_ranges, + plan.subset_enc_supp_codes))) + plan.info.encoding_link = c->pop_pack (); + else + { + c->pop_discard (); + return false; + } + } + + /* global subrs */ + { + c->push (); + CFF1Subrs *dest = c->start_embed (); + if (unlikely (!dest)) return false; + if (likely (dest->serialize (c, plan.subset_globalsubrs))) + c->pop_pack (); + else + { + c->pop_discard (); + return false; + } + } + + /* String INDEX */ + { + CFF1StringIndex *dest = c->start_embed (); + if (unlikely (!dest)) return false; + c->push (); + if (likely (dest->serialize (c, *acc.stringIndex, plan.sidmap))) + c->pop_pack (); + else + { + c->pop_discard (); + return false; + } + } + + OT::cff1 *cff = c->allocate_min (); + if (unlikely (!cff)) + return false; + + /* header */ + cff->version.major = 0x01; + cff->version.minor = 0x00; + cff->nameIndex = cff->min_size; + cff->offSize = 4; /* unused? */ + + /* name INDEX */ + if (unlikely (!(*acc.nameIndex).copy (c))) return false; + + /* top dict INDEX */ + { + /* serialize singleton TopDict */ + TopDict *top = c->start_embed (); + if (!top) return false; + c->push (); + cff1_top_dict_op_serializer_t topSzr; + unsigned top_size = 0; + top_dict_modifiers_t modifier (plan.info, plan.topDictModSIDs); + if (likely (top->serialize (c, plan.topdict_mod, topSzr, modifier))) + { + top_size = c->length (); + c->pop_pack (false); + } + else + { + c->pop_discard (); + return false; + } + /* serialize INDEX header for above */ + CFF1Index *dest = c->start_embed (); + if (!dest) return false; + return dest->serialize_header (c, hb_iter (hb_array_t (&top_size, 1))); + } +} + +static bool +_hb_subset_cff1 (const OT::cff1::accelerator_subset_t &acc, + hb_subset_context_t *c) +{ + cff_subset_plan cff_plan; + + if (unlikely (!cff_plan.create (acc, c->plan))) + { + DEBUG_MSG(SUBSET, nullptr, "Failed to generate a cff subsetting plan."); + return false; + } + + return _serialize_cff1 (c->serializer, cff_plan, acc, c->plan->num_output_glyphs ()); +} + +bool +hb_subset_cff1 (hb_subset_context_t *c) +{ + OT::cff1::accelerator_subset_t acc; + acc.init (c->plan->source); + bool result = likely (acc.is_valid ()) && _hb_subset_cff1 (acc, c); + acc.fini (); + + return result; +} + + +#endif diff --git a/libs/harfbuzz/src/hb-subset-cff1.hh b/libs/harfbuzz/src/hb-subset-cff1.hh new file mode 100644 index 00000000..aaf5def1 --- /dev/null +++ b/libs/harfbuzz/src/hb-subset-cff1.hh @@ -0,0 +1,37 @@ +/* + * Copyright © 2018 Adobe 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. + * + * Adobe Author(s): Michiharu Ariza + */ + +#ifndef HB_SUBSET_CFF1_HH +#define HB_SUBSET_CFF1_HH + +#include "hb.hh" + +#include "hb-subset-plan.hh" + +HB_INTERNAL bool +hb_subset_cff1 (hb_subset_context_t *c); + +#endif /* HB_SUBSET_CFF1_HH */ diff --git a/libs/harfbuzz/src/hb-subset-cff2.cc b/libs/harfbuzz/src/hb-subset-cff2.cc new file mode 100644 index 00000000..08e820ef --- /dev/null +++ b/libs/harfbuzz/src/hb-subset-cff2.cc @@ -0,0 +1,457 @@ +/* + * Copyright © 2018 Adobe 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. + * + * Adobe Author(s): Michiharu Ariza + */ + +#include "hb.hh" + +#ifndef HB_NO_SUBSET_CFF + +#include "hb-open-type.hh" +#include "hb-ot-cff2-table.hh" +#include "hb-set.h" +#include "hb-subset-cff2.hh" +#include "hb-subset-plan.hh" +#include "hb-subset-cff-common.hh" +#include "hb-cff2-interp-cs.hh" + +using namespace CFF; + +struct cff2_sub_table_info_t : cff_sub_table_info_t +{ + cff2_sub_table_info_t () + : cff_sub_table_info_t (), + var_store_link (0) + {} + + objidx_t var_store_link; +}; + +struct cff2_top_dict_op_serializer_t : cff_top_dict_op_serializer_t<> +{ + bool serialize (hb_serialize_context_t *c, + const op_str_t &opstr, + const cff2_sub_table_info_t &info) const + { + TRACE_SERIALIZE (this); + + switch (opstr.op) + { + case OpCode_vstore: + return_trace (FontDict::serialize_link4_op(c, opstr.op, info.var_store_link)); + + default: + return_trace (cff_top_dict_op_serializer_t<>::serialize (c, opstr, info)); + } + } +}; + +struct cff2_cs_opset_flatten_t : cff2_cs_opset_t +{ + static void flush_args_and_op (op_code_t op, cff2_cs_interp_env_t &env, flatten_param_t& param) + { + switch (op) + { + case OpCode_return: + case OpCode_endchar: + /* dummy opcodes in CFF2. ignore */ + break; + + case OpCode_hstem: + case OpCode_hstemhm: + case OpCode_vstem: + case OpCode_vstemhm: + case OpCode_hintmask: + case OpCode_cntrmask: + if (param.drop_hints) + { + env.clear_args (); + return; + } + HB_FALLTHROUGH; + + default: + SUPER::flush_args_and_op (op, env, param); + break; + } + } + + static void flush_args (cff2_cs_interp_env_t &env, flatten_param_t& param) + { + for (unsigned int i = 0; i < env.argStack.get_count ();) + { + const blend_arg_t &arg = env.argStack[i]; + if (arg.blending ()) + { + if (unlikely (!((arg.numValues > 0) && (env.argStack.get_count () >= arg.numValues)))) + { + env.set_error (); + return; + } + flatten_blends (arg, i, env, param); + i += arg.numValues; + } + else + { + str_encoder_t encoder (param.flatStr); + encoder.encode_num (arg); + i++; + } + } + SUPER::flush_args (env, param); + } + + static void flatten_blends (const blend_arg_t &arg, unsigned int i, cff2_cs_interp_env_t &env, flatten_param_t& param) + { + /* flatten the default values */ + str_encoder_t encoder (param.flatStr); + for (unsigned int j = 0; j < arg.numValues; j++) + { + const blend_arg_t &arg1 = env.argStack[i + j]; + if (unlikely (!((arg1.blending () && (arg.numValues == arg1.numValues) && (arg1.valueIndex == j) && + (arg1.deltas.length == env.get_region_count ()))))) + { + env.set_error (); + return; + } + encoder.encode_num (arg1); + } + /* flatten deltas for each value */ + for (unsigned int j = 0; j < arg.numValues; j++) + { + const blend_arg_t &arg1 = env.argStack[i + j]; + for (unsigned int k = 0; k < arg1.deltas.length; k++) + encoder.encode_num (arg1.deltas[k]); + } + /* flatten the number of values followed by blend operator */ + encoder.encode_int (arg.numValues); + encoder.encode_op (OpCode_blendcs); + } + + static void flush_op (op_code_t op, cff2_cs_interp_env_t &env, flatten_param_t& param) + { + switch (op) + { + case OpCode_return: + case OpCode_endchar: + return; + default: + str_encoder_t encoder (param.flatStr); + encoder.encode_op (op); + } + } + + private: + typedef cff2_cs_opset_t SUPER; + typedef cs_opset_t, flatten_param_t> CSOPSET; +}; + +struct cff2_cs_opset_subr_subset_t : cff2_cs_opset_t +{ + static void process_op (op_code_t op, cff2_cs_interp_env_t &env, subr_subset_param_t& param) + { + switch (op) { + + case OpCode_return: + param.current_parsed_str->set_parsed (); + env.return_from_subr (); + param.set_current_str (env, false); + break; + + case OpCode_endchar: + param.current_parsed_str->set_parsed (); + SUPER::process_op (op, env, param); + break; + + case OpCode_callsubr: + process_call_subr (op, CSType_LocalSubr, env, param, env.localSubrs, param.local_closure); + break; + + case OpCode_callgsubr: + process_call_subr (op, CSType_GlobalSubr, env, param, env.globalSubrs, param.global_closure); + break; + + default: + SUPER::process_op (op, env, param); + param.current_parsed_str->add_op (op, env.str_ref); + break; + } + } + + protected: + static void process_call_subr (op_code_t op, cs_type_t type, + cff2_cs_interp_env_t &env, subr_subset_param_t& param, + cff2_biased_subrs_t& subrs, hb_set_t *closure) + { + byte_str_ref_t str_ref = env.str_ref; + env.call_subr (subrs, type); + param.current_parsed_str->add_call_op (op, str_ref, env.context.subr_num); + closure->add (env.context.subr_num); + param.set_current_str (env, true); + } + + private: + typedef cff2_cs_opset_t SUPER; +}; + +struct cff2_subr_subsetter_t : subr_subsetter_t, cff2_cs_opset_subr_subset_t> +{ + cff2_subr_subsetter_t (const OT::cff2::accelerator_subset_t &acc_, const hb_subset_plan_t *plan_) + : subr_subsetter_t (acc_, plan_) {} + + static void complete_parsed_str (cff2_cs_interp_env_t &env, subr_subset_param_t& param, parsed_cs_str_t &charstring) + { + /* vsindex is inserted at the beginning of the charstring as necessary */ + if (env.seen_vsindex ()) + { + number_t ivs; + ivs.set_int ((int)env.get_ivs ()); + charstring.set_prefix (ivs, OpCode_vsindexcs); + } + } +}; + +struct cff2_subset_plan { + + bool create (const OT::cff2::accelerator_subset_t &acc, + hb_subset_plan_t *plan) + { + orig_fdcount = acc.fdArray->count; + + drop_hints = plan->flags & HB_SUBSET_FLAGS_NO_HINTING; + desubroutinize = plan->flags & HB_SUBSET_FLAGS_DESUBROUTINIZE; + + if (desubroutinize) + { + /* Flatten global & local subrs */ + subr_flattener_t, cff2_cs_opset_flatten_t> + flattener(acc, plan); + if (!flattener.flatten (subset_charstrings)) + return false; + } + else + { + cff2_subr_subsetter_t subr_subsetter (acc, plan); + + /* Subset subrs: collect used subroutines, leaving all unused ones behind */ + if (!subr_subsetter.subset ()) + return false; + + /* encode charstrings, global subrs, local subrs with new subroutine numbers */ + if (!subr_subsetter.encode_charstrings (subset_charstrings)) + return false; + + if (!subr_subsetter.encode_globalsubrs (subset_globalsubrs)) + return false; + + /* local subrs */ + if (!subset_localsubrs.resize (orig_fdcount)) + return false; + for (unsigned int fd = 0; fd < orig_fdcount; fd++) + { + subset_localsubrs[fd].init (); + if (!subr_subsetter.encode_localsubrs (fd, subset_localsubrs[fd])) + return false; + } + } + + /* FDSelect */ + if (acc.fdSelect != &Null (CFF2FDSelect)) + { + if (unlikely (!hb_plan_subset_cff_fdselect (plan, + orig_fdcount, + *(const FDSelect *)acc.fdSelect, + subset_fdcount, + subset_fdselect_size, + subset_fdselect_format, + subset_fdselect_ranges, + fdmap))) + return false; + } + else + fdmap.identity (1); + + return true; + } + + cff2_sub_table_info_t info; + + unsigned int orig_fdcount = 0; + unsigned int subset_fdcount = 1; + unsigned int subset_fdselect_size = 0; + unsigned int subset_fdselect_format = 0; + hb_vector_t subset_fdselect_ranges; + + hb_inc_bimap_t fdmap; + + str_buff_vec_t subset_charstrings; + str_buff_vec_t subset_globalsubrs; + hb_vector_t subset_localsubrs; + + bool drop_hints = false; + bool desubroutinize = false; +}; + +static bool _serialize_cff2 (hb_serialize_context_t *c, + cff2_subset_plan &plan, + const OT::cff2::accelerator_subset_t &acc, + unsigned int num_glyphs) +{ + /* private dicts & local subrs */ + hb_vector_t private_dict_infos; + if (unlikely (!private_dict_infos.resize (plan.subset_fdcount))) return false; + + for (int i = (int)acc.privateDicts.length; --i >= 0 ;) + { + if (plan.fdmap.has (i)) + { + objidx_t subrs_link = 0; + + if (plan.subset_localsubrs[i].length > 0) + { + CFF2Subrs *dest = c->start_embed (); + if (unlikely (!dest)) return false; + c->push (); + if (likely (dest->serialize (c, plan.subset_localsubrs[i]))) + subrs_link = c->pop_pack (); + else + { + c->pop_discard (); + return false; + } + } + PrivateDict *pd = c->start_embed (); + if (unlikely (!pd)) return false; + c->push (); + cff_private_dict_op_serializer_t privSzr (plan.desubroutinize, plan.drop_hints); + if (likely (pd->serialize (c, acc.privateDicts[i], privSzr, subrs_link))) + { + unsigned fd = plan.fdmap[i]; + private_dict_infos[fd].size = c->length (); + private_dict_infos[fd].link = c->pop_pack (); + } + else + { + c->pop_discard (); + return false; + } + } + } + + /* CharStrings */ + { + CFF2CharStrings *cs = c->start_embed (); + if (unlikely (!cs)) return false; + c->push (); + if (likely (cs->serialize (c, plan.subset_charstrings))) + plan.info.char_strings_link = c->pop_pack (); + else + { + c->pop_discard (); + return false; + } + } + + /* FDSelect */ + if (acc.fdSelect != &Null (CFF2FDSelect)) + { + c->push (); + if (likely (hb_serialize_cff_fdselect (c, num_glyphs, *(const FDSelect *)acc.fdSelect, plan.orig_fdcount, + plan.subset_fdselect_format, plan.subset_fdselect_size, + plan.subset_fdselect_ranges))) + plan.info.fd_select.link = c->pop_pack (); + else + { + c->pop_discard (); + return false; + } + } + + /* FDArray (FD Index) */ + { + c->push (); + CFF2FDArray *fda = c->start_embed (); + if (unlikely (!fda)) return false; + cff_font_dict_op_serializer_t fontSzr; + auto it = + + hb_zip (+ hb_iter (acc.fontDicts) + | hb_filter ([&] (const cff2_font_dict_values_t &_) + { return plan.fdmap.has (&_ - &acc.fontDicts[0]); }), + hb_iter (private_dict_infos)) + ; + if (unlikely (!fda->serialize (c, it, fontSzr))) return false; + plan.info.fd_array_link = c->pop_pack (); + } + + /* variation store */ + if (acc.varStore != &Null (CFF2VariationStore)) + { + c->push (); + CFF2VariationStore *dest = c->start_embed (); + if (unlikely (!dest || !dest->serialize (c, acc.varStore))) return false; + plan.info.var_store_link = c->pop_pack (); + } + + OT::cff2 *cff2 = c->allocate_min (); + if (unlikely (!cff2)) return false; + + /* header */ + cff2->version.major = 0x02; + cff2->version.minor = 0x00; + cff2->topDict = OT::cff2::static_size; + + /* top dict */ + { + TopDict &dict = cff2 + cff2->topDict; + cff2_top_dict_op_serializer_t topSzr; + if (unlikely (!dict.serialize (c, acc.topDict, topSzr, plan.info))) return false; + cff2->topDictSize = c->head - (const char *)&dict; + } + + /* global subrs */ + { + CFF2Subrs *dest = c->start_embed (); + if (unlikely (!dest)) return false; + return dest->serialize (c, plan.subset_globalsubrs); + } +} + +static bool +_hb_subset_cff2 (const OT::cff2::accelerator_subset_t &acc, + hb_subset_context_t *c) +{ + cff2_subset_plan cff2_plan; + + if (unlikely (!cff2_plan.create (acc, c->plan))) return false; + return _serialize_cff2 (c->serializer, cff2_plan, acc, c->plan->num_output_glyphs ()); +} + +bool +hb_subset_cff2 (hb_subset_context_t *c) +{ + OT::cff2::accelerator_subset_t acc (c->plan->source); + return acc.is_valid () && _hb_subset_cff2 (acc, c); +} + +#endif diff --git a/libs/harfbuzz/src/hb-subset-cff2.hh b/libs/harfbuzz/src/hb-subset-cff2.hh new file mode 100644 index 00000000..f10556dd --- /dev/null +++ b/libs/harfbuzz/src/hb-subset-cff2.hh @@ -0,0 +1,37 @@ +/* + * Copyright © 2018 Adobe 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. + * + * Adobe Author(s): Michiharu Ariza + */ + +#ifndef HB_SUBSET_CFF2_HH +#define HB_SUBSET_CFF2_HH + +#include "hb.hh" + +#include "hb-subset-plan.hh" + +HB_INTERNAL bool +hb_subset_cff2 (hb_subset_context_t *c); + +#endif /* HB_SUBSET_CFF2_HH */ diff --git a/libs/harfbuzz/src/hb-subset-input.cc b/libs/harfbuzz/src/hb-subset-input.cc new file mode 100644 index 00000000..fd250104 --- /dev/null +++ b/libs/harfbuzz/src/hb-subset-input.cc @@ -0,0 +1,481 @@ +/* + * Copyright © 2018 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): Garret Rieger, Rod Sheeter, Behdad Esfahbod + */ + +#include "hb-subset.hh" +#include "hb-set.hh" + +/** + * hb_subset_input_create_or_fail: + * + * Creates a new subset input object. + * + * Return value: (transfer full): New subset input, or `NULL` if failed. Destroy + * with hb_subset_input_destroy(). + * + * Since: 1.8.0 + **/ +hb_subset_input_t * +hb_subset_input_create_or_fail (void) +{ + hb_subset_input_t *input = hb_object_create(); + + if (unlikely (!input)) + return nullptr; + + for (auto& set : input->sets_iter ()) + set = hb_set_create (); + + input->axes_location = hb_hashmap_create (); + + if (!input->axes_location || input->in_error ()) + { + hb_subset_input_destroy (input); + return nullptr; + } + + input->flags = HB_SUBSET_FLAGS_DEFAULT; + + hb_set_add_range (input->sets.name_ids, 0, 6); + hb_set_add (input->sets.name_languages, 0x0409); + + hb_tag_t default_drop_tables[] = { + // Layout disabled by default + HB_TAG ('m', 'o', 'r', 'x'), + HB_TAG ('m', 'o', 'r', 't'), + HB_TAG ('k', 'e', 'r', 'x'), + HB_TAG ('k', 'e', 'r', 'n'), + + // Copied from fontTools: + HB_TAG ('B', 'A', 'S', 'E'), + HB_TAG ('J', 'S', 'T', 'F'), + HB_TAG ('D', 'S', 'I', 'G'), + HB_TAG ('E', 'B', 'D', 'T'), + HB_TAG ('E', 'B', 'L', 'C'), + HB_TAG ('E', 'B', 'S', 'C'), + HB_TAG ('S', 'V', 'G', ' '), + HB_TAG ('P', 'C', 'L', 'T'), + HB_TAG ('L', 'T', 'S', 'H'), + // Graphite tables + HB_TAG ('F', 'e', 'a', 't'), + HB_TAG ('G', 'l', 'a', 't'), + HB_TAG ('G', 'l', 'o', 'c'), + HB_TAG ('S', 'i', 'l', 'f'), + HB_TAG ('S', 'i', 'l', 'l'), + }; + input->sets.drop_tables->add_array (default_drop_tables, ARRAY_LENGTH (default_drop_tables)); + + hb_tag_t default_no_subset_tables[] = { + HB_TAG ('a', 'v', 'a', 'r'), + HB_TAG ('g', 'a', 's', 'p'), + HB_TAG ('c', 'v', 't', ' '), + HB_TAG ('f', 'p', 'g', 'm'), + HB_TAG ('p', 'r', 'e', 'p'), + HB_TAG ('V', 'D', 'M', 'X'), + HB_TAG ('D', 'S', 'I', 'G'), + HB_TAG ('M', 'V', 'A', 'R'), + HB_TAG ('c', 'v', 'a', 'r'), + }; + input->sets.no_subset_tables->add_array (default_no_subset_tables, + ARRAY_LENGTH (default_no_subset_tables)); + + //copied from _layout_features_groups in fonttools + hb_tag_t default_layout_features[] = { + // default shaper + // common + HB_TAG ('r', 'v', 'r', 'n'), + HB_TAG ('c', 'c', 'm', 'p'), + HB_TAG ('l', 'i', 'g', 'a'), + HB_TAG ('l', 'o', 'c', 'l'), + HB_TAG ('m', 'a', 'r', 'k'), + HB_TAG ('m', 'k', 'm', 'k'), + HB_TAG ('r', 'l', 'i', 'g'), + + //fractions + HB_TAG ('f', 'r', 'a', 'c'), + HB_TAG ('n', 'u', 'm', 'r'), + HB_TAG ('d', 'n', 'o', 'm'), + + //horizontal + HB_TAG ('c', 'a', 'l', 't'), + HB_TAG ('c', 'l', 'i', 'g'), + HB_TAG ('c', 'u', 'r', 's'), + HB_TAG ('k', 'e', 'r', 'n'), + HB_TAG ('r', 'c', 'l', 't'), + + //vertical + HB_TAG ('v', 'a', 'l', 't'), + HB_TAG ('v', 'e', 'r', 't'), + HB_TAG ('v', 'k', 'r', 'n'), + HB_TAG ('v', 'p', 'a', 'l'), + HB_TAG ('v', 'r', 't', '2'), + + //ltr + HB_TAG ('l', 't', 'r', 'a'), + HB_TAG ('l', 't', 'r', 'm'), + + //rtl + HB_TAG ('r', 't', 'l', 'a'), + HB_TAG ('r', 't', 'l', 'm'), + + //random + HB_TAG ('r', 'a', 'n', 'd'), + + //justify + HB_TAG ('j', 'a', 'l', 't'), // HarfBuzz doesn't use; others might + + //private + HB_TAG ('H', 'a', 'r', 'f'), + HB_TAG ('H', 'A', 'R', 'F'), + HB_TAG ('B', 'u', 'z', 'z'), + HB_TAG ('B', 'U', 'Z', 'Z'), + + //shapers + + //arabic + HB_TAG ('i', 'n', 'i', 't'), + HB_TAG ('m', 'e', 'd', 'i'), + HB_TAG ('f', 'i', 'n', 'a'), + HB_TAG ('i', 's', 'o', 'l'), + HB_TAG ('m', 'e', 'd', '2'), + HB_TAG ('f', 'i', 'n', '2'), + HB_TAG ('f', 'i', 'n', '3'), + HB_TAG ('c', 's', 'w', 'h'), + HB_TAG ('m', 's', 'e', 't'), + HB_TAG ('s', 't', 'c', 'h'), + + //hangul + HB_TAG ('l', 'j', 'm', 'o'), + HB_TAG ('v', 'j', 'm', 'o'), + HB_TAG ('t', 'j', 'm', 'o'), + + //tibetan + HB_TAG ('a', 'b', 'v', 's'), + HB_TAG ('b', 'l', 'w', 's'), + HB_TAG ('a', 'b', 'v', 'm'), + HB_TAG ('b', 'l', 'w', 'm'), + + //indic + HB_TAG ('n', 'u', 'k', 't'), + HB_TAG ('a', 'k', 'h', 'n'), + HB_TAG ('r', 'p', 'h', 'f'), + HB_TAG ('r', 'k', 'r', 'f'), + HB_TAG ('p', 'r', 'e', 'f'), + HB_TAG ('b', 'l', 'w', 'f'), + HB_TAG ('h', 'a', 'l', 'f'), + HB_TAG ('a', 'b', 'v', 'f'), + HB_TAG ('p', 's', 't', 'f'), + HB_TAG ('c', 'f', 'a', 'r'), + HB_TAG ('v', 'a', 't', 'u'), + HB_TAG ('c', 'j', 'c', 't'), + HB_TAG ('i', 'n', 'i', 't'), + HB_TAG ('p', 'r', 'e', 's'), + HB_TAG ('a', 'b', 'v', 's'), + HB_TAG ('b', 'l', 'w', 's'), + HB_TAG ('p', 's', 't', 's'), + HB_TAG ('h', 'a', 'l', 'n'), + HB_TAG ('d', 'i', 's', 't'), + HB_TAG ('a', 'b', 'v', 'm'), + HB_TAG ('b', 'l', 'w', 'm'), + }; + + input->sets.layout_features->add_array (default_layout_features, ARRAY_LENGTH (default_layout_features)); + + input->sets.layout_scripts->invert (); // Default to all scripts. + + if (input->in_error ()) + { + hb_subset_input_destroy (input); + return nullptr; + } + return input; +} + +/** + * hb_subset_input_reference: (skip) + * @input: a #hb_subset_input_t object. + * + * Increases the reference count on @input. + * + * Return value: @input. + * + * Since: 1.8.0 + **/ +hb_subset_input_t * +hb_subset_input_reference (hb_subset_input_t *input) +{ + return hb_object_reference (input); +} + +/** + * hb_subset_input_destroy: + * @input: a #hb_subset_input_t object. + * + * Decreases the reference count on @input, and if it reaches zero, destroys + * @input, freeing all memory. + * + * Since: 1.8.0 + **/ +void +hb_subset_input_destroy (hb_subset_input_t *input) +{ + if (!hb_object_destroy (input)) return; + + for (hb_set_t* set : input->sets_iter ()) + hb_set_destroy (set); + + hb_hashmap_destroy (input->axes_location); + + hb_free (input); +} + +/** + * hb_subset_input_unicode_set: + * @input: a #hb_subset_input_t object. + * + * Gets the set of Unicode code points to retain, the caller should modify the + * set as needed. + * + * Return value: (transfer none): pointer to the #hb_set_t of Unicode code + * points. + * + * Since: 1.8.0 + **/ +HB_EXTERN hb_set_t * +hb_subset_input_unicode_set (hb_subset_input_t *input) +{ + return input->sets.unicodes; +} + +/** + * hb_subset_input_glyph_set: + * @input: a #hb_subset_input_t object. + * + * Gets the set of glyph IDs to retain, the caller should modify the set as + * needed. + * + * Return value: (transfer none): pointer to the #hb_set_t of glyph IDs. + * + * Since: 1.8.0 + **/ +HB_EXTERN hb_set_t * +hb_subset_input_glyph_set (hb_subset_input_t *input) +{ + return input->sets.glyphs; +} + +/** + * hb_subset_input_set: + * @input: a #hb_subset_input_t object. + * @set_type: a #hb_subset_sets_t set type. + * + * Gets the set of the specified type. + * + * Return value: (transfer none): pointer to the #hb_set_t of the specified type. + * + * Since: 2.9.1 + **/ +HB_EXTERN hb_set_t * +hb_subset_input_set (hb_subset_input_t *input, hb_subset_sets_t set_type) +{ + return input->sets_iter () [set_type]; +} + +/** + * hb_subset_input_get_flags: + * @input: a #hb_subset_input_t object. + * + * Gets all of the subsetting flags in the input object. + * + * Return value: the subsetting flags bit field. + * + * Since: 2.9.0 + **/ +HB_EXTERN hb_subset_flags_t +hb_subset_input_get_flags (hb_subset_input_t *input) +{ + return (hb_subset_flags_t) input->flags; +} + +/** + * hb_subset_input_set_flags: + * @input: a #hb_subset_input_t object. + * @value: bit field of flags + * + * Sets all of the flags in the input object to the values specified by the bit + * field. + * + * Since: 2.9.0 + **/ +HB_EXTERN void +hb_subset_input_set_flags (hb_subset_input_t *input, + unsigned value) +{ + input->flags = (hb_subset_flags_t) value; +} + +/** + * hb_subset_input_set_user_data: (skip) + * @input: a #hb_subset_input_t object. + * @key: The user-data key to set + * @data: A pointer to the user data + * @destroy: (nullable): A callback to call when @data is not needed anymore + * @replace: Whether to replace an existing data with the same key + * + * Attaches a user-data key/data pair to the given subset input object. + * + * Return value: `true` if success, `false` otherwise + * + * Since: 2.9.0 + **/ +hb_bool_t +hb_subset_input_set_user_data (hb_subset_input_t *input, + hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy, + hb_bool_t replace) +{ + return hb_object_set_user_data (input, key, data, destroy, replace); +} + +/** + * hb_subset_input_get_user_data: (skip) + * @input: a #hb_subset_input_t object. + * @key: The user-data key to query + * + * Fetches the user data associated with the specified key, + * attached to the specified subset input object. + * + * Return value: (transfer none): A pointer to the user data + * + * Since: 2.9.0 + **/ +void * +hb_subset_input_get_user_data (const hb_subset_input_t *input, + hb_user_data_key_t *key) +{ + return hb_object_get_user_data (input, key); +} + +#ifdef HB_EXPERIMENTAL_API +#ifndef HB_NO_VAR +/** + * hb_subset_input_pin_axis_to_default: (skip) + * @input: a #hb_subset_input_t object. + * @axis_tag: Tag of the axis to be pinned + * + * Pin an axis to its default location in the given subset input object. + * + * Return value: `true` if success, `false` otherwise + * + * Since: EXPERIMENTAL + **/ +HB_EXTERN hb_bool_t +hb_subset_input_pin_axis_to_default (hb_subset_input_t *input, + hb_face_t *face, + hb_tag_t axis_tag) +{ + hb_ot_var_axis_info_t axis_info; + if (!hb_ot_var_find_axis_info (face, axis_tag, &axis_info)) + return false; + + return input->axes_location->set (axis_tag, axis_info.default_value); +} + +/** + * hb_subset_input_pin_axis_location: (skip) + * @input: a #hb_subset_input_t object. + * @axis_tag: Tag of the axis to be pinned + * @axis_value: Location on the axis to be pinned at + * + * Pin an axis to a fixed location in the given subset input object. + * + * Return value: `true` if success, `false` otherwise + * + * Since: EXPERIMENTAL + **/ +HB_EXTERN hb_bool_t +hb_subset_input_pin_axis_location (hb_subset_input_t *input, + hb_face_t *face, + hb_tag_t axis_tag, + float axis_value) +{ + hb_ot_var_axis_info_t axis_info; + if (!hb_ot_var_find_axis_info (face, axis_tag, &axis_info)) + return false; + + float val = hb_clamp(axis_value, axis_info.min_value, axis_info.max_value); + return input->axes_location->set (axis_tag, val); +} +#endif +#endif + +#ifdef HB_EXPERIMENTAL_API +/** + * hb_subset_preprocess + * @input: a #hb_face_t object. + * + * Preprocesses the face and attaches data that will be needed by the + * subsetter. Future subsetting operations can then use the precomputed data + * to speed up the subsetting operation. + * + * Since: EXPERIMENTAL + **/ + +HB_EXTERN hb_face_t * +hb_subset_preprocess (hb_face_t *source) +{ + hb_subset_input_t* input = hb_subset_input_create_or_fail (); + + hb_set_clear (hb_subset_input_set(input, HB_SUBSET_SETS_UNICODE)); + hb_set_invert (hb_subset_input_set(input, HB_SUBSET_SETS_UNICODE)); + + hb_set_clear (hb_subset_input_set(input, + HB_SUBSET_SETS_LAYOUT_FEATURE_TAG)); + hb_set_invert (hb_subset_input_set(input, + HB_SUBSET_SETS_LAYOUT_FEATURE_TAG)); + + hb_set_clear (hb_subset_input_set(input, + HB_SUBSET_SETS_LAYOUT_SCRIPT_TAG)); + hb_set_invert (hb_subset_input_set(input, + HB_SUBSET_SETS_LAYOUT_SCRIPT_TAG)); + + hb_set_clear (hb_subset_input_set(input, + HB_SUBSET_SETS_NAME_ID)); + hb_set_invert (hb_subset_input_set(input, + HB_SUBSET_SETS_NAME_ID)); + + hb_subset_input_set_flags(input, + HB_SUBSET_FLAGS_NOTDEF_OUTLINE | + HB_SUBSET_FLAGS_GLYPH_NAMES | + HB_SUBSET_FLAGS_RETAIN_GIDS); + input->attach_accelerator_data = true; + + hb_face_t* new_source = hb_subset_or_fail (source, input); + hb_subset_input_destroy (input); + + return new_source; +} +#endif diff --git a/libs/harfbuzz/src/hb-subset-input.hh b/libs/harfbuzz/src/hb-subset-input.hh new file mode 100644 index 00000000..dabb4918 --- /dev/null +++ b/libs/harfbuzz/src/hb-subset-input.hh @@ -0,0 +1,88 @@ +/* + * Copyright © 2018 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): Garret Rieger, Roderick Sheeter + */ + +#ifndef HB_SUBSET_INPUT_HH +#define HB_SUBSET_INPUT_HH + + +#include "hb.hh" + +#include "hb-subset.h" +#include "hb-map.hh" +#include "hb-set.hh" + +#include "hb-font.hh" + +HB_MARK_AS_FLAG_T (hb_subset_flags_t); + +struct hb_subset_input_t +{ + hb_object_header_t header; + + struct sets_t { + hb_set_t *glyphs; + hb_set_t *unicodes; + hb_set_t *no_subset_tables; + hb_set_t *drop_tables; + hb_set_t *name_ids; + hb_set_t *name_languages; + hb_set_t *layout_features; + hb_set_t *layout_scripts; + }; + + union { + sets_t sets; + hb_set_t* set_ptrs[sizeof (sets_t) / sizeof (hb_set_t*)]; + }; + + unsigned flags; + bool attach_accelerator_data = false; + hb_hashmap_t *axes_location; + + inline unsigned num_sets () const + { + return sizeof (set_ptrs) / sizeof (hb_set_t*); + } + + inline hb_array_t sets_iter () + { + return hb_array_t (set_ptrs, num_sets ()); + } + + bool in_error () const + { + for (unsigned i = 0; i < num_sets (); i++) + { + if (unlikely (set_ptrs[i]->in_error ())) + return true; + } + + return axes_location->in_error (); + } +}; + + +#endif /* HB_SUBSET_INPUT_HH */ diff --git a/libs/harfbuzz/src/hb-subset-plan.cc b/libs/harfbuzz/src/hb-subset-plan.cc new file mode 100644 index 00000000..9cf7c9e4 --- /dev/null +++ b/libs/harfbuzz/src/hb-subset-plan.cc @@ -0,0 +1,1027 @@ +/* + * Copyright © 2018 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): Garret Rieger, Roderick Sheeter + */ + +#include "hb-subset-plan.hh" +#include "hb-subset-accelerator.hh" +#include "hb-map.hh" +#include "hb-set.hh" + +#include "hb-ot-cmap-table.hh" +#include "hb-ot-glyf-table.hh" +#include "hb-ot-layout-gdef-table.hh" +#include "hb-ot-layout-gpos-table.hh" +#include "hb-ot-layout-gsub-table.hh" +#include "hb-ot-cff1-table.hh" +#include "hb-ot-color-colr-table.hh" +#include "hb-ot-color-colrv1-closure.hh" +#include "hb-ot-var-fvar-table.hh" +#include "hb-ot-var-avar-table.hh" +#include "hb-ot-stat-table.hh" +#include "hb-ot-math-table.hh" + +using OT::Layout::GSUB; +using OT::Layout::GPOS; + +typedef hb_hashmap_t> script_langsys_map; +#ifndef HB_NO_SUBSET_CFF +static inline void +_add_cff_seac_components (const OT::cff1::accelerator_t &cff, + hb_codepoint_t gid, + hb_set_t *gids_to_retain) +{ + hb_codepoint_t base_gid, accent_gid; + if (cff.get_seac_components (gid, &base_gid, &accent_gid)) + { + gids_to_retain->add (base_gid); + gids_to_retain->add (accent_gid); + } +} +#endif + +static void +_remap_palette_indexes (const hb_set_t *palette_indexes, + hb_map_t *mapping /* OUT */) +{ + unsigned new_idx = 0; + for (unsigned palette_index : palette_indexes->iter ()) + { + if (palette_index == 0xFFFF) + { + mapping->set (palette_index, palette_index); + continue; + } + mapping->set (palette_index, new_idx); + new_idx++; + } +} + +static void +_remap_indexes (const hb_set_t *indexes, + hb_map_t *mapping /* OUT */) +{ + unsigned count = indexes->get_population (); + + for (auto _ : + hb_zip (indexes->iter (), hb_range (count))) + mapping->set (_.first, _.second); + +} + +#ifndef HB_NO_SUBSET_LAYOUT + +/* + * Removes all tags from 'tags' that are not in filter. Additionally eliminates any duplicates. + * Returns true if anything was removed (not including duplicates). + */ +static bool _filter_tag_list(hb_vector_t* tags, /* IN/OUT */ + const hb_set_t* filter) +{ + hb_vector_t out; + out.alloc (tags->get_size() + 1); // +1 is to allocate room for the null terminator. + + bool removed = false; + hb_set_t visited; + + for (hb_tag_t tag : *tags) + { + if (!tag) continue; + if (visited.has (tag)) continue; + + if (!filter->has (tag)) + { + removed = true; + continue; + } + + visited.add (tag); + out.push (tag); + } + + // The collect function needs a null element to signal end of the array. + out.push (HB_TAG_NONE); + + hb_swap (out, *tags); + return removed; +} + +template +static void _collect_layout_indices (hb_subset_plan_t *plan, + const T& table, + hb_set_t *lookup_indices, /* OUT */ + hb_set_t *feature_indices, /* OUT */ + hb_hashmap_t> *feature_record_cond_idx_map, /* OUT */ + hb_hashmap_t *feature_substitutes_map /* OUT */) +{ + unsigned num_features = table.get_feature_count (); + hb_vector_t features; + if (!plan->check_success (features.resize (num_features))) return; + table.get_feature_tags (0, &num_features, features.arrayZ); + bool retain_all_features = !_filter_tag_list (&features, plan->layout_features); + + unsigned num_scripts = table.get_script_count (); + hb_vector_t scripts; + if (!plan->check_success (scripts.resize (num_scripts))) return; + table.get_script_tags (0, &num_scripts, scripts.arrayZ); + bool retain_all_scripts = !_filter_tag_list (&scripts, plan->layout_scripts); + + if (!plan->check_success (!features.in_error ()) || !features + || !plan->check_success (!scripts.in_error ()) || !scripts) + return; + + hb_ot_layout_collect_features (plan->source, + T::tableTag, + retain_all_scripts ? nullptr : scripts.arrayZ, + nullptr, + retain_all_features ? nullptr : features.arrayZ, + feature_indices); + +#ifndef HB_NO_VAR + // collect feature substitutes with variations + if (!plan->user_axes_location->is_empty ()) + { + hb_hashmap_t, unsigned> conditionset_map; + OT::hb_collect_feature_substitutes_with_var_context_t c = + { + plan->axes_old_index_tag_map, + plan->axes_location, + feature_record_cond_idx_map, + feature_substitutes_map, + feature_indices, + true, + 0, + &conditionset_map + }; + table.collect_feature_substitutes_with_variations (&c); + } +#endif + + for (unsigned feature_index : *feature_indices) + { + const OT::Feature* f = &(table.get_feature (feature_index)); + const OT::Feature **p = nullptr; + if (feature_substitutes_map->has (feature_index, &p)) + f = *p; + + f->add_lookup_indexes_to (lookup_indices); + } + + table.feature_variation_collect_lookups (feature_indices, feature_substitutes_map, lookup_indices); +} + + +static inline void +_GSUBGPOS_find_duplicate_features (const OT::GSUBGPOS &g, + const hb_map_t *lookup_indices, + const hb_set_t *feature_indices, + const hb_hashmap_t *feature_substitutes_map, + hb_map_t *duplicate_feature_map /* OUT */) +{ + if (feature_indices->is_empty ()) return; + hb_hashmap_t> unique_features; + //find out duplicate features after subset + for (unsigned i : feature_indices->iter ()) + { + hb_tag_t t = g.get_feature_tag (i); + if (t == HB_MAP_VALUE_INVALID) continue; + if (!unique_features.has (t)) + { + if (unlikely (!unique_features.set (t, hb::unique_ptr {hb_set_create ()}))) + return; + if (unique_features.has (t)) + unique_features.get (t)->add (i); + duplicate_feature_map->set (i, i); + continue; + } + + bool found = false; + + hb_set_t* same_tag_features = unique_features.get (t); + for (unsigned other_f_index : same_tag_features->iter ()) + { + const OT::Feature* f = &(g.get_feature (i)); + const OT::Feature **p = nullptr; + if (feature_substitutes_map->has (i, &p)) + f = *p; + + const OT::Feature* other_f = &(g.get_feature (other_f_index)); + if (feature_substitutes_map->has (other_f_index, &p)) + f = *p; + + auto f_iter = + + hb_iter (f->lookupIndex) + | hb_filter (lookup_indices) + ; + + auto other_f_iter = + + hb_iter (other_f->lookupIndex) + | hb_filter (lookup_indices) + ; + + bool is_equal = true; + for (; f_iter && other_f_iter; f_iter++, other_f_iter++) + { + unsigned a = *f_iter; + unsigned b = *other_f_iter; + if (a != b) { is_equal = false; break; } + } + + if (is_equal == false || f_iter || other_f_iter) continue; + + found = true; + duplicate_feature_map->set (i, other_f_index); + break; + } + + if (found == false) + { + same_tag_features->add (i); + duplicate_feature_map->set (i, i); + } + } +} + +template +static inline void +_closure_glyphs_lookups_features (hb_subset_plan_t *plan, + hb_set_t *gids_to_retain, + hb_map_t *lookups, + hb_map_t *features, + script_langsys_map *langsys_map, + hb_hashmap_t> *feature_record_cond_idx_map, + hb_hashmap_t *feature_substitutes_map) +{ + hb_blob_ptr_t table = plan->source_table (); + hb_tag_t table_tag = table->tableTag; + hb_set_t lookup_indices, feature_indices; + _collect_layout_indices (plan, + *table, + &lookup_indices, + &feature_indices, + feature_record_cond_idx_map, + feature_substitutes_map); + + if (table_tag == HB_OT_TAG_GSUB) + hb_ot_layout_lookups_substitute_closure (plan->source, + &lookup_indices, + gids_to_retain); + table->closure_lookups (plan->source, + gids_to_retain, + &lookup_indices); + _remap_indexes (&lookup_indices, lookups); + + // prune features + table->prune_features (lookups, + plan->user_axes_location->is_empty () ? nullptr : feature_record_cond_idx_map, + feature_substitutes_map, + &feature_indices); + hb_map_t duplicate_feature_map; + _GSUBGPOS_find_duplicate_features (*table, lookups, &feature_indices, feature_substitutes_map, &duplicate_feature_map); + + feature_indices.clear (); + table->prune_langsys (&duplicate_feature_map, plan->layout_scripts, langsys_map, &feature_indices); + _remap_indexes (&feature_indices, features); + + table.destroy (); +} + +#endif + +#ifndef HB_NO_VAR +static inline void +_generate_varstore_inner_maps (const hb_set_t& varidx_set, + unsigned subtable_count, + hb_vector_t &inner_maps /* OUT */) +{ + if (varidx_set.is_empty () || subtable_count == 0) return; + + inner_maps.resize (subtable_count); + for (unsigned idx : varidx_set) + { + uint16_t major = idx >> 16; + uint16_t minor = idx & 0xFFFF; + + if (major >= subtable_count) + continue; + inner_maps[major].add (minor); + } +} + +static inline hb_font_t* +_get_hb_font_with_variations (const hb_subset_plan_t *plan) +{ + hb_font_t *font = hb_font_create (plan->source); + + hb_vector_t vars; + vars.alloc (plan->user_axes_location->get_population ()); + + for (auto _ : *plan->user_axes_location) + { + hb_variation_t var; + var.tag = _.first; + var.value = _.second; + vars.push (var); + } + + hb_font_set_variations (font, vars.arrayZ, plan->user_axes_location->get_population ()); + return font; +} + +static inline void +_collect_layout_variation_indices (hb_subset_plan_t* plan) +{ + hb_blob_ptr_t gdef = plan->source_table (); + hb_blob_ptr_t gpos = plan->source_table (); + + if (!gdef->has_data ()) + { + gdef.destroy (); + gpos.destroy (); + return; + } + + const OT::VariationStore *var_store = nullptr; + hb_set_t varidx_set; + hb_font_t *font = nullptr; + float *store_cache = nullptr; + bool collect_delta = plan->pinned_at_default ? false : true; + if (collect_delta) + { + font = _get_hb_font_with_variations (plan); + if (gdef->has_var_store ()) + { + var_store = &(gdef->get_var_store ()); + store_cache = var_store->create_cache (); + } + } + + OT::hb_collect_variation_indices_context_t c (&varidx_set, + plan->layout_variation_idx_delta_map, + font, var_store, + plan->_glyphset_gsub, + plan->gpos_lookups, + store_cache); + gdef->collect_variation_indices (&c); + + if (hb_ot_layout_has_positioning (plan->source)) + gpos->collect_variation_indices (&c); + + hb_font_destroy (font); + var_store->destroy_cache (store_cache); + + gdef->remap_layout_variation_indices (&varidx_set, plan->layout_variation_idx_delta_map); + + unsigned subtable_count = gdef->has_var_store () ? gdef->get_var_store ().get_sub_table_count () : 0; + _generate_varstore_inner_maps (varidx_set, subtable_count, plan->gdef_varstore_inner_maps); + + gdef.destroy (); + gpos.destroy (); +} +#endif + +static inline void +_cmap_closure (hb_face_t *face, + const hb_set_t *unicodes, + hb_set_t *glyphset) +{ + OT::cmap::accelerator_t cmap (face); + cmap.table->closure_glyphs (unicodes, glyphset); +} + +static void _colr_closure (hb_face_t *face, + hb_map_t *layers_map, + hb_map_t *palettes_map, + hb_set_t *glyphs_colred) +{ + OT::COLR::accelerator_t colr (face); + if (!colr.is_valid ()) return; + + hb_set_t palette_indices, layer_indices; + // Collect all glyphs referenced by COLRv0 + hb_set_t glyphset_colrv0; + for (hb_codepoint_t gid : *glyphs_colred) + colr.closure_glyphs (gid, &glyphset_colrv0); + + glyphs_colred->union_ (glyphset_colrv0); + + //closure for COLRv1 + colr.closure_forV1 (glyphs_colred, &layer_indices, &palette_indices); + + colr.closure_V0palette_indices (glyphs_colred, &palette_indices); + _remap_indexes (&layer_indices, layers_map); + _remap_palette_indexes (&palette_indices, palettes_map); +} + +static inline void +_math_closure (hb_subset_plan_t *plan, + hb_set_t *glyphset) +{ + hb_blob_ptr_t math = plan->source_table (); + if (math->has_data ()) + math->closure_glyphs (glyphset); + math.destroy (); +} + + +static inline void +_remove_invalid_gids (hb_set_t *glyphs, + unsigned int num_glyphs) +{ + glyphs->del_range (num_glyphs, HB_SET_VALUE_INVALID); +} + +static void +_populate_unicodes_to_retain (const hb_set_t *unicodes, + const hb_set_t *glyphs, + hb_subset_plan_t *plan) +{ + OT::cmap::accelerator_t cmap (plan->source); + unsigned size_threshold = plan->source->get_num_glyphs (); + if (glyphs->is_empty () && unicodes->get_population () < size_threshold) + { + + const hb_map_t* unicode_to_gid = nullptr; + if (plan->accelerator) + unicode_to_gid = &plan->accelerator->unicode_to_gid; + + // This is approach to collection is faster, but can only be used if glyphs + // are not being explicitly added to the subset and the input unicodes set is + // not excessively large (eg. an inverted set). + plan->unicode_to_new_gid_list.alloc (unicodes->get_population ()); + if (!unicode_to_gid) { + for (hb_codepoint_t cp : *unicodes) + { + hb_codepoint_t gid; + if (!cmap.get_nominal_glyph (cp, &gid)) + { + DEBUG_MSG(SUBSET, nullptr, "Drop U+%04X; no gid", cp); + continue; + } + + plan->codepoint_to_glyph->set (cp, gid); + plan->unicode_to_new_gid_list.push (hb_pair (cp, gid)); + } + } else { + // Use in memory unicode to gid map it's faster then looking up from + // the map. This code is mostly duplicated from above to avoid doing + // conditionals on the presence of the unicode_to_gid map each + // iteration. + for (hb_codepoint_t cp : *unicodes) + { + hb_codepoint_t gid = unicode_to_gid->get (cp); + if (gid == HB_MAP_VALUE_INVALID) + { + DEBUG_MSG(SUBSET, nullptr, "Drop U+%04X; no gid", cp); + continue; + } + + plan->codepoint_to_glyph->set (cp, gid); + plan->unicode_to_new_gid_list.push (hb_pair (cp, gid)); + } + } + } + else + { + // This approach is slower, but can handle adding in glyphs to the subset and will match + // them with cmap entries. + + hb_map_t unicode_glyphid_map_storage; + hb_set_t cmap_unicodes_storage; + const hb_map_t* unicode_glyphid_map = &unicode_glyphid_map_storage; + const hb_set_t* cmap_unicodes = &cmap_unicodes_storage; + + if (!plan->accelerator) { + cmap.collect_mapping (&cmap_unicodes_storage, &unicode_glyphid_map_storage); + plan->unicode_to_new_gid_list.alloc (hb_min(unicodes->get_population () + + glyphs->get_population (), + cmap_unicodes->get_population ())); + } else { + unicode_glyphid_map = &plan->accelerator->unicode_to_gid; + cmap_unicodes = &plan->accelerator->unicodes; + } + + for (hb_codepoint_t cp : *cmap_unicodes) + { + hb_codepoint_t gid = (*unicode_glyphid_map)[cp]; + if (!unicodes->has (cp) && !glyphs->has (gid)) + continue; + + plan->codepoint_to_glyph->set (cp, gid); + plan->unicode_to_new_gid_list.push (hb_pair (cp, gid)); + } + + /* Add gids which where requested, but not mapped in cmap */ + for (hb_codepoint_t gid : *glyphs) + { + if (gid >= plan->source->get_num_glyphs ()) + break; + plan->_glyphset_gsub->add (gid); + } + } + + auto &arr = plan->unicode_to_new_gid_list; + if (arr.length) + { + plan->unicodes->add_sorted_array (&arr.arrayZ->first, arr.length, sizeof (*arr.arrayZ)); + plan->_glyphset_gsub->add_array (&arr.arrayZ->second, arr.length, sizeof (*arr.arrayZ)); + } +} + +#ifndef HB_COMPOSITE_OPERATIONS_PER_GLYPH +#define HB_COMPOSITE_OPERATIONS_PER_GLYPH 64 +#endif + +static unsigned +_glyf_add_gid_and_children (const OT::glyf_accelerator_t &glyf, + hb_codepoint_t gid, + hb_set_t *gids_to_retain, + int operation_count, + unsigned depth = 0) +{ + if (unlikely (depth++ > HB_MAX_NESTING_LEVEL)) return operation_count; + if (unlikely (--operation_count < 0)) return operation_count; + /* Check if is already visited */ + if (gids_to_retain->has (gid)) return operation_count; + + gids_to_retain->add (gid); + + for (auto item : glyf.glyph_for_gid (gid).get_composite_iterator ()) + operation_count = + _glyf_add_gid_and_children (glyf, + item.get_gid (), + gids_to_retain, + operation_count, + depth); + + return operation_count; +} + +static void +_populate_gids_to_retain (hb_subset_plan_t* plan, + hb_set_t* drop_tables) +{ + OT::glyf_accelerator_t glyf (plan->source); +#ifndef HB_NO_SUBSET_CFF + OT::cff1::accelerator_t cff (plan->source); +#endif + + plan->_glyphset_gsub->add (0); // Not-def + + _cmap_closure (plan->source, plan->unicodes, plan->_glyphset_gsub); + +#ifndef HB_NO_SUBSET_LAYOUT + if (!drop_tables->has (HB_OT_TAG_GSUB)) + // closure all glyphs/lookups/features needed for GSUB substitutions. + _closure_glyphs_lookups_features ( + plan, + plan->_glyphset_gsub, + plan->gsub_lookups, + plan->gsub_features, + plan->gsub_langsys, + plan->gsub_feature_record_cond_idx_map, + plan->gsub_feature_substitutes_map); + + if (!drop_tables->has (HB_OT_TAG_GPOS)) + _closure_glyphs_lookups_features ( + plan, + plan->_glyphset_gsub, + plan->gpos_lookups, + plan->gpos_features, + plan->gpos_langsys, + plan->gpos_feature_record_cond_idx_map, + plan->gpos_feature_substitutes_map); +#endif + _remove_invalid_gids (plan->_glyphset_gsub, plan->source->get_num_glyphs ()); + + hb_set_set (plan->_glyphset_mathed, plan->_glyphset_gsub); + if (!drop_tables->has (HB_OT_TAG_MATH)) + { + _math_closure (plan, plan->_glyphset_mathed); + _remove_invalid_gids (plan->_glyphset_mathed, plan->source->get_num_glyphs ()); + } + + hb_set_t cur_glyphset = *plan->_glyphset_mathed; + if (!drop_tables->has (HB_OT_TAG_COLR)) + { + _colr_closure (plan->source, plan->colrv1_layers, plan->colr_palettes, &cur_glyphset); + _remove_invalid_gids (&cur_glyphset, plan->source->get_num_glyphs ()); + } + + hb_set_set (plan->_glyphset_colred, &cur_glyphset); + + /* Populate a full set of glyphs to retain by adding all referenced + * composite glyphs. */ + if (glyf.has_data ()) + for (hb_codepoint_t gid : cur_glyphset) + _glyf_add_gid_and_children (glyf, gid, plan->_glyphset, + cur_glyphset.get_population () * HB_COMPOSITE_OPERATIONS_PER_GLYPH); + else + plan->_glyphset->union_ (cur_glyphset); +#ifndef HB_NO_SUBSET_CFF + if (cff.is_valid ()) + for (hb_codepoint_t gid : cur_glyphset) + _add_cff_seac_components (cff, gid, plan->_glyphset); +#endif + + _remove_invalid_gids (plan->_glyphset, plan->source->get_num_glyphs ()); + + +#ifndef HB_NO_VAR + if (!drop_tables->has (HB_OT_TAG_GDEF)) + _collect_layout_variation_indices (plan); +#endif +} + +static void +_create_glyph_map_gsub (const hb_set_t* glyph_set_gsub, + const hb_map_t* glyph_map, + hb_map_t* out) +{ + + hb_iter (glyph_set_gsub) + | hb_map ([&] (hb_codepoint_t gid) { + return hb_pair_t (gid, + glyph_map->get (gid)); + }) + | hb_sink (out) + ; +} + +static void +_create_old_gid_to_new_gid_map (const hb_face_t *face, + bool retain_gids, + const hb_set_t *all_gids_to_retain, + hb_map_t *glyph_map, /* OUT */ + hb_map_t *reverse_glyph_map, /* OUT */ + unsigned int *num_glyphs /* OUT */) +{ + unsigned pop = all_gids_to_retain->get_population (); + reverse_glyph_map->resize (pop); + glyph_map->resize (pop); + + if (!retain_gids) + { + + hb_enumerate (hb_iter (all_gids_to_retain), (hb_codepoint_t) 0) + | hb_sink (reverse_glyph_map) + ; + *num_glyphs = reverse_glyph_map->get_population (); + } + else + { + + hb_iter (all_gids_to_retain) + | hb_map ([] (hb_codepoint_t _) { + return hb_pair_t (_, _); + }) + | hb_sink (reverse_glyph_map) + ; + + hb_codepoint_t max_glyph = HB_SET_VALUE_INVALID; + hb_set_previous (all_gids_to_retain, &max_glyph); + + *num_glyphs = max_glyph + 1; + } + + + reverse_glyph_map->iter () + | hb_map (&hb_pair_t::reverse) + | hb_sink (glyph_map) + ; +} + +static void +_nameid_closure (hb_face_t *face, + hb_set_t *nameids, + bool all_axes_pinned, + hb_hashmap_t *user_axes_location) +{ +#ifndef HB_NO_STYLE + face->table.STAT->collect_name_ids (user_axes_location, nameids); +#endif +#ifndef HB_NO_VAR + if (!all_axes_pinned) + face->table.fvar->collect_name_ids (user_axes_location, nameids); +#endif +} + +#ifndef HB_NO_VAR +static void +_normalize_axes_location (hb_face_t *face, hb_subset_plan_t *plan) +{ + if (plan->user_axes_location->is_empty ()) + return; + + hb_array_t axes = face->table.fvar->get_axes (); + + bool has_avar = face->table.avar->has_data (); + const OT::SegmentMaps *seg_maps = nullptr; + if (has_avar) + seg_maps = face->table.avar->get_segment_maps (); + + bool axis_not_pinned = false; + unsigned old_axis_idx = 0, new_axis_idx = 0; + for (const auto& axis : axes) + { + hb_tag_t axis_tag = axis.get_axis_tag (); + plan->axes_old_index_tag_map->set (old_axis_idx, axis_tag); + + if (!plan->user_axes_location->has (axis_tag)) + { + axis_not_pinned = true; + plan->axes_index_map->set (old_axis_idx, new_axis_idx); + new_axis_idx++; + } + else + { + int normalized_v = axis.normalize_axis_value (plan->user_axes_location->get (axis_tag)); + if (has_avar && old_axis_idx < face->table.avar->get_axis_count ()) + { + normalized_v = seg_maps->map (normalized_v); + } + plan->axes_location->set (axis_tag, normalized_v); + if (normalized_v != 0) + plan->pinned_at_default = false; + } + if (has_avar) + seg_maps = &StructAfter (*seg_maps); + + old_axis_idx++; + } + plan->all_axes_pinned = !axis_not_pinned; +} +#endif +/** + * hb_subset_plan_create_or_fail: + * @face: font face to create the plan for. + * @input: a #hb_subset_input_t input. + * + * Computes a plan for subsetting the supplied face according + * to a provided input. The plan describes + * which tables and glyphs should be retained. + * + * Return value: (transfer full): New subset plan. Destroy with + * hb_subset_plan_destroy(). If there is a failure creating the plan + * nullptr will be returned. + * + * Since: 4.0.0 + **/ +hb_subset_plan_t * +hb_subset_plan_create_or_fail (hb_face_t *face, + const hb_subset_input_t *input) +{ + hb_subset_plan_t *plan; + if (unlikely (!(plan = hb_object_create ()))) + return nullptr; + + plan->successful = true; + plan->flags = input->flags; + plan->unicodes = hb_set_create (); + + plan->unicode_to_new_gid_list.init (); + + plan->name_ids = hb_set_copy (input->sets.name_ids); + plan->name_languages = hb_set_copy (input->sets.name_languages); + plan->layout_features = hb_set_copy (input->sets.layout_features); + plan->layout_scripts = hb_set_copy (input->sets.layout_scripts); + plan->glyphs_requested = hb_set_copy (input->sets.glyphs); + plan->drop_tables = hb_set_copy (input->sets.drop_tables); + plan->no_subset_tables = hb_set_copy (input->sets.no_subset_tables); + plan->source = hb_face_reference (face); + plan->dest = hb_face_builder_create (); + + plan->_glyphset = hb_set_create (); + plan->_glyphset_gsub = hb_set_create (); + plan->_glyphset_mathed = hb_set_create (); + plan->_glyphset_colred = hb_set_create (); + plan->codepoint_to_glyph = hb_map_create (); + plan->glyph_map = hb_map_create (); + plan->reverse_glyph_map = hb_map_create (); + plan->glyph_map_gsub = hb_map_create (); + plan->gsub_lookups = hb_map_create (); + plan->gpos_lookups = hb_map_create (); + + plan->check_success (plan->gsub_langsys = hb_hashmap_create> ()); + plan->check_success (plan->gpos_langsys = hb_hashmap_create> ()); + + plan->gsub_features = hb_map_create (); + plan->gpos_features = hb_map_create (); + + plan->check_success (plan->gsub_feature_record_cond_idx_map = hb_hashmap_create> ()); + plan->check_success (plan->gpos_feature_record_cond_idx_map = hb_hashmap_create> ()); + + plan->check_success (plan->gsub_feature_substitutes_map = hb_hashmap_create ()); + plan->check_success (plan->gpos_feature_substitutes_map = hb_hashmap_create ()); + + plan->colrv1_layers = hb_map_create (); + plan->colr_palettes = hb_map_create (); + plan->check_success (plan->layout_variation_idx_delta_map = hb_hashmap_create> ()); + plan->gdef_varstore_inner_maps.init (); + + plan->check_success (plan->sanitized_table_cache = hb_hashmap_create> ()); + plan->check_success (plan->axes_location = hb_hashmap_create ()); + plan->check_success (plan->user_axes_location = hb_hashmap_create ()); + if (plan->user_axes_location && input->axes_location) + *plan->user_axes_location = *input->axes_location; + plan->check_success (plan->axes_index_map = hb_map_create ()); + plan->check_success (plan->axes_old_index_tag_map = hb_map_create ()); + plan->all_axes_pinned = false; + plan->pinned_at_default = true; + + plan->check_success (plan->vmtx_map = hb_hashmap_create> ()); + plan->check_success (plan->hmtx_map = hb_hashmap_create> ()); + + void* accel = hb_face_get_user_data(face, hb_subset_accelerator_t::user_data_key()); + + plan->attach_accelerator_data = input->attach_accelerator_data; + if (accel) + plan->accelerator = (hb_subset_accelerator_t*) accel; + + + if (unlikely (plan->in_error ())) { + hb_subset_plan_destroy (plan); + return nullptr; + } + +#ifndef HB_NO_VAR + _normalize_axes_location (face, plan); +#endif + + _populate_unicodes_to_retain (input->sets.unicodes, input->sets.glyphs, plan); + + _populate_gids_to_retain (plan, input->sets.drop_tables); + + _create_old_gid_to_new_gid_map (face, + input->flags & HB_SUBSET_FLAGS_RETAIN_GIDS, + plan->_glyphset, + plan->glyph_map, + plan->reverse_glyph_map, + &plan->_num_output_glyphs); + + _create_glyph_map_gsub ( + plan->_glyphset_gsub, + plan->glyph_map, + plan->glyph_map_gsub); + + // Now that we have old to new gid map update the unicode to new gid list. + for (unsigned i = 0; i < plan->unicode_to_new_gid_list.length; i++) + { + // Use raw array access for performance. + plan->unicode_to_new_gid_list.arrayZ[i].second = + plan->glyph_map->get(plan->unicode_to_new_gid_list.arrayZ[i].second); + } + + _nameid_closure (face, plan->name_ids, plan->all_axes_pinned, plan->user_axes_location); + if (unlikely (plan->in_error ())) { + hb_subset_plan_destroy (plan); + return nullptr; + } + return plan; +} + +/** + * hb_subset_plan_destroy: + * @plan: a #hb_subset_plan_t + * + * Decreases the reference count on @plan, and if it reaches zero, destroys + * @plan, freeing all memory. + * + * Since: 4.0.0 + **/ +void +hb_subset_plan_destroy (hb_subset_plan_t *plan) +{ + if (!hb_object_destroy (plan)) return; + + hb_free (plan); +} + +/** + * hb_subset_plan_old_to_new_glyph_mapping: + * @plan: a subsetting plan. + * + * Returns the mapping between glyphs in the original font to glyphs in the + * subset that will be produced by @plan + * + * Return value: (transfer none): + * A pointer to the #hb_map_t of the mapping. + * + * Since: 4.0.0 + **/ +const hb_map_t* +hb_subset_plan_old_to_new_glyph_mapping (const hb_subset_plan_t *plan) +{ + return plan->glyph_map; +} + +/** + * hb_subset_plan_new_to_old_glyph_mapping: + * @plan: a subsetting plan. + * + * Returns the mapping between glyphs in the subset that will be produced by + * @plan and the glyph in the original font. + * + * Return value: (transfer none): + * A pointer to the #hb_map_t of the mapping. + * + * Since: 4.0.0 + **/ +const hb_map_t* +hb_subset_plan_new_to_old_glyph_mapping (const hb_subset_plan_t *plan) +{ + return plan->reverse_glyph_map; +} + +/** + * hb_subset_plan_unicode_to_old_glyph_mapping: + * @plan: a subsetting plan. + * + * Returns the mapping between codepoints in the original font and the + * associated glyph id in the original font. + * + * Return value: (transfer none): + * A pointer to the #hb_map_t of the mapping. + * + * Since: 4.0.0 + **/ +const hb_map_t* +hb_subset_plan_unicode_to_old_glyph_mapping (const hb_subset_plan_t *plan) +{ + return plan->codepoint_to_glyph; +} + +/** + * hb_subset_plan_reference: (skip) + * @plan: a #hb_subset_plan_t object. + * + * Increases the reference count on @plan. + * + * Return value: @plan. + * + * Since: 4.0.0 + **/ +hb_subset_plan_t * +hb_subset_plan_reference (hb_subset_plan_t *plan) +{ + return hb_object_reference (plan); +} + +/** + * hb_subset_plan_set_user_data: (skip) + * @plan: a #hb_subset_plan_t object. + * @key: The user-data key to set + * @data: A pointer to the user data + * @destroy: (nullable): A callback to call when @data is not needed anymore + * @replace: Whether to replace an existing data with the same key + * + * Attaches a user-data key/data pair to the given subset plan object. + * + * Return value: `true` if success, `false` otherwise + * + * Since: 4.0.0 + **/ +hb_bool_t +hb_subset_plan_set_user_data (hb_subset_plan_t *plan, + hb_user_data_key_t *key, + void *data, + hb_destroy_func_t destroy, + hb_bool_t replace) +{ + return hb_object_set_user_data (plan, key, data, destroy, replace); +} + +/** + * hb_subset_plan_get_user_data: (skip) + * @plan: a #hb_subset_plan_t object. + * @key: The user-data key to query + * + * Fetches the user data associated with the specified key, + * attached to the specified subset plan object. + * + * Return value: (transfer none): A pointer to the user data + * + * Since: 4.0.0 + **/ +void * +hb_subset_plan_get_user_data (const hb_subset_plan_t *plan, + hb_user_data_key_t *key) +{ + return hb_object_get_user_data (plan, key); +} diff --git a/libs/harfbuzz/src/hb-subset-plan.hh b/libs/harfbuzz/src/hb-subset-plan.hh new file mode 100644 index 00000000..15fabba9 --- /dev/null +++ b/libs/harfbuzz/src/hb-subset-plan.hh @@ -0,0 +1,312 @@ +/* + * Copyright © 2018 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): Garret Rieger, Roderick Sheeter + */ + +#ifndef HB_SUBSET_PLAN_HH +#define HB_SUBSET_PLAN_HH + +#include "hb.hh" + +#include "hb-subset.h" +#include "hb-subset-input.hh" +#include "hb-subset-accelerator.hh" + +#include "hb-map.hh" +#include "hb-bimap.hh" +#include "hb-set.hh" + +namespace OT { +struct Feature; +} + +struct hb_subset_plan_t +{ + hb_subset_plan_t () + {} + + ~hb_subset_plan_t() + { + hb_set_destroy (unicodes); + hb_set_destroy (name_ids); + hb_set_destroy (name_languages); + hb_set_destroy (layout_features); + hb_set_destroy (layout_scripts); + hb_set_destroy (glyphs_requested); + hb_set_destroy (drop_tables); + hb_set_destroy (no_subset_tables); + hb_face_destroy (source); + hb_face_destroy (dest); + hb_map_destroy (codepoint_to_glyph); + hb_map_destroy (glyph_map); + hb_map_destroy (reverse_glyph_map); + hb_map_destroy (glyph_map_gsub); + hb_set_destroy (_glyphset); + hb_set_destroy (_glyphset_gsub); + hb_set_destroy (_glyphset_mathed); + hb_set_destroy (_glyphset_colred); + hb_map_destroy (gsub_lookups); + hb_map_destroy (gpos_lookups); + hb_map_destroy (gsub_features); + hb_map_destroy (gpos_features); + hb_map_destroy (colrv1_layers); + hb_map_destroy (colr_palettes); + hb_map_destroy (axes_index_map); + hb_map_destroy (axes_old_index_tag_map); + + hb_hashmap_destroy (gsub_langsys); + hb_hashmap_destroy (gpos_langsys); + hb_hashmap_destroy (gsub_feature_record_cond_idx_map); + hb_hashmap_destroy (gpos_feature_record_cond_idx_map); + hb_hashmap_destroy (gsub_feature_substitutes_map); + hb_hashmap_destroy (gpos_feature_substitutes_map); + hb_hashmap_destroy (axes_location); + hb_hashmap_destroy (sanitized_table_cache); + hb_hashmap_destroy (hmtx_map); + hb_hashmap_destroy (vmtx_map); + hb_hashmap_destroy (layout_variation_idx_delta_map); + + if (user_axes_location) + { + hb_object_destroy (user_axes_location); + hb_free (user_axes_location); + } + } + + hb_object_header_t header; + + bool successful; + unsigned flags; + bool attach_accelerator_data = false; + + // For each cp that we'd like to retain maps to the corresponding gid. + hb_set_t *unicodes; + hb_vector_t> unicode_to_new_gid_list; + + // name_ids we would like to retain + hb_set_t *name_ids; + + // name_languages we would like to retain + hb_set_t *name_languages; + + //layout features which will be preserved + hb_set_t *layout_features; + + // layout scripts which will be preserved. + hb_set_t *layout_scripts; + + //glyph ids requested to retain + hb_set_t *glyphs_requested; + + // Tables which should not be processed, just pass them through. + hb_set_t *no_subset_tables; + + // Tables which should be dropped. + hb_set_t *drop_tables; + + // The glyph subset + hb_map_t *codepoint_to_glyph; + + // Old -> New glyph id mapping + hb_map_t *glyph_map; + hb_map_t *reverse_glyph_map; + hb_map_t *glyph_map_gsub; + + // Plan is only good for a specific source/dest so keep them with it + hb_face_t *source; + hb_face_t *dest; + + unsigned int _num_output_glyphs; + hb_set_t *_glyphset; + hb_set_t *_glyphset_gsub; + hb_set_t *_glyphset_mathed; + hb_set_t *_glyphset_colred; + + //active lookups we'd like to retain + hb_map_t *gsub_lookups; + hb_map_t *gpos_lookups; + + //active langsys we'd like to retain + hb_hashmap_t> *gsub_langsys; + hb_hashmap_t> *gpos_langsys; + + //active features after removing redundant langsys and prune_features + hb_map_t *gsub_features; + hb_map_t *gpos_features; + + //active feature variation records/condition index with variations + hb_hashmap_t> *gsub_feature_record_cond_idx_map; + hb_hashmap_t> *gpos_feature_record_cond_idx_map; + + //feature index-> address of substituation feature table mapping with + //variations + hb_hashmap_t *gsub_feature_substitutes_map; + hb_hashmap_t *gpos_feature_substitutes_map; + + //active layers/palettes we'd like to retain + hb_map_t *colrv1_layers; + hb_map_t *colr_palettes; + + //Old layout item variation index -> (New varidx, delta) mapping + hb_hashmap_t> *layout_variation_idx_delta_map; + + //gdef varstore retained varidx mapping + hb_vector_t gdef_varstore_inner_maps; + + hb_hashmap_t>* sanitized_table_cache; + //normalized axes location map + hb_hashmap_t *axes_location; + //user specified axes location map + hb_hashmap_t *user_axes_location; + //retained old axis index -> new axis index mapping in fvar axis array + hb_map_t *axes_index_map; + //axis_index->axis_tag mapping in fvar axis array + hb_map_t *axes_old_index_tag_map; + bool all_axes_pinned; + bool pinned_at_default; + + //hmtx metrics map: new gid->(advance, lsb) + hb_hashmap_t> *hmtx_map; + //vmtx metrics map: new gid->(advance, lsb) + hb_hashmap_t> *vmtx_map; + + const hb_subset_accelerator_t* accelerator; + + public: + + template + hb_blob_ptr_t source_table() + { + if (sanitized_table_cache + && !sanitized_table_cache->in_error () + && sanitized_table_cache->has (T::tableTag)) { + return hb_blob_reference (sanitized_table_cache->get (T::tableTag).get ()); + } + + hb::unique_ptr table_blob {hb_sanitize_context_t ().reference_table (source)}; + hb_blob_t* ret = hb_blob_reference (table_blob.get ()); + + if (likely (sanitized_table_cache)) + sanitized_table_cache->set (T::tableTag, + std::move (table_blob)); + + return ret; + } + + bool in_error () const { return !successful; } + + bool check_success(bool success) + { + successful = (successful && success); + return successful; + } + + /* + * The set of input glyph ids which will be retained in the subset. + * Does NOT include ids kept due to retain_gids. You probably want to use + * glyph_map/reverse_glyph_map. + */ + inline const hb_set_t * + glyphset () const + { + return _glyphset; + } + + /* + * The set of input glyph ids which will be retained in the subset. + */ + inline const hb_set_t * + glyphset_gsub () const + { + return _glyphset_gsub; + } + + /* + * The total number of output glyphs in the final subset. + */ + inline unsigned int + num_output_glyphs () const + { + return _num_output_glyphs; + } + + /* + * Given an output gid , returns true if that glyph id is an empty + * glyph (ie. it's a gid that we are dropping all data for). + */ + inline bool is_empty_glyph (hb_codepoint_t gid) const + { + return !_glyphset->has (gid); + } + + inline bool new_gid_for_codepoint (hb_codepoint_t codepoint, + hb_codepoint_t *new_gid) const + { + hb_codepoint_t old_gid = codepoint_to_glyph->get (codepoint); + if (old_gid == HB_MAP_VALUE_INVALID) + return false; + + return new_gid_for_old_gid (old_gid, new_gid); + } + + inline bool new_gid_for_old_gid (hb_codepoint_t old_gid, + hb_codepoint_t *new_gid) const + { + hb_codepoint_t gid = glyph_map->get (old_gid); + if (gid == HB_MAP_VALUE_INVALID) + return false; + + *new_gid = gid; + return true; + } + + inline bool old_gid_for_new_gid (hb_codepoint_t new_gid, + hb_codepoint_t *old_gid) const + { + hb_codepoint_t gid = reverse_glyph_map->get (new_gid); + if (gid == HB_MAP_VALUE_INVALID) + return false; + + *old_gid = gid; + return true; + } + + inline bool + add_table (hb_tag_t tag, + hb_blob_t *contents) + { + if (HB_DEBUG_SUBSET) + { + hb_blob_t *source_blob = source->reference_table (tag); + DEBUG_MSG(SUBSET, nullptr, "add table %c%c%c%c, dest %d bytes, source %d bytes", + HB_UNTAG(tag), + hb_blob_get_length (contents), + hb_blob_get_length (source_blob)); + hb_blob_destroy (source_blob); + } + return hb_face_builder_add_table (dest, tag, contents); + } +}; + +#endif /* HB_SUBSET_PLAN_HH */ diff --git a/libs/harfbuzz/src/hb-subset-repacker.cc b/libs/harfbuzz/src/hb-subset-repacker.cc new file mode 100644 index 00000000..55ca48d7 --- /dev/null +++ b/libs/harfbuzz/src/hb-subset-repacker.cc @@ -0,0 +1,58 @@ +/* + * Copyright © 2022 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. + * + */ +#include "hb-repacker.hh" + +#ifdef HB_EXPERIMENTAL_API + +/** + * hb_subset_repack_or_fail: + * @table_tag: tag of the table being packed, needed to allow table specific optimizations. + * @hb_objects: raw array of struct hb_object_t, which provides + * object graph info + * @num_hb_objs: number of hb_object_t in the hb_objects array. + * + * Given the input object graph info, repack a table to eliminate + * offset overflows. A nullptr is returned if the repacking attempt fails. + * Table specific optimizations (eg. extension promotion in GSUB/GPOS) may be performed. + * Passing HB_TAG_NONE will disable table specific optimizations. + * + * Since: EXPERIMENTAL + **/ +hb_blob_t* hb_subset_repack_or_fail (hb_tag_t table_tag, + hb_object_t* hb_objects, + unsigned num_hb_objs) +{ + hb_vector_t packed; + packed.alloc (num_hb_objs + 1); + packed.push (nullptr); + for (unsigned i = 0 ; i < num_hb_objs ; i++) + packed.push (&(hb_objects[i])); + + return hb_resolve_overflows (packed, + table_tag, + 20, + true); +} +#endif diff --git a/libs/harfbuzz/src/hb-subset-repacker.h b/libs/harfbuzz/src/hb-subset-repacker.h new file mode 100644 index 00000000..245cf607 --- /dev/null +++ b/libs/harfbuzz/src/hb-subset-repacker.h @@ -0,0 +1,81 @@ +/* + * Copyright © 2022 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. + * + */ + +#ifndef HB_SUBSET_REPACKER_H +#define HB_SUBSET_REPACKER_H + +#include "hb.h" + +HB_BEGIN_DECLS + +#ifdef HB_EXPERIMENTAL_API +/* + * struct hb_link_t + * width: offsetSize in bytes + * position: position of the offset field in bytes + * from beginning of subtable + * objidx: index of subtable + */ +struct hb_link_t +{ + unsigned width; + unsigned position; + unsigned objidx; +}; + +typedef struct hb_link_t hb_link_t; + +/* + * struct hb_object_t + * head: start of object data + * tail: end of object data + * num_real_links: num of offset field in the object + * real_links: pointer to array of offset info + * num_virtual_links: num of objects that must be packed + * after current object in the final serialized order + * virtual_links: array of virtual link info + */ +struct hb_object_t +{ + char *head; + char *tail; + unsigned num_real_links; + hb_link_t *real_links; + unsigned num_virtual_links; + hb_link_t *virtual_links; +}; + +typedef struct hb_object_t hb_object_t; + +HB_EXTERN hb_blob_t* +hb_subset_repack_or_fail (hb_tag_t table_tag, + hb_object_t* hb_objects, + unsigned num_hb_objs); + +#endif + +HB_END_DECLS + +#endif /* HB_SUBSET_REPACKER_H */ diff --git a/libs/harfbuzz/src/hb-subset.cc b/libs/harfbuzz/src/hb-subset.cc new file mode 100644 index 00000000..6026aa6e --- /dev/null +++ b/libs/harfbuzz/src/hb-subset.cc @@ -0,0 +1,607 @@ +/* + * Copyright © 2018 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): Garret Rieger, Rod Sheeter, Behdad Esfahbod + */ + +#include "hb.hh" +#include "hb-open-type.hh" + +#include "hb-subset.hh" + +#include "hb-open-file.hh" +#include "hb-ot-cmap-table.hh" +#include "hb-ot-glyf-table.hh" +#include "hb-ot-hdmx-table.hh" +#include "hb-ot-head-table.hh" +#include "hb-ot-hhea-table.hh" +#include "hb-ot-hmtx-table.hh" +#include "hb-ot-maxp-table.hh" +#include "hb-ot-color-sbix-table.hh" +#include "hb-ot-color-colr-table.hh" +#include "hb-ot-color-cpal-table.hh" +#include "hb-ot-os2-table.hh" +#include "hb-ot-post-table.hh" +#include "hb-ot-post-table-v2subset.hh" +#include "hb-ot-cff1-table.hh" +#include "hb-ot-cff2-table.hh" +#include "hb-ot-vorg-table.hh" +#include "hb-ot-name-table.hh" +#include "hb-ot-color-cbdt-table.hh" +#include "hb-ot-layout-gsub-table.hh" +#include "hb-ot-layout-gpos-table.hh" +#include "hb-ot-var-fvar-table.hh" +#include "hb-ot-var-gvar-table.hh" +#include "hb-ot-var-hvar-table.hh" +#include "hb-ot-math-table.hh" +#include "hb-ot-stat-table.hh" +#include "hb-repacker.hh" +#include "hb-subset-accelerator.hh" + +using OT::Layout::GSUB; +using OT::Layout::GPOS; + +/** + * SECTION:hb-subset + * @title: hb-subset + * @short_description: Subsets font files. + * @include: hb-subset.h + * + * Subsetting reduces the codepoint coverage of font files and removes all data + * that is no longer needed. A subset input describes the desired subset. The input is + * provided along with a font to the subsetting operation. Output is a new font file + * containing only the data specified in the input. + * + * Currently most outline and bitmap tables are supported: glyf, CFF, CFF2, sbix, + * COLR, and CBDT/CBLC. This also includes fonts with variable outlines via OpenType + * variations. Notably EBDT/EBLC and SVG are not supported. Layout subsetting is supported + * only for OpenType Layout tables (GSUB, GPOS, GDEF). Notably subsetting of graphite or AAT tables + * is not yet supported. + * + * Fonts with graphite or AAT tables may still be subsetted but will likely need to use the + * retain glyph ids option and configure the subset to pass through the layout tables untouched. + */ + +/* + * The list of tables in the open type spec. Used to check for tables that may need handling + * if we are unable to list the tables in a face. + */ +static hb_tag_t known_tables[] { + HB_TAG ('a', 'v', 'a', 'r'), + HB_OT_TAG_BASE, + HB_OT_TAG_CBDT, + HB_OT_TAG_CBLC, + HB_OT_TAG_cff1, + HB_OT_TAG_cff2, + HB_OT_TAG_cmap, + HB_OT_TAG_COLR, + HB_OT_TAG_CPAL, + HB_TAG ('c', 'v', 'a', 'r'), + HB_TAG ('c', 'v', 't', ' '), + HB_TAG ('D', 'S', 'I', 'G'), + HB_TAG ('E', 'B', 'D', 'T'), + HB_TAG ('E', 'B', 'L', 'C'), + HB_TAG ('E', 'B', 'S', 'C'), + HB_TAG ('f', 'p', 'g', 'm'), + HB_TAG ('f', 'v', 'a', 'r'), + HB_TAG ('g', 'a', 's', 'p'), + HB_OT_TAG_GDEF, + HB_OT_TAG_glyf, + HB_OT_TAG_GPOS, + HB_OT_TAG_GSUB, + HB_OT_TAG_gvar, + HB_OT_TAG_hdmx, + HB_OT_TAG_head, + HB_OT_TAG_hhea, + HB_OT_TAG_hmtx, + HB_OT_TAG_HVAR, + HB_OT_TAG_JSTF, + HB_TAG ('k', 'e', 'r', 'n'), + HB_OT_TAG_loca, + HB_TAG ('L', 'T', 'S', 'H'), + HB_OT_TAG_MATH, + HB_OT_TAG_maxp, + HB_TAG ('M', 'E', 'R', 'G'), + HB_TAG ('m', 'e', 't', 'a'), + HB_TAG ('M', 'V', 'A', 'R'), + HB_TAG ('P', 'C', 'L', 'T'), + HB_OT_TAG_post, + HB_TAG ('p', 'r', 'e', 'p'), + HB_OT_TAG_sbix, + HB_TAG ('S', 'T', 'A', 'T'), + HB_TAG ('S', 'V', 'G', ' '), + HB_TAG ('V', 'D', 'M', 'X'), + HB_OT_TAG_vhea, + HB_OT_TAG_vmtx, + HB_OT_TAG_VORG, + HB_OT_TAG_VVAR, + HB_OT_TAG_name, + HB_OT_TAG_OS2 +}; + +static bool _table_is_empty (const hb_face_t *face, hb_tag_t tag) +{ + hb_blob_t* blob = hb_face_reference_table (face, tag); + bool result = (blob == hb_blob_get_empty ()); + hb_blob_destroy (blob); + return result; +} + +static unsigned int +_get_table_tags (const hb_subset_plan_t* plan, + unsigned int start_offset, + unsigned int *table_count, /* IN/OUT */ + hb_tag_t *table_tags /* OUT */) +{ + unsigned num_tables = hb_face_get_table_tags (plan->source, 0, nullptr, nullptr); + if (num_tables) + return hb_face_get_table_tags (plan->source, start_offset, table_count, table_tags); + + // If face has 0 tables associated with it, assume that it was built from + // hb_face_create_tables and thus is unable to list its tables. Fallback to + // checking each table type we can handle for existence instead. + auto it = + hb_concat ( + + hb_array (known_tables) + | hb_filter ([&] (hb_tag_t tag) { + return !_table_is_empty (plan->source, tag) && !plan->no_subset_tables->has (tag); + }) + | hb_map ([] (hb_tag_t tag) -> hb_tag_t { return tag; }), + + plan->no_subset_tables->iter () + | hb_filter([&] (hb_tag_t tag) { + return !_table_is_empty (plan->source, tag); + })); + + it += start_offset; + + unsigned num_written = 0; + while (bool (it) && num_written < *table_count) + table_tags[num_written++] = *it++; + + *table_count = num_written; + return num_written; +} + + +static unsigned +_plan_estimate_subset_table_size (hb_subset_plan_t *plan, + unsigned table_len, + bool same_size) +{ + unsigned src_glyphs = plan->source->get_num_glyphs (); + unsigned dst_glyphs = plan->glyphset ()->get_population (); + + if (unlikely (!src_glyphs) || same_size) + return 512 + table_len; + + return 512 + (unsigned) (table_len * sqrt ((double) dst_glyphs / src_glyphs)); +} + +/* + * Repack the serialization buffer if any offset overflows exist. + */ +static hb_blob_t* +_repack (hb_tag_t tag, const hb_serialize_context_t& c) +{ + if (tag != HB_OT_TAG_GPOS + && tag != HB_OT_TAG_GSUB) + { + // Check for overflow in a non-handled table. + return c.successful () ? c.copy_blob () : nullptr; + } + + if (!c.offset_overflow ()) + return c.copy_blob (); + + hb_blob_t* result = hb_resolve_overflows (c.object_graph (), tag); + + if (unlikely (!result)) + { + DEBUG_MSG (SUBSET, nullptr, "OT::%c%c%c%c offset overflow resolution failed.", + HB_UNTAG (tag)); + return nullptr; + } + + return result; +} + +template +static +bool +_try_subset (const TableType *table, + hb_vector_t* buf, + hb_subset_context_t* c /* OUT */) +{ + c->serializer->start_serialize (); + if (c->serializer->in_error ()) return false; + + bool needed = table->subset (c); + if (!c->serializer->ran_out_of_room ()) + { + c->serializer->end_serialize (); + return needed; + } + + unsigned buf_size = buf->allocated; + buf_size = buf_size * 2 + 16; + + + + + DEBUG_MSG (SUBSET, nullptr, "OT::%c%c%c%c ran out of room; reallocating to %u bytes.", + HB_UNTAG (c->table_tag), buf_size); + + if (unlikely (buf_size > c->source_blob->length * 16 || + !buf->alloc (buf_size))) + { + DEBUG_MSG (SUBSET, nullptr, "OT::%c%c%c%c failed to reallocate %u bytes.", + HB_UNTAG (c->table_tag), buf_size); + return needed; + } + + c->serializer->reset (buf->arrayZ, buf->allocated); + return _try_subset (table, buf, c); +} + +template +static bool +_subset (hb_subset_plan_t *plan, hb_vector_t &buf) +{ + hb_blob_ptr_t source_blob = plan->source_table (); + const TableType *table = source_blob.get (); + + hb_tag_t tag = TableType::tableTag; + if (!source_blob.get_blob()->data) + { + DEBUG_MSG (SUBSET, nullptr, + "OT::%c%c%c%c::subset sanitize failed on source table.", HB_UNTAG (tag)); + source_blob.destroy (); + return false; + } + + /* Tables that we want to allocate same space as the source table. For GSUB/GPOS it's + * because those are expensive to subset, so giving them more room is fine. */ + bool same_size_table = TableType::tableTag == HB_OT_TAG_GSUB || + TableType::tableTag == HB_OT_TAG_GPOS || + TableType::tableTag == HB_OT_TAG_name; + + unsigned buf_size = _plan_estimate_subset_table_size (plan, source_blob.get_length (), same_size_table); + DEBUG_MSG (SUBSET, nullptr, + "OT::%c%c%c%c initial estimated table size: %u bytes.", HB_UNTAG (tag), buf_size); + if (unlikely (!buf.alloc (buf_size))) + { + DEBUG_MSG (SUBSET, nullptr, "OT::%c%c%c%c failed to allocate %u bytes.", HB_UNTAG (tag), buf_size); + source_blob.destroy (); + return false; + } + + bool needed = false; + hb_serialize_context_t serializer (buf.arrayZ, buf.allocated); + { + hb_subset_context_t c (source_blob.get_blob (), plan, &serializer, tag); + needed = _try_subset (table, &buf, &c); + } + source_blob.destroy (); + + if (serializer.in_error () && !serializer.only_offset_overflow ()) + { + DEBUG_MSG (SUBSET, nullptr, "OT::%c%c%c%c::subset FAILED!", HB_UNTAG (tag)); + return false; + } + + if (!needed) + { + DEBUG_MSG (SUBSET, nullptr, "OT::%c%c%c%c::subset table subsetted to empty.", HB_UNTAG (tag)); + return true; + } + + bool result = false; + hb_blob_t *dest_blob = _repack (tag, serializer); + if (dest_blob) + { + DEBUG_MSG (SUBSET, nullptr, + "OT::%c%c%c%c final subset table size: %u bytes.", + HB_UNTAG (tag), dest_blob->length); + result = plan->add_table (tag, dest_blob); + hb_blob_destroy (dest_blob); + } + + DEBUG_MSG (SUBSET, nullptr, "OT::%c%c%c%c::subset %s", + HB_UNTAG (tag), result ? "success" : "FAILED!"); + return result; +} + +static bool +_is_table_present (hb_face_t *source, hb_tag_t tag) +{ + + if (!hb_face_get_table_tags (source, 0, nullptr, nullptr)) { + // If face has 0 tables associated with it, assume that it was built from + // hb_face_create_tables and thus is unable to list its tables. Fallback to + // checking if the blob associated with tag is empty. + return !_table_is_empty (source, tag); + } + + hb_tag_t table_tags[32]; + unsigned offset = 0, num_tables = ARRAY_LENGTH (table_tags); + while (((void) hb_face_get_table_tags (source, offset, &num_tables, table_tags), num_tables)) + { + for (unsigned i = 0; i < num_tables; ++i) + if (table_tags[i] == tag) + return true; + offset += num_tables; + } + return false; +} + +static bool +_should_drop_table (hb_subset_plan_t *plan, hb_tag_t tag) +{ + if (plan->drop_tables->has (tag)) + return true; + + switch (tag) + { + case HB_TAG ('c','v','a','r'): /* hint table, fallthrough */ + return plan->all_axes_pinned || (plan->flags & HB_SUBSET_FLAGS_NO_HINTING); + + case HB_TAG ('c','v','t',' '): /* hint table, fallthrough */ + case HB_TAG ('f','p','g','m'): /* hint table, fallthrough */ + case HB_TAG ('p','r','e','p'): /* hint table, fallthrough */ + case HB_TAG ('h','d','m','x'): /* hint table, fallthrough */ + case HB_TAG ('V','D','M','X'): /* hint table, fallthrough */ + return plan->flags & HB_SUBSET_FLAGS_NO_HINTING; + +#ifdef HB_NO_SUBSET_LAYOUT + // Drop Layout Tables if requested. + case HB_OT_TAG_GDEF: + case HB_OT_TAG_GPOS: + case HB_OT_TAG_GSUB: + case HB_TAG ('m','o','r','x'): + case HB_TAG ('m','o','r','t'): + case HB_TAG ('k','e','r','x'): + case HB_TAG ('k','e','r','n'): + return true; +#endif + + case HB_TAG ('a','v','a','r'): + case HB_TAG ('f','v','a','r'): + case HB_TAG ('g','v','a','r'): + case HB_OT_TAG_HVAR: + case HB_OT_TAG_VVAR: + case HB_TAG ('M','V','A','R'): + return plan->all_axes_pinned; + + default: + return false; + } +} + +static bool +_passthrough (hb_subset_plan_t *plan, hb_tag_t tag) +{ + hb_blob_t *source_table = hb_face_reference_table (plan->source, tag); + bool result = plan->add_table (tag, source_table); + hb_blob_destroy (source_table); + return result; +} + +static bool +_dependencies_satisfied (hb_subset_plan_t *plan, hb_tag_t tag, + hb_set_t &visited_set, hb_set_t &revisit_set) +{ + switch (tag) + { + case HB_OT_TAG_hmtx: + case HB_OT_TAG_vmtx: + if (!plan->pinned_at_default && + !visited_set.has (HB_OT_TAG_glyf)) + { + revisit_set.add (tag); + return false; + } + return true; + + default: + return true; + } +} + +static bool +_subset_table (hb_subset_plan_t *plan, + hb_vector_t &buf, + hb_tag_t tag) +{ + if (plan->no_subset_tables->has (tag)) { + return _passthrough (plan, tag); + } + + DEBUG_MSG (SUBSET, nullptr, "subset %c%c%c%c", HB_UNTAG (tag)); + switch (tag) + { + case HB_OT_TAG_glyf: return _subset (plan, buf); + case HB_OT_TAG_hdmx: return _subset (plan, buf); + case HB_OT_TAG_name: return _subset (plan, buf); + case HB_OT_TAG_head: + if (_is_table_present (plan->source, HB_OT_TAG_glyf) && !_should_drop_table (plan, HB_OT_TAG_glyf)) + return true; /* skip head, handled by glyf */ + return _subset (plan, buf); + case HB_OT_TAG_hhea: return true; /* skip hhea, handled by hmtx */ + case HB_OT_TAG_hmtx: return _subset (plan, buf); + case HB_OT_TAG_vhea: return true; /* skip vhea, handled by vmtx */ + case HB_OT_TAG_vmtx: return _subset (plan, buf); + case HB_OT_TAG_maxp: return _subset (plan, buf); + case HB_OT_TAG_sbix: return _subset (plan, buf); + case HB_OT_TAG_loca: return true; /* skip loca, handled by glyf */ + case HB_OT_TAG_cmap: return _subset (plan, buf); + case HB_OT_TAG_OS2 : return _subset (plan, buf); + case HB_OT_TAG_post: return _subset (plan, buf); + case HB_OT_TAG_COLR: return _subset (plan, buf); + case HB_OT_TAG_CPAL: return _subset (plan, buf); + case HB_OT_TAG_CBLC: return _subset (plan, buf); + case HB_OT_TAG_CBDT: return true; /* skip CBDT, handled by CBLC */ + case HB_OT_TAG_MATH: return _subset (plan, buf); + +#ifndef HB_NO_SUBSET_CFF + case HB_OT_TAG_cff1: return _subset (plan, buf); + case HB_OT_TAG_cff2: return _subset (plan, buf); + case HB_OT_TAG_VORG: return _subset (plan, buf); +#endif + +#ifndef HB_NO_SUBSET_LAYOUT + case HB_OT_TAG_GDEF: return _subset (plan, buf); + case HB_OT_TAG_GSUB: return _subset (plan, buf); + case HB_OT_TAG_GPOS: return _subset (plan, buf); + case HB_OT_TAG_gvar: return _subset (plan, buf); + case HB_OT_TAG_HVAR: return _subset (plan, buf); + case HB_OT_TAG_VVAR: return _subset (plan, buf); +#endif + case HB_OT_TAG_fvar: + if (plan->user_axes_location->is_empty ()) return _passthrough (plan, tag); + return _subset (plan, buf); + case HB_OT_TAG_STAT: + /*TODO(qxliu): change the condition as we support more complex + * instancing operation*/ + if (plan->all_axes_pinned) return _subset (plan, buf); + else return _passthrough (plan, tag); + + default: + if (plan->flags & HB_SUBSET_FLAGS_PASSTHROUGH_UNRECOGNIZED) + return _passthrough (plan, tag); + + // Drop table + return true; + } +} + +static void _attach_accelerator_data (const hb_subset_plan_t* plan, + hb_face_t* face /* IN/OUT */) +{ + hb_subset_accelerator_t* accel = + hb_subset_accelerator_t::create (*plan->codepoint_to_glyph, + *plan->unicodes); + + if (accel->in_error ()) + { + hb_subset_accelerator_t::destroy (accel); + return; + } + + if (!hb_face_set_user_data(face, + hb_subset_accelerator_t::user_data_key(), + accel, + hb_subset_accelerator_t::destroy, + true)) + hb_subset_accelerator_t::destroy (accel); +} + +/** + * hb_subset_or_fail: + * @source: font face data to be subset. + * @input: input to use for the subsetting. + * + * Subsets a font according to provided input. Returns nullptr + * if the subset operation fails. + * + * Since: 2.9.0 + **/ +hb_face_t * +hb_subset_or_fail (hb_face_t *source, const hb_subset_input_t *input) +{ + if (unlikely (!input || !source)) return hb_face_get_empty (); + + hb_subset_plan_t *plan = hb_subset_plan_create_or_fail (source, input); + if (unlikely (!plan)) { + return nullptr; + } + + hb_face_t * result = hb_subset_plan_execute_or_fail (plan); + hb_subset_plan_destroy (plan); + return result; +} + + +/** + * hb_subset_plan_execute_or_fail: + * @plan: a subsetting plan. + * + * Executes the provided subsetting @plan. + * + * Return value: + * on success returns a reference to generated font subset. If the subsetting operation fails + * returns nullptr. + * + * Since: 4.0.0 + **/ +hb_face_t * +hb_subset_plan_execute_or_fail (hb_subset_plan_t *plan) +{ + if (unlikely (!plan || plan->in_error ())) { + return nullptr; + } + + hb_set_t tags_set, revisit_set; + bool success = true; + hb_tag_t table_tags[32]; + unsigned offset = 0, num_tables = ARRAY_LENGTH (table_tags); + hb_vector_t buf; + buf.alloc (4096 - 16); + + while (((void) _get_table_tags (plan, offset, &num_tables, table_tags), num_tables)) + { + for (unsigned i = 0; i < num_tables; ++i) + { + hb_tag_t tag = table_tags[i]; + if (_should_drop_table (plan, tag) && !tags_set.has (tag)) continue; + if (!_dependencies_satisfied (plan, tag, tags_set, revisit_set)) continue; + tags_set.add (tag); + success = _subset_table (plan, buf, tag); + if (unlikely (!success)) goto end; + } + + /*delayed subsetting for some tables since they might have dependency on other tables in some cases: + e.g: during instantiating glyf tables, hmetrics/vmetrics are updated and saved in subset plan, + hmtx/vmtx subsetting need to use these updated metrics values*/ + while (!revisit_set.is_empty ()) + { + hb_set_t revisit_temp; + for (hb_tag_t tag : revisit_set) + { + if (!_dependencies_satisfied (plan, tag, tags_set, revisit_temp)) continue; + tags_set.add (tag); + success = _subset_table (plan, buf, tag); + if (unlikely (!success)) goto end; + } + revisit_set = revisit_temp; + } + offset += num_tables; + } + + if (success && plan->attach_accelerator_data) { + _attach_accelerator_data (plan, plan->dest); + } + +end: + return success ? hb_face_reference (plan->dest) : nullptr; +} diff --git a/libs/harfbuzz/src/hb-subset.h b/libs/harfbuzz/src/hb-subset.h new file mode 100644 index 00000000..6a2c5f61 --- /dev/null +++ b/libs/harfbuzz/src/hb-subset.h @@ -0,0 +1,229 @@ +/* + * Copyright © 2018 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): Rod Sheeter + */ + +#ifndef HB_SUBSET_H +#define HB_SUBSET_H + +#include "hb.h" + +HB_BEGIN_DECLS + +/** + * hb_subset_input_t: + * + * Things that change based on the input. Characters to keep, etc. + */ + +typedef struct hb_subset_input_t hb_subset_input_t; + +/** + * hb_subset_plan_t: + * + * Contains information about how the subset operation will be executed. + * Such as mappings from the old glyph ids to the new ones in the subset. + */ + +typedef struct hb_subset_plan_t hb_subset_plan_t; + +/** + * hb_subset_flags_t: + * @HB_SUBSET_FLAGS_DEFAULT: all flags at their default value of false. + * @HB_SUBSET_FLAGS_NO_HINTING: If set hinting instructions will be dropped in + * the produced subset. Otherwise hinting instructions will be retained. + * @HB_SUBSET_FLAGS_RETAIN_GIDS: If set glyph indices will not be modified in + * the produced subset. If glyphs are dropped their indices will be retained + * as an empty glyph. + * @HB_SUBSET_FLAGS_DESUBROUTINIZE: If set and subsetting a CFF font the + * subsetter will attempt to remove subroutines from the CFF glyphs. + * @HB_SUBSET_FLAGS_NAME_LEGACY: If set non-unicode name records will be + * retained in the subset. + * @HB_SUBSET_FLAGS_SET_OVERLAPS_FLAG: If set the subsetter will set the + * OVERLAP_SIMPLE flag on each simple glyph. + * @HB_SUBSET_FLAGS_PASSTHROUGH_UNRECOGNIZED: If set the subsetter will not + * drop unrecognized tables and instead pass them through untouched. + * @HB_SUBSET_FLAGS_NOTDEF_OUTLINE: If set the notdef glyph outline will be + * retained in the final subset. + * @HB_SUBSET_FLAGS_GLYPH_NAMES: If set the PS glyph names will be retained + * in the final subset. + * @HB_SUBSET_FLAGS_NO_PRUNE_UNICODE_RANGES: If set then the unicode ranges in + * OS/2 will not be recalculated. + * @HB_SUBSET_FLAGS_PATCH_MODE: If set the subsetter behaviour will be modified + * to produce a subset that is better suited to patching. For example cmap + * subtable format will be kept stable. + * @HB_SUBSET_FLAGS_OMIT_GLYF: If set the subsetter won't actually produce the final + * glyf table bytes. The table directory will include and entry as if the table was + * there but the actual final font blob will be truncated prior to the glyf data. This + * is a useful performance optimization when a font aware binary patching algorithm + * is being used to diff two subsets. + * + * List of boolean properties that can be configured on the subset input. + * + * Since: 2.9.0 + **/ +typedef enum { /*< flags >*/ + HB_SUBSET_FLAGS_DEFAULT = 0x00000000u, + HB_SUBSET_FLAGS_NO_HINTING = 0x00000001u, + HB_SUBSET_FLAGS_RETAIN_GIDS = 0x00000002u, + HB_SUBSET_FLAGS_DESUBROUTINIZE = 0x00000004u, + HB_SUBSET_FLAGS_NAME_LEGACY = 0x00000008u, + HB_SUBSET_FLAGS_SET_OVERLAPS_FLAG = 0x00000010u, + HB_SUBSET_FLAGS_PASSTHROUGH_UNRECOGNIZED = 0x00000020u, + HB_SUBSET_FLAGS_NOTDEF_OUTLINE = 0x00000040u, + HB_SUBSET_FLAGS_GLYPH_NAMES = 0x00000080u, + HB_SUBSET_FLAGS_NO_PRUNE_UNICODE_RANGES = 0x00000100u, + // Not supported yet: HB_SUBSET_FLAGS_PATCH_MODE = 0x00000200u, + // Not supported yet: HB_SUBSET_FLAGS_OMIT_GLYF = 0x00000400u, +} hb_subset_flags_t; + +/** + * hb_subset_sets_t: + * @HB_SUBSET_SETS_GLYPH_INDEX: the set of glyph indexes to retain in the subset. + * @HB_SUBSET_SETS_UNICODE: the set of unicode codepoints to retain in the subset. + * @HB_SUBSET_SETS_NO_SUBSET_TABLE_TAG: the set of table tags which specifies tables that should not be + * subsetted. + * @HB_SUBSET_SETS_DROP_TABLE_TAG: the set of table tags which specifies tables which will be dropped + * in the subset. + * @HB_SUBSET_SETS_NAME_ID: the set of name ids that will be retained. + * @HB_SUBSET_SETS_NAME_LANG_ID: the set of name lang ids that will be retained. + * @HB_SUBSET_SETS_LAYOUT_FEATURE_TAG: the set of layout feature tags that will be retained + * in the subset. + * @HB_SUBSET_SETS_LAYOUT_SCRIPT_TAG: the set of layout script tags that will be retained + * in the subset. Defaults to all tags. Since: 5.0.0 + * + * List of sets that can be configured on the subset input. + * + * Since: 2.9.1 + **/ +typedef enum { + HB_SUBSET_SETS_GLYPH_INDEX = 0, + HB_SUBSET_SETS_UNICODE, + HB_SUBSET_SETS_NO_SUBSET_TABLE_TAG, + HB_SUBSET_SETS_DROP_TABLE_TAG, + HB_SUBSET_SETS_NAME_ID, + HB_SUBSET_SETS_NAME_LANG_ID, + HB_SUBSET_SETS_LAYOUT_FEATURE_TAG, + HB_SUBSET_SETS_LAYOUT_SCRIPT_TAG, +} hb_subset_sets_t; + +HB_EXTERN hb_subset_input_t * +hb_subset_input_create_or_fail (void); + +HB_EXTERN hb_subset_input_t * +hb_subset_input_reference (hb_subset_input_t *input); + +HB_EXTERN void +hb_subset_input_destroy (hb_subset_input_t *input); + +HB_EXTERN hb_bool_t +hb_subset_input_set_user_data (hb_subset_input_t *input, + hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy, + hb_bool_t replace); + +HB_EXTERN void * +hb_subset_input_get_user_data (const hb_subset_input_t *input, + hb_user_data_key_t *key); + +HB_EXTERN hb_set_t * +hb_subset_input_unicode_set (hb_subset_input_t *input); + +HB_EXTERN hb_set_t * +hb_subset_input_glyph_set (hb_subset_input_t *input); + +HB_EXTERN hb_set_t * +hb_subset_input_set (hb_subset_input_t *input, hb_subset_sets_t set_type); + +HB_EXTERN hb_subset_flags_t +hb_subset_input_get_flags (hb_subset_input_t *input); + +HB_EXTERN void +hb_subset_input_set_flags (hb_subset_input_t *input, + unsigned value); + +#ifdef HB_EXPERIMENTAL_API +#ifndef HB_NO_VAR +HB_EXTERN hb_bool_t +hb_subset_input_pin_axis_to_default (hb_subset_input_t *input, + hb_face_t *face, + hb_tag_t axis_tag); + +HB_EXTERN hb_bool_t +hb_subset_input_pin_axis_location (hb_subset_input_t *input, + hb_face_t *face, + hb_tag_t axis_tag, + float axis_value); +#endif +#endif + +#ifdef HB_EXPERIMENTAL_API + +HB_EXTERN hb_face_t * +hb_subset_preprocess (hb_face_t *source); + +#endif + +HB_EXTERN hb_face_t * +hb_subset_or_fail (hb_face_t *source, const hb_subset_input_t *input); + +HB_EXTERN hb_face_t * +hb_subset_plan_execute_or_fail (hb_subset_plan_t *plan); + +HB_EXTERN hb_subset_plan_t * +hb_subset_plan_create_or_fail (hb_face_t *face, + const hb_subset_input_t *input); + +HB_EXTERN void +hb_subset_plan_destroy (hb_subset_plan_t *plan); + +HB_EXTERN const hb_map_t* +hb_subset_plan_old_to_new_glyph_mapping (const hb_subset_plan_t *plan); + +HB_EXTERN const hb_map_t* +hb_subset_plan_new_to_old_glyph_mapping (const hb_subset_plan_t *plan); + +HB_EXTERN const hb_map_t* +hb_subset_plan_unicode_to_old_glyph_mapping (const hb_subset_plan_t *plan); + + +HB_EXTERN hb_subset_plan_t * +hb_subset_plan_reference (hb_subset_plan_t *plan); + +HB_EXTERN hb_bool_t +hb_subset_plan_set_user_data (hb_subset_plan_t *plan, + hb_user_data_key_t *key, + void *data, + hb_destroy_func_t destroy, + hb_bool_t replace); + +HB_EXTERN void * +hb_subset_plan_get_user_data (const hb_subset_plan_t *plan, + hb_user_data_key_t *key); + + +HB_END_DECLS + +#endif /* HB_SUBSET_H */ diff --git a/libs/harfbuzz/src/hb-subset.hh b/libs/harfbuzz/src/hb-subset.hh new file mode 100644 index 00000000..98c5f06f --- /dev/null +++ b/libs/harfbuzz/src/hb-subset.hh @@ -0,0 +1,73 @@ +/* + * Copyright © 2018 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): Garret Rieger, Roderick Sheeter + */ + +#ifndef HB_SUBSET_HH +#define HB_SUBSET_HH + + +#include "hb.hh" + +#include "hb-subset.h" + +#include "hb-machinery.hh" +#include "hb-subset-input.hh" +#include "hb-subset-plan.hh" + +struct hb_subset_context_t : + hb_dispatch_context_t +{ + const char *get_name () { return "SUBSET"; } + static return_t default_return_value () { return true; } + + private: + template auto + _dispatch (const T &obj, hb_priority<1>, Ts&&... ds) HB_AUTO_RETURN + ( obj.subset (this, std::forward (ds)...) ) + template auto + _dispatch (const T &obj, hb_priority<0>, Ts&&... ds) HB_AUTO_RETURN + ( obj.dispatch (this, std::forward (ds)...) ) + public: + template auto + dispatch (const T &obj, Ts&&... ds) HB_AUTO_RETURN + ( _dispatch (obj, hb_prioritize, std::forward (ds)...) ) + + hb_blob_t *source_blob; + hb_subset_plan_t *plan; + hb_serialize_context_t *serializer; + hb_tag_t table_tag; + + hb_subset_context_t (hb_blob_t *source_blob_, + hb_subset_plan_t *plan_, + hb_serialize_context_t *serializer_, + hb_tag_t table_tag_) : + source_blob (source_blob_), + plan (plan_), + serializer (serializer_), + table_tag (table_tag_) {} +}; + + +#endif /* HB_SUBSET_HH */ diff --git a/libs/harfbuzz/src/hb-ucd-table.hh b/libs/harfbuzz/src/hb-ucd-table.hh new file mode 100644 index 00000000..a372a4af --- /dev/null +++ b/libs/harfbuzz/src/hb-ucd-table.hh @@ -0,0 +1,5815 @@ +/* == Start of generated table == */ +/* + * The following table is generated by running: + * + * ./gen-ucd-table.py ucd.nounihan.grouped.xml + * + * on file with this description: Unicode 15.0.0 + */ + +#ifndef HB_UCD_TABLE_HH +#define HB_UCD_TABLE_HH + +#include "hb.hh" + +static const hb_script_t +_hb_ucd_sc_map[165] = +{ + HB_SCRIPT_COMMON, HB_SCRIPT_INHERITED, + HB_SCRIPT_UNKNOWN, HB_SCRIPT_ARABIC, + HB_SCRIPT_ARMENIAN, HB_SCRIPT_BENGALI, + HB_SCRIPT_CYRILLIC, HB_SCRIPT_DEVANAGARI, + HB_SCRIPT_GEORGIAN, HB_SCRIPT_GREEK, + HB_SCRIPT_GUJARATI, HB_SCRIPT_GURMUKHI, + HB_SCRIPT_HANGUL, HB_SCRIPT_HAN, + HB_SCRIPT_HEBREW, HB_SCRIPT_HIRAGANA, + HB_SCRIPT_KANNADA, HB_SCRIPT_KATAKANA, + HB_SCRIPT_LAO, HB_SCRIPT_LATIN, + HB_SCRIPT_MALAYALAM, HB_SCRIPT_ORIYA, + HB_SCRIPT_TAMIL, HB_SCRIPT_TELUGU, + HB_SCRIPT_THAI, HB_SCRIPT_TIBETAN, + HB_SCRIPT_BOPOMOFO, HB_SCRIPT_BRAILLE, + HB_SCRIPT_CANADIAN_SYLLABICS, HB_SCRIPT_CHEROKEE, + HB_SCRIPT_ETHIOPIC, HB_SCRIPT_KHMER, + HB_SCRIPT_MONGOLIAN, HB_SCRIPT_MYANMAR, + HB_SCRIPT_OGHAM, HB_SCRIPT_RUNIC, + HB_SCRIPT_SINHALA, HB_SCRIPT_SYRIAC, + HB_SCRIPT_THAANA, HB_SCRIPT_YI, + HB_SCRIPT_DESERET, HB_SCRIPT_GOTHIC, + HB_SCRIPT_OLD_ITALIC, HB_SCRIPT_BUHID, + HB_SCRIPT_HANUNOO, HB_SCRIPT_TAGALOG, + HB_SCRIPT_TAGBANWA, HB_SCRIPT_CYPRIOT, + HB_SCRIPT_LIMBU, HB_SCRIPT_LINEAR_B, + HB_SCRIPT_OSMANYA, HB_SCRIPT_SHAVIAN, + HB_SCRIPT_TAI_LE, HB_SCRIPT_UGARITIC, + HB_SCRIPT_BUGINESE, HB_SCRIPT_COPTIC, + HB_SCRIPT_GLAGOLITIC, HB_SCRIPT_KHAROSHTHI, + HB_SCRIPT_NEW_TAI_LUE, HB_SCRIPT_OLD_PERSIAN, + HB_SCRIPT_SYLOTI_NAGRI, HB_SCRIPT_TIFINAGH, + HB_SCRIPT_BALINESE, HB_SCRIPT_CUNEIFORM, + HB_SCRIPT_NKO, HB_SCRIPT_PHAGS_PA, + HB_SCRIPT_PHOENICIAN, HB_SCRIPT_CARIAN, + HB_SCRIPT_CHAM, HB_SCRIPT_KAYAH_LI, + HB_SCRIPT_LEPCHA, HB_SCRIPT_LYCIAN, + HB_SCRIPT_LYDIAN, HB_SCRIPT_OL_CHIKI, + HB_SCRIPT_REJANG, HB_SCRIPT_SAURASHTRA, + HB_SCRIPT_SUNDANESE, HB_SCRIPT_VAI, + HB_SCRIPT_AVESTAN, HB_SCRIPT_BAMUM, + HB_SCRIPT_EGYPTIAN_HIEROGLYPHS, HB_SCRIPT_IMPERIAL_ARAMAIC, + HB_SCRIPT_INSCRIPTIONAL_PAHLAVI, HB_SCRIPT_INSCRIPTIONAL_PARTHIAN, + HB_SCRIPT_JAVANESE, HB_SCRIPT_KAITHI, + HB_SCRIPT_LISU, HB_SCRIPT_MEETEI_MAYEK, + HB_SCRIPT_OLD_SOUTH_ARABIAN, HB_SCRIPT_OLD_TURKIC, + HB_SCRIPT_SAMARITAN, HB_SCRIPT_TAI_THAM, + HB_SCRIPT_TAI_VIET, HB_SCRIPT_BATAK, + HB_SCRIPT_BRAHMI, HB_SCRIPT_MANDAIC, + HB_SCRIPT_CHAKMA, HB_SCRIPT_MEROITIC_CURSIVE, + HB_SCRIPT_MEROITIC_HIEROGLYPHS, HB_SCRIPT_MIAO, + HB_SCRIPT_SHARADA, HB_SCRIPT_SORA_SOMPENG, + HB_SCRIPT_TAKRI, HB_SCRIPT_BASSA_VAH, + HB_SCRIPT_CAUCASIAN_ALBANIAN, HB_SCRIPT_DUPLOYAN, + HB_SCRIPT_ELBASAN, HB_SCRIPT_GRANTHA, + HB_SCRIPT_KHOJKI, HB_SCRIPT_KHUDAWADI, + HB_SCRIPT_LINEAR_A, HB_SCRIPT_MAHAJANI, + HB_SCRIPT_MANICHAEAN, HB_SCRIPT_MENDE_KIKAKUI, + HB_SCRIPT_MODI, HB_SCRIPT_MRO, + HB_SCRIPT_NABATAEAN, HB_SCRIPT_OLD_NORTH_ARABIAN, + HB_SCRIPT_OLD_PERMIC, HB_SCRIPT_PAHAWH_HMONG, + HB_SCRIPT_PALMYRENE, HB_SCRIPT_PAU_CIN_HAU, + HB_SCRIPT_PSALTER_PAHLAVI, HB_SCRIPT_SIDDHAM, + HB_SCRIPT_TIRHUTA, HB_SCRIPT_WARANG_CITI, + HB_SCRIPT_AHOM, HB_SCRIPT_ANATOLIAN_HIEROGLYPHS, + HB_SCRIPT_HATRAN, HB_SCRIPT_MULTANI, + HB_SCRIPT_OLD_HUNGARIAN, HB_SCRIPT_SIGNWRITING, + HB_SCRIPT_ADLAM, HB_SCRIPT_BHAIKSUKI, + HB_SCRIPT_MARCHEN, HB_SCRIPT_OSAGE, + HB_SCRIPT_TANGUT, HB_SCRIPT_NEWA, + HB_SCRIPT_MASARAM_GONDI, HB_SCRIPT_NUSHU, + HB_SCRIPT_SOYOMBO, HB_SCRIPT_ZANABAZAR_SQUARE, + HB_SCRIPT_DOGRA, HB_SCRIPT_GUNJALA_GONDI, + HB_SCRIPT_HANIFI_ROHINGYA, HB_SCRIPT_MAKASAR, + HB_SCRIPT_MEDEFAIDRIN, HB_SCRIPT_OLD_SOGDIAN, + HB_SCRIPT_SOGDIAN, HB_SCRIPT_ELYMAIC, + HB_SCRIPT_NANDINAGARI, HB_SCRIPT_NYIAKENG_PUACHUE_HMONG, + HB_SCRIPT_WANCHO, HB_SCRIPT_CHORASMIAN, + HB_SCRIPT_DIVES_AKURU, HB_SCRIPT_KHITAN_SMALL_SCRIPT, + HB_SCRIPT_YEZIDI, HB_SCRIPT_CYPRO_MINOAN, + HB_SCRIPT_OLD_UYGHUR, HB_SCRIPT_TANGSA, + HB_SCRIPT_TOTO, HB_SCRIPT_VITHKUQI, + HB_SCRIPT_MATH, HB_SCRIPT_KAWI, + HB_SCRIPT_NAG_MUNDARI, +}; +static const uint16_t +_hb_ucd_dm1_p0_map[825] = +{ + 0x003Bu, 0x004Bu, 0x0060u, 0x00B4u, 0x00B7u, 0x00C5u, 0x02B9u, 0x0300u, + 0x0301u, 0x0313u, 0x0385u, 0x0386u, 0x0388u, 0x0389u, 0x038Au, 0x038Cu, + 0x038Eu, 0x038Fu, 0x0390u, 0x03A9u, 0x03ACu, 0x03ADu, 0x03AEu, 0x03AFu, + 0x03B0u, 0x03B9u, 0x03CCu, 0x03CDu, 0x03CEu, 0x2002u, 0x2003u, 0x3008u, + 0x3009u, 0x349Eu, 0x34B9u, 0x34BBu, 0x34DFu, 0x3515u, 0x36EEu, 0x36FCu, + 0x3781u, 0x382Fu, 0x3862u, 0x387Cu, 0x38C7u, 0x38E3u, 0x391Cu, 0x393Au, + 0x3A2Eu, 0x3A6Cu, 0x3AE4u, 0x3B08u, 0x3B19u, 0x3B49u, 0x3B9Du, 0x3C18u, + 0x3C4Eu, 0x3D33u, 0x3D96u, 0x3EACu, 0x3EB8u, 0x3F1Bu, 0x3FFCu, 0x4008u, + 0x4018u, 0x4039u, 0x4046u, 0x4096u, 0x40E3u, 0x412Fu, 0x4202u, 0x4227u, + 0x42A0u, 0x4301u, 0x4334u, 0x4359u, 0x43D5u, 0x43D9u, 0x440Bu, 0x446Bu, + 0x452Bu, 0x455Du, 0x4561u, 0x456Bu, 0x45D7u, 0x45F9u, 0x4635u, 0x46BEu, + 0x46C7u, 0x4995u, 0x49E6u, 0x4A6Eu, 0x4A76u, 0x4AB2u, 0x4B33u, 0x4BCEu, + 0x4CCEu, 0x4CEDu, 0x4CF8u, 0x4D56u, 0x4E0Du, 0x4E26u, 0x4E32u, 0x4E38u, + 0x4E39u, 0x4E3Du, 0x4E41u, 0x4E82u, 0x4E86u, 0x4EAEu, 0x4EC0u, 0x4ECCu, + 0x4EE4u, 0x4F60u, 0x4F80u, 0x4F86u, 0x4F8Bu, 0x4FAEu, 0x4FBBu, 0x4FBFu, + 0x5002u, 0x502Bu, 0x507Au, 0x5099u, 0x50CFu, 0x50DAu, 0x50E7u, 0x5140u, + 0x5145u, 0x514Du, 0x5154u, 0x5164u, 0x5167u, 0x5168u, 0x5169u, 0x516Du, + 0x5177u, 0x5180u, 0x518Du, 0x5192u, 0x5195u, 0x5197u, 0x51A4u, 0x51ACu, + 0x51B5u, 0x51B7u, 0x51C9u, 0x51CCu, 0x51DCu, 0x51DEu, 0x51F5u, 0x5203u, + 0x5207u, 0x5217u, 0x5229u, 0x523Au, 0x523Bu, 0x5246u, 0x5272u, 0x5277u, + 0x5289u, 0x529Bu, 0x52A3u, 0x52B3u, 0x52C7u, 0x52C9u, 0x52D2u, 0x52DEu, + 0x52E4u, 0x52F5u, 0x52FAu, 0x5305u, 0x5306u, 0x5317u, 0x533Fu, 0x5349u, + 0x5351u, 0x535Au, 0x5373u, 0x5375u, 0x537Du, 0x537Fu, 0x53C3u, 0x53CAu, + 0x53DFu, 0x53E5u, 0x53EBu, 0x53F1u, 0x5406u, 0x540Fu, 0x541Du, 0x5438u, + 0x5442u, 0x5448u, 0x5468u, 0x549Eu, 0x54A2u, 0x54BDu, 0x54F6u, 0x5510u, + 0x5553u, 0x5555u, 0x5563u, 0x5584u, 0x5587u, 0x5599u, 0x559Du, 0x55ABu, + 0x55B3u, 0x55C0u, 0x55C2u, 0x55E2u, 0x5606u, 0x5651u, 0x5668u, 0x5674u, + 0x56F9u, 0x5716u, 0x5717u, 0x578Bu, 0x57CEu, 0x57F4u, 0x580Du, 0x5831u, + 0x5832u, 0x5840u, 0x585Au, 0x585Eu, 0x58A8u, 0x58ACu, 0x58B3u, 0x58D8u, + 0x58DFu, 0x58EEu, 0x58F2u, 0x58F7u, 0x5906u, 0x591Au, 0x5922u, 0x5944u, + 0x5948u, 0x5951u, 0x5954u, 0x5962u, 0x5973u, 0x59D8u, 0x59ECu, 0x5A1Bu, + 0x5A27u, 0x5A62u, 0x5A66u, 0x5AB5u, 0x5B08u, 0x5B28u, 0x5B3Eu, 0x5B85u, + 0x5BC3u, 0x5BD8u, 0x5BE7u, 0x5BEEu, 0x5BF3u, 0x5BFFu, 0x5C06u, 0x5C22u, + 0x5C3Fu, 0x5C60u, 0x5C62u, 0x5C64u, 0x5C65u, 0x5C6Eu, 0x5C8Du, 0x5CC0u, + 0x5D19u, 0x5D43u, 0x5D50u, 0x5D6Bu, 0x5D6Eu, 0x5D7Cu, 0x5DB2u, 0x5DBAu, + 0x5DE1u, 0x5DE2u, 0x5DFDu, 0x5E28u, 0x5E3Du, 0x5E69u, 0x5E74u, 0x5EA6u, + 0x5EB0u, 0x5EB3u, 0x5EB6u, 0x5EC9u, 0x5ECAu, 0x5ED2u, 0x5ED3u, 0x5ED9u, + 0x5EECu, 0x5EFEu, 0x5F04u, 0x5F22u, 0x5F53u, 0x5F62u, 0x5F69u, 0x5F6Bu, + 0x5F8Bu, 0x5F9Au, 0x5FA9u, 0x5FADu, 0x5FCDu, 0x5FD7u, 0x5FF5u, 0x5FF9u, + 0x6012u, 0x601Cu, 0x6075u, 0x6081u, 0x6094u, 0x60C7u, 0x60D8u, 0x60E1u, + 0x6108u, 0x6144u, 0x6148u, 0x614Cu, 0x614Eu, 0x6160u, 0x6168u, 0x617Au, + 0x618Eu, 0x6190u, 0x61A4u, 0x61AFu, 0x61B2u, 0x61DEu, 0x61F2u, 0x61F6u, + 0x6200u, 0x6210u, 0x621Bu, 0x622Eu, 0x6234u, 0x625Du, 0x62B1u, 0x62C9u, + 0x62CFu, 0x62D3u, 0x62D4u, 0x62FCu, 0x62FEu, 0x633Du, 0x6350u, 0x6368u, + 0x637Bu, 0x6383u, 0x63A0u, 0x63A9u, 0x63C4u, 0x63C5u, 0x63E4u, 0x641Cu, + 0x6422u, 0x6452u, 0x6469u, 0x6477u, 0x647Eu, 0x649Au, 0x649Du, 0x64C4u, + 0x654Fu, 0x6556u, 0x656Cu, 0x6578u, 0x6599u, 0x65C5u, 0x65E2u, 0x65E3u, + 0x6613u, 0x6649u, 0x6674u, 0x6688u, 0x6691u, 0x669Cu, 0x66B4u, 0x66C6u, + 0x66F4u, 0x66F8u, 0x6700u, 0x6717u, 0x671Bu, 0x6721u, 0x674Eu, 0x6753u, + 0x6756u, 0x675Eu, 0x677Bu, 0x6785u, 0x6797u, 0x67F3u, 0x67FAu, 0x6817u, + 0x681Fu, 0x6852u, 0x6881u, 0x6885u, 0x688Eu, 0x68A8u, 0x6914u, 0x6942u, + 0x69A3u, 0x69EAu, 0x6A02u, 0x6A13u, 0x6AA8u, 0x6AD3u, 0x6ADBu, 0x6B04u, + 0x6B21u, 0x6B54u, 0x6B72u, 0x6B77u, 0x6B79u, 0x6B9Fu, 0x6BAEu, 0x6BBAu, + 0x6BBBu, 0x6C4Eu, 0x6C67u, 0x6C88u, 0x6CBFu, 0x6CCCu, 0x6CCDu, 0x6CE5u, + 0x6D16u, 0x6D1Bu, 0x6D1Eu, 0x6D34u, 0x6D3Eu, 0x6D41u, 0x6D69u, 0x6D6Au, + 0x6D77u, 0x6D78u, 0x6D85u, 0x6DCBu, 0x6DDAu, 0x6DEAu, 0x6DF9u, 0x6E1Au, + 0x6E2Fu, 0x6E6Eu, 0x6E9Cu, 0x6EBAu, 0x6EC7u, 0x6ECBu, 0x6ED1u, 0x6EDBu, + 0x6F0Fu, 0x6F22u, 0x6F23u, 0x6F6Eu, 0x6FC6u, 0x6FEBu, 0x6FFEu, 0x701Bu, + 0x701Eu, 0x7039u, 0x704Au, 0x7070u, 0x7077u, 0x707Du, 0x7099u, 0x70ADu, + 0x70C8u, 0x70D9u, 0x7145u, 0x7149u, 0x716Eu, 0x719Cu, 0x71CEu, 0x71D0u, + 0x7210u, 0x721Bu, 0x7228u, 0x722Bu, 0x7235u, 0x7250u, 0x7262u, 0x7280u, + 0x7295u, 0x72AFu, 0x72C0u, 0x72FCu, 0x732Au, 0x7375u, 0x737Au, 0x7387u, + 0x738Bu, 0x73A5u, 0x73B2u, 0x73DEu, 0x7406u, 0x7409u, 0x7422u, 0x7447u, + 0x745Cu, 0x7469u, 0x7471u, 0x7485u, 0x7489u, 0x7498u, 0x74CAu, 0x7506u, + 0x7524u, 0x753Bu, 0x753Eu, 0x7559u, 0x7565u, 0x7570u, 0x75E2u, 0x7610u, + 0x761Du, 0x761Fu, 0x7642u, 0x7669u, 0x76CAu, 0x76DBu, 0x76E7u, 0x76F4u, + 0x7701u, 0x771Eu, 0x771Fu, 0x7740u, 0x774Au, 0x778Bu, 0x77A7u, 0x784Eu, + 0x786Bu, 0x788Cu, 0x7891u, 0x78CAu, 0x78CCu, 0x78FBu, 0x792Au, 0x793Cu, + 0x793Eu, 0x7948u, 0x7949u, 0x7950u, 0x7956u, 0x795Du, 0x795Eu, 0x7965u, + 0x797Fu, 0x798Du, 0x798Eu, 0x798Fu, 0x79AEu, 0x79CAu, 0x79EBu, 0x7A1Cu, + 0x7A40u, 0x7A4Au, 0x7A4Fu, 0x7A81u, 0x7AB1u, 0x7ACBu, 0x7AEEu, 0x7B20u, + 0x7BC0u, 0x7BC6u, 0x7BC9u, 0x7C3Eu, 0x7C60u, 0x7C7Bu, 0x7C92u, 0x7CBEu, + 0x7CD2u, 0x7CD6u, 0x7CE3u, 0x7CE7u, 0x7CE8u, 0x7D00u, 0x7D10u, 0x7D22u, + 0x7D2Fu, 0x7D5Bu, 0x7D63u, 0x7DA0u, 0x7DBEu, 0x7DC7u, 0x7DF4u, 0x7E02u, + 0x7E09u, 0x7E37u, 0x7E41u, 0x7E45u, 0x7F3Eu, 0x7F72u, 0x7F79u, 0x7F7Au, + 0x7F85u, 0x7F95u, 0x7F9Au, 0x7FBDu, 0x7FFAu, 0x8001u, 0x8005u, 0x8046u, + 0x8060u, 0x806Fu, 0x8070u, 0x807Eu, 0x808Bu, 0x80ADu, 0x80B2u, 0x8103u, + 0x813Eu, 0x81D8u, 0x81E8u, 0x81EDu, 0x8201u, 0x8204u, 0x8218u, 0x826Fu, + 0x8279u, 0x828Bu, 0x8291u, 0x829Du, 0x82B1u, 0x82B3u, 0x82BDu, 0x82E5u, + 0x82E6u, 0x831Du, 0x8323u, 0x8336u, 0x8352u, 0x8353u, 0x8363u, 0x83ADu, + 0x83BDu, 0x83C9u, 0x83CAu, 0x83CCu, 0x83DCu, 0x83E7u, 0x83EFu, 0x83F1u, + 0x843Du, 0x8449u, 0x8457u, 0x84EEu, 0x84F1u, 0x84F3u, 0x84FCu, 0x8516u, + 0x8564u, 0x85CDu, 0x85FAu, 0x8606u, 0x8612u, 0x862Du, 0x863Fu, 0x8650u, + 0x865Cu, 0x8667u, 0x8669u, 0x8688u, 0x86A9u, 0x86E2u, 0x870Eu, 0x8728u, + 0x876Bu, 0x8779u, 0x8786u, 0x87BAu, 0x87E1u, 0x8801u, 0x881Fu, 0x884Cu, + 0x8860u, 0x8863u, 0x88C2u, 0x88CFu, 0x88D7u, 0x88DEu, 0x88E1u, 0x88F8u, + 0x88FAu, 0x8910u, 0x8941u, 0x8964u, 0x8986u, 0x898Bu, 0x8996u, 0x8AA0u, + 0x8AAAu, 0x8ABFu, 0x8ACBu, 0x8AD2u, 0x8AD6u, 0x8AEDu, 0x8AF8u, 0x8AFEu, + 0x8B01u, 0x8B39u, 0x8B58u, 0x8B80u, 0x8B8Au, 0x8C48u, 0x8C55u, 0x8CABu, + 0x8CC1u, 0x8CC2u, 0x8CC8u, 0x8CD3u, 0x8D08u, 0x8D1Bu, 0x8D77u, 0x8DBCu, + 0x8DCBu, 0x8DEFu, 0x8DF0u, 0x8ECAu, 0x8ED4u, 0x8F26u, 0x8F2Au, 0x8F38u, + 0x8F3Bu, 0x8F62u, 0x8F9Eu, 0x8FB0u, 0x8FB6u, 0x9023u, 0x9038u, 0x9072u, + 0x907Cu, 0x908Fu, 0x9094u, 0x90CEu, 0x90DEu, 0x90F1u, 0x90FDu, 0x9111u, + 0x911Bu, 0x916Au, 0x9199u, 0x91B4u, 0x91CCu, 0x91CFu, 0x91D1u, 0x9234u, + 0x9238u, 0x9276u, 0x927Cu, 0x92D7u, 0x92D8u, 0x9304u, 0x934Au, 0x93F9u, + 0x9415u, 0x958Bu, 0x95ADu, 0x95B7u, 0x962Eu, 0x964Bu, 0x964Du, 0x9675u, + 0x9678u, 0x967Cu, 0x9686u, 0x96A3u, 0x96B7u, 0x96B8u, 0x96C3u, 0x96E2u, + 0x96E3u, 0x96F6u, 0x96F7u, 0x9723u, 0x9732u, 0x9748u, 0x9756u, 0x97DBu, + 0x97E0u, 0x97FFu, 0x980Bu, 0x9818u, 0x9829u, 0x983Bu, 0x985Eu, 0x98E2u, + 0x98EFu, 0x98FCu, 0x9928u, 0x9929u, 0x99A7u, 0x99C2u, 0x99F1u, 0x99FEu, + 0x9A6Au, 0x9B12u, 0x9B6Fu, 0x9C40u, 0x9C57u, 0x9CFDu, 0x9D67u, 0x9DB4u, + 0x9DFAu, 0x9E1Eu, 0x9E7Fu, 0x9E97u, 0x9E9Fu, 0x9EBBu, 0x9ECEu, 0x9EF9u, + 0x9EFEu, 0x9F05u, 0x9F0Fu, 0x9F16u, 0x9F3Bu, 0x9F43u, 0x9F8Du, 0x9F8Eu, + 0x9F9Cu, +}; +static const uint16_t +_hb_ucd_dm1_p2_map[110] = +{ + 0x0122u, 0x051Cu, 0x0525u, 0x054Bu, 0x063Au, 0x0804u, 0x08DEu, 0x0A2Cu, + 0x0B63u, 0x14E4u, 0x16A8u, 0x16EAu, 0x19C8u, 0x1B18u, 0x1D0Bu, 0x1DE4u, + 0x1DE6u, 0x2183u, 0x219Fu, 0x2331u, 0x26D4u, 0x2844u, 0x284Au, 0x2B0Cu, + 0x2BF1u, 0x300Au, 0x32B8u, 0x335Fu, 0x3393u, 0x339Cu, 0x33C3u, 0x33D5u, + 0x346Du, 0x36A3u, 0x38A7u, 0x3A8Du, 0x3AFAu, 0x3CBCu, 0x3D1Eu, 0x3ED1u, + 0x3F5Eu, 0x3F8Eu, 0x4263u, 0x42EEu, 0x43ABu, 0x4608u, 0x4735u, 0x4814u, + 0x4C36u, 0x4C92u, 0x4FA1u, 0x4FB8u, 0x5044u, 0x50F2u, 0x50F3u, 0x5119u, + 0x5133u, 0x5249u, 0x541Du, 0x5626u, 0x569Au, 0x56C5u, 0x597Cu, 0x5AA7u, + 0x5BABu, 0x5C80u, 0x5CD0u, 0x5F86u, 0x61DAu, 0x6228u, 0x6247u, 0x62D9u, + 0x633Eu, 0x64DAu, 0x6523u, 0x65A8u, 0x67A7u, 0x67B5u, 0x6B3Cu, 0x6C36u, + 0x6CD5u, 0x6D6Bu, 0x6F2Cu, 0x6FB1u, 0x70D2u, 0x73CAu, 0x7667u, 0x78AEu, + 0x7966u, 0x7CA8u, 0x7ED3u, 0x7F2Fu, 0x85D2u, 0x85EDu, 0x872Eu, 0x8BFAu, + 0x8D77u, 0x9145u, 0x91DFu, 0x921Au, 0x940Au, 0x9496u, 0x95B6u, 0x9B30u, + 0xA0CEu, 0xA105u, 0xA20Eu, 0xA291u, 0xA392u, 0xA600u, +}; +static const uint32_t +_hb_ucd_dm2_u32_map[638] = +{ + HB_CODEPOINT_ENCODE3_11_7_14 (0x003Cu, 0x0338u, 0x226Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x003Du, 0x0338u, 0x2260u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x003Eu, 0x0338u, 0x226Fu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x0300u, 0x00C0u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x0301u, 0x00C1u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x0302u, 0x00C2u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x0303u, 0x00C3u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x0304u, 0x0100u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x0306u, 0x0102u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x0307u, 0x0226u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x0308u, 0x00C4u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x0309u, 0x1EA2u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x030Au, 0x00C5u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x030Cu, 0x01CDu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x030Fu, 0x0200u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x0311u, 0x0202u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x0323u, 0x1EA0u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x0325u, 0x1E00u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x0328u, 0x0104u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0042u, 0x0307u, 0x1E02u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0042u, 0x0323u, 0x1E04u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0042u, 0x0331u, 0x1E06u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0043u, 0x0301u, 0x0106u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0043u, 0x0302u, 0x0108u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0043u, 0x0307u, 0x010Au), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0043u, 0x030Cu, 0x010Cu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0043u, 0x0327u, 0x00C7u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0044u, 0x0307u, 0x1E0Au), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0044u, 0x030Cu, 0x010Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0044u, 0x0323u, 0x1E0Cu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0044u, 0x0327u, 0x1E10u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0044u, 0x032Du, 0x1E12u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0044u, 0x0331u, 0x1E0Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x0300u, 0x00C8u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x0301u, 0x00C9u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x0302u, 0x00CAu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x0303u, 0x1EBCu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x0304u, 0x0112u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x0306u, 0x0114u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x0307u, 0x0116u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x0308u, 0x00CBu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x0309u, 0x1EBAu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x030Cu, 0x011Au), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x030Fu, 0x0204u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x0311u, 0x0206u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x0323u, 0x1EB8u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x0327u, 0x0228u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x0328u, 0x0118u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x032Du, 0x1E18u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x0330u, 0x1E1Au), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0046u, 0x0307u, 0x1E1Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0047u, 0x0301u, 0x01F4u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0047u, 0x0302u, 0x011Cu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0047u, 0x0304u, 0x1E20u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0047u, 0x0306u, 0x011Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0047u, 0x0307u, 0x0120u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0047u, 0x030Cu, 0x01E6u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0047u, 0x0327u, 0x0122u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0048u, 0x0302u, 0x0124u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0048u, 0x0307u, 0x1E22u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0048u, 0x0308u, 0x1E26u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0048u, 0x030Cu, 0x021Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0048u, 0x0323u, 0x1E24u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0048u, 0x0327u, 0x1E28u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0048u, 0x032Eu, 0x1E2Au), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0049u, 0x0300u, 0x00CCu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0049u, 0x0301u, 0x00CDu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0049u, 0x0302u, 0x00CEu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0049u, 0x0303u, 0x0128u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0049u, 0x0304u, 0x012Au), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0049u, 0x0306u, 0x012Cu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0049u, 0x0307u, 0x0130u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0049u, 0x0308u, 0x00CFu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0049u, 0x0309u, 0x1EC8u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0049u, 0x030Cu, 0x01CFu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0049u, 0x030Fu, 0x0208u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0049u, 0x0311u, 0x020Au), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0049u, 0x0323u, 0x1ECAu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0049u, 0x0328u, 0x012Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0049u, 0x0330u, 0x1E2Cu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Au, 0x0302u, 0x0134u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Bu, 0x0301u, 0x1E30u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Bu, 0x030Cu, 0x01E8u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Bu, 0x0323u, 0x1E32u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Bu, 0x0327u, 0x0136u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Bu, 0x0331u, 0x1E34u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Cu, 0x0301u, 0x0139u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Cu, 0x030Cu, 0x013Du), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Cu, 0x0323u, 0x1E36u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Cu, 0x0327u, 0x013Bu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Cu, 0x032Du, 0x1E3Cu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Cu, 0x0331u, 0x1E3Au), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Du, 0x0301u, 0x1E3Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Du, 0x0307u, 0x1E40u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Du, 0x0323u, 0x1E42u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Eu, 0x0300u, 0x01F8u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Eu, 0x0301u, 0x0143u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Eu, 0x0303u, 0x00D1u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Eu, 0x0307u, 0x1E44u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Eu, 0x030Cu, 0x0147u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Eu, 0x0323u, 0x1E46u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Eu, 0x0327u, 0x0145u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Eu, 0x032Du, 0x1E4Au), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Eu, 0x0331u, 0x1E48u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x0300u, 0x00D2u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x0301u, 0x00D3u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x0302u, 0x00D4u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x0303u, 0x00D5u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x0304u, 0x014Cu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x0306u, 0x014Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x0307u, 0x022Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x0308u, 0x00D6u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x0309u, 0x1ECEu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x030Bu, 0x0150u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x030Cu, 0x01D1u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x030Fu, 0x020Cu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x0311u, 0x020Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x031Bu, 0x01A0u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x0323u, 0x1ECCu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x0328u, 0x01EAu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0050u, 0x0301u, 0x1E54u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0050u, 0x0307u, 0x1E56u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0052u, 0x0301u, 0x0154u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0052u, 0x0307u, 0x1E58u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0052u, 0x030Cu, 0x0158u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0052u, 0x030Fu, 0x0210u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0052u, 0x0311u, 0x0212u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0052u, 0x0323u, 0x1E5Au), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0052u, 0x0327u, 0x0156u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0052u, 0x0331u, 0x1E5Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0053u, 0x0301u, 0x015Au), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0053u, 0x0302u, 0x015Cu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0053u, 0x0307u, 0x1E60u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0053u, 0x030Cu, 0x0160u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0053u, 0x0323u, 0x1E62u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0053u, 0x0326u, 0x0218u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0053u, 0x0327u, 0x015Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0054u, 0x0307u, 0x1E6Au), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0054u, 0x030Cu, 0x0164u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0054u, 0x0323u, 0x1E6Cu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0054u, 0x0326u, 0x021Au), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0054u, 0x0327u, 0x0162u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0054u, 0x032Du, 0x1E70u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0054u, 0x0331u, 0x1E6Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x0300u, 0x00D9u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x0301u, 0x00DAu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x0302u, 0x00DBu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x0303u, 0x0168u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x0304u, 0x016Au), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x0306u, 0x016Cu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x0308u, 0x00DCu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x0309u, 0x1EE6u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x030Au, 0x016Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x030Bu, 0x0170u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x030Cu, 0x01D3u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x030Fu, 0x0214u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x0311u, 0x0216u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x031Bu, 0x01AFu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x0323u, 0x1EE4u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x0324u, 0x1E72u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x0328u, 0x0172u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x032Du, 0x1E76u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x0330u, 0x1E74u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0056u, 0x0303u, 0x1E7Cu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0056u, 0x0323u, 0x1E7Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0057u, 0x0300u, 0x1E80u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0057u, 0x0301u, 0x1E82u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0057u, 0x0302u, 0x0174u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0057u, 0x0307u, 0x1E86u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0057u, 0x0308u, 0x1E84u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0057u, 0x0323u, 0x1E88u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0058u, 0x0307u, 0x1E8Au), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0058u, 0x0308u, 0x1E8Cu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0059u, 0x0300u, 0x1EF2u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0059u, 0x0301u, 0x00DDu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0059u, 0x0302u, 0x0176u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0059u, 0x0303u, 0x1EF8u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0059u, 0x0304u, 0x0232u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0059u, 0x0307u, 0x1E8Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0059u, 0x0308u, 0x0178u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0059u, 0x0309u, 0x1EF6u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0059u, 0x0323u, 0x1EF4u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x005Au, 0x0301u, 0x0179u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x005Au, 0x0302u, 0x1E90u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x005Au, 0x0307u, 0x017Bu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x005Au, 0x030Cu, 0x017Du), + HB_CODEPOINT_ENCODE3_11_7_14 (0x005Au, 0x0323u, 0x1E92u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x005Au, 0x0331u, 0x1E94u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x0300u, 0x00E0u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x0301u, 0x00E1u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x0302u, 0x00E2u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x0303u, 0x00E3u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x0304u, 0x0101u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x0306u, 0x0103u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x0307u, 0x0227u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x0308u, 0x00E4u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x0309u, 0x1EA3u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x030Au, 0x00E5u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x030Cu, 0x01CEu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x030Fu, 0x0201u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x0311u, 0x0203u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x0323u, 0x1EA1u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x0325u, 0x1E01u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x0328u, 0x0105u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0062u, 0x0307u, 0x1E03u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0062u, 0x0323u, 0x1E05u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0062u, 0x0331u, 0x1E07u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0063u, 0x0301u, 0x0107u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0063u, 0x0302u, 0x0109u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0063u, 0x0307u, 0x010Bu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0063u, 0x030Cu, 0x010Du), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0063u, 0x0327u, 0x00E7u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0064u, 0x0307u, 0x1E0Bu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0064u, 0x030Cu, 0x010Fu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0064u, 0x0323u, 0x1E0Du), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0064u, 0x0327u, 0x1E11u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0064u, 0x032Du, 0x1E13u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0064u, 0x0331u, 0x1E0Fu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x0300u, 0x00E8u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x0301u, 0x00E9u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x0302u, 0x00EAu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x0303u, 0x1EBDu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x0304u, 0x0113u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x0306u, 0x0115u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x0307u, 0x0117u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x0308u, 0x00EBu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x0309u, 0x1EBBu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x030Cu, 0x011Bu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x030Fu, 0x0205u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x0311u, 0x0207u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x0323u, 0x1EB9u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x0327u, 0x0229u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x0328u, 0x0119u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x032Du, 0x1E19u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x0330u, 0x1E1Bu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0066u, 0x0307u, 0x1E1Fu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0067u, 0x0301u, 0x01F5u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0067u, 0x0302u, 0x011Du), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0067u, 0x0304u, 0x1E21u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0067u, 0x0306u, 0x011Fu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0067u, 0x0307u, 0x0121u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0067u, 0x030Cu, 0x01E7u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0067u, 0x0327u, 0x0123u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0068u, 0x0302u, 0x0125u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0068u, 0x0307u, 0x1E23u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0068u, 0x0308u, 0x1E27u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0068u, 0x030Cu, 0x021Fu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0068u, 0x0323u, 0x1E25u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0068u, 0x0327u, 0x1E29u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0068u, 0x032Eu, 0x1E2Bu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0068u, 0x0331u, 0x1E96u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0069u, 0x0300u, 0x00ECu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0069u, 0x0301u, 0x00EDu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0069u, 0x0302u, 0x00EEu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0069u, 0x0303u, 0x0129u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0069u, 0x0304u, 0x012Bu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0069u, 0x0306u, 0x012Du), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0069u, 0x0308u, 0x00EFu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0069u, 0x0309u, 0x1EC9u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0069u, 0x030Cu, 0x01D0u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0069u, 0x030Fu, 0x0209u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0069u, 0x0311u, 0x020Bu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0069u, 0x0323u, 0x1ECBu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0069u, 0x0328u, 0x012Fu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0069u, 0x0330u, 0x1E2Du), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Au, 0x0302u, 0x0135u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Au, 0x030Cu, 0x01F0u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Bu, 0x0301u, 0x1E31u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Bu, 0x030Cu, 0x01E9u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Bu, 0x0323u, 0x1E33u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Bu, 0x0327u, 0x0137u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Bu, 0x0331u, 0x1E35u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Cu, 0x0301u, 0x013Au), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Cu, 0x030Cu, 0x013Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Cu, 0x0323u, 0x1E37u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Cu, 0x0327u, 0x013Cu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Cu, 0x032Du, 0x1E3Du), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Cu, 0x0331u, 0x1E3Bu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Du, 0x0301u, 0x1E3Fu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Du, 0x0307u, 0x1E41u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Du, 0x0323u, 0x1E43u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Eu, 0x0300u, 0x01F9u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Eu, 0x0301u, 0x0144u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Eu, 0x0303u, 0x00F1u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Eu, 0x0307u, 0x1E45u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Eu, 0x030Cu, 0x0148u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Eu, 0x0323u, 0x1E47u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Eu, 0x0327u, 0x0146u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Eu, 0x032Du, 0x1E4Bu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Eu, 0x0331u, 0x1E49u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x0300u, 0x00F2u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x0301u, 0x00F3u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x0302u, 0x00F4u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x0303u, 0x00F5u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x0304u, 0x014Du), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x0306u, 0x014Fu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x0307u, 0x022Fu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x0308u, 0x00F6u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x0309u, 0x1ECFu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x030Bu, 0x0151u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x030Cu, 0x01D2u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x030Fu, 0x020Du), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x0311u, 0x020Fu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x031Bu, 0x01A1u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x0323u, 0x1ECDu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x0328u, 0x01EBu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0070u, 0x0301u, 0x1E55u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0070u, 0x0307u, 0x1E57u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0072u, 0x0301u, 0x0155u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0072u, 0x0307u, 0x1E59u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0072u, 0x030Cu, 0x0159u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0072u, 0x030Fu, 0x0211u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0072u, 0x0311u, 0x0213u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0072u, 0x0323u, 0x1E5Bu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0072u, 0x0327u, 0x0157u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0072u, 0x0331u, 0x1E5Fu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0073u, 0x0301u, 0x015Bu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0073u, 0x0302u, 0x015Du), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0073u, 0x0307u, 0x1E61u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0073u, 0x030Cu, 0x0161u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0073u, 0x0323u, 0x1E63u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0073u, 0x0326u, 0x0219u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0073u, 0x0327u, 0x015Fu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0074u, 0x0307u, 0x1E6Bu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0074u, 0x0308u, 0x1E97u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0074u, 0x030Cu, 0x0165u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0074u, 0x0323u, 0x1E6Du), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0074u, 0x0326u, 0x021Bu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0074u, 0x0327u, 0x0163u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0074u, 0x032Du, 0x1E71u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0074u, 0x0331u, 0x1E6Fu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x0300u, 0x00F9u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x0301u, 0x00FAu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x0302u, 0x00FBu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x0303u, 0x0169u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x0304u, 0x016Bu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x0306u, 0x016Du), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x0308u, 0x00FCu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x0309u, 0x1EE7u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x030Au, 0x016Fu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x030Bu, 0x0171u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x030Cu, 0x01D4u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x030Fu, 0x0215u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x0311u, 0x0217u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x031Bu, 0x01B0u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x0323u, 0x1EE5u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x0324u, 0x1E73u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x0328u, 0x0173u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x032Du, 0x1E77u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x0330u, 0x1E75u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0076u, 0x0303u, 0x1E7Du), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0076u, 0x0323u, 0x1E7Fu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0077u, 0x0300u, 0x1E81u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0077u, 0x0301u, 0x1E83u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0077u, 0x0302u, 0x0175u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0077u, 0x0307u, 0x1E87u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0077u, 0x0308u, 0x1E85u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0077u, 0x030Au, 0x1E98u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0077u, 0x0323u, 0x1E89u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0078u, 0x0307u, 0x1E8Bu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0078u, 0x0308u, 0x1E8Du), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0079u, 0x0300u, 0x1EF3u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0079u, 0x0301u, 0x00FDu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0079u, 0x0302u, 0x0177u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0079u, 0x0303u, 0x1EF9u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0079u, 0x0304u, 0x0233u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0079u, 0x0307u, 0x1E8Fu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0079u, 0x0308u, 0x00FFu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0079u, 0x0309u, 0x1EF7u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0079u, 0x030Au, 0x1E99u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0079u, 0x0323u, 0x1EF5u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x007Au, 0x0301u, 0x017Au), + HB_CODEPOINT_ENCODE3_11_7_14 (0x007Au, 0x0302u, 0x1E91u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x007Au, 0x0307u, 0x017Cu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x007Au, 0x030Cu, 0x017Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x007Au, 0x0323u, 0x1E93u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x007Au, 0x0331u, 0x1E95u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00A8u, 0x0300u, 0x1FEDu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00A8u, 0x0301u, 0x0385u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00A8u, 0x0342u, 0x1FC1u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00C2u, 0x0300u, 0x1EA6u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00C2u, 0x0301u, 0x1EA4u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00C2u, 0x0303u, 0x1EAAu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00C2u, 0x0309u, 0x1EA8u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00C4u, 0x0304u, 0x01DEu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00C5u, 0x0301u, 0x01FAu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00C6u, 0x0301u, 0x01FCu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00C6u, 0x0304u, 0x01E2u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00C7u, 0x0301u, 0x1E08u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00CAu, 0x0300u, 0x1EC0u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00CAu, 0x0301u, 0x1EBEu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00CAu, 0x0303u, 0x1EC4u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00CAu, 0x0309u, 0x1EC2u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00CFu, 0x0301u, 0x1E2Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00D4u, 0x0300u, 0x1ED2u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00D4u, 0x0301u, 0x1ED0u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00D4u, 0x0303u, 0x1ED6u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00D4u, 0x0309u, 0x1ED4u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00D5u, 0x0301u, 0x1E4Cu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00D5u, 0x0304u, 0x022Cu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00D5u, 0x0308u, 0x1E4Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00D6u, 0x0304u, 0x022Au), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00D8u, 0x0301u, 0x01FEu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00DCu, 0x0300u, 0x01DBu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00DCu, 0x0301u, 0x01D7u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00DCu, 0x0304u, 0x01D5u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00DCu, 0x030Cu, 0x01D9u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00E2u, 0x0300u, 0x1EA7u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00E2u, 0x0301u, 0x1EA5u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00E2u, 0x0303u, 0x1EABu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00E2u, 0x0309u, 0x1EA9u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00E4u, 0x0304u, 0x01DFu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00E5u, 0x0301u, 0x01FBu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00E6u, 0x0301u, 0x01FDu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00E6u, 0x0304u, 0x01E3u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00E7u, 0x0301u, 0x1E09u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00EAu, 0x0300u, 0x1EC1u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00EAu, 0x0301u, 0x1EBFu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00EAu, 0x0303u, 0x1EC5u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00EAu, 0x0309u, 0x1EC3u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00EFu, 0x0301u, 0x1E2Fu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00F4u, 0x0300u, 0x1ED3u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00F4u, 0x0301u, 0x1ED1u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00F4u, 0x0303u, 0x1ED7u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00F4u, 0x0309u, 0x1ED5u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00F5u, 0x0301u, 0x1E4Du), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00F5u, 0x0304u, 0x022Du), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00F5u, 0x0308u, 0x1E4Fu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00F6u, 0x0304u, 0x022Bu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00F8u, 0x0301u, 0x01FFu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00FCu, 0x0300u, 0x01DCu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00FCu, 0x0301u, 0x01D8u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00FCu, 0x0304u, 0x01D6u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00FCu, 0x030Cu, 0x01DAu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0102u, 0x0300u, 0x1EB0u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0102u, 0x0301u, 0x1EAEu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0102u, 0x0303u, 0x1EB4u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0102u, 0x0309u, 0x1EB2u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0103u, 0x0300u, 0x1EB1u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0103u, 0x0301u, 0x1EAFu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0103u, 0x0303u, 0x1EB5u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0103u, 0x0309u, 0x1EB3u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0112u, 0x0300u, 0x1E14u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0112u, 0x0301u, 0x1E16u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0113u, 0x0300u, 0x1E15u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0113u, 0x0301u, 0x1E17u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x014Cu, 0x0300u, 0x1E50u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x014Cu, 0x0301u, 0x1E52u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x014Du, 0x0300u, 0x1E51u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x014Du, 0x0301u, 0x1E53u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x015Au, 0x0307u, 0x1E64u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x015Bu, 0x0307u, 0x1E65u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0160u, 0x0307u, 0x1E66u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0161u, 0x0307u, 0x1E67u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0168u, 0x0301u, 0x1E78u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0169u, 0x0301u, 0x1E79u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x016Au, 0x0308u, 0x1E7Au), + HB_CODEPOINT_ENCODE3_11_7_14 (0x016Bu, 0x0308u, 0x1E7Bu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x017Fu, 0x0307u, 0x1E9Bu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01A0u, 0x0300u, 0x1EDCu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01A0u, 0x0301u, 0x1EDAu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01A0u, 0x0303u, 0x1EE0u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01A0u, 0x0309u, 0x1EDEu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01A0u, 0x0323u, 0x1EE2u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01A1u, 0x0300u, 0x1EDDu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01A1u, 0x0301u, 0x1EDBu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01A1u, 0x0303u, 0x1EE1u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01A1u, 0x0309u, 0x1EDFu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01A1u, 0x0323u, 0x1EE3u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01AFu, 0x0300u, 0x1EEAu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01AFu, 0x0301u, 0x1EE8u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01AFu, 0x0303u, 0x1EEEu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01AFu, 0x0309u, 0x1EECu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01AFu, 0x0323u, 0x1EF0u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01B0u, 0x0300u, 0x1EEBu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01B0u, 0x0301u, 0x1EE9u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01B0u, 0x0303u, 0x1EEFu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01B0u, 0x0309u, 0x1EEDu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01B0u, 0x0323u, 0x1EF1u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01B7u, 0x030Cu, 0x01EEu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01EAu, 0x0304u, 0x01ECu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01EBu, 0x0304u, 0x01EDu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0226u, 0x0304u, 0x01E0u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0227u, 0x0304u, 0x01E1u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0228u, 0x0306u, 0x1E1Cu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0229u, 0x0306u, 0x1E1Du), + HB_CODEPOINT_ENCODE3_11_7_14 (0x022Eu, 0x0304u, 0x0230u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x022Fu, 0x0304u, 0x0231u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0292u, 0x030Cu, 0x01EFu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0308u, 0x0301u, 0x0000u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0391u, 0x0300u, 0x1FBAu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0391u, 0x0301u, 0x0386u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0391u, 0x0304u, 0x1FB9u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0391u, 0x0306u, 0x1FB8u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0391u, 0x0313u, 0x1F08u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0391u, 0x0314u, 0x1F09u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0391u, 0x0345u, 0x1FBCu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0395u, 0x0300u, 0x1FC8u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0395u, 0x0301u, 0x0388u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0395u, 0x0313u, 0x1F18u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0395u, 0x0314u, 0x1F19u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0397u, 0x0300u, 0x1FCAu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0397u, 0x0301u, 0x0389u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0397u, 0x0313u, 0x1F28u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0397u, 0x0314u, 0x1F29u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0397u, 0x0345u, 0x1FCCu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0399u, 0x0300u, 0x1FDAu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0399u, 0x0301u, 0x038Au), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0399u, 0x0304u, 0x1FD9u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0399u, 0x0306u, 0x1FD8u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0399u, 0x0308u, 0x03AAu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0399u, 0x0313u, 0x1F38u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0399u, 0x0314u, 0x1F39u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x039Fu, 0x0300u, 0x1FF8u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x039Fu, 0x0301u, 0x038Cu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x039Fu, 0x0313u, 0x1F48u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x039Fu, 0x0314u, 0x1F49u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03A1u, 0x0314u, 0x1FECu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03A5u, 0x0300u, 0x1FEAu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03A5u, 0x0301u, 0x038Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03A5u, 0x0304u, 0x1FE9u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03A5u, 0x0306u, 0x1FE8u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03A5u, 0x0308u, 0x03ABu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03A5u, 0x0314u, 0x1F59u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03A9u, 0x0300u, 0x1FFAu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03A9u, 0x0301u, 0x038Fu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03A9u, 0x0313u, 0x1F68u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03A9u, 0x0314u, 0x1F69u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03A9u, 0x0345u, 0x1FFCu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03ACu, 0x0345u, 0x1FB4u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03AEu, 0x0345u, 0x1FC4u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B1u, 0x0300u, 0x1F70u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B1u, 0x0301u, 0x03ACu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B1u, 0x0304u, 0x1FB1u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B1u, 0x0306u, 0x1FB0u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B1u, 0x0313u, 0x1F00u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B1u, 0x0314u, 0x1F01u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B1u, 0x0342u, 0x1FB6u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B1u, 0x0345u, 0x1FB3u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B5u, 0x0300u, 0x1F72u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B5u, 0x0301u, 0x03ADu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B5u, 0x0313u, 0x1F10u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B5u, 0x0314u, 0x1F11u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B7u, 0x0300u, 0x1F74u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B7u, 0x0301u, 0x03AEu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B7u, 0x0313u, 0x1F20u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B7u, 0x0314u, 0x1F21u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B7u, 0x0342u, 0x1FC6u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B7u, 0x0345u, 0x1FC3u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B9u, 0x0300u, 0x1F76u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B9u, 0x0301u, 0x03AFu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B9u, 0x0304u, 0x1FD1u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B9u, 0x0306u, 0x1FD0u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B9u, 0x0308u, 0x03CAu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B9u, 0x0313u, 0x1F30u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B9u, 0x0314u, 0x1F31u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B9u, 0x0342u, 0x1FD6u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03BFu, 0x0300u, 0x1F78u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03BFu, 0x0301u, 0x03CCu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03BFu, 0x0313u, 0x1F40u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03BFu, 0x0314u, 0x1F41u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03C1u, 0x0313u, 0x1FE4u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03C1u, 0x0314u, 0x1FE5u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03C5u, 0x0300u, 0x1F7Au), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03C5u, 0x0301u, 0x03CDu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03C5u, 0x0304u, 0x1FE1u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03C5u, 0x0306u, 0x1FE0u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03C5u, 0x0308u, 0x03CBu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03C5u, 0x0313u, 0x1F50u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03C5u, 0x0314u, 0x1F51u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03C5u, 0x0342u, 0x1FE6u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03C9u, 0x0300u, 0x1F7Cu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03C9u, 0x0301u, 0x03CEu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03C9u, 0x0313u, 0x1F60u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03C9u, 0x0314u, 0x1F61u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03C9u, 0x0342u, 0x1FF6u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03C9u, 0x0345u, 0x1FF3u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03CAu, 0x0300u, 0x1FD2u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03CAu, 0x0301u, 0x0390u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03CAu, 0x0342u, 0x1FD7u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03CBu, 0x0300u, 0x1FE2u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03CBu, 0x0301u, 0x03B0u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03CBu, 0x0342u, 0x1FE7u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03CEu, 0x0345u, 0x1FF4u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03D2u, 0x0301u, 0x03D3u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03D2u, 0x0308u, 0x03D4u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0406u, 0x0308u, 0x0407u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0410u, 0x0306u, 0x04D0u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0410u, 0x0308u, 0x04D2u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0413u, 0x0301u, 0x0403u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0415u, 0x0300u, 0x0400u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0415u, 0x0306u, 0x04D6u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0415u, 0x0308u, 0x0401u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0416u, 0x0306u, 0x04C1u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0416u, 0x0308u, 0x04DCu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0417u, 0x0308u, 0x04DEu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0418u, 0x0300u, 0x040Du), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0418u, 0x0304u, 0x04E2u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0418u, 0x0306u, 0x0419u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0418u, 0x0308u, 0x04E4u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x041Au, 0x0301u, 0x040Cu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x041Eu, 0x0308u, 0x04E6u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0423u, 0x0304u, 0x04EEu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0423u, 0x0306u, 0x040Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0423u, 0x0308u, 0x04F0u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0423u, 0x030Bu, 0x04F2u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0427u, 0x0308u, 0x04F4u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x042Bu, 0x0308u, 0x04F8u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x042Du, 0x0308u, 0x04ECu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0430u, 0x0306u, 0x04D1u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0430u, 0x0308u, 0x04D3u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0433u, 0x0301u, 0x0453u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0435u, 0x0300u, 0x0450u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0435u, 0x0306u, 0x04D7u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0435u, 0x0308u, 0x0451u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0436u, 0x0306u, 0x04C2u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0436u, 0x0308u, 0x04DDu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0437u, 0x0308u, 0x04DFu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0438u, 0x0300u, 0x045Du), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0438u, 0x0304u, 0x04E3u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0438u, 0x0306u, 0x0439u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0438u, 0x0308u, 0x04E5u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x043Au, 0x0301u, 0x045Cu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x043Eu, 0x0308u, 0x04E7u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0443u, 0x0304u, 0x04EFu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0443u, 0x0306u, 0x045Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0443u, 0x0308u, 0x04F1u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0443u, 0x030Bu, 0x04F3u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0447u, 0x0308u, 0x04F5u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x044Bu, 0x0308u, 0x04F9u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x044Du, 0x0308u, 0x04EDu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0456u, 0x0308u, 0x0457u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0474u, 0x030Fu, 0x0476u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0475u, 0x030Fu, 0x0477u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x04D8u, 0x0308u, 0x04DAu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x04D9u, 0x0308u, 0x04DBu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x04E8u, 0x0308u, 0x04EAu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x04E9u, 0x0308u, 0x04EBu), +}; +static const uint64_t +_hb_ucd_dm2_u64_map[388] = +{ + HB_CODEPOINT_ENCODE3 (0x05D0u, 0x05B7u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05D0u, 0x05B8u, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x05D0u, 0x05BCu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05D1u, 0x05BCu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x05D1u, 0x05BFu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05D2u, 0x05BCu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x05D3u, 0x05BCu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05D4u, 0x05BCu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x05D5u, 0x05B9u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05D5u, 0x05BCu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x05D6u, 0x05BCu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05D8u, 0x05BCu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x05D9u, 0x05B4u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05D9u, 0x05BCu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x05DAu, 0x05BCu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05DBu, 0x05BCu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x05DBu, 0x05BFu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05DCu, 0x05BCu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x05DEu, 0x05BCu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05E0u, 0x05BCu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x05E1u, 0x05BCu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05E3u, 0x05BCu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x05E4u, 0x05BCu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05E4u, 0x05BFu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x05E6u, 0x05BCu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05E7u, 0x05BCu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x05E8u, 0x05BCu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05E9u, 0x05BCu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x05E9u, 0x05C1u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05E9u, 0x05C2u, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x05EAu, 0x05BCu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05F2u, 0x05B7u, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x0627u, 0x0653u, 0x0622u), HB_CODEPOINT_ENCODE3 (0x0627u, 0x0654u, 0x0623u), + HB_CODEPOINT_ENCODE3 (0x0627u, 0x0655u, 0x0625u), HB_CODEPOINT_ENCODE3 (0x0648u, 0x0654u, 0x0624u), + HB_CODEPOINT_ENCODE3 (0x064Au, 0x0654u, 0x0626u), HB_CODEPOINT_ENCODE3 (0x06C1u, 0x0654u, 0x06C2u), + HB_CODEPOINT_ENCODE3 (0x06D2u, 0x0654u, 0x06D3u), HB_CODEPOINT_ENCODE3 (0x06D5u, 0x0654u, 0x06C0u), + HB_CODEPOINT_ENCODE3 (0x0915u, 0x093Cu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x0916u, 0x093Cu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x0917u, 0x093Cu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x091Cu, 0x093Cu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x0921u, 0x093Cu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x0922u, 0x093Cu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x0928u, 0x093Cu, 0x0929u), HB_CODEPOINT_ENCODE3 (0x092Bu, 0x093Cu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x092Fu, 0x093Cu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x0930u, 0x093Cu, 0x0931u), + HB_CODEPOINT_ENCODE3 (0x0933u, 0x093Cu, 0x0934u), HB_CODEPOINT_ENCODE3 (0x09A1u, 0x09BCu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x09A2u, 0x09BCu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x09AFu, 0x09BCu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x09C7u, 0x09BEu, 0x09CBu), HB_CODEPOINT_ENCODE3 (0x09C7u, 0x09D7u, 0x09CCu), + HB_CODEPOINT_ENCODE3 (0x0A16u, 0x0A3Cu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x0A17u, 0x0A3Cu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x0A1Cu, 0x0A3Cu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x0A2Bu, 0x0A3Cu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x0A32u, 0x0A3Cu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x0A38u, 0x0A3Cu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x0B21u, 0x0B3Cu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x0B22u, 0x0B3Cu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x0B47u, 0x0B3Eu, 0x0B4Bu), HB_CODEPOINT_ENCODE3 (0x0B47u, 0x0B56u, 0x0B48u), + HB_CODEPOINT_ENCODE3 (0x0B47u, 0x0B57u, 0x0B4Cu), HB_CODEPOINT_ENCODE3 (0x0B92u, 0x0BD7u, 0x0B94u), + HB_CODEPOINT_ENCODE3 (0x0BC6u, 0x0BBEu, 0x0BCAu), HB_CODEPOINT_ENCODE3 (0x0BC6u, 0x0BD7u, 0x0BCCu), + HB_CODEPOINT_ENCODE3 (0x0BC7u, 0x0BBEu, 0x0BCBu), HB_CODEPOINT_ENCODE3 (0x0C46u, 0x0C56u, 0x0C48u), + HB_CODEPOINT_ENCODE3 (0x0CBFu, 0x0CD5u, 0x0CC0u), HB_CODEPOINT_ENCODE3 (0x0CC6u, 0x0CC2u, 0x0CCAu), + HB_CODEPOINT_ENCODE3 (0x0CC6u, 0x0CD5u, 0x0CC7u), HB_CODEPOINT_ENCODE3 (0x0CC6u, 0x0CD6u, 0x0CC8u), + HB_CODEPOINT_ENCODE3 (0x0CCAu, 0x0CD5u, 0x0CCBu), HB_CODEPOINT_ENCODE3 (0x0D46u, 0x0D3Eu, 0x0D4Au), + HB_CODEPOINT_ENCODE3 (0x0D46u, 0x0D57u, 0x0D4Cu), HB_CODEPOINT_ENCODE3 (0x0D47u, 0x0D3Eu, 0x0D4Bu), + HB_CODEPOINT_ENCODE3 (0x0DD9u, 0x0DCAu, 0x0DDAu), HB_CODEPOINT_ENCODE3 (0x0DD9u, 0x0DCFu, 0x0DDCu), + HB_CODEPOINT_ENCODE3 (0x0DD9u, 0x0DDFu, 0x0DDEu), HB_CODEPOINT_ENCODE3 (0x0DDCu, 0x0DCAu, 0x0DDDu), + HB_CODEPOINT_ENCODE3 (0x0F40u, 0x0FB5u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x0F42u, 0x0FB7u, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x0F4Cu, 0x0FB7u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x0F51u, 0x0FB7u, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x0F56u, 0x0FB7u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x0F5Bu, 0x0FB7u, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x0F71u, 0x0F72u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x0F71u, 0x0F74u, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x0F71u, 0x0F80u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x0F90u, 0x0FB5u, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x0F92u, 0x0FB7u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x0F9Cu, 0x0FB7u, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x0FA1u, 0x0FB7u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x0FA6u, 0x0FB7u, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x0FABu, 0x0FB7u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x0FB2u, 0x0F80u, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x0FB3u, 0x0F80u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x1025u, 0x102Eu, 0x1026u), + HB_CODEPOINT_ENCODE3 (0x1B05u, 0x1B35u, 0x1B06u), HB_CODEPOINT_ENCODE3 (0x1B07u, 0x1B35u, 0x1B08u), + HB_CODEPOINT_ENCODE3 (0x1B09u, 0x1B35u, 0x1B0Au), HB_CODEPOINT_ENCODE3 (0x1B0Bu, 0x1B35u, 0x1B0Cu), + HB_CODEPOINT_ENCODE3 (0x1B0Du, 0x1B35u, 0x1B0Eu), HB_CODEPOINT_ENCODE3 (0x1B11u, 0x1B35u, 0x1B12u), + HB_CODEPOINT_ENCODE3 (0x1B3Au, 0x1B35u, 0x1B3Bu), HB_CODEPOINT_ENCODE3 (0x1B3Cu, 0x1B35u, 0x1B3Du), + HB_CODEPOINT_ENCODE3 (0x1B3Eu, 0x1B35u, 0x1B40u), HB_CODEPOINT_ENCODE3 (0x1B3Fu, 0x1B35u, 0x1B41u), + HB_CODEPOINT_ENCODE3 (0x1B42u, 0x1B35u, 0x1B43u), HB_CODEPOINT_ENCODE3 (0x1E36u, 0x0304u, 0x1E38u), + HB_CODEPOINT_ENCODE3 (0x1E37u, 0x0304u, 0x1E39u), HB_CODEPOINT_ENCODE3 (0x1E5Au, 0x0304u, 0x1E5Cu), + HB_CODEPOINT_ENCODE3 (0x1E5Bu, 0x0304u, 0x1E5Du), HB_CODEPOINT_ENCODE3 (0x1E62u, 0x0307u, 0x1E68u), + HB_CODEPOINT_ENCODE3 (0x1E63u, 0x0307u, 0x1E69u), HB_CODEPOINT_ENCODE3 (0x1EA0u, 0x0302u, 0x1EACu), + HB_CODEPOINT_ENCODE3 (0x1EA0u, 0x0306u, 0x1EB6u), HB_CODEPOINT_ENCODE3 (0x1EA1u, 0x0302u, 0x1EADu), + HB_CODEPOINT_ENCODE3 (0x1EA1u, 0x0306u, 0x1EB7u), HB_CODEPOINT_ENCODE3 (0x1EB8u, 0x0302u, 0x1EC6u), + HB_CODEPOINT_ENCODE3 (0x1EB9u, 0x0302u, 0x1EC7u), HB_CODEPOINT_ENCODE3 (0x1ECCu, 0x0302u, 0x1ED8u), + HB_CODEPOINT_ENCODE3 (0x1ECDu, 0x0302u, 0x1ED9u), HB_CODEPOINT_ENCODE3 (0x1F00u, 0x0300u, 0x1F02u), + HB_CODEPOINT_ENCODE3 (0x1F00u, 0x0301u, 0x1F04u), HB_CODEPOINT_ENCODE3 (0x1F00u, 0x0342u, 0x1F06u), + HB_CODEPOINT_ENCODE3 (0x1F00u, 0x0345u, 0x1F80u), HB_CODEPOINT_ENCODE3 (0x1F01u, 0x0300u, 0x1F03u), + HB_CODEPOINT_ENCODE3 (0x1F01u, 0x0301u, 0x1F05u), HB_CODEPOINT_ENCODE3 (0x1F01u, 0x0342u, 0x1F07u), + HB_CODEPOINT_ENCODE3 (0x1F01u, 0x0345u, 0x1F81u), HB_CODEPOINT_ENCODE3 (0x1F02u, 0x0345u, 0x1F82u), + HB_CODEPOINT_ENCODE3 (0x1F03u, 0x0345u, 0x1F83u), HB_CODEPOINT_ENCODE3 (0x1F04u, 0x0345u, 0x1F84u), + HB_CODEPOINT_ENCODE3 (0x1F05u, 0x0345u, 0x1F85u), HB_CODEPOINT_ENCODE3 (0x1F06u, 0x0345u, 0x1F86u), + HB_CODEPOINT_ENCODE3 (0x1F07u, 0x0345u, 0x1F87u), HB_CODEPOINT_ENCODE3 (0x1F08u, 0x0300u, 0x1F0Au), + HB_CODEPOINT_ENCODE3 (0x1F08u, 0x0301u, 0x1F0Cu), HB_CODEPOINT_ENCODE3 (0x1F08u, 0x0342u, 0x1F0Eu), + HB_CODEPOINT_ENCODE3 (0x1F08u, 0x0345u, 0x1F88u), HB_CODEPOINT_ENCODE3 (0x1F09u, 0x0300u, 0x1F0Bu), + HB_CODEPOINT_ENCODE3 (0x1F09u, 0x0301u, 0x1F0Du), HB_CODEPOINT_ENCODE3 (0x1F09u, 0x0342u, 0x1F0Fu), + HB_CODEPOINT_ENCODE3 (0x1F09u, 0x0345u, 0x1F89u), HB_CODEPOINT_ENCODE3 (0x1F0Au, 0x0345u, 0x1F8Au), + HB_CODEPOINT_ENCODE3 (0x1F0Bu, 0x0345u, 0x1F8Bu), HB_CODEPOINT_ENCODE3 (0x1F0Cu, 0x0345u, 0x1F8Cu), + HB_CODEPOINT_ENCODE3 (0x1F0Du, 0x0345u, 0x1F8Du), HB_CODEPOINT_ENCODE3 (0x1F0Eu, 0x0345u, 0x1F8Eu), + HB_CODEPOINT_ENCODE3 (0x1F0Fu, 0x0345u, 0x1F8Fu), HB_CODEPOINT_ENCODE3 (0x1F10u, 0x0300u, 0x1F12u), + HB_CODEPOINT_ENCODE3 (0x1F10u, 0x0301u, 0x1F14u), HB_CODEPOINT_ENCODE3 (0x1F11u, 0x0300u, 0x1F13u), + HB_CODEPOINT_ENCODE3 (0x1F11u, 0x0301u, 0x1F15u), HB_CODEPOINT_ENCODE3 (0x1F18u, 0x0300u, 0x1F1Au), + HB_CODEPOINT_ENCODE3 (0x1F18u, 0x0301u, 0x1F1Cu), HB_CODEPOINT_ENCODE3 (0x1F19u, 0x0300u, 0x1F1Bu), + HB_CODEPOINT_ENCODE3 (0x1F19u, 0x0301u, 0x1F1Du), HB_CODEPOINT_ENCODE3 (0x1F20u, 0x0300u, 0x1F22u), + HB_CODEPOINT_ENCODE3 (0x1F20u, 0x0301u, 0x1F24u), HB_CODEPOINT_ENCODE3 (0x1F20u, 0x0342u, 0x1F26u), + HB_CODEPOINT_ENCODE3 (0x1F20u, 0x0345u, 0x1F90u), HB_CODEPOINT_ENCODE3 (0x1F21u, 0x0300u, 0x1F23u), + HB_CODEPOINT_ENCODE3 (0x1F21u, 0x0301u, 0x1F25u), HB_CODEPOINT_ENCODE3 (0x1F21u, 0x0342u, 0x1F27u), + HB_CODEPOINT_ENCODE3 (0x1F21u, 0x0345u, 0x1F91u), HB_CODEPOINT_ENCODE3 (0x1F22u, 0x0345u, 0x1F92u), + HB_CODEPOINT_ENCODE3 (0x1F23u, 0x0345u, 0x1F93u), HB_CODEPOINT_ENCODE3 (0x1F24u, 0x0345u, 0x1F94u), + HB_CODEPOINT_ENCODE3 (0x1F25u, 0x0345u, 0x1F95u), HB_CODEPOINT_ENCODE3 (0x1F26u, 0x0345u, 0x1F96u), + HB_CODEPOINT_ENCODE3 (0x1F27u, 0x0345u, 0x1F97u), HB_CODEPOINT_ENCODE3 (0x1F28u, 0x0300u, 0x1F2Au), + HB_CODEPOINT_ENCODE3 (0x1F28u, 0x0301u, 0x1F2Cu), HB_CODEPOINT_ENCODE3 (0x1F28u, 0x0342u, 0x1F2Eu), + HB_CODEPOINT_ENCODE3 (0x1F28u, 0x0345u, 0x1F98u), HB_CODEPOINT_ENCODE3 (0x1F29u, 0x0300u, 0x1F2Bu), + HB_CODEPOINT_ENCODE3 (0x1F29u, 0x0301u, 0x1F2Du), HB_CODEPOINT_ENCODE3 (0x1F29u, 0x0342u, 0x1F2Fu), + HB_CODEPOINT_ENCODE3 (0x1F29u, 0x0345u, 0x1F99u), HB_CODEPOINT_ENCODE3 (0x1F2Au, 0x0345u, 0x1F9Au), + HB_CODEPOINT_ENCODE3 (0x1F2Bu, 0x0345u, 0x1F9Bu), HB_CODEPOINT_ENCODE3 (0x1F2Cu, 0x0345u, 0x1F9Cu), + HB_CODEPOINT_ENCODE3 (0x1F2Du, 0x0345u, 0x1F9Du), HB_CODEPOINT_ENCODE3 (0x1F2Eu, 0x0345u, 0x1F9Eu), + HB_CODEPOINT_ENCODE3 (0x1F2Fu, 0x0345u, 0x1F9Fu), HB_CODEPOINT_ENCODE3 (0x1F30u, 0x0300u, 0x1F32u), + HB_CODEPOINT_ENCODE3 (0x1F30u, 0x0301u, 0x1F34u), HB_CODEPOINT_ENCODE3 (0x1F30u, 0x0342u, 0x1F36u), + HB_CODEPOINT_ENCODE3 (0x1F31u, 0x0300u, 0x1F33u), HB_CODEPOINT_ENCODE3 (0x1F31u, 0x0301u, 0x1F35u), + HB_CODEPOINT_ENCODE3 (0x1F31u, 0x0342u, 0x1F37u), HB_CODEPOINT_ENCODE3 (0x1F38u, 0x0300u, 0x1F3Au), + HB_CODEPOINT_ENCODE3 (0x1F38u, 0x0301u, 0x1F3Cu), HB_CODEPOINT_ENCODE3 (0x1F38u, 0x0342u, 0x1F3Eu), + HB_CODEPOINT_ENCODE3 (0x1F39u, 0x0300u, 0x1F3Bu), HB_CODEPOINT_ENCODE3 (0x1F39u, 0x0301u, 0x1F3Du), + HB_CODEPOINT_ENCODE3 (0x1F39u, 0x0342u, 0x1F3Fu), HB_CODEPOINT_ENCODE3 (0x1F40u, 0x0300u, 0x1F42u), + HB_CODEPOINT_ENCODE3 (0x1F40u, 0x0301u, 0x1F44u), HB_CODEPOINT_ENCODE3 (0x1F41u, 0x0300u, 0x1F43u), + HB_CODEPOINT_ENCODE3 (0x1F41u, 0x0301u, 0x1F45u), HB_CODEPOINT_ENCODE3 (0x1F48u, 0x0300u, 0x1F4Au), + HB_CODEPOINT_ENCODE3 (0x1F48u, 0x0301u, 0x1F4Cu), HB_CODEPOINT_ENCODE3 (0x1F49u, 0x0300u, 0x1F4Bu), + HB_CODEPOINT_ENCODE3 (0x1F49u, 0x0301u, 0x1F4Du), HB_CODEPOINT_ENCODE3 (0x1F50u, 0x0300u, 0x1F52u), + HB_CODEPOINT_ENCODE3 (0x1F50u, 0x0301u, 0x1F54u), HB_CODEPOINT_ENCODE3 (0x1F50u, 0x0342u, 0x1F56u), + HB_CODEPOINT_ENCODE3 (0x1F51u, 0x0300u, 0x1F53u), HB_CODEPOINT_ENCODE3 (0x1F51u, 0x0301u, 0x1F55u), + HB_CODEPOINT_ENCODE3 (0x1F51u, 0x0342u, 0x1F57u), HB_CODEPOINT_ENCODE3 (0x1F59u, 0x0300u, 0x1F5Bu), + HB_CODEPOINT_ENCODE3 (0x1F59u, 0x0301u, 0x1F5Du), HB_CODEPOINT_ENCODE3 (0x1F59u, 0x0342u, 0x1F5Fu), + HB_CODEPOINT_ENCODE3 (0x1F60u, 0x0300u, 0x1F62u), HB_CODEPOINT_ENCODE3 (0x1F60u, 0x0301u, 0x1F64u), + HB_CODEPOINT_ENCODE3 (0x1F60u, 0x0342u, 0x1F66u), HB_CODEPOINT_ENCODE3 (0x1F60u, 0x0345u, 0x1FA0u), + HB_CODEPOINT_ENCODE3 (0x1F61u, 0x0300u, 0x1F63u), HB_CODEPOINT_ENCODE3 (0x1F61u, 0x0301u, 0x1F65u), + HB_CODEPOINT_ENCODE3 (0x1F61u, 0x0342u, 0x1F67u), HB_CODEPOINT_ENCODE3 (0x1F61u, 0x0345u, 0x1FA1u), + HB_CODEPOINT_ENCODE3 (0x1F62u, 0x0345u, 0x1FA2u), HB_CODEPOINT_ENCODE3 (0x1F63u, 0x0345u, 0x1FA3u), + HB_CODEPOINT_ENCODE3 (0x1F64u, 0x0345u, 0x1FA4u), HB_CODEPOINT_ENCODE3 (0x1F65u, 0x0345u, 0x1FA5u), + HB_CODEPOINT_ENCODE3 (0x1F66u, 0x0345u, 0x1FA6u), HB_CODEPOINT_ENCODE3 (0x1F67u, 0x0345u, 0x1FA7u), + HB_CODEPOINT_ENCODE3 (0x1F68u, 0x0300u, 0x1F6Au), HB_CODEPOINT_ENCODE3 (0x1F68u, 0x0301u, 0x1F6Cu), + HB_CODEPOINT_ENCODE3 (0x1F68u, 0x0342u, 0x1F6Eu), HB_CODEPOINT_ENCODE3 (0x1F68u, 0x0345u, 0x1FA8u), + HB_CODEPOINT_ENCODE3 (0x1F69u, 0x0300u, 0x1F6Bu), HB_CODEPOINT_ENCODE3 (0x1F69u, 0x0301u, 0x1F6Du), + HB_CODEPOINT_ENCODE3 (0x1F69u, 0x0342u, 0x1F6Fu), HB_CODEPOINT_ENCODE3 (0x1F69u, 0x0345u, 0x1FA9u), + HB_CODEPOINT_ENCODE3 (0x1F6Au, 0x0345u, 0x1FAAu), HB_CODEPOINT_ENCODE3 (0x1F6Bu, 0x0345u, 0x1FABu), + HB_CODEPOINT_ENCODE3 (0x1F6Cu, 0x0345u, 0x1FACu), HB_CODEPOINT_ENCODE3 (0x1F6Du, 0x0345u, 0x1FADu), + HB_CODEPOINT_ENCODE3 (0x1F6Eu, 0x0345u, 0x1FAEu), HB_CODEPOINT_ENCODE3 (0x1F6Fu, 0x0345u, 0x1FAFu), + HB_CODEPOINT_ENCODE3 (0x1F70u, 0x0345u, 0x1FB2u), HB_CODEPOINT_ENCODE3 (0x1F74u, 0x0345u, 0x1FC2u), + HB_CODEPOINT_ENCODE3 (0x1F7Cu, 0x0345u, 0x1FF2u), HB_CODEPOINT_ENCODE3 (0x1FB6u, 0x0345u, 0x1FB7u), + HB_CODEPOINT_ENCODE3 (0x1FBFu, 0x0300u, 0x1FCDu), HB_CODEPOINT_ENCODE3 (0x1FBFu, 0x0301u, 0x1FCEu), + HB_CODEPOINT_ENCODE3 (0x1FBFu, 0x0342u, 0x1FCFu), HB_CODEPOINT_ENCODE3 (0x1FC6u, 0x0345u, 0x1FC7u), + HB_CODEPOINT_ENCODE3 (0x1FF6u, 0x0345u, 0x1FF7u), HB_CODEPOINT_ENCODE3 (0x1FFEu, 0x0300u, 0x1FDDu), + HB_CODEPOINT_ENCODE3 (0x1FFEu, 0x0301u, 0x1FDEu), HB_CODEPOINT_ENCODE3 (0x1FFEu, 0x0342u, 0x1FDFu), + HB_CODEPOINT_ENCODE3 (0x2190u, 0x0338u, 0x219Au), HB_CODEPOINT_ENCODE3 (0x2192u, 0x0338u, 0x219Bu), + HB_CODEPOINT_ENCODE3 (0x2194u, 0x0338u, 0x21AEu), HB_CODEPOINT_ENCODE3 (0x21D0u, 0x0338u, 0x21CDu), + HB_CODEPOINT_ENCODE3 (0x21D2u, 0x0338u, 0x21CFu), HB_CODEPOINT_ENCODE3 (0x21D4u, 0x0338u, 0x21CEu), + HB_CODEPOINT_ENCODE3 (0x2203u, 0x0338u, 0x2204u), HB_CODEPOINT_ENCODE3 (0x2208u, 0x0338u, 0x2209u), + HB_CODEPOINT_ENCODE3 (0x220Bu, 0x0338u, 0x220Cu), HB_CODEPOINT_ENCODE3 (0x2223u, 0x0338u, 0x2224u), + HB_CODEPOINT_ENCODE3 (0x2225u, 0x0338u, 0x2226u), HB_CODEPOINT_ENCODE3 (0x223Cu, 0x0338u, 0x2241u), + HB_CODEPOINT_ENCODE3 (0x2243u, 0x0338u, 0x2244u), HB_CODEPOINT_ENCODE3 (0x2245u, 0x0338u, 0x2247u), + HB_CODEPOINT_ENCODE3 (0x2248u, 0x0338u, 0x2249u), HB_CODEPOINT_ENCODE3 (0x224Du, 0x0338u, 0x226Du), + HB_CODEPOINT_ENCODE3 (0x2261u, 0x0338u, 0x2262u), HB_CODEPOINT_ENCODE3 (0x2264u, 0x0338u, 0x2270u), + HB_CODEPOINT_ENCODE3 (0x2265u, 0x0338u, 0x2271u), HB_CODEPOINT_ENCODE3 (0x2272u, 0x0338u, 0x2274u), + HB_CODEPOINT_ENCODE3 (0x2273u, 0x0338u, 0x2275u), HB_CODEPOINT_ENCODE3 (0x2276u, 0x0338u, 0x2278u), + HB_CODEPOINT_ENCODE3 (0x2277u, 0x0338u, 0x2279u), HB_CODEPOINT_ENCODE3 (0x227Au, 0x0338u, 0x2280u), + HB_CODEPOINT_ENCODE3 (0x227Bu, 0x0338u, 0x2281u), HB_CODEPOINT_ENCODE3 (0x227Cu, 0x0338u, 0x22E0u), + HB_CODEPOINT_ENCODE3 (0x227Du, 0x0338u, 0x22E1u), HB_CODEPOINT_ENCODE3 (0x2282u, 0x0338u, 0x2284u), + HB_CODEPOINT_ENCODE3 (0x2283u, 0x0338u, 0x2285u), HB_CODEPOINT_ENCODE3 (0x2286u, 0x0338u, 0x2288u), + HB_CODEPOINT_ENCODE3 (0x2287u, 0x0338u, 0x2289u), HB_CODEPOINT_ENCODE3 (0x2291u, 0x0338u, 0x22E2u), + HB_CODEPOINT_ENCODE3 (0x2292u, 0x0338u, 0x22E3u), HB_CODEPOINT_ENCODE3 (0x22A2u, 0x0338u, 0x22ACu), + HB_CODEPOINT_ENCODE3 (0x22A8u, 0x0338u, 0x22ADu), HB_CODEPOINT_ENCODE3 (0x22A9u, 0x0338u, 0x22AEu), + HB_CODEPOINT_ENCODE3 (0x22ABu, 0x0338u, 0x22AFu), HB_CODEPOINT_ENCODE3 (0x22B2u, 0x0338u, 0x22EAu), + HB_CODEPOINT_ENCODE3 (0x22B3u, 0x0338u, 0x22EBu), HB_CODEPOINT_ENCODE3 (0x22B4u, 0x0338u, 0x22ECu), + HB_CODEPOINT_ENCODE3 (0x22B5u, 0x0338u, 0x22EDu), HB_CODEPOINT_ENCODE3 (0x2ADDu, 0x0338u, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x3046u, 0x3099u, 0x3094u), HB_CODEPOINT_ENCODE3 (0x304Bu, 0x3099u, 0x304Cu), + HB_CODEPOINT_ENCODE3 (0x304Du, 0x3099u, 0x304Eu), HB_CODEPOINT_ENCODE3 (0x304Fu, 0x3099u, 0x3050u), + HB_CODEPOINT_ENCODE3 (0x3051u, 0x3099u, 0x3052u), HB_CODEPOINT_ENCODE3 (0x3053u, 0x3099u, 0x3054u), + HB_CODEPOINT_ENCODE3 (0x3055u, 0x3099u, 0x3056u), HB_CODEPOINT_ENCODE3 (0x3057u, 0x3099u, 0x3058u), + HB_CODEPOINT_ENCODE3 (0x3059u, 0x3099u, 0x305Au), HB_CODEPOINT_ENCODE3 (0x305Bu, 0x3099u, 0x305Cu), + HB_CODEPOINT_ENCODE3 (0x305Du, 0x3099u, 0x305Eu), HB_CODEPOINT_ENCODE3 (0x305Fu, 0x3099u, 0x3060u), + HB_CODEPOINT_ENCODE3 (0x3061u, 0x3099u, 0x3062u), HB_CODEPOINT_ENCODE3 (0x3064u, 0x3099u, 0x3065u), + HB_CODEPOINT_ENCODE3 (0x3066u, 0x3099u, 0x3067u), HB_CODEPOINT_ENCODE3 (0x3068u, 0x3099u, 0x3069u), + HB_CODEPOINT_ENCODE3 (0x306Fu, 0x3099u, 0x3070u), HB_CODEPOINT_ENCODE3 (0x306Fu, 0x309Au, 0x3071u), + HB_CODEPOINT_ENCODE3 (0x3072u, 0x3099u, 0x3073u), HB_CODEPOINT_ENCODE3 (0x3072u, 0x309Au, 0x3074u), + HB_CODEPOINT_ENCODE3 (0x3075u, 0x3099u, 0x3076u), HB_CODEPOINT_ENCODE3 (0x3075u, 0x309Au, 0x3077u), + HB_CODEPOINT_ENCODE3 (0x3078u, 0x3099u, 0x3079u), HB_CODEPOINT_ENCODE3 (0x3078u, 0x309Au, 0x307Au), + HB_CODEPOINT_ENCODE3 (0x307Bu, 0x3099u, 0x307Cu), HB_CODEPOINT_ENCODE3 (0x307Bu, 0x309Au, 0x307Du), + HB_CODEPOINT_ENCODE3 (0x309Du, 0x3099u, 0x309Eu), HB_CODEPOINT_ENCODE3 (0x30A6u, 0x3099u, 0x30F4u), + HB_CODEPOINT_ENCODE3 (0x30ABu, 0x3099u, 0x30ACu), HB_CODEPOINT_ENCODE3 (0x30ADu, 0x3099u, 0x30AEu), + HB_CODEPOINT_ENCODE3 (0x30AFu, 0x3099u, 0x30B0u), HB_CODEPOINT_ENCODE3 (0x30B1u, 0x3099u, 0x30B2u), + HB_CODEPOINT_ENCODE3 (0x30B3u, 0x3099u, 0x30B4u), HB_CODEPOINT_ENCODE3 (0x30B5u, 0x3099u, 0x30B6u), + HB_CODEPOINT_ENCODE3 (0x30B7u, 0x3099u, 0x30B8u), HB_CODEPOINT_ENCODE3 (0x30B9u, 0x3099u, 0x30BAu), + HB_CODEPOINT_ENCODE3 (0x30BBu, 0x3099u, 0x30BCu), HB_CODEPOINT_ENCODE3 (0x30BDu, 0x3099u, 0x30BEu), + HB_CODEPOINT_ENCODE3 (0x30BFu, 0x3099u, 0x30C0u), HB_CODEPOINT_ENCODE3 (0x30C1u, 0x3099u, 0x30C2u), + HB_CODEPOINT_ENCODE3 (0x30C4u, 0x3099u, 0x30C5u), HB_CODEPOINT_ENCODE3 (0x30C6u, 0x3099u, 0x30C7u), + HB_CODEPOINT_ENCODE3 (0x30C8u, 0x3099u, 0x30C9u), HB_CODEPOINT_ENCODE3 (0x30CFu, 0x3099u, 0x30D0u), + HB_CODEPOINT_ENCODE3 (0x30CFu, 0x309Au, 0x30D1u), HB_CODEPOINT_ENCODE3 (0x30D2u, 0x3099u, 0x30D3u), + HB_CODEPOINT_ENCODE3 (0x30D2u, 0x309Au, 0x30D4u), HB_CODEPOINT_ENCODE3 (0x30D5u, 0x3099u, 0x30D6u), + HB_CODEPOINT_ENCODE3 (0x30D5u, 0x309Au, 0x30D7u), HB_CODEPOINT_ENCODE3 (0x30D8u, 0x3099u, 0x30D9u), + HB_CODEPOINT_ENCODE3 (0x30D8u, 0x309Au, 0x30DAu), HB_CODEPOINT_ENCODE3 (0x30DBu, 0x3099u, 0x30DCu), + HB_CODEPOINT_ENCODE3 (0x30DBu, 0x309Au, 0x30DDu), HB_CODEPOINT_ENCODE3 (0x30EFu, 0x3099u, 0x30F7u), + HB_CODEPOINT_ENCODE3 (0x30F0u, 0x3099u, 0x30F8u), HB_CODEPOINT_ENCODE3 (0x30F1u, 0x3099u, 0x30F9u), + HB_CODEPOINT_ENCODE3 (0x30F2u, 0x3099u, 0x30FAu), HB_CODEPOINT_ENCODE3 (0x30FDu, 0x3099u, 0x30FEu), + HB_CODEPOINT_ENCODE3 (0xFB49u, 0x05C1u, 0x0000u), HB_CODEPOINT_ENCODE3 (0xFB49u, 0x05C2u, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x11099u, 0x110BAu, 0x1109Au),HB_CODEPOINT_ENCODE3 (0x1109Bu, 0x110BAu, 0x1109Cu), + HB_CODEPOINT_ENCODE3 (0x110A5u, 0x110BAu, 0x110ABu),HB_CODEPOINT_ENCODE3 (0x11131u, 0x11127u, 0x1112Eu), + HB_CODEPOINT_ENCODE3 (0x11132u, 0x11127u, 0x1112Fu),HB_CODEPOINT_ENCODE3 (0x11347u, 0x1133Eu, 0x1134Bu), + HB_CODEPOINT_ENCODE3 (0x11347u, 0x11357u, 0x1134Cu),HB_CODEPOINT_ENCODE3 (0x114B9u, 0x114B0u, 0x114BCu), + HB_CODEPOINT_ENCODE3 (0x114B9u, 0x114BAu, 0x114BBu),HB_CODEPOINT_ENCODE3 (0x114B9u, 0x114BDu, 0x114BEu), + HB_CODEPOINT_ENCODE3 (0x115B8u, 0x115AFu, 0x115BAu),HB_CODEPOINT_ENCODE3 (0x115B9u, 0x115AFu, 0x115BBu), + HB_CODEPOINT_ENCODE3 (0x11935u, 0x11930u, 0x11938u), HB_CODEPOINT_ENCODE3 (0x1D157u, 0x1D165u, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x1D158u, 0x1D165u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x1D15Fu, 0x1D16Eu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x1D15Fu, 0x1D16Fu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x1D15Fu, 0x1D170u, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x1D15Fu, 0x1D171u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x1D15Fu, 0x1D172u, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x1D1B9u, 0x1D165u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x1D1BAu, 0x1D165u, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x1D1BBu, 0x1D16Eu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x1D1BBu, 0x1D16Fu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x1D1BCu, 0x1D16Eu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x1D1BCu, 0x1D16Fu, 0x0000u), +}; + +#ifndef HB_OPTIMIZE_SIZE + +static const uint8_t +_hb_ucd_u8[18260] = +{ + 0, 1, 2, 3, 4, 5, 6, 7, 7, 8, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 9, 10, 7, 7, 7, 7, 11, 12, 13, 13, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 22, 22, 22, 22, 24, 7, 7, + 25, 26, 22, 22, 22, 27, 28, 29, 22, 30, 31, 32, 33, 34, 35, 36, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 37, 7, 38, 39, 7, 40, 7, 7, 7, 41, 22, 42, + 7, 7, 43, 7, 44, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 45, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 46, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 47, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 34, 35, 36, 37, 38, 39, 34, 34, 34, 40, 41, 42, 43, + 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, + 60, 61, 62, 63, 64, 64, 65, 66, 67, 68, 69, 70, 71, 69, 72, 73, + 69, 69, 64, 74, 64, 64, 75, 76, 77, 78, 79, 80, 81, 82, 69, 83, + 84, 85, 86, 87, 88, 89, 69, 69, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 90, 34, 34, 34, 34, + 91, 34, 34, 34, 34, 34, 34, 34, 34, 92, 34, 34, 93, 94, 95, 96, + 97, 98, 99,100,101,102,103,104, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,105, + 106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106, + 107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107, + 107,107, 34, 34,108,109,110,111, 34, 34,112,113,114,115,116,117, + 118,119,120,121,122,123,124,125,126,127,128,129, 34, 34,130,131, + 132,133,134,135,136,137,138,139,140,141,142,122,143,144,145,146, + 147,148,149,150,151,152,153,122,154,155,122,156,157,158,159,122, + 160,161,162,163,164,165,166,122,167,168,169,170,122,171,172,173, + 34, 34, 34, 34, 34, 34, 34,174,175, 34,176,122,122,122,122,122, + 122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,177, + 34, 34, 34, 34, 34, 34, 34, 34,178,122,122,122,122,122,122,122, + 122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122, + 122,122,122,122,122,122,122,122, 34, 34, 34, 34,179,122,122,122, + 34, 34, 34, 34,180,181,182,183,122,122,122,122,184,185,186,187, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,188, + 34, 34, 34, 34, 34, 34, 34, 34, 34,189,190,122,122,122,122,122, + 122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,191, + 34, 34,192, 34, 34,193,122,122,122,122,122,122,122,122,122,122, + 122,122,122,122,122,122,122,122,194,195,122,122,122,122,122,122, + 122,122,122,122,122,122,122,122,122,122,122,122,122,122,196,197, + 69,198,199,200,201,202,203,122,204,205,206,207,208,209,210,211, + 69, 69, 69, 69,212,213,122,122,122,122,122,122,122,122,214,122, + 215,216,217,122,122,218,122,122,122,219,122,122,122,122,122,220, + 34,221,222,122,122,122,122,122,223,224,225,122,226,227,122,122, + 228,229,230,231,232,122, 69,233, 69, 69, 69, 69, 69,234,235,236, + 237,238, 69, 69,239,240, 69,241,122,122,122,122,122,122,122,122, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,242, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,243, 34, + 244, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,245, 34, 34, + 34, 34, 34, 34, 34, 34, 34,246,122,122,122,122,122,122,122,122, + 34, 34, 34, 34,247,122,122,122,122,122,122,122,122,122,122,122, + 34, 34, 34, 34, 34, 34,248, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34,249,122,122,122,122,122,122,122,122, + 250,122,251,252,122,122,122,122,122,122,122,122,122,122,122,122, + 107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,253, + 107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,254, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 2, 4, 5, 6, 2, + 7, 7, 7, 7, 7, 2, 8, 9, 10, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 12, 13, 14, 15, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 17, 18, 19, 1, 20, 20, 21, 22, 23, 24, 25, + 26, 27, 15, 2, 28, 29, 27, 30, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 31, 11, 11, 11, 32, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 33, 16, 16, 16, 16, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 34, 34, 34, 34, 34, 34, 34, 34, 16, 32, 32, 32, + 32, 32, 32, 32, 11, 34, 34, 16, 34, 32, 32, 11, 34, 11, 16, 11, + 11, 34, 32, 11, 32, 16, 11, 34, 32, 32, 32, 11, 34, 16, 32, 11, + 34, 11, 34, 34, 32, 35, 32, 16, 36, 36, 37, 34, 38, 37, 34, 34, + 34, 34, 34, 34, 34, 34, 16, 32, 34, 38, 32, 11, 32, 32, 32, 32, + 32, 32, 16, 16, 16, 11, 34, 32, 34, 34, 11, 32, 32, 32, 32, 32, + 16, 16, 39, 16, 16, 16, 16, 16, 40, 40, 40, 40, 40, 40, 40, 40, + 40, 41, 41, 40, 40, 40, 40, 40, 40, 41, 41, 41, 41, 41, 41, 41, + 40, 40, 42, 41, 41, 41, 42, 42, 41, 41, 41, 41, 41, 41, 41, 41, + 43, 43, 43, 43, 43, 43, 43, 43, 32, 32, 42, 32, 44, 45, 16, 10, + 44, 44, 41, 46, 11, 47, 47, 11, 34, 11, 11, 11, 11, 11, 11, 11, + 11, 48, 11, 11, 11, 11, 16, 16, 16, 16, 16, 16, 16, 16, 16, 34, + 16, 11, 32, 16, 32, 32, 32, 32, 16, 16, 32, 49, 34, 32, 34, 11, + 32, 50, 43, 43, 51, 32, 32, 32, 11, 34, 34, 34, 34, 34, 34, 16, + 48, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 47, 52, 2, 2, 2, + 16, 16, 16, 16, 53, 54, 55, 56, 57, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 58, 59, 60, 43, 59, 44, 44, 44, 44, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 61, 44, 62, + 36, 63, 64, 44, 44, 44, 44, 44, 65, 65, 65, 8, 9, 66, 2, 67, + 43, 43, 43, 43, 43, 60, 68, 2, 69, 36, 36, 36, 36, 70, 43, 43, + 7, 7, 7, 7, 7, 2, 2, 36, 71, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 72, 43, 43, 43, 73, 50, 43, 43, 74, 75, 76, 43, 43, 36, + 7, 7, 7, 7, 7, 36, 77, 78, 2, 2, 2, 2, 2, 2, 2, 79, + 70, 36, 36, 36, 36, 36, 36, 36, 43, 43, 43, 43, 43, 80, 62, 36, + 36, 36, 36, 43, 43, 43, 43, 43, 71, 44, 44, 44, 44, 44, 44, 44, + 7, 7, 7, 7, 7, 36, 36, 36, 36, 36, 36, 36, 36, 70, 43, 43, + 43, 43, 40, 21, 2, 81, 57, 20, 36, 36, 36, 43, 43, 75, 43, 43, + 43, 43, 75, 43, 75, 43, 43, 44, 2, 2, 2, 2, 2, 2, 2, 64, + 36, 36, 36, 36, 70, 43, 44, 64, 36, 36, 36, 36, 36, 61, 44, 44, + 36, 36, 36, 36, 82, 36, 36, 61, 65, 44, 44, 44, 43, 43, 43, 43, + 36, 36, 36, 36, 83, 43, 43, 43, 43, 84, 43, 43, 43, 43, 43, 43, + 43, 85, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 85, 71, 86, + 87, 43, 43, 43, 85, 86, 87, 86, 70, 43, 43, 43, 36, 36, 36, 36, + 36, 43, 2, 7, 7, 7, 7, 7, 88, 36, 36, 36, 36, 36, 36, 36, + 70, 86, 62, 36, 36, 36, 61, 62, 61, 62, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 61, 36, 36, 36, 61, 61, 44, 36, 36, 44, 71, 86, + 87, 43, 80, 89, 90, 89, 87, 61, 44, 44, 44, 89, 44, 44, 36, 62, + 36, 43, 44, 7, 7, 7, 7, 7, 36, 20, 27, 27, 27, 56, 63, 80, + 57, 85, 62, 36, 36, 61, 44, 62, 61, 36, 62, 61, 36, 44, 80, 86, + 87, 80, 44, 57, 80, 57, 43, 44, 57, 44, 44, 44, 62, 36, 61, 61, + 44, 44, 44, 7, 7, 7, 7, 7, 43, 36, 70, 64, 44, 44, 44, 44, + 57, 85, 62, 36, 36, 36, 36, 62, 36, 62, 36, 36, 36, 36, 36, 36, + 61, 36, 62, 36, 36, 44, 71, 86, 87, 43, 43, 57, 85, 89, 87, 44, + 61, 44, 44, 44, 44, 44, 44, 44, 66, 44, 44, 44, 62, 43, 43, 43, + 57, 86, 62, 36, 36, 36, 61, 62, 61, 36, 62, 36, 36, 44, 71, 87, + 87, 43, 80, 89, 90, 89, 87, 44, 44, 44, 57, 85, 44, 44, 36, 62, + 78, 27, 27, 27, 44, 44, 44, 44, 44, 71, 62, 36, 36, 61, 44, 36, + 61, 36, 36, 44, 62, 61, 61, 36, 44, 62, 61, 44, 36, 61, 44, 36, + 36, 36, 36, 36, 36, 44, 44, 86, 85, 90, 44, 86, 90, 86, 87, 44, + 61, 44, 44, 89, 44, 44, 44, 44, 27, 91, 67, 67, 56, 92, 44, 44, + 85, 86, 71, 36, 36, 36, 61, 36, 61, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 44, 71, 43, 85, 86, 90, 43, 80, 43, 43, 44, + 44, 44, 57, 80, 36, 61, 62, 44, 44, 44, 44, 93, 27, 27, 27, 91, + 70, 86, 72, 36, 36, 36, 61, 36, 36, 36, 62, 36, 36, 44, 71, 87, + 86, 86, 90, 85, 90, 86, 43, 44, 44, 44, 89, 90, 44, 44, 62, 61, + 62, 94, 44, 44, 44, 44, 44, 44, 43, 86, 36, 36, 36, 36, 61, 36, + 36, 36, 36, 36, 36, 70, 71, 86, 87, 43, 80, 86, 90, 86, 87, 77, + 44, 44, 36, 94, 27, 27, 27, 95, 27, 27, 27, 27, 91, 36, 36, 36, + 57, 86, 62, 36, 36, 36, 36, 36, 36, 36, 36, 61, 44, 36, 36, 36, + 36, 62, 36, 36, 36, 36, 62, 44, 36, 36, 36, 61, 44, 80, 44, 89, + 86, 43, 80, 80, 86, 86, 86, 86, 44, 86, 64, 44, 44, 44, 44, 44, + 62, 36, 36, 36, 36, 36, 36, 36, 70, 36, 43, 43, 43, 80, 44, 96, + 36, 36, 36, 75, 43, 43, 43, 60, 7, 7, 7, 7, 7, 2, 44, 44, + 44, 44, 44, 44, 44, 44, 44, 44, 62, 61, 61, 36, 36, 61, 36, 36, + 36, 36, 62, 62, 36, 36, 36, 36, 70, 36, 43, 43, 43, 43, 71, 44, + 36, 36, 61, 81, 43, 43, 43, 80, 7, 7, 7, 7, 7, 44, 36, 36, + 77, 67, 2, 2, 2, 2, 2, 2, 2, 97, 97, 67, 43, 67, 67, 67, + 7, 7, 7, 7, 7, 27, 27, 27, 27, 27, 50, 50, 50, 4, 4, 86, + 36, 36, 36, 36, 62, 36, 36, 36, 36, 36, 36, 36, 36, 36, 61, 44, + 57, 43, 43, 43, 43, 43, 43, 85, 43, 43, 60, 43, 36, 36, 70, 43, + 43, 43, 43, 43, 57, 43, 43, 43, 43, 43, 43, 43, 43, 43, 80, 67, + 67, 67, 67, 76, 67, 67, 92, 67, 2, 2, 97, 67, 21, 64, 44, 44, + 36, 36, 36, 36, 36, 94, 87, 43, 85, 43, 43, 43, 87, 85, 87, 71, + 7, 7, 7, 7, 7, 2, 2, 2, 36, 36, 36, 86, 43, 36, 36, 43, + 71, 86, 98, 94, 86, 86, 86, 36, 70, 43, 71, 36, 36, 36, 36, 36, + 36, 85, 87, 85, 86, 86, 87, 94, 7, 7, 7, 7, 7, 86, 87, 67, + 11, 11, 11, 48, 44, 44, 48, 44, 16, 16, 16, 16, 16, 53, 45, 16, + 36, 36, 36, 36, 61, 36, 36, 44, 36, 36, 36, 61, 61, 36, 36, 44, + 61, 36, 36, 44, 36, 36, 36, 61, 61, 36, 36, 44, 36, 36, 36, 36, + 36, 36, 36, 61, 36, 36, 36, 36, 36, 36, 36, 36, 36, 61, 57, 43, + 2, 2, 2, 2, 99, 27, 27, 27, 27, 27, 27, 27, 27, 27,100, 44, + 67, 67, 67, 67, 67, 44, 44, 44, 11, 11, 11, 44, 16, 16, 16, 44, + 101, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 77, 72, + 102, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36,103,104, 44, + 36, 36, 36, 36, 36, 63, 2,105,106, 36, 36, 36, 61, 44, 44, 44, + 36, 43, 85, 44, 44, 44, 44, 62, 36, 43,107, 64, 44, 44, 44, 44, + 36, 43, 44, 44, 44, 44, 44, 44, 36, 36, 36, 36, 36, 36, 61, 36, + 61, 43, 44, 44, 44, 44, 44, 44, 36, 36, 43, 87, 43, 43, 43, 86, + 86, 86, 86, 85, 87, 43, 43, 43, 43, 43, 2, 88, 2, 66, 70, 44, + 7, 7, 7, 7, 7, 44, 44, 44, 27, 27, 27, 27, 27, 44, 44, 44, + 2, 2, 2,108, 2, 59, 43, 84, 36, 83, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 61, 44, 44, 44, 36, 36, 70, 71, 36, 36, 36, 36, + 36, 36, 36, 36, 70, 61, 44, 44, 36, 36, 36, 44, 44, 44, 44, 44, + 36, 36, 36, 36, 36, 36, 36, 61, 43, 85, 86, 87, 85, 86, 44, 44, + 86, 85, 86, 86, 87, 43, 44, 44, 92, 44, 2, 7, 7, 7, 7, 7, + 36, 36, 36, 36, 36, 36, 36, 44, 36, 36, 61, 44, 44, 44, 44, 44, + 36, 36, 36, 36, 36, 36, 44, 44, 36, 36, 36, 36, 36, 44, 44, 44, + 7, 7, 7, 7, 7,100, 44, 67, 67, 67, 67, 67, 67, 67, 67, 67, + 36, 36, 36, 70, 85, 87, 44, 2, 36, 36, 94, 85, 43, 43, 43, 80, + 85, 85, 87, 43, 43, 43, 85, 86, 86, 87, 43, 43, 43, 43, 80, 57, + 2, 2, 2, 88, 2, 2, 2, 44, 43, 43, 43, 43, 43, 43, 43,109, + 43, 43, 43, 43, 43, 43, 43, 80, 43, 43, 98, 36, 36, 36, 36, 36, + 36, 36, 85, 43, 43, 85, 85, 86, 86, 85, 98, 36, 36, 36, 61, 44, + 97, 67, 67, 67, 67, 50, 43, 43, 43, 43, 67, 67, 67, 67, 21, 64, + 43, 98, 36, 36, 36, 36, 36, 36, 94, 43, 43, 86, 43, 87, 43, 36, + 36, 36, 36, 85, 43, 86, 87, 87, 43, 86, 44, 44, 44, 44, 2, 2, + 36, 36, 86, 86, 86, 86, 43, 43, 43, 43, 86, 43, 44, 93, 2, 2, + 7, 7, 7, 7, 7, 44, 62, 36, 36, 36, 36, 36, 40, 40, 40, 2, + 16, 16, 16, 16,110, 44, 44, 44, 11, 11, 11, 11, 11, 47, 48, 11, + 2, 2, 2, 2, 44, 44, 44, 44, 43, 60, 43, 43, 43, 43, 43, 43, + 85, 43, 43, 43, 71, 36, 70, 36, 36, 36, 71, 94, 43, 61, 44, 44, + 16, 16, 16, 16, 16, 16, 40, 40, 40, 40, 40, 40, 40, 45, 16, 16, + 16, 16, 16, 16, 45, 16, 16, 16, 16, 16, 16, 16, 16,111, 40, 40, + 32, 32, 32, 16, 16, 16, 16, 32, 16, 16, 16, 16, 11, 11, 11, 11, + 16, 16, 16, 44, 11, 11, 11, 44, 16, 16, 16, 16, 48, 48, 48, 48, + 16, 16, 16, 16, 16, 16, 16, 44, 16, 16, 16, 16,112,112,112,112, + 16, 16,110, 16, 11, 11,113,114, 41, 16,110, 16, 11, 11,113, 41, + 16, 16, 44, 16, 11, 11,115, 41, 16, 16, 16, 16, 11, 11,116, 41, + 44, 16,110, 16, 11, 11,113,117,118,118,118,118,118,119, 65, 65, + 120,120,120, 2,121,122,121,122, 2, 2, 2, 2,123, 65, 65,124, + 2, 2, 2, 2,125,126, 2,127,128, 2,129,130, 2, 2, 2, 2, + 2, 9,128, 2, 2, 2, 2,131, 65, 65,132, 65, 65, 65, 65, 65, + 133, 44, 27, 27, 27, 8,129,134, 27, 27, 27, 27, 27, 8,129,104, + 40, 40, 40, 40, 40, 40, 81, 44, 20, 20, 20, 20, 20, 20, 20, 20, + 135, 44, 44, 44, 44, 44, 44, 44, 43, 43, 43, 43, 43, 43,136, 51, + 109, 51,109, 43, 43, 43, 43, 43, 80, 44, 44, 44, 44, 44, 44, 44, + 67,137, 67,138, 67, 34, 11, 16, 11, 32,138, 67, 49, 11, 11, 67, + 67, 67,137,137,137, 11, 11,139, 11, 11, 35, 36, 39, 67, 16, 11, + 8, 8, 49, 16, 16, 26, 67,140, 27, 27, 27, 27, 27, 27, 27, 27, + 105,105,105,105,105,105,105,105,105,141,142,105,143, 67, 44, 44, + 8, 8,144, 67, 67, 8, 67, 67,144, 26, 67,144, 67, 67, 67,144, + 67, 67, 67, 67, 67, 67, 67, 8, 67,144,144, 67, 67, 67, 67, 67, + 67, 67, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 67, 67, 67, 67, 4, 4, 67, 67, 8, 67, 67, 67,145,146, 67, 67, + 67, 67, 67, 67, 67, 67,144, 67, 67, 67, 67, 67, 67, 26, 8, 8, + 8, 8, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 8, 8, + 8, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 92, 44, 44, 44, 44, + 67, 67, 67, 67, 67, 92, 44, 44, 27, 27, 27, 27, 27, 27, 67, 67, + 67, 67, 67, 67, 67, 27, 27, 27, 67, 67, 67, 26, 67, 67, 67, 67, + 26, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 8, 8, 8, 8, + 67, 67, 67, 67, 67, 67, 67, 26, 67, 67, 67, 67, 4, 4, 4, 4, + 4, 4, 4, 27, 27, 27, 27, 27, 27, 27, 67, 67, 67, 67, 67, 67, + 8, 8,129,147, 8, 8, 8, 8, 8, 8, 8, 4, 4, 4, 4, 4, + 8,129,148,148,148,148,148,148,148,148,148,148,147, 8, 8, 8, + 8, 8, 8, 8, 4, 4, 8, 8, 8, 8, 8, 8, 8, 8, 4, 8, + 8, 8,144, 26, 8, 8,144, 67, 67, 67, 44, 67, 67, 67, 67, 67, + 67, 67, 67, 55, 67, 67, 67, 67, 32, 11, 32, 34, 34, 34, 34, 11, + 32, 32, 34, 16, 16, 16, 40, 11, 32, 32,140, 67, 67,138, 34,149, + 43, 32, 44, 44, 93, 2, 99, 2, 16, 16, 16,150, 44, 44,150, 44, + 36, 36, 36, 36, 44, 44, 44, 52, 64, 44, 44, 44, 44, 44, 44, 57, + 36, 36, 36, 61, 44, 44, 44, 44, 36, 36, 36, 61, 36, 36, 36, 61, + 2,121,121, 2,125,126,121, 2, 2, 2, 2, 6, 2,108,121, 2, + 121, 4, 4, 4, 4, 2, 2, 88, 2, 2, 2, 2, 2,120, 2, 2, + 108,151, 2, 2, 2, 2, 2, 2, 67, 2,152,148,148,148,153, 44, + 67, 67, 67, 67, 67, 55, 67, 67, 67, 67, 44, 44, 44, 44, 44, 44, + 67, 67, 67, 44, 44, 44, 44, 44, 67, 67, 67, 67, 67, 67, 44, 44, + 1, 2,154,155, 4, 4, 4, 4, 4, 67, 4, 4, 4, 4,156,157, + 158,105,105,105,105, 43, 43, 86,159, 40, 40, 67,105,160, 63, 67, + 36, 36, 36, 61, 57,161,162, 69, 36, 36, 36, 36, 36, 63, 40, 69, + 44, 44, 62, 36, 36, 36, 36, 36, 67, 27, 27, 67, 67, 67, 67, 67, + 67, 67, 67, 67, 67, 67, 67, 92, 27, 27, 27, 27, 27, 67, 67, 67, + 67, 67, 67, 67, 27, 27, 27, 27,163, 27, 27, 27, 27, 27, 27, 27, + 36, 36, 83, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36,164, 2, + 7, 7, 7, 7, 7, 36, 44, 44, 32, 32, 32, 32, 32, 32, 32, 70, + 51,165, 43, 43, 43, 43, 43, 88, 32, 32, 32, 32, 32, 32, 40, 43, + 36, 36, 36,105,105,105,105,105, 43, 2, 2, 2, 44, 44, 44, 44, + 41, 41, 41,162, 40, 40, 40, 40, 41, 32, 32, 32, 32, 32, 32, 32, + 16, 32, 32, 32, 32, 32, 32, 32, 45, 16, 16, 16, 34, 34, 34, 32, + 32, 32, 32, 32, 42,166, 34, 35, 32, 32, 16, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 11, 11, 32, 11, 11, 32, 32, 32, 32, 32, 32, + 32, 32, 11, 11, 34,110, 44, 44, 32,150,150, 32, 32, 44, 44, 44, + 44, 40,167, 35, 40, 35, 36, 36, 36, 71, 36, 71, 36, 70, 36, 36, + 36, 94, 87, 85, 67, 67, 80, 44, 27, 27, 27, 67,168, 44, 44, 44, + 36, 36, 2, 2, 44, 44, 44, 44, 86, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 86, 86, 86, 86, 86, 86, 86, 86, 43, 44, 44, 44, 44, 2, + 43, 36, 36, 36, 2, 72, 72, 70, 36, 36, 36, 43, 43, 43, 43, 2, + 36, 36, 36, 70, 43, 43, 43, 43, 43, 86, 44, 44, 44, 44, 44, 93, + 36, 70, 86, 43, 43, 86, 43, 86,107, 2, 2, 2, 2, 2, 2, 52, + 7, 7, 7, 7, 7, 44, 44, 2, 36, 36, 70, 69, 36, 36, 36, 36, + 7, 7, 7, 7, 7, 36, 36, 61, 36, 36, 36, 36, 70, 43, 43, 85, + 87, 85, 87, 80, 44, 44, 44, 44, 36, 70, 36, 36, 36, 36, 85, 44, + 7, 7, 7, 7, 7, 44, 2, 2, 69, 36, 36, 77, 67, 94, 85, 36, + 71, 43, 71, 70, 71, 36, 36, 43, 70, 61, 44, 44, 44, 44, 44, 44, + 44, 44, 44, 44, 44, 62, 83, 2, 36, 36, 36, 36, 36, 94, 43, 86, + 2, 83,169, 80, 44, 44, 44, 44, 62, 36, 36, 61, 62, 36, 36, 61, + 62, 36, 36, 61, 44, 44, 44, 44, 16, 16, 16, 16, 16,114, 40, 40, + 16, 16, 16, 16,111, 41, 44, 44, 36, 94, 87, 86, 85,107, 87, 44, + 36, 36, 44, 44, 44, 44, 44, 44, 36, 36, 36, 61, 44, 62, 36, 36, + 170,170,170,170,170,170,170,170,171,171,171,171,171,171,171,171, + 16, 16, 16,110, 44, 44, 44, 44, 44,150, 16, 16, 44, 44, 62, 71, + 36, 36, 36, 36,172, 36, 36, 36, 36, 36, 36, 61, 36, 36, 61, 61, + 36, 62, 61, 36, 36, 36, 36, 36, 36, 41, 41, 41, 41, 41, 41, 41, + 41,117, 44, 44, 44, 44, 44, 44, 44, 62, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36,148, 44, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 44, 44, 44, 55, 36, 36, 36, 36, 36, 36,168, 67, + 2, 2, 2,152,130, 44, 44, 44, 6,173,174,148,148,148,148,148, + 148,148,130,152,130, 2,127,175, 2, 64, 2, 2,156,148,148,130, + 2,176, 8,177, 66, 2, 44, 44, 36, 36, 61, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 61, 79, 93, 2, 3, 2, 4, 5, 6, 2, + 16, 16, 16, 16, 16, 17, 18,129,130, 4, 2, 36, 36, 36, 36, 36, + 69, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 40, + 44, 36, 36, 36, 44, 36, 36, 36, 44, 36, 36, 36, 44, 36, 61, 44, + 20,178, 56,135, 26, 8,144, 92, 44, 44, 44, 44, 79, 65, 67, 44, + 36, 36, 36, 36, 36, 36, 62, 36, 36, 36, 36, 36, 36, 61, 36, 62, + 2, 64, 44,179, 27, 27, 27, 27, 27, 27, 44, 55, 67, 67, 67, 67, + 105,105,143, 27, 91, 67, 67, 67, 67, 67, 67, 67, 67, 27, 67, 92, + 67, 67, 67, 67, 67, 67, 92, 44, 92, 44, 44, 44, 44, 44, 44, 44, + 67, 67, 67, 67, 67, 67, 50, 44,180, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 44, 44, 27, 27, 44, 44, 44, 44, 62, 36, + 155, 36, 36, 36, 36,181, 44, 44, 36, 36, 36, 43, 43, 80, 44, 44, + 36, 36, 36, 36, 36, 36, 36, 93, 36, 36, 44, 44, 36, 36, 36, 36, + 182,105,105, 44, 44, 44, 44, 44, 11, 11, 11, 11, 16, 16, 16, 16, + 11, 11, 44, 44, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 44, 44, + 36, 36, 36, 36, 44, 44, 44, 44, 36, 36, 44, 44, 44, 44, 44, 93, + 11, 11, 11, 11, 11, 47, 11, 11, 11, 47, 11,150, 16, 16, 16, 16, + 16,150, 16, 16, 16, 16, 16, 16, 16,150, 16, 16, 16,150,110, 44, + 40, 40, 40, 52, 40, 40, 40, 40, 81, 40, 40, 40, 40, 81, 44, 44, + 36, 36, 36, 44, 61, 36, 36, 36, 36, 36, 36, 62, 61, 44, 61, 62, + 36, 36, 36, 93, 27, 27, 27, 27, 36, 36, 36, 77,163, 27, 27, 27, + 44, 44, 44,179, 27, 27, 27, 27, 36, 61, 36, 44, 44,179, 27, 27, + 36, 36, 36, 27, 27, 27, 44, 93, 36, 36, 36, 36, 36, 44, 44, 93, + 36, 36, 36, 36, 44, 44, 27, 36, 44, 27, 27, 27, 27, 27, 27, 27, + 70, 43, 57, 80, 44, 44, 43, 43, 36, 36, 62, 36, 62, 36, 36, 36, + 36, 36, 36, 44, 43, 80, 44, 57, 27, 27, 27, 27,100, 44, 44, 44, + 2, 2, 2, 2, 64, 44, 44, 44, 36, 36, 36, 36, 36, 36,183, 30, + 36, 36, 36, 36, 36, 36,183, 27, 36, 36, 36, 36, 78, 36, 36, 36, + 36, 36, 70, 80, 44,179, 27, 27, 2, 2, 2, 64, 44, 44, 44, 44, + 36, 36, 36, 44, 93, 2, 2, 2, 36, 36, 36, 44, 27, 27, 27, 27, + 36, 61, 44, 44, 27, 27, 27, 27, 36, 44, 44, 44, 93, 2, 64, 44, + 44, 44, 44, 44,179, 27, 27, 27, 11, 47, 44, 44, 44, 44, 44, 44, + 16,110, 44, 44, 44, 27, 27, 27, 36, 36, 43, 43, 44, 44, 44, 44, + 27, 27, 27, 27, 27, 27, 27,100, 36, 36, 36, 36, 36, 57,184, 44, + 36, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 57, 43, + 27, 27, 27, 95, 44, 44, 44, 44,180, 27, 30, 2, 2, 44, 44, 44, + 36, 43, 43, 2, 2, 44, 44, 44, 36, 36,183, 27, 27, 27, 44, 44, + 87, 98, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 43, 43, 43, 43, + 43, 43, 43, 60, 2, 2, 2, 44, 27, 27, 27, 7, 7, 7, 7, 7, + 71, 70, 71, 44, 44, 44, 44, 57, 86, 87, 43, 85, 87, 60,185, 2, + 2, 80, 44, 44, 44, 44, 79, 44, 43, 71, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 70, 43, 43, 87, 43, 43, 43, 80, 7, 7, 7, 7, 7, + 2, 2, 94, 98, 44, 44, 44, 44, 36, 70, 2, 61, 44, 44, 44, 44, + 36, 94, 86, 43, 43, 43, 43, 85, 98, 36, 63, 2, 59, 43, 60, 87, + 7, 7, 7, 7, 7, 63, 63, 2,179, 27, 27, 27, 27, 27, 27, 27, + 27, 27,100, 44, 44, 44, 44, 44, 36, 36, 36, 36, 36, 36, 86, 87, + 43, 86, 85, 43, 2, 2, 2, 71, 70, 44, 44, 44, 44, 44, 44, 44, + 36, 36, 36, 61, 61, 36, 36, 62, 36, 36, 36, 36, 36, 36, 36, 62, + 36, 36, 36, 36, 63, 44, 44, 44, 36, 36, 36, 36, 36, 36, 36, 70, + 86, 87, 43, 43, 43, 80, 44, 44, 43, 86, 62, 36, 36, 36, 61, 62, + 61, 36, 62, 36, 36, 57, 71, 86, 85, 86, 90, 89, 90, 89, 86, 44, + 61, 44, 44, 89, 44, 44, 62, 36, 36, 86, 44, 43, 43, 43, 80, 44, + 43, 43, 80, 44, 44, 44, 44, 44, 36, 36, 94, 86, 43, 43, 43, 43, + 86, 43, 85, 71, 36, 63, 2, 2, 7, 7, 7, 7, 7, 2, 93, 71, + 86, 87, 43, 43, 85, 85, 86, 87, 85, 43, 36, 72, 44, 44, 44, 44, + 36, 36, 36, 36, 36, 36, 36, 94, 86, 43, 43, 44, 86, 86, 43, 87, + 60, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 36, 36, 43, 44, + 86, 87, 43, 43, 43, 85, 87, 87, 60, 2, 61, 44, 44, 44, 44, 44, + 2, 2, 2, 2, 2, 2, 64, 44, 36, 36, 36, 36, 36, 70, 87, 86, + 43, 43, 43, 87, 63, 44, 44, 44, 86, 43, 43, 87, 43, 43, 44, 44, + 7, 7, 7, 7, 7, 27, 2, 97, 43, 43, 43, 43, 87, 60, 44, 44, + 27,100, 44, 44, 44, 44, 44, 62, 36, 36, 36, 61, 62, 44, 36, 36, + 36, 36, 62, 61, 36, 36, 36, 36, 86, 86, 86, 89, 90, 57, 85, 71, + 98, 87, 2, 64, 44, 44, 44, 44, 36, 36, 36, 36, 44, 36, 36, 36, + 94, 86, 43, 43, 44, 43, 86, 86, 71, 72, 90, 44, 44, 44, 44, 44, + 70, 43, 43, 43, 43, 71, 36, 36, 36, 70, 43, 43, 85, 70, 43, 60, + 2, 2, 2, 59, 44, 44, 44, 44, 70, 43, 43, 85, 87, 43, 36, 36, + 36, 36, 36, 36, 36, 43, 43, 43, 43, 43, 43, 85, 43, 2, 72, 2, + 2, 64, 44, 44, 44, 44, 44, 44, 2, 2, 2, 2, 2, 44, 44, 44, + 43, 43, 43, 80, 43, 43, 43, 87, 63, 2, 2, 44, 44, 44, 44, 44, + 2, 36, 36, 36, 36, 36, 36, 36, 44, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 89, 43, 43, 43, 85, 43, 87, 80, 44, 44, 44, 44, + 36, 36, 36, 61, 36, 62, 36, 36, 70, 43, 43, 80, 44, 80, 43, 57, + 43, 43, 43, 70, 44, 44, 44, 44, 36, 36, 36, 62, 61, 36, 36, 36, + 36, 36, 36, 36, 36, 86, 86, 90, 43, 89, 87, 87, 61, 44, 44, 44, + 36, 70, 85,107, 64, 44, 44, 44, 43, 94, 36, 36, 36, 36, 36, 36, + 36, 36, 86, 43, 43, 80, 44, 86, 85, 60, 2, 2, 2, 2, 2, 2, + 27, 27, 91, 67, 67, 67, 56, 20,168, 67, 67, 67, 67, 67, 67, 67, + 67, 44, 44, 44, 44, 44, 44, 93,105,105,105,105,105,105,105,181, + 2, 2, 64, 44, 44, 44, 44, 44, 63, 64, 44, 44, 44, 44, 44, 44, + 65, 65, 65, 65, 65, 65, 65, 65, 71, 36, 36, 70, 43, 43, 43, 43, + 43, 43, 43, 44, 44, 44, 44, 44, 43, 43, 60, 44, 44, 44, 44, 44, + 43, 43, 43, 60, 2, 2, 67, 67, 40, 40, 97, 44, 44, 44, 44, 44, + 7, 7, 7, 7, 7,179, 27, 27, 27, 62, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 44, 44, 62, 36, 27, 27, 27, 30, 2, 64, 44, 44, + 36, 36, 36, 36, 36, 61, 44, 57, 94, 86, 86, 86, 86, 86, 86, 86, + 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 44, 44, 44, 57, + 43, 74, 40, 40, 40, 40, 40, 40, 40, 88, 80, 44, 44, 44, 44, 44, + 86, 44, 44, 44, 44, 44, 44, 44, 40, 40, 52, 40, 40, 40, 52, 81, + 36, 61, 44, 44, 44, 44, 44, 44, 44, 61, 44, 44, 44, 44, 44, 44, + 36, 61, 62, 44, 44, 44, 44, 44, 44, 44, 36, 36, 44, 44, 44, 44, + 36, 36, 36, 36, 36, 44, 50, 60, 65, 65, 44, 44, 44, 44, 44, 44, + 43, 43, 43, 43, 43, 43, 43, 44, 43, 43, 43, 80, 44, 44, 44, 44, + 67, 67, 67, 92, 55, 67, 67, 67, 67, 67,186, 87, 43, 67,186, 86, + 86,187, 65, 65, 65, 84, 43, 43, 43, 76, 50, 43, 43, 43, 67, 67, + 67, 67, 67, 67, 67, 43, 43, 67, 67, 43, 76, 44, 44, 44, 44, 44, + 27, 27, 44, 44, 44, 44, 44, 44, 11, 11, 11, 11, 11, 16, 16, 16, + 16, 16, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 16, + 16, 16,110, 16, 16, 16, 16, 16, 11, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 47, 11, 44, 47, 48, 47, 48, 11, 47, 11, + 11, 11, 11, 16, 16,150,150, 16, 16, 16,150, 16, 16, 16, 16, 16, + 16, 16, 11, 48, 11, 47, 48, 11, 11, 11, 47, 11, 11, 11, 47, 16, + 16, 16, 16, 16, 11, 48, 11, 47, 11, 11, 47, 47, 44, 11, 11, 11, + 47, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 11, 11, + 11, 11, 11, 16, 16, 16, 16, 16, 16, 16, 16, 44, 11, 11, 11, 11, + 31, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 33, 16, 16, + 16, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 31, 16, 16, + 16, 16, 33, 16, 16, 16, 11, 11, 11, 11, 31, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 33, 16, 16, 16, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 31, 16, 16, 16, 16, 33, 16, 16, 16, + 11, 11, 11, 11, 31, 16, 16, 16, 16, 33, 16, 16, 16, 32, 44, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 43, 43, 43, 76, 67, 50, 43, 43, + 43, 43, 43, 43, 43, 43, 76, 67, 67, 67, 50, 67, 67, 67, 67, 67, + 67, 67, 76, 21, 2, 2, 44, 44, 44, 44, 44, 44, 44, 57, 43, 43, + 16, 16, 16, 16, 16, 39, 16, 16, 16, 16, 16, 16, 16, 16, 16,110, + 44, 44,150, 16, 16,110, 44, 44, 43, 43, 43, 80, 43, 43, 43, 43, + 43, 43, 43, 43, 80, 57, 43, 43, 43, 57, 80, 43, 43, 80, 44, 44, + 40, 40, 40, 40, 40, 40, 40, 44, 44, 44, 44, 44, 44, 44, 44, 57, + 43, 43, 43, 74, 40, 40, 40, 44, 7, 7, 7, 7, 7, 44, 44, 77, + 36, 36, 36, 36, 36, 36, 36, 80, 36, 36, 36, 36, 36, 36, 43, 43, + 7, 7, 7, 7, 7, 44, 44, 96, 36, 36, 36, 36, 36, 83, 43, 43, + 36, 36, 36, 61, 36, 36, 62, 61, 36, 36, 61,179, 27, 27, 27, 27, + 16, 16, 43, 43, 43, 74, 44, 44, 27, 27, 27, 27, 27, 27,163, 27, + 188, 27,100, 44, 44, 44, 44, 44, 27, 27, 27, 27, 27, 27, 27,163, + 27, 27, 27, 27, 27, 27, 27, 44, 36, 36, 62, 36, 36, 36, 36, 36, + 62, 61, 61, 62, 62, 36, 36, 36, 36, 61, 36, 36, 62, 62, 44, 44, + 44, 61, 44, 62, 62, 62, 62, 36, 62, 61, 61, 62, 62, 62, 62, 62, + 62, 61, 61, 62, 36, 61, 36, 36, 36, 61, 36, 36, 62, 36, 61, 61, + 36, 36, 36, 36, 36, 62, 36, 36, 62, 36, 62, 36, 36, 62, 36, 36, + 8, 44, 44, 44, 44, 44, 44, 44, 55, 67, 67, 67, 67, 67, 67, 67, + 27, 27, 27, 27, 27, 27, 91, 67, 67, 67, 67, 67, 67, 67, 67, 44, + 44, 44, 44, 67, 67, 67, 67, 67, 67, 92, 44, 44, 44, 44, 44, 44, + 67, 67, 67, 67, 92, 44, 44, 44, 67, 44, 44, 44, 44, 44, 44, 44, + 67, 67, 67, 67, 67, 25, 41, 41, 67, 67, 67, 67, 44, 44, 67, 67, + 67, 67, 67, 92, 44, 55, 67, 67, 67, 67, 67, 67, 44, 44, 44, 44, + 67, 67, 67, 67, 67, 67, 67, 55, 67, 67, 67, 44, 44, 44, 44, 67, + 67, 92, 67, 67, 67, 67, 67, 67, 79, 44, 44, 44, 44, 44, 44, 44, + 171,171,171,171,171,171,171, 44,171,171,171,171,171,171,171, 0, + 0, 0, 29, 21, 21, 21, 23, 21, 22, 18, 21, 25, 21, 17, 13, 13, + 25, 25, 25, 21, 21, 9, 9, 9, 9, 22, 21, 18, 24, 16, 24, 5, + 5, 5, 5, 22, 25, 18, 25, 0, 23, 23, 26, 21, 24, 26, 7, 20, + 25, 1, 26, 24, 26, 25, 15, 15, 24, 15, 7, 19, 15, 21, 9, 25, + 9, 5, 5, 25, 5, 9, 5, 7, 7, 7, 9, 8, 8, 5, 7, 5, + 6, 6, 24, 24, 6, 24, 12, 12, 2, 2, 6, 5, 9, 21, 9, 2, + 2, 9, 25, 9, 26, 12, 11, 11, 2, 6, 5, 21, 17, 2, 2, 26, + 26, 23, 2, 12, 17, 12, 21, 12, 12, 21, 7, 2, 2, 7, 7, 21, + 21, 2, 1, 1, 21, 23, 26, 26, 1, 21, 6, 7, 7, 12, 12, 7, + 21, 7, 12, 1, 12, 6, 6, 12, 12, 26, 7, 26, 26, 7, 2, 1, + 12, 2, 6, 2, 24, 7, 7, 6, 1, 12, 12, 10, 10, 10, 10, 12, + 21, 6, 2, 10, 10, 2, 15, 26, 26, 2, 2, 21, 7, 10, 15, 7, + 2, 23, 21, 26, 10, 7, 21, 15, 15, 2, 17, 7, 29, 7, 7, 22, + 18, 2, 14, 14, 14, 7, 10, 21, 17, 21, 11, 12, 5, 2, 5, 6, + 8, 8, 8, 24, 5, 24, 2, 24, 9, 24, 24, 2, 29, 29, 29, 1, + 17, 17, 20, 19, 22, 20, 27, 28, 1, 29, 21, 20, 19, 21, 21, 16, + 16, 21, 25, 22, 18, 21, 21, 29, 1, 2, 15, 6, 18, 6, 23, 2, + 12, 11, 9, 26, 26, 9, 26, 5, 5, 26, 14, 9, 5, 14, 14, 15, + 25, 26, 26, 22, 18, 26, 18, 25, 18, 22, 5, 12, 2, 5, 22, 21, + 21, 22, 18, 17, 26, 6, 7, 14, 17, 22, 18, 18, 26, 14, 17, 6, + 14, 6, 12, 24, 24, 6, 26, 15, 6, 21, 11, 21, 24, 9, 6, 9, + 23, 26, 6, 10, 4, 4, 3, 3, 7, 25, 17, 16, 16, 22, 16, 16, + 25, 17, 25, 2, 25, 24, 2, 15, 12, 15, 14, 2, 21, 14, 7, 15, + 12, 17, 21, 1, 26, 10, 10, 1, 23, 15, 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, 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, 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, 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, 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, 35, + 0, 36, 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, 37, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 38, 39, 0, 0, 0, 0, 0, 0, 40, 41, 42, 0, 43, 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, 1, 2, 3, 4, 0, 0, 0, 0, + 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 6, 7, 8, 0, + 9, 0, 10, 11, 0, 0, 12, 13, 14, 15, 16, 0, 0, 0, 0, 17, + 18, 19, 20, 0, 21, 0, 22, 23, 0, 24, 25, 0, 0, 24, 26, 27, + 0, 24, 26, 0, 0, 24, 26, 0, 0, 24, 26, 0, 0, 0, 26, 0, + 0, 24, 28, 0, 0, 24, 26, 0, 0, 29, 26, 0, 0, 0, 30, 0, + 0, 31, 32, 0, 0, 33, 34, 0, 35, 36, 0, 37, 38, 0, 39, 0, + 0, 40, 0, 0, 41, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 43, 44, 0, 0, 0, 0, 45, 0, + 0, 0, 0, 0, 0, 46, 0, 0, 0, 47, 0, 0, 0, 0, 0, 0, + 48, 0, 0, 49, 0, 50, 51, 0, 0, 52, 53, 54, 0, 55, 0, 56, + 0, 57, 0, 0, 0, 0, 58, 59, 0, 0, 0, 0, 0, 0, 60, 61, + 0, 0, 0, 0, 0, 0, 62, 63, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 65, 0, 0, 0, 66, + 0, 67, 0, 0, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 69, 70, 0, 0, 71, 0, 0, 0, 0, 0, 0, 0, 0, + 72, 73, 0, 0, 0, 0, 53, 74, 0, 75, 76, 0, 0, 77, 78, 0, + 0, 0, 0, 0, 0, 79, 80, 81, 0, 0, 0, 0, 0, 0, 0, 26, + 0, 0, 0, 0, 0, 0, 0, 0, 82, 0, 0, 0, 0, 0, 0, 0, + 0, 83, 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, 84, + 0, 0, 0, 0, 0, 0, 0, 85, 0, 0, 0, 86, 0, 0, 0, 0, + 87, 88, 0, 0, 0, 0, 0, 89, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 90, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 91, 0, 92, 0, 0, 93, 0, 94, 0, 0, 0, + 0, 0, 72, 95, 0, 96, 0, 0, 97, 98, 0, 77, 0, 0, 99, 0, + 0,100, 0, 0, 0, 0, 0,101, 0,102, 26,103, 0, 0, 0, 0, + 0, 0,104, 0, 0, 0,105, 0, 0, 0, 0, 0, 0, 65,106, 0, + 0, 65, 0, 0, 0,107, 0, 0, 0,108, 0, 0, 0, 0, 0, 0, + 0, 96, 0, 0, 0, 0, 0, 0, 0,109,110, 0, 0, 0, 0, 78, + 0, 44,111, 0,112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 65, 0, 0, 0, 0, 0, 0, 0, 0,113, 0,114, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,115, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0,116, 0,117, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,118, + 0, 0, 0, 0,119, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,120,121,122, 0, 0, + 0, 0,123, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 124,125, 0, 0,126, 0, 0, 0, 0,117, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0,127, 0,128, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0,129, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0,130, 0, 0, 0,131, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 3, 4, + 5, 6, 7, 4, 4, 8, 9, 10, 1, 11, 12, 13, 14, 15, 16, 17, + 18, 1, 1, 1, 0, 0, 0, 0, 19, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 20, 21, 22, 1, 23, 4, 21, 24, 25, 26, 27, 28, + 29, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 31, 0, + 0, 0, 32, 33, 34, 35, 1, 36, 0, 0, 0, 0, 37, 0, 0, 0, + 0, 0, 0, 0, 0, 38, 1, 39, 14, 39, 40, 41, 0, 0, 0, 0, + 0, 0, 0, 0, 42, 0, 0, 0, 0, 0, 0, 0, 43, 36, 44, 45, + 21, 45, 46, 0, 0, 0, 0, 0, 0, 0, 19, 1, 21, 0, 0, 47, + 0, 0, 0, 0, 0, 38, 48, 1, 1, 49, 49, 50, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 51, 0, 0, 0, 0, 0, 0, 0, 52, 1, + 0, 0, 38, 14, 4, 1, 1, 1, 53, 21, 43, 52, 54, 21, 35, 1, + 0, 0, 0, 0, 0, 0, 0, 55, 0, 0, 0, 56, 57, 58, 0, 0, + 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 59, + 0, 0, 0, 56, 0, 60, 0, 0, 0, 0, 0, 0, 0, 0, 61, 62, + 0, 0, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, + 0, 0, 65, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 66, 0, + 0, 0, 67, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 68, 0, + 0, 0, 0, 0, 0, 69, 70, 0, 0, 0, 0, 0, 71, 72, 73, 74, + 75, 76, 0, 0, 0, 0, 0, 0, 0, 77, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 78, 79, 0, 0, 0, 0, 47, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 49, 0, 0, 0, 0, 0, 80, 0, 0, + 0, 0, 0, 0, 0, 62, 0, 0, 0, 0, 0, 0, 63, 0, 0, 81, + 0, 0, 82, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 83, 0, + 0, 0, 0, 0, 0, 19, 84, 0, 62, 0, 0, 0, 0, 49, 1, 85, + 0, 0, 0, 0, 1, 52, 15, 86, 36, 10, 21, 87, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 55, 0, 0, 0, 62, 0, 0, 0, 0, 0, 0, + 0, 0, 19, 10, 1, 0, 0, 0, 0, 0, 88, 0, 0, 0, 0, 0, + 0, 89, 0, 0, 88, 0, 0, 0, 0, 0, 0, 0, 0, 78, 0, 0, + 0, 0, 0, 0, 87, 9, 12, 4, 90, 8, 91, 47, 0, 58, 50, 0, + 21, 1, 21, 92, 93, 1, 1, 1, 1, 1, 1, 1, 1, 94, 95, 96, + 0, 0, 0, 0, 97, 1, 98, 58, 81, 99,100, 4, 58, 0, 0, 0, + 0, 0, 0, 19, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 61, + 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,101,102, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0,103, 0, 0, 0, 0, 19, 0, 1, 1, 50, + 0, 0, 0, 0, 0, 0, 0, 38, 0, 0, 0, 0, 50, 0, 0, 0, + 0, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 0, 0, 0, 0, + 1, 1, 1, 1, 50, 0, 0, 0, 0, 0,104, 68, 0, 0, 0, 0, + 0, 0, 0, 0, 61, 0, 0, 0, 0, 0, 0, 0, 78, 0, 0, 0, + 62, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,105,106, 58, 38, + 81, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 63, 0, 0, + 0, 0, 0, 0, 0, 0, 0,107, 1, 14, 4, 12, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 47, 84, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 38, 87, 0, 0, 0, 0,108, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0,109, 61, 0,110, 0, 0, 0, 0, 0, 0, + 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 19, 58, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 51, 0,111, 14, 52, 84, 0, 0, 0, + 112, 41, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 0, 0, 61, + 0, 0, 0, 0, 0, 0,113, 0, 87, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 61, 62, 0, 0, 62, 0, 89, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0,113, 0, 0, 0, 0,114, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 78, 55, 0, 38, 1, 58, 1, 58, 0, 0, + 63, 89, 0, 0, 0, 0, 0, 59,115, 0, 0, 0, 0, 0, 0, 0, + 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,115, 0, 0, + 0, 0, 61, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 79, + 78, 0, 0, 0, 0, 0, 0, 0, 0, 61, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 56, 0, 89, 80, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 61, 0, 0, 79, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 8, 91, 0, 0, 0, 0, 0, 0, 1, 87, 0, 0, + 0, 0, 0, 0,116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,117, + 0,118,119,120,121, 0,104, 4,122, 49, 23, 0, 0, 0, 0, 0, + 0, 0, 38, 50, 0, 0, 0, 0, 38, 58, 0, 0, 0, 0, 0, 0, + 1, 87, 1, 1, 1, 1, 39, 1, 48,105, 87, 0, 0, 0, 0, 0, + 0, 0, 0, 19, 0, 0, 0, 0, 0, 0, 0, 59, 0, 0, 0, 0, + 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,123, 0, 0, 0, 0, + 0, 0, 0, 0, 4,122, 0, 0, 0, 1,124, 0, 0, 0, 0, 0, + 0, 0, 0, 0,230,230,230,230,230,232,220,220,220,220,232,216, + 220,220,220,220,220,202,202,220,220,220,220,202,202,220,220,220, + 1, 1, 1, 1, 1,220,220,220,220,230,230,230,230,240,230,220, + 220,220,230,230,230,220,220, 0,230,230,230,220,220,220,220,230, + 232,220,220,230,233,234,234,233,234,234,233,230, 0, 0, 0,230, + 0,220,230,230,230,230,220,230,230,230,222,220,230,230,220,220, + 230,222,228,230, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, + 21, 22, 0, 23, 0, 24, 25, 0,230,220, 0, 18, 30, 31, 32, 0, + 0, 0, 0, 27, 28, 29, 30, 31, 32, 33, 34,230,230,220,220,230, + 220,230,230,220, 35, 0, 0, 0, 0, 0,230,230,230, 0, 0,230, + 230, 0,220,230,230,220, 0, 0, 0, 36, 0, 0,230,220,230,230, + 220,220,230,220,220,230,220,230,220,230,230, 0, 0,220, 0, 0, + 230,230, 0,230, 0,230,230,230,230,230, 0, 0, 0,220,220,220, + 230,220,220,220,230,230, 0,220, 27, 28, 29,230, 7, 0, 0, 0, + 0, 9, 0, 0, 0,230,220,230,230, 0, 0, 0, 0, 0,230, 0, + 0, 84, 91, 0, 0, 0, 0, 9, 9, 0, 0, 0, 0, 0, 9, 0, + 103,103, 9, 0,107,107,107,107,118,118, 9, 0,122,122,122,122, + 220,220, 0, 0, 0,220, 0,220, 0,216, 0, 0, 0,129,130, 0, + 132, 0, 0, 0, 0, 0,130,130,130,130, 0, 0,130, 0,230,230, + 9, 0,230,230, 0, 0,220, 0, 0, 0, 0, 7, 0, 9, 9, 0, + 9, 9, 0, 0, 0,230, 0, 0, 0,228, 0, 0, 0,222,230,220, + 220, 0, 0, 0,230, 0, 0,220,230,220, 0,220,230,230,230, 0, + 0, 0, 9, 9, 0, 0, 7, 0,230, 0, 1, 1, 1, 0, 0, 0, + 230,234,214,220,202,230,230,230,230,230,232,228,228,220,218,230, + 233,220,230,220,230,230, 1, 1, 1, 1, 1,230, 0, 1, 1,230, + 220,230, 1, 1, 0, 0,218,228,232,222,224,224, 0, 8, 8, 0, + 0, 0, 0,220,230, 0,230,230,220, 0, 0,230, 0, 0, 26, 0, + 0,220, 0,230,230, 1,220, 0, 0,230,220, 0, 0, 0,220,220, + 0, 0,230,220, 0, 9, 7, 0, 0, 7, 9, 0, 0, 0, 9, 7, + 6, 6, 0, 0, 0, 0, 1, 0, 0,216,216, 1, 1, 1, 0, 0, + 0,226,216,216,216,216,216, 0,220,220,220, 0,232,232,220,230, + 230,230, 7, 0, 16, 17, 17, 17, 17, 17, 17, 33, 17, 17, 17, 19, + 17, 17, 17, 17, 20,101, 17,113,129,169, 17, 27, 28, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17,237, 0, 1, 2, 2, 0, 3, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 5, 0, 0, 0, 0, 6, 7, 8, 9, 0, 0, 0, 10, 11, 12, 13, + 14, 15, 16, 17, 18, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, + 0, 0, 21, 22, 0, 0, 0, 0, 23, 24, 25, 26, 0, 27, 0, 28, + 29, 30, 31, 32, 0, 0, 0, 0, 0, 0, 0, 33, 34, 35, 36, 0, + 0, 0, 0, 0, 37, 0, 0, 0, 0, 0, 0, 0, 0, 0, 38, 39, + 0, 0, 0, 0, 1, 2, 40, 41, 0, 1, 2, 2, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 2, 0, 0, 0, 0, + 0, 0, 3, 4, 0, 0, 5, 0, 0, 0, 6, 0, 0, 0, 0, 0, + 0, 0, 7, 1, 0, 0, 0, 0, 0, 0, 8, 9, 0, 0, 0, 0, + 0, 0, 10, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 10, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 11, 12, + 0, 13, 0, 14, 15, 16, 0, 0, 0, 0, 0, 1, 17, 18, 0, 19, + 7, 1, 0, 0, 0, 20, 20, 7, 20, 20, 20, 20, 20, 20, 20, 8, + 21, 0, 22, 0, 7, 23, 24, 0, 20, 20, 25, 0, 0, 0, 26, 27, + 1, 7, 20, 20, 20, 20, 20, 1, 28, 29, 30, 31, 0, 0, 20, 0, + 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 20, 20, + 20, 1, 0, 0, 8, 21, 32, 4, 0, 10, 0, 33, 7, 20, 20, 20, + 0, 0, 0, 0, 8, 34, 34, 35, 36, 34, 37, 0, 38, 1, 20, 20, + 0, 0, 39, 0, 1, 1, 0, 8, 21, 1, 20, 0, 0, 0, 1, 0, + 0, 40, 1, 1, 0, 0, 8, 21, 0, 1, 0, 1, 0, 1, 0, 0, + 0, 0, 26, 34, 34, 34, 34, 34, 34, 34, 34, 34, 21, 7, 20, 41, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 21, 0, 42, 43, 44, 0, 45, + 0, 8, 21, 0, 0, 0, 0, 0, 0, 0, 0, 46, 7, 1, 10, 1, + 0, 0, 0, 1, 20, 20, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 26, 34, 9, 0, 0, 20, 20, 1, 20, 20, 0, 0, 0, 0, 0, + 0, 0, 26, 21, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 3, 47, 48, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, + 4, 5, 6, 7, 7, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 9, 10, 11, 11, 11, 11, 12, 13, 13, 13, 13, 14, 15, 16, 17, 18, + 19, 20, 21, 13, 22, 13, 13, 13, 13, 23, 24, 24, 25, 26, 13, 13, + 13, 27, 28, 29, 13, 30, 31, 32, 33, 34, 35, 36, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 37, 7, 38, 39, 7, 40, 7, 7, 7, 41, 13, 42, 7, 7, 43, 7, + 44, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 45, 0, 0, 1, + 2, 2, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 32, 33, 34, 35, 36, 37, 37, 37, 37, 37, 38, 39, 40, 41, 42, + 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 2, 2, 53, 54, 55, 56, + 57, 58, 59, 59, 59, 59, 60, 59, 59, 59, 59, 59, 59, 59, 61, 61, + 59, 59, 59, 59, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 59, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 79, 70, 70, 70, 70, 80, 80, + 80, 80, 80, 80, 80, 80, 80, 81, 82, 82, 83, 84, 85, 86, 87, 88, + 89, 90, 91, 92, 93, 94, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 95, 96, 96, + 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, + 70, 70, 97, 98, 99,100,101,101,102,103,104,105,106,107,108,109, + 110,111, 96,112,113,114,115,116,117,118,119,119,120,121,122,123, + 124,125,126,127,128,129,130,131,132, 96,133,134,135,136,137,138, + 139,140,141,142,143, 96,144,145, 96,146,147,148,149, 96,150,151, + 152,153,154,155,156, 96,157,158,159,160, 96,161,162,163,164,164, + 164,164,164,164,164,165,166,164,167, 96, 96, 96, 96, 96, 96, 96, + 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96,168,169,169, + 169,169,169,169,169,169,170, 96, 96, 96, 96, 96, 96, 96, 96, 96, + 96, 96, 96, 96, 96, 96,171,171,171,171,172, 96, 96, 96,173,173, + 173,173,174,175,176,177, 96, 96, 96, 96,178,179,180,181,182,182, + 182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182, + 182,182,182,182,182,182,182,182,182,182,182,182,182,183,182,182, + 182,182,182,182,184,184,184,185,186, 96, 96, 96, 96, 96, 96, 96, + 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96,187,188,189, + 190,191,191,192, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, + 96, 96, 96, 96, 96, 96,193,194, 96, 96, 96, 96, 96, 96, 96, 96, + 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96,195,196, 59,197, + 198,199,200,201,202, 96,203,204,205, 59, 59,206, 59,207,208,208, + 208,208,208,209, 96, 96, 96, 96, 96, 96, 96, 96,210, 96,211,212, + 213, 96, 96,214, 96, 96, 96,215, 96, 96, 96, 96, 96,216,217,218, + 219, 96, 96, 96, 96, 96,220,221,222, 96,223,224, 96, 96,225,226, + 59,227,228, 96, 59, 59, 59, 59, 59, 59, 59,229,230,231,232,233, + 59, 59,234,235, 59,236, 96, 96, 96, 96, 96, 96, 96, 96, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70,237, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70,238, 70,239, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70,240, 70, 70, 70, 70, + 70, 70, 70, 70, 70,241, 96, 96, 96, 96, 96, 96, 96, 96, 70, 70, + 70, 70,242, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 70, 70, + 70, 70, 70, 70,243, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70,244, 96, 96, 96, 96, 96, 96, 96, 96,245, 96, + 246,247, 0, 1, 2, 2, 0, 1, 2, 2, 2, 3, 4, 5, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 0, 0, 0, 0, 0, 0, 0, + 19, 0, 0, 0, 0, 0, 19, 19, 19, 19, 19, 19, 19, 0, 19, 0, + 0, 0, 0, 0, 0, 0, 19, 19, 19, 19, 19, 0, 0, 0, 0, 0, + 26, 26, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, + 9, 9, 0, 9, 9, 9, 2, 2, 9, 9, 9, 9, 0, 9, 2, 2, + 2, 2, 9, 0, 9, 0, 9, 9, 9, 2, 9, 2, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 2, 9, 9, 9, 9, 9, 9, 9, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 1, 1, 6, 2, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 2, 4, 4, 4, 2, 2, 4, 4, 4, 2, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 2, 2, + 2, 2, 2, 2, 2, 2, 14, 14, 14, 2, 2, 2, 2, 14, 14, 14, + 14, 14, 14, 2, 2, 2, 3, 3, 3, 3, 3, 0, 3, 3, 3, 3, + 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 0, 3, 3, 3, 0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 1, 3, + 3, 3, 3, 3, 3, 3, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, + 37, 37, 37, 37, 2, 37, 37, 37, 37, 2, 2, 37, 37, 37, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 38, 2, 2, 2, 2, 2, 2, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 2, 2, 64, 64, 64, 90, 90, + 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 2, 2, 90, 90, + 90, 90, 90, 90, 90, 2, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, + 95, 95, 2, 2, 95, 2, 37, 37, 37, 2, 2, 2, 2, 2, 3, 3, + 3, 3, 3, 3, 3, 2, 3, 3, 2, 2, 2, 2, 2, 2, 3, 3, + 0, 3, 3, 3, 3, 3, 7, 7, 7, 7, 7, 7, 7, 7, 7, 1, + 1, 1, 1, 7, 7, 7, 7, 7, 7, 7, 0, 0, 7, 7, 5, 5, + 5, 5, 2, 5, 5, 5, 5, 5, 5, 5, 5, 2, 2, 5, 5, 2, + 2, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 2, + 5, 5, 5, 5, 5, 5, 5, 2, 5, 2, 2, 2, 5, 5, 5, 5, + 2, 2, 5, 5, 5, 5, 5, 2, 2, 5, 5, 5, 5, 2, 2, 2, + 2, 2, 2, 2, 2, 5, 2, 2, 2, 2, 5, 5, 2, 5, 5, 5, + 5, 5, 2, 2, 5, 5, 5, 5, 5, 5, 5, 5, 5, 2, 2, 11, + 11, 11, 2, 11, 11, 11, 11, 11, 11, 2, 2, 2, 2, 11, 11, 2, + 2, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 2, + 11, 11, 11, 11, 11, 11, 11, 2, 11, 11, 2, 11, 11, 2, 11, 11, + 2, 2, 11, 2, 11, 11, 11, 2, 2, 11, 11, 11, 2, 2, 2, 11, + 2, 2, 2, 2, 2, 2, 2, 11, 11, 11, 11, 2, 11, 2, 2, 2, + 2, 2, 2, 2, 11, 11, 11, 11, 11, 11, 11, 11, 11, 2, 2, 10, + 10, 10, 2, 10, 10, 10, 10, 10, 10, 10, 10, 10, 2, 10, 10, 10, + 2, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 2, + 10, 10, 10, 10, 10, 10, 10, 2, 10, 10, 2, 10, 10, 10, 10, 10, + 2, 2, 10, 10, 10, 10, 10, 10, 2, 10, 10, 10, 2, 2, 10, 2, + 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 2, 2, 10, 10, 10, 10, + 2, 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 10, 10, 10, 2, 21, + 21, 21, 2, 21, 21, 21, 21, 21, 21, 21, 21, 2, 2, 21, 21, 2, + 2, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 2, + 21, 21, 21, 21, 21, 21, 21, 2, 21, 21, 2, 21, 21, 21, 21, 21, + 2, 2, 21, 21, 21, 21, 21, 2, 2, 21, 21, 21, 2, 2, 2, 2, + 2, 2, 2, 21, 21, 21, 2, 2, 2, 2, 21, 21, 2, 21, 21, 21, + 21, 21, 2, 2, 21, 21, 2, 2, 22, 22, 2, 22, 22, 22, 22, 22, + 22, 2, 2, 2, 22, 22, 22, 2, 22, 22, 22, 22, 2, 2, 2, 22, + 22, 2, 22, 2, 22, 22, 2, 2, 2, 22, 22, 2, 2, 2, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 2, 2, 2, 2, 22, 22, 22, 2, + 2, 2, 2, 2, 2, 22, 2, 2, 2, 2, 2, 2, 22, 22, 22, 22, + 22, 2, 2, 2, 2, 2, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 2, 23, 23, 23, 2, 23, 23, 23, 23, 23, 23, 23, 23, + 2, 2, 23, 23, 23, 23, 23, 2, 23, 23, 23, 23, 2, 2, 2, 2, + 2, 2, 2, 23, 23, 2, 23, 23, 23, 2, 2, 23, 2, 2, 23, 23, + 23, 23, 2, 2, 23, 23, 2, 2, 2, 2, 2, 2, 2, 23, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 2, 16, 16, 16, 2, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 2, 16, 16, 16, 16, 16, + 2, 2, 16, 16, 16, 16, 16, 2, 16, 16, 16, 16, 2, 2, 2, 2, + 2, 2, 2, 16, 16, 2, 16, 16, 16, 16, 2, 2, 16, 16, 2, 16, + 16, 16, 2, 2, 2, 2, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 2, 20, 20, 20, 2, 20, 20, 20, 20, 20, 20, 2, 2, + 2, 2, 20, 20, 20, 20, 20, 20, 20, 20, 2, 2, 20, 20, 2, 36, + 36, 36, 2, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 2, 2, 2, 36, 36, 36, 36, 36, 36, 36, 36, + 2, 36, 36, 36, 36, 36, 36, 36, 36, 36, 2, 36, 2, 2, 2, 2, + 36, 2, 2, 2, 2, 36, 36, 36, 36, 36, 36, 2, 36, 2, 2, 2, + 2, 2, 2, 2, 36, 36, 2, 2, 36, 36, 36, 2, 2, 2, 2, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 2, 2, 2, 2, 0, 24, 24, 24, 24, 2, 2, 2, 2, 2, 18, + 18, 2, 18, 2, 18, 18, 18, 18, 18, 2, 18, 18, 18, 18, 18, 18, + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 2, 18, 2, 18, 18, 18, + 18, 18, 18, 18, 2, 2, 18, 18, 18, 18, 18, 2, 18, 2, 18, 18, + 18, 18, 18, 18, 18, 2, 18, 18, 2, 2, 18, 18, 18, 18, 25, 25, + 25, 25, 25, 25, 25, 25, 2, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 2, 2, 2, 25, 25, 25, 25, 25, 2, 25, 25, 25, 25, + 25, 25, 25, 0, 0, 0, 0, 25, 25, 2, 2, 2, 2, 2, 33, 33, + 33, 33, 33, 33, 33, 33, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 2, 8, 2, 2, 2, 2, 2, 8, 2, 2, 8, 8, + 8, 0, 8, 8, 8, 8, 12, 12, 12, 12, 12, 12, 12, 12, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 2, 30, 30, 30, 30, 2, 2, 30, 30, + 30, 30, 30, 30, 30, 2, 30, 30, 30, 2, 2, 30, 30, 30, 30, 30, + 30, 30, 30, 2, 2, 2, 30, 30, 2, 2, 2, 2, 2, 2, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 2, 2, 28, 28, + 28, 28, 28, 28, 28, 28, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 2, 2, 2, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 0, 0, 0, 35, 35, 35, 2, 2, 2, 2, 2, 2, 2, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 45, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, + 44, 44, 44, 0, 0, 2, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 2, 2, 2, 2, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, + 46, 46, 46, 2, 46, 46, 46, 2, 46, 46, 2, 2, 2, 2, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 2, 2, 31, 31, + 2, 2, 2, 2, 2, 2, 32, 32, 0, 0, 32, 0, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 2, 2, 2, 2, 2, 2, 32, 2, + 2, 2, 2, 2, 2, 2, 32, 32, 32, 2, 2, 2, 2, 2, 28, 28, + 28, 28, 28, 28, 2, 2, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 2, 48, 48, 48, 48, 2, 2, 2, 2, 48, 2, + 2, 2, 48, 48, 48, 48, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, + 52, 52, 52, 52, 2, 2, 52, 52, 52, 52, 52, 2, 2, 2, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 2, 2, 2, 2, 58, 58, + 2, 2, 2, 2, 2, 2, 58, 58, 58, 2, 2, 2, 58, 58, 54, 54, + 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 2, 2, 54, 54, 91, 91, + 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 2, 91, 91, + 91, 91, 91, 2, 2, 91, 91, 91, 2, 2, 2, 2, 2, 2, 91, 91, + 91, 91, 91, 91, 2, 2, 1, 1, 1, 1, 1, 1, 1, 2, 62, 62, + 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 2, 2, 2, 62, 62, + 62, 62, 62, 62, 62, 2, 76, 76, 76, 76, 76, 76, 76, 76, 93, 93, + 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 2, 2, 2, 2, 2, 2, + 2, 2, 93, 93, 93, 93, 70, 70, 70, 70, 70, 70, 70, 70, 2, 2, + 2, 70, 70, 70, 70, 70, 70, 70, 2, 2, 2, 70, 70, 70, 73, 73, + 73, 73, 73, 73, 73, 73, 6, 2, 2, 2, 2, 2, 2, 2, 8, 8, + 8, 2, 2, 8, 8, 8, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, + 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0, 1, 1, 0, 2, 2, 2, 2, 2, 19, 19, + 19, 19, 19, 19, 9, 9, 9, 9, 9, 6, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 9, 9, 9, 9, 9, 19, 19, 19, 19, 9, 9, 9, 9, + 9, 19, 19, 19, 19, 19, 6, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 9, 9, 9, 9, 9, 9, 9, 2, 2, 2, 9, + 2, 9, 2, 9, 2, 9, 9, 9, 9, 9, 9, 2, 9, 9, 9, 9, + 9, 9, 2, 2, 9, 9, 9, 9, 9, 9, 2, 9, 9, 9, 2, 2, + 9, 9, 9, 2, 9, 9, 9, 9, 9, 9, 9, 9, 9, 2, 0, 0, + 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 19, + 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 0, 0, + 0, 0, 0, 0, 0, 2, 19, 19, 19, 19, 19, 2, 2, 2, 0, 2, + 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 2, 0, 0, + 0, 0, 0, 0, 9, 0, 0, 0, 19, 19, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 19, 0, 19, 0, 0, 0, 2, 2, 2, 2, 0, 0, + 0, 2, 2, 2, 2, 2, 27, 27, 27, 27, 27, 27, 27, 27, 0, 0, + 0, 0, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 56, 56, + 56, 56, 56, 56, 56, 56, 55, 55, 55, 55, 2, 2, 2, 2, 2, 55, + 55, 55, 55, 55, 55, 55, 61, 61, 61, 61, 61, 61, 61, 61, 2, 2, + 2, 2, 2, 2, 2, 61, 61, 2, 2, 2, 2, 2, 2, 2, 0, 0, + 0, 0, 0, 0, 2, 2, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 2, 13, 13, 13, 13, 13, 13, 13, 13, 13, 2, 2, 2, 2, 13, 13, + 13, 13, 13, 13, 2, 2, 0, 0, 0, 0, 2, 2, 2, 2, 0, 0, + 0, 0, 0, 13, 0, 13, 0, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 1, 1, 1, 1, 12, 12, 13, 13, 13, 13, 0, 0, 0, 0, 2, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 2, 2, 1, 1, 0, 0, 15, 15, 15, 0, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 0, 0, 17, 17, 17, 2, 2, 2, 2, 2, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 2, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 2, 12, 12, 12, 12, 12, 12, 12, 0, 17, 17, + 17, 17, 17, 17, 17, 0, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, + 39, 39, 39, 2, 2, 2, 39, 39, 39, 39, 39, 39, 39, 2, 86, 86, + 86, 86, 86, 86, 86, 86, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 2, 2, 2, 2, 79, 79, 79, 79, 79, 79, 79, 79, 0, 0, + 19, 19, 19, 19, 19, 19, 0, 0, 0, 19, 19, 19, 19, 19, 19, 19, + 19, 2, 2, 2, 2, 2, 19, 19, 2, 19, 2, 19, 19, 19, 19, 19, + 2, 2, 2, 2, 2, 2, 2, 2, 19, 19, 19, 19, 19, 19, 60, 60, + 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 2, 2, 2, 0, 0, + 2, 2, 2, 2, 2, 2, 65, 65, 65, 65, 65, 65, 65, 65, 75, 75, + 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 2, 2, 2, 2, + 2, 2, 2, 2, 75, 75, 75, 75, 2, 2, 2, 2, 2, 2, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 0, 69, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 74, 12, 12, 12, 12, 12, 2, 2, 2, 84, 84, + 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 2, 0, 84, 84, + 2, 2, 2, 2, 84, 84, 33, 33, 33, 33, 33, 33, 33, 2, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 2, 68, 68, + 68, 68, 68, 68, 2, 2, 68, 68, 2, 2, 68, 68, 68, 68, 92, 92, + 92, 92, 92, 92, 92, 92, 92, 92, 92, 2, 2, 2, 2, 2, 2, 2, + 2, 92, 92, 92, 92, 92, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, + 87, 87, 87, 87, 87, 2, 2, 30, 30, 30, 30, 30, 30, 2, 19, 19, + 19, 0, 19, 19, 19, 19, 19, 19, 19, 19, 19, 9, 19, 19, 19, 19, + 0, 0, 2, 2, 2, 2, 87, 87, 87, 87, 87, 87, 2, 2, 87, 87, + 2, 2, 2, 2, 2, 2, 12, 12, 12, 12, 2, 2, 2, 2, 2, 2, + 2, 12, 12, 12, 12, 12, 13, 13, 2, 2, 2, 2, 2, 2, 19, 19, + 19, 19, 19, 19, 19, 2, 2, 2, 2, 4, 4, 4, 4, 4, 2, 2, + 2, 2, 2, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 2, 14, 14, + 14, 14, 14, 2, 14, 2, 14, 14, 2, 14, 14, 2, 14, 14, 3, 3, + 3, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 0, 0, 2, 2, 3, 3, 3, 3, 3, 3, 2, 2, + 2, 2, 2, 2, 2, 3, 1, 1, 1, 1, 1, 1, 6, 6, 0, 0, + 0, 2, 0, 0, 0, 0, 3, 3, 3, 3, 3, 2, 3, 3, 3, 3, + 3, 3, 3, 2, 2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 17, 17, 17, 17, 17, 17, 17, 17, 0, 0, 2, 2, + 12, 12, 12, 12, 12, 12, 2, 2, 12, 12, 12, 2, 2, 2, 2, 0, + 0, 0, 0, 0, 2, 2, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 2, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 2, 49, 49, + 49, 2, 49, 49, 2, 49, 49, 49, 49, 49, 49, 49, 2, 2, 49, 49, + 49, 2, 2, 2, 2, 2, 0, 0, 0, 2, 2, 2, 2, 0, 0, 0, + 0, 0, 2, 2, 2, 0, 0, 0, 0, 0, 0, 2, 2, 2, 9, 2, + 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 1, 2, 2, 71, 71, + 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 2, 2, 2, 67, 67, + 67, 67, 67, 67, 67, 67, 67, 2, 2, 2, 2, 2, 2, 2, 1, 0, + 0, 0, 0, 0, 0, 0, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, + 42, 42, 2, 2, 2, 2, 2, 2, 2, 2, 2, 42, 42, 42, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 2, 2, 2, 2, 2,118,118, + 118,118,118,118,118,118,118,118,118, 2, 2, 2, 2, 2, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 2, 53, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 2, 2, 2, 2, 59, 59, + 59, 59, 59, 59, 2, 2, 40, 40, 40, 40, 40, 40, 40, 40, 51, 51, + 51, 51, 51, 51, 51, 51, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 2, 2, 50, 50, 2, 2, 2, 2, 2, 2,135,135, + 135,135,135,135,135,135,135,135,135,135, 2, 2, 2, 2,106,106, + 106,106,106,106,106,106,104,104,104,104,104,104,104,104,104,104, + 104,104, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,104,161,161, + 161,161,161,161,161,161,161,161,161, 2,161,161,161,161,161,161, + 161, 2,161,161, 2,161,161,161, 2,161,161,161,161,161,161,161, + 2,161,161, 2, 2, 2,110,110,110,110,110,110,110,110,110,110, + 110,110,110,110,110, 2,110,110,110,110,110,110, 2, 2, 19, 19, + 19, 19, 19, 19, 2, 19, 19, 2, 19, 19, 19, 19, 19, 19, 47, 47, + 47, 47, 47, 47, 2, 2, 47, 2, 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 2, 47, 47, 2, + 2, 2, 47, 2, 2, 47, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, + 81, 81, 81, 81, 2, 81,120,120,120,120,120,120,120,120,116,116, + 116,116,116,116,116,116,116,116,116,116,116,116,116, 2, 2, 2, + 2, 2, 2, 2, 2,116,128,128,128,128,128,128,128,128,128,128, + 128, 2,128,128, 2, 2, 2, 2, 2,128,128,128,128,128, 66, 66, + 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 2, 2, 2, 66, 72, 72, + 72, 72, 72, 72, 72, 72, 72, 72, 2, 2, 2, 2, 2, 72, 98, 98, + 98, 98, 98, 98, 98, 98, 97, 97, 97, 97, 97, 97, 97, 97, 2, 2, + 2, 2, 97, 97, 97, 97, 2, 2, 97, 97, 97, 97, 97, 97, 57, 57, + 57, 57, 2, 57, 57, 2, 2, 2, 2, 2, 57, 57, 57, 57, 57, 57, + 57, 57, 2, 57, 57, 57, 2, 57, 57, 57, 57, 57, 57, 57, 57, 57, + 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 2, 2, 57, 57, + 57, 2, 2, 2, 2, 57, 57, 2, 2, 2, 2, 2, 2, 2, 88, 88, + 88, 88, 88, 88, 88, 88,117,117,117,117,117,117,117,117,112,112, + 112,112,112,112,112,112,112,112,112,112,112,112,112, 2, 2, 2, + 2,112,112,112,112,112, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, + 78, 78, 78, 78, 2, 2, 2, 78, 78, 78, 78, 78, 78, 78, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 2, 2, 82, 82, + 82, 82, 82, 82, 82, 82, 82, 82, 82, 2, 2, 2, 2, 2,122,122, + 122,122,122,122,122,122,122,122, 2, 2, 2, 2, 2, 2, 2,122, + 122,122,122, 2, 2, 2, 2,122,122,122,122,122,122,122, 89, 89, + 89, 89, 89, 89, 89, 89, 89, 2, 2, 2, 2, 2, 2, 2,130,130, + 130,130,130,130,130,130,130,130,130, 2, 2, 2, 2, 2, 2, 2, + 130,130,130,130,130,130,144,144,144,144,144,144,144,144,144,144, + 2, 2, 2, 2, 2, 2,156,156,156,156,156,156,156,156,156,156, + 2,156,156,156, 2, 2,156,156, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 3, 3, 3,147,147,147,147,147,147,147,147,148,148, + 148,148,148,148,148,148,148,148, 2, 2, 2, 2, 2, 2,158,158, + 158,158,158,158,158,158,158,158, 2, 2, 2, 2, 2, 2,153,153, + 153,153,153,153,153,153,153,153,153,153, 2, 2, 2, 2,149,149, + 149,149,149,149,149,149,149,149,149,149,149,149,149, 2, 94, 94, + 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 2, 2, 2, 2, + 94, 94, 94, 94, 94, 94, 2, 2, 2, 2, 2, 2, 2, 94, 85, 85, + 85, 85, 85, 85, 85, 85, 85, 85, 85, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 85, 2, 2,101,101,101,101,101,101,101,101,101, 2, + 2, 2, 2, 2, 2, 2,101,101, 2, 2, 2, 2, 2, 2, 96, 96, + 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 2, 96, 96,111,111, + 111,111,111,111,111,111,111,111,111,111,111,111,111, 2,100,100, + 100,100,100,100,100,100, 2, 36, 36, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 2, 2, 2,108,108,108,108,108,108,108,108,108,108, + 2,108,108,108,108,108,108,108, 2, 2, 2, 2, 2, 2,129,129, + 129,129,129,129,129, 2,129, 2,129,129,129,129, 2,129,129,129, + 129,129,129,129,129,129,129,129,129,129,129,129, 2,129,129,129, + 2, 2, 2, 2, 2, 2,109,109,109,109,109,109,109,109,109,109, + 109, 2, 2, 2, 2, 2,109,109, 2, 2, 2, 2, 2, 2,107,107, + 107,107, 2,107,107,107,107,107,107,107,107, 2, 2,107,107, 2, + 2,107,107,107,107,107,107,107,107,107,107,107,107,107,107, 2, + 107,107,107,107,107,107,107, 2,107,107, 2,107,107,107,107,107, + 2, 1,107,107,107,107,107, 2, 2,107,107,107, 2, 2,107, 2, + 2, 2, 2, 2, 2,107, 2, 2, 2, 2, 2,107,107,107,107,107, + 107,107, 2, 2,107,107,107,107,107,107,107, 2, 2, 2,137,137, + 137,137,137,137,137,137,137,137,137,137, 2,137,137,137,137,137, + 2, 2, 2, 2, 2, 2,124,124,124,124,124,124,124,124,124,124, + 2, 2, 2, 2, 2, 2,123,123,123,123,123,123,123,123,123,123, + 123,123,123,123, 2, 2,114,114,114,114,114,114,114,114,114,114, + 114,114,114, 2, 2, 2,114,114, 2, 2, 2, 2, 2, 2, 32, 32, + 32, 32, 32, 2, 2, 2,102,102,102,102,102,102,102,102,102,102, + 2, 2, 2, 2, 2, 2,126,126,126,126,126,126,126,126,126,126, + 126, 2, 2,126,126,126,126,126,126,126, 2, 2, 2, 2,126,126, + 126,126,126,126,126, 2,142,142,142,142,142,142,142,142,142,142, + 142,142, 2, 2, 2, 2,125,125,125,125,125,125,125,125,125,125, + 125, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,125,154,154, + 154,154,154,154,154, 2, 2,154, 2, 2,154,154,154,154,154,154, + 154,154, 2,154,154, 2,154,154,154,154,154,154,154,154,154,154, + 154,154,154,154, 2,154,154, 2, 2,154,154,154,154,154,154,154, + 2, 2, 2, 2, 2, 2,150,150,150,150,150,150,150,150, 2, 2, + 150,150,150,150,150,150,150,150,150,150,150, 2, 2, 2,141,141, + 141,141,141,141,141,141,140,140,140,140,140,140,140,140,140,140, + 140, 2, 2, 2, 2, 2,121,121,121,121,121,121,121,121,121, 2, + 2, 2, 2, 2, 2, 2, 7, 7, 2, 2, 2, 2, 2, 2,133,133, + 133,133,133,133,133,133,133, 2,133,133,133,133,133,133,133,133, + 133,133,133,133,133, 2,133,133,133,133,133,133, 2, 2,133,133, + 133,133,133, 2, 2, 2,134,134,134,134,134,134,134,134, 2, 2, + 134,134,134,134,134,134, 2,134,134,134,134,134,134,134,134,134, + 134,134,134,134,134, 2,138,138,138,138,138,138,138, 2,138,138, + 2,138,138,138,138,138,138,138,138,138,138,138,138,138, 2, 2, + 138, 2,138,138, 2,138,138,138, 2, 2, 2, 2, 2, 2,143,143, + 143,143,143,143, 2,143,143, 2,143,143,143,143,143,143,143,143, + 143,143,143,143,143,143,143,143,143,143,143,143,143, 2,143,143, + 2,143,143,143,143,143,143, 2, 2, 2, 2, 2, 2, 2,143,143, + 2, 2, 2, 2, 2, 2,145,145,145,145,145,145,145,145,145, 2, + 2, 2, 2, 2, 2, 2,163,163,163,163,163,163,163,163,163, 2, + 163,163,163,163,163,163,163,163,163, 2, 2, 2,163,163,163,163, + 2, 2, 2, 2, 2, 2, 86, 2, 2, 2, 2, 2, 2, 2, 22, 22, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 22, 63, 63, + 63, 63, 63, 63, 63, 63, 63, 63, 2, 2, 2, 2, 2, 2, 63, 63, + 63, 63, 63, 63, 63, 2, 63, 63, 63, 63, 63, 2, 2, 2, 63, 63, + 63, 63, 2, 2, 2, 2,157,157,157,157,157,157,157,157,157,157, + 157, 2, 2, 2, 2, 2, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, + 80, 80, 80, 80, 2, 2,127,127,127,127,127,127,127,127,127,127, + 127,127,127,127,127, 2, 79, 2, 2, 2, 2, 2, 2, 2,115,115, + 115,115,115,115,115,115,115,115,115,115,115,115,115, 2,115,115, + 2, 2, 2, 2,115,115,159,159,159,159,159,159,159,159,159,159, + 159,159,159,159,159, 2,159,159, 2, 2, 2, 2, 2, 2,103,103, + 103,103,103,103,103,103,103,103,103,103,103,103, 2, 2,119,119, + 119,119,119,119,119,119,119,119,119,119,119,119, 2, 2,119,119, + 2,119,119,119,119,119, 2, 2, 2, 2, 2,119,119,119,146,146, + 146,146,146,146,146,146,146,146,146, 2, 2, 2, 2, 2, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, 99, 2, 2, 2, 2, 99, 2, 2, + 2, 2, 2, 2, 2, 99,136,139, 13, 13,155, 2, 2, 2,136,136, + 136,136,136,136,136,136,155,155,155,155,155,155,155,155,155,155, + 155,155,155,155, 2, 2,136, 2, 2, 2, 2, 2, 2, 2, 17, 17, + 17, 17, 2, 17, 17, 17, 17, 17, 17, 17, 2, 17, 17, 2, 17, 15, + 15, 15, 15, 15, 15, 15, 17, 17, 17, 2, 2, 2, 2, 2, 2, 2, + 15, 2, 2, 2, 2, 2, 15, 15, 15, 2, 2, 17, 2, 2, 2, 2, + 2, 2, 17, 17, 17, 17,139,139,139,139,139,139,139,139,139,139, + 139,139, 2, 2, 2, 2,105,105,105,105,105,105,105,105,105,105, + 105, 2, 2, 2, 2, 2,105,105,105,105,105, 2, 2, 2,105, 2, + 2, 2, 2, 2, 2, 2,105,105, 2, 2,105,105,105,105, 1, 1, + 1, 1, 1, 1, 2, 2, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, + 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 0, 0, 2, 2, 0, 2, 2, 0, 0, 2, 2, 0, + 0, 0, 0, 2, 0, 0, 0, 0, 2, 0, 2, 0, 0, 0, 0, 0, + 0, 0, 2, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, + 2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 2, 0, 2, 2, 2, + 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0,131,131, + 131,131,131,131,131,131,131,131,131,131, 2, 2, 2, 2, 2, 2, + 2,131,131,131,131,131, 2,131,131,131,131,131,131,131, 2, 2, + 2, 2, 2, 19, 19, 19, 56, 56, 56, 56, 56, 56, 56, 2, 56, 2, + 2, 56, 56, 56, 56, 56, 56, 56, 2, 56, 56, 2, 56, 56, 56, 56, + 56, 2, 2, 2, 2, 2, 6, 6, 6, 6, 6, 6, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 6,151,151,151,151,151,151,151,151,151,151, + 151,151,151, 2, 2, 2,151,151,151,151,151,151, 2, 2,151,151, + 2, 2, 2, 2,151,151,160,160,160,160,160,160,160,160,160,160, + 160,160,160,160,160, 2,152,152,152,152,152,152,152,152,152,152, + 2, 2, 2, 2, 2,152,164,164,164,164,164,164,164,164,164,164, + 2, 2, 2, 2, 2, 2, 30, 30, 30, 30, 2, 30, 30, 2,113,113, + 113,113,113,113,113,113,113,113,113,113,113, 2, 2,113,113,113, + 113,113,113,113,113, 2,132,132,132,132,132,132,132,132,132,132, + 132,132, 2, 2, 2, 2,132,132, 2, 2, 2, 2,132,132, 3, 3, + 3, 3, 2, 3, 3, 3, 2, 3, 3, 2, 3, 2, 2, 3, 2, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 3, 3, 3, 3, 2, 3, + 2, 3, 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 3, 2, 3, + 2, 3, 2, 3, 3, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, + 3, 3, 3, 2, 3, 2, 3, 3, 2, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 2, 2, 2, 2, 2, 3, 3, 3, 2, 3, 3, 3, 2, 2, + 2, 2, 2, 2, 0, 0, 15, 0, 0, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, 0, 0, 13, 2, + 2, 2, 2, 2, 2, 2, 13, 13, 13, 2, 2, 2, 2, 2, 2, 0, + 2, 2, 2, 2, 2, 2, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 9, 9, 9, 10, 9, 11, 12, 13, 9, 9, 9, 14, 9, 9, 15, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 16, 17, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 18, 19, + 20, 9, 21, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 22, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 23, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, 0, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 23, 0, 0, 24, 25, 26, 27, 28, + 29, 30, 0, 0, 31, 32, 0, 33, 0, 34, 0, 35, 0, 0, 0, 0, + 36, 37, 38, 39, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 40, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 41, 42, 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, 43, 44, 0, 45, 0, 0, 0, 0, 0, 0, + 46, 47, 0, 0, 0, 0, 0, 48, 0, 49, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 50, 51, 0, 0, 0, 52, 0, 0, + 53, 0, 0, 0, 0, 0, 0, 0, 54, 0, 0, 0, 0, 0, 0, 0, + 55, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, + 0, 57, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 58, 59, 60, 61, 62, 63, 64, 65, + 0, 0, 0, 0, 0, 0, 66, 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, 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, 67, 68, 0, 69, 70, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, + 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, + 99,100,101,102,103, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0,104, 0, 0, 0, 0, 0, 0,105,106, 0, + 107, 0, 0, 0,108, 0,109, 0,110, 0,111,112,113, 0,114, 0, + 0, 0,115, 0, 0, 0,116, 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,117, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0,118,119,120,121, 0,122,123,124, + 125,126, 0,127, 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,128,129,130,131,132,133,134,135,136,137,138,139, + 140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155, + 156,157, 0, 0, 0,158,159,160,161, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,162,163, 0, + 0, 0, 0, 0, 0, 0,164, 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,165, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,166, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,167, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0,168, 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,169,170, 0, 0, 0, 0,171, + 172, 0, 0, 0,173,174,175,176,177,178,179,180,181,182,183,184, + 185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200, + 201,202,203,204,205,206, 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, + 1, 2, 3, 4, +}; +static const uint16_t +_hb_ucd_u16[9320] = +{ + 0, 0, 1, 2, 3, 4, 5, 6, 0, 0, 7, 8, 9, 10, 11, 12, + 13, 13, 13, 14, 15, 13, 13, 16, 17, 18, 19, 20, 21, 22, 13, 23, + 13, 13, 13, 24, 25, 11, 11, 11, 11, 26, 11, 27, 28, 29, 30, 31, + 32, 32, 32, 32, 32, 32, 32, 33, 34, 35, 36, 11, 37, 38, 13, 39, + 9, 9, 9, 11, 11, 11, 13, 13, 40, 13, 13, 13, 41, 13, 13, 13, + 13, 13, 13, 42, 9, 43, 11, 11, 44, 45, 32, 46, 47, 48, 49, 50, + 51, 52, 48, 48, 53, 32, 54, 55, 48, 48, 48, 48, 48, 56, 57, 58, + 59, 60, 48, 32, 61, 48, 48, 48, 48, 48, 62, 63, 64, 48, 65, 66, + 48, 67, 68, 69, 48, 70, 71, 48, 72, 73, 48, 48, 74, 32, 75, 32, + 76, 48, 48, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, + 90, 83, 84, 91, 92, 93, 94, 95, 96, 97, 84, 98, 99, 100, 88, 101, + 102, 83, 84, 103, 104, 105, 88, 106, 107, 108, 109, 110, 111, 112, 94, 113, + 114, 115, 84, 116, 117, 118, 88, 119, 120, 115, 84, 121, 122, 123, 88, 124, + 125, 115, 48, 126, 127, 128, 88, 129, 130, 131, 48, 132, 133, 134, 94, 135, + 136, 48, 48, 137, 138, 139, 140, 140, 141, 48, 142, 143, 144, 145, 140, 140, + 146, 147, 148, 149, 150, 48, 151, 152, 153, 154, 32, 155, 156, 157, 140, 140, + 48, 48, 158, 159, 160, 161, 162, 163, 164, 165, 9, 9, 166, 11, 11, 167, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 168, 169, 48, 48, + 168, 48, 48, 170, 171, 172, 48, 48, 48, 171, 48, 48, 48, 173, 174, 175, + 48, 176, 9, 9, 9, 9, 9, 177, 178, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 179, 48, 180, 181, 48, 48, 48, 48, 182, 183, + 48, 184, 48, 185, 48, 186, 187, 188, 48, 48, 48, 189, 190, 191, 192, 193, + 194, 192, 48, 48, 195, 48, 48, 196, 197, 48, 198, 48, 48, 48, 48, 199, + 48, 200, 201, 202, 203, 48, 204, 205, 48, 48, 206, 48, 207, 208, 209, 209, + 48, 210, 48, 48, 48, 211, 212, 213, 192, 192, 214, 215, 216, 140, 140, 140, + 217, 48, 48, 218, 219, 160, 220, 221, 222, 48, 223, 64, 48, 48, 224, 225, + 48, 48, 226, 227, 228, 64, 48, 229, 230, 9, 9, 231, 232, 233, 234, 235, + 11, 11, 236, 27, 27, 27, 237, 238, 11, 239, 27, 27, 32, 32, 32, 32, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 240, 13, 13, 13, 13, 13, 13, + 241, 242, 241, 241, 242, 243, 241, 244, 245, 245, 245, 246, 247, 248, 249, 250, + 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 261, 262, 263, 264, 265, + 266, 267, 268, 269, 270, 271, 272, 272, 273, 274, 275, 209, 276, 277, 209, 278, + 279, 279, 279, 279, 279, 279, 279, 279, 280, 209, 281, 209, 209, 209, 209, 282, + 209, 283, 279, 284, 209, 285, 286, 209, 209, 209, 287, 140, 288, 140, 271, 271, + 271, 289, 209, 209, 209, 209, 290, 271, 209, 209, 209, 209, 209, 209, 209, 209, + 209, 209, 209, 291, 292, 209, 209, 293, 209, 209, 209, 209, 209, 209, 294, 209, + 209, 209, 209, 209, 209, 209, 295, 296, 271, 297, 209, 209, 298, 279, 299, 279, + 300, 301, 279, 279, 279, 302, 279, 303, 209, 209, 209, 279, 304, 209, 209, 305, + 209, 306, 209, 209, 209, 209, 209, 209, 9, 9, 9, 11, 11, 11, 307, 308, + 13, 13, 13, 13, 13, 13, 309, 310, 11, 11, 311, 48, 48, 48, 312, 313, + 48, 314, 315, 315, 315, 315, 32, 32, 316, 317, 318, 319, 320, 321, 140, 140, + 209, 322, 209, 209, 209, 209, 209, 323, 209, 209, 209, 209, 209, 324, 140, 325, + 326, 327, 328, 329, 136, 48, 48, 48, 48, 330, 178, 48, 48, 48, 48, 331, + 332, 48, 48, 136, 48, 48, 48, 48, 200, 333, 48, 48, 209, 209, 323, 48, + 209, 334, 335, 209, 336, 337, 209, 209, 335, 209, 209, 337, 209, 209, 209, 209, + 48, 48, 48, 48, 209, 209, 209, 209, 48, 338, 48, 48, 48, 48, 48, 48, + 151, 209, 209, 209, 287, 48, 48, 229, 339, 48, 340, 140, 13, 13, 341, 342, + 13, 343, 48, 48, 48, 48, 344, 345, 31, 346, 347, 348, 13, 13, 13, 349, + 350, 351, 352, 353, 354, 355, 140, 356, 357, 48, 358, 359, 48, 48, 48, 360, + 361, 48, 48, 362, 363, 192, 32, 364, 64, 48, 365, 48, 366, 367, 48, 151, + 76, 48, 48, 368, 369, 370, 371, 372, 48, 48, 373, 374, 375, 376, 48, 377, + 48, 48, 48, 378, 379, 380, 381, 382, 383, 384, 315, 11, 11, 385, 386, 11, + 11, 11, 11, 11, 48, 48, 387, 192, 48, 48, 388, 48, 389, 48, 48, 206, + 390, 390, 390, 390, 390, 390, 390, 390, 391, 391, 391, 391, 391, 391, 391, 391, + 48, 48, 48, 48, 48, 48, 204, 48, 48, 48, 48, 48, 48, 207, 140, 140, + 392, 393, 394, 395, 396, 48, 48, 48, 48, 48, 48, 397, 398, 399, 48, 48, + 48, 48, 48, 400, 209, 48, 48, 48, 48, 401, 48, 48, 402, 140, 140, 403, + 32, 404, 32, 405, 406, 407, 408, 409, 48, 48, 48, 48, 48, 48, 48, 410, + 411, 2, 3, 4, 5, 412, 413, 414, 48, 415, 48, 200, 416, 417, 418, 419, + 420, 48, 172, 421, 204, 204, 140, 140, 48, 48, 48, 48, 48, 48, 48, 71, + 422, 271, 271, 423, 272, 272, 272, 424, 425, 426, 427, 140, 140, 209, 209, 428, + 140, 140, 140, 140, 140, 140, 140, 140, 48, 151, 48, 48, 48, 100, 429, 430, + 48, 48, 431, 48, 432, 48, 48, 433, 48, 434, 48, 48, 435, 436, 140, 140, + 9, 9, 437, 11, 11, 48, 48, 48, 48, 204, 192, 9, 9, 438, 11, 439, + 48, 48, 440, 48, 48, 48, 441, 442, 442, 443, 444, 445, 140, 140, 140, 140, + 48, 48, 48, 314, 48, 199, 440, 140, 446, 27, 27, 447, 140, 140, 140, 140, + 448, 48, 48, 449, 48, 450, 48, 451, 48, 200, 452, 140, 140, 140, 48, 453, + 48, 454, 48, 455, 140, 140, 140, 140, 48, 48, 48, 456, 271, 457, 271, 271, + 458, 459, 48, 460, 461, 462, 48, 463, 48, 464, 140, 140, 465, 48, 466, 467, + 48, 48, 48, 468, 48, 469, 48, 470, 48, 471, 472, 140, 140, 140, 140, 140, + 48, 48, 48, 48, 196, 140, 140, 140, 9, 9, 9, 473, 11, 11, 11, 474, + 48, 48, 475, 192, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 271, 476, + 48, 48, 477, 478, 140, 140, 140, 479, 48, 464, 480, 48, 62, 481, 140, 48, + 482, 140, 140, 48, 483, 140, 48, 314, 484, 48, 48, 485, 486, 457, 487, 488, + 222, 48, 48, 489, 490, 48, 196, 192, 491, 48, 492, 493, 494, 48, 48, 495, + 222, 48, 48, 496, 497, 498, 499, 500, 48, 97, 501, 502, 503, 140, 140, 140, + 504, 505, 506, 48, 48, 507, 508, 192, 509, 83, 84, 510, 511, 512, 513, 514, + 48, 48, 48, 515, 516, 517, 478, 140, 48, 48, 48, 518, 519, 192, 140, 140, + 48, 48, 520, 521, 522, 523, 140, 140, 48, 48, 48, 524, 525, 192, 526, 140, + 48, 48, 527, 528, 192, 140, 140, 140, 48, 173, 529, 530, 314, 140, 140, 140, + 48, 48, 501, 531, 140, 140, 140, 140, 140, 140, 9, 9, 11, 11, 148, 532, + 533, 534, 48, 535, 536, 192, 140, 140, 140, 140, 537, 48, 48, 538, 539, 140, + 540, 48, 48, 541, 542, 543, 48, 48, 544, 545, 546, 48, 48, 48, 48, 196, + 547, 140, 140, 140, 140, 140, 140, 140, 84, 48, 520, 548, 549, 148, 175, 550, + 48, 551, 552, 553, 140, 140, 140, 140, 554, 48, 48, 555, 556, 192, 557, 48, + 558, 559, 192, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 48, 560, + 561, 115, 48, 562, 563, 192, 140, 140, 140, 140, 140, 100, 271, 564, 565, 566, + 48, 207, 140, 140, 140, 140, 140, 140, 272, 272, 272, 272, 272, 272, 567, 568, + 48, 48, 48, 48, 388, 140, 140, 140, 140, 48, 48, 48, 48, 48, 48, 569, + 48, 48, 48, 570, 571, 572, 140, 140, 48, 48, 48, 48, 314, 140, 140, 140, + 48, 48, 48, 196, 48, 200, 370, 48, 48, 48, 48, 200, 192, 48, 204, 573, + 48, 48, 48, 574, 575, 576, 577, 578, 48, 140, 140, 140, 140, 140, 140, 140, + 140, 140, 140, 140, 9, 9, 11, 11, 271, 579, 140, 140, 140, 140, 140, 140, + 48, 48, 48, 48, 580, 581, 582, 582, 583, 584, 140, 140, 140, 140, 585, 586, + 48, 48, 48, 48, 48, 48, 48, 440, 48, 48, 48, 48, 48, 199, 140, 140, + 196, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 587, + 48, 48, 588, 589, 140, 590, 591, 48, 48, 48, 48, 48, 48, 48, 48, 206, + 48, 48, 48, 48, 48, 48, 71, 151, 196, 592, 593, 140, 140, 140, 140, 140, + 32, 32, 594, 32, 595, 209, 209, 209, 209, 209, 209, 209, 323, 140, 140, 140, + 209, 209, 209, 209, 209, 209, 209, 324, 209, 209, 596, 209, 209, 209, 597, 598, + 599, 209, 600, 209, 209, 209, 288, 140, 209, 209, 209, 209, 601, 140, 140, 140, + 140, 140, 140, 140, 271, 602, 271, 602, 209, 209, 209, 209, 209, 287, 271, 461, + 9, 603, 11, 604, 605, 606, 241, 9, 607, 608, 609, 610, 611, 9, 603, 11, + 612, 613, 11, 614, 615, 616, 617, 9, 618, 11, 9, 603, 11, 604, 605, 11, + 241, 9, 607, 617, 9, 618, 11, 9, 603, 11, 619, 9, 620, 621, 622, 623, + 11, 624, 9, 625, 626, 627, 628, 11, 629, 9, 630, 11, 631, 632, 632, 632, + 32, 32, 32, 633, 32, 32, 634, 635, 636, 637, 45, 140, 140, 140, 140, 140, + 638, 639, 640, 140, 140, 140, 140, 140, 641, 642, 643, 27, 27, 27, 644, 140, + 645, 140, 140, 140, 140, 140, 140, 140, 48, 48, 151, 646, 647, 140, 140, 140, + 140, 48, 648, 140, 48, 48, 649, 650, 140, 140, 140, 140, 140, 48, 651, 192, + 140, 140, 140, 140, 140, 140, 652, 200, 48, 48, 48, 48, 653, 595, 140, 140, + 9, 9, 607, 11, 654, 370, 140, 140, 140, 140, 140, 140, 140, 140, 140, 499, + 271, 271, 655, 656, 140, 140, 140, 140, 499, 271, 657, 658, 140, 140, 140, 140, + 659, 48, 660, 661, 662, 663, 664, 665, 666, 206, 667, 206, 140, 140, 140, 668, + 209, 209, 325, 209, 209, 209, 209, 209, 209, 323, 334, 669, 669, 669, 209, 324, + 670, 209, 209, 209, 209, 209, 209, 209, 209, 209, 671, 140, 140, 140, 672, 209, + 673, 209, 209, 325, 674, 675, 324, 140, 209, 209, 209, 209, 209, 209, 209, 676, + 209, 209, 209, 209, 209, 677, 426, 426, 209, 209, 209, 209, 209, 209, 209, 678, + 209, 209, 209, 209, 209, 176, 325, 427, 325, 209, 209, 209, 679, 176, 209, 209, + 679, 209, 671, 675, 140, 140, 140, 140, 209, 209, 209, 209, 209, 323, 671, 426, + 674, 209, 209, 680, 681, 325, 674, 674, 209, 682, 209, 209, 288, 140, 140, 192, + 48, 48, 48, 48, 48, 48, 140, 140, 48, 48, 48, 207, 48, 48, 48, 48, + 48, 204, 48, 48, 48, 48, 48, 48, 48, 48, 478, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 100, 140, 48, 204, 140, 140, 140, 140, 140, 140, + 48, 48, 48, 48, 71, 48, 48, 48, 48, 48, 48, 140, 140, 140, 140, 140, + 683, 140, 570, 570, 570, 570, 570, 570, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 140, 391, 391, 391, 391, 391, 391, 391, 684, + 391, 391, 391, 391, 391, 391, 391, 685, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 2, 2, 3, 1, 2, 2, 3, 0, 0, 0, 0, 0, 4, 0, 4, + 2, 2, 5, 2, 2, 2, 5, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, + 0, 0, 0, 0, 7, 8, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 10, 11, 12, 13, 14, 14, 15, 14, 14, 14, + 14, 14, 14, 14, 16, 17, 14, 14, 18, 18, 18, 18, 18, 18, 18, 18, + 18, 18, 18, 18, 18, 18, 18, 18, 19, 18, 18, 18, 18, 18, 18, 18, + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 20, 21, + 21, 21, 22, 20, 21, 21, 21, 21, 21, 23, 24, 25, 25, 25, 25, 25, + 25, 26, 25, 25, 25, 27, 28, 26, 29, 30, 31, 32, 31, 31, 31, 31, + 33, 34, 35, 31, 31, 31, 36, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 29, 31, 31, 31, 31, 37, 38, 37, 37, 37, 37, 37, 37, + 37, 39, 31, 31, 31, 31, 31, 31, 40, 40, 40, 40, 40, 40, 41, 26, + 42, 42, 42, 42, 42, 42, 42, 43, 44, 44, 44, 44, 44, 45, 44, 46, + 47, 47, 47, 48, 37, 49, 31, 31, 31, 50, 51, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 52, 31, 31, 31, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 54, 53, 55, 53, 53, 53, 56, 57, 58, 59, 59, 60, 61, 62, + 57, 63, 64, 65, 66, 59, 59, 67, 68, 69, 70, 71, 71, 72, 73, 74, + 69, 75, 76, 77, 78, 71, 79, 26, 80, 81, 82, 83, 83, 84, 85, 86, + 81, 87, 88, 26, 89, 83, 90, 91, 92, 93, 94, 95, 95, 96, 97, 98, + 93, 99, 100, 101, 102, 95, 95, 26, 103, 104, 105, 106, 107, 104, 108, 109, + 104, 105, 110, 26, 111, 108, 108, 112, 113, 114, 115, 113, 113, 115, 113, 116, + 114, 117, 118, 119, 120, 113, 121, 113, 122, 123, 124, 122, 122, 124, 125, 126, + 123, 127, 128, 128, 129, 122, 130, 26, 131, 132, 133, 131, 131, 131, 131, 131, + 132, 133, 134, 131, 135, 131, 131, 131, 136, 137, 138, 139, 137, 137, 140, 141, + 138, 142, 143, 137, 144, 137, 145, 26, 146, 147, 147, 147, 147, 147, 147, 148, + 147, 147, 147, 149, 26, 26, 26, 26, 150, 151, 152, 152, 153, 152, 152, 154, + 155, 156, 152, 157, 26, 26, 26, 26, 158, 158, 158, 158, 158, 158, 158, 158, + 158, 159, 158, 158, 158, 160, 159, 158, 158, 158, 158, 159, 158, 158, 158, 161, + 158, 161, 162, 163, 26, 26, 26, 26, 164, 164, 164, 164, 164, 164, 164, 164, + 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 165, 165, 165, 165, + 166, 167, 165, 165, 165, 165, 165, 168, 169, 169, 169, 169, 169, 169, 169, 169, + 169, 169, 169, 169, 169, 169, 169, 169, 170, 170, 170, 170, 170, 170, 170, 170, + 170, 171, 172, 171, 170, 170, 170, 170, 170, 171, 170, 170, 170, 170, 171, 172, + 171, 170, 172, 170, 170, 170, 170, 170, 170, 170, 171, 170, 170, 170, 170, 170, + 170, 170, 170, 173, 170, 170, 170, 174, 170, 170, 170, 175, 176, 176, 176, 176, + 176, 176, 176, 176, 176, 176, 177, 177, 178, 178, 178, 178, 178, 178, 178, 178, + 178, 178, 178, 178, 178, 178, 178, 178, 179, 179, 179, 180, 181, 181, 181, 181, + 181, 181, 181, 181, 181, 182, 181, 183, 184, 184, 185, 186, 187, 187, 188, 26, + 189, 189, 190, 26, 191, 192, 193, 26, 194, 194, 194, 194, 194, 194, 194, 194, + 194, 194, 194, 195, 194, 196, 194, 196, 197, 198, 198, 199, 198, 198, 198, 198, + 198, 198, 198, 198, 198, 198, 198, 200, 198, 198, 198, 198, 198, 201, 178, 178, + 178, 178, 178, 178, 178, 178, 202, 26, 203, 203, 203, 204, 203, 205, 203, 205, + 206, 203, 207, 207, 207, 208, 209, 26, 210, 210, 210, 210, 210, 211, 210, 210, + 210, 212, 210, 213, 194, 194, 194, 194, 214, 214, 214, 215, 216, 216, 216, 216, + 216, 216, 216, 217, 216, 216, 216, 218, 216, 219, 216, 219, 216, 220, 9, 9, + 9, 221, 26, 26, 26, 26, 26, 26, 222, 222, 222, 222, 222, 222, 222, 222, + 222, 223, 222, 222, 222, 222, 222, 224, 225, 225, 225, 225, 225, 225, 225, 225, + 226, 226, 226, 226, 226, 226, 227, 228, 229, 229, 229, 229, 229, 229, 229, 230, + 229, 231, 232, 232, 232, 232, 232, 232, 18, 233, 165, 165, 165, 165, 165, 234, + 225, 26, 235, 9, 236, 237, 238, 239, 2, 2, 2, 2, 240, 241, 2, 2, + 2, 2, 2, 242, 243, 244, 2, 245, 2, 2, 2, 2, 2, 2, 2, 246, + 9, 9, 9, 9, 9, 9, 9, 9, 14, 14, 247, 247, 14, 14, 14, 14, + 247, 247, 14, 248, 14, 14, 14, 247, 14, 14, 14, 14, 14, 14, 249, 14, + 249, 14, 250, 251, 14, 14, 252, 253, 0, 254, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 0, 256, 257, 0, 258, 2, 259, 0, 0, 0, 0, + 260, 26, 9, 9, 9, 9, 261, 26, 0, 0, 0, 0, 262, 263, 4, 0, + 0, 264, 0, 0, 2, 2, 2, 2, 2, 265, 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, 258, 26, 26, 26, + 0, 266, 26, 26, 0, 0, 0, 0, 267, 267, 267, 267, 267, 267, 267, 267, + 267, 267, 267, 267, 267, 267, 267, 267, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 268, 0, 0, 0, 269, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 270, 270, 270, 270, 270, 270, 270, 270, + 270, 270, 270, 270, 2, 2, 2, 2, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 271, 272, 165, 165, 165, 165, 166, 167, 273, 273, + 273, 273, 273, 273, 273, 274, 275, 274, 170, 170, 172, 26, 172, 172, 172, 172, + 172, 172, 172, 172, 18, 18, 18, 18, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 276, 26, 26, 26, 26, 277, 277, 277, 278, 277, 277, 277, 277, + 277, 277, 277, 277, 277, 277, 279, 26, 277, 277, 277, 277, 277, 277, 277, 277, + 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, + 277, 277, 280, 26, 26, 26, 0, 281, 282, 0, 0, 0, 283, 284, 0, 285, + 286, 287, 287, 287, 287, 287, 287, 287, 287, 287, 288, 289, 290, 291, 291, 291, + 291, 291, 291, 291, 291, 291, 291, 292, 293, 294, 294, 294, 294, 294, 295, 169, + 169, 169, 169, 169, 169, 169, 169, 169, 169, 296, 0, 0, 294, 294, 294, 294, + 0, 0, 0, 0, 281, 26, 291, 291, 169, 169, 169, 296, 0, 0, 0, 0, + 0, 0, 0, 0, 169, 169, 169, 297, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 291, 291, 291, 291, 291, 298, 291, 291, 291, 291, 291, 291, 291, 291, + 291, 291, 291, 0, 0, 0, 0, 0, 277, 277, 277, 277, 277, 277, 277, 277, + 0, 0, 0, 0, 0, 0, 0, 0, 299, 299, 299, 299, 299, 299, 299, 299, + 299, 299, 299, 299, 299, 299, 299, 299, 299, 300, 299, 299, 299, 299, 299, 299, + 301, 26, 302, 302, 302, 302, 302, 302, 303, 303, 303, 303, 303, 303, 303, 303, + 303, 303, 303, 303, 303, 303, 303, 303, 303, 303, 303, 303, 303, 304, 26, 26, + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 305, 305, 305, 305, + 305, 305, 305, 305, 305, 305, 305, 26, 0, 0, 0, 0, 306, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 307, 2, 2, 2, 2, 2, 2, + 2, 308, 309, 310, 26, 26, 311, 2, 312, 312, 312, 312, 312, 313, 0, 314, + 315, 315, 315, 315, 315, 315, 315, 26, 316, 316, 316, 316, 316, 316, 316, 316, + 317, 318, 316, 319, 53, 53, 53, 53, 320, 320, 320, 320, 320, 321, 322, 322, + 322, 322, 323, 324, 169, 169, 169, 325, 326, 326, 326, 326, 326, 326, 326, 326, + 326, 327, 326, 328, 164, 164, 164, 329, 330, 330, 330, 330, 330, 330, 331, 26, + 330, 332, 330, 333, 164, 164, 164, 164, 334, 334, 334, 334, 334, 334, 334, 334, + 335, 26, 26, 336, 337, 337, 338, 26, 339, 339, 339, 26, 172, 172, 2, 2, + 2, 2, 2, 340, 341, 342, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, + 337, 337, 337, 337, 337, 343, 337, 344, 169, 169, 169, 169, 345, 26, 169, 169, + 296, 346, 169, 169, 169, 169, 169, 345, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 277, 277, 277, 277, 277, 277, 277, 277, + 277, 277, 277, 277, 277, 280, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, + 277, 277, 277, 347, 26, 26, 26, 26, 348, 26, 349, 350, 25, 25, 351, 352, + 353, 25, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 354, 26, 355, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 356, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 357, 31, 31, 31, 31, 31, + 31, 358, 26, 26, 26, 26, 31, 31, 9, 9, 0, 314, 9, 359, 0, 0, + 0, 0, 360, 0, 258, 281, 361, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 362, 363, 0, 0, 0, 1, 2, 2, 3, + 1, 2, 2, 3, 364, 291, 290, 291, 291, 291, 291, 365, 169, 169, 169, 296, + 366, 366, 366, 367, 258, 258, 26, 368, 369, 370, 369, 369, 371, 369, 369, 372, + 369, 373, 369, 373, 26, 26, 26, 26, 369, 369, 369, 369, 369, 369, 369, 369, + 369, 369, 369, 369, 369, 369, 369, 374, 375, 0, 0, 0, 0, 0, 376, 0, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 253, 0, 377, 378, 26, 26, 26, + 26, 26, 0, 0, 0, 0, 0, 379, 380, 380, 380, 381, 382, 382, 382, 382, + 382, 382, 383, 26, 384, 0, 0, 281, 385, 385, 385, 385, 386, 387, 388, 388, + 388, 389, 390, 390, 390, 390, 390, 391, 392, 392, 392, 393, 394, 394, 394, 394, + 395, 394, 396, 26, 26, 26, 26, 26, 397, 397, 397, 397, 397, 397, 397, 397, + 397, 397, 398, 398, 398, 398, 398, 398, 399, 399, 399, 400, 399, 401, 402, 402, + 402, 402, 403, 402, 402, 402, 402, 403, 404, 404, 404, 404, 404, 26, 405, 405, + 405, 405, 405, 405, 406, 407, 408, 409, 408, 409, 410, 408, 411, 408, 411, 412, + 26, 26, 26, 26, 26, 26, 26, 26, 413, 413, 413, 413, 413, 413, 413, 413, + 413, 413, 413, 413, 413, 413, 413, 413, 413, 413, 413, 413, 413, 413, 414, 26, + 413, 413, 415, 26, 413, 26, 26, 26, 416, 2, 2, 2, 2, 2, 417, 308, + 26, 26, 26, 26, 26, 26, 26, 26, 418, 419, 420, 420, 420, 420, 421, 422, + 423, 423, 424, 423, 425, 425, 425, 425, 426, 426, 426, 427, 428, 426, 26, 26, + 26, 26, 26, 26, 429, 429, 430, 431, 432, 432, 432, 433, 434, 434, 434, 435, + 26, 26, 26, 26, 26, 26, 26, 26, 436, 436, 436, 436, 437, 437, 437, 438, + 437, 437, 439, 437, 437, 437, 437, 437, 440, 441, 442, 443, 444, 444, 445, 446, + 444, 447, 444, 447, 448, 448, 448, 448, 449, 449, 449, 449, 26, 26, 26, 26, + 450, 450, 450, 450, 451, 452, 451, 26, 453, 453, 453, 453, 453, 453, 454, 455, + 456, 456, 457, 456, 458, 458, 459, 458, 460, 460, 461, 462, 26, 463, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 464, 464, 464, 464, 464, 464, 464, 464, + 464, 465, 26, 26, 26, 26, 26, 26, 466, 466, 466, 466, 466, 466, 467, 26, + 466, 466, 466, 466, 466, 466, 467, 468, 469, 469, 469, 469, 469, 26, 469, 470, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 31, 31, 31, 50, 471, 471, 471, 471, 471, 472, 473, 26, + 26, 26, 26, 26, 26, 26, 26, 474, 475, 475, 475, 475, 475, 26, 476, 476, + 476, 476, 476, 477, 26, 26, 478, 478, 478, 479, 26, 26, 26, 26, 480, 480, + 480, 481, 26, 26, 482, 482, 483, 26, 484, 484, 484, 484, 484, 484, 484, 484, + 484, 485, 486, 484, 484, 484, 485, 487, 488, 488, 488, 488, 488, 488, 488, 488, + 489, 490, 491, 491, 491, 492, 491, 493, 494, 494, 494, 494, 494, 494, 495, 494, + 494, 26, 496, 496, 496, 496, 497, 26, 498, 498, 498, 498, 498, 498, 498, 498, + 498, 498, 498, 498, 499, 137, 500, 26, 501, 501, 502, 501, 501, 501, 501, 501, + 503, 26, 26, 26, 26, 26, 26, 26, 504, 505, 506, 507, 506, 508, 509, 509, + 509, 509, 509, 509, 509, 510, 509, 511, 512, 513, 514, 515, 515, 516, 517, 518, + 513, 519, 520, 521, 522, 523, 523, 26, 524, 524, 524, 524, 524, 524, 524, 524, + 524, 524, 524, 525, 526, 26, 26, 26, 527, 527, 527, 527, 527, 527, 527, 527, + 527, 26, 527, 528, 26, 26, 26, 26, 529, 529, 529, 529, 529, 529, 530, 529, + 529, 529, 529, 530, 26, 26, 26, 26, 531, 531, 531, 531, 531, 531, 531, 531, + 532, 26, 531, 533, 198, 534, 26, 26, 535, 535, 535, 535, 535, 535, 535, 536, + 535, 536, 26, 26, 26, 26, 26, 26, 537, 537, 537, 538, 537, 539, 537, 537, + 540, 26, 26, 26, 26, 26, 26, 26, 541, 541, 541, 541, 541, 541, 541, 542, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 543, 543, 543, 543, + 543, 543, 543, 543, 543, 543, 544, 545, 546, 547, 548, 549, 549, 549, 550, 551, + 546, 26, 549, 552, 26, 26, 26, 26, 26, 26, 26, 26, 553, 554, 553, 553, + 553, 553, 553, 554, 555, 26, 26, 26, 556, 556, 556, 556, 556, 556, 556, 556, + 556, 26, 557, 557, 557, 557, 557, 557, 557, 557, 557, 557, 558, 26, 178, 178, + 559, 559, 559, 559, 559, 559, 559, 560, 53, 561, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 562, 563, 562, 562, 562, 562, 564, 562, + 565, 26, 562, 562, 562, 566, 567, 567, 567, 567, 568, 567, 567, 569, 570, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 571, 572, 573, 573, 573, 573, 571, 574, + 573, 26, 573, 575, 576, 577, 578, 578, 578, 579, 580, 581, 578, 582, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 583, 583, 583, 584, 585, 585, 586, 585, 585, 585, 585, 587, + 585, 585, 585, 588, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 589, 26, + 108, 108, 108, 108, 108, 108, 590, 591, 592, 592, 592, 592, 592, 592, 592, 592, + 592, 592, 592, 592, 592, 592, 592, 592, 592, 592, 592, 593, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 592, 592, 592, 592, 592, 592, 592, 592, + 592, 592, 592, 592, 592, 594, 595, 26, 592, 592, 592, 592, 592, 592, 592, 592, + 596, 26, 26, 26, 26, 26, 26, 26, 26, 26, 597, 597, 597, 597, 597, 597, + 597, 597, 597, 597, 597, 597, 598, 26, 599, 599, 599, 599, 599, 599, 599, 599, + 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, + 599, 599, 600, 26, 26, 26, 26, 26, 601, 601, 601, 601, 601, 601, 601, 601, + 601, 601, 601, 601, 601, 601, 601, 601, 601, 601, 601, 601, 601, 601, 601, 601, + 602, 26, 26, 26, 26, 26, 26, 26, 305, 305, 305, 305, 305, 305, 305, 305, + 305, 305, 305, 305, 305, 305, 305, 305, 305, 305, 305, 305, 305, 305, 305, 603, + 604, 604, 604, 605, 604, 606, 607, 607, 607, 607, 607, 607, 607, 607, 607, 608, + 607, 609, 610, 610, 610, 611, 611, 26, 612, 612, 612, 612, 612, 612, 612, 612, + 613, 26, 612, 614, 614, 612, 612, 615, 612, 612, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 616, 616, 616, 616, 616, 616, 616, 616, 616, 616, 616, 617, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 618, 618, 618, 618, 618, 618, 618, 618, + 618, 619, 618, 618, 618, 618, 618, 618, 618, 620, 618, 618, 26, 26, 26, 26, + 26, 26, 26, 26, 621, 26, 347, 26, 622, 622, 622, 622, 622, 622, 622, 622, + 622, 622, 622, 622, 622, 622, 622, 622, 622, 622, 622, 622, 622, 622, 622, 622, + 622, 622, 622, 622, 622, 622, 622, 26, 623, 623, 623, 623, 623, 623, 623, 623, + 623, 623, 623, 623, 623, 623, 623, 623, 623, 623, 623, 623, 623, 623, 623, 623, + 623, 623, 624, 26, 26, 26, 26, 26, 622, 625, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 626, 627, 628, 287, 287, 287, 287, 287, 287, 287, + 287, 287, 287, 287, 287, 287, 287, 287, 287, 287, 287, 287, 287, 287, 287, 287, + 287, 287, 287, 287, 287, 287, 287, 287, 287, 287, 287, 287, 629, 26, 630, 26, + 26, 26, 631, 26, 632, 26, 633, 633, 633, 633, 633, 633, 633, 633, 633, 633, + 633, 633, 633, 633, 633, 633, 633, 633, 633, 633, 633, 633, 633, 633, 633, 633, + 633, 633, 633, 633, 633, 633, 633, 634, 635, 635, 635, 635, 635, 635, 635, 635, + 635, 635, 635, 635, 635, 636, 635, 637, 635, 638, 635, 639, 281, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 9, 9, 9, 9, 9, 640, 9, 9, + 221, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 281, 26, 26, 26, 26, 26, 26, 26, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 276, 26, 0, 0, 0, 0, 258, 363, 0, 0, + 0, 0, 0, 0, 641, 642, 0, 643, 644, 645, 0, 0, 0, 646, 0, 0, + 0, 0, 0, 0, 0, 266, 26, 26, 14, 14, 14, 14, 14, 14, 14, 14, + 247, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 0, 0, 281, 26, 0, 0, 281, 26, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 258, 26, 0, 0, 0, 260, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 647, 648, 0, 649, + 650, 0, 0, 0, 0, 0, 0, 0, 269, 651, 255, 255, 0, 0, 0, 652, + 653, 654, 655, 0, 0, 0, 0, 0, 0, 0, 0, 0, 276, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 268, 0, 0, 0, 0, 0, 0, 656, 656, 656, 656, 656, 656, 656, 656, + 656, 656, 656, 656, 656, 656, 656, 656, 656, 657, 26, 658, 659, 656, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 2, 2, 2, 348, 660, 308, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 661, 270, 270, 662, 663, 664, 18, 18, + 18, 18, 18, 18, 18, 665, 26, 26, 26, 666, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 667, 667, 667, 667, 667, 668, 667, 669, + 667, 670, 26, 26, 26, 26, 26, 26, 26, 26, 671, 671, 671, 672, 26, 26, + 673, 673, 673, 673, 673, 673, 673, 674, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 675, 675, 675, 675, 675, 676, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 172, 677, 170, 172, 678, 678, 678, 678, 678, 678, 678, 678, + 678, 678, 678, 678, 678, 678, 678, 678, 678, 678, 678, 678, 678, 678, 678, 678, + 679, 678, 680, 26, 26, 26, 26, 26, 681, 681, 681, 681, 681, 681, 681, 681, + 681, 682, 681, 683, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 363, 0, 0, 0, 0, 0, 0, 0, 377, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 363, 0, 0, 0, 0, 0, 0, 276, + 26, 26, 26, 26, 26, 26, 26, 26, 684, 31, 31, 31, 685, 686, 687, 688, + 689, 690, 685, 691, 685, 687, 687, 692, 31, 693, 31, 694, 695, 693, 31, 694, + 26, 26, 26, 26, 26, 26, 51, 26, 0, 0, 0, 0, 0, 281, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 281, 26, 0, 258, 363, 0, + 363, 0, 363, 0, 0, 0, 276, 26, 0, 0, 0, 0, 0, 276, 26, 26, + 26, 26, 26, 26, 696, 0, 0, 0, 697, 26, 0, 0, 0, 0, 0, 281, + 0, 260, 314, 26, 276, 26, 26, 26, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 698, 0, 377, 0, 377, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 258, 699, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 314, 0, 281, 260, 26, 0, 281, 0, 0, 0, 0, 0, 0, + 0, 26, 0, 314, 0, 0, 0, 0, 0, 26, 0, 0, 0, 276, 314, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 281, 26, 0, 276, 0, 377, 0, 260, 0, 0, 0, 0, 0, 269, + 276, 696, 0, 281, 0, 260, 0, 260, 0, 0, 360, 0, 0, 0, 0, 0, + 0, 266, 26, 26, 26, 26, 0, 314, 277, 277, 277, 277, 277, 277, 277, 277, + 277, 277, 277, 277, 26, 26, 26, 26, 277, 277, 277, 277, 277, 277, 277, 347, + 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 280, 277, 277, 277, 277, + 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 347, 26, 277, 277, + 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, + 277, 277, 277, 277, 700, 26, 26, 26, 277, 277, 277, 280, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 277, 277, 277, 277, 277, 277, 277, 277, + 277, 701, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 702, 26, 26, 26, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 939, 940, 941, 942, 946, 948, 0, 962, + 969, 970, 971, 976,1001,1002,1003,1008, 0,1033,1040,1041,1042,1043,1047, 0, + 0,1080,1081,1082,1086,1110, 0, 0,1124,1125,1126,1127,1131,1133, 0,1147, + 1154,1155,1156,1161,1187,1188,1189,1193, 0,1219,1226,1227,1228,1229,1233, 0, + 0,1267,1268,1269,1273,1298, 0,1303, 943,1128, 944,1129, 954,1139, 958,1143, + 959,1144, 960,1145, 961,1146, 964,1149, 0, 0, 973,1158, 974,1159, 975,1160, + 983,1168, 978,1163, 988,1173, 990,1175, 991,1176, 993,1178, 994,1179, 0, 0, + 1004,1190,1005,1191,1006,1192,1014,1199,1007, 0, 0, 0,1016,1201,1020,1206, + 0,1022,1208,1025,1211,1023,1209, 0, 0, 0, 0,1032,1218,1037,1223,1035, + 1221, 0, 0, 0,1044,1230,1045,1231,1049,1235, 0, 0,1058,1244,1064,1250, + 1060,1246,1066,1252,1067,1253,1072,1258,1069,1255,1077,1264,1074,1261, 0, 0, + 1083,1270,1084,1271,1085,1272,1088,1275,1089,1276,1096,1283,1103,1290,1111,1299, + 1115,1118,1307,1120,1309,1121,1310, 0,1053,1239, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0,1093,1280, 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, 949,1134,1010,1195,1050,1236,1090,1277,1341,1368,1340, + 1367,1342,1369,1339,1366, 0,1320,1347,1418,1419,1323,1350, 0, 0, 992,1177, + 1018,1204,1055,1241,1416,1417,1415,1424,1202, 0, 0, 0, 987,1172, 0, 0, + 1031,1217,1321,1348,1322,1349,1338,1365, 950,1135, 951,1136, 979,1164, 980,1165, + 1011,1196,1012,1197,1051,1237,1052,1238,1061,1247,1062,1248,1091,1278,1092,1279, + 1071,1257,1076,1263, 0, 0, 997,1182, 0, 0, 0, 0, 0, 0, 945,1130, + 982,1167,1337,1364,1335,1362,1046,1232,1422,1423,1113,1301, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 8, 9, 0, 10,1425, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,1314,1427, 5, + 1434,1438,1443, 0,1450, 0,1455,1461,1514, 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,1446,1458,1468,1476,1480,1486,1517, 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,1489,1503,1494,1500,1508, 0, 0, 0, 0,1520,1521, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0,1526,1528, 0,1525, 0, 0, 0,1522, + 0, 0, 0, 0,1536,1532,1539, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0,1534, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0,1556, 0, 0, 0, 0, 0, 0,1548,1550, 0,1547, 0, 0, 0,1567, + 0, 0, 0, 0,1558,1554,1561, 0, 0, 0, 0, 0, 0, 0,1568,1569, + 0, 0, 0, 0, 0, 0, 0, 0, 0,1529,1551, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0,1523,1545,1524,1546, 0, 0,1527,1549, + 0, 0,1570,1571,1530,1552,1531,1553, 0, 0,1533,1555,1535,1557,1537,1559, + 0, 0,1572,1573,1544,1566,1538,1560,1540,1562,1541,1563,1542,1564, 0, 0, + 1543,1565, 0, 0, 0, 0, 0, 0, 0, 0,1606,1607,1609,1608,1610, 0, + 0, 0, 0, 0, 0, 0, 0, 0,1613, 0,1611, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1612, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0,1620, 0, 0, 0, 0, 0, 0, 0,1623, 0, 0,1624, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1614,1615,1616,1617,1618,1619,1621,1622, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0,1628,1629, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0,1625,1626, 0,1627, 0, 0, 0,1634, 0, 0,1635, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0,1630,1631,1632, 0, 0,1633, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1639, 0, 0,1638,1640, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0,1636,1637, 0, 0, 0, 0, 0, 0,1641, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0,1642,1644,1643, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1645, 0, 0, 0, 0, 0, 0, 0,1646, 0, 0, 0, 0, 0, 0,1648, + 1649, 0,1647,1650, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0,1651,1653,1652, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0,1654, 0,1655,1657,1656, 0, 0, 0, 0,1659, 0, 0, 0, 0, + 0, 0, 0, 0, 0,1660, 0, 0, 0, 0,1661, 0, 0, 0, 0,1662, + 0, 0, 0, 0,1663, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0,1658, 0, 0, 0, 0, 0, 0, 0, 0, 0,1664, 0,1665,1673, 0, + 1674, 0, 0, 0, 0, 0, 0, 0, 0,1666, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1668, 0, 0, 0, 0, + 0, 0, 0, 0, 0,1669, 0, 0, 0, 0,1670, 0, 0, 0, 0,1671, + 0, 0, 0, 0,1672, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0,1667, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1675, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1676, 0, + 1677, 0,1678, 0,1679, 0,1680, 0, 0, 0,1681, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0,1682, 0,1683, 0, 0,1684,1685, 0,1686, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 953,1138, 955,1140, 956,1141, 957,1142, + 1324,1351, 963,1148, 965,1150, 968,1153, 966,1151, 967,1152,1378,1380,1379,1381, + 984,1169, 985,1170,1420,1421, 986,1171, 989,1174, 995,1180, 998,1183, 996,1181, + 999,1184,1000,1185,1015,1200,1329,1356,1017,1203,1019,1205,1021,1207,1024,1210, + 1687,1688,1027,1213,1026,1212,1028,1214,1029,1215,1030,1216,1034,1220,1036,1222, + 1039,1225,1038,1224,1334,1361,1336,1363,1382,1384,1383,1385,1056,1242,1057,1243, + 1059,1245,1063,1249,1689,1690,1065,1251,1068,1254,1070,1256,1386,1387,1388,1389, + 1691,1692,1073,1259,1075,1262,1079,1266,1078,1265,1095,1282,1098,1285,1097,1284, + 1390,1391,1392,1393,1099,1286,1100,1287,1101,1288,1102,1289,1105,1292,1104,1291, + 1106,1294,1107,1295,1108,1296,1114,1302,1119,1308,1122,1311,1123,1312,1186,1260, + 1293,1305, 0,1394, 0, 0, 0, 0, 952,1137, 947,1132,1317,1344,1316,1343, + 1319,1346,1318,1345,1693,1695,1371,1375,1370,1374,1373,1377,1372,1376,1694,1696, + 981,1166, 977,1162, 972,1157,1326,1353,1325,1352,1328,1355,1327,1354,1697,1698, + 1009,1194,1013,1198,1054,1240,1048,1234,1331,1358,1330,1357,1333,1360,1332,1359, + 1699,1700,1396,1401,1395,1400,1398,1403,1397,1402,1399,1404,1094,1281,1087,1274, + 1406,1411,1405,1410,1408,1413,1407,1412,1409,1414,1109,1297,1117,1306,1116,1304, + 1112,1300, 0, 0, 0, 0, 0, 0,1471,1472,1701,1705,1702,1706,1703,1707, + 1430,1431,1715,1719,1716,1720,1717,1721,1477,1478,1729,1731,1730,1732, 0, 0, + 1435,1436,1733,1735,1734,1736, 0, 0,1481,1482,1737,1741,1738,1742,1739,1743, + 1439,1440,1751,1755,1752,1756,1753,1757,1490,1491,1765,1768,1766,1769,1767,1770, + 1447,1448,1771,1774,1772,1775,1773,1776,1495,1496,1777,1779,1778,1780, 0, 0, + 1451,1452,1781,1783,1782,1784, 0, 0,1504,1505,1785,1788,1786,1789,1787,1790, + 0,1459, 0,1791, 0,1792, 0,1793,1509,1510,1794,1798,1795,1799,1796,1800, + 1462,1463,1808,1812,1809,1813,1810,1814,1467, 21,1475, 22,1479, 23,1485, 24, + 1493, 27,1499, 28,1507, 29, 0, 0,1704,1708,1709,1710,1711,1712,1713,1714, + 1718,1722,1723,1724,1725,1726,1727,1728,1740,1744,1745,1746,1747,1748,1749,1750, + 1754,1758,1759,1760,1761,1762,1763,1764,1797,1801,1802,1803,1804,1805,1806,1807, + 1811,1815,1816,1817,1818,1819,1820,1821,1470,1469,1822,1474,1465, 0,1473,1825, + 1429,1428,1426, 12,1432, 0, 26, 0, 0,1315,1823,1484,1466, 0,1483,1829, + 1433, 13,1437, 14,1441,1826,1827,1828,1488,1487,1513, 19, 0, 0,1492,1515, + 1445,1444,1442, 15, 0,1831,1832,1833,1502,1501,1516, 25,1497,1498,1506,1518, + 1457,1456,1454, 17,1453,1313, 11, 3, 0, 0,1824,1512,1519, 0,1511,1830, + 1449, 16,1460, 18,1464, 4, 0, 0, 30, 31, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, + 0, 0, 2, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0,1834,1835, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0,1836, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0,1837,1839,1838, 0, 0, 0, 0,1840, 0, 0, 0, + 0,1841, 0, 0,1842, 0, 0, 0, 0, 0, 0, 0,1843, 0,1844, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,1845, 0, 0,1846, 0, 0,1847, + 0,1848, 0, 0, 0, 0, 0, 0, 937, 0,1850, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0,1849, 936, 938,1851,1852, 0, 0,1853,1854, 0, 0, + 1855,1856, 0, 0, 0, 0, 0, 0,1857,1858, 0, 0,1861,1862, 0, 0, + 1863,1864, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0,1867,1868,1869,1870,1859,1860,1865,1866, 0, 0, 0, 0, + 0, 0,1871,1872,1873,1874, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 32, 33, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0,1875, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0,1877, 0,1878, 0,1879, 0,1880, 0,1881, 0,1882, 0, + 1883, 0,1884, 0,1885, 0,1886, 0,1887, 0,1888, 0, 0,1889, 0,1890, + 0,1891, 0, 0, 0, 0, 0, 0,1892,1893, 0,1894,1895, 0,1896,1897, + 0,1898,1899, 0,1900,1901, 0, 0, 0, 0, 0, 0,1876, 0, 0, 0, + 0, 0, 0, 0, 0, 0,1902, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0,1904, 0,1905, 0,1906, 0,1907, 0,1908, 0,1909, 0, + 1910, 0,1911, 0,1912, 0,1913, 0,1914, 0,1915, 0, 0,1916, 0,1917, + 0,1918, 0, 0, 0, 0, 0, 0,1919,1920, 0,1921,1922, 0,1923,1924, + 0,1925,1926, 0,1927,1928, 0, 0, 0, 0, 0, 0,1903, 0, 0,1929, + 1930,1931,1932, 0, 0, 0,1933, 0, 710, 385, 724, 715, 455, 103, 186, 825, + 825, 242, 751, 205, 241, 336, 524, 601, 663, 676, 688, 738, 411, 434, 474, 500, + 649, 746, 799, 108, 180, 416, 482, 662, 810, 275, 462, 658, 692, 344, 618, 679, + 293, 388, 440, 492, 740, 116, 146, 168, 368, 414, 481, 527, 606, 660, 665, 722, + 781, 803, 809, 538, 553, 588, 642, 758, 811, 701, 233, 299, 573, 612, 487, 540, + 714, 779, 232, 267, 412, 445, 457, 585, 594, 766, 167, 613, 149, 148, 560, 589, + 648, 768, 708, 345, 411, 704, 105, 259, 313, 496, 518, 174, 542, 120, 307, 101, + 430, 372, 584, 183, 228, 529, 650, 697, 424, 732, 428, 349, 632, 355, 517, 110, + 135, 147, 403, 580, 624, 700, 750, 170, 193, 245, 297, 374, 463, 543, 763, 801, + 812, 815, 162, 384, 420, 730, 287, 330, 337, 366, 459, 476, 509, 558, 591, 610, + 726, 652, 734, 759, 154, 163, 198, 473, 683, 697, 292, 311, 353, 423, 572, 494, + 113, 217, 259, 280, 314, 499, 506, 603, 608, 752, 778, 782, 788, 117, 557, 748, + 774, 320, 109, 126, 260, 265, 373, 411, 479, 523, 655, 737, 823, 380, 765, 161, + 395, 398, 438, 451, 502, 516, 537, 583, 791, 136, 340, 769, 122, 273, 446, 727, + 305, 322, 400, 496, 771, 155, 190, 269, 377, 391, 406, 432, 501, 519, 599, 684, + 687, 749, 776, 175, 452, 191, 480, 510, 659, 772, 805, 813, 397, 444, 619, 566, + 568, 575, 491, 471, 707, 111, 636, 156, 153, 288, 346, 578, 256, 435, 383, 729, + 680, 767, 694, 295, 128, 210, 0, 0, 227, 0, 379, 0, 0, 150, 493, 525, + 544, 551, 552, 556, 783, 576, 604, 0, 661, 0, 703, 0, 0, 735, 743, 0, + 0, 0, 793, 794, 795, 808, 741, 773, 118, 127, 130, 166, 169, 177, 207, 213, + 215, 226, 229, 268, 270, 317, 327, 329, 335, 369, 375, 381, 404, 441, 448, 458, + 477, 484, 503, 539, 545, 547, 546, 548, 549, 550, 554, 555, 561, 564, 569, 591, + 593, 595, 598, 607, 620, 625, 625, 651, 690, 695, 705, 706, 716, 717, 733, 735, + 777, 786, 790, 315, 869, 623, 0, 0, 102, 145, 134, 115, 129, 138, 165, 171, + 207, 202, 206, 212, 227, 231, 240, 243, 250, 254, 294, 296, 303, 308, 319, 325, + 321, 329, 326, 335, 341, 357, 360, 362, 370, 379, 388, 389, 393, 421, 424, 438, + 456, 454, 458, 465, 477, 535, 485, 490, 493, 507, 512, 514, 521, 522, 525, 526, + 528, 533, 532, 541, 565, 569, 574, 586, 591, 597, 607, 637, 647, 674, 691, 693, + 695, 698, 703, 699, 705, 704, 702, 706, 709, 717, 728, 736, 747, 754, 770, 777, + 783, 784, 786, 787, 790, 802, 825, 848, 847, 857, 55, 65, 66, 883, 892, 916, + 822, 824, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0,1586, 0,1605, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0,1602,1603,1934,1935,1574,1575,1576,1577,1579,1580,1581,1583,1584, 0, + 1585,1587,1588,1589,1591, 0,1592, 0,1593,1594, 0,1595,1596, 0,1598,1599, + 1600,1601,1604,1582,1578,1590,1597, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0,1936, 0,1937, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0,1938, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0,1939,1940, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0,1941,1942, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0,1944,1943, 0,1945, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0,1946,1947, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1948, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0,1949,1950,1951,1952,1953,1954,1955, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0,1956,1957,1958,1960,1959,1961, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 106, 104, 107, 826, 114, 118, 119, 121, + 123, 124, 127, 125, 34, 830, 130, 131, 132, 137, 827, 35, 133, 139, 829, 142, + 143, 112, 144, 145, 924, 151, 152, 37, 157, 158, 159, 160, 38, 165, 166, 169, + 171, 172, 173, 174, 176, 177, 178, 179, 181, 182, 182, 182, 833, 468, 184, 185, + 834, 187, 188, 189, 196, 192, 194, 195, 197, 199, 200, 201, 203, 204, 204, 206, + 208, 209, 211, 218, 213, 219, 214, 216, 153, 234, 221, 222, 223, 220, 225, 224, + 230, 835, 235, 236, 237, 238, 239, 244, 836, 837, 247, 248, 249, 246, 251, 39, + 40, 253, 255, 255, 838, 257, 258, 259, 261, 839, 262, 263, 301, 264, 41, 266, + 270, 272, 271, 841, 274, 842, 277, 276, 278, 281, 282, 42, 283, 284, 285, 286, + 43, 843, 44, 289, 290, 291, 293, 934, 298, 845, 845, 621, 300, 300, 45, 852, + 894, 302, 304, 46, 306, 309, 310, 312, 316, 48, 47, 317, 846, 318, 323, 324, + 325, 324, 328, 329, 333, 331, 332, 334, 335, 336, 338, 339, 342, 343, 347, 351, + 849, 350, 348, 352, 354, 359, 850, 361, 358, 356, 49, 363, 365, 367, 364, 50, + 369, 371, 851, 376, 386, 378, 53, 381, 52, 51, 140, 141, 387, 382, 614, 78, + 388, 389, 390, 394, 392, 856, 54, 399, 396, 402, 404, 858, 405, 401, 407, 55, + 408, 409, 410, 413, 859, 415, 56, 417, 860, 418, 57, 419, 422, 424, 425, 861, + 840, 862, 426, 863, 429, 431, 427, 433, 437, 441, 438, 439, 442, 443, 864, 436, + 449, 450, 58, 454, 453, 865, 447, 460, 866, 867, 461, 466, 465, 464, 59, 467, + 470, 469, 472, 828, 475, 868, 478, 870, 483, 485, 486, 871, 488, 489, 872, 873, + 495, 497, 60, 498, 61, 61, 504, 505, 507, 508, 511, 62, 513, 874, 515, 875, + 518, 844, 520, 876, 877, 878, 63, 64, 528, 880, 879, 881, 882, 530, 531, 531, + 533, 66, 534, 67, 68, 884, 536, 538, 541, 69, 885, 549, 886, 887, 556, 559, + 70, 561, 562, 563, 888, 889, 889, 567, 71, 890, 570, 571, 72, 891, 577, 73, + 581, 579, 582, 893, 587, 74, 590, 592, 596, 75, 895, 896, 76, 897, 600, 898, + 602, 605, 607, 899, 900, 609, 901, 611, 853, 77, 615, 616, 79, 617, 252, 902, + 903, 854, 855, 621, 622, 731, 80, 627, 626, 628, 164, 629, 630, 631, 633, 904, + 632, 634, 639, 640, 635, 641, 646, 651, 638, 643, 644, 645, 905, 907, 906, 81, + 653, 654, 656, 911, 657, 908, 82, 83, 909, 910, 84, 664, 665, 666, 667, 669, + 668, 671, 670, 674, 672, 673, 675, 85, 677, 678, 86, 681, 682, 912, 685, 686, + 87, 689, 36, 913, 914, 88, 89, 696, 702, 709, 711, 915, 712, 713, 718, 719, + 917, 831, 721, 720, 723, 832, 725, 728, 918, 919, 739, 742, 744, 920, 745, 753, + 756, 757, 755, 760, 761, 921, 762, 90, 764, 922, 91, 775, 279, 780, 923, 925, + 92, 93, 785, 926, 94, 927, 787, 787, 789, 928, 792, 95, 796, 797, 798, 800, + 96, 929, 802, 804, 806, 97, 98, 807, 930, 99, 931, 932, 933, 814, 100, 816, + 817, 818, 819, 820, 821, 935, 0, 0, +}; +static const int16_t +_hb_ucd_i16[196] = +{ + 0, 0, 0, 0, 1, -1, 0, 0, 2, 0, -2, 0, 0, 0, 0, 2, + 0, -2, 0, 0, 0, 0, 0, 16, 0, 0, 0, -16, 0, 0, 1, -1, + 0, 0, 0, 1, -1, 0, 0, 0, 0, 1, -1, 0, 3, 3, 3, -3, + -3, -3, 0, 0, 0, 2016, 0, 0, 0, 0, 0, 2527, 1923, 1914, 1918, 0, + 2250, 0, 0, 0, 0, 0, 0, 138, 0, 7, 0, 0, -7, 0, 0, 0, + 1, -1, 1, -1, -1, 1, -1, 0, 1824, 0, 0, 0, 0, 0, 2104, 0, + 2108, 2106, 0, 2106, 1316, 0, 0, 0, 0, 1, -1, 1, -1, -138, 0, 0, + 1, -1, 8, 8, 8, 0, 7, 7, 0, 0, -8, -8, -8, -7, -7, 0, + 1, -1, 0, 2,-1316, 1, -1, 0, -1, 1, -1, 1, -1, 3, 1, -1, + -3, 1, -1, 1, -1, 0, 0,-1914,-1918, 0, 0,-1923,-1824, 0, 0, 0, + 0,-2016, 0, 0, 1, -1, 0, 1, 0, 0,-2104, 0, 0, 0, 0,-2106, + -2108,-2106, 0, 0, 1, -1,-2250, 0, 0, 0,-2527, 0, 0, -2, 0, 1, + -1, 0, 1, -1, +}; + +static inline uint_fast8_t +_hb_ucd_gc (unsigned u) +{ + return u<1114110u?_hb_ucd_u8[6800+(((_hb_ucd_u8[1312+(((_hb_ucd_u16[((_hb_ucd_u8[544+(((_hb_ucd_u8[u>>1>>3>>3>>4])<<4)+((u>>1>>3>>3)&15u))])<<3)+((u>>1>>3)&7u)])<<3)+((u>>1)&7u))])<<1)+((u)&1u))]:2; +} +static inline uint_fast8_t +_hb_ucd_ccc (unsigned u) +{ + return u<125259u?_hb_ucd_u8[9184+(((_hb_ucd_u8[8128+(((_hb_ucd_u8[7424+(((_hb_ucd_u8[7178+(u>>2>>3>>4)])<<4)+((u>>2>>3)&15u))])<<3)+((u>>2)&7u))])<<2)+((u)&3u))]:0; +} +static inline unsigned +_hb_ucd_b4 (const uint8_t* a, unsigned i) +{ + return (a[i>>1]>>((i&1u)<<2))&15u; +} +static inline int_fast16_t +_hb_ucd_bmg (unsigned u) +{ + return u<65380u?_hb_ucd_i16[((_hb_ucd_u8[9932+(((_hb_ucd_u8[9812+(((_hb_ucd_b4(9684+_hb_ucd_u8,u>>2>>3>>3))<<3)+((u>>2>>3)&7u))])<<3)+((u>>2)&7u))])<<2)+((u)&3u)]:0; +} +static inline uint_fast8_t +_hb_ucd_sc (unsigned u) +{ + return u<918000u?_hb_ucd_u8[11454+(((_hb_ucd_u16[2040+(((_hb_ucd_u8[10718+(((_hb_ucd_u8[10268+(u>>3>>4>>4)])<<4)+((u>>3>>4)&15u))])<<4)+((u>>3)&15u))])<<3)+((u)&7u))]:2; +} +static inline uint_fast16_t +_hb_ucd_dm (unsigned u) +{ + return u<195102u?_hb_ucd_u16[6008+(((_hb_ucd_u8[17460+(((_hb_ucd_u8[17078+(u>>4>>5)])<<5)+((u>>4)&31u))])<<4)+((u)&15u))]:0; +} + + +#elif !defined(HB_NO_UCD_UNASSIGNED) + +static const uint8_t +_hb_ucd_u8[17868] = +{ + 0, 1, 2, 3, 4, 5, 6, 7, 7, 8, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 9, 10, 7, 7, 7, 7, 11, 12, 13, 13, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 22, 22, 22, 22, 24, 7, 7, + 25, 26, 22, 22, 22, 27, 28, 29, 22, 30, 31, 32, 33, 34, 35, 36, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 37, 7, 38, 39, 7, 40, 7, 7, 7, 41, 22, 42, + 7, 7, 43, 7, 44, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 45, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 46, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 47, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 34, 35, 36, 37, 38, 39, 34, 34, 34, 40, 41, 42, 43, + 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, + 60, 61, 62, 63, 64, 64, 65, 66, 67, 68, 69, 70, 71, 69, 72, 73, + 69, 69, 64, 74, 64, 64, 75, 76, 77, 78, 79, 80, 81, 82, 69, 83, + 84, 85, 86, 87, 88, 89, 69, 69, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 90, 34, 34, 34, 34, + 91, 34, 34, 34, 34, 34, 34, 34, 34, 92, 34, 34, 93, 94, 95, 96, + 97, 98, 99,100,101,102,103,104, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,105, + 106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106, + 107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107, + 107,107, 34, 34,108,109,110,111, 34, 34,112,113,114,115,116,117, + 118,119,120,121,122,123,124,125,126,127,128,129, 34, 34,130,131, + 132,133,134,135,136,137,138,139,140,141,142,122,143,144,145,146, + 147,148,149,150,151,152,153,122,154,155,122,156,157,158,159,122, + 160,161,162,163,164,165,166,122,167,168,169,170,122,171,172,173, + 34, 34, 34, 34, 34, 34, 34,174,175, 34,176,122,122,122,122,122, + 122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,177, + 34, 34, 34, 34, 34, 34, 34, 34,178,122,122,122,122,122,122,122, + 122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122, + 122,122,122,122,122,122,122,122, 34, 34, 34, 34,179,122,122,122, + 34, 34, 34, 34,180,181,182,183,122,122,122,122,184,185,186,187, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,188, + 34, 34, 34, 34, 34, 34, 34, 34, 34,189,190,122,122,122,122,122, + 122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,191, + 34, 34,192, 34, 34,193,122,122,122,122,122,122,122,122,122,122, + 122,122,122,122,122,122,122,122,194,195,122,122,122,122,122,122, + 122,122,122,122,122,122,122,122,122,122,122,122,122,122,196,197, + 69,198,199,200,201,202,203,122,204,205,206,207,208,209,210,211, + 69, 69, 69, 69,212,213,122,122,122,122,122,122,122,122,214,122, + 215,216,217,122,122,218,122,122,122,219,122,122,122,122,122,220, + 34,221,222,122,122,122,122,122,223,224,225,122,226,227,122,122, + 228,229,230,231,232,122, 69,233, 69, 69, 69, 69, 69,234,235,236, + 237,238, 69, 69,239,240, 69,241,122,122,122,122,122,122,122,122, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,242, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,243, 34, + 244, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,245, 34, 34, + 34, 34, 34, 34, 34, 34, 34,246,122,122,122,122,122,122,122,122, + 34, 34, 34, 34,247,122,122,122,122,122,122,122,122,122,122,122, + 34, 34, 34, 34, 34, 34,248, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34,249,122,122,122,122,122,122,122,122, + 250,122,251,252,122,122,122,122,122,122,122,122,122,122,122,122, + 107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,253, + 107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,254, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 2, 4, 5, 6, 2, + 7, 7, 7, 7, 7, 2, 8, 9, 10, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 12, 13, 14, 15, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 17, 18, 19, 1, 20, 20, 21, 22, 23, 24, 25, + 26, 27, 15, 2, 28, 29, 27, 30, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 31, 11, 11, 11, 32, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 33, 16, 16, 16, 16, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 34, 34, 34, 34, 34, 34, 34, 34, 16, 32, 32, 32, + 32, 32, 32, 32, 11, 34, 34, 16, 34, 32, 32, 11, 34, 11, 16, 11, + 11, 34, 32, 11, 32, 16, 11, 34, 32, 32, 32, 11, 34, 16, 32, 11, + 34, 11, 34, 34, 32, 35, 32, 16, 36, 36, 37, 34, 38, 37, 34, 34, + 34, 34, 34, 34, 34, 34, 16, 32, 34, 38, 32, 11, 32, 32, 32, 32, + 32, 32, 16, 16, 16, 11, 34, 32, 34, 34, 11, 32, 32, 32, 32, 32, + 16, 16, 39, 16, 16, 16, 16, 16, 40, 40, 40, 40, 40, 40, 40, 40, + 40, 41, 41, 40, 40, 40, 40, 40, 40, 41, 41, 41, 41, 41, 41, 41, + 40, 40, 42, 41, 41, 41, 42, 42, 41, 41, 41, 41, 41, 41, 41, 41, + 43, 43, 43, 43, 43, 43, 43, 43, 32, 32, 42, 32, 44, 45, 16, 10, + 44, 44, 41, 46, 11, 47, 47, 11, 34, 11, 11, 11, 11, 11, 11, 11, + 11, 48, 11, 11, 11, 11, 16, 16, 16, 16, 16, 16, 16, 16, 16, 34, + 16, 11, 32, 16, 32, 32, 32, 32, 16, 16, 32, 49, 34, 32, 34, 11, + 32, 50, 43, 43, 51, 32, 32, 32, 11, 34, 34, 34, 34, 34, 34, 16, + 48, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 47, 52, 2, 2, 2, + 16, 16, 16, 16, 53, 54, 55, 56, 57, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 58, 59, 60, 43, 59, 44, 44, 44, 44, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 61, 44, 62, + 36, 63, 64, 44, 44, 44, 44, 44, 65, 65, 65, 8, 9, 66, 2, 67, + 43, 43, 43, 43, 43, 60, 68, 2, 69, 36, 36, 36, 36, 70, 43, 43, + 7, 7, 7, 7, 7, 2, 2, 36, 71, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 72, 43, 43, 43, 73, 50, 43, 43, 74, 75, 76, 43, 43, 36, + 7, 7, 7, 7, 7, 36, 77, 78, 2, 2, 2, 2, 2, 2, 2, 79, + 70, 36, 36, 36, 36, 36, 36, 36, 43, 43, 43, 43, 43, 80, 62, 36, + 36, 36, 36, 43, 43, 43, 43, 43, 71, 44, 44, 44, 44, 44, 44, 44, + 7, 7, 7, 7, 7, 36, 36, 36, 36, 36, 36, 36, 36, 70, 43, 43, + 43, 43, 40, 21, 2, 81, 57, 20, 36, 36, 36, 43, 43, 75, 43, 43, + 43, 43, 75, 43, 75, 43, 43, 44, 2, 2, 2, 2, 2, 2, 2, 64, + 36, 36, 36, 36, 70, 43, 44, 64, 36, 36, 36, 36, 36, 61, 44, 44, + 36, 36, 36, 36, 82, 36, 36, 61, 65, 44, 44, 44, 43, 43, 43, 43, + 36, 36, 36, 36, 83, 43, 43, 43, 43, 84, 43, 43, 43, 43, 43, 43, + 43, 85, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 85, 71, 86, + 87, 43, 43, 43, 85, 86, 87, 86, 70, 43, 43, 43, 36, 36, 36, 36, + 36, 43, 2, 7, 7, 7, 7, 7, 88, 36, 36, 36, 36, 36, 36, 36, + 70, 86, 62, 36, 36, 36, 61, 62, 61, 62, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 61, 36, 36, 36, 61, 61, 44, 36, 36, 44, 71, 86, + 87, 43, 80, 89, 90, 89, 87, 61, 44, 44, 44, 89, 44, 44, 36, 62, + 36, 43, 44, 7, 7, 7, 7, 7, 36, 20, 27, 27, 27, 56, 63, 80, + 57, 85, 62, 36, 36, 61, 44, 62, 61, 36, 62, 61, 36, 44, 80, 86, + 87, 80, 44, 57, 80, 57, 43, 44, 57, 44, 44, 44, 62, 36, 61, 61, + 44, 44, 44, 7, 7, 7, 7, 7, 43, 36, 70, 64, 44, 44, 44, 44, + 57, 85, 62, 36, 36, 36, 36, 62, 36, 62, 36, 36, 36, 36, 36, 36, + 61, 36, 62, 36, 36, 44, 71, 86, 87, 43, 43, 57, 85, 89, 87, 44, + 61, 44, 44, 44, 44, 44, 44, 44, 66, 44, 44, 44, 62, 43, 43, 43, + 57, 86, 62, 36, 36, 36, 61, 62, 61, 36, 62, 36, 36, 44, 71, 87, + 87, 43, 80, 89, 90, 89, 87, 44, 44, 44, 57, 85, 44, 44, 36, 62, + 78, 27, 27, 27, 44, 44, 44, 44, 44, 71, 62, 36, 36, 61, 44, 36, + 61, 36, 36, 44, 62, 61, 61, 36, 44, 62, 61, 44, 36, 61, 44, 36, + 36, 36, 36, 36, 36, 44, 44, 86, 85, 90, 44, 86, 90, 86, 87, 44, + 61, 44, 44, 89, 44, 44, 44, 44, 27, 91, 67, 67, 56, 92, 44, 44, + 85, 86, 71, 36, 36, 36, 61, 36, 61, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 44, 71, 43, 85, 86, 90, 43, 80, 43, 43, 44, + 44, 44, 57, 80, 36, 61, 62, 44, 44, 44, 44, 93, 27, 27, 27, 91, + 70, 86, 72, 36, 36, 36, 61, 36, 36, 36, 62, 36, 36, 44, 71, 87, + 86, 86, 90, 85, 90, 86, 43, 44, 44, 44, 89, 90, 44, 44, 62, 61, + 62, 94, 44, 44, 44, 44, 44, 44, 43, 86, 36, 36, 36, 36, 61, 36, + 36, 36, 36, 36, 36, 70, 71, 86, 87, 43, 80, 86, 90, 86, 87, 77, + 44, 44, 36, 94, 27, 27, 27, 95, 27, 27, 27, 27, 91, 36, 36, 36, + 57, 86, 62, 36, 36, 36, 36, 36, 36, 36, 36, 61, 44, 36, 36, 36, + 36, 62, 36, 36, 36, 36, 62, 44, 36, 36, 36, 61, 44, 80, 44, 89, + 86, 43, 80, 80, 86, 86, 86, 86, 44, 86, 64, 44, 44, 44, 44, 44, + 62, 36, 36, 36, 36, 36, 36, 36, 70, 36, 43, 43, 43, 80, 44, 96, + 36, 36, 36, 75, 43, 43, 43, 60, 7, 7, 7, 7, 7, 2, 44, 44, + 44, 44, 44, 44, 44, 44, 44, 44, 62, 61, 61, 36, 36, 61, 36, 36, + 36, 36, 62, 62, 36, 36, 36, 36, 70, 36, 43, 43, 43, 43, 71, 44, + 36, 36, 61, 81, 43, 43, 43, 80, 7, 7, 7, 7, 7, 44, 36, 36, + 77, 67, 2, 2, 2, 2, 2, 2, 2, 97, 97, 67, 43, 67, 67, 67, + 7, 7, 7, 7, 7, 27, 27, 27, 27, 27, 50, 50, 50, 4, 4, 86, + 36, 36, 36, 36, 62, 36, 36, 36, 36, 36, 36, 36, 36, 36, 61, 44, + 57, 43, 43, 43, 43, 43, 43, 85, 43, 43, 60, 43, 36, 36, 70, 43, + 43, 43, 43, 43, 57, 43, 43, 43, 43, 43, 43, 43, 43, 43, 80, 67, + 67, 67, 67, 76, 67, 67, 92, 67, 2, 2, 97, 67, 21, 64, 44, 44, + 36, 36, 36, 36, 36, 94, 87, 43, 85, 43, 43, 43, 87, 85, 87, 71, + 7, 7, 7, 7, 7, 2, 2, 2, 36, 36, 36, 86, 43, 36, 36, 43, + 71, 86, 98, 94, 86, 86, 86, 36, 70, 43, 71, 36, 36, 36, 36, 36, + 36, 85, 87, 85, 86, 86, 87, 94, 7, 7, 7, 7, 7, 86, 87, 67, + 11, 11, 11, 48, 44, 44, 48, 44, 16, 16, 16, 16, 16, 53, 45, 16, + 36, 36, 36, 36, 61, 36, 36, 44, 36, 36, 36, 61, 61, 36, 36, 44, + 61, 36, 36, 44, 36, 36, 36, 61, 61, 36, 36, 44, 36, 36, 36, 36, + 36, 36, 36, 61, 36, 36, 36, 36, 36, 36, 36, 36, 36, 61, 57, 43, + 2, 2, 2, 2, 99, 27, 27, 27, 27, 27, 27, 27, 27, 27,100, 44, + 67, 67, 67, 67, 67, 44, 44, 44, 11, 11, 11, 44, 16, 16, 16, 44, + 101, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 77, 72, + 102, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36,103,104, 44, + 36, 36, 36, 36, 36, 63, 2,105,106, 36, 36, 36, 61, 44, 44, 44, + 36, 43, 85, 44, 44, 44, 44, 62, 36, 43,107, 64, 44, 44, 44, 44, + 36, 43, 44, 44, 44, 44, 44, 44, 36, 36, 36, 36, 36, 36, 61, 36, + 61, 43, 44, 44, 44, 44, 44, 44, 36, 36, 43, 87, 43, 43, 43, 86, + 86, 86, 86, 85, 87, 43, 43, 43, 43, 43, 2, 88, 2, 66, 70, 44, + 7, 7, 7, 7, 7, 44, 44, 44, 27, 27, 27, 27, 27, 44, 44, 44, + 2, 2, 2,108, 2, 59, 43, 84, 36, 83, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 61, 44, 44, 44, 36, 36, 70, 71, 36, 36, 36, 36, + 36, 36, 36, 36, 70, 61, 44, 44, 36, 36, 36, 44, 44, 44, 44, 44, + 36, 36, 36, 36, 36, 36, 36, 61, 43, 85, 86, 87, 85, 86, 44, 44, + 86, 85, 86, 86, 87, 43, 44, 44, 92, 44, 2, 7, 7, 7, 7, 7, + 36, 36, 36, 36, 36, 36, 36, 44, 36, 36, 61, 44, 44, 44, 44, 44, + 36, 36, 36, 36, 36, 36, 44, 44, 36, 36, 36, 36, 36, 44, 44, 44, + 7, 7, 7, 7, 7,100, 44, 67, 67, 67, 67, 67, 67, 67, 67, 67, + 36, 36, 36, 70, 85, 87, 44, 2, 36, 36, 94, 85, 43, 43, 43, 80, + 85, 85, 87, 43, 43, 43, 85, 86, 86, 87, 43, 43, 43, 43, 80, 57, + 2, 2, 2, 88, 2, 2, 2, 44, 43, 43, 43, 43, 43, 43, 43,109, + 43, 43, 43, 43, 43, 43, 43, 80, 43, 43, 98, 36, 36, 36, 36, 36, + 36, 36, 85, 43, 43, 85, 85, 86, 86, 85, 98, 36, 36, 36, 61, 44, + 97, 67, 67, 67, 67, 50, 43, 43, 43, 43, 67, 67, 67, 67, 21, 64, + 43, 98, 36, 36, 36, 36, 36, 36, 94, 43, 43, 86, 43, 87, 43, 36, + 36, 36, 36, 85, 43, 86, 87, 87, 43, 86, 44, 44, 44, 44, 2, 2, + 36, 36, 86, 86, 86, 86, 43, 43, 43, 43, 86, 43, 44, 93, 2, 2, + 7, 7, 7, 7, 7, 44, 62, 36, 36, 36, 36, 36, 40, 40, 40, 2, + 16, 16, 16, 16,110, 44, 44, 44, 11, 11, 11, 11, 11, 47, 48, 11, + 2, 2, 2, 2, 44, 44, 44, 44, 43, 60, 43, 43, 43, 43, 43, 43, + 85, 43, 43, 43, 71, 36, 70, 36, 36, 36, 71, 94, 43, 61, 44, 44, + 16, 16, 16, 16, 16, 16, 40, 40, 40, 40, 40, 40, 40, 45, 16, 16, + 16, 16, 16, 16, 45, 16, 16, 16, 16, 16, 16, 16, 16,111, 40, 40, + 32, 32, 32, 16, 16, 16, 16, 32, 16, 16, 16, 16, 11, 11, 11, 11, + 16, 16, 16, 44, 11, 11, 11, 44, 16, 16, 16, 16, 48, 48, 48, 48, + 16, 16, 16, 16, 16, 16, 16, 44, 16, 16, 16, 16,112,112,112,112, + 16, 16,110, 16, 11, 11,113,114, 41, 16,110, 16, 11, 11,113, 41, + 16, 16, 44, 16, 11, 11,115, 41, 16, 16, 16, 16, 11, 11,116, 41, + 44, 16,110, 16, 11, 11,113,117,118,118,118,118,118,119, 65, 65, + 120,120,120, 2,121,122,121,122, 2, 2, 2, 2,123, 65, 65,124, + 2, 2, 2, 2,125,126, 2,127,128, 2,129,130, 2, 2, 2, 2, + 2, 9,128, 2, 2, 2, 2,131, 65, 65,132, 65, 65, 65, 65, 65, + 133, 44, 27, 27, 27, 8,129,134, 27, 27, 27, 27, 27, 8,129,104, + 40, 40, 40, 40, 40, 40, 81, 44, 20, 20, 20, 20, 20, 20, 20, 20, + 135, 44, 44, 44, 44, 44, 44, 44, 43, 43, 43, 43, 43, 43,136, 51, + 109, 51,109, 43, 43, 43, 43, 43, 80, 44, 44, 44, 44, 44, 44, 44, + 67,137, 67,138, 67, 34, 11, 16, 11, 32,138, 67, 49, 11, 11, 67, + 67, 67,137,137,137, 11, 11,139, 11, 11, 35, 36, 39, 67, 16, 11, + 8, 8, 49, 16, 16, 26, 67,140, 27, 27, 27, 27, 27, 27, 27, 27, + 105,105,105,105,105,105,105,105,105,141,142,105,143, 67, 44, 44, + 8, 8,144, 67, 67, 8, 67, 67,144, 26, 67,144, 67, 67, 67,144, + 67, 67, 67, 67, 67, 67, 67, 8, 67,144,144, 67, 67, 67, 67, 67, + 67, 67, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 67, 67, 67, 67, 4, 4, 67, 67, 8, 67, 67, 67,145,146, 67, 67, + 67, 67, 67, 67, 67, 67,144, 67, 67, 67, 67, 67, 67, 26, 8, 8, + 8, 8, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 8, 8, + 8, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 92, 44, 44, 44, 44, + 67, 67, 67, 67, 67, 92, 44, 44, 27, 27, 27, 27, 27, 27, 67, 67, + 67, 67, 67, 67, 67, 27, 27, 27, 67, 67, 67, 26, 67, 67, 67, 67, + 26, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 8, 8, 8, 8, + 67, 67, 67, 67, 67, 67, 67, 26, 67, 67, 67, 67, 4, 4, 4, 4, + 4, 4, 4, 27, 27, 27, 27, 27, 27, 27, 67, 67, 67, 67, 67, 67, + 8, 8,129,147, 8, 8, 8, 8, 8, 8, 8, 4, 4, 4, 4, 4, + 8,129,148,148,148,148,148,148,148,148,148,148,147, 8, 8, 8, + 8, 8, 8, 8, 4, 4, 8, 8, 8, 8, 8, 8, 8, 8, 4, 8, + 8, 8,144, 26, 8, 8,144, 67, 67, 67, 44, 67, 67, 67, 67, 67, + 67, 67, 67, 55, 67, 67, 67, 67, 32, 11, 32, 34, 34, 34, 34, 11, + 32, 32, 34, 16, 16, 16, 40, 11, 32, 32,140, 67, 67,138, 34,149, + 43, 32, 44, 44, 93, 2, 99, 2, 16, 16, 16,150, 44, 44,150, 44, + 36, 36, 36, 36, 44, 44, 44, 52, 64, 44, 44, 44, 44, 44, 44, 57, + 36, 36, 36, 61, 44, 44, 44, 44, 36, 36, 36, 61, 36, 36, 36, 61, + 2,121,121, 2,125,126,121, 2, 2, 2, 2, 6, 2,108,121, 2, + 121, 4, 4, 4, 4, 2, 2, 88, 2, 2, 2, 2, 2,120, 2, 2, + 108,151, 2, 2, 2, 2, 2, 2, 67, 2,152,148,148,148,153, 44, + 67, 67, 67, 67, 67, 55, 67, 67, 67, 67, 44, 44, 44, 44, 44, 44, + 67, 67, 67, 44, 44, 44, 44, 44, 67, 67, 67, 67, 67, 67, 44, 44, + 1, 2,154,155, 4, 4, 4, 4, 4, 67, 4, 4, 4, 4,156,157, + 158,105,105,105,105, 43, 43, 86,159, 40, 40, 67,105,160, 63, 67, + 36, 36, 36, 61, 57,161,162, 69, 36, 36, 36, 36, 36, 63, 40, 69, + 44, 44, 62, 36, 36, 36, 36, 36, 67, 27, 27, 67, 67, 67, 67, 67, + 67, 67, 67, 67, 67, 67, 67, 92, 27, 27, 27, 27, 27, 67, 67, 67, + 67, 67, 67, 67, 27, 27, 27, 27,163, 27, 27, 27, 27, 27, 27, 27, + 36, 36, 83, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36,164, 2, + 7, 7, 7, 7, 7, 36, 44, 44, 32, 32, 32, 32, 32, 32, 32, 70, + 51,165, 43, 43, 43, 43, 43, 88, 32, 32, 32, 32, 32, 32, 40, 43, + 36, 36, 36,105,105,105,105,105, 43, 2, 2, 2, 44, 44, 44, 44, + 41, 41, 41,162, 40, 40, 40, 40, 41, 32, 32, 32, 32, 32, 32, 32, + 16, 32, 32, 32, 32, 32, 32, 32, 45, 16, 16, 16, 34, 34, 34, 32, + 32, 32, 32, 32, 42,166, 34, 35, 32, 32, 16, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 11, 11, 32, 11, 11, 32, 32, 32, 32, 32, 32, + 32, 32, 11, 11, 34,110, 44, 44, 32,150,150, 32, 32, 44, 44, 44, + 44, 40,167, 35, 40, 35, 36, 36, 36, 71, 36, 71, 36, 70, 36, 36, + 36, 94, 87, 85, 67, 67, 80, 44, 27, 27, 27, 67,168, 44, 44, 44, + 36, 36, 2, 2, 44, 44, 44, 44, 86, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 86, 86, 86, 86, 86, 86, 86, 86, 43, 44, 44, 44, 44, 2, + 43, 36, 36, 36, 2, 72, 72, 70, 36, 36, 36, 43, 43, 43, 43, 2, + 36, 36, 36, 70, 43, 43, 43, 43, 43, 86, 44, 44, 44, 44, 44, 93, + 36, 70, 86, 43, 43, 86, 43, 86,107, 2, 2, 2, 2, 2, 2, 52, + 7, 7, 7, 7, 7, 44, 44, 2, 36, 36, 70, 69, 36, 36, 36, 36, + 7, 7, 7, 7, 7, 36, 36, 61, 36, 36, 36, 36, 70, 43, 43, 85, + 87, 85, 87, 80, 44, 44, 44, 44, 36, 70, 36, 36, 36, 36, 85, 44, + 7, 7, 7, 7, 7, 44, 2, 2, 69, 36, 36, 77, 67, 94, 85, 36, + 71, 43, 71, 70, 71, 36, 36, 43, 70, 61, 44, 44, 44, 44, 44, 44, + 44, 44, 44, 44, 44, 62, 83, 2, 36, 36, 36, 36, 36, 94, 43, 86, + 2, 83,169, 80, 44, 44, 44, 44, 62, 36, 36, 61, 62, 36, 36, 61, + 62, 36, 36, 61, 44, 44, 44, 44, 16, 16, 16, 16, 16,114, 40, 40, + 16, 16, 16, 16,111, 41, 44, 44, 36, 94, 87, 86, 85,107, 87, 44, + 36, 36, 44, 44, 44, 44, 44, 44, 36, 36, 36, 61, 44, 62, 36, 36, + 170,170,170,170,170,170,170,170,171,171,171,171,171,171,171,171, + 16, 16, 16,110, 44, 44, 44, 44, 44,150, 16, 16, 44, 44, 62, 71, + 36, 36, 36, 36,172, 36, 36, 36, 36, 36, 36, 61, 36, 36, 61, 61, + 36, 62, 61, 36, 36, 36, 36, 36, 36, 41, 41, 41, 41, 41, 41, 41, + 41,117, 44, 44, 44, 44, 44, 44, 44, 62, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36,148, 44, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 44, 44, 44, 55, 36, 36, 36, 36, 36, 36,168, 67, + 2, 2, 2,152,130, 44, 44, 44, 6,173,174,148,148,148,148,148, + 148,148,130,152,130, 2,127,175, 2, 64, 2, 2,156,148,148,130, + 2,176, 8,177, 66, 2, 44, 44, 36, 36, 61, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 61, 79, 93, 2, 3, 2, 4, 5, 6, 2, + 16, 16, 16, 16, 16, 17, 18,129,130, 4, 2, 36, 36, 36, 36, 36, + 69, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 40, + 44, 36, 36, 36, 44, 36, 36, 36, 44, 36, 36, 36, 44, 36, 61, 44, + 20,178, 56,135, 26, 8,144, 92, 44, 44, 44, 44, 79, 65, 67, 44, + 36, 36, 36, 36, 36, 36, 62, 36, 36, 36, 36, 36, 36, 61, 36, 62, + 2, 64, 44,179, 27, 27, 27, 27, 27, 27, 44, 55, 67, 67, 67, 67, + 105,105,143, 27, 91, 67, 67, 67, 67, 67, 67, 67, 67, 27, 67, 92, + 67, 67, 67, 67, 67, 67, 92, 44, 92, 44, 44, 44, 44, 44, 44, 44, + 67, 67, 67, 67, 67, 67, 50, 44,180, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 44, 44, 27, 27, 44, 44, 44, 44, 62, 36, + 155, 36, 36, 36, 36,181, 44, 44, 36, 36, 36, 43, 43, 80, 44, 44, + 36, 36, 36, 36, 36, 36, 36, 93, 36, 36, 44, 44, 36, 36, 36, 36, + 182,105,105, 44, 44, 44, 44, 44, 11, 11, 11, 11, 16, 16, 16, 16, + 11, 11, 44, 44, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 44, 44, + 36, 36, 36, 36, 44, 44, 44, 44, 36, 36, 44, 44, 44, 44, 44, 93, + 11, 11, 11, 11, 11, 47, 11, 11, 11, 47, 11,150, 16, 16, 16, 16, + 16,150, 16, 16, 16, 16, 16, 16, 16,150, 16, 16, 16,150,110, 44, + 40, 40, 40, 52, 40, 40, 40, 40, 81, 40, 40, 40, 40, 81, 44, 44, + 36, 36, 36, 44, 61, 36, 36, 36, 36, 36, 36, 62, 61, 44, 61, 62, + 36, 36, 36, 93, 27, 27, 27, 27, 36, 36, 36, 77,163, 27, 27, 27, + 44, 44, 44,179, 27, 27, 27, 27, 36, 61, 36, 44, 44,179, 27, 27, + 36, 36, 36, 27, 27, 27, 44, 93, 36, 36, 36, 36, 36, 44, 44, 93, + 36, 36, 36, 36, 44, 44, 27, 36, 44, 27, 27, 27, 27, 27, 27, 27, + 70, 43, 57, 80, 44, 44, 43, 43, 36, 36, 62, 36, 62, 36, 36, 36, + 36, 36, 36, 44, 43, 80, 44, 57, 27, 27, 27, 27,100, 44, 44, 44, + 2, 2, 2, 2, 64, 44, 44, 44, 36, 36, 36, 36, 36, 36,183, 30, + 36, 36, 36, 36, 36, 36,183, 27, 36, 36, 36, 36, 78, 36, 36, 36, + 36, 36, 70, 80, 44,179, 27, 27, 2, 2, 2, 64, 44, 44, 44, 44, + 36, 36, 36, 44, 93, 2, 2, 2, 36, 36, 36, 44, 27, 27, 27, 27, + 36, 61, 44, 44, 27, 27, 27, 27, 36, 44, 44, 44, 93, 2, 64, 44, + 44, 44, 44, 44,179, 27, 27, 27, 11, 47, 44, 44, 44, 44, 44, 44, + 16,110, 44, 44, 44, 27, 27, 27, 36, 36, 43, 43, 44, 44, 44, 44, + 27, 27, 27, 27, 27, 27, 27,100, 36, 36, 36, 36, 36, 57,184, 44, + 36, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 57, 43, + 27, 27, 27, 95, 44, 44, 44, 44,180, 27, 30, 2, 2, 44, 44, 44, + 36, 43, 43, 2, 2, 44, 44, 44, 36, 36,183, 27, 27, 27, 44, 44, + 87, 98, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 43, 43, 43, 43, + 43, 43, 43, 60, 2, 2, 2, 44, 27, 27, 27, 7, 7, 7, 7, 7, + 71, 70, 71, 44, 44, 44, 44, 57, 86, 87, 43, 85, 87, 60,185, 2, + 2, 80, 44, 44, 44, 44, 79, 44, 43, 71, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 70, 43, 43, 87, 43, 43, 43, 80, 7, 7, 7, 7, 7, + 2, 2, 94, 98, 44, 44, 44, 44, 36, 70, 2, 61, 44, 44, 44, 44, + 36, 94, 86, 43, 43, 43, 43, 85, 98, 36, 63, 2, 59, 43, 60, 87, + 7, 7, 7, 7, 7, 63, 63, 2,179, 27, 27, 27, 27, 27, 27, 27, + 27, 27,100, 44, 44, 44, 44, 44, 36, 36, 36, 36, 36, 36, 86, 87, + 43, 86, 85, 43, 2, 2, 2, 71, 70, 44, 44, 44, 44, 44, 44, 44, + 36, 36, 36, 61, 61, 36, 36, 62, 36, 36, 36, 36, 36, 36, 36, 62, + 36, 36, 36, 36, 63, 44, 44, 44, 36, 36, 36, 36, 36, 36, 36, 70, + 86, 87, 43, 43, 43, 80, 44, 44, 43, 86, 62, 36, 36, 36, 61, 62, + 61, 36, 62, 36, 36, 57, 71, 86, 85, 86, 90, 89, 90, 89, 86, 44, + 61, 44, 44, 89, 44, 44, 62, 36, 36, 86, 44, 43, 43, 43, 80, 44, + 43, 43, 80, 44, 44, 44, 44, 44, 36, 36, 94, 86, 43, 43, 43, 43, + 86, 43, 85, 71, 36, 63, 2, 2, 7, 7, 7, 7, 7, 2, 93, 71, + 86, 87, 43, 43, 85, 85, 86, 87, 85, 43, 36, 72, 44, 44, 44, 44, + 36, 36, 36, 36, 36, 36, 36, 94, 86, 43, 43, 44, 86, 86, 43, 87, + 60, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 36, 36, 43, 44, + 86, 87, 43, 43, 43, 85, 87, 87, 60, 2, 61, 44, 44, 44, 44, 44, + 2, 2, 2, 2, 2, 2, 64, 44, 36, 36, 36, 36, 36, 70, 87, 86, + 43, 43, 43, 87, 63, 44, 44, 44, 86, 43, 43, 87, 43, 43, 44, 44, + 7, 7, 7, 7, 7, 27, 2, 97, 43, 43, 43, 43, 87, 60, 44, 44, + 27,100, 44, 44, 44, 44, 44, 62, 36, 36, 36, 61, 62, 44, 36, 36, + 36, 36, 62, 61, 36, 36, 36, 36, 86, 86, 86, 89, 90, 57, 85, 71, + 98, 87, 2, 64, 44, 44, 44, 44, 36, 36, 36, 36, 44, 36, 36, 36, + 94, 86, 43, 43, 44, 43, 86, 86, 71, 72, 90, 44, 44, 44, 44, 44, + 70, 43, 43, 43, 43, 71, 36, 36, 36, 70, 43, 43, 85, 70, 43, 60, + 2, 2, 2, 59, 44, 44, 44, 44, 70, 43, 43, 85, 87, 43, 36, 36, + 36, 36, 36, 36, 36, 43, 43, 43, 43, 43, 43, 85, 43, 2, 72, 2, + 2, 64, 44, 44, 44, 44, 44, 44, 2, 2, 2, 2, 2, 44, 44, 44, + 43, 43, 43, 80, 43, 43, 43, 87, 63, 2, 2, 44, 44, 44, 44, 44, + 2, 36, 36, 36, 36, 36, 36, 36, 44, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 89, 43, 43, 43, 85, 43, 87, 80, 44, 44, 44, 44, + 36, 36, 36, 61, 36, 62, 36, 36, 70, 43, 43, 80, 44, 80, 43, 57, + 43, 43, 43, 70, 44, 44, 44, 44, 36, 36, 36, 62, 61, 36, 36, 36, + 36, 36, 36, 36, 36, 86, 86, 90, 43, 89, 87, 87, 61, 44, 44, 44, + 36, 70, 85,107, 64, 44, 44, 44, 43, 94, 36, 36, 36, 36, 36, 36, + 36, 36, 86, 43, 43, 80, 44, 86, 85, 60, 2, 2, 2, 2, 2, 2, + 27, 27, 91, 67, 67, 67, 56, 20,168, 67, 67, 67, 67, 67, 67, 67, + 67, 44, 44, 44, 44, 44, 44, 93,105,105,105,105,105,105,105,181, + 2, 2, 64, 44, 44, 44, 44, 44, 63, 64, 44, 44, 44, 44, 44, 44, + 65, 65, 65, 65, 65, 65, 65, 65, 71, 36, 36, 70, 43, 43, 43, 43, + 43, 43, 43, 44, 44, 44, 44, 44, 43, 43, 60, 44, 44, 44, 44, 44, + 43, 43, 43, 60, 2, 2, 67, 67, 40, 40, 97, 44, 44, 44, 44, 44, + 7, 7, 7, 7, 7,179, 27, 27, 27, 62, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 44, 44, 62, 36, 27, 27, 27, 30, 2, 64, 44, 44, + 36, 36, 36, 36, 36, 61, 44, 57, 94, 86, 86, 86, 86, 86, 86, 86, + 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 44, 44, 44, 57, + 43, 74, 40, 40, 40, 40, 40, 40, 40, 88, 80, 44, 44, 44, 44, 44, + 86, 44, 44, 44, 44, 44, 44, 44, 40, 40, 52, 40, 40, 40, 52, 81, + 36, 61, 44, 44, 44, 44, 44, 44, 44, 61, 44, 44, 44, 44, 44, 44, + 36, 61, 62, 44, 44, 44, 44, 44, 44, 44, 36, 36, 44, 44, 44, 44, + 36, 36, 36, 36, 36, 44, 50, 60, 65, 65, 44, 44, 44, 44, 44, 44, + 43, 43, 43, 43, 43, 43, 43, 44, 43, 43, 43, 80, 44, 44, 44, 44, + 67, 67, 67, 92, 55, 67, 67, 67, 67, 67,186, 87, 43, 67,186, 86, + 86,187, 65, 65, 65, 84, 43, 43, 43, 76, 50, 43, 43, 43, 67, 67, + 67, 67, 67, 67, 67, 43, 43, 67, 67, 43, 76, 44, 44, 44, 44, 44, + 27, 27, 44, 44, 44, 44, 44, 44, 11, 11, 11, 11, 11, 16, 16, 16, + 16, 16, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 16, + 16, 16,110, 16, 16, 16, 16, 16, 11, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 47, 11, 44, 47, 48, 47, 48, 11, 47, 11, + 11, 11, 11, 16, 16,150,150, 16, 16, 16,150, 16, 16, 16, 16, 16, + 16, 16, 11, 48, 11, 47, 48, 11, 11, 11, 47, 11, 11, 11, 47, 16, + 16, 16, 16, 16, 11, 48, 11, 47, 11, 11, 47, 47, 44, 11, 11, 11, + 47, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 11, 11, + 11, 11, 11, 16, 16, 16, 16, 16, 16, 16, 16, 44, 11, 11, 11, 11, + 31, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 33, 16, 16, + 16, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 31, 16, 16, + 16, 16, 33, 16, 16, 16, 11, 11, 11, 11, 31, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 33, 16, 16, 16, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 31, 16, 16, 16, 16, 33, 16, 16, 16, + 11, 11, 11, 11, 31, 16, 16, 16, 16, 33, 16, 16, 16, 32, 44, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 43, 43, 43, 76, 67, 50, 43, 43, + 43, 43, 43, 43, 43, 43, 76, 67, 67, 67, 50, 67, 67, 67, 67, 67, + 67, 67, 76, 21, 2, 2, 44, 44, 44, 44, 44, 44, 44, 57, 43, 43, + 16, 16, 16, 16, 16, 39, 16, 16, 16, 16, 16, 16, 16, 16, 16,110, + 44, 44,150, 16, 16,110, 44, 44, 43, 43, 43, 80, 43, 43, 43, 43, + 43, 43, 43, 43, 80, 57, 43, 43, 43, 57, 80, 43, 43, 80, 44, 44, + 40, 40, 40, 40, 40, 40, 40, 44, 44, 44, 44, 44, 44, 44, 44, 57, + 43, 43, 43, 74, 40, 40, 40, 44, 7, 7, 7, 7, 7, 44, 44, 77, + 36, 36, 36, 36, 36, 36, 36, 80, 36, 36, 36, 36, 36, 36, 43, 43, + 7, 7, 7, 7, 7, 44, 44, 96, 36, 36, 36, 36, 36, 83, 43, 43, + 36, 36, 36, 61, 36, 36, 62, 61, 36, 36, 61,179, 27, 27, 27, 27, + 16, 16, 43, 43, 43, 74, 44, 44, 27, 27, 27, 27, 27, 27,163, 27, + 188, 27,100, 44, 44, 44, 44, 44, 27, 27, 27, 27, 27, 27, 27,163, + 27, 27, 27, 27, 27, 27, 27, 44, 36, 36, 62, 36, 36, 36, 36, 36, + 62, 61, 61, 62, 62, 36, 36, 36, 36, 61, 36, 36, 62, 62, 44, 44, + 44, 61, 44, 62, 62, 62, 62, 36, 62, 61, 61, 62, 62, 62, 62, 62, + 62, 61, 61, 62, 36, 61, 36, 36, 36, 61, 36, 36, 62, 36, 61, 61, + 36, 36, 36, 36, 36, 62, 36, 36, 62, 36, 62, 36, 36, 62, 36, 36, + 8, 44, 44, 44, 44, 44, 44, 44, 55, 67, 67, 67, 67, 67, 67, 67, + 27, 27, 27, 27, 27, 27, 91, 67, 67, 67, 67, 67, 67, 67, 67, 44, + 44, 44, 44, 67, 67, 67, 67, 67, 67, 92, 44, 44, 44, 44, 44, 44, + 67, 67, 67, 67, 92, 44, 44, 44, 67, 44, 44, 44, 44, 44, 44, 44, + 67, 67, 67, 67, 67, 25, 41, 41, 67, 67, 67, 67, 44, 44, 67, 67, + 67, 67, 67, 92, 44, 55, 67, 67, 67, 67, 67, 67, 44, 44, 44, 44, + 67, 67, 67, 67, 67, 67, 67, 55, 67, 67, 67, 44, 44, 44, 44, 67, + 67, 92, 67, 67, 67, 67, 67, 67, 79, 44, 44, 44, 44, 44, 44, 44, + 171,171,171,171,171,171,171, 44,171,171,171,171,171,171,171, 0, + 0, 0, 29, 21, 21, 21, 23, 21, 22, 18, 21, 25, 21, 17, 13, 13, + 25, 25, 25, 21, 21, 9, 9, 9, 9, 22, 21, 18, 24, 16, 24, 5, + 5, 5, 5, 22, 25, 18, 25, 0, 23, 23, 26, 21, 24, 26, 7, 20, + 25, 1, 26, 24, 26, 25, 15, 15, 24, 15, 7, 19, 15, 21, 9, 25, + 9, 5, 5, 25, 5, 9, 5, 7, 7, 7, 9, 8, 8, 5, 7, 5, + 6, 6, 24, 24, 6, 24, 12, 12, 2, 2, 6, 5, 9, 21, 9, 2, + 2, 9, 25, 9, 26, 12, 11, 11, 2, 6, 5, 21, 17, 2, 2, 26, + 26, 23, 2, 12, 17, 12, 21, 12, 12, 21, 7, 2, 2, 7, 7, 21, + 21, 2, 1, 1, 21, 23, 26, 26, 1, 21, 6, 7, 7, 12, 12, 7, + 21, 7, 12, 1, 12, 6, 6, 12, 12, 26, 7, 26, 26, 7, 2, 1, + 12, 2, 6, 2, 24, 7, 7, 6, 1, 12, 12, 10, 10, 10, 10, 12, + 21, 6, 2, 10, 10, 2, 15, 26, 26, 2, 2, 21, 7, 10, 15, 7, + 2, 23, 21, 26, 10, 7, 21, 15, 15, 2, 17, 7, 29, 7, 7, 22, + 18, 2, 14, 14, 14, 7, 10, 21, 17, 21, 11, 12, 5, 2, 5, 6, + 8, 8, 8, 24, 5, 24, 2, 24, 9, 24, 24, 2, 29, 29, 29, 1, + 17, 17, 20, 19, 22, 20, 27, 28, 1, 29, 21, 20, 19, 21, 21, 16, + 16, 21, 25, 22, 18, 21, 21, 29, 1, 2, 15, 6, 18, 6, 23, 2, + 12, 11, 9, 26, 26, 9, 26, 5, 5, 26, 14, 9, 5, 14, 14, 15, + 25, 26, 26, 22, 18, 26, 18, 25, 18, 22, 5, 12, 2, 5, 22, 21, + 21, 22, 18, 17, 26, 6, 7, 14, 17, 22, 18, 18, 26, 14, 17, 6, + 14, 6, 12, 24, 24, 6, 26, 15, 6, 21, 11, 21, 24, 9, 6, 9, + 23, 26, 6, 10, 4, 4, 3, 3, 7, 25, 17, 16, 16, 22, 16, 16, + 25, 17, 25, 2, 25, 24, 2, 15, 12, 15, 14, 2, 21, 14, 7, 15, + 12, 17, 21, 1, 26, 10, 10, 1, 23, 15, 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, 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, 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, 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, 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, 35, + 0, 36, 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, 37, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 38, 39, 0, 0, 0, 0, 0, 0, 40, 41, 42, 0, 43, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 0, + 0, 0, 3, 0, 0, 0, 4, 5, 6, 7, 0, 8, 9, 10, 0, 11, + 12, 13, 14, 15, 16, 17, 16, 18, 16, 19, 16, 19, 16, 19, 0, 19, + 16, 20, 16, 19, 21, 19, 0, 22, 23, 24, 25, 26, 27, 28, 29, 30, + 31, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 33, 0, 0, + 0, 0, 0, 0, 34, 0, 0, 35, 0, 0, 36, 0, 37, 0, 0, 0, + 38, 39, 40, 41, 42, 43, 44, 45, 46, 0, 0, 47, 0, 0, 0, 48, + 0, 0, 0, 49, 0, 0, 0, 0, 0, 0, 0, 50, 0, 51, 0, 52, + 53, 0, 54, 0, 0, 0, 0, 0, 0, 55, 56, 57, 0, 0, 0, 0, + 58, 0, 0, 59, 60, 61, 62, 63, 0, 0, 64, 65, 0, 0, 0, 66, + 0, 0, 0, 0, 67, 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 69, 0, 0, 0, 70, 0, 71, 0, 0, + 72, 0, 0, 73, 0, 0, 0, 0, 0, 0, 0, 0, 74, 0, 0, 0, + 0, 0, 75, 76, 0, 77, 78, 0, 0, 79, 80, 0, 81, 62, 0, 82, + 83, 0, 0, 84, 85, 86, 0, 0, 0, 87, 0, 88, 0, 0, 51, 89, + 51, 0, 90, 0, 91, 0, 0, 0, 80, 0, 0, 0, 92, 93, 0, 94, + 95, 96, 97, 0, 0, 0, 0, 0, 51, 0, 0, 0, 0, 98, 99, 0, + 0, 0, 0, 0, 0,100, 0, 0, 0, 0, 0,101,102, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0,103, 0, 0,104, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0,105,106, 0, 0,107, 0, 0, 0, 0, 0, 0, + 108, 0,109, 0,102, 0, 0, 0, 0, 0,110,111, 0, 0, 0, 0, + 0, 0, 0,112, 0, 0, 0, 0, 0, 0, 0,113, 0,114, 0, 0, + 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 0, 8, 0, 0, 0, + 0, 9, 10, 11, 12, 0, 0, 0, 0, 13, 0, 0, 14, 15, 0, 16, + 0, 17, 18, 0, 0, 19, 0, 20, 21, 0, 0, 0, 0, 0, 22, 23, + 0, 24, 25, 0, 0, 26, 0, 0, 0, 27, 0, 0, 28, 29, 30, 31, + 0, 0, 0, 32, 33, 34, 0, 0, 33, 0, 0, 35, 33, 0, 0, 0, + 33, 36, 0, 0, 0, 0, 0, 37, 38, 0, 0, 0, 0, 0, 0, 39, + 40, 0, 0, 0, 0, 0, 0, 41, 42, 0, 0, 0, 0, 43, 0, 44, + 0, 0, 0, 45, 46, 0, 0, 0, 47, 0, 0, 0, 0, 0, 0, 48, + 49, 0, 0, 0, 0, 50, 0, 0, 0, 51, 0, 52, 0, 53, 0, 0, + 0, 0, 54, 0, 0, 0, 0, 55, 0, 56, 0, 0, 0, 0, 57, 58, + 0, 0, 0, 59, 60, 0, 0, 0, 0, 0, 0, 61, 52, 0, 62, 63, + 0, 0, 64, 0, 0, 0, 65, 66, 0, 0, 0, 67, 0, 68, 69, 70, + 71, 72, 1, 73, 0, 74, 75, 76, 0, 0, 77, 78, 0, 0, 0, 79, + 0, 0, 1, 1, 0, 0, 80, 0, 0, 81, 0, 0, 0, 0, 77, 82, + 0, 83, 0, 0, 0, 0, 0, 78, 84, 0, 85, 0, 52, 0, 1, 78, + 0, 0, 86, 0, 0, 87, 0, 0, 0, 0, 0, 88, 57, 0, 0, 0, + 0, 0, 0, 89, 90, 0, 0, 84, 0, 0, 33, 0, 0, 91, 0, 0, + 0, 0, 92, 0, 0, 0, 0, 49, 0, 0, 93, 0, 0, 0, 0, 94, + 95, 0, 0, 96, 0, 0, 97, 0, 0, 0, 98, 0, 0, 0, 99, 0, + 0, 0, 0,100,101, 93, 0, 0,102, 0, 0, 0, 84, 0, 0,103, + 0, 0, 0,104,105, 0, 0,106,107, 0, 0, 0, 0, 0, 0,108, + 0, 0,109, 0, 0, 0, 0,110, 33, 0,111,112,113, 35, 0, 0, + 114, 0, 0, 0,115, 0, 0, 0, 0, 0, 0,116, 0, 0,117, 0, + 0, 0, 0,118, 88, 0, 0, 0, 0, 0, 57, 0, 0, 0, 0, 52, + 119, 0, 0, 0, 0,120, 0, 0,121, 0, 0, 0, 0,119, 0, 0, + 122, 0, 0, 0, 0, 0, 0,123, 0, 0, 0,124, 0, 0, 0,125, + 0,126, 0, 0, 0, 0,127,128,129, 0,130, 0,131, 0, 0, 0, + 132,133,134, 0, 77, 0, 0, 0, 0, 0, 35, 0, 0, 0,135, 0, + 0, 0,136, 0, 0,137, 0, 0,138, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 2, 3, 4, 5, 6, 7, 4, 4, 8, 9, 10, + 1, 11, 12, 13, 14, 15, 16, 17, 18, 1, 1, 1, 19, 1, 0, 0, + 20, 21, 22, 1, 23, 4, 21, 24, 25, 26, 27, 28, 29, 30, 0, 0, + 1, 1, 31, 0, 0, 0, 32, 33, 34, 35, 1, 36, 37, 0, 0, 0, + 0, 38, 1, 39, 14, 39, 40, 41, 42, 0, 0, 0, 43, 36, 44, 45, + 21, 45, 46, 0, 0, 0, 19, 1, 21, 0, 0, 47, 0, 38, 48, 1, + 1, 49, 49, 50, 0, 0, 51, 0, 0, 0, 52, 1, 0, 0, 38, 14, + 4, 1, 1, 1, 53, 21, 43, 52, 54, 21, 35, 1, 0, 0, 0, 55, + 0, 0, 0, 56, 57, 58, 0, 0, 0, 0, 0, 59, 0, 60, 0, 0, + 0, 0, 61, 62, 0, 0, 63, 0, 0, 0, 64, 0, 0, 0, 65, 0, + 0, 0, 66, 0, 0, 0, 67, 0, 0, 0, 68, 0, 0, 69, 70, 0, + 71, 72, 73, 74, 75, 76, 0, 0, 0, 77, 0, 0, 0, 78, 79, 0, + 0, 0, 0, 47, 0, 0, 0, 49, 0, 80, 0, 0, 0, 62, 0, 0, + 63, 0, 0, 81, 0, 0, 82, 0, 0, 0, 83, 0, 0, 19, 84, 0, + 62, 0, 0, 0, 0, 49, 1, 85, 1, 52, 15, 86, 36, 10, 21, 87, + 0, 55, 0, 0, 0, 0, 19, 10, 1, 0, 0, 0, 0, 0, 88, 0, + 0, 89, 0, 0, 88, 0, 0, 0, 0, 78, 0, 0, 87, 9, 12, 4, + 90, 8, 91, 47, 0, 58, 50, 0, 21, 1, 21, 92, 93, 1, 1, 1, + 1, 94, 95, 96, 97, 1, 98, 58, 81, 99,100, 4, 58, 0, 0, 0, + 0, 0, 0, 19, 50, 0, 0, 0, 0, 0, 0, 61, 0, 0,101,102, + 0, 0,103, 0, 0, 1, 1, 50, 0, 0, 0, 38, 0, 63, 0, 0, + 0, 0, 0, 62, 0, 0,104, 68, 61, 0, 0, 0, 78, 0, 0, 0, + 105,106, 58, 38, 81, 0, 0, 0, 0, 0, 0,107, 1, 14, 4, 12, + 84, 0, 0, 0, 0, 38, 87, 0, 0, 0, 0,108, 0, 0,109, 61, + 0,110, 0, 0, 0, 1, 0, 0, 0, 0, 19, 58, 0, 0, 0, 51, + 0,111, 14, 52,112, 41, 0, 0, 62, 0, 0, 61, 0, 0,113, 0, + 87, 0, 0, 0, 61, 62, 0, 0, 62, 0, 89, 0, 0,113, 0, 0, + 0, 0,114, 0, 0, 0, 78, 55, 0, 38, 1, 58, 1, 58, 0, 0, + 63, 89, 0, 0,115, 0, 0, 0, 55, 0, 0, 0, 0,115, 0, 0, + 0, 0, 61, 0, 0, 0, 0, 79, 0, 61, 0, 0, 0, 0, 56, 0, + 89, 80, 0, 0, 79, 0, 0, 0, 8, 91, 0, 0, 1, 87, 0, 0, + 116, 0, 0, 0, 0, 0, 0,117, 0,118,119,120,121, 0,104, 4, + 122, 49, 23, 0, 0, 0, 38, 50, 38, 58, 0, 0, 1, 87, 1, 1, + 1, 1, 39, 1, 48,105, 87, 0, 0, 0, 0, 1, 0, 0, 0,123, + 4,122, 0, 0, 0, 1,124, 0, 0, 0, 0, 0,230,230,230,230, + 230,232,220,220,220,220,232,216,220,220,220,220,220,202,202,220, + 220,220,220,202,202,220,220,220, 1, 1, 1, 1, 1,220,220,220, + 220,230,230,230,230,240,230,220,220,220,230,230,230,220,220, 0, + 230,230,230,220,220,220,220,230,232,220,220,230,233,234,234,233, + 234,234,233,230, 0, 0, 0,230, 0,220,230,230,230,230,220,230, + 230,230,222,220,230,230,220,220,230,222,228,230, 10, 11, 12, 13, + 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 0, 23, 0, 24, 25, 0, + 230,220, 0, 18, 30, 31, 32, 0, 0, 0, 0, 27, 28, 29, 30, 31, + 32, 33, 34,230,230,220,220,230,220,230,230,220, 35, 0, 0, 0, + 0, 0,230,230,230, 0, 0,230,230, 0,220,230,230,220, 0, 0, + 0, 36, 0, 0,230,220,230,230,220,220,230,220,220,230,220,230, + 220,230,230, 0, 0,220, 0, 0,230,230, 0,230, 0,230,230,230, + 230,230, 0, 0, 0,220,220,220,230,220,220,220,230,230, 0,220, + 27, 28, 29,230, 7, 0, 0, 0, 0, 9, 0, 0, 0,230,220,230, + 230, 0, 0, 0, 0, 0,230, 0, 0, 84, 91, 0, 0, 0, 0, 9, + 9, 0, 0, 0, 0, 0, 9, 0,103,103, 9, 0,107,107,107,107, + 118,118, 9, 0,122,122,122,122,220,220, 0, 0, 0,220, 0,220, + 0,216, 0, 0, 0,129,130, 0,132, 0, 0, 0, 0, 0,130,130, + 130,130, 0, 0,130, 0,230,230, 9, 0,230,230, 0, 0,220, 0, + 0, 0, 0, 7, 0, 9, 9, 0, 9, 9, 0, 0, 0,230, 0, 0, + 0,228, 0, 0, 0,222,230,220,220, 0, 0, 0,230, 0, 0,220, + 230,220, 0,220,230,230,230, 0, 0, 0, 9, 9, 0, 0, 7, 0, + 230, 0, 1, 1, 1, 0, 0, 0,230,234,214,220,202,230,230,230, + 230,230,232,228,228,220,218,230,233,220,230,220,230,230, 1, 1, + 1, 1, 1,230, 0, 1, 1,230,220,230, 1, 1, 0, 0,218,228, + 232,222,224,224, 0, 8, 8, 0, 0, 0, 0,220,230, 0,230,230, + 220, 0, 0,230, 0, 0, 26, 0, 0,220, 0,230,230, 1,220, 0, + 0,230,220, 0, 0, 0,220,220, 0, 0,230,220, 0, 9, 7, 0, + 0, 7, 9, 0, 0, 0, 9, 7, 6, 6, 0, 0, 0, 0, 1, 0, + 0,216,216, 1, 1, 1, 0, 0, 0,226,216,216,216,216,216, 0, + 220,220,220, 0,232,232,220,230,230,230, 7, 0, 16, 17, 17, 17, + 17, 17, 17, 33, 17, 17, 17, 19, 17, 17, 17, 17, 20,101, 17,113, + 129,169, 17, 27, 28, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,237, 0, 1, 2, 2, + 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 6, 7, 8, + 9, 0, 0, 0, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 21, 22, 0, 0, 0, 0, + 23, 24, 25, 26, 0, 27, 0, 28, 29, 30, 31, 32, 0, 0, 0, 0, + 0, 0, 0, 33, 34, 35, 36, 0, 0, 0, 0, 0, 37, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 38, 39, 0, 0, 0, 0, 1, 2, 40, 41, + 0, 1, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, + 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 3, 4, 0, 0, 5, 0, + 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 7, 1, 0, 0, 0, 0, + 0, 0, 8, 9, 0, 0, 0, 0, 0, 0, 10, 0, 0, 10, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 10, + 0, 0, 0, 0, 0, 0, 11, 12, 0, 13, 0, 14, 15, 16, 0, 0, + 0, 0, 0, 1, 17, 18, 0, 19, 7, 1, 0, 0, 0, 20, 20, 7, + 20, 20, 20, 20, 20, 20, 20, 8, 21, 0, 22, 0, 7, 23, 24, 0, + 20, 20, 25, 0, 0, 0, 26, 27, 1, 7, 20, 20, 20, 20, 20, 1, + 28, 29, 30, 31, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, 10, 0, + 0, 0, 0, 0, 0, 0, 20, 20, 20, 1, 0, 0, 8, 21, 32, 4, + 0, 10, 0, 33, 7, 20, 20, 20, 0, 0, 0, 0, 8, 34, 34, 35, + 36, 34, 37, 0, 38, 1, 20, 20, 0, 0, 39, 0, 1, 1, 0, 8, + 21, 1, 20, 0, 0, 0, 1, 0, 0, 40, 1, 1, 0, 0, 8, 21, + 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 26, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 21, 7, 20, 41, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 21, 0, 42, 43, 44, 0, 45, 0, 8, 21, 0, 0, 0, 0, 0, + 0, 0, 0, 46, 7, 1, 10, 1, 0, 0, 0, 1, 20, 20, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 26, 34, 9, 0, 0, 20, 20, + 1, 20, 20, 0, 0, 0, 0, 0, 0, 0, 26, 21, 0, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 47, 48, 0, 0, 0, + 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 7, 8, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 9, 10, 11, 11, 11, 11, 12, 13, + 13, 13, 13, 14, 15, 16, 17, 18, 19, 20, 21, 13, 22, 13, 13, 13, + 13, 23, 24, 24, 25, 26, 13, 13, 13, 27, 28, 29, 13, 30, 31, 32, + 33, 34, 35, 36, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 37, 7, 38, 39, 7, 40, 7, 7, + 7, 41, 13, 42, 7, 7, 43, 7, 44, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 45, 0, 0, 1, 2, 2, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, 32, 32, 33, 34, 35, 36, 37, 37, + 37, 37, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, + 51, 52, 2, 2, 53, 54, 55, 56, 57, 58, 59, 59, 59, 59, 60, 59, + 59, 59, 59, 59, 59, 59, 61, 61, 59, 59, 59, 59, 62, 63, 64, 65, + 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 59, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 79, 70, 70, 70, 70, 80, 80, 80, 80, 80, 80, 80, 80, 80, 81, + 82, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 95, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, + 96, 96, 96, 96, 96, 96, 96, 96, 70, 70, 97, 98, 99,100,101,101, + 102,103,104,105,106,107,108,109,110,111, 96,112,113,114,115,116, + 117,118,119,119,120,121,122,123,124,125,126,127,128,129,130,131, + 132, 96,133,134,135,136,137,138,139,140,141,142,143, 96,144,145, + 96,146,147,148,149, 96,150,151,152,153,154,155,156, 96,157,158, + 159,160, 96,161,162,163,164,164,164,164,164,164,164,165,166,164, + 167, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, + 96, 96, 96, 96, 96,168,169,169,169,169,169,169,169,169,170, 96, + 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96,171,171, + 171,171,172, 96, 96, 96,173,173,173,173,174,175,176,177, 96, 96, + 96, 96,178,179,180,181,182,182,182,182,182,182,182,182,182,182, + 182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182, + 182,182,182,182,182,183,182,182,182,182,182,182,184,184,184,185, + 186, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, + 96, 96, 96, 96, 96,187,188,189,190,191,191,192, 96, 96, 96, 96, + 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96,193,194, + 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, + 96, 96, 96, 96,195,196, 59,197,198,199,200,201,202, 96,203,204, + 205, 59, 59,206, 59,207,208,208,208,208,208,209, 96, 96, 96, 96, + 96, 96, 96, 96,210, 96,211,212,213, 96, 96,214, 96, 96, 96,215, + 96, 96, 96, 96, 96,216,217,218,219, 96, 96, 96, 96, 96,220,221, + 222, 96,223,224, 96, 96,225,226, 59,227,228, 96, 59, 59, 59, 59, + 59, 59, 59,229,230,231,232,233, 59, 59,234,235, 59,236, 96, 96, + 96, 96, 96, 96, 96, 96, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70,237, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70,238, 70,239, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70,240, 70, 70, 70, 70, 70, 70, 70, 70, 70,241, 96, 96, + 96, 96, 96, 96, 96, 96, 70, 70, 70, 70,242, 96, 96, 96, 96, 96, + 96, 96, 96, 96, 96, 96, 70, 70, 70, 70, 70, 70,243, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70,244, 96, 96, + 96, 96, 96, 96, 96, 96,245, 96,246,247, 0, 1, 2, 2, 0, 1, + 2, 2, 2, 3, 4, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 0, 0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, 0, 19, 19, + 19, 19, 19, 19, 19, 0, 19, 0, 0, 0, 0, 0, 0, 0, 19, 19, + 19, 19, 19, 0, 0, 0, 0, 0, 26, 26, 0, 0, 0, 0, 1, 1, + 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 0, 9, 9, 9, 2, 2, + 9, 9, 9, 9, 0, 9, 2, 2, 2, 2, 9, 0, 9, 0, 9, 9, + 9, 2, 9, 2, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 2, 9, 9, 9, 9, 9, 9, 9, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 1, 1, 6, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 4, 4, + 4, 2, 2, 4, 4, 4, 2, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 2, 2, 2, 2, 2, 2, 2, 2, 14, 14, + 14, 2, 2, 2, 2, 14, 14, 14, 14, 14, 14, 2, 2, 2, 3, 3, + 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 0, 0, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 3, 3, 1, 3, 3, 3, 3, 3, 3, 3, 37, 37, + 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 2, 37, 37, 37, + 37, 2, 2, 37, 37, 37, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, + 2, 2, 2, 2, 2, 2, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 2, 2, 64, 64, 64, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, + 90, 90, 90, 90, 2, 2, 90, 90, 90, 90, 90, 90, 90, 2, 95, 95, + 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 2, 2, 95, 2, 37, 37, + 37, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 2, 3, 3, + 2, 2, 2, 2, 2, 2, 3, 3, 0, 3, 3, 3, 3, 3, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 1, 1, 1, 1, 7, 7, 7, 7, 7, + 7, 7, 0, 0, 7, 7, 5, 5, 5, 5, 2, 5, 5, 5, 5, 5, + 5, 5, 5, 2, 2, 5, 5, 2, 2, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 2, 5, 5, 5, 5, 5, 5, 5, 2, + 5, 2, 2, 2, 5, 5, 5, 5, 2, 2, 5, 5, 5, 5, 5, 2, + 2, 5, 5, 5, 5, 2, 2, 2, 2, 2, 2, 2, 2, 5, 2, 2, + 2, 2, 5, 5, 2, 5, 5, 5, 5, 5, 2, 2, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 2, 2, 11, 11, 11, 2, 11, 11, 11, 11, 11, + 11, 2, 2, 2, 2, 11, 11, 2, 2, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 2, 11, 11, 11, 11, 11, 11, 11, 2, + 11, 11, 2, 11, 11, 2, 11, 11, 2, 2, 11, 2, 11, 11, 11, 2, + 2, 11, 11, 11, 2, 2, 2, 11, 2, 2, 2, 2, 2, 2, 2, 11, + 11, 11, 11, 2, 11, 2, 2, 2, 2, 2, 2, 2, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 2, 2, 10, 10, 10, 2, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 2, 10, 10, 10, 2, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 2, 10, 10, 10, 10, 10, 10, 10, 2, + 10, 10, 2, 10, 10, 10, 10, 10, 2, 2, 10, 10, 10, 10, 10, 10, + 2, 10, 10, 10, 2, 2, 10, 2, 2, 2, 2, 2, 2, 2, 10, 10, + 10, 10, 2, 2, 10, 10, 10, 10, 2, 2, 2, 2, 2, 2, 2, 10, + 10, 10, 10, 10, 10, 10, 2, 21, 21, 21, 2, 21, 21, 21, 21, 21, + 21, 21, 21, 2, 2, 21, 21, 2, 2, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 2, 21, 21, 21, 21, 21, 21, 21, 2, + 21, 21, 2, 21, 21, 21, 21, 21, 2, 2, 21, 21, 21, 21, 21, 2, + 2, 21, 21, 21, 2, 2, 2, 2, 2, 2, 2, 21, 21, 21, 2, 2, + 2, 2, 21, 21, 2, 21, 21, 21, 21, 21, 2, 2, 21, 21, 2, 2, + 22, 22, 2, 22, 22, 22, 22, 22, 22, 2, 2, 2, 22, 22, 22, 2, + 22, 22, 22, 22, 2, 2, 2, 22, 22, 2, 22, 2, 22, 22, 2, 2, + 2, 22, 22, 2, 2, 2, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 2, 2, 2, 2, 22, 22, 22, 2, 2, 2, 2, 2, 2, 22, 2, 2, + 2, 2, 2, 2, 22, 22, 22, 22, 22, 2, 2, 2, 2, 2, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 2, 23, 23, 23, 2, + 23, 23, 23, 23, 23, 23, 23, 23, 2, 2, 23, 23, 23, 23, 23, 2, + 23, 23, 23, 23, 2, 2, 2, 2, 2, 2, 2, 23, 23, 2, 23, 23, + 23, 2, 2, 23, 2, 2, 23, 23, 23, 23, 2, 2, 23, 23, 2, 2, + 2, 2, 2, 2, 2, 23, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 2, 16, 16, 16, 2, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 2, 16, 16, 16, 16, 16, 2, 2, 16, 16, 16, 16, 16, 2, + 16, 16, 16, 16, 2, 2, 2, 2, 2, 2, 2, 16, 16, 2, 16, 16, + 16, 16, 2, 2, 16, 16, 2, 16, 16, 16, 2, 2, 2, 2, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 2, 20, 20, 20, 2, + 20, 20, 20, 20, 20, 20, 2, 2, 2, 2, 20, 20, 20, 20, 20, 20, + 20, 20, 2, 2, 20, 20, 2, 36, 36, 36, 2, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 2, 2, 2, + 36, 36, 36, 36, 36, 36, 36, 36, 2, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 2, 36, 2, 2, 2, 2, 36, 2, 2, 2, 2, 36, 36, 36, + 36, 36, 36, 2, 36, 2, 2, 2, 2, 2, 2, 2, 36, 36, 2, 2, + 36, 36, 36, 2, 2, 2, 2, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 2, 2, 2, 2, 0, 24, 24, + 24, 24, 2, 2, 2, 2, 2, 18, 18, 2, 18, 2, 18, 18, 18, 18, + 18, 2, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, + 18, 18, 2, 18, 2, 18, 18, 18, 18, 18, 18, 18, 2, 2, 18, 18, + 18, 18, 18, 2, 18, 2, 18, 18, 18, 18, 18, 18, 18, 2, 18, 18, + 2, 2, 18, 18, 18, 18, 25, 25, 25, 25, 25, 25, 25, 25, 2, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 2, 2, 2, 25, 25, + 25, 25, 25, 2, 25, 25, 25, 25, 25, 25, 25, 0, 0, 0, 0, 25, + 25, 2, 2, 2, 2, 2, 33, 33, 33, 33, 33, 33, 33, 33, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 2, 8, 2, 2, + 2, 2, 2, 8, 2, 2, 8, 8, 8, 0, 8, 8, 8, 8, 12, 12, + 12, 12, 12, 12, 12, 12, 30, 30, 30, 30, 30, 30, 30, 30, 30, 2, + 30, 30, 30, 30, 2, 2, 30, 30, 30, 30, 30, 30, 30, 2, 30, 30, + 30, 2, 2, 30, 30, 30, 30, 30, 30, 30, 30, 2, 2, 2, 30, 30, + 2, 2, 2, 2, 2, 2, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 2, 2, 28, 28, 28, 28, 28, 28, 28, 28, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 2, 2, 2, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 0, 0, 0, 35, 35, 35, 2, + 2, 2, 2, 2, 2, 2, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 2, 2, 2, 2, 2, 2, 2, 2, 2, 45, 44, 44, + 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 0, 0, 2, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 2, 2, 2, 2, 46, 46, + 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 2, 46, 46, 46, 2, + 46, 46, 2, 2, 2, 2, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 2, 2, 31, 31, 2, 2, 2, 2, 2, 2, 32, 32, + 0, 0, 32, 0, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 2, 2, 2, 2, 2, 2, 32, 2, 2, 2, 2, 2, 2, 2, 32, 32, + 32, 2, 2, 2, 2, 2, 28, 28, 28, 28, 28, 28, 2, 2, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 2, 48, 48, + 48, 48, 2, 2, 2, 2, 48, 2, 2, 2, 48, 48, 48, 48, 52, 52, + 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 2, 2, 52, 52, + 52, 52, 52, 2, 2, 2, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 2, 2, 2, 2, 58, 58, 2, 2, 2, 2, 2, 2, 58, 58, + 58, 2, 2, 2, 58, 58, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, + 54, 54, 2, 2, 54, 54, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, + 91, 91, 91, 91, 91, 2, 91, 91, 91, 91, 91, 2, 2, 91, 91, 91, + 2, 2, 2, 2, 2, 2, 91, 91, 91, 91, 91, 91, 2, 2, 1, 1, + 1, 1, 1, 1, 1, 2, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, + 62, 62, 62, 2, 2, 2, 62, 62, 62, 62, 62, 62, 62, 2, 76, 76, + 76, 76, 76, 76, 76, 76, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, + 93, 93, 2, 2, 2, 2, 2, 2, 2, 2, 93, 93, 93, 93, 70, 70, + 70, 70, 70, 70, 70, 70, 2, 2, 2, 70, 70, 70, 70, 70, 70, 70, + 2, 2, 2, 70, 70, 70, 73, 73, 73, 73, 73, 73, 73, 73, 6, 2, + 2, 2, 2, 2, 2, 2, 8, 8, 8, 2, 2, 8, 8, 8, 1, 1, + 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, + 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, + 0, 2, 2, 2, 2, 2, 19, 19, 19, 19, 19, 19, 9, 9, 9, 9, + 9, 6, 19, 19, 19, 19, 19, 19, 19, 19, 19, 9, 9, 9, 9, 9, + 19, 19, 19, 19, 9, 9, 9, 9, 9, 19, 19, 19, 19, 19, 6, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 9, 9, 9, + 9, 9, 9, 9, 2, 2, 2, 9, 2, 9, 2, 9, 2, 9, 9, 9, + 9, 9, 9, 2, 9, 9, 9, 9, 9, 9, 2, 2, 9, 9, 9, 9, + 9, 9, 2, 9, 9, 9, 2, 2, 9, 9, 9, 2, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 2, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, + 0, 0, 0, 2, 0, 0, 0, 19, 2, 2, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, 0, 0, 0, 2, 19, 19, + 19, 19, 19, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 1, 2, + 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, + 19, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 0, 19, 0, + 0, 0, 2, 2, 2, 2, 0, 0, 0, 2, 2, 2, 2, 2, 27, 27, + 27, 27, 27, 27, 27, 27, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, + 0, 0, 0, 0, 2, 0, 56, 56, 56, 56, 56, 56, 56, 56, 55, 55, + 55, 55, 2, 2, 2, 2, 2, 55, 55, 55, 55, 55, 55, 55, 61, 61, + 61, 61, 61, 61, 61, 61, 2, 2, 2, 2, 2, 2, 2, 61, 61, 2, + 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 2, 2, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 2, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 2, 2, 2, 2, 13, 13, 13, 13, 13, 13, 2, 2, 0, 0, + 0, 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 13, 0, 13, 0, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 1, 1, 1, 1, 12, 12, 13, 13, + 13, 13, 0, 0, 0, 0, 2, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 2, 2, 1, + 1, 0, 0, 15, 15, 15, 0, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 0, 0, 17, 17, 17, 2, 2, + 2, 2, 2, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 2, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 2, 12, 12, + 12, 12, 12, 12, 12, 0, 17, 17, 17, 17, 17, 17, 17, 0, 39, 39, + 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 2, 2, 2, 39, 39, + 39, 39, 39, 39, 39, 2, 86, 86, 86, 86, 86, 86, 86, 86, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 2, 2, 2, 2, 79, 79, + 79, 79, 79, 79, 79, 79, 0, 0, 19, 19, 19, 19, 19, 19, 0, 0, + 0, 19, 19, 19, 19, 19, 19, 19, 19, 2, 2, 2, 2, 2, 19, 19, + 2, 19, 2, 19, 19, 19, 19, 19, 2, 2, 2, 2, 2, 2, 2, 2, + 19, 19, 19, 19, 19, 19, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, + 60, 60, 60, 2, 2, 2, 0, 0, 2, 2, 2, 2, 2, 2, 65, 65, + 65, 65, 65, 65, 65, 65, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, + 75, 75, 75, 75, 2, 2, 2, 2, 2, 2, 2, 2, 75, 75, 75, 75, + 2, 2, 2, 2, 2, 2, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 69, 69, 0, 69, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 74, 12, 12, + 12, 12, 12, 2, 2, 2, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, + 84, 84, 84, 84, 2, 0, 84, 84, 2, 2, 2, 2, 84, 84, 33, 33, + 33, 33, 33, 33, 33, 2, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 2, 68, 68, 68, 68, 68, 68, 2, 2, 68, 68, + 2, 2, 68, 68, 68, 68, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, + 92, 2, 2, 2, 2, 2, 2, 2, 2, 92, 92, 92, 92, 92, 87, 87, + 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 2, 2, 30, + 30, 30, 30, 30, 30, 2, 19, 19, 19, 0, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 9, 19, 19, 19, 19, 0, 0, 2, 2, 2, 2, 87, 87, + 87, 87, 87, 87, 2, 2, 87, 87, 2, 2, 2, 2, 2, 2, 12, 12, + 12, 12, 2, 2, 2, 2, 2, 2, 2, 12, 12, 12, 12, 12, 13, 13, + 2, 2, 2, 2, 2, 2, 19, 19, 19, 19, 19, 19, 19, 2, 2, 2, + 2, 4, 4, 4, 4, 4, 2, 2, 2, 2, 2, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 2, 14, 14, 14, 14, 14, 2, 14, 2, 14, 14, + 2, 14, 14, 2, 14, 14, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, + 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 2, 2, + 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 3, 1, 1, + 1, 1, 1, 1, 6, 6, 0, 0, 0, 2, 0, 0, 0, 0, 3, 3, + 3, 3, 3, 2, 3, 3, 3, 3, 3, 3, 3, 2, 2, 0, 2, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 17, 17, 17, + 17, 17, 17, 17, 0, 0, 2, 2, 12, 12, 12, 12, 12, 12, 2, 2, + 12, 12, 12, 2, 2, 2, 2, 0, 0, 0, 0, 0, 2, 2, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 2, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 2, 49, 49, 49, 2, 49, 49, 2, 49, 49, 49, + 49, 49, 49, 49, 2, 2, 49, 49, 49, 2, 2, 2, 2, 2, 0, 0, + 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, + 0, 0, 0, 2, 2, 2, 9, 2, 2, 2, 2, 2, 2, 2, 0, 0, + 0, 0, 0, 1, 2, 2, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, + 71, 71, 71, 2, 2, 2, 67, 67, 67, 67, 67, 67, 67, 67, 67, 2, + 2, 2, 2, 2, 2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 42, 42, + 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 42, 42, 42, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 2, 2, 2, 2, 2,118,118,118,118,118,118,118,118,118,118, + 118, 2, 2, 2, 2, 2, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 2, 53, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 2, 2, 2, 2, 59, 59, 59, 59, 59, 59, 2, 2, 40, 40, + 40, 40, 40, 40, 40, 40, 51, 51, 51, 51, 51, 51, 51, 51, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 2, 2, 50, 50, + 2, 2, 2, 2, 2, 2,135,135,135,135,135,135,135,135,135,135, + 135,135, 2, 2, 2, 2,106,106,106,106,106,106,106,106,104,104, + 104,104,104,104,104,104,104,104,104,104, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2,104,161,161,161,161,161,161,161,161,161,161, + 161, 2,161,161,161,161,161,161,161, 2,161,161, 2,161,161,161, + 2,161,161,161,161,161,161,161, 2,161,161, 2, 2, 2,110,110, + 110,110,110,110,110,110,110,110,110,110,110,110,110, 2,110,110, + 110,110,110,110, 2, 2, 19, 19, 19, 19, 19, 19, 2, 19, 19, 2, + 19, 19, 19, 19, 19, 19, 47, 47, 47, 47, 47, 47, 2, 2, 47, 2, + 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 2, 47, 47, 2, 2, 2, 47, 2, 2, 47, 81, 81, + 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 2, 81,120,120, + 120,120,120,120,120,120,116,116,116,116,116,116,116,116,116,116, + 116,116,116,116,116, 2, 2, 2, 2, 2, 2, 2, 2,116,128,128, + 128,128,128,128,128,128,128,128,128, 2,128,128, 2, 2, 2, 2, + 2,128,128,128,128,128, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, + 66, 66, 2, 2, 2, 66, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, + 2, 2, 2, 2, 2, 72, 98, 98, 98, 98, 98, 98, 98, 98, 97, 97, + 97, 97, 97, 97, 97, 97, 2, 2, 2, 2, 97, 97, 97, 97, 2, 2, + 97, 97, 97, 97, 97, 97, 57, 57, 57, 57, 2, 57, 57, 2, 2, 2, + 2, 2, 57, 57, 57, 57, 57, 57, 57, 57, 2, 57, 57, 57, 2, 57, + 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, + 57, 57, 57, 57, 2, 2, 57, 57, 57, 2, 2, 2, 2, 57, 57, 2, + 2, 2, 2, 2, 2, 2, 88, 88, 88, 88, 88, 88, 88, 88,117,117, + 117,117,117,117,117,117,112,112,112,112,112,112,112,112,112,112, + 112,112,112,112,112, 2, 2, 2, 2,112,112,112,112,112, 78, 78, + 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 2, 2, 2, 78, + 78, 78, 78, 78, 78, 78, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 2, 2, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, + 82, 2, 2, 2, 2, 2,122,122,122,122,122,122,122,122,122,122, + 2, 2, 2, 2, 2, 2, 2,122,122,122,122, 2, 2, 2, 2,122, + 122,122,122,122,122,122, 89, 89, 89, 89, 89, 89, 89, 89, 89, 2, + 2, 2, 2, 2, 2, 2,130,130,130,130,130,130,130,130,130,130, + 130, 2, 2, 2, 2, 2, 2, 2,130,130,130,130,130,130,144,144, + 144,144,144,144,144,144,144,144, 2, 2, 2, 2, 2, 2,156,156, + 156,156,156,156,156,156,156,156, 2,156,156,156, 2, 2,156,156, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3,147,147, + 147,147,147,147,147,147,148,148,148,148,148,148,148,148,148,148, + 2, 2, 2, 2, 2, 2,158,158,158,158,158,158,158,158,158,158, + 2, 2, 2, 2, 2, 2,153,153,153,153,153,153,153,153,153,153, + 153,153, 2, 2, 2, 2,149,149,149,149,149,149,149,149,149,149, + 149,149,149,149,149, 2, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, + 94, 94, 94, 94, 2, 2, 2, 2, 94, 94, 94, 94, 94, 94, 2, 2, + 2, 2, 2, 2, 2, 94, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, + 85, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 85, 2, 2,101,101, + 101,101,101,101,101,101,101, 2, 2, 2, 2, 2, 2, 2,101,101, + 2, 2, 2, 2, 2, 2, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, + 96, 96, 96, 2, 96, 96,111,111,111,111,111,111,111,111,111,111, + 111,111,111,111,111, 2,100,100,100,100,100,100,100,100, 2, 36, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 2, 2, 2,108,108, + 108,108,108,108,108,108,108,108, 2,108,108,108,108,108,108,108, + 2, 2, 2, 2, 2, 2,129,129,129,129,129,129,129, 2,129, 2, + 129,129,129,129, 2,129,129,129,129,129,129,129,129,129,129,129, + 129,129,129,129, 2,129,129,129, 2, 2, 2, 2, 2, 2,109,109, + 109,109,109,109,109,109,109,109,109, 2, 2, 2, 2, 2,109,109, + 2, 2, 2, 2, 2, 2,107,107,107,107, 2,107,107,107,107,107, + 107,107,107, 2, 2,107,107, 2, 2,107,107,107,107,107,107,107, + 107,107,107,107,107,107,107, 2,107,107,107,107,107,107,107, 2, + 107,107, 2,107,107,107,107,107, 2, 1,107,107,107,107,107, 2, + 2,107,107,107, 2, 2,107, 2, 2, 2, 2, 2, 2,107, 2, 2, + 2, 2, 2,107,107,107,107,107,107,107, 2, 2,107,107,107,107, + 107,107,107, 2, 2, 2,137,137,137,137,137,137,137,137,137,137, + 137,137, 2,137,137,137,137,137, 2, 2, 2, 2, 2, 2,124,124, + 124,124,124,124,124,124,124,124, 2, 2, 2, 2, 2, 2,123,123, + 123,123,123,123,123,123,123,123,123,123,123,123, 2, 2,114,114, + 114,114,114,114,114,114,114,114,114,114,114, 2, 2, 2,114,114, + 2, 2, 2, 2, 2, 2, 32, 32, 32, 32, 32, 2, 2, 2,102,102, + 102,102,102,102,102,102,102,102, 2, 2, 2, 2, 2, 2,126,126, + 126,126,126,126,126,126,126,126,126, 2, 2,126,126,126,126,126, + 126,126, 2, 2, 2, 2,126,126,126,126,126,126,126, 2,142,142, + 142,142,142,142,142,142,142,142,142,142, 2, 2, 2, 2,125,125, + 125,125,125,125,125,125,125,125,125, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2,125,154,154,154,154,154,154,154, 2, 2,154, + 2, 2,154,154,154,154,154,154,154,154, 2,154,154, 2,154,154, + 154,154,154,154,154,154,154,154,154,154,154,154, 2,154,154, 2, + 2,154,154,154,154,154,154,154, 2, 2, 2, 2, 2, 2,150,150, + 150,150,150,150,150,150, 2, 2,150,150,150,150,150,150,150,150, + 150,150,150, 2, 2, 2,141,141,141,141,141,141,141,141,140,140, + 140,140,140,140,140,140,140,140,140, 2, 2, 2, 2, 2,121,121, + 121,121,121,121,121,121,121, 2, 2, 2, 2, 2, 2, 2, 7, 7, + 2, 2, 2, 2, 2, 2,133,133,133,133,133,133,133,133,133, 2, + 133,133,133,133,133,133,133,133,133,133,133,133,133, 2,133,133, + 133,133,133,133, 2, 2,133,133,133,133,133, 2, 2, 2,134,134, + 134,134,134,134,134,134, 2, 2,134,134,134,134,134,134, 2,134, + 134,134,134,134,134,134,134,134,134,134,134,134,134, 2,138,138, + 138,138,138,138,138, 2,138,138, 2,138,138,138,138,138,138,138, + 138,138,138,138,138,138, 2, 2,138, 2,138,138, 2,138,138,138, + 2, 2, 2, 2, 2, 2,143,143,143,143,143,143, 2,143,143, 2, + 143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143, + 143,143,143,143,143, 2,143,143, 2,143,143,143,143,143,143, 2, + 2, 2, 2, 2, 2, 2,143,143, 2, 2, 2, 2, 2, 2,145,145, + 145,145,145,145,145,145,145, 2, 2, 2, 2, 2, 2, 2,163,163, + 163,163,163,163,163,163,163, 2,163,163,163,163,163,163,163,163, + 163, 2, 2, 2,163,163,163,163, 2, 2, 2, 2, 2, 2, 86, 2, + 2, 2, 2, 2, 2, 2, 22, 22, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 22, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, + 2, 2, 2, 2, 2, 2, 63, 63, 63, 63, 63, 63, 63, 2, 63, 63, + 63, 63, 63, 2, 2, 2, 63, 63, 63, 63, 2, 2, 2, 2,157,157, + 157,157,157,157,157,157,157,157,157, 2, 2, 2, 2, 2, 80, 80, + 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 2, 2,127,127, + 127,127,127,127,127,127,127,127,127,127,127,127,127, 2, 79, 2, + 2, 2, 2, 2, 2, 2,115,115,115,115,115,115,115,115,115,115, + 115,115,115,115,115, 2,115,115, 2, 2, 2, 2,115,115,159,159, + 159,159,159,159,159,159,159,159,159,159,159,159,159, 2,159,159, + 2, 2, 2, 2, 2, 2,103,103,103,103,103,103,103,103,103,103, + 103,103,103,103, 2, 2,119,119,119,119,119,119,119,119,119,119, + 119,119,119,119, 2, 2,119,119, 2,119,119,119,119,119, 2, 2, + 2, 2, 2,119,119,119,146,146,146,146,146,146,146,146,146,146, + 146, 2, 2, 2, 2, 2, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, + 99, 2, 2, 2, 2, 99, 2, 2, 2, 2, 2, 2, 2, 99,136,139, + 13, 13,155, 2, 2, 2,136,136,136,136,136,136,136,136,155,155, + 155,155,155,155,155,155,155,155,155,155,155,155, 2, 2,136, 2, + 2, 2, 2, 2, 2, 2, 17, 17, 17, 17, 2, 17, 17, 17, 17, 17, + 17, 17, 2, 17, 17, 2, 17, 15, 15, 15, 15, 15, 15, 15, 17, 17, + 17, 2, 2, 2, 2, 2, 2, 2, 15, 2, 2, 2, 2, 2, 15, 15, + 15, 2, 2, 17, 2, 2, 2, 2, 2, 2, 17, 17, 17, 17,139,139, + 139,139,139,139,139,139,139,139,139,139, 2, 2, 2, 2,105,105, + 105,105,105,105,105,105,105,105,105, 2, 2, 2, 2, 2,105,105, + 105,105,105, 2, 2, 2,105, 2, 2, 2, 2, 2, 2, 2,105,105, + 2, 2,105,105,105,105, 1, 1, 1, 1, 1, 1, 2, 2, 0, 0, + 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, + 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 2, 2, + 0, 2, 2, 0, 0, 2, 2, 0, 0, 0, 0, 2, 0, 0, 0, 0, + 2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, + 0, 2, 2, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 2, 0, 0, + 0, 0, 0, 2, 0, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 2, + 0, 0, 0, 0, 0, 0,131,131,131,131,131,131,131,131,131,131, + 131,131, 2, 2, 2, 2, 2, 2, 2,131,131,131,131,131, 2,131, + 131,131,131,131,131,131, 2, 2, 2, 2, 2, 19, 19, 19, 56, 56, + 56, 56, 56, 56, 56, 2, 56, 2, 2, 56, 56, 56, 56, 56, 56, 56, + 2, 56, 56, 2, 56, 56, 56, 56, 56, 2, 2, 2, 2, 2, 6, 6, + 6, 6, 6, 6, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6,151,151, + 151,151,151,151,151,151,151,151,151,151,151, 2, 2, 2,151,151, + 151,151,151,151, 2, 2,151,151, 2, 2, 2, 2,151,151,160,160, + 160,160,160,160,160,160,160,160,160,160,160,160,160, 2,152,152, + 152,152,152,152,152,152,152,152, 2, 2, 2, 2, 2,152,164,164, + 164,164,164,164,164,164,164,164, 2, 2, 2, 2, 2, 2, 30, 30, + 30, 30, 2, 30, 30, 2,113,113,113,113,113,113,113,113,113,113, + 113,113,113, 2, 2,113,113,113,113,113,113,113,113, 2,132,132, + 132,132,132,132,132,132,132,132,132,132, 2, 2, 2, 2,132,132, + 2, 2, 2, 2,132,132, 3, 3, 3, 3, 2, 3, 3, 3, 2, 3, + 3, 2, 3, 2, 2, 3, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 2, 3, 3, 3, 3, 2, 3, 2, 3, 2, 2, 2, 2, 2, 2, + 3, 2, 2, 2, 2, 3, 2, 3, 2, 3, 2, 3, 3, 3, 2, 3, + 2, 3, 2, 3, 2, 3, 2, 3, 3, 3, 3, 2, 3, 2, 3, 3, + 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 3, + 3, 3, 2, 3, 3, 3, 2, 2, 2, 2, 2, 2, 0, 0, 15, 0, + 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 2, 2, + 2, 0, 0, 0, 0, 0, 13, 2, 2, 2, 2, 2, 2, 2, 13, 13, + 13, 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 0, 1, + 2, 3, 4, 5, 6, 7, 8, 9, 9, 9, 9, 10, 9, 11, 12, 13, + 9, 9, 9, 14, 9, 9, 15, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 16, 17, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 18, 19, 20, 9, 21, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 22, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 23, 24, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, + 9, 10, 11, 12, 0, 0, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 23, 0, 0, 24, 25, 26, 27, 28, 29, 30, 0, 0, 31, 32, 0, 33, + 0, 34, 0, 35, 0, 0, 0, 0, 36, 37, 38, 39, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 41, 42, 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, 43, 44, + 0, 45, 0, 0, 0, 0, 0, 0, 46, 47, 0, 0, 0, 0, 0, 48, + 0, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 50, 51, 0, 0, 0, 52, 0, 0, 53, 0, 0, 0, 0, 0, 0, 0, + 54, 0, 0, 0, 0, 0, 0, 0, 55, 0, 0, 0, 0, 0, 0, 0, + 56, 0, 0, 0, 0, 0, 0, 0, 0, 57, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 58, 59, 60, 61, 62, 63, 64, 65, 0, 0, 0, 0, 0, 0, 66, 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, 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, 67, 68, 0, 69, + 70, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 71, 72, 73, 74, + 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, + 91, 92, 93, 94, 95, 96, 97, 98, 99,100,101,102,103, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,104, 0, + 0, 0, 0, 0, 0,105,106, 0,107, 0, 0, 0,108, 0,109, 0, + 110, 0,111,112,113, 0,114, 0, 0, 0,115, 0, 0, 0,116, 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,117, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 118,119,120,121, 0,122,123,124,125,126, 0,127, 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,128,129,130,131, + 132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147, + 148,149,150,151,152,153,154,155,156,157, 0, 0, 0,158,159,160, + 161, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0,162,163, 0, 0, 0, 0, 0, 0, 0,164, 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, + 165, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0,166, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0,167, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,168, + 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,169,170, 0, 0, 0, 0,171,172, 0, 0, 0,173,174,175,176, + 177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192, + 193,194,195,196,197,198,199,200,201,202,203,204,205,206, 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, 1, 2, 3, 4, +}; +static const uint16_t +_hb_ucd_u16[9320] = +{ + 0, 0, 1, 2, 3, 4, 5, 6, 0, 0, 7, 8, 9, 10, 11, 12, + 13, 13, 13, 14, 15, 13, 13, 16, 17, 18, 19, 20, 21, 22, 13, 23, + 13, 13, 13, 24, 25, 11, 11, 11, 11, 26, 11, 27, 28, 29, 30, 31, + 32, 32, 32, 32, 32, 32, 32, 33, 34, 35, 36, 11, 37, 38, 13, 39, + 9, 9, 9, 11, 11, 11, 13, 13, 40, 13, 13, 13, 41, 13, 13, 13, + 13, 13, 13, 42, 9, 43, 11, 11, 44, 45, 32, 46, 47, 48, 49, 50, + 51, 52, 48, 48, 53, 32, 54, 55, 48, 48, 48, 48, 48, 56, 57, 58, + 59, 60, 48, 32, 61, 48, 48, 48, 48, 48, 62, 63, 64, 48, 65, 66, + 48, 67, 68, 69, 48, 70, 71, 48, 72, 73, 48, 48, 74, 32, 75, 32, + 76, 48, 48, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, + 90, 83, 84, 91, 92, 93, 94, 95, 96, 97, 84, 98, 99, 100, 88, 101, + 102, 83, 84, 103, 104, 105, 88, 106, 107, 108, 109, 110, 111, 112, 94, 113, + 114, 115, 84, 116, 117, 118, 88, 119, 120, 115, 84, 121, 122, 123, 88, 124, + 125, 115, 48, 126, 127, 128, 88, 129, 130, 131, 48, 132, 133, 134, 94, 135, + 136, 48, 48, 137, 138, 139, 140, 140, 141, 48, 142, 143, 144, 145, 140, 140, + 146, 147, 148, 149, 150, 48, 151, 152, 153, 154, 32, 155, 156, 157, 140, 140, + 48, 48, 158, 159, 160, 161, 162, 163, 164, 165, 9, 9, 166, 11, 11, 167, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 168, 169, 48, 48, + 168, 48, 48, 170, 171, 172, 48, 48, 48, 171, 48, 48, 48, 173, 174, 175, + 48, 176, 9, 9, 9, 9, 9, 177, 178, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 179, 48, 180, 181, 48, 48, 48, 48, 182, 183, + 48, 184, 48, 185, 48, 186, 187, 188, 48, 48, 48, 189, 190, 191, 192, 193, + 194, 192, 48, 48, 195, 48, 48, 196, 197, 48, 198, 48, 48, 48, 48, 199, + 48, 200, 201, 202, 203, 48, 204, 205, 48, 48, 206, 48, 207, 208, 209, 209, + 48, 210, 48, 48, 48, 211, 212, 213, 192, 192, 214, 215, 216, 140, 140, 140, + 217, 48, 48, 218, 219, 160, 220, 221, 222, 48, 223, 64, 48, 48, 224, 225, + 48, 48, 226, 227, 228, 64, 48, 229, 230, 9, 9, 231, 232, 233, 234, 235, + 11, 11, 236, 27, 27, 27, 237, 238, 11, 239, 27, 27, 32, 32, 32, 32, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 240, 13, 13, 13, 13, 13, 13, + 241, 242, 241, 241, 242, 243, 241, 244, 245, 245, 245, 246, 247, 248, 249, 250, + 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 261, 262, 263, 264, 265, + 266, 267, 268, 269, 270, 271, 272, 272, 273, 274, 275, 209, 276, 277, 209, 278, + 279, 279, 279, 279, 279, 279, 279, 279, 280, 209, 281, 209, 209, 209, 209, 282, + 209, 283, 279, 284, 209, 285, 286, 209, 209, 209, 287, 140, 288, 140, 271, 271, + 271, 289, 209, 209, 209, 209, 290, 271, 209, 209, 209, 209, 209, 209, 209, 209, + 209, 209, 209, 291, 292, 209, 209, 293, 209, 209, 209, 209, 209, 209, 294, 209, + 209, 209, 209, 209, 209, 209, 295, 296, 271, 297, 209, 209, 298, 279, 299, 279, + 300, 301, 279, 279, 279, 302, 279, 303, 209, 209, 209, 279, 304, 209, 209, 305, + 209, 306, 209, 209, 209, 209, 209, 209, 9, 9, 9, 11, 11, 11, 307, 308, + 13, 13, 13, 13, 13, 13, 309, 310, 11, 11, 311, 48, 48, 48, 312, 313, + 48, 314, 315, 315, 315, 315, 32, 32, 316, 317, 318, 319, 320, 321, 140, 140, + 209, 322, 209, 209, 209, 209, 209, 323, 209, 209, 209, 209, 209, 324, 140, 325, + 326, 327, 328, 329, 136, 48, 48, 48, 48, 330, 178, 48, 48, 48, 48, 331, + 332, 48, 48, 136, 48, 48, 48, 48, 200, 333, 48, 48, 209, 209, 323, 48, + 209, 334, 335, 209, 336, 337, 209, 209, 335, 209, 209, 337, 209, 209, 209, 209, + 48, 48, 48, 48, 209, 209, 209, 209, 48, 338, 48, 48, 48, 48, 48, 48, + 151, 209, 209, 209, 287, 48, 48, 229, 339, 48, 340, 140, 13, 13, 341, 342, + 13, 343, 48, 48, 48, 48, 344, 345, 31, 346, 347, 348, 13, 13, 13, 349, + 350, 351, 352, 353, 354, 355, 140, 356, 357, 48, 358, 359, 48, 48, 48, 360, + 361, 48, 48, 362, 363, 192, 32, 364, 64, 48, 365, 48, 366, 367, 48, 151, + 76, 48, 48, 368, 369, 370, 371, 372, 48, 48, 373, 374, 375, 376, 48, 377, + 48, 48, 48, 378, 379, 380, 381, 382, 383, 384, 315, 11, 11, 385, 386, 11, + 11, 11, 11, 11, 48, 48, 387, 192, 48, 48, 388, 48, 389, 48, 48, 206, + 390, 390, 390, 390, 390, 390, 390, 390, 391, 391, 391, 391, 391, 391, 391, 391, + 48, 48, 48, 48, 48, 48, 204, 48, 48, 48, 48, 48, 48, 207, 140, 140, + 392, 393, 394, 395, 396, 48, 48, 48, 48, 48, 48, 397, 398, 399, 48, 48, + 48, 48, 48, 400, 209, 48, 48, 48, 48, 401, 48, 48, 402, 140, 140, 403, + 32, 404, 32, 405, 406, 407, 408, 409, 48, 48, 48, 48, 48, 48, 48, 410, + 411, 2, 3, 4, 5, 412, 413, 414, 48, 415, 48, 200, 416, 417, 418, 419, + 420, 48, 172, 421, 204, 204, 140, 140, 48, 48, 48, 48, 48, 48, 48, 71, + 422, 271, 271, 423, 272, 272, 272, 424, 425, 426, 427, 140, 140, 209, 209, 428, + 140, 140, 140, 140, 140, 140, 140, 140, 48, 151, 48, 48, 48, 100, 429, 430, + 48, 48, 431, 48, 432, 48, 48, 433, 48, 434, 48, 48, 435, 436, 140, 140, + 9, 9, 437, 11, 11, 48, 48, 48, 48, 204, 192, 9, 9, 438, 11, 439, + 48, 48, 440, 48, 48, 48, 441, 442, 442, 443, 444, 445, 140, 140, 140, 140, + 48, 48, 48, 314, 48, 199, 440, 140, 446, 27, 27, 447, 140, 140, 140, 140, + 448, 48, 48, 449, 48, 450, 48, 451, 48, 200, 452, 140, 140, 140, 48, 453, + 48, 454, 48, 455, 140, 140, 140, 140, 48, 48, 48, 456, 271, 457, 271, 271, + 458, 459, 48, 460, 461, 462, 48, 463, 48, 464, 140, 140, 465, 48, 466, 467, + 48, 48, 48, 468, 48, 469, 48, 470, 48, 471, 472, 140, 140, 140, 140, 140, + 48, 48, 48, 48, 196, 140, 140, 140, 9, 9, 9, 473, 11, 11, 11, 474, + 48, 48, 475, 192, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 271, 476, + 48, 48, 477, 478, 140, 140, 140, 479, 48, 464, 480, 48, 62, 481, 140, 48, + 482, 140, 140, 48, 483, 140, 48, 314, 484, 48, 48, 485, 486, 457, 487, 488, + 222, 48, 48, 489, 490, 48, 196, 192, 491, 48, 492, 493, 494, 48, 48, 495, + 222, 48, 48, 496, 497, 498, 499, 500, 48, 97, 501, 502, 503, 140, 140, 140, + 504, 505, 506, 48, 48, 507, 508, 192, 509, 83, 84, 510, 511, 512, 513, 514, + 48, 48, 48, 515, 516, 517, 478, 140, 48, 48, 48, 518, 519, 192, 140, 140, + 48, 48, 520, 521, 522, 523, 140, 140, 48, 48, 48, 524, 525, 192, 526, 140, + 48, 48, 527, 528, 192, 140, 140, 140, 48, 173, 529, 530, 314, 140, 140, 140, + 48, 48, 501, 531, 140, 140, 140, 140, 140, 140, 9, 9, 11, 11, 148, 532, + 533, 534, 48, 535, 536, 192, 140, 140, 140, 140, 537, 48, 48, 538, 539, 140, + 540, 48, 48, 541, 542, 543, 48, 48, 544, 545, 546, 48, 48, 48, 48, 196, + 547, 140, 140, 140, 140, 140, 140, 140, 84, 48, 520, 548, 549, 148, 175, 550, + 48, 551, 552, 553, 140, 140, 140, 140, 554, 48, 48, 555, 556, 192, 557, 48, + 558, 559, 192, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 48, 560, + 561, 115, 48, 562, 563, 192, 140, 140, 140, 140, 140, 100, 271, 564, 565, 566, + 48, 207, 140, 140, 140, 140, 140, 140, 272, 272, 272, 272, 272, 272, 567, 568, + 48, 48, 48, 48, 388, 140, 140, 140, 140, 48, 48, 48, 48, 48, 48, 569, + 48, 48, 48, 570, 571, 572, 140, 140, 48, 48, 48, 48, 314, 140, 140, 140, + 48, 48, 48, 196, 48, 200, 370, 48, 48, 48, 48, 200, 192, 48, 204, 573, + 48, 48, 48, 574, 575, 576, 577, 578, 48, 140, 140, 140, 140, 140, 140, 140, + 140, 140, 140, 140, 9, 9, 11, 11, 271, 579, 140, 140, 140, 140, 140, 140, + 48, 48, 48, 48, 580, 581, 582, 582, 583, 584, 140, 140, 140, 140, 585, 586, + 48, 48, 48, 48, 48, 48, 48, 440, 48, 48, 48, 48, 48, 199, 140, 140, + 196, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 587, + 48, 48, 588, 589, 140, 590, 591, 48, 48, 48, 48, 48, 48, 48, 48, 206, + 48, 48, 48, 48, 48, 48, 71, 151, 196, 592, 593, 140, 140, 140, 140, 140, + 32, 32, 594, 32, 595, 209, 209, 209, 209, 209, 209, 209, 323, 140, 140, 140, + 209, 209, 209, 209, 209, 209, 209, 324, 209, 209, 596, 209, 209, 209, 597, 598, + 599, 209, 600, 209, 209, 209, 288, 140, 209, 209, 209, 209, 601, 140, 140, 140, + 140, 140, 140, 140, 271, 602, 271, 602, 209, 209, 209, 209, 209, 287, 271, 461, + 9, 603, 11, 604, 605, 606, 241, 9, 607, 608, 609, 610, 611, 9, 603, 11, + 612, 613, 11, 614, 615, 616, 617, 9, 618, 11, 9, 603, 11, 604, 605, 11, + 241, 9, 607, 617, 9, 618, 11, 9, 603, 11, 619, 9, 620, 621, 622, 623, + 11, 624, 9, 625, 626, 627, 628, 11, 629, 9, 630, 11, 631, 632, 632, 632, + 32, 32, 32, 633, 32, 32, 634, 635, 636, 637, 45, 140, 140, 140, 140, 140, + 638, 639, 640, 140, 140, 140, 140, 140, 641, 642, 643, 27, 27, 27, 644, 140, + 645, 140, 140, 140, 140, 140, 140, 140, 48, 48, 151, 646, 647, 140, 140, 140, + 140, 48, 648, 140, 48, 48, 649, 650, 140, 140, 140, 140, 140, 48, 651, 192, + 140, 140, 140, 140, 140, 140, 652, 200, 48, 48, 48, 48, 653, 595, 140, 140, + 9, 9, 607, 11, 654, 370, 140, 140, 140, 140, 140, 140, 140, 140, 140, 499, + 271, 271, 655, 656, 140, 140, 140, 140, 499, 271, 657, 658, 140, 140, 140, 140, + 659, 48, 660, 661, 662, 663, 664, 665, 666, 206, 667, 206, 140, 140, 140, 668, + 209, 209, 325, 209, 209, 209, 209, 209, 209, 323, 334, 669, 669, 669, 209, 324, + 670, 209, 209, 209, 209, 209, 209, 209, 209, 209, 671, 140, 140, 140, 672, 209, + 673, 209, 209, 325, 674, 675, 324, 140, 209, 209, 209, 209, 209, 209, 209, 676, + 209, 209, 209, 209, 209, 677, 426, 426, 209, 209, 209, 209, 209, 209, 209, 678, + 209, 209, 209, 209, 209, 176, 325, 427, 325, 209, 209, 209, 679, 176, 209, 209, + 679, 209, 671, 675, 140, 140, 140, 140, 209, 209, 209, 209, 209, 323, 671, 426, + 674, 209, 209, 680, 681, 325, 674, 674, 209, 682, 209, 209, 288, 140, 140, 192, + 48, 48, 48, 48, 48, 48, 140, 140, 48, 48, 48, 207, 48, 48, 48, 48, + 48, 204, 48, 48, 48, 48, 48, 48, 48, 48, 478, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 100, 140, 48, 204, 140, 140, 140, 140, 140, 140, + 48, 48, 48, 48, 71, 48, 48, 48, 48, 48, 48, 140, 140, 140, 140, 140, + 683, 140, 570, 570, 570, 570, 570, 570, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 140, 391, 391, 391, 391, 391, 391, 391, 684, + 391, 391, 391, 391, 391, 391, 391, 685, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 2, 2, 3, 1, 2, 2, 3, 0, 0, 0, 0, 0, 4, 0, 4, + 2, 2, 5, 2, 2, 2, 5, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, + 0, 0, 0, 0, 7, 8, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 10, 11, 12, 13, 14, 14, 15, 14, 14, 14, + 14, 14, 14, 14, 16, 17, 14, 14, 18, 18, 18, 18, 18, 18, 18, 18, + 18, 18, 18, 18, 18, 18, 18, 18, 19, 18, 18, 18, 18, 18, 18, 18, + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 20, 21, + 21, 21, 22, 20, 21, 21, 21, 21, 21, 23, 24, 25, 25, 25, 25, 25, + 25, 26, 25, 25, 25, 27, 28, 26, 29, 30, 31, 32, 31, 31, 31, 31, + 33, 34, 35, 31, 31, 31, 36, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 29, 31, 31, 31, 31, 37, 38, 37, 37, 37, 37, 37, 37, + 37, 39, 31, 31, 31, 31, 31, 31, 40, 40, 40, 40, 40, 40, 41, 26, + 42, 42, 42, 42, 42, 42, 42, 43, 44, 44, 44, 44, 44, 45, 44, 46, + 47, 47, 47, 48, 37, 49, 31, 31, 31, 50, 51, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 52, 31, 31, 31, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 54, 53, 55, 53, 53, 53, 56, 57, 58, 59, 59, 60, 61, 62, + 57, 63, 64, 65, 66, 59, 59, 67, 68, 69, 70, 71, 71, 72, 73, 74, + 69, 75, 76, 77, 78, 71, 79, 26, 80, 81, 82, 83, 83, 84, 85, 86, + 81, 87, 88, 26, 89, 83, 90, 91, 92, 93, 94, 95, 95, 96, 97, 98, + 93, 99, 100, 101, 102, 95, 95, 26, 103, 104, 105, 106, 107, 104, 108, 109, + 104, 105, 110, 26, 111, 108, 108, 112, 113, 114, 115, 113, 113, 115, 113, 116, + 114, 117, 118, 119, 120, 113, 121, 113, 122, 123, 124, 122, 122, 124, 125, 126, + 123, 127, 128, 128, 129, 122, 130, 26, 131, 132, 133, 131, 131, 131, 131, 131, + 132, 133, 134, 131, 135, 131, 131, 131, 136, 137, 138, 139, 137, 137, 140, 141, + 138, 142, 143, 137, 144, 137, 145, 26, 146, 147, 147, 147, 147, 147, 147, 148, + 147, 147, 147, 149, 26, 26, 26, 26, 150, 151, 152, 152, 153, 152, 152, 154, + 155, 156, 152, 157, 26, 26, 26, 26, 158, 158, 158, 158, 158, 158, 158, 158, + 158, 159, 158, 158, 158, 160, 159, 158, 158, 158, 158, 159, 158, 158, 158, 161, + 158, 161, 162, 163, 26, 26, 26, 26, 164, 164, 164, 164, 164, 164, 164, 164, + 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 165, 165, 165, 165, + 166, 167, 165, 165, 165, 165, 165, 168, 169, 169, 169, 169, 169, 169, 169, 169, + 169, 169, 169, 169, 169, 169, 169, 169, 170, 170, 170, 170, 170, 170, 170, 170, + 170, 171, 172, 171, 170, 170, 170, 170, 170, 171, 170, 170, 170, 170, 171, 172, + 171, 170, 172, 170, 170, 170, 170, 170, 170, 170, 171, 170, 170, 170, 170, 170, + 170, 170, 170, 173, 170, 170, 170, 174, 170, 170, 170, 175, 176, 176, 176, 176, + 176, 176, 176, 176, 176, 176, 177, 177, 178, 178, 178, 178, 178, 178, 178, 178, + 178, 178, 178, 178, 178, 178, 178, 178, 179, 179, 179, 180, 181, 181, 181, 181, + 181, 181, 181, 181, 181, 182, 181, 183, 184, 184, 185, 186, 187, 187, 188, 26, + 189, 189, 190, 26, 191, 192, 193, 26, 194, 194, 194, 194, 194, 194, 194, 194, + 194, 194, 194, 195, 194, 196, 194, 196, 197, 198, 198, 199, 198, 198, 198, 198, + 198, 198, 198, 198, 198, 198, 198, 200, 198, 198, 198, 198, 198, 201, 178, 178, + 178, 178, 178, 178, 178, 178, 202, 26, 203, 203, 203, 204, 203, 205, 203, 205, + 206, 203, 207, 207, 207, 208, 209, 26, 210, 210, 210, 210, 210, 211, 210, 210, + 210, 212, 210, 213, 194, 194, 194, 194, 214, 214, 214, 215, 216, 216, 216, 216, + 216, 216, 216, 217, 216, 216, 216, 218, 216, 219, 216, 219, 216, 220, 9, 9, + 9, 221, 26, 26, 26, 26, 26, 26, 222, 222, 222, 222, 222, 222, 222, 222, + 222, 223, 222, 222, 222, 222, 222, 224, 225, 225, 225, 225, 225, 225, 225, 225, + 226, 226, 226, 226, 226, 226, 227, 228, 229, 229, 229, 229, 229, 229, 229, 230, + 229, 231, 232, 232, 232, 232, 232, 232, 18, 233, 165, 165, 165, 165, 165, 234, + 225, 26, 235, 9, 236, 237, 238, 239, 2, 2, 2, 2, 240, 241, 2, 2, + 2, 2, 2, 242, 243, 244, 2, 245, 2, 2, 2, 2, 2, 2, 2, 246, + 9, 9, 9, 9, 9, 9, 9, 9, 14, 14, 247, 247, 14, 14, 14, 14, + 247, 247, 14, 248, 14, 14, 14, 247, 14, 14, 14, 14, 14, 14, 249, 14, + 249, 14, 250, 251, 14, 14, 252, 253, 0, 254, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 0, 256, 257, 0, 258, 2, 259, 0, 0, 0, 0, + 260, 26, 9, 9, 9, 9, 261, 26, 0, 0, 0, 0, 262, 263, 4, 0, + 0, 264, 0, 0, 2, 2, 2, 2, 2, 265, 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, 258, 26, 26, 26, + 0, 266, 26, 26, 0, 0, 0, 0, 267, 267, 267, 267, 267, 267, 267, 267, + 267, 267, 267, 267, 267, 267, 267, 267, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 268, 0, 0, 0, 269, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 270, 270, 270, 270, 270, 270, 270, 270, + 270, 270, 270, 270, 2, 2, 2, 2, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 271, 272, 165, 165, 165, 165, 166, 167, 273, 273, + 273, 273, 273, 273, 273, 274, 275, 274, 170, 170, 172, 26, 172, 172, 172, 172, + 172, 172, 172, 172, 18, 18, 18, 18, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 276, 26, 26, 26, 26, 277, 277, 277, 278, 277, 277, 277, 277, + 277, 277, 277, 277, 277, 277, 279, 26, 277, 277, 277, 277, 277, 277, 277, 277, + 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, + 277, 277, 280, 26, 26, 26, 0, 281, 282, 0, 0, 0, 283, 284, 0, 285, + 286, 287, 287, 287, 287, 287, 287, 287, 287, 287, 288, 289, 290, 291, 291, 291, + 291, 291, 291, 291, 291, 291, 291, 292, 293, 294, 294, 294, 294, 294, 295, 169, + 169, 169, 169, 169, 169, 169, 169, 169, 169, 296, 0, 0, 294, 294, 294, 294, + 0, 0, 0, 0, 281, 26, 291, 291, 169, 169, 169, 296, 0, 0, 0, 0, + 0, 0, 0, 0, 169, 169, 169, 297, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 291, 291, 291, 291, 291, 298, 291, 291, 291, 291, 291, 291, 291, 291, + 291, 291, 291, 0, 0, 0, 0, 0, 277, 277, 277, 277, 277, 277, 277, 277, + 0, 0, 0, 0, 0, 0, 0, 0, 299, 299, 299, 299, 299, 299, 299, 299, + 299, 299, 299, 299, 299, 299, 299, 299, 299, 300, 299, 299, 299, 299, 299, 299, + 301, 26, 302, 302, 302, 302, 302, 302, 303, 303, 303, 303, 303, 303, 303, 303, + 303, 303, 303, 303, 303, 303, 303, 303, 303, 303, 303, 303, 303, 304, 26, 26, + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 305, 305, 305, 305, + 305, 305, 305, 305, 305, 305, 305, 26, 0, 0, 0, 0, 306, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 307, 2, 2, 2, 2, 2, 2, + 2, 308, 309, 310, 26, 26, 311, 2, 312, 312, 312, 312, 312, 313, 0, 314, + 315, 315, 315, 315, 315, 315, 315, 26, 316, 316, 316, 316, 316, 316, 316, 316, + 317, 318, 316, 319, 53, 53, 53, 53, 320, 320, 320, 320, 320, 321, 322, 322, + 322, 322, 323, 324, 169, 169, 169, 325, 326, 326, 326, 326, 326, 326, 326, 326, + 326, 327, 326, 328, 164, 164, 164, 329, 330, 330, 330, 330, 330, 330, 331, 26, + 330, 332, 330, 333, 164, 164, 164, 164, 334, 334, 334, 334, 334, 334, 334, 334, + 335, 26, 26, 336, 337, 337, 338, 26, 339, 339, 339, 26, 172, 172, 2, 2, + 2, 2, 2, 340, 341, 342, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, + 337, 337, 337, 337, 337, 343, 337, 344, 169, 169, 169, 169, 345, 26, 169, 169, + 296, 346, 169, 169, 169, 169, 169, 345, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 277, 277, 277, 277, 277, 277, 277, 277, + 277, 277, 277, 277, 277, 280, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, + 277, 277, 277, 347, 26, 26, 26, 26, 348, 26, 349, 350, 25, 25, 351, 352, + 353, 25, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 354, 26, 355, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 356, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 357, 31, 31, 31, 31, 31, + 31, 358, 26, 26, 26, 26, 31, 31, 9, 9, 0, 314, 9, 359, 0, 0, + 0, 0, 360, 0, 258, 281, 361, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 362, 363, 0, 0, 0, 1, 2, 2, 3, + 1, 2, 2, 3, 364, 291, 290, 291, 291, 291, 291, 365, 169, 169, 169, 296, + 366, 366, 366, 367, 258, 258, 26, 368, 369, 370, 369, 369, 371, 369, 369, 372, + 369, 373, 369, 373, 26, 26, 26, 26, 369, 369, 369, 369, 369, 369, 369, 369, + 369, 369, 369, 369, 369, 369, 369, 374, 375, 0, 0, 0, 0, 0, 376, 0, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 253, 0, 377, 378, 26, 26, 26, + 26, 26, 0, 0, 0, 0, 0, 379, 380, 380, 380, 381, 382, 382, 382, 382, + 382, 382, 383, 26, 384, 0, 0, 281, 385, 385, 385, 385, 386, 387, 388, 388, + 388, 389, 390, 390, 390, 390, 390, 391, 392, 392, 392, 393, 394, 394, 394, 394, + 395, 394, 396, 26, 26, 26, 26, 26, 397, 397, 397, 397, 397, 397, 397, 397, + 397, 397, 398, 398, 398, 398, 398, 398, 399, 399, 399, 400, 399, 401, 402, 402, + 402, 402, 403, 402, 402, 402, 402, 403, 404, 404, 404, 404, 404, 26, 405, 405, + 405, 405, 405, 405, 406, 407, 408, 409, 408, 409, 410, 408, 411, 408, 411, 412, + 26, 26, 26, 26, 26, 26, 26, 26, 413, 413, 413, 413, 413, 413, 413, 413, + 413, 413, 413, 413, 413, 413, 413, 413, 413, 413, 413, 413, 413, 413, 414, 26, + 413, 413, 415, 26, 413, 26, 26, 26, 416, 2, 2, 2, 2, 2, 417, 308, + 26, 26, 26, 26, 26, 26, 26, 26, 418, 419, 420, 420, 420, 420, 421, 422, + 423, 423, 424, 423, 425, 425, 425, 425, 426, 426, 426, 427, 428, 426, 26, 26, + 26, 26, 26, 26, 429, 429, 430, 431, 432, 432, 432, 433, 434, 434, 434, 435, + 26, 26, 26, 26, 26, 26, 26, 26, 436, 436, 436, 436, 437, 437, 437, 438, + 437, 437, 439, 437, 437, 437, 437, 437, 440, 441, 442, 443, 444, 444, 445, 446, + 444, 447, 444, 447, 448, 448, 448, 448, 449, 449, 449, 449, 26, 26, 26, 26, + 450, 450, 450, 450, 451, 452, 451, 26, 453, 453, 453, 453, 453, 453, 454, 455, + 456, 456, 457, 456, 458, 458, 459, 458, 460, 460, 461, 462, 26, 463, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 464, 464, 464, 464, 464, 464, 464, 464, + 464, 465, 26, 26, 26, 26, 26, 26, 466, 466, 466, 466, 466, 466, 467, 26, + 466, 466, 466, 466, 466, 466, 467, 468, 469, 469, 469, 469, 469, 26, 469, 470, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 31, 31, 31, 50, 471, 471, 471, 471, 471, 472, 473, 26, + 26, 26, 26, 26, 26, 26, 26, 474, 475, 475, 475, 475, 475, 26, 476, 476, + 476, 476, 476, 477, 26, 26, 478, 478, 478, 479, 26, 26, 26, 26, 480, 480, + 480, 481, 26, 26, 482, 482, 483, 26, 484, 484, 484, 484, 484, 484, 484, 484, + 484, 485, 486, 484, 484, 484, 485, 487, 488, 488, 488, 488, 488, 488, 488, 488, + 489, 490, 491, 491, 491, 492, 491, 493, 494, 494, 494, 494, 494, 494, 495, 494, + 494, 26, 496, 496, 496, 496, 497, 26, 498, 498, 498, 498, 498, 498, 498, 498, + 498, 498, 498, 498, 499, 137, 500, 26, 501, 501, 502, 501, 501, 501, 501, 501, + 503, 26, 26, 26, 26, 26, 26, 26, 504, 505, 506, 507, 506, 508, 509, 509, + 509, 509, 509, 509, 509, 510, 509, 511, 512, 513, 514, 515, 515, 516, 517, 518, + 513, 519, 520, 521, 522, 523, 523, 26, 524, 524, 524, 524, 524, 524, 524, 524, + 524, 524, 524, 525, 526, 26, 26, 26, 527, 527, 527, 527, 527, 527, 527, 527, + 527, 26, 527, 528, 26, 26, 26, 26, 529, 529, 529, 529, 529, 529, 530, 529, + 529, 529, 529, 530, 26, 26, 26, 26, 531, 531, 531, 531, 531, 531, 531, 531, + 532, 26, 531, 533, 198, 534, 26, 26, 535, 535, 535, 535, 535, 535, 535, 536, + 535, 536, 26, 26, 26, 26, 26, 26, 537, 537, 537, 538, 537, 539, 537, 537, + 540, 26, 26, 26, 26, 26, 26, 26, 541, 541, 541, 541, 541, 541, 541, 542, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 543, 543, 543, 543, + 543, 543, 543, 543, 543, 543, 544, 545, 546, 547, 548, 549, 549, 549, 550, 551, + 546, 26, 549, 552, 26, 26, 26, 26, 26, 26, 26, 26, 553, 554, 553, 553, + 553, 553, 553, 554, 555, 26, 26, 26, 556, 556, 556, 556, 556, 556, 556, 556, + 556, 26, 557, 557, 557, 557, 557, 557, 557, 557, 557, 557, 558, 26, 178, 178, + 559, 559, 559, 559, 559, 559, 559, 560, 53, 561, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 562, 563, 562, 562, 562, 562, 564, 562, + 565, 26, 562, 562, 562, 566, 567, 567, 567, 567, 568, 567, 567, 569, 570, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 571, 572, 573, 573, 573, 573, 571, 574, + 573, 26, 573, 575, 576, 577, 578, 578, 578, 579, 580, 581, 578, 582, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 583, 583, 583, 584, 585, 585, 586, 585, 585, 585, 585, 587, + 585, 585, 585, 588, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 589, 26, + 108, 108, 108, 108, 108, 108, 590, 591, 592, 592, 592, 592, 592, 592, 592, 592, + 592, 592, 592, 592, 592, 592, 592, 592, 592, 592, 592, 593, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 592, 592, 592, 592, 592, 592, 592, 592, + 592, 592, 592, 592, 592, 594, 595, 26, 592, 592, 592, 592, 592, 592, 592, 592, + 596, 26, 26, 26, 26, 26, 26, 26, 26, 26, 597, 597, 597, 597, 597, 597, + 597, 597, 597, 597, 597, 597, 598, 26, 599, 599, 599, 599, 599, 599, 599, 599, + 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, + 599, 599, 600, 26, 26, 26, 26, 26, 601, 601, 601, 601, 601, 601, 601, 601, + 601, 601, 601, 601, 601, 601, 601, 601, 601, 601, 601, 601, 601, 601, 601, 601, + 602, 26, 26, 26, 26, 26, 26, 26, 305, 305, 305, 305, 305, 305, 305, 305, + 305, 305, 305, 305, 305, 305, 305, 305, 305, 305, 305, 305, 305, 305, 305, 603, + 604, 604, 604, 605, 604, 606, 607, 607, 607, 607, 607, 607, 607, 607, 607, 608, + 607, 609, 610, 610, 610, 611, 611, 26, 612, 612, 612, 612, 612, 612, 612, 612, + 613, 26, 612, 614, 614, 612, 612, 615, 612, 612, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 616, 616, 616, 616, 616, 616, 616, 616, 616, 616, 616, 617, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 618, 618, 618, 618, 618, 618, 618, 618, + 618, 619, 618, 618, 618, 618, 618, 618, 618, 620, 618, 618, 26, 26, 26, 26, + 26, 26, 26, 26, 621, 26, 347, 26, 622, 622, 622, 622, 622, 622, 622, 622, + 622, 622, 622, 622, 622, 622, 622, 622, 622, 622, 622, 622, 622, 622, 622, 622, + 622, 622, 622, 622, 622, 622, 622, 26, 623, 623, 623, 623, 623, 623, 623, 623, + 623, 623, 623, 623, 623, 623, 623, 623, 623, 623, 623, 623, 623, 623, 623, 623, + 623, 623, 624, 26, 26, 26, 26, 26, 622, 625, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 626, 627, 628, 287, 287, 287, 287, 287, 287, 287, + 287, 287, 287, 287, 287, 287, 287, 287, 287, 287, 287, 287, 287, 287, 287, 287, + 287, 287, 287, 287, 287, 287, 287, 287, 287, 287, 287, 287, 629, 26, 630, 26, + 26, 26, 631, 26, 632, 26, 633, 633, 633, 633, 633, 633, 633, 633, 633, 633, + 633, 633, 633, 633, 633, 633, 633, 633, 633, 633, 633, 633, 633, 633, 633, 633, + 633, 633, 633, 633, 633, 633, 633, 634, 635, 635, 635, 635, 635, 635, 635, 635, + 635, 635, 635, 635, 635, 636, 635, 637, 635, 638, 635, 639, 281, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 9, 9, 9, 9, 9, 640, 9, 9, + 221, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 281, 26, 26, 26, 26, 26, 26, 26, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 276, 26, 0, 0, 0, 0, 258, 363, 0, 0, + 0, 0, 0, 0, 641, 642, 0, 643, 644, 645, 0, 0, 0, 646, 0, 0, + 0, 0, 0, 0, 0, 266, 26, 26, 14, 14, 14, 14, 14, 14, 14, 14, + 247, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 0, 0, 281, 26, 0, 0, 281, 26, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 258, 26, 0, 0, 0, 260, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 647, 648, 0, 649, + 650, 0, 0, 0, 0, 0, 0, 0, 269, 651, 255, 255, 0, 0, 0, 652, + 653, 654, 655, 0, 0, 0, 0, 0, 0, 0, 0, 0, 276, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 268, 0, 0, 0, 0, 0, 0, 656, 656, 656, 656, 656, 656, 656, 656, + 656, 656, 656, 656, 656, 656, 656, 656, 656, 657, 26, 658, 659, 656, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 2, 2, 2, 348, 660, 308, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 661, 270, 270, 662, 663, 664, 18, 18, + 18, 18, 18, 18, 18, 665, 26, 26, 26, 666, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 667, 667, 667, 667, 667, 668, 667, 669, + 667, 670, 26, 26, 26, 26, 26, 26, 26, 26, 671, 671, 671, 672, 26, 26, + 673, 673, 673, 673, 673, 673, 673, 674, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 675, 675, 675, 675, 675, 676, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 172, 677, 170, 172, 678, 678, 678, 678, 678, 678, 678, 678, + 678, 678, 678, 678, 678, 678, 678, 678, 678, 678, 678, 678, 678, 678, 678, 678, + 679, 678, 680, 26, 26, 26, 26, 26, 681, 681, 681, 681, 681, 681, 681, 681, + 681, 682, 681, 683, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 363, 0, 0, 0, 0, 0, 0, 0, 377, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 363, 0, 0, 0, 0, 0, 0, 276, + 26, 26, 26, 26, 26, 26, 26, 26, 684, 31, 31, 31, 685, 686, 687, 688, + 689, 690, 685, 691, 685, 687, 687, 692, 31, 693, 31, 694, 695, 693, 31, 694, + 26, 26, 26, 26, 26, 26, 51, 26, 0, 0, 0, 0, 0, 281, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 281, 26, 0, 258, 363, 0, + 363, 0, 363, 0, 0, 0, 276, 26, 0, 0, 0, 0, 0, 276, 26, 26, + 26, 26, 26, 26, 696, 0, 0, 0, 697, 26, 0, 0, 0, 0, 0, 281, + 0, 260, 314, 26, 276, 26, 26, 26, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 698, 0, 377, 0, 377, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 258, 699, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 314, 0, 281, 260, 26, 0, 281, 0, 0, 0, 0, 0, 0, + 0, 26, 0, 314, 0, 0, 0, 0, 0, 26, 0, 0, 0, 276, 314, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 281, 26, 0, 276, 0, 377, 0, 260, 0, 0, 0, 0, 0, 269, + 276, 696, 0, 281, 0, 260, 0, 260, 0, 0, 360, 0, 0, 0, 0, 0, + 0, 266, 26, 26, 26, 26, 0, 314, 277, 277, 277, 277, 277, 277, 277, 277, + 277, 277, 277, 277, 26, 26, 26, 26, 277, 277, 277, 277, 277, 277, 277, 347, + 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 280, 277, 277, 277, 277, + 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 347, 26, 277, 277, + 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, + 277, 277, 277, 277, 700, 26, 26, 26, 277, 277, 277, 280, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 277, 277, 277, 277, 277, 277, 277, 277, + 277, 701, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 702, 26, 26, 26, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 939, 940, 941, 942, 946, 948, 0, 962, + 969, 970, 971, 976,1001,1002,1003,1008, 0,1033,1040,1041,1042,1043,1047, 0, + 0,1080,1081,1082,1086,1110, 0, 0,1124,1125,1126,1127,1131,1133, 0,1147, + 1154,1155,1156,1161,1187,1188,1189,1193, 0,1219,1226,1227,1228,1229,1233, 0, + 0,1267,1268,1269,1273,1298, 0,1303, 943,1128, 944,1129, 954,1139, 958,1143, + 959,1144, 960,1145, 961,1146, 964,1149, 0, 0, 973,1158, 974,1159, 975,1160, + 983,1168, 978,1163, 988,1173, 990,1175, 991,1176, 993,1178, 994,1179, 0, 0, + 1004,1190,1005,1191,1006,1192,1014,1199,1007, 0, 0, 0,1016,1201,1020,1206, + 0,1022,1208,1025,1211,1023,1209, 0, 0, 0, 0,1032,1218,1037,1223,1035, + 1221, 0, 0, 0,1044,1230,1045,1231,1049,1235, 0, 0,1058,1244,1064,1250, + 1060,1246,1066,1252,1067,1253,1072,1258,1069,1255,1077,1264,1074,1261, 0, 0, + 1083,1270,1084,1271,1085,1272,1088,1275,1089,1276,1096,1283,1103,1290,1111,1299, + 1115,1118,1307,1120,1309,1121,1310, 0,1053,1239, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0,1093,1280, 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, 949,1134,1010,1195,1050,1236,1090,1277,1341,1368,1340, + 1367,1342,1369,1339,1366, 0,1320,1347,1418,1419,1323,1350, 0, 0, 992,1177, + 1018,1204,1055,1241,1416,1417,1415,1424,1202, 0, 0, 0, 987,1172, 0, 0, + 1031,1217,1321,1348,1322,1349,1338,1365, 950,1135, 951,1136, 979,1164, 980,1165, + 1011,1196,1012,1197,1051,1237,1052,1238,1061,1247,1062,1248,1091,1278,1092,1279, + 1071,1257,1076,1263, 0, 0, 997,1182, 0, 0, 0, 0, 0, 0, 945,1130, + 982,1167,1337,1364,1335,1362,1046,1232,1422,1423,1113,1301, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 8, 9, 0, 10,1425, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,1314,1427, 5, + 1434,1438,1443, 0,1450, 0,1455,1461,1514, 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,1446,1458,1468,1476,1480,1486,1517, 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,1489,1503,1494,1500,1508, 0, 0, 0, 0,1520,1521, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0,1526,1528, 0,1525, 0, 0, 0,1522, + 0, 0, 0, 0,1536,1532,1539, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0,1534, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0,1556, 0, 0, 0, 0, 0, 0,1548,1550, 0,1547, 0, 0, 0,1567, + 0, 0, 0, 0,1558,1554,1561, 0, 0, 0, 0, 0, 0, 0,1568,1569, + 0, 0, 0, 0, 0, 0, 0, 0, 0,1529,1551, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0,1523,1545,1524,1546, 0, 0,1527,1549, + 0, 0,1570,1571,1530,1552,1531,1553, 0, 0,1533,1555,1535,1557,1537,1559, + 0, 0,1572,1573,1544,1566,1538,1560,1540,1562,1541,1563,1542,1564, 0, 0, + 1543,1565, 0, 0, 0, 0, 0, 0, 0, 0,1606,1607,1609,1608,1610, 0, + 0, 0, 0, 0, 0, 0, 0, 0,1613, 0,1611, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1612, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0,1620, 0, 0, 0, 0, 0, 0, 0,1623, 0, 0,1624, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1614,1615,1616,1617,1618,1619,1621,1622, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0,1628,1629, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0,1625,1626, 0,1627, 0, 0, 0,1634, 0, 0,1635, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0,1630,1631,1632, 0, 0,1633, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1639, 0, 0,1638,1640, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0,1636,1637, 0, 0, 0, 0, 0, 0,1641, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0,1642,1644,1643, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1645, 0, 0, 0, 0, 0, 0, 0,1646, 0, 0, 0, 0, 0, 0,1648, + 1649, 0,1647,1650, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0,1651,1653,1652, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0,1654, 0,1655,1657,1656, 0, 0, 0, 0,1659, 0, 0, 0, 0, + 0, 0, 0, 0, 0,1660, 0, 0, 0, 0,1661, 0, 0, 0, 0,1662, + 0, 0, 0, 0,1663, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0,1658, 0, 0, 0, 0, 0, 0, 0, 0, 0,1664, 0,1665,1673, 0, + 1674, 0, 0, 0, 0, 0, 0, 0, 0,1666, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1668, 0, 0, 0, 0, + 0, 0, 0, 0, 0,1669, 0, 0, 0, 0,1670, 0, 0, 0, 0,1671, + 0, 0, 0, 0,1672, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0,1667, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1675, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1676, 0, + 1677, 0,1678, 0,1679, 0,1680, 0, 0, 0,1681, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0,1682, 0,1683, 0, 0,1684,1685, 0,1686, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 953,1138, 955,1140, 956,1141, 957,1142, + 1324,1351, 963,1148, 965,1150, 968,1153, 966,1151, 967,1152,1378,1380,1379,1381, + 984,1169, 985,1170,1420,1421, 986,1171, 989,1174, 995,1180, 998,1183, 996,1181, + 999,1184,1000,1185,1015,1200,1329,1356,1017,1203,1019,1205,1021,1207,1024,1210, + 1687,1688,1027,1213,1026,1212,1028,1214,1029,1215,1030,1216,1034,1220,1036,1222, + 1039,1225,1038,1224,1334,1361,1336,1363,1382,1384,1383,1385,1056,1242,1057,1243, + 1059,1245,1063,1249,1689,1690,1065,1251,1068,1254,1070,1256,1386,1387,1388,1389, + 1691,1692,1073,1259,1075,1262,1079,1266,1078,1265,1095,1282,1098,1285,1097,1284, + 1390,1391,1392,1393,1099,1286,1100,1287,1101,1288,1102,1289,1105,1292,1104,1291, + 1106,1294,1107,1295,1108,1296,1114,1302,1119,1308,1122,1311,1123,1312,1186,1260, + 1293,1305, 0,1394, 0, 0, 0, 0, 952,1137, 947,1132,1317,1344,1316,1343, + 1319,1346,1318,1345,1693,1695,1371,1375,1370,1374,1373,1377,1372,1376,1694,1696, + 981,1166, 977,1162, 972,1157,1326,1353,1325,1352,1328,1355,1327,1354,1697,1698, + 1009,1194,1013,1198,1054,1240,1048,1234,1331,1358,1330,1357,1333,1360,1332,1359, + 1699,1700,1396,1401,1395,1400,1398,1403,1397,1402,1399,1404,1094,1281,1087,1274, + 1406,1411,1405,1410,1408,1413,1407,1412,1409,1414,1109,1297,1117,1306,1116,1304, + 1112,1300, 0, 0, 0, 0, 0, 0,1471,1472,1701,1705,1702,1706,1703,1707, + 1430,1431,1715,1719,1716,1720,1717,1721,1477,1478,1729,1731,1730,1732, 0, 0, + 1435,1436,1733,1735,1734,1736, 0, 0,1481,1482,1737,1741,1738,1742,1739,1743, + 1439,1440,1751,1755,1752,1756,1753,1757,1490,1491,1765,1768,1766,1769,1767,1770, + 1447,1448,1771,1774,1772,1775,1773,1776,1495,1496,1777,1779,1778,1780, 0, 0, + 1451,1452,1781,1783,1782,1784, 0, 0,1504,1505,1785,1788,1786,1789,1787,1790, + 0,1459, 0,1791, 0,1792, 0,1793,1509,1510,1794,1798,1795,1799,1796,1800, + 1462,1463,1808,1812,1809,1813,1810,1814,1467, 21,1475, 22,1479, 23,1485, 24, + 1493, 27,1499, 28,1507, 29, 0, 0,1704,1708,1709,1710,1711,1712,1713,1714, + 1718,1722,1723,1724,1725,1726,1727,1728,1740,1744,1745,1746,1747,1748,1749,1750, + 1754,1758,1759,1760,1761,1762,1763,1764,1797,1801,1802,1803,1804,1805,1806,1807, + 1811,1815,1816,1817,1818,1819,1820,1821,1470,1469,1822,1474,1465, 0,1473,1825, + 1429,1428,1426, 12,1432, 0, 26, 0, 0,1315,1823,1484,1466, 0,1483,1829, + 1433, 13,1437, 14,1441,1826,1827,1828,1488,1487,1513, 19, 0, 0,1492,1515, + 1445,1444,1442, 15, 0,1831,1832,1833,1502,1501,1516, 25,1497,1498,1506,1518, + 1457,1456,1454, 17,1453,1313, 11, 3, 0, 0,1824,1512,1519, 0,1511,1830, + 1449, 16,1460, 18,1464, 4, 0, 0, 30, 31, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, + 0, 0, 2, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0,1834,1835, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0,1836, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0,1837,1839,1838, 0, 0, 0, 0,1840, 0, 0, 0, + 0,1841, 0, 0,1842, 0, 0, 0, 0, 0, 0, 0,1843, 0,1844, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,1845, 0, 0,1846, 0, 0,1847, + 0,1848, 0, 0, 0, 0, 0, 0, 937, 0,1850, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0,1849, 936, 938,1851,1852, 0, 0,1853,1854, 0, 0, + 1855,1856, 0, 0, 0, 0, 0, 0,1857,1858, 0, 0,1861,1862, 0, 0, + 1863,1864, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0,1867,1868,1869,1870,1859,1860,1865,1866, 0, 0, 0, 0, + 0, 0,1871,1872,1873,1874, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 32, 33, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0,1875, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0,1877, 0,1878, 0,1879, 0,1880, 0,1881, 0,1882, 0, + 1883, 0,1884, 0,1885, 0,1886, 0,1887, 0,1888, 0, 0,1889, 0,1890, + 0,1891, 0, 0, 0, 0, 0, 0,1892,1893, 0,1894,1895, 0,1896,1897, + 0,1898,1899, 0,1900,1901, 0, 0, 0, 0, 0, 0,1876, 0, 0, 0, + 0, 0, 0, 0, 0, 0,1902, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0,1904, 0,1905, 0,1906, 0,1907, 0,1908, 0,1909, 0, + 1910, 0,1911, 0,1912, 0,1913, 0,1914, 0,1915, 0, 0,1916, 0,1917, + 0,1918, 0, 0, 0, 0, 0, 0,1919,1920, 0,1921,1922, 0,1923,1924, + 0,1925,1926, 0,1927,1928, 0, 0, 0, 0, 0, 0,1903, 0, 0,1929, + 1930,1931,1932, 0, 0, 0,1933, 0, 710, 385, 724, 715, 455, 103, 186, 825, + 825, 242, 751, 205, 241, 336, 524, 601, 663, 676, 688, 738, 411, 434, 474, 500, + 649, 746, 799, 108, 180, 416, 482, 662, 810, 275, 462, 658, 692, 344, 618, 679, + 293, 388, 440, 492, 740, 116, 146, 168, 368, 414, 481, 527, 606, 660, 665, 722, + 781, 803, 809, 538, 553, 588, 642, 758, 811, 701, 233, 299, 573, 612, 487, 540, + 714, 779, 232, 267, 412, 445, 457, 585, 594, 766, 167, 613, 149, 148, 560, 589, + 648, 768, 708, 345, 411, 704, 105, 259, 313, 496, 518, 174, 542, 120, 307, 101, + 430, 372, 584, 183, 228, 529, 650, 697, 424, 732, 428, 349, 632, 355, 517, 110, + 135, 147, 403, 580, 624, 700, 750, 170, 193, 245, 297, 374, 463, 543, 763, 801, + 812, 815, 162, 384, 420, 730, 287, 330, 337, 366, 459, 476, 509, 558, 591, 610, + 726, 652, 734, 759, 154, 163, 198, 473, 683, 697, 292, 311, 353, 423, 572, 494, + 113, 217, 259, 280, 314, 499, 506, 603, 608, 752, 778, 782, 788, 117, 557, 748, + 774, 320, 109, 126, 260, 265, 373, 411, 479, 523, 655, 737, 823, 380, 765, 161, + 395, 398, 438, 451, 502, 516, 537, 583, 791, 136, 340, 769, 122, 273, 446, 727, + 305, 322, 400, 496, 771, 155, 190, 269, 377, 391, 406, 432, 501, 519, 599, 684, + 687, 749, 776, 175, 452, 191, 480, 510, 659, 772, 805, 813, 397, 444, 619, 566, + 568, 575, 491, 471, 707, 111, 636, 156, 153, 288, 346, 578, 256, 435, 383, 729, + 680, 767, 694, 295, 128, 210, 0, 0, 227, 0, 379, 0, 0, 150, 493, 525, + 544, 551, 552, 556, 783, 576, 604, 0, 661, 0, 703, 0, 0, 735, 743, 0, + 0, 0, 793, 794, 795, 808, 741, 773, 118, 127, 130, 166, 169, 177, 207, 213, + 215, 226, 229, 268, 270, 317, 327, 329, 335, 369, 375, 381, 404, 441, 448, 458, + 477, 484, 503, 539, 545, 547, 546, 548, 549, 550, 554, 555, 561, 564, 569, 591, + 593, 595, 598, 607, 620, 625, 625, 651, 690, 695, 705, 706, 716, 717, 733, 735, + 777, 786, 790, 315, 869, 623, 0, 0, 102, 145, 134, 115, 129, 138, 165, 171, + 207, 202, 206, 212, 227, 231, 240, 243, 250, 254, 294, 296, 303, 308, 319, 325, + 321, 329, 326, 335, 341, 357, 360, 362, 370, 379, 388, 389, 393, 421, 424, 438, + 456, 454, 458, 465, 477, 535, 485, 490, 493, 507, 512, 514, 521, 522, 525, 526, + 528, 533, 532, 541, 565, 569, 574, 586, 591, 597, 607, 637, 647, 674, 691, 693, + 695, 698, 703, 699, 705, 704, 702, 706, 709, 717, 728, 736, 747, 754, 770, 777, + 783, 784, 786, 787, 790, 802, 825, 848, 847, 857, 55, 65, 66, 883, 892, 916, + 822, 824, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0,1586, 0,1605, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0,1602,1603,1934,1935,1574,1575,1576,1577,1579,1580,1581,1583,1584, 0, + 1585,1587,1588,1589,1591, 0,1592, 0,1593,1594, 0,1595,1596, 0,1598,1599, + 1600,1601,1604,1582,1578,1590,1597, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0,1936, 0,1937, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0,1938, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0,1939,1940, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0,1941,1942, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0,1944,1943, 0,1945, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0,1946,1947, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1948, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0,1949,1950,1951,1952,1953,1954,1955, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0,1956,1957,1958,1960,1959,1961, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 106, 104, 107, 826, 114, 118, 119, 121, + 123, 124, 127, 125, 34, 830, 130, 131, 132, 137, 827, 35, 133, 139, 829, 142, + 143, 112, 144, 145, 924, 151, 152, 37, 157, 158, 159, 160, 38, 165, 166, 169, + 171, 172, 173, 174, 176, 177, 178, 179, 181, 182, 182, 182, 833, 468, 184, 185, + 834, 187, 188, 189, 196, 192, 194, 195, 197, 199, 200, 201, 203, 204, 204, 206, + 208, 209, 211, 218, 213, 219, 214, 216, 153, 234, 221, 222, 223, 220, 225, 224, + 230, 835, 235, 236, 237, 238, 239, 244, 836, 837, 247, 248, 249, 246, 251, 39, + 40, 253, 255, 255, 838, 257, 258, 259, 261, 839, 262, 263, 301, 264, 41, 266, + 270, 272, 271, 841, 274, 842, 277, 276, 278, 281, 282, 42, 283, 284, 285, 286, + 43, 843, 44, 289, 290, 291, 293, 934, 298, 845, 845, 621, 300, 300, 45, 852, + 894, 302, 304, 46, 306, 309, 310, 312, 316, 48, 47, 317, 846, 318, 323, 324, + 325, 324, 328, 329, 333, 331, 332, 334, 335, 336, 338, 339, 342, 343, 347, 351, + 849, 350, 348, 352, 354, 359, 850, 361, 358, 356, 49, 363, 365, 367, 364, 50, + 369, 371, 851, 376, 386, 378, 53, 381, 52, 51, 140, 141, 387, 382, 614, 78, + 388, 389, 390, 394, 392, 856, 54, 399, 396, 402, 404, 858, 405, 401, 407, 55, + 408, 409, 410, 413, 859, 415, 56, 417, 860, 418, 57, 419, 422, 424, 425, 861, + 840, 862, 426, 863, 429, 431, 427, 433, 437, 441, 438, 439, 442, 443, 864, 436, + 449, 450, 58, 454, 453, 865, 447, 460, 866, 867, 461, 466, 465, 464, 59, 467, + 470, 469, 472, 828, 475, 868, 478, 870, 483, 485, 486, 871, 488, 489, 872, 873, + 495, 497, 60, 498, 61, 61, 504, 505, 507, 508, 511, 62, 513, 874, 515, 875, + 518, 844, 520, 876, 877, 878, 63, 64, 528, 880, 879, 881, 882, 530, 531, 531, + 533, 66, 534, 67, 68, 884, 536, 538, 541, 69, 885, 549, 886, 887, 556, 559, + 70, 561, 562, 563, 888, 889, 889, 567, 71, 890, 570, 571, 72, 891, 577, 73, + 581, 579, 582, 893, 587, 74, 590, 592, 596, 75, 895, 896, 76, 897, 600, 898, + 602, 605, 607, 899, 900, 609, 901, 611, 853, 77, 615, 616, 79, 617, 252, 902, + 903, 854, 855, 621, 622, 731, 80, 627, 626, 628, 164, 629, 630, 631, 633, 904, + 632, 634, 639, 640, 635, 641, 646, 651, 638, 643, 644, 645, 905, 907, 906, 81, + 653, 654, 656, 911, 657, 908, 82, 83, 909, 910, 84, 664, 665, 666, 667, 669, + 668, 671, 670, 674, 672, 673, 675, 85, 677, 678, 86, 681, 682, 912, 685, 686, + 87, 689, 36, 913, 914, 88, 89, 696, 702, 709, 711, 915, 712, 713, 718, 719, + 917, 831, 721, 720, 723, 832, 725, 728, 918, 919, 739, 742, 744, 920, 745, 753, + 756, 757, 755, 760, 761, 921, 762, 90, 764, 922, 91, 775, 279, 780, 923, 925, + 92, 93, 785, 926, 94, 927, 787, 787, 789, 928, 792, 95, 796, 797, 798, 800, + 96, 929, 802, 804, 806, 97, 98, 807, 930, 99, 931, 932, 933, 814, 100, 816, + 817, 818, 819, 820, 821, 935, 0, 0, +}; +static const int16_t +_hb_ucd_i16[196] = +{ + 0, 0, 0, 0, 1, -1, 0, 0, 2, 0, -2, 0, 0, 0, 0, 2, + 0, -2, 0, 0, 0, 0, 0, 16, 0, 0, 0, -16, 0, 0, 1, -1, + 0, 0, 0, 1, -1, 0, 0, 0, 0, 1, -1, 0, 3, 3, 3, -3, + -3, -3, 0, 0, 0, 2016, 0, 0, 0, 0, 0, 2527, 1923, 1914, 1918, 0, + 2250, 0, 0, 0, 0, 0, 0, 138, 0, 7, 0, 0, -7, 0, 0, 0, + 1, -1, 1, -1, -1, 1, -1, 0, 1824, 0, 0, 0, 0, 0, 2104, 0, + 2108, 2106, 0, 2106, 1316, 0, 0, 0, 0, 1, -1, 1, -1, -138, 0, 0, + 1, -1, 8, 8, 8, 0, 7, 7, 0, 0, -8, -8, -8, -7, -7, 0, + 1, -1, 0, 2,-1316, 1, -1, 0, -1, 1, -1, 1, -1, 3, 1, -1, + -3, 1, -1, 1, -1, 0, 0,-1914,-1918, 0, 0,-1923,-1824, 0, 0, 0, + 0,-2016, 0, 0, 1, -1, 0, 1, 0, 0,-2104, 0, 0, 0, 0,-2106, + -2108,-2106, 0, 0, 1, -1,-2250, 0, 0, 0,-2527, 0, 0, -2, 0, 1, + -1, 0, 1, -1, +}; + +static inline uint_fast8_t +_hb_ucd_gc (unsigned u) +{ + return u<1114110u?_hb_ucd_u8[6800+(((_hb_ucd_u8[1312+(((_hb_ucd_u16[((_hb_ucd_u8[544+(((_hb_ucd_u8[u>>1>>3>>3>>4])<<4)+((u>>1>>3>>3)&15u))])<<3)+((u>>1>>3)&7u)])<<3)+((u>>1)&7u))])<<1)+((u)&1u))]:2; +} +static inline uint_fast8_t +_hb_ucd_ccc (unsigned u) +{ + return u<125259u?_hb_ucd_u8[8792+(((_hb_ucd_u8[8236+(((_hb_ucd_u8[7776+(((_hb_ucd_u8[7424+(((_hb_ucd_u8[7178+(u>>2>>2>>2>>3)])<<3)+((u>>2>>2>>2)&7u))])<<2)+((u>>2>>2)&3u))])<<2)+((u>>2)&3u))])<<2)+((u)&3u))]:0; +} +static inline unsigned +_hb_ucd_b4 (const uint8_t* a, unsigned i) +{ + return (a[i>>1]>>((i&1u)<<2))&15u; +} +static inline int_fast16_t +_hb_ucd_bmg (unsigned u) +{ + return u<65380u?_hb_ucd_i16[((_hb_ucd_u8[9540+(((_hb_ucd_u8[9420+(((_hb_ucd_b4(9292+_hb_ucd_u8,u>>2>>3>>3))<<3)+((u>>2>>3)&7u))])<<3)+((u>>2)&7u))])<<2)+((u)&3u)]:0; +} +static inline uint_fast8_t +_hb_ucd_sc (unsigned u) +{ + return u<918000u?_hb_ucd_u8[11062+(((_hb_ucd_u16[2040+(((_hb_ucd_u8[10326+(((_hb_ucd_u8[9876+(u>>3>>4>>4)])<<4)+((u>>3>>4)&15u))])<<4)+((u>>3)&15u))])<<3)+((u)&7u))]:2; +} +static inline uint_fast16_t +_hb_ucd_dm (unsigned u) +{ + return u<195102u?_hb_ucd_u16[6008+(((_hb_ucd_u8[17068+(((_hb_ucd_u8[16686+(u>>4>>5)])<<5)+((u>>4)&31u))])<<4)+((u)&15u))]:0; +} + + +#else + +static const uint8_t +_hb_ucd_u8[13370] = +{ + 0, 1, 2, 3, 4, 5, 6, 7, 7, 8, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 9, 10, 7, 7, 7, 7, 7, 11, 12, 12, 12, 13, + 14, 15, 16, 17, 18, 19, 20, 21, 22, 21, 21, 21, 21, 23, 7, 7, + 7, 24, 21, 21, 21, 25, 26, 27, 21, 28, 29, 30, 31, 32, 33, 34, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 35, 21, 36, + 7, 7, 7, 7, 35, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 37, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 34, 34, 34, 35, 36, 37, 34, 34, 34, 38, 39, 40, 41, + 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, + 58, 59, 60, 61, 62, 62, 63, 64, 65, 66, 67, 68, 69, 67, 70, 71, + 67, 67, 62, 72, 62, 62, 73, 67, 74, 75, 76, 77, 78, 67, 67, 67, + 79, 80, 34, 81, 82, 83, 67, 67, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 84, 34, 34, 34, 34, + 85, 34, 34, 34, 34, 34, 34, 34, 34, 86, 34, 34, 87, 88, 89, 90, + 91, 92, 93, 94, 95, 96, 97, 98, 34, 34, 34, 34, 34, 34, 34, 34, + 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, + 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100, + 100,100, 34, 34, 34, 34,101,102, 34, 34,103,104,105,106,107,108, + 34, 34,109,110,111,112,113,114,115,116,117,118, 34, 34, 34,119, + 120,121,122,123,124,125,126,127, 34,128,129,111,130,131,132,133, + 134,135,136,137,138,139,140,111,141,142,111,143,144,145,146,111, + 147,148,149,150,151,152,153,111,154,155,156,157,111,158,159,160, + 34, 34, 34, 34, 34, 34, 34, 34,161, 34, 34,111,111,111,111,111, + 111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,162, + 34, 34, 34, 34, 34, 34, 34, 34,163,111,111,111,111,111,111,111, + 111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111, + 111,111,111,111,111,111,111,111, 34, 34, 34, 34, 34,111,111,111, + 34, 34, 34, 34,164,165,166, 34,111,111,111,111,167,168,169,170, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,111,111,111,111,111, + 111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,119, + 34, 34, 34, 34, 34, 34,111,111,111,111,111,111,111,111,111,111, + 111,111,111,111,111,111,111,111, 34,171,111,111,111,111,111,111, + 111,111,111,111,111,111,111,111,111,111,111,111,111,111,172, 67, + 67, 67,173,174,175,130, 65,111,176,177,178,179,180,181,182,183, + 67, 67, 67, 67,184,185,111,111,111,111,111,111,111,111,186,111, + 187,188,189,111,111,190,111,111,111,191,111,111,111,111,111, 34, + 34,192,193,111,111,111,111,111,130,194,195,111, 34,196,111,111, + 67, 67,197, 67, 67,111, 67,198, 67, 67, 67, 67, 67, 67, 67, 67, + 67, 67, 67, 67, 67, 67, 67,199,111,111,111,111,111,111,111,111, + 34, 34, 34, 34, 34, 34, 34, 34,111,111,111,111,111,111,111,111, + 34, 34, 34, 34, 34,111,111,111,111,111,111,111,111,111,111,111, + 200,111,188,188,111,111,111,111,111,111,111,111,111,111,111,111, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 2, 4, 5, 6, 2, + 7, 7, 7, 7, 7, 2, 8, 9, 10, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 12, 13, 14, 15, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 17, 18, 19, 1, 20, 20, 21, 22, 23, 24, 25, + 26, 27, 15, 2, 28, 29, 27, 30, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 31, 11, 11, 11, 32, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 33, 16, 16, 16, 16, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 34, 34, 34, 34, 34, 34, 34, 34, 16, 32, 32, 32, + 32, 32, 32, 32, 11, 34, 34, 16, 34, 32, 32, 11, 34, 11, 16, 11, + 11, 34, 32, 11, 32, 16, 11, 34, 32, 32, 32, 11, 34, 16, 32, 11, + 34, 11, 34, 34, 32, 35, 32, 16, 36, 36, 37, 34, 38, 37, 34, 34, + 34, 34, 34, 34, 34, 34, 16, 32, 34, 38, 32, 11, 32, 32, 32, 32, + 32, 32, 16, 16, 16, 11, 34, 32, 34, 34, 11, 32, 32, 32, 32, 32, + 16, 16, 39, 16, 16, 16, 16, 16, 40, 40, 40, 40, 40, 40, 40, 40, + 40, 41, 41, 40, 40, 40, 40, 40, 40, 41, 41, 41, 41, 41, 41, 41, + 40, 40, 42, 41, 41, 41, 42, 42, 41, 41, 41, 41, 41, 41, 41, 41, + 43, 43, 43, 43, 43, 43, 43, 43, 32, 32, 42, 32, 16, 44, 16, 10, + 41, 41, 41, 45, 11, 11, 11, 11, 34, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 16, 16, 16, 16, 16, 16, 16, 16, 16, 34, + 16, 11, 32, 16, 32, 32, 32, 32, 16, 16, 32, 46, 34, 32, 34, 11, + 32, 47, 43, 43, 48, 32, 32, 32, 11, 34, 34, 34, 34, 34, 34, 16, + 11, 11, 11, 11, 49, 2, 2, 2, 16, 16, 16, 16, 50, 51, 52, 53, + 54, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 55, + 56, 57, 43, 56, 43, 43, 43, 43, 36, 36, 36, 36, 36, 36, 36, 36, + 36, 58, 2, 2, 2, 2, 2, 2, 59, 59, 59, 8, 9, 60, 2, 61, + 43, 43, 43, 43, 43, 57, 62, 2, 63, 36, 36, 36, 36, 64, 43, 43, + 7, 7, 7, 7, 7, 2, 2, 36, 65, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 66, 43, 43, 43, 67, 47, 43, 43, 68, 69, 70, 43, 43, 36, + 7, 7, 7, 7, 7, 36, 71, 72, 2, 2, 2, 2, 2, 2, 2, 73, + 64, 36, 36, 36, 36, 36, 36, 36, 43, 43, 43, 43, 43, 43, 65, 36, + 36, 36, 36, 43, 43, 43, 43, 43, 7, 7, 7, 7, 7, 36, 36, 36, + 36, 36, 36, 36, 36, 64, 43, 43, 43, 43, 40, 21, 2, 40, 69, 20, + 36, 36, 36, 43, 43, 69, 43, 43, 43, 43, 69, 43, 69, 43, 43, 43, + 2, 2, 2, 2, 2, 2, 2, 2, 36, 36, 36, 36, 64, 43, 43, 2, + 36, 36, 36, 36, 74, 36, 36, 36, 59, 59, 59, 59, 43, 43, 43, 43, + 36, 36, 36, 36, 75, 43, 43, 43, 43, 76, 43, 43, 43, 43, 43, 43, + 43, 77, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 77, 65, 78, + 79, 43, 43, 43, 77, 78, 79, 78, 64, 43, 43, 43, 36, 36, 36, 36, + 36, 43, 2, 7, 7, 7, 7, 7, 80, 36, 36, 36, 36, 36, 36, 36, + 64, 78, 81, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 65, 78, + 79, 43, 43, 77, 78, 78, 79, 36, 36, 36, 36, 82, 78, 78, 36, 36, + 36, 43, 43, 7, 7, 7, 7, 7, 36, 20, 27, 27, 27, 53, 58, 43, + 43, 77, 81, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 43, 78, + 79, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 65, 36, 36, 36, + 36, 36, 36, 7, 7, 7, 7, 7, 43, 36, 64, 2, 2, 2, 2, 2, + 79, 43, 43, 43, 77, 78, 79, 43, 60, 20, 20, 20, 83, 43, 43, 43, + 43, 78, 81, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 65, 79, + 79, 43, 43, 77, 78, 78, 79, 43, 43, 43, 43, 77, 78, 78, 36, 36, + 72, 27, 27, 27, 27, 27, 27, 27, 43, 65, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36, 78, 77, 78, 78, 78, 78, 78, 79, 43, + 36, 36, 36, 82, 78, 78, 78, 78, 78, 78, 78, 7, 7, 7, 7, 7, + 27, 84, 61, 61, 53, 61, 61, 61, 77, 78, 65, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 65, 43, 77, 78, 78, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 36, 36, 36, 36, 7, 7, 7, 85, 27, 27, 27, 84, + 64, 78, 66, 36, 36, 36, 36, 36, 78, 78, 78, 77, 78, 78, 43, 43, + 43, 43, 77, 78, 78, 78, 81, 36, 86, 82, 78, 78, 78, 78, 78, 78, + 43, 78, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 64, 65, 78, + 79, 43, 43, 78, 78, 78, 79, 71, 61, 61, 36, 82, 27, 27, 27, 87, + 27, 27, 27, 27, 84, 36, 36, 36, 36, 36, 36, 36, 36, 43, 43, 77, + 78, 43, 43, 43, 78, 78, 78, 78, 7, 78, 2, 2, 2, 2, 2, 2, + 64, 36, 43, 43, 43, 43, 43, 88, 36, 36, 36, 69, 43, 43, 43, 57, + 7, 7, 7, 7, 7, 2, 2, 2, 64, 36, 43, 43, 43, 43, 65, 36, + 36, 36, 36, 40, 43, 43, 43, 43, 7, 7, 7, 7, 7, 7, 36, 36, + 71, 61, 2, 2, 2, 2, 2, 2, 2, 89, 89, 61, 43, 61, 61, 61, + 7, 7, 7, 7, 7, 27, 27, 27, 27, 27, 47, 47, 47, 4, 4, 78, + 64, 43, 43, 43, 43, 43, 43, 77, 43, 43, 57, 43, 36, 36, 64, 43, + 43, 43, 43, 43, 43, 43, 43, 61, 61, 61, 61, 70, 61, 61, 61, 61, + 2, 2, 89, 61, 21, 2, 2, 2, 36, 36, 36, 36, 36, 82, 79, 43, + 77, 43, 43, 43, 79, 77, 79, 65, 36, 36, 36, 78, 43, 36, 36, 43, + 65, 78, 81, 82, 78, 78, 78, 36, 64, 43, 65, 36, 36, 36, 36, 36, + 36, 77, 79, 77, 78, 78, 79, 82, 7, 7, 7, 7, 7, 78, 79, 61, + 16, 16, 16, 16, 16, 50, 44, 16, 36, 36, 36, 36, 36, 36, 64, 43, + 2, 2, 2, 2, 90, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, + 61, 61, 61, 61, 61, 61, 61, 61, 11, 11, 11, 11, 16, 16, 16, 16, + 91, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 71, 66, + 92, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 93, 94, 94, + 36, 36, 36, 36, 36, 58, 2, 95, 96, 36, 36, 36, 36, 36, 36, 36, + 36, 43, 77, 78, 78, 78, 78, 81, 36, 43, 97, 2, 2, 2, 2, 2, + 36, 43, 43, 43, 43, 43, 43, 43, 36, 36, 43, 79, 43, 43, 43, 78, + 78, 78, 78, 77, 79, 43, 43, 43, 43, 43, 2, 80, 2, 60, 64, 43, + 7, 7, 7, 7, 7, 7, 7, 7, 2, 2, 2, 98, 2, 56, 43, 76, + 36, 75, 36, 36, 36, 36, 36, 36, 36, 36, 64, 65, 36, 36, 36, 36, + 36, 36, 36, 36, 64, 36, 36, 36, 43, 77, 78, 79, 77, 78, 78, 78, + 78, 77, 78, 78, 79, 43, 43, 43, 61, 61, 2, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 27, 27, 61, 36, 36, 36, 64, 77, 79, 43, 2, + 36, 36, 82, 77, 43, 43, 43, 43, 77, 77, 79, 43, 43, 43, 77, 78, + 78, 79, 43, 43, 43, 43, 43, 43, 2, 2, 2, 80, 2, 2, 2, 2, + 43, 43, 43, 43, 43, 43, 43, 99, 43, 43, 81, 36, 36, 36, 36, 36, + 36, 36, 77, 43, 43, 77, 77, 78, 78, 77, 81, 36, 36, 36, 36, 36, + 89, 61, 61, 61, 61, 47, 43, 43, 43, 43, 61, 61, 61, 61, 21, 2, + 43, 81, 36, 36, 36, 36, 36, 36, 82, 43, 43, 78, 43, 79, 43, 36, + 36, 36, 36, 77, 43, 78, 79, 79, 43, 78, 78, 78, 78, 78, 2, 2, + 36, 36, 78, 78, 78, 78, 43, 43, 43, 43, 78, 43, 43, 57, 2, 2, + 7, 7, 7, 7, 7, 7, 86, 36, 36, 36, 36, 36, 40, 40, 40, 2, + 43, 57, 43, 43, 43, 43, 43, 43, 77, 43, 43, 43, 65, 36, 64, 36, + 36, 36, 65, 82, 43, 36, 36, 36, 16, 16, 16, 16, 16, 16, 40, 40, + 40, 40, 40, 40, 40, 44, 16, 16, 16, 16, 16, 16, 44, 16, 16, 16, + 16, 16, 16, 16, 16,100, 40, 40, 32, 32, 32, 16, 16, 16, 16, 32, + 16, 16, 16, 16, 11, 11, 11, 11, 16, 16, 16, 16, 34, 11, 11, 11, + 16, 16, 16, 16,101,101,101,101, 16, 16, 16, 16, 11, 11,102,103, + 41, 16, 16, 16, 11, 11,102, 41, 16, 16, 16, 16, 11, 11,104, 41, + 105,105,105,105,105,106, 59, 59, 51, 51, 51, 2,107,108,107,108, + 2, 2, 2, 2,109, 59, 59,110, 2, 2, 2, 2,111,112, 2,113, + 114, 2,115,116, 2, 2, 2, 2, 2, 9,114, 2, 2, 2, 2,117, + 59, 59, 59, 59, 59, 59, 59, 59,118, 40, 27, 27, 27, 8,115,119, + 27, 27, 27, 27, 27, 8,115, 94, 20, 20, 20, 20, 20, 20, 20, 20, + 43, 43, 43, 43, 43, 43,120, 48, 99, 48, 99, 43, 43, 43, 43, 43, + 61,121, 61,122, 61, 34, 11, 16, 11, 32,122, 61, 46, 11, 11, 61, + 61, 61,121,121,121, 11, 11,123, 11, 11, 35, 36, 39, 61, 16, 11, + 8, 8, 46, 16, 16, 26, 61,124, 95, 95, 95, 95, 95, 95, 95, 95, + 95,125,126, 95,127, 61, 61, 61, 8, 8,128, 61, 61, 8, 61, 61, + 128, 26, 61,128, 61, 61, 61,128, 61, 61, 61, 61, 61, 61, 61, 8, + 61,128,128, 61, 61, 61, 61, 61, 61, 61, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 61, 61, 61, 61, 4, 4, 61, 61, + 8, 61, 61, 61,129,130, 61, 61, 61, 61, 61, 61, 61, 61,128, 61, + 61, 61, 61, 61, 61, 26, 8, 8, 8, 8, 61, 61, 61, 61, 61, 61, + 61, 61, 61, 61, 61, 61, 8, 8, 8, 61, 61, 61, 61, 61, 61, 61, + 27, 27, 27, 27, 27, 27, 61, 61, 61, 61, 61, 61, 61, 27, 27, 27, + 61, 61, 61, 26, 61, 61, 61, 61, 26, 61, 61, 61, 61, 61, 61, 61, + 61, 61, 61, 61, 8, 8, 8, 8, 61, 61, 61, 61, 61, 61, 61, 26, + 61, 61, 61, 61, 4, 4, 4, 4, 4, 4, 4, 27, 27, 27, 27, 27, + 27, 27, 61, 61, 61, 61, 61, 61, 8, 8,115,131, 8, 8, 8, 8, + 8, 8, 8, 4, 4, 4, 4, 4, 8,115,132,132,132,132,132,132, + 132,132,132,132,131, 8, 8, 8, 8, 8, 8, 8, 4, 4, 8, 8, + 8, 8, 8, 8, 8, 8, 4, 8, 8, 8,128, 26, 8, 8,128, 61, + 32, 11, 32, 34, 34, 34, 34, 11, 32, 32, 34, 16, 16, 16, 40, 11, + 32, 32,124, 61, 61,122, 34,133, 43, 32, 16, 16, 50, 2, 90, 2, + 36, 36, 36, 36, 36, 36, 36, 75, 2, 2, 2, 2, 2, 2, 2, 56, + 2,107,107, 2,111,112,107, 2, 2, 2, 2, 6, 2, 98,107, 2, + 107, 4, 4, 4, 4, 2, 2, 80, 2, 2, 2, 2, 2, 51, 2, 2, + 98,134, 2, 2, 2, 2, 2, 2, 61, 2,135,132,132,132,136, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 1, 2,137,138, 4, 4, 4, 4, + 4, 61, 4, 4, 4, 4,139, 94,140, 95, 95, 95, 95, 43, 43, 78, + 141, 40, 40, 61, 95,142, 58, 61, 72, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 64,143,144, 63, 36, 36, 36, 36, 36, 58, 40, 63, + 61, 27, 27, 61, 61, 61, 61, 61, 27, 27, 27, 27, 27, 61, 61, 61, + 61, 61, 61, 61, 27, 27, 27, 27,145, 27, 27, 27, 27, 27, 27, 27, + 36, 36, 75, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36,146, 2, + 32, 32, 32, 32, 32, 32, 32, 64, 48,147, 43, 43, 43, 43, 43, 80, + 32, 32, 32, 32, 32, 32, 40, 43, 36, 36, 36, 95, 95, 95, 95, 95, + 43, 2, 2, 2, 2, 2, 2, 2, 41, 41, 41,144, 40, 40, 40, 40, + 41, 32, 32, 32, 32, 32, 32, 32, 16, 32, 32, 32, 32, 32, 32, 32, + 44, 16, 16, 16, 34, 34, 34, 32, 32, 32, 32, 32, 42,148, 34, 35, + 32, 32, 16, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 11, 11, 32, + 11, 11, 32, 32, 32, 32, 32, 32, 32, 32, 11, 11, 34, 16, 16, 16, + 32, 16, 16, 32, 32, 16, 16, 16, 16, 40,149, 35, 40, 35, 36, 36, + 36, 65, 36, 65, 36, 64, 36, 36, 36, 82, 79, 77, 61, 61, 43, 43, + 27, 27, 27, 61,150, 61, 61, 61, 36, 36, 2, 2, 2, 2, 2, 2, + 78, 36, 36, 36, 36, 36, 36, 36, 36, 36, 78, 78, 78, 78, 78, 78, + 78, 78, 43, 43, 43, 43, 43, 2, 43, 36, 36, 36, 2, 66, 66, 64, + 36, 36, 36, 43, 43, 43, 43, 2, 36, 36, 36, 64, 43, 43, 43, 43, + 43, 78, 78, 78, 78, 78, 78, 97, 36, 64, 78, 43, 43, 78, 43, 78, + 97, 2, 2, 2, 2, 2, 2, 80, 7, 7, 7, 7, 7, 7, 7, 2, + 36, 36, 64, 63, 36, 36, 36, 36, 36, 36, 36, 36, 64, 43, 43, 77, + 79, 77, 79, 43, 43, 43, 43, 43, 36, 64, 36, 36, 36, 36, 77, 78, + 7, 7, 7, 7, 7, 7, 2, 2, 63, 36, 36, 71, 61, 82, 77, 36, + 65, 43, 65, 64, 65, 36, 36, 43, 36, 36, 36, 36, 36, 36, 75, 2, + 36, 36, 36, 36, 36, 82, 43, 78, 2, 75,151, 43, 43, 43, 43, 43, + 16, 16, 16, 16, 16,103, 40, 40, 16, 16, 16, 16,100, 41, 41, 41, + 36, 82, 79, 78, 77, 97, 79, 43,152,152,152,152,152,152,152,152, + 153,153,153,153,153,153,153,153, 16, 16, 16, 16, 16, 16, 35, 65, + 36, 36, 36, 36,154, 36, 36, 36, 36, 41, 41, 41, 41, 41, 41, 41, + 41, 74, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36,132, + 36, 36, 36, 36, 36, 36, 36, 71, 36, 36, 36, 36, 36, 36,150, 61, + 2, 2, 2,135,116, 2, 2, 2, 6,155,156,132,132,132,132,132, + 132,132,116,135,116, 2,113,157, 2, 2, 2, 2,139,132,132,116, + 2,158, 8, 8, 60, 2, 2, 2, 36, 36, 36, 36, 36, 36, 36,159, + 2, 2, 3, 2, 4, 5, 6, 2, 16, 16, 16, 16, 16, 17, 18,115, + 116, 4, 2, 36, 36, 36, 36, 36, 63, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36, 40, 20,160, 53, 20, 26, 8,128, 61, + 61, 61, 61, 61,161, 59, 61, 61, 2, 2, 2, 90, 27, 27, 27, 27, + 27, 27, 27, 84, 61, 61, 61, 61, 95, 95,127, 27, 84, 61, 61, 61, + 61, 61, 61, 61, 61, 27, 61, 61, 61, 61, 61, 61, 61, 61, 47, 43, + 162,162,162,162,162,162,162,162,163, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 87, 36,138, 36, 36, 36, 36, 95, 95, 95, + 36, 36, 36, 36, 36, 36, 36, 58,164, 95, 95, 95, 95, 95, 95, 95, + 11, 11, 11, 32, 16, 16, 16, 16, 36, 36, 36, 58, 27, 27, 27, 27, + 36, 36, 36, 71,145, 27, 27, 27, 36, 36, 36,165, 27, 27, 27, 27, + 36, 36, 36, 36, 36,165, 27, 27, 36, 36, 36, 27, 27, 27, 27, 30, + 36, 36, 36, 36, 36, 36, 27, 36, 64, 43, 43, 43, 43, 43, 43, 43, + 36, 36, 36, 36, 43, 43, 43, 43, 36, 36, 36, 36, 36, 36,165, 30, + 36, 36, 36, 36, 36, 36,165, 27, 36, 36, 36, 36, 72, 36, 36, 36, + 36, 36, 64, 43, 43,163, 27, 27, 36, 36, 36, 36, 58, 2, 2, 2, + 36, 36, 36, 36, 27, 27, 27, 27, 16, 16, 16, 16, 16, 27, 27, 27, + 36, 36, 43, 43, 43, 43, 43, 43, 36, 36, 36, 36, 36, 64,166, 51, + 27, 27, 27, 87, 36, 36, 36, 36,163, 27, 30, 2, 2, 2, 2, 2, + 36, 43, 43, 2, 2, 2, 2, 2, 36, 36,165, 27, 27, 27, 27, 27, + 79, 81, 36, 36, 36, 36, 36, 36, 43, 43, 43, 57, 2, 2, 2, 2, + 2, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 7, 7, 7, 7, 7, + 65, 64, 65, 36, 36, 36, 36, 64, 78, 79, 43, 77, 79, 57, 73, 2, + 2, 43, 43, 43, 43, 43, 67, 59, 36, 36, 36, 64, 43, 43, 79, 43, + 43, 43, 43, 7, 7, 7, 7, 7, 2, 2, 82, 81, 36, 36, 36, 36, + 36, 64, 2, 36, 36, 36, 36, 36, 36, 82, 78, 43, 43, 43, 43, 77, + 81, 36, 58, 2, 56, 43, 57, 79, 7, 7, 7, 7, 7, 58, 58, 2, + 90, 27, 27, 27, 27, 27, 27, 27, 36, 36, 36, 36, 36, 36, 78, 79, + 43, 78, 77, 43, 2, 2, 2, 65, 36, 36, 36, 36, 36, 36, 36, 64, + 77, 78, 78, 78, 78, 78, 78, 78, 36, 36, 36, 82, 78, 78, 81, 36, + 36, 78, 78, 43, 43, 43, 43, 43, 36, 36, 82, 78, 43, 43, 43, 43, + 78, 43, 77, 65, 36, 58, 2, 2, 7, 7, 7, 7, 7, 2, 2, 65, + 78, 79, 43, 43, 77, 77, 78, 79, 77, 43, 36, 66, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36, 82, 78, 43, 43, 43, 78, 78, 43, 79, + 57, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 36, 36, 43, 43, + 78, 79, 43, 43, 43, 77, 79, 79, 57, 2, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 64, 79, 78, 43, 43, 43, 79, 58, 2, 2, 2, + 78, 43, 43, 79, 43, 43, 43, 43, 7, 7, 7, 7, 7, 27, 2, 89, + 43, 43, 43, 43, 79, 57, 2, 2, 27, 27, 27, 27, 27, 27, 27, 87, + 78, 78, 78, 78, 78, 79, 77, 65, 81, 79, 2, 2, 2, 2, 2, 2, + 82, 78, 43, 43, 43, 43, 78, 78, 65, 66, 78, 78, 78, 78, 78, 78, + 78, 78, 78, 78, 78, 78, 78, 78, 64, 43, 43, 43, 43, 65, 36, 36, + 36, 64, 43, 43, 77, 64, 43, 57, 2, 2, 2, 56, 43, 43, 43, 43, + 64, 43, 43, 77, 79, 43, 36, 36, 36, 36, 36, 36, 36, 43, 43, 43, + 43, 43, 43, 77, 43, 2, 66, 2, 43, 43, 43, 43, 43, 43, 43, 79, + 58, 2, 2, 2, 2, 2, 2, 2, 2, 36, 36, 36, 36, 36, 36, 36, + 43, 43, 43, 43, 77, 43, 43, 43, 77, 43, 79, 43, 43, 43, 43, 43, + 43, 43, 43, 64, 43, 43, 43, 43, 36, 36, 36, 36, 36, 78, 78, 78, + 43, 77, 79, 79, 36, 36, 36, 36, 36, 64, 77, 97, 2, 2, 2, 2, + 43, 82, 36, 36, 36, 36, 36, 36, 36, 36, 78, 43, 43, 43, 43, 78, + 77, 57, 2, 2, 2, 2, 2, 2, 27, 27, 84, 61, 61, 61, 53, 20, + 150, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 21, + 65, 36, 36, 64, 43, 43, 43, 43, 43, 43, 57, 2, 2, 2, 2, 2, + 43, 43, 43, 57, 2, 2, 61, 61, 40, 40, 89, 61, 61, 61, 61, 61, + 7, 7, 7, 7, 7,167, 27, 27, 27, 87, 36, 36, 36, 36, 36, 36, + 27, 27, 27, 30, 2, 2, 2, 2, 82, 78, 78, 78, 78, 78, 78, 78, + 78, 78, 78, 78, 78, 78, 78, 79, 43, 68, 40, 40, 40, 40, 40, 40, + 40, 80, 43, 43, 43, 43, 43, 43, 36, 36, 36, 36, 36, 36, 47, 57, + 61, 61,168, 79, 43, 61,168, 78, 78,169, 59, 59, 59, 76, 43, 43, + 43, 70, 47, 43, 43, 43, 61, 61, 61, 61, 61, 61, 61, 43, 43, 61, + 61, 43, 70, 61, 61, 61, 61, 61, 11, 11, 11, 11, 11, 16, 16, 16, + 16, 16, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 16, + 11, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 11, 11, + 11, 11, 11, 16, 16, 16, 16, 16, 31, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 33, 16, 16, 16, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 31, 16, 16, 16, 16, 33, 16, 16, 16, 11, 11, + 11, 11, 31, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 33, + 16, 16, 16, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 31, + 16, 16, 16, 16, 33, 16, 16, 16, 11, 11, 11, 11, 31, 16, 16, 16, + 16, 33, 16, 16, 16, 32, 16, 7, 43, 43, 43, 70, 61, 47, 43, 43, + 43, 43, 43, 43, 43, 43, 70, 61, 61, 61, 47, 61, 61, 61, 61, 61, + 61, 61, 70, 21, 2, 2, 2, 2, 2, 2, 2, 2, 2, 56, 43, 43, + 16, 16, 16, 16, 16, 39, 16, 16, 43, 43, 43, 68, 40, 40, 40, 40, + 7, 7, 7, 7, 7, 7, 7, 71, 36, 36, 36, 36, 36, 36, 36, 43, + 36, 36, 36, 36, 36, 36, 43, 43, 7, 7, 7, 7, 7, 7, 7,170, + 36, 36, 36, 36, 36, 75, 43, 43, 16, 16, 43, 43, 43, 68, 40, 40, + 27, 27, 27, 27, 27, 27,145, 27,171, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 27,145, 27, 27, 27, 27, 27, 27, 84, 61, + 61, 61, 61, 61, 61, 25, 41, 41, 0, 0, 29, 21, 21, 21, 23, 21, + 22, 18, 21, 25, 21, 17, 13, 13, 25, 25, 25, 21, 21, 9, 9, 9, + 9, 22, 21, 18, 24, 16, 24, 5, 5, 5, 5, 22, 25, 18, 25, 0, + 23, 23, 26, 21, 24, 26, 7, 20, 25, 1, 26, 24, 26, 25, 15, 15, + 24, 15, 7, 19, 15, 21, 9, 25, 9, 5, 5, 25, 5, 9, 5, 7, + 7, 7, 9, 8, 8, 5, 7, 5, 6, 6, 24, 24, 6, 24, 12, 12, + 6, 5, 9, 21, 25, 9, 26, 12, 11, 11, 9, 6, 5, 21, 17, 17, + 17, 26, 26, 23, 23, 12, 17, 12, 21, 12, 12, 21, 7, 21, 1, 1, + 21, 23, 26, 26, 1, 21, 6, 7, 7, 12, 12, 7, 21, 7, 12, 1, + 12, 6, 6, 12, 12, 26, 7, 26, 26, 7, 21, 1, 24, 7, 7, 6, + 1, 12, 12, 10, 10, 10, 10, 12, 21, 6, 10, 7, 7, 10, 23, 7, + 15, 26, 13, 21, 13, 7, 15, 7, 12, 23, 21, 26, 21, 15, 17, 7, + 29, 7, 7, 22, 18, 18, 14, 14, 14, 7, 10, 21, 17, 21, 11, 12, + 5, 6, 8, 8, 8, 24, 5, 24, 9, 24, 29, 29, 29, 1, 20, 19, + 22, 20, 27, 28, 1, 29, 21, 20, 19, 21, 21, 16, 16, 21, 25, 22, + 18, 21, 21, 29, 15, 6, 18, 6, 12, 11, 9, 26, 26, 9, 26, 5, + 5, 26, 14, 9, 5, 14, 14, 15, 25, 26, 26, 22, 18, 26, 18, 25, + 18, 22, 5, 12, 22, 21, 21, 22, 18, 17, 26, 6, 7, 14, 17, 22, + 26, 14, 17, 6, 14, 6, 12, 24, 24, 6, 26, 15, 6, 21, 11, 21, + 24, 9, 6, 9, 23, 26, 6, 10, 4, 4, 3, 3, 7, 25, 17, 16, + 16, 22, 16, 16, 25, 17, 7, 1, 25, 24, 26, 1, 2, 2, 12, 15, + 21, 14, 7, 15, 12, 17, 13, 15, 26, 10, 10, 1, 13, 23, 23, 15, + 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, 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, 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, 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, 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, 35, 0, 36, 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, 37, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 38, 39, 0, 0, 0, 0, 0, 0, + 40, 41, 42, 0, 43, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1, 2, 0, 0, 0, 0, 3, 0, 0, 0, 4, 5, 6, 7, + 0, 8, 9, 10, 0, 11, 12, 13, 14, 15, 16, 17, 16, 18, 16, 19, + 16, 19, 16, 19, 0, 19, 16, 20, 16, 19, 21, 19, 0, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 0, 32, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 33, 0, 0, 0, 0, 0, 0, 34, 0, 0, 35, 0, 0, + 36, 0, 37, 0, 0, 0, 38, 39, 40, 41, 42, 43, 44, 45, 46, 0, + 0, 47, 0, 0, 0, 48, 0, 0, 0, 49, 0, 0, 0, 0, 0, 0, + 0, 50, 0, 51, 0, 52, 53, 0, 54, 0, 0, 0, 0, 0, 0, 55, + 56, 57, 0, 0, 0, 0, 58, 0, 0, 59, 60, 61, 62, 63, 0, 0, + 64, 65, 0, 0, 0, 66, 0, 0, 0, 0, 67, 0, 0, 0, 68, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 69, 0, 0, + 0, 70, 0, 71, 0, 0, 72, 0, 0, 73, 0, 0, 0, 0, 0, 0, + 0, 0, 74, 0, 0, 0, 0, 0, 75, 76, 0, 77, 78, 0, 0, 79, + 80, 0, 81, 62, 0, 82, 83, 0, 0, 84, 85, 86, 0, 0, 0, 87, + 0, 88, 0, 0, 51, 89, 51, 0, 90, 0, 91, 0, 0, 0, 80, 0, + 0, 0, 92, 93, 0, 94, 95, 96, 97, 0, 0, 0, 0, 0, 51, 0, + 0, 0, 0, 98, 99, 0, 0, 0, 0, 0, 0,100, 0, 0, 0, 0, + 0,101,102, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,103, 0, 0, + 104, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,105,106, 0, 0,107, + 0, 0, 0, 0, 0, 0,108, 0,109, 0,102, 0, 0, 0, 0, 0, + 110,111, 0, 0, 0, 0, 0, 0, 0,112, 0, 0, 0, 0, 0, 0, + 0,113, 0,114, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, + 7, 0, 8, 0, 0, 0, 0, 9, 10, 11, 12, 0, 0, 0, 0, 13, + 0, 0, 14, 15, 0, 16, 0, 17, 18, 0, 0, 19, 0, 20, 21, 0, + 0, 0, 0, 0, 22, 23, 0, 24, 25, 0, 0, 26, 0, 0, 0, 27, + 0, 0, 28, 29, 30, 31, 0, 0, 0, 32, 33, 34, 0, 0, 33, 0, + 0, 35, 33, 0, 0, 0, 33, 36, 0, 0, 0, 0, 0, 37, 38, 0, + 0, 0, 0, 0, 0, 39, 40, 0, 0, 0, 0, 0, 0, 41, 42, 0, + 0, 0, 0, 43, 0, 44, 0, 0, 0, 45, 46, 0, 0, 0, 47, 0, + 0, 0, 0, 0, 0, 48, 49, 0, 0, 0, 0, 50, 0, 0, 0, 51, + 0, 52, 0, 53, 0, 0, 0, 0, 54, 0, 0, 0, 0, 55, 0, 56, + 0, 0, 0, 0, 57, 58, 0, 0, 0, 59, 60, 0, 0, 0, 0, 0, + 0, 61, 52, 0, 62, 63, 0, 0, 64, 0, 0, 0, 65, 66, 0, 0, + 0, 67, 0, 68, 69, 70, 71, 72, 1, 73, 0, 74, 75, 76, 0, 0, + 77, 78, 0, 0, 0, 79, 0, 0, 1, 1, 0, 0, 80, 0, 0, 81, + 0, 0, 0, 0, 77, 82, 0, 83, 0, 0, 0, 0, 0, 78, 84, 0, + 85, 0, 52, 0, 1, 78, 0, 0, 86, 0, 0, 87, 0, 0, 0, 0, + 0, 88, 57, 0, 0, 0, 0, 0, 0, 89, 90, 0, 0, 84, 0, 0, + 33, 0, 0, 91, 0, 0, 0, 0, 92, 0, 0, 0, 0, 49, 0, 0, + 93, 0, 0, 0, 0, 94, 95, 0, 0, 96, 0, 0, 97, 0, 0, 0, + 98, 0, 0, 0, 99, 0, 0, 0, 0,100,101, 93, 0, 0,102, 0, + 0, 0, 84, 0, 0,103, 0, 0, 0,104,105, 0, 0,106,107, 0, + 0, 0, 0, 0, 0,108, 0, 0,109, 0, 0, 0, 0,110, 33, 0, + 111,112,113, 35, 0, 0,114, 0, 0, 0,115, 0, 0, 0, 0, 0, + 0,116, 0, 0,117, 0, 0, 0, 0,118, 88, 0, 0, 0, 0, 0, + 57, 0, 0, 0, 0, 52,119, 0, 0, 0, 0,120, 0, 0,121, 0, + 0, 0, 0,119, 0, 0,122, 0, 0, 0, 0, 0, 0,123, 0, 0, + 0,124, 0, 0, 0,125, 0,126, 0, 0, 0, 0,127,128,129, 0, + 130, 0,131, 0, 0, 0,132,133,134, 0, 77, 0, 0, 0, 0, 0, + 35, 0, 0, 0,135, 0, 0, 0,136, 0, 0,137, 0, 0,138, 0, + 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 3, 4, 5, 6, + 7, 4, 4, 8, 9, 10, 1, 11, 12, 13, 14, 15, 16, 17, 18, 1, + 1, 1, 19, 1, 0, 0, 20, 21, 22, 1, 23, 4, 21, 24, 25, 26, + 27, 28, 29, 30, 0, 0, 1, 1, 31, 0, 0, 0, 32, 33, 34, 35, + 1, 36, 37, 0, 0, 0, 0, 38, 1, 39, 14, 39, 40, 41, 42, 0, + 0, 0, 43, 36, 44, 45, 21, 45, 46, 0, 0, 0, 19, 1, 21, 0, + 0, 47, 0, 38, 48, 1, 1, 49, 49, 50, 0, 0, 51, 0, 0, 0, + 52, 1, 0, 0, 38, 14, 4, 1, 1, 1, 53, 21, 43, 52, 54, 21, + 35, 1, 0, 0, 0, 55, 0, 0, 0, 56, 57, 58, 0, 0, 0, 0, + 0, 59, 0, 60, 0, 0, 0, 0, 61, 62, 0, 0, 63, 0, 0, 0, + 64, 0, 0, 0, 65, 0, 0, 0, 66, 0, 0, 0, 67, 0, 0, 0, + 68, 0, 0, 69, 70, 0, 71, 72, 73, 74, 75, 76, 0, 0, 0, 77, + 0, 0, 0, 78, 79, 0, 0, 0, 0, 47, 0, 0, 0, 49, 0, 80, + 0, 0, 0, 62, 0, 0, 63, 0, 0, 81, 0, 0, 82, 0, 0, 0, + 83, 0, 0, 19, 84, 0, 62, 0, 0, 0, 0, 49, 1, 85, 1, 52, + 15, 86, 36, 10, 21, 87, 0, 55, 0, 0, 0, 0, 19, 10, 1, 0, + 0, 0, 0, 0, 88, 0, 0, 89, 0, 0, 88, 0, 0, 0, 0, 78, + 0, 0, 87, 9, 12, 4, 90, 8, 91, 47, 0, 58, 50, 0, 21, 1, + 21, 92, 93, 1, 1, 1, 1, 94, 95, 96, 97, 1, 98, 58, 81, 99, + 100, 4, 58, 0, 0, 0, 0, 0, 0, 19, 50, 0, 0, 0, 0, 0, + 0, 61, 0, 0,101,102, 0, 0,103, 0, 0, 1, 1, 50, 0, 0, + 0, 38, 0, 63, 0, 0, 0, 0, 0, 62, 0, 0,104, 68, 61, 0, + 0, 0, 78, 0, 0, 0,105,106, 58, 38, 81, 0, 0, 0, 0, 0, + 0,107, 1, 14, 4, 12, 84, 0, 0, 0, 0, 38, 87, 0, 0, 0, + 0,108, 0, 0,109, 61, 0,110, 0, 0, 0, 1, 0, 0, 0, 0, + 19, 58, 0, 0, 0, 51, 0,111, 14, 52,112, 41, 0, 0, 62, 0, + 0, 61, 0, 0,113, 0, 87, 0, 0, 0, 61, 62, 0, 0, 62, 0, + 89, 0, 0,113, 0, 0, 0, 0,114, 0, 0, 0, 78, 55, 0, 38, + 1, 58, 1, 58, 0, 0, 63, 89, 0, 0,115, 0, 0, 0, 55, 0, + 0, 0, 0,115, 0, 0, 0, 0, 61, 0, 0, 0, 0, 79, 0, 61, + 0, 0, 0, 0, 56, 0, 89, 80, 0, 0, 79, 0, 0, 0, 8, 91, + 0, 0, 1, 87, 0, 0,116, 0, 0, 0, 0, 0, 0,117, 0,118, + 119,120,121, 0,104, 4,122, 49, 23, 0, 0, 0, 38, 50, 38, 58, + 0, 0, 1, 87, 1, 1, 1, 1, 39, 1, 48,105, 87, 0, 0, 0, + 0, 1, 0, 0, 0,123, 4,122, 0, 0, 0, 1,124, 0, 0, 0, + 0, 0,230,230,230,230,230,232,220,220,220,220,232,216,220,220, + 220,220,220,202,202,220,220,220,220,202,202,220,220,220, 1, 1, + 1, 1, 1,220,220,220,220,230,230,230,230,240,230,220,220,220, + 230,230,230,220,220, 0,230,230,230,220,220,220,220,230,232,220, + 220,230,233,234,234,233,234,234,233,230, 0, 0, 0,230, 0,220, + 230,230,230,230,220,230,230,230,222,220,230,230,220,220,230,222, + 228,230, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, + 0, 23, 0, 24, 25, 0,230,220, 0, 18, 30, 31, 32, 0, 0, 0, + 0, 27, 28, 29, 30, 31, 32, 33, 34,230,230,220,220,230,220,230, + 230,220, 35, 0, 0, 0, 0, 0,230,230,230, 0, 0,230,230, 0, + 220,230,230,220, 0, 0, 0, 36, 0, 0,230,220,230,230,220,220, + 230,220,220,230,220,230,220,230,230, 0, 0,220, 0, 0,230,230, + 0,230, 0,230,230,230,230,230, 0, 0, 0,220,220,220,230,220, + 220,220,230,230, 0,220, 27, 28, 29,230, 7, 0, 0, 0, 0, 9, + 0, 0, 0,230,220,230,230, 0, 0, 0, 0, 0,230, 0, 0, 84, + 91, 0, 0, 0, 0, 9, 9, 0, 0, 0, 0, 0, 9, 0,103,103, + 9, 0,107,107,107,107,118,118, 9, 0,122,122,122,122,220,220, + 0, 0, 0,220, 0,220, 0,216, 0, 0, 0,129,130, 0,132, 0, + 0, 0, 0, 0,130,130,130,130, 0, 0,130, 0,230,230, 9, 0, + 230,230, 0, 0,220, 0, 0, 0, 0, 7, 0, 9, 9, 0, 9, 9, + 0, 0, 0,230, 0, 0, 0,228, 0, 0, 0,222,230,220,220, 0, + 0, 0,230, 0, 0,220,230,220, 0,220,230,230,230, 0, 0, 0, + 9, 9, 0, 0, 7, 0,230, 0, 1, 1, 1, 0, 0, 0,230,234, + 214,220,202,230,230,230,230,230,232,228,228,220,218,230,233,220, + 230,220,230,230, 1, 1, 1, 1, 1,230, 0, 1, 1,230,220,230, + 1, 1, 0, 0,218,228,232,222,224,224, 0, 8, 8, 0, 0, 0, + 0,220,230, 0,230,230,220, 0, 0,230, 0, 0, 26, 0, 0,220, + 0,230,230, 1,220, 0, 0,230,220, 0, 0, 0,220,220, 0, 0, + 230,220, 0, 9, 7, 0, 0, 7, 9, 0, 0, 0, 9, 7, 6, 6, + 0, 0, 0, 0, 1, 0, 0,216,216, 1, 1, 1, 0, 0, 0,226, + 216,216,216,216,216, 0,220,220,220, 0,232,232,220,230,230,230, + 7, 0, 16, 17, 17, 33, 17, 49, 17, 17, 84, 97,135,145, 26, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17,177, 0, 1, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 4, 3, 3, 3, 3, 3, 5, 3, 3, 3, + 3, 3, 6, 7, 8, 3, 3, 3, 3, 3, 9, 10, 11, 12, 13, 3, + 3, 3, 3, 3, 3, 3, 3, 14, 3, 15, 3, 3, 3, 3, 3, 3, + 16, 17, 18, 19, 20, 21, 3, 3, 3, 22, 23, 24, 3, 3, 3, 3, + 3, 3, 25, 3, 3, 3, 3, 3, 3, 3, 3, 26, 3, 3, 27, 28, + 0, 1, 0, 0, 0, 0, 0, 1, 0, 2, 0, 0, 0, 3, 0, 0, + 0, 3, 0, 0, 0, 0, 0, 4, 0, 5, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 7, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 9, 0, 0, 0, 0, 0, + 0, 9, 0, 9, 0, 0, 0, 0, 0, 0, 0, 10, 11, 12, 13, 0, + 0, 14, 15, 16, 6, 0, 17, 18, 19, 19, 19, 20, 21, 22, 23, 24, + 19, 25, 0, 26, 27, 19, 19, 28, 29, 30, 0, 31, 0, 0, 0, 8, + 0, 0, 0, 0, 0, 0, 0, 19, 28, 0, 32, 33, 9, 34, 35, 19, + 0, 0, 36, 37, 38, 39, 40, 19, 0, 41, 42, 43, 44, 31, 0, 1, + 45, 42, 0, 0, 0, 0, 0, 32, 14, 14, 0, 0, 0, 0, 14, 0, + 0, 46, 47, 47, 47, 47, 48, 49, 47, 47, 47, 47, 50, 51, 52, 53, + 43, 21, 0, 0, 0, 0, 0, 0, 0, 54, 6, 55, 0, 14, 19, 1, + 0, 0, 0, 0, 56, 57, 0, 0, 0, 0, 0, 19, 58, 31, 0, 0, + 0, 0, 0, 0, 0, 59, 14, 0, 0, 0, 0, 1, 0, 2, 0, 0, + 0, 3, 0, 0, 0, 60, 61, 0, 0, 0, 0, 0, 0, 0, 1, 0, + 0, 0, 0, 0, 2, 3, 0, 4, 5, 0, 0, 6, 0, 0, 0, 7, + 0, 0, 0, 1, 1, 0, 0, 8, 9, 0, 8, 9, 0, 0, 0, 0, + 8, 9, 10, 11, 12, 0, 0, 0, 13, 0, 0, 0, 0, 14, 15, 16, + 17, 0, 0, 0, 1, 0, 0, 18, 19, 0, 0, 0, 20, 0, 0, 0, + 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 8, 21, 9, + 0, 0, 22, 0, 0, 0, 0, 1, 0, 23, 24, 25, 0, 0, 26, 0, + 0, 0, 8, 21, 27, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 28, + 29, 30, 0, 31, 32, 20, 1, 1, 0, 0, 0, 8, 21, 9, 1, 4, + 5, 0, 0, 0, 33, 9, 0, 1, 1, 1, 0, 8, 21, 21, 21, 21, + 34, 1, 35, 21, 21, 21, 9, 36, 0, 0, 37, 38, 1, 0, 39, 0, + 0, 0, 1, 0, 1, 0, 0, 0, 0, 8, 21, 9, 1, 0, 0, 0, + 40, 0, 8, 21, 21, 21, 21, 21, 21, 21, 21, 9, 0, 1, 1, 1, + 1, 8, 21, 21, 21, 9, 0, 0, 0, 41, 0, 42, 43, 0, 0, 0, + 1, 44, 0, 0, 0, 45, 8, 9, 1, 0, 0, 0, 8, 21, 21, 21, + 9, 0, 1, 0, 1, 1, 8, 21, 21, 9, 0, 4, 5, 8, 9, 1, + 0, 0, 0, 1, 2, 3, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, + 13, 14, 3, 3, 3, 3, 3, 3, 3, 15, 3, 16, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 18, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, + 12, 13, 14, 15, 16, 17, 17, 17, 18, 17, 19, 20, 21, 22, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 24, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 25, 25, 26, 27, 28, 29, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 52, 53, 31, 31, 31, 31, 54, 55, 55, 56, 31, + 31, 31, 31, 31, 31, 31, 57, 58, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 59, 60, 31, 61, 62, 62, 62, 62, + 62, 62, 62, 62, 62, 62, 62, 62, 62, 63, 64, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 65, 66, 67, 31, 31, + 31, 31, 68, 31, 31, 31, 31, 31, 31, 31, 31, 69, 70, 71, 17, 17, + 72, 73, 31, 74, 75, 76, 77, 78, 79, 31, 80, 81, 17, 82, 17, 17, + 17, 17, 31, 31, 23, 23, 23, 23, 23, 23, 31, 31, 31, 31, 31, 31, + 23, 83, 31, 31, 23, 23, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 84, 0, 0, 1, 0, 1, 2, 3, 0, 1, 2, 3, + 4, 5, 6, 7, 0, 1, 2, 3, 4, 4, 4, 4, 4, 4, 5, 6, + 7, 8, 9, 10, 11, 11, 12, 11, 13, 14, 15, 16, 17, 18, 19, 20, + 21, 22, 23, 24, 25, 26, 19, 27, 28, 29, 30, 30, 31, 31, 32, 32, + 33, 33, 34, 34, 35, 35, 36, 36, 37, 37, 38, 38, 39, 40, 41, 41, + 42, 42, 42, 43, 44, 44, 45, 46, 47, 47, 47, 47, 48, 48, 48, 48, + 48, 48, 49, 50, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 52, 53, + 54, 55, 56, 56, 57, 58, 59, 51, 60, 61, 62, 63, 64, 65, 66, 7, + 67, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 7, 4, 4, 4, 4, + 77, 77, 77, 77, 78, 79, 80, 81, 82, 83, 84, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 85, 85, 85, 85, 0, 0, 0, 0, 86, 87, 88, 88, + 89, 90, 48, 91, 0, 0, 92, 92, 92, 92, 92, 93, 94, 95, 96, 97, + 98, 47, 99,100,101,102, 0,103,104,105, 0, 0, 92, 92, 92, 92, + 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 0,106,106,106,106, + 106,106,106,106,106,106,106,107,108,108,108,108,108, 11,109,110, + 111, 4,112, 4,113,114,115,116,117,118,119,120,121,122,123,124, + 125,126, 50,127, 47, 47, 47, 47, 47, 47, 47, 47,128,128,128,128, + 128,128,128,128,128,128,128,128, 92, 92, 92, 92, 92, 92, 92, 92, + 129,130, 19, 19, 19, 19, 19, 19,131, 19, 19, 19,132,133, 19,134, + 135,136,137,101,138,138,138,138, 0, 77,139,140,128,128,141,142, + 143,144,145,146,147,148,149,150,151,152,153,153,154,154,154,154, + 154,154, 4, 4,155,156,157,158,159,160,161,162,163,164,165,166, + 167,168,169,169,170,170,171,171,172,172,128,128, 19, 19,173,174, + 175,176,177,178,179,179,180,181,182,183,184,185,186,186,187,188, + 189,190,128,128,191,191,192,192,128,128,193,193,194,195,196,196, + 197,197,128,128,198,198,199,199,200,200,201,201,202,203,204,205, + 28, 28,128,128,206,207,208,208,209,210,211,211,128,128,212,212, + 213,213,214, 34,215,215,215,215,215,215,215,215,215,215,215,215, + 215,215,128,128,128,128,128,128,128,128,216,216,217,217,217,217, + 217,217,217,217,217,217,128,128,128,128,128,128,218,218,218,218, + 218,218,218,218,218,218,128,128,128,128,128,128,110,110,110,110, + 110,110,110,110,110,219,220,221,222,222,222,222,223,223,223,223, + 224,224,224,225,226,226,226,226,226,226,226,226,226,226,226,226, + 227,227,227,227,227,227,227,227,226,226,128,128,128,128,128,128, + 128,128,104,104,228,229,229,229,230,231,232,232,232,232,232,232, + 128,128,128,128,233,233,234, 0,128,128,128,128,128,128,128,128, + 7,235, 0, 0, 0, 0, 0, 0, 0,236,237, 0, 77, 77, 0, 0, + 0, 0,128,128,238,238,238,238,238,238,238,238,238,238,238,238, + 128,128,128,128,128,128,128,128, 4, 4,128,128,239, 11, 11, 11, + 240,240,128,128,128,128,241,242,128,128,128,128,128,128,243,243, + 128,128,128,128,128,128,128,128,128,128, 48, 48,244,244,244,244, + 245,245,128,128, 0, 0, 0, 0, 0, 0,128,128, 19, 19, 19, 19, + 128,128,128,128,246, 0,128,128, 0, 0, 0, 0, 92, 92,128,128, + 128,128,128,128, 0, 0,128,128, 7, 7, 7, 7, 0, 0, 0, 0, + 1, 2, 1, 2, 0, 0, 3, 3, 4, 5, 4, 5, 4, 4, 4, 4, + 4, 4, 4, 6, 0, 0, 7, 0, 8, 8, 8, 8, 8, 8, 8, 9, + 10, 11, 11, 11, 11, 11, 12, 11, 13, 13, 13, 13, 14, 13, 13, 13, + 13, 13, 13, 15, 16, 16, 16, 16, 16, 17, 18, 18, 18, 18, 18, 18, + 19, 20, 21, 21, 22, 23, 21, 24, 21, 21, 21, 21, 21, 25, 21, 21, + 26, 26, 26, 26, 26, 21, 21, 21, 27, 27, 27, 27, 28, 28, 28, 28, + 29, 29, 29, 29, 30, 30, 26, 21, 21, 21, 31, 21, 32, 32, 32, 32, + 32, 33, 34, 32, 35, 35, 35, 35, 36, 36, 36, 36, 37, 37, 37, 37, + 38, 38, 38, 38, 39, 39, 39, 39, 40, 40, 40, 40, 41, 41, 41, 41, + 42, 42, 42, 42, 43, 43, 43, 43, 44, 44, 44, 45, 44, 44, 44, 44, + 46, 46, 46, 46, 47, 47, 47, 47, 47, 48, 47, 47, 49, 49, 49, 49, + 49, 49, 50, 50, 50, 50, 50, 51, 52, 52, 52, 52, 53, 53, 53, 53, + 53, 53, 54, 54, 54, 54, 54, 54, 55, 55, 55, 55, 56, 56, 57, 57, + 57, 57, 58, 57, 59, 59, 60, 61, 62, 62, 63, 63, 64, 64, 64, 64, + 65, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 55, 67, 67, 67, 67, + 67, 68, 68, 68, 69, 69, 69, 69, 69, 69, 64, 64, 70, 70, 71, 71, + 71, 71, 71, 71, 71, 71, 71, 8, 72, 72, 72, 72, 73, 73, 73, 73, + 74, 74, 74, 74, 75, 75, 75, 75, 75, 76, 76, 76, 13, 50, 50, 50, + 73, 77, 78, 79, 4, 4, 80, 4, 4, 81, 82, 83, 4, 4, 4, 84, + 11, 11, 11, 11, 85, 0, 0, 0, 0, 0, 0, 86, 0, 4, 0, 0, + 0, 8, 8, 8, 0, 0, 87, 88, 89, 0, 4, 4, 6, 0, 0, 0, + 90, 90, 90, 90, 91, 91, 91, 91, 91, 91, 4, 4, 92, 92, 92, 92, + 50, 50, 50, 93, 93, 93, 93, 93, 53, 53, 13, 13, 94, 94, 94, 94, + 94, 94, 94, 0, 95, 0, 96, 97, 98, 99, 99, 99, 99,100,101,102, + 102,102,102,103,104,104,104,105, 52, 0,104,104, 0, 0, 0,102, + 52, 52, 0, 0, 0, 0, 52,106, 0,102,102,107,102,102,102,102, + 102,108, 0, 0,109,109,109,109,109,110,110,110,111,111,111,111, + 13, 13,112,112,112,112,112,112, 0, 0,113, 4,114, 4, 4, 4, + 115,115,115, 0,116,116,116,116,117,117,117,117,117,117, 32, 32, + 118,118,119,120,120,120, 52, 52,121,121,121,121,122,121, 49, 49, + 123,123,123,123,123,123, 49, 49,124,124,124,124,124,124,125,125, + 53, 53, 53, 4, 4,126,127, 54,125,125,125,125,128,128,128,128, + 4,129, 18, 18, 18, 21, 21, 21, 21, 21, 21,130, 8, 0,131, 0, + 0, 0, 0, 21, 21, 21, 21,132, 0, 0, 1, 2, 1, 2,133,101, + 102,134, 52, 52,135,135,135,135, 11, 0, 11, 11, 11, 0, 0,136, + 137,137,138,138,138,138,139, 0,140,140,140,141,141,142,142,142, + 143,143,144,144,144,144,144,144,145,145,145,145,145,146,146,146, + 147,147,147,148,148,148,148,148,149,149,149,150,150,150,150,151, + 151,151,151,151,152,152,152,152,153,153,153,153,154,154,155,155, + 156,156,156,156,156,156,157,157,158,158,159,159,159,159,159,159, + 160,160,161,161,161,161,161,161,162,162,162,162,162,162,163,163, + 164,164,164,164,165,165,165,165,166,166,166,166,167,167,168,168, + 169,169,169,169,170,170,170,170,171,171,171,171,172,172,172,172, + 173,173,173,173,173,173,173,174,175,175,175,176,176,176,176,177, + 177,177,177,178,178,178,179,179,180,180,180,180,181,181,181,181, + 181,182,182,182,183,183,183,183,183,184,184,184,185,185,185,185, + 185,185,186, 43,187,187,187,187,188,188,188,189,189,189,189,189, + 190,190,190,191,190,190,190,190,192,192,192,192,193,193,193,193, + 194,194,194,194,195,195,195,195,195,195, 66, 66,196,196,196,196, + 197,197,197,197,198,198,198,198,199,199,199,199,200,200,200,200, + 201,201,201,201,202,202,202,202,202,203,203,203,203,203,203, 55, + 204,204,204,204,205,205,205,205,205,205,205,206,206,206,206,206, + 207,207,207,207,207,207,208,208,208,208,208,208,209,209,209,209, + 210,210,210,210,110,110,110,110,211,211,211,211,212,212,212,212, + 213,213,213,213,214,214,214,214,215,215,215,216,216,216,216,216, + 216,217,217,217,218,218,218,218,219,219,219,219,220,220,220,220, + 220,220,221, 94,222,222,222,222,223,223,223,223,224, 99, 99, 99, + 99, 99, 99, 99, 99, 99,102,225, 99,226,102,227,227,227,227,227, + 228,228,228,228,228,228, 0, 0, 8, 0, 0, 0, 0, 0,229,230, + 231, 0,232, 0,233,233,233,233, 91, 91, 91, 13,234,234,234,234, + 235,235,235,235,236,236,236,236,237,237,237,237,238,238,238,238, + 239,239,239,239,240, 0, 0, 0, 0, 0, 0, 0, 1, 2, 2, 2, + 2, 2, 3, 0, 0, 0, 4, 0, 2, 2, 2, 2, 2, 3, 2, 2, + 2, 2, 5, 0, 2, 5, 6, 0, 7, 7, 7, 7, 8, 9, 8, 10, + 8, 11, 8, 8, 8, 8, 8, 8, 12, 13, 13, 13, 14, 14, 14, 14, + 14, 15, 14, 14, 16, 17, 17, 17, 17, 17, 17, 17, 18, 19, 19, 19, + 19, 19, 19, 19, 20, 21, 20, 22, 20, 20, 23, 23, 20, 20, 20, 20, + 22, 20, 24, 7, 7, 25, 20, 20, 26, 20, 20, 20, 20, 20, 20, 21, + 27, 27, 27, 27, 28, 28, 28, 28, 29, 29, 29, 29, 30, 30, 30, 30, + 31, 31, 31, 31, 32, 20, 20, 20, 33, 33, 33, 33, 34, 35, 33, 33, + 33, 36, 33, 33, 37, 37, 37, 37, 38, 38, 38, 38, 39, 39, 39, 39, + 40, 40, 40, 40, 41, 41, 41, 41, 42, 42, 42, 42, 43, 43, 43, 43, + 44, 44, 44, 44, 45, 45, 45, 45, 46, 46, 46, 46, 46, 46, 46, 47, + 48, 48, 48, 48, 49, 49, 49, 49, 49, 50, 51, 49, 52, 52, 52, 52, + 53, 53, 53, 53, 53, 53, 54, 53, 55, 55, 55, 55, 56, 56, 56, 56, + 57, 57, 57, 57, 58, 58, 58, 58, 59, 59, 59, 59, 60, 60, 60, 60, + 60, 60, 61, 62, 63, 63, 63, 63, 64, 64, 64, 64, 64, 65, 0, 0, + 66, 66, 66, 66, 67, 67, 67, 67, 68, 68, 68, 68, 69, 70, 71, 71, + 71, 71, 71, 71, 72, 72, 72, 72, 73, 73, 73, 73, 74, 74, 74, 74, + 75, 75, 75, 75, 76, 76, 76, 76, 77, 77, 77, 77, 78, 78, 78, 78, + 79, 79, 79, 79, 80, 80, 80, 80, 81, 81, 81, 81, 82, 7, 7, 7, + 83, 7, 84, 85, 0, 84, 86, 0, 2, 87, 88, 2, 2, 2, 2, 89, + 90, 87, 91, 2, 2, 2, 92, 2, 2, 2, 2, 93, 0, 0, 0, 86, + 1, 0, 0, 94, 0, 95, 96, 0, 4, 0, 0, 0, 0, 0, 0, 4, + 97, 97, 97, 97, 98, 98, 98, 98, 13, 13, 13, 13, 99, 99, 99, 99, + 100,100,100,100, 0,101, 0, 0,102,100,103,104, 0, 0,100, 0, + 105,106,106,106,106,106,106,106,106,106,107,105,108,109,109,109, + 109,109,109,109,109,109,110,108,111,111,111,111,112, 55, 55, 55, + 55, 55, 55,113,109,109,109,110,109,109, 0, 0,114,114,114,114, + 115,115,115,115,116,116,116,116,117,117,117,117, 96, 2, 2, 2, + 2, 2, 94, 2,118,118,118,118,119,119,119,119,120,120,120,120, + 121,121,121,121,121,121,121,122,123,123,123,123,124,124,124,124, + 124,124,124,125,126,126,126,126,127,127,127,127,128,128,128,128, + 2, 2, 3, 2, 2,129,130, 0,131,131,131,131,132, 17, 17, 18, + 20, 20, 20,133, 7, 7, 7,134, 20, 20, 20, 23, 0,135,109,109, + 109,109,109,136,137,137,137,137, 0, 0, 0,138,139,139,139,139, + 140,140,140,140, 84, 0, 0, 0,141,141,141,141,142,142,142,142, + 143,143,143,143,144,144,144,144,145,145,145,145,146,146,146,146, + 147,147,147,147,148,148,148,148,149,149,149,149,150,150,150,150, + 151,151,151,151,152,152,152,152,153,153,153,153,154,154,154,154, + 155,155,155,155,156,156,156,156,157,157,157,157,158,158,158,158, + 159,159,159,159,160,160,160,160,161,161,161,161,162,162,162,162, + 163,163,163,163,164,164,164,164,165,165,165,165,166,166,166,166, + 167,167,167,167,168,168,168,168,169,169,169,169,170,170,170,170, + 171,171,171,171,172,172,172,172,173,173,173,173,174,174,174,174, + 174,174,174,175,176,176,176,176,177,177,177,177,178,178,178,178, + 179,179,179,179,180,180,180,180,181,181,181,181,182,182,182,182, + 183,183,183,183,184,184,184,184,185,185,185,185,186,186,186,186, + 187, 45, 45, 45,188,188,188,188,189,189,189,189,190,190,190,190, + 191,191,191,191,191,191,192,191,193,193,193,193,194,194,194,194, + 195,195,195,195,196,196,196,196,197,197,197,197,198,198,198,198, + 199,199,199,199,200,200,200,200,201,201,201,201,202,202,202,202, + 203,203,203,203,204,204,204,204,205,205,205,205,206,206,206,206, + 207,207,207,207,208,208,208,208,209,209,209,209,210,210,210,210, + 211,211,211,211,212,212,212,212,213,213,213,213,214,214,214,214, + 215,215,215,215,216,216,216,216,217,217,217,217,218,218,218,218, + 219,219,219,219,220,220,220,220,221,221,221,221,222,223,223,223, + 224,224,224,224,223,223,223,223,225,106,106,106,226,106,106,106, + 106,227,109,109,228,228,228,228,229,229,229,229, 0,230, 86, 0, + 0, 0,230, 7, 82,138, 7, 0, 0, 0,231, 86,232,232,232,232, + 233,233,233,233,234,234,234,234,235,235,235,235,236,236,236,236, + 237,237,237,237,238,238,238,238,239, 0, 0, 0, 0, 0, 0, 0, + 0, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 0, 0, 0, 19, 0, + 19, 0, 0, 0, 0, 0, 26, 26, 1, 1, 1, 1, 9, 9, 9, 9, + 0, 9, 9, 9, 9, 9, 0, 9, 9, 0, 9, 0, 9, 9, 55, 55, + 55, 55, 55, 55, 6, 6, 6, 6, 6, 1, 1, 6, 6, 4, 4, 4, + 4, 4, 4, 4, 4, 14, 14, 14, 14, 14, 14, 14, 3, 3, 3, 3, + 3, 0, 3, 3, 0, 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 1, + 1, 1, 3, 3, 1, 3, 3, 3, 37, 37, 37, 37, 38, 38, 38, 38, + 64, 64, 64, 64, 90, 90, 90, 90, 95, 95, 95, 95, 3, 3, 0, 3, + 7, 7, 7, 7, 7, 1, 1, 1, 1, 7, 7, 7, 0, 0, 7, 7, + 5, 5, 5, 5, 11, 11, 11, 11, 10, 10, 10, 10, 21, 21, 21, 21, + 22, 22, 22, 22, 23, 23, 23, 23, 16, 16, 16, 16, 20, 20, 20, 20, + 36, 36, 36, 36, 24, 24, 24, 24, 24, 24, 24, 0, 18, 18, 18, 18, + 25, 25, 25, 25, 25, 0, 0, 0, 0, 25, 25, 25, 33, 33, 33, 33, + 8, 8, 8, 8, 8, 8, 8, 0, 12, 12, 12, 12, 30, 30, 30, 30, + 29, 29, 29, 29, 28, 28, 28, 28, 34, 34, 34, 34, 35, 35, 35, 35, + 35, 35, 35, 0, 0, 0, 35, 35, 45, 45, 45, 45, 44, 44, 44, 44, + 44, 0, 0, 0, 43, 43, 43, 43, 46, 46, 46, 46, 31, 31, 31, 31, + 32, 32, 0, 0, 32, 0, 32, 32, 32, 32, 32, 32, 48, 48, 48, 48, + 52, 52, 52, 52, 58, 58, 58, 58, 54, 54, 54, 54, 91, 91, 91, 91, + 62, 62, 62, 62, 76, 76, 76, 76, 93, 93, 93, 93, 70, 70, 70, 70, + 73, 73, 73, 73, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, + 0, 1, 0, 0, 1, 1, 0, 0, 19, 19, 9, 9, 9, 9, 9, 6, + 19, 9, 9, 9, 9, 9, 19, 19, 9, 9, 9, 19, 6, 19, 19, 19, + 19, 19, 19, 9, 0, 0, 0, 19, 0, 0, 9, 0, 0, 0, 19, 19, + 27, 27, 27, 27, 56, 56, 56, 56, 61, 61, 61, 61, 13, 13, 13, 13, + 0, 13, 0, 13, 0, 13, 13, 13, 13, 13, 1, 1, 1, 1, 12, 12, + 0, 15, 15, 15, 15, 15, 15, 15, 15, 1, 1, 0, 0, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 0, 26, 26, 26, 26, 26, 12, 12, 12, + 12, 12, 12, 0, 39, 39, 39, 39, 86, 86, 86, 86, 77, 77, 77, 77, + 79, 79, 79, 79, 60, 60, 60, 60, 65, 65, 65, 65, 75, 75, 75, 75, + 69, 69, 69, 69, 69, 69, 0, 69, 74, 74, 74, 74, 84, 84, 84, 84, + 84, 84, 84, 0, 68, 68, 68, 68, 92, 92, 92, 92, 87, 87, 87, 87, + 19, 9, 19, 19, 19, 19, 0, 0, 2, 2, 2, 2, 19, 19, 19, 4, + 3, 3, 0, 0, 1, 1, 6, 6, 0, 0, 17, 17, 17, 17, 0, 0, + 49, 49, 49, 49, 0, 1, 1, 1, 71, 71, 71, 71, 67, 67, 67, 67, + 42, 42, 42, 42, 41, 41, 41, 41,118,118,118,118, 53, 53, 53, 53, + 59, 59, 59, 59, 40, 40, 40, 40, 51, 51, 51, 51, 50, 50, 50, 50, + 135,135,135,135,106,106,106,106,104,104,104,104,161,161,161,161, + 110,110,110,110, 47, 47, 47, 47, 81, 81, 81, 81,120,120,120,120, + 116,116,116,116,128,128,128,128, 66, 66, 66, 66, 72, 72, 72, 72, + 98, 98, 98, 98, 97, 97, 97, 97, 57, 57, 57, 57, 88, 88, 88, 88, + 117,117,117,117,112,112,112,112, 78, 78, 78, 78, 83, 83, 83, 83, + 82, 82, 82, 82,122,122,122,122, 89, 89, 89, 89,130,130,130,130, + 144,144,144,144,156,156,156,156,156, 3, 3, 3,147,147,147,147, + 148,148,148,148,158,158,158,158,153,153,153,153,149,149,149,149, + 94, 94, 94, 94, 85, 85, 85, 85,101,101,101,101, 96, 96, 96, 96, + 111,111,111,111,100,100,100,100,100, 36, 36, 36,108,108,108,108, + 129,129,129,129,109,109,109,109,107,107,107,107,107,107,107, 1, + 137,137,137,137,124,124,124,124,123,123,123,123,114,114,114,114, + 102,102,102,102,126,126,126,126,142,142,142,142,125,125,125,125, + 154,154,154,154,150,150,150,150,141,141,141,141,140,140,140,140, + 121,121,121,121,133,133,133,133,134,134,134,134,138,138,138,138, + 143,143,143,143,145,145,145,145,163,163,163,163, 63, 63, 63, 63, + 157,157,157,157, 80, 80, 80, 80,127,127,127,127,115,115,115,115, + 159,159,159,159,103,103,103,103,119,119,119,119,146,146,146,146, + 99, 99, 99, 99,136,139, 13, 13,155,155,155,155,136,136,136,136, + 17, 15, 15, 15, 17, 17, 15, 15, 15, 17, 17, 17,139,139,139,139, + 105,105,105,105, 0, 0, 0, 1, 0, 0, 1, 1,131,131,131,131, + 151,151,151,151,160,160,160,160,152,152,152,152,164,164,164,164, + 113,113,113,113,132,132,132,132, 15, 0, 0, 0, 0, 1, 2, 3, + 4, 5, 6, 7, 8, 9, 9, 9, 9, 10, 9, 11, 12, 13, 9, 9, + 9, 14, 9, 9, 15, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 16, 17, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 18, 19, 20, 9, 21, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 22, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 23, 24, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, + 11, 12, 0, 0, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 23, 0, + 0, 24, 25, 26, 27, 28, 29, 30, 0, 0, 31, 32, 0, 33, 0, 34, + 0, 35, 0, 0, 0, 0, 36, 37, 38, 39, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 41, 42, 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, 43, 44, 0, 45, + 0, 0, 0, 0, 0, 0, 46, 47, 0, 0, 0, 0, 0, 48, 0, 49, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 51, + 0, 0, 0, 52, 0, 0, 53, 0, 0, 0, 0, 0, 0, 0, 54, 0, + 0, 0, 0, 0, 0, 0, 55, 0, 0, 0, 0, 0, 0, 0, 56, 0, + 0, 0, 0, 0, 0, 0, 0, 57, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 58, 59, + 60, 61, 62, 63, 64, 65, 0, 0, 0, 0, 0, 0, 66, 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, 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, 67, 68, 0, 69, 70, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 71, 72, 73, 74, 75, 76, + 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, + 93, 94, 95, 96, 97, 98, 99,100,101,102,103, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,104, 0, 0, 0, + 0, 0, 0,105,106, 0,107, 0, 0, 0,108, 0,109, 0,110, 0, + 111,112,113, 0,114, 0, 0, 0,115, 0, 0, 0,116, 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,117, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,118,119, + 120,121, 0,122,123,124,125,126, 0,127, 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,128,129,130,131,132,133, + 134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149, + 150,151,152,153,154,155,156,157, 0, 0, 0,158,159,160,161, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0,162,163, 0, 0, 0, 0, 0, 0, 0,164, 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,165, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0,166, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0,167, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,168, 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,169, + 170, 0, 0, 0, 0,171,172, 0, 0, 0,173,174,175,176,177,178, + 179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194, + 195,196,197,198,199,200,201,202,203,204,205,206, 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, 1, 2, 3, 4, +}; +static const uint16_t +_hb_ucd_u16[4920] = +{ + 0, 0, 1, 2, 3, 4, 5, 6, 0, 0, 7, 8, 9, 10, 11, 12, + 13, 13, 13, 14, 15, 13, 13, 16, 17, 18, 19, 20, 21, 22, 13, 23, + 13, 13, 13, 24, 25, 11, 11, 11, 11, 26, 11, 27, 28, 29, 30, 31, + 32, 32, 32, 32, 32, 32, 32, 33, 34, 35, 36, 11, 37, 38, 13, 39, + 9, 9, 9, 11, 11, 11, 13, 13, 40, 13, 13, 13, 41, 13, 13, 13, + 13, 13, 13, 35, 9, 42, 11, 11, 43, 44, 32, 45, 46, 47, 47, 48, + 49, 50, 47, 47, 51, 32, 52, 53, 47, 47, 47, 47, 47, 54, 55, 56, + 57, 58, 47, 32, 59, 47, 47, 47, 47, 47, 60, 53, 61, 47, 62, 63, + 47, 64, 65, 66, 47, 67, 47, 47, 68, 69, 47, 47, 70, 32, 71, 32, + 72, 47, 47, 73, 74, 75, 76, 77, 78, 47, 47, 79, 80, 81, 82, 83, + 84, 47, 47, 85, 86, 87, 88, 89, 84, 47, 47, 79, 90, 47, 82, 91, + 92, 47, 47, 93, 94, 95, 82, 96, 97, 47, 47, 98, 99, 100, 101, 102, + 103, 47, 47, 104, 105, 106, 82, 107, 108, 47, 47, 93, 109, 110, 82, 111, + 112, 47, 47, 113, 114, 115, 82, 116, 92, 47, 47, 47, 117, 118, 101, 119, + 47, 47, 47, 120, 121, 122, 66, 66, 47, 47, 47, 123, 124, 125, 47, 47, + 126, 127, 128, 129, 47, 47, 47, 130, 131, 32, 32, 132, 133, 134, 66, 66, + 47, 47, 135, 136, 122, 137, 138, 139, 140, 141, 9, 9, 9, 11, 11, 142, + 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 143, 144, 145, + 47, 146, 9, 9, 9, 9, 9, 147, 148, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 149, 47, 150, 151, 47, 47, 47, 47, 152, 153, + 47, 154, 47, 155, 47, 156, 47, 156, 47, 47, 47, 157, 158, 159, 160, 145, + 161, 160, 47, 47, 162, 47, 47, 47, 163, 47, 164, 47, 47, 47, 47, 47, + 47, 47, 165, 166, 167, 47, 47, 47, 47, 47, 47, 47, 47, 168, 146, 146, + 47, 169, 47, 47, 47, 170, 171, 172, 160, 160, 173, 174, 32, 32, 32, 32, + 175, 47, 47, 176, 177, 122, 178, 179, 180, 47, 181, 61, 47, 47, 182, 183, + 47, 47, 184, 185, 186, 61, 47, 187, 11, 9, 9, 9, 66, 188, 189, 190, + 11, 11, 191, 27, 27, 27, 192, 193, 11, 194, 27, 27, 32, 32, 32, 32, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 195, 13, 13, 13, 13, 13, 13, + 196, 196, 196, 196, 196, 197, 196, 11, 198, 198, 198, 199, 200, 201, 201, 200, + 202, 203, 204, 205, 206, 207, 208, 209, 210, 27, 211, 211, 211, 212, 213, 32, + 214, 215, 216, 217, 218, 145, 219, 219, 220, 221, 222, 146, 223, 224, 146, 225, + 226, 226, 226, 226, 226, 226, 226, 226, 227, 146, 228, 146, 146, 146, 146, 229, + 146, 230, 226, 231, 146, 232, 233, 146, 146, 146, 146, 146, 146, 146, 145, 145, + 145, 234, 146, 146, 146, 146, 235, 145, 146, 146, 146, 146, 146, 146, 146, 146, + 146, 146, 146, 236, 237, 146, 146, 238, 146, 146, 146, 146, 146, 146, 239, 146, + 146, 146, 146, 146, 146, 146, 240, 241, 145, 242, 146, 146, 243, 226, 244, 226, + 245, 246, 226, 226, 226, 247, 226, 248, 146, 146, 146, 226, 249, 146, 146, 146, + 9, 9, 9, 11, 11, 11, 250, 251, 13, 13, 13, 13, 13, 13, 252, 253, + 11, 11, 11, 47, 47, 47, 254, 255, 47, 47, 47, 47, 47, 47, 32, 32, + 256, 257, 258, 259, 260, 261, 262, 262, 263, 264, 265, 266, 267, 47, 47, 47, + 47, 268, 148, 47, 47, 47, 47, 269, 47, 270, 47, 47, 146, 146, 146, 47, + 146, 146, 271, 146, 272, 273, 146, 146, 271, 146, 146, 273, 146, 146, 146, 146, + 47, 47, 47, 47, 146, 146, 146, 146, 47, 274, 47, 47, 47, 47, 47, 47, + 47, 146, 146, 146, 146, 47, 47, 187, 275, 47, 61, 47, 13, 13, 276, 277, + 13, 278, 47, 47, 47, 47, 279, 280, 31, 281, 282, 283, 13, 13, 13, 284, + 285, 286, 287, 288, 289, 290, 11, 291, 292, 47, 293, 294, 47, 47, 47, 295, + 296, 47, 47, 297, 298, 160, 32, 299, 61, 47, 300, 47, 301, 302, 47, 47, + 72, 47, 47, 303, 304, 305, 306, 61, 47, 47, 307, 308, 309, 310, 47, 311, + 47, 47, 47, 312, 58, 313, 314, 315, 47, 47, 47, 11, 11, 316, 317, 11, + 11, 11, 11, 11, 47, 47, 318, 160, 319, 319, 319, 319, 319, 319, 319, 319, + 320, 320, 320, 320, 320, 320, 320, 320, 11, 321, 322, 47, 47, 47, 47, 47, + 47, 47, 47, 323, 31, 324, 47, 47, 47, 47, 47, 325, 146, 47, 47, 47, + 47, 47, 47, 47, 326, 146, 146, 327, 32, 328, 32, 329, 330, 331, 332, 47, + 47, 47, 47, 47, 47, 47, 47, 333, 334, 2, 3, 4, 5, 335, 336, 337, + 47, 338, 47, 47, 47, 47, 339, 340, 341, 145, 145, 342, 219, 219, 219, 343, + 344, 146, 146, 146, 146, 146, 146, 345, 346, 346, 346, 346, 346, 346, 346, 346, + 47, 47, 47, 47, 47, 47, 347, 145, 47, 47, 348, 47, 349, 47, 47, 60, + 47, 350, 47, 47, 47, 351, 219, 219, 9, 9, 147, 11, 11, 47, 47, 47, + 47, 47, 160, 9, 9, 147, 11, 11, 47, 47, 47, 47, 47, 47, 350, 9, + 9, 352, 11, 11, 11, 11, 11, 11, 27, 27, 27, 27, 27, 27, 27, 27, + 47, 47, 47, 47, 47, 353, 47, 354, 47, 47, 355, 145, 145, 145, 47, 356, + 47, 357, 47, 350, 66, 66, 66, 66, 47, 47, 47, 358, 145, 145, 145, 145, + 359, 47, 47, 360, 145, 66, 47, 361, 47, 362, 145, 145, 363, 47, 364, 66, + 47, 47, 47, 365, 47, 366, 47, 366, 47, 365, 144, 145, 145, 145, 145, 145, + 9, 9, 9, 9, 11, 11, 11, 367, 47, 47, 368, 160, 160, 160, 160, 160, + 145, 145, 145, 145, 145, 145, 145, 145, 47, 47, 369, 47, 47, 47, 47, 143, + 47, 362, 370, 47, 60, 371, 66, 47, 372, 66, 66, 47, 373, 145, 47, 47, + 374, 47, 47, 360, 375, 376, 377, 378, 180, 47, 47, 379, 380, 47, 47, 160, + 97, 47, 381, 382, 383, 47, 47, 384, 180, 47, 47, 385, 386, 387, 388, 145, + 47, 47, 389, 390, 359, 32, 32, 32, 47, 47, 365, 47, 47, 391, 172, 160, + 92, 47, 47, 113, 392, 393, 394, 32, 47, 47, 47, 395, 396, 397, 47, 47, + 47, 47, 47, 398, 399, 160, 160, 160, 47, 47, 400, 401, 402, 403, 32, 32, + 47, 47, 47, 404, 405, 160, 66, 66, 47, 47, 406, 407, 160, 160, 160, 160, + 47, 143, 408, 409, 47, 47, 47, 47, 47, 47, 389, 410, 66, 66, 66, 66, + 9, 9, 9, 9, 11, 11, 128, 411, 47, 47, 47, 412, 413, 160, 160, 160, + 47, 47, 47, 47, 47, 414, 415, 416, 417, 47, 47, 418, 419, 420, 47, 47, + 421, 422, 66, 47, 47, 47, 47, 47, 66, 66, 66, 66, 66, 66, 66, 66, + 47, 47, 400, 423, 424, 128, 145, 425, 47, 156, 426, 427, 32, 32, 32, 32, + 47, 47, 47, 359, 428, 160, 47, 47, 429, 430, 160, 160, 160, 160, 160, 160, + 47, 47, 47, 47, 47, 47, 47, 431, 432, 47, 47, 433, 434, 160, 160, 160, + 47, 47, 47, 47, 145, 435, 436, 437, 219, 219, 219, 219, 219, 219, 219, 66, + 47, 47, 47, 47, 47, 47, 47, 424, 47, 47, 47, 208, 438, 32, 32, 32, + 47, 47, 47, 47, 47, 47, 305, 47, 47, 47, 47, 47, 160, 47, 47, 439, + 47, 47, 47, 440, 441, 442, 443, 47, 9, 9, 9, 9, 9, 9, 11, 11, + 145, 444, 66, 66, 66, 66, 66, 66, 47, 47, 47, 47, 391, 445, 416, 416, + 446, 447, 27, 27, 27, 27, 448, 416, 47, 449, 208, 208, 208, 208, 208, 208, + 32, 32, 32, 32, 32, 146, 146, 146, 146, 146, 146, 146, 146, 146, 450, 451, + 452, 146, 453, 146, 146, 146, 146, 146, 146, 146, 146, 146, 454, 146, 146, 146, + 9, 455, 11, 456, 457, 11, 196, 9, 458, 459, 9, 460, 11, 9, 455, 11, + 456, 457, 11, 196, 9, 458, 459, 9, 460, 11, 9, 455, 11, 456, 457, 11, + 196, 9, 458, 459, 9, 460, 11, 9, 455, 11, 196, 9, 461, 462, 463, 464, + 11, 465, 9, 466, 467, 468, 469, 11, 470, 9, 471, 11, 472, 160, 160, 160, + 32, 32, 32, 473, 32, 32, 474, 475, 476, 477, 32, 32, 32, 32, 32, 32, + 478, 11, 11, 11, 11, 11, 11, 11, 32, 32, 32, 27, 27, 27, 27, 27, + 32, 32, 32, 32, 32, 32, 32, 32, 47, 47, 47, 479, 480, 146, 146, 146, + 47, 47, 481, 32, 47, 47, 482, 483, 47, 47, 47, 47, 47, 47, 484, 160, + 47, 47, 47, 47, 355, 32, 32, 32, 9, 9, 458, 11, 485, 305, 66, 66, + 145, 145, 486, 487, 145, 145, 145, 145, 145, 145, 488, 145, 145, 145, 145, 145, + 47, 47, 47, 47, 47, 47, 47, 226, 489, 146, 146, 146, 146, 146, 146, 146, + 146, 146, 146, 146, 146, 146, 146, 490, 146, 146, 146, 146, 146, 146, 146, 160, + 208, 208, 208, 208, 208, 208, 208, 208, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 939, 940, 941, 942, 946, 948, 0, 962, + 969, 970, 971, 976,1001,1002,1003,1008, 0,1033,1040,1041,1042,1043,1047, 0, + 0,1080,1081,1082,1086,1110, 0, 0,1124,1125,1126,1127,1131,1133, 0,1147, + 1154,1155,1156,1161,1187,1188,1189,1193, 0,1219,1226,1227,1228,1229,1233, 0, + 0,1267,1268,1269,1273,1298, 0,1303, 943,1128, 944,1129, 954,1139, 958,1143, + 959,1144, 960,1145, 961,1146, 964,1149, 0, 0, 973,1158, 974,1159, 975,1160, + 983,1168, 978,1163, 988,1173, 990,1175, 991,1176, 993,1178, 994,1179, 0, 0, + 1004,1190,1005,1191,1006,1192,1014,1199,1007, 0, 0, 0,1016,1201,1020,1206, + 0,1022,1208,1025,1211,1023,1209, 0, 0, 0, 0,1032,1218,1037,1223,1035, + 1221, 0, 0, 0,1044,1230,1045,1231,1049,1235, 0, 0,1058,1244,1064,1250, + 1060,1246,1066,1252,1067,1253,1072,1258,1069,1255,1077,1264,1074,1261, 0, 0, + 1083,1270,1084,1271,1085,1272,1088,1275,1089,1276,1096,1283,1103,1290,1111,1299, + 1115,1118,1307,1120,1309,1121,1310, 0,1053,1239, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0,1093,1280, 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, 949,1134,1010,1195,1050,1236,1090,1277,1341,1368,1340, + 1367,1342,1369,1339,1366, 0,1320,1347,1418,1419,1323,1350, 0, 0, 992,1177, + 1018,1204,1055,1241,1416,1417,1415,1424,1202, 0, 0, 0, 987,1172, 0, 0, + 1031,1217,1321,1348,1322,1349,1338,1365, 950,1135, 951,1136, 979,1164, 980,1165, + 1011,1196,1012,1197,1051,1237,1052,1238,1061,1247,1062,1248,1091,1278,1092,1279, + 1071,1257,1076,1263, 0, 0, 997,1182, 0, 0, 0, 0, 0, 0, 945,1130, + 982,1167,1337,1364,1335,1362,1046,1232,1422,1423,1113,1301, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 8, 9, 0, 10,1425, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,1314,1427, 5, + 1434,1438,1443, 0,1450, 0,1455,1461,1514, 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,1446,1458,1468,1476,1480,1486,1517, 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,1489,1503,1494,1500,1508, 0, 0, 0, 0,1520,1521, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0,1526,1528, 0,1525, 0, 0, 0,1522, + 0, 0, 0, 0,1536,1532,1539, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0,1534, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0,1556, 0, 0, 0, 0, 0, 0,1548,1550, 0,1547, 0, 0, 0,1567, + 0, 0, 0, 0,1558,1554,1561, 0, 0, 0, 0, 0, 0, 0,1568,1569, + 0, 0, 0, 0, 0, 0, 0, 0, 0,1529,1551, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0,1523,1545,1524,1546, 0, 0,1527,1549, + 0, 0,1570,1571,1530,1552,1531,1553, 0, 0,1533,1555,1535,1557,1537,1559, + 0, 0,1572,1573,1544,1566,1538,1560,1540,1562,1541,1563,1542,1564, 0, 0, + 1543,1565, 0, 0, 0, 0, 0, 0, 0, 0,1606,1607,1609,1608,1610, 0, + 0, 0, 0, 0, 0, 0, 0, 0,1613, 0,1611, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1612, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0,1620, 0, 0, 0, 0, 0, 0, 0,1623, 0, 0,1624, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1614,1615,1616,1617,1618,1619,1621,1622, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0,1628,1629, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0,1625,1626, 0,1627, 0, 0, 0,1634, 0, 0,1635, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0,1630,1631,1632, 0, 0,1633, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1639, 0, 0,1638,1640, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0,1636,1637, 0, 0, 0, 0, 0, 0,1641, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0,1642,1644,1643, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1645, 0, 0, 0, 0, 0, 0, 0,1646, 0, 0, 0, 0, 0, 0,1648, + 1649, 0,1647,1650, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0,1651,1653,1652, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0,1654, 0,1655,1657,1656, 0, 0, 0, 0,1659, 0, 0, 0, 0, + 0, 0, 0, 0, 0,1660, 0, 0, 0, 0,1661, 0, 0, 0, 0,1662, + 0, 0, 0, 0,1663, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0,1658, 0, 0, 0, 0, 0, 0, 0, 0, 0,1664, 0,1665,1673, 0, + 1674, 0, 0, 0, 0, 0, 0, 0, 0,1666, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1668, 0, 0, 0, 0, + 0, 0, 0, 0, 0,1669, 0, 0, 0, 0,1670, 0, 0, 0, 0,1671, + 0, 0, 0, 0,1672, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0,1667, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1675, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1676, 0, + 1677, 0,1678, 0,1679, 0,1680, 0, 0, 0,1681, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0,1682, 0,1683, 0, 0,1684,1685, 0,1686, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 953,1138, 955,1140, 956,1141, 957,1142, + 1324,1351, 963,1148, 965,1150, 968,1153, 966,1151, 967,1152,1378,1380,1379,1381, + 984,1169, 985,1170,1420,1421, 986,1171, 989,1174, 995,1180, 998,1183, 996,1181, + 999,1184,1000,1185,1015,1200,1329,1356,1017,1203,1019,1205,1021,1207,1024,1210, + 1687,1688,1027,1213,1026,1212,1028,1214,1029,1215,1030,1216,1034,1220,1036,1222, + 1039,1225,1038,1224,1334,1361,1336,1363,1382,1384,1383,1385,1056,1242,1057,1243, + 1059,1245,1063,1249,1689,1690,1065,1251,1068,1254,1070,1256,1386,1387,1388,1389, + 1691,1692,1073,1259,1075,1262,1079,1266,1078,1265,1095,1282,1098,1285,1097,1284, + 1390,1391,1392,1393,1099,1286,1100,1287,1101,1288,1102,1289,1105,1292,1104,1291, + 1106,1294,1107,1295,1108,1296,1114,1302,1119,1308,1122,1311,1123,1312,1186,1260, + 1293,1305, 0,1394, 0, 0, 0, 0, 952,1137, 947,1132,1317,1344,1316,1343, + 1319,1346,1318,1345,1693,1695,1371,1375,1370,1374,1373,1377,1372,1376,1694,1696, + 981,1166, 977,1162, 972,1157,1326,1353,1325,1352,1328,1355,1327,1354,1697,1698, + 1009,1194,1013,1198,1054,1240,1048,1234,1331,1358,1330,1357,1333,1360,1332,1359, + 1699,1700,1396,1401,1395,1400,1398,1403,1397,1402,1399,1404,1094,1281,1087,1274, + 1406,1411,1405,1410,1408,1413,1407,1412,1409,1414,1109,1297,1117,1306,1116,1304, + 1112,1300, 0, 0, 0, 0, 0, 0,1471,1472,1701,1705,1702,1706,1703,1707, + 1430,1431,1715,1719,1716,1720,1717,1721,1477,1478,1729,1731,1730,1732, 0, 0, + 1435,1436,1733,1735,1734,1736, 0, 0,1481,1482,1737,1741,1738,1742,1739,1743, + 1439,1440,1751,1755,1752,1756,1753,1757,1490,1491,1765,1768,1766,1769,1767,1770, + 1447,1448,1771,1774,1772,1775,1773,1776,1495,1496,1777,1779,1778,1780, 0, 0, + 1451,1452,1781,1783,1782,1784, 0, 0,1504,1505,1785,1788,1786,1789,1787,1790, + 0,1459, 0,1791, 0,1792, 0,1793,1509,1510,1794,1798,1795,1799,1796,1800, + 1462,1463,1808,1812,1809,1813,1810,1814,1467, 21,1475, 22,1479, 23,1485, 24, + 1493, 27,1499, 28,1507, 29, 0, 0,1704,1708,1709,1710,1711,1712,1713,1714, + 1718,1722,1723,1724,1725,1726,1727,1728,1740,1744,1745,1746,1747,1748,1749,1750, + 1754,1758,1759,1760,1761,1762,1763,1764,1797,1801,1802,1803,1804,1805,1806,1807, + 1811,1815,1816,1817,1818,1819,1820,1821,1470,1469,1822,1474,1465, 0,1473,1825, + 1429,1428,1426, 12,1432, 0, 26, 0, 0,1315,1823,1484,1466, 0,1483,1829, + 1433, 13,1437, 14,1441,1826,1827,1828,1488,1487,1513, 19, 0, 0,1492,1515, + 1445,1444,1442, 15, 0,1831,1832,1833,1502,1501,1516, 25,1497,1498,1506,1518, + 1457,1456,1454, 17,1453,1313, 11, 3, 0, 0,1824,1512,1519, 0,1511,1830, + 1449, 16,1460, 18,1464, 4, 0, 0, 30, 31, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, + 0, 0, 2, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0,1834,1835, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0,1836, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0,1837,1839,1838, 0, 0, 0, 0,1840, 0, 0, 0, + 0,1841, 0, 0,1842, 0, 0, 0, 0, 0, 0, 0,1843, 0,1844, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,1845, 0, 0,1846, 0, 0,1847, + 0,1848, 0, 0, 0, 0, 0, 0, 937, 0,1850, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0,1849, 936, 938,1851,1852, 0, 0,1853,1854, 0, 0, + 1855,1856, 0, 0, 0, 0, 0, 0,1857,1858, 0, 0,1861,1862, 0, 0, + 1863,1864, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0,1867,1868,1869,1870,1859,1860,1865,1866, 0, 0, 0, 0, + 0, 0,1871,1872,1873,1874, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 32, 33, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0,1875, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0,1877, 0,1878, 0,1879, 0,1880, 0,1881, 0,1882, 0, + 1883, 0,1884, 0,1885, 0,1886, 0,1887, 0,1888, 0, 0,1889, 0,1890, + 0,1891, 0, 0, 0, 0, 0, 0,1892,1893, 0,1894,1895, 0,1896,1897, + 0,1898,1899, 0,1900,1901, 0, 0, 0, 0, 0, 0,1876, 0, 0, 0, + 0, 0, 0, 0, 0, 0,1902, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0,1904, 0,1905, 0,1906, 0,1907, 0,1908, 0,1909, 0, + 1910, 0,1911, 0,1912, 0,1913, 0,1914, 0,1915, 0, 0,1916, 0,1917, + 0,1918, 0, 0, 0, 0, 0, 0,1919,1920, 0,1921,1922, 0,1923,1924, + 0,1925,1926, 0,1927,1928, 0, 0, 0, 0, 0, 0,1903, 0, 0,1929, + 1930,1931,1932, 0, 0, 0,1933, 0, 710, 385, 724, 715, 455, 103, 186, 825, + 825, 242, 751, 205, 241, 336, 524, 601, 663, 676, 688, 738, 411, 434, 474, 500, + 649, 746, 799, 108, 180, 416, 482, 662, 810, 275, 462, 658, 692, 344, 618, 679, + 293, 388, 440, 492, 740, 116, 146, 168, 368, 414, 481, 527, 606, 660, 665, 722, + 781, 803, 809, 538, 553, 588, 642, 758, 811, 701, 233, 299, 573, 612, 487, 540, + 714, 779, 232, 267, 412, 445, 457, 585, 594, 766, 167, 613, 149, 148, 560, 589, + 648, 768, 708, 345, 411, 704, 105, 259, 313, 496, 518, 174, 542, 120, 307, 101, + 430, 372, 584, 183, 228, 529, 650, 697, 424, 732, 428, 349, 632, 355, 517, 110, + 135, 147, 403, 580, 624, 700, 750, 170, 193, 245, 297, 374, 463, 543, 763, 801, + 812, 815, 162, 384, 420, 730, 287, 330, 337, 366, 459, 476, 509, 558, 591, 610, + 726, 652, 734, 759, 154, 163, 198, 473, 683, 697, 292, 311, 353, 423, 572, 494, + 113, 217, 259, 280, 314, 499, 506, 603, 608, 752, 778, 782, 788, 117, 557, 748, + 774, 320, 109, 126, 260, 265, 373, 411, 479, 523, 655, 737, 823, 380, 765, 161, + 395, 398, 438, 451, 502, 516, 537, 583, 791, 136, 340, 769, 122, 273, 446, 727, + 305, 322, 400, 496, 771, 155, 190, 269, 377, 391, 406, 432, 501, 519, 599, 684, + 687, 749, 776, 175, 452, 191, 480, 510, 659, 772, 805, 813, 397, 444, 619, 566, + 568, 575, 491, 471, 707, 111, 636, 156, 153, 288, 346, 578, 256, 435, 383, 729, + 680, 767, 694, 295, 128, 210, 0, 0, 227, 0, 379, 0, 0, 150, 493, 525, + 544, 551, 552, 556, 783, 576, 604, 0, 661, 0, 703, 0, 0, 735, 743, 0, + 0, 0, 793, 794, 795, 808, 741, 773, 118, 127, 130, 166, 169, 177, 207, 213, + 215, 226, 229, 268, 270, 317, 327, 329, 335, 369, 375, 381, 404, 441, 448, 458, + 477, 484, 503, 539, 545, 547, 546, 548, 549, 550, 554, 555, 561, 564, 569, 591, + 593, 595, 598, 607, 620, 625, 625, 651, 690, 695, 705, 706, 716, 717, 733, 735, + 777, 786, 790, 315, 869, 623, 0, 0, 102, 145, 134, 115, 129, 138, 165, 171, + 207, 202, 206, 212, 227, 231, 240, 243, 250, 254, 294, 296, 303, 308, 319, 325, + 321, 329, 326, 335, 341, 357, 360, 362, 370, 379, 388, 389, 393, 421, 424, 438, + 456, 454, 458, 465, 477, 535, 485, 490, 493, 507, 512, 514, 521, 522, 525, 526, + 528, 533, 532, 541, 565, 569, 574, 586, 591, 597, 607, 637, 647, 674, 691, 693, + 695, 698, 703, 699, 705, 704, 702, 706, 709, 717, 728, 736, 747, 754, 770, 777, + 783, 784, 786, 787, 790, 802, 825, 848, 847, 857, 55, 65, 66, 883, 892, 916, + 822, 824, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0,1586, 0,1605, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0,1602,1603,1934,1935,1574,1575,1576,1577,1579,1580,1581,1583,1584, 0, + 1585,1587,1588,1589,1591, 0,1592, 0,1593,1594, 0,1595,1596, 0,1598,1599, + 1600,1601,1604,1582,1578,1590,1597, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0,1936, 0,1937, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0,1938, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0,1939,1940, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0,1941,1942, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0,1944,1943, 0,1945, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0,1946,1947, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1948, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0,1949,1950,1951,1952,1953,1954,1955, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0,1956,1957,1958,1960,1959,1961, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 106, 104, 107, 826, 114, 118, 119, 121, + 123, 124, 127, 125, 34, 830, 130, 131, 132, 137, 827, 35, 133, 139, 829, 142, + 143, 112, 144, 145, 924, 151, 152, 37, 157, 158, 159, 160, 38, 165, 166, 169, + 171, 172, 173, 174, 176, 177, 178, 179, 181, 182, 182, 182, 833, 468, 184, 185, + 834, 187, 188, 189, 196, 192, 194, 195, 197, 199, 200, 201, 203, 204, 204, 206, + 208, 209, 211, 218, 213, 219, 214, 216, 153, 234, 221, 222, 223, 220, 225, 224, + 230, 835, 235, 236, 237, 238, 239, 244, 836, 837, 247, 248, 249, 246, 251, 39, + 40, 253, 255, 255, 838, 257, 258, 259, 261, 839, 262, 263, 301, 264, 41, 266, + 270, 272, 271, 841, 274, 842, 277, 276, 278, 281, 282, 42, 283, 284, 285, 286, + 43, 843, 44, 289, 290, 291, 293, 934, 298, 845, 845, 621, 300, 300, 45, 852, + 894, 302, 304, 46, 306, 309, 310, 312, 316, 48, 47, 317, 846, 318, 323, 324, + 325, 324, 328, 329, 333, 331, 332, 334, 335, 336, 338, 339, 342, 343, 347, 351, + 849, 350, 348, 352, 354, 359, 850, 361, 358, 356, 49, 363, 365, 367, 364, 50, + 369, 371, 851, 376, 386, 378, 53, 381, 52, 51, 140, 141, 387, 382, 614, 78, + 388, 389, 390, 394, 392, 856, 54, 399, 396, 402, 404, 858, 405, 401, 407, 55, + 408, 409, 410, 413, 859, 415, 56, 417, 860, 418, 57, 419, 422, 424, 425, 861, + 840, 862, 426, 863, 429, 431, 427, 433, 437, 441, 438, 439, 442, 443, 864, 436, + 449, 450, 58, 454, 453, 865, 447, 460, 866, 867, 461, 466, 465, 464, 59, 467, + 470, 469, 472, 828, 475, 868, 478, 870, 483, 485, 486, 871, 488, 489, 872, 873, + 495, 497, 60, 498, 61, 61, 504, 505, 507, 508, 511, 62, 513, 874, 515, 875, + 518, 844, 520, 876, 877, 878, 63, 64, 528, 880, 879, 881, 882, 530, 531, 531, + 533, 66, 534, 67, 68, 884, 536, 538, 541, 69, 885, 549, 886, 887, 556, 559, + 70, 561, 562, 563, 888, 889, 889, 567, 71, 890, 570, 571, 72, 891, 577, 73, + 581, 579, 582, 893, 587, 74, 590, 592, 596, 75, 895, 896, 76, 897, 600, 898, + 602, 605, 607, 899, 900, 609, 901, 611, 853, 77, 615, 616, 79, 617, 252, 902, + 903, 854, 855, 621, 622, 731, 80, 627, 626, 628, 164, 629, 630, 631, 633, 904, + 632, 634, 639, 640, 635, 641, 646, 651, 638, 643, 644, 645, 905, 907, 906, 81, + 653, 654, 656, 911, 657, 908, 82, 83, 909, 910, 84, 664, 665, 666, 667, 669, + 668, 671, 670, 674, 672, 673, 675, 85, 677, 678, 86, 681, 682, 912, 685, 686, + 87, 689, 36, 913, 914, 88, 89, 696, 702, 709, 711, 915, 712, 713, 718, 719, + 917, 831, 721, 720, 723, 832, 725, 728, 918, 919, 739, 742, 744, 920, 745, 753, + 756, 757, 755, 760, 761, 921, 762, 90, 764, 922, 91, 775, 279, 780, 923, 925, + 92, 93, 785, 926, 94, 927, 787, 787, 789, 928, 792, 95, 796, 797, 798, 800, + 96, 929, 802, 804, 806, 97, 98, 807, 930, 99, 931, 932, 933, 814, 100, 816, + 817, 818, 819, 820, 821, 935, 0, 0, +}; +static const int16_t +_hb_ucd_i16[92] = +{ + 0, 0, 1, -1, 2, 0, -2, 0, 0, 2, 0, -2, 0, 16, 0, -16, + 0, 1, -1, 0, 3, 3, 3, -3, -3, -3, 0, 2016, 0, 2527, 1923, 1914, + 1918, 0, 2250, 0, 0, 138, 0, 7, -7, 0, -1, 1, 1824, 0, 2104, 0, + 2108, 2106, 0, 2106, 1316, 0, -1, -138, 8, 8, 8, 0, 7, 7, -8, -8, + -8, -7,-1316, 1, -1, 3, -3, 1, 0,-1914,-1918, 0, 0,-1923,-1824, 0, + 0,-2016,-2104, 0, 0,-2106,-2108,-2106,-2250, 0,-2527, 0, +}; + +static inline uint_fast8_t +_hb_ucd_gc (unsigned u) +{ + return u<1114112u?_hb_ucd_u8[5080+(((_hb_ucd_u8[1152+(((_hb_ucd_u16[((_hb_ucd_u8[544+(((_hb_ucd_u8[u>>1>>3>>3>>4])<<4)+((u>>1>>3>>3)&15u))])<<3)+((u>>1>>3)&7u)])<<3)+((u>>1)&7u))])<<1)+((u)&1u))]:2; +} +static inline uint_fast8_t +_hb_ucd_ccc (unsigned u) +{ + return u<125259u?_hb_ucd_u8[7038+(((_hb_ucd_u8[6482+(((_hb_ucd_u8[6022+(((_hb_ucd_u8[5670+(((_hb_ucd_u8[5424+(u>>2>>2>>2>>3)])<<3)+((u>>2>>2>>2)&7u))])<<2)+((u>>2>>2)&3u))])<<2)+((u>>2)&3u))])<<2)+((u)&3u))]:0; +} +static inline unsigned +_hb_ucd_b4 (const uint8_t* a, unsigned i) +{ + return (a[i>>1]>>((i&1u)<<2))&15u; +} +static inline int_fast16_t +_hb_ucd_bmg (unsigned u) +{ + return u<65380u?_hb_ucd_i16[((_hb_ucd_u8[7930+(((_hb_ucd_u8[7698+(((_hb_ucd_u8[7602+(((_hb_ucd_b4(7538+_hb_ucd_u8,u>>1>>2>>3>>3))<<3)+((u>>1>>2>>3)&7u))])<<3)+((u>>1>>2)&7u))])<<2)+((u>>1)&3u))])<<1)+((u)&1u)]:0; +} +static inline uint_fast8_t +_hb_ucd_sc (unsigned u) +{ + return u<918016u?_hb_ucd_u8[11228+(((_hb_ucd_u8[10264+(((_hb_ucd_u8[9276+(((_hb_ucd_u8[8596+(((_hb_ucd_u8[8292+(((_hb_ucd_u8[8178+(u>>2>>2>>2>>3>>4)])<<4)+((u>>2>>2>>2>>3)&15u))])<<3)+((u>>2>>2>>2)&7u))])<<2)+((u>>2>>2)&3u))])<<2)+((u>>2)&3u))])<<2)+((u)&3u))]:2; +} +static inline uint_fast16_t +_hb_ucd_dm (unsigned u) +{ + return u<195102u?_hb_ucd_u16[1608+(((_hb_ucd_u8[12570+(((_hb_ucd_u8[12188+(u>>4>>5)])<<5)+((u>>4)&31u))])<<4)+((u)&15u))]:0; +} + +#endif + + +#endif /* HB_UCD_TABLE_HH */ + +/* == End of generated table == */ diff --git a/libs/harfbuzz/src/hb-ucd.cc b/libs/harfbuzz/src/hb-ucd.cc new file mode 100644 index 00000000..baea224a --- /dev/null +++ b/libs/harfbuzz/src/hb-ucd.cc @@ -0,0 +1,242 @@ +/* + * Copyright (C) 2012 Grigori Goronzy + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "hb.hh" +#include "hb-unicode.hh" +#include "hb-machinery.hh" + +#include "hb-ucd-table.hh" + +static hb_unicode_combining_class_t +hb_ucd_combining_class (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t unicode, + void *user_data HB_UNUSED) +{ + return (hb_unicode_combining_class_t) _hb_ucd_ccc (unicode); +} + +static hb_unicode_general_category_t +hb_ucd_general_category (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t unicode, + void *user_data HB_UNUSED) +{ + return (hb_unicode_general_category_t) _hb_ucd_gc (unicode); +} + +static hb_codepoint_t +hb_ucd_mirroring (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t unicode, + void *user_data HB_UNUSED) +{ + return unicode + _hb_ucd_bmg (unicode); +} + +static hb_script_t +hb_ucd_script (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t unicode, + void *user_data HB_UNUSED) +{ + return _hb_ucd_sc_map[_hb_ucd_sc (unicode)]; +} + + +#define SBASE 0xAC00u +#define LBASE 0x1100u +#define VBASE 0x1161u +#define TBASE 0x11A7u +#define SCOUNT 11172u +#define LCOUNT 19u +#define VCOUNT 21u +#define TCOUNT 28u +#define NCOUNT (VCOUNT * TCOUNT) + +static inline bool +_hb_ucd_decompose_hangul (hb_codepoint_t ab, hb_codepoint_t *a, hb_codepoint_t *b) +{ + unsigned si = ab - SBASE; + + if (si >= SCOUNT) + return false; + + if (si % TCOUNT) + { + /* LV,T */ + *a = SBASE + (si / TCOUNT) * TCOUNT; + *b = TBASE + (si % TCOUNT); + return true; + } else { + /* L,V */ + *a = LBASE + (si / NCOUNT); + *b = VBASE + (si % NCOUNT) / TCOUNT; + return true; + } +} + +static inline bool +_hb_ucd_compose_hangul (hb_codepoint_t a, hb_codepoint_t b, hb_codepoint_t *ab) +{ + if (a >= SBASE && a < (SBASE + SCOUNT) && b > TBASE && b < (TBASE + TCOUNT) && + !((a - SBASE) % TCOUNT)) + { + /* LV,T */ + *ab = a + (b - TBASE); + return true; + } + else if (a >= LBASE && a < (LBASE + LCOUNT) && b >= VBASE && b < (VBASE + VCOUNT)) + { + /* L,V */ + int li = a - LBASE; + int vi = b - VBASE; + *ab = SBASE + li * NCOUNT + vi * TCOUNT; + return true; + } + else + return false; +} + +static int +_cmp_pair (const void *_key, const void *_item) +{ + uint64_t& a = * (uint64_t*) _key; + uint64_t b = (* (uint64_t*) _item) & HB_CODEPOINT_ENCODE3(0x1FFFFFu, 0x1FFFFFu, 0); + + return a < b ? -1 : a > b ? +1 : 0; +} +static int +_cmp_pair_11_7_14 (const void *_key, const void *_item) +{ + uint32_t& a = * (uint32_t*) _key; + uint32_t b = (* (uint32_t*) _item) & HB_CODEPOINT_ENCODE3_11_7_14(0x1FFFFFu, 0x1FFFFFu, 0); + + return a < b ? -1 : a > b ? +1 : 0; +} + +static hb_bool_t +hb_ucd_compose (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t a, hb_codepoint_t b, hb_codepoint_t *ab, + void *user_data HB_UNUSED) +{ + if (_hb_ucd_compose_hangul (a, b, ab)) return true; + + hb_codepoint_t u = 0; + + if ((a & 0xFFFFF800u) == 0x0000u && (b & 0xFFFFFF80) == 0x0300u) + { + uint32_t k = HB_CODEPOINT_ENCODE3_11_7_14 (a, b, 0); + const uint32_t *v = hb_bsearch (k, + _hb_ucd_dm2_u32_map, + ARRAY_LENGTH (_hb_ucd_dm2_u32_map), + sizeof (*_hb_ucd_dm2_u32_map), + _cmp_pair_11_7_14); + if (likely (!v)) return false; + u = HB_CODEPOINT_DECODE3_11_7_14_3 (*v); + } + else + { + uint64_t k = HB_CODEPOINT_ENCODE3 (a, b, 0); + const uint64_t *v = hb_bsearch (k, + _hb_ucd_dm2_u64_map, + ARRAY_LENGTH (_hb_ucd_dm2_u64_map), + sizeof (*_hb_ucd_dm2_u64_map), + _cmp_pair); + if (likely (!v)) return false; + u = HB_CODEPOINT_DECODE3_3 (*v); + } + + if (unlikely (!u)) return false; + *ab = u; + return true; +} + +static hb_bool_t +hb_ucd_decompose (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t ab, hb_codepoint_t *a, hb_codepoint_t *b, + void *user_data HB_UNUSED) +{ + if (_hb_ucd_decompose_hangul (ab, a, b)) return true; + + unsigned i = _hb_ucd_dm (ab); + + if (likely (!i)) return false; + i--; + + if (i < ARRAY_LENGTH (_hb_ucd_dm1_p0_map) + ARRAY_LENGTH (_hb_ucd_dm1_p2_map)) + { + if (i < ARRAY_LENGTH (_hb_ucd_dm1_p0_map)) + *a = _hb_ucd_dm1_p0_map[i]; + else + { + i -= ARRAY_LENGTH (_hb_ucd_dm1_p0_map); + *a = 0x20000 | _hb_ucd_dm1_p2_map[i]; + } + *b = 0; + return true; + } + i -= ARRAY_LENGTH (_hb_ucd_dm1_p0_map) + ARRAY_LENGTH (_hb_ucd_dm1_p2_map); + + if (i < ARRAY_LENGTH (_hb_ucd_dm2_u32_map)) + { + uint32_t v = _hb_ucd_dm2_u32_map[i]; + *a = HB_CODEPOINT_DECODE3_11_7_14_1 (v); + *b = HB_CODEPOINT_DECODE3_11_7_14_2 (v); + return true; + } + i -= ARRAY_LENGTH (_hb_ucd_dm2_u32_map); + + uint64_t v = _hb_ucd_dm2_u64_map[i]; + *a = HB_CODEPOINT_DECODE3_1 (v); + *b = HB_CODEPOINT_DECODE3_2 (v); + return true; +} + + +static void free_static_ucd_funcs (); + +static struct hb_ucd_unicode_funcs_lazy_loader_t : hb_unicode_funcs_lazy_loader_t +{ + static hb_unicode_funcs_t *create () + { + hb_unicode_funcs_t *funcs = hb_unicode_funcs_create (nullptr); + + hb_unicode_funcs_set_combining_class_func (funcs, hb_ucd_combining_class, nullptr, nullptr); + hb_unicode_funcs_set_general_category_func (funcs, hb_ucd_general_category, nullptr, nullptr); + hb_unicode_funcs_set_mirroring_func (funcs, hb_ucd_mirroring, nullptr, nullptr); + hb_unicode_funcs_set_script_func (funcs, hb_ucd_script, nullptr, nullptr); + hb_unicode_funcs_set_compose_func (funcs, hb_ucd_compose, nullptr, nullptr); + hb_unicode_funcs_set_decompose_func (funcs, hb_ucd_decompose, nullptr, nullptr); + + hb_unicode_funcs_make_immutable (funcs); + + hb_atexit (free_static_ucd_funcs); + + return funcs; + } +} static_ucd_funcs; + +static inline +void free_static_ucd_funcs () +{ + static_ucd_funcs.free_instance (); +} + +hb_unicode_funcs_t * +hb_ucd_get_unicode_funcs () +{ +#ifdef HB_NO_UCD + return hb_unicode_funcs_get_empty (); +#endif + return static_ucd_funcs.get_unconst (); +} diff --git a/libs/harfbuzz/src/hb-unicode-emoji-table.hh b/libs/harfbuzz/src/hb-unicode-emoji-table.hh new file mode 100644 index 00000000..13b1c4b1 --- /dev/null +++ b/libs/harfbuzz/src/hb-unicode-emoji-table.hh @@ -0,0 +1,79 @@ +/* == Start of generated table == */ +/* + * The following tables are generated by running: + * + * ./gen-emoji-table.py emoji-data.txt + * + * on file with this header: + * + * # emoji-data.txt + * # Date: 2022-08-02, 00:26:10 GMT + * # © 2022 Unicode®, Inc. + * # Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in the U.S. and other countries. + * # For terms of use, see https://www.unicode.org/terms_of_use.html + * # + * # Emoji Data for UTS #51 + * # Used with Emoji Version 15.0 and subsequent minor revisions (if any) + * # + * # For documentation and usage, see https://www.unicode.org/reports/tr51 + */ + +#ifndef HB_UNICODE_EMOJI_TABLE_HH +#define HB_UNICODE_EMOJI_TABLE_HH + +#include "hb-unicode.hh" + +static const uint8_t +_hb_emoji_u8[464] = +{ + 16, 17, 17, 17, 50, 20, 21, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,118,152, + 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 2, 0, 3, 4, 0, 0, 5, 6, 0, 7, 0, 8, 9, 10, 11, 12, + 0, 0, 13, 0, 0, 0, 14, 0, 15, 0, 0, 0, 0, 16, 0, 0, + 17, 17, 18, 19, 20, 17, 17, 21, 17, 17, 22, 17, 23, 17, 24, 25, + 26, 27, 28, 17, 17, 17, 0, 0, 17, 17, 17, 17, 17, 17, 17, 29, + 0, 0, 0, 0, 0, 1, 0, 0, 0, 2, 3, 0, 0, 4, 0, 0, + 5, 6, 0, 0, 7, 8, 0, 0, 8, 0, 9, 10, 0, 0, 11, 0, + 0, 12, 13, 14, 15, 16, 16, 16, 17, 16, 16, 16, 18, 19, 20, 21, + 22, 23, 0, 0, 0, 24, 0, 0, 25, 0, 26, 0, 0, 27, 0, 0, + 28, 0, 0, 0, 16, 16, 16, 16, 29, 9, 0, 30, 31, 32, 16, 33, + 34, 35, 36, 16, 16, 16, 16, 37, 16, 38, 39, 16, 16, 16, 40, 0, + 0, 0, 0, 41, 0, 0, 42, 16, 43, 0, 44, 0, 45, 46, 16, 16, + 47, 48, 49, 16, 16, 16, 16, 38, 0, 0, 0, 0, 0, 66, 0, 0, + 0, 0, 0, 16, 0, 2, 0, 0, 4, 0, 0, 2, 0, 0,240, 3, + 0, 6, 0, 0, 0, 0, 0, 12, 0, 1, 0, 0, 0,128, 0, 0, + 0,254, 15, 7, 4, 0, 0, 0, 0, 12, 64, 0, 1, 0, 0, 0, + 0, 0, 0,120,191,255,247,255,255,255,255,255, 63, 0,255,255, + 63,255, 87, 32, 2, 1, 24, 0,144, 80,184, 0,248, 0, 0, 0, + 0, 0,224, 0, 2, 0, 1,128, 0, 0, 48, 0,224, 0, 0, 24, + 0, 0, 33, 0, 0, 0, 1, 32, 0, 0,128, 2, 0,224, 0, 0, + 0,240, 3,192, 0, 64,254, 7, 0,224,255,255, 63, 0, 0, 0, + 254,255, 0, 4, 0,128,252,247, 0,254,255,255,255,255,255, 7, + 255,255,255, 63,192,255,255,255,255,255, 0, 0, 0, 0,240,255, + 0, 0,224,255, 0,240, 0, 0, 0,255, 0,252, 0,255, 0, 0, + 0,192,255,255, 0,240,255,255,255,255,255,247,191,255,255,255, +}; + +static inline unsigned +_hb_emoji_b4 (const uint8_t* a, unsigned i) +{ + return (a[i>>1]>>((i&1u)<<2))&15u; +} +static inline unsigned +_hb_emoji_b1 (const uint8_t* a, unsigned i) +{ + return (a[i>>3]>>((i&7u)<<0))&1u; +} +static inline uint_fast8_t +_hb_emoji_is_Extended_Pictographic (unsigned u) +{ + return u<131070u?_hb_emoji_b1(264+_hb_emoji_u8,((_hb_emoji_u8[144+(((_hb_emoji_u8[64+(((_hb_emoji_b4(_hb_emoji_u8,u>>5>>2>>3))<<3)+((u>>5>>2)&7u))])<<2)+((u>>5)&3u))])<<5)+((u)&31u)):0; +} + + +#endif /* HB_UNICODE_EMOJI_TABLE_HH */ + +/* == End of generated table == */ diff --git a/libs/harfbuzz/src/hb-unicode.cc b/libs/harfbuzz/src/hb-unicode.cc new file mode 100644 index 00000000..c1795dc7 --- /dev/null +++ b/libs/harfbuzz/src/hb-unicode.cc @@ -0,0 +1,615 @@ +/* + * Copyright © 2009 Red Hat, Inc. + * Copyright © 2011 Codethink Limited + * Copyright © 2010,2011,2012 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. + * + * Red Hat Author(s): Behdad Esfahbod + * Codethink Author(s): Ryan Lortie + * Google Author(s): Behdad Esfahbod + */ + +#include "hb.hh" + +#include "hb-unicode.hh" + + +/** + * SECTION: hb-unicode + * @title: hb-unicode + * @short_description: Unicode character property access + * @include: hb.h + * + * Unicode functions are used to access Unicode character properties. + * With these functions, client programs can query various properties from + * the Unicode Character Database for any code point, such as General + * Category (gc), Script (sc), Canonical Combining Class (ccc), etc. + * + * Client programs can optionally pass in their own Unicode functions + * that implement the same queries. The set of functions available is + * defined by the virtual methods in #hb_unicode_funcs_t. + * + * HarfBuzz provides built-in default functions for each method in + * #hb_unicode_funcs_t. + **/ + + +/* + * hb_unicode_funcs_t + */ + +static hb_unicode_combining_class_t +hb_unicode_combining_class_nil (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t unicode HB_UNUSED, + void *user_data HB_UNUSED) +{ + return HB_UNICODE_COMBINING_CLASS_NOT_REORDERED; +} + +#ifndef HB_DISABLE_DEPRECATED +static unsigned int +hb_unicode_eastasian_width_nil (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t unicode HB_UNUSED, + void *user_data HB_UNUSED) +{ + return 1; +} +#endif + +static hb_unicode_general_category_t +hb_unicode_general_category_nil (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t unicode HB_UNUSED, + void *user_data HB_UNUSED) +{ + return HB_UNICODE_GENERAL_CATEGORY_OTHER_LETTER; +} + +static hb_codepoint_t +hb_unicode_mirroring_nil (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t unicode, + void *user_data HB_UNUSED) +{ + return unicode; +} + +static hb_script_t +hb_unicode_script_nil (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t unicode HB_UNUSED, + void *user_data HB_UNUSED) +{ + return HB_SCRIPT_UNKNOWN; +} + +static hb_bool_t +hb_unicode_compose_nil (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t a HB_UNUSED, + hb_codepoint_t b HB_UNUSED, + hb_codepoint_t *ab HB_UNUSED, + void *user_data HB_UNUSED) +{ + return false; +} + +static hb_bool_t +hb_unicode_decompose_nil (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t ab HB_UNUSED, + hb_codepoint_t *a HB_UNUSED, + hb_codepoint_t *b HB_UNUSED, + void *user_data HB_UNUSED) +{ + return false; +} + + +#ifndef HB_DISABLE_DEPRECATED +static unsigned int +hb_unicode_decompose_compatibility_nil (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t u HB_UNUSED, + hb_codepoint_t *decomposed HB_UNUSED, + void *user_data HB_UNUSED) +{ + return 0; +} +#endif + +#if !defined(HB_NO_UNICODE_FUNCS) && defined(HAVE_GLIB) +#include "hb-glib.h" +#endif +#if !defined(HB_NO_UNICODE_FUNCS) && defined(HAVE_ICU) && defined(HAVE_ICU_BUILTIN) +#include "hb-icu.h" +#endif + +/** + * hb_unicode_funcs_get_default: + * + * Fetches a pointer to the default Unicode-functions structure that is used + * when no functions are explicitly set on #hb_buffer_t. + * + * Return value: (transfer none): a pointer to the #hb_unicode_funcs_t Unicode-functions structure + * + * Since: 0.9.2 + **/ +hb_unicode_funcs_t * +hb_unicode_funcs_get_default () +{ +#if !defined(HB_NO_UNICODE_FUNCS) && !defined(HB_NO_UCD) + return hb_ucd_get_unicode_funcs (); +#elif !defined(HB_NO_UNICODE_FUNCS) && defined(HAVE_GLIB) + return hb_glib_get_unicode_funcs (); +#elif !defined(HB_NO_UNICODE_FUNCS) && defined(HAVE_ICU) && defined(HAVE_ICU_BUILTIN) + return hb_icu_get_unicode_funcs (); +#else +#define HB_UNICODE_FUNCS_NIL 1 + return hb_unicode_funcs_get_empty (); +#endif +} + +#if !defined(HB_NO_UNICODE_FUNCS) && defined(HB_UNICODE_FUNCS_NIL) +#error "Could not find any Unicode functions implementation, you have to provide your own" +#error "Consider building hb-ucd.cc. If you absolutely want to build without any, check the code." +#endif + +/** + * hb_unicode_funcs_create: + * @parent: (nullable): Parent Unicode-functions structure + * + * Creates a new #hb_unicode_funcs_t structure of Unicode functions. + * + * Return value: (transfer full): The Unicode-functions structure + * + * Since: 0.9.2 + **/ +hb_unicode_funcs_t * +hb_unicode_funcs_create (hb_unicode_funcs_t *parent) +{ + hb_unicode_funcs_t *ufuncs; + + if (!(ufuncs = hb_object_create ())) + return hb_unicode_funcs_get_empty (); + + if (!parent) + parent = hb_unicode_funcs_get_empty (); + + hb_unicode_funcs_make_immutable (parent); + ufuncs->parent = hb_unicode_funcs_reference (parent); + + ufuncs->func = parent->func; + + /* We can safely copy user_data from parent since we hold a reference + * onto it and it's immutable. We should not copy the destroy notifiers + * though. */ + ufuncs->user_data = parent->user_data; + + return ufuncs; +} + + +DEFINE_NULL_INSTANCE (hb_unicode_funcs_t) = +{ + HB_OBJECT_HEADER_STATIC, + + nullptr, /* parent */ + { +#define HB_UNICODE_FUNC_IMPLEMENT(name) hb_unicode_##name##_nil, + HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_UNICODE_FUNC_IMPLEMENT + } +}; + +/** + * hb_unicode_funcs_get_empty: + * + * Fetches the singleton empty Unicode-functions structure. + * + * Return value: (transfer full): The empty Unicode-functions structure + * + * Since: 0.9.2 + **/ +hb_unicode_funcs_t * +hb_unicode_funcs_get_empty () +{ + return const_cast (&Null (hb_unicode_funcs_t)); +} + +/** + * hb_unicode_funcs_reference: (skip) + * @ufuncs: The Unicode-functions structure + * + * Increases the reference count on a Unicode-functions structure. + * + * Return value: (transfer full): The Unicode-functions structure + * + * Since: 0.9.2 + **/ +hb_unicode_funcs_t * +hb_unicode_funcs_reference (hb_unicode_funcs_t *ufuncs) +{ + return hb_object_reference (ufuncs); +} + +/** + * hb_unicode_funcs_destroy: (skip) + * @ufuncs: The Unicode-functions structure + * + * Decreases the reference count on a Unicode-functions structure. When + * the reference count reaches zero, the Unicode-functions structure is + * destroyed, freeing all memory. + * + * Since: 0.9.2 + **/ +void +hb_unicode_funcs_destroy (hb_unicode_funcs_t *ufuncs) +{ + if (!hb_object_destroy (ufuncs)) return; + +#define HB_UNICODE_FUNC_IMPLEMENT(name) \ + if (ufuncs->destroy.name) ufuncs->destroy.name (ufuncs->user_data.name); + HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_UNICODE_FUNC_IMPLEMENT + + hb_unicode_funcs_destroy (ufuncs->parent); + + hb_free (ufuncs); +} + +/** + * hb_unicode_funcs_set_user_data: (skip) + * @ufuncs: The Unicode-functions structure + * @key: The user-data key + * @data: A pointer to the user data + * @destroy: (nullable): A callback to call when @data is not needed anymore + * @replace: Whether to replace an existing data with the same key + * + * Attaches a user-data key/data pair to the specified Unicode-functions structure. + * + * Return value: `true` if success, `false` otherwise + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_unicode_funcs_set_user_data (hb_unicode_funcs_t *ufuncs, + hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy, + hb_bool_t replace) +{ + return hb_object_set_user_data (ufuncs, key, data, destroy, replace); +} + +/** + * hb_unicode_funcs_get_user_data: (skip) + * @ufuncs: The Unicode-functions structure + * @key: The user-data key to query + * + * Fetches the user-data associated with the specified key, + * attached to the specified Unicode-functions structure. + * + * Return value: (transfer none): A pointer to the user data + * + * Since: 0.9.2 + **/ +void * +hb_unicode_funcs_get_user_data (const hb_unicode_funcs_t *ufuncs, + hb_user_data_key_t *key) +{ + return hb_object_get_user_data (ufuncs, key); +} + + +/** + * hb_unicode_funcs_make_immutable: + * @ufuncs: The Unicode-functions structure + * + * Makes the specified Unicode-functions structure + * immutable. + * + * Since: 0.9.2 + **/ +void +hb_unicode_funcs_make_immutable (hb_unicode_funcs_t *ufuncs) +{ + if (hb_object_is_immutable (ufuncs)) + return; + + hb_object_make_immutable (ufuncs); +} + +/** + * hb_unicode_funcs_is_immutable: + * @ufuncs: The Unicode-functions structure + * + * Tests whether the specified Unicode-functions structure + * is immutable. + * + * Return value: `true` if @ufuncs is immutable, `false` otherwise + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_unicode_funcs_is_immutable (hb_unicode_funcs_t *ufuncs) +{ + return hb_object_is_immutable (ufuncs); +} + +/** + * hb_unicode_funcs_get_parent: + * @ufuncs: The Unicode-functions structure + * + * Fetches the parent of the Unicode-functions structure + * @ufuncs. + * + * Return value: The parent Unicode-functions structure + * + * Since: 0.9.2 + **/ +hb_unicode_funcs_t * +hb_unicode_funcs_get_parent (hb_unicode_funcs_t *ufuncs) +{ + return ufuncs->parent ? ufuncs->parent : hb_unicode_funcs_get_empty (); +} + + +#define HB_UNICODE_FUNC_IMPLEMENT(name) \ + \ +void \ +hb_unicode_funcs_set_##name##_func (hb_unicode_funcs_t *ufuncs, \ + hb_unicode_##name##_func_t func, \ + void *user_data, \ + hb_destroy_func_t destroy) \ +{ \ + if (hb_object_is_immutable (ufuncs)) \ + return; \ + \ + if (ufuncs->destroy.name) \ + ufuncs->destroy.name (ufuncs->user_data.name); \ + \ + if (func) { \ + ufuncs->func.name = func; \ + ufuncs->user_data.name = user_data; \ + ufuncs->destroy.name = destroy; \ + } else { \ + ufuncs->func.name = ufuncs->parent->func.name; \ + ufuncs->user_data.name = ufuncs->parent->user_data.name; \ + ufuncs->destroy.name = nullptr; \ + } \ +} + +HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_UNICODE_FUNC_IMPLEMENT + + +#define HB_UNICODE_FUNC_IMPLEMENT(return_type, name) \ + \ +return_type \ +hb_unicode_##name (hb_unicode_funcs_t *ufuncs, \ + hb_codepoint_t unicode) \ +{ \ + return ufuncs->name (unicode); \ +} +HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS_SIMPLE +#undef HB_UNICODE_FUNC_IMPLEMENT + +/** + * hb_unicode_compose: + * @ufuncs: The Unicode-functions structure + * @a: The first Unicode code point to compose + * @b: The second Unicode code point to compose + * @ab: (out): The composition of @a, @b + * + * Fetches the composition of a sequence of two Unicode + * code points. + * + * Calls the composition function of the specified + * Unicode-functions structure @ufuncs. + * + * Return value: `true` if @a and @b composed, `false` otherwise + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_unicode_compose (hb_unicode_funcs_t *ufuncs, + hb_codepoint_t a, + hb_codepoint_t b, + hb_codepoint_t *ab) +{ + return ufuncs->compose (a, b, ab); +} + +/** + * hb_unicode_decompose: + * @ufuncs: The Unicode-functions structure + * @ab: Unicode code point to decompose + * @a: (out): The first code point of the decomposition of @ab + * @b: (out): The second code point of the decomposition of @ab + * + * Fetches the decomposition of a Unicode code point. + * + * Calls the decomposition function of the specified + * Unicode-functions structure @ufuncs. + * + * Return value: `true` if @ab was decomposed, `false` otherwise + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_unicode_decompose (hb_unicode_funcs_t *ufuncs, + hb_codepoint_t ab, + hb_codepoint_t *a, + hb_codepoint_t *b) +{ + return ufuncs->decompose (ab, a, b); +} + +#ifndef HB_DISABLE_DEPRECATED +/** + * hb_unicode_decompose_compatibility: + * @ufuncs: The Unicode-functions structure + * @u: Code point to decompose + * @decomposed: (out): Compatibility decomposition of @u + * + * Fetches the compatibility decomposition of a Unicode + * code point. Deprecated. + * + * Return value: length of @decomposed. + * + * Since: 0.9.2 + * Deprecated: 2.0.0 + **/ +unsigned int +hb_unicode_decompose_compatibility (hb_unicode_funcs_t *ufuncs, + hb_codepoint_t u, + hb_codepoint_t *decomposed) +{ + return ufuncs->decompose_compatibility (u, decomposed); +} +#endif + + +#ifndef HB_NO_OT_SHAPE +/* See hb-unicode.hh for details. */ +const uint8_t +_hb_modified_combining_class[256] = +{ + 0, /* HB_UNICODE_COMBINING_CLASS_NOT_REORDERED */ + 1, /* HB_UNICODE_COMBINING_CLASS_OVERLAY */ + 2, 3, 4, 5, 6, + 7, /* HB_UNICODE_COMBINING_CLASS_NUKTA */ + 8, /* HB_UNICODE_COMBINING_CLASS_KANA_VOICING */ + 9, /* HB_UNICODE_COMBINING_CLASS_VIRAMA */ + + /* Hebrew */ + HB_MODIFIED_COMBINING_CLASS_CCC10, + HB_MODIFIED_COMBINING_CLASS_CCC11, + HB_MODIFIED_COMBINING_CLASS_CCC12, + HB_MODIFIED_COMBINING_CLASS_CCC13, + HB_MODIFIED_COMBINING_CLASS_CCC14, + HB_MODIFIED_COMBINING_CLASS_CCC15, + HB_MODIFIED_COMBINING_CLASS_CCC16, + HB_MODIFIED_COMBINING_CLASS_CCC17, + HB_MODIFIED_COMBINING_CLASS_CCC18, + HB_MODIFIED_COMBINING_CLASS_CCC19, + HB_MODIFIED_COMBINING_CLASS_CCC20, + HB_MODIFIED_COMBINING_CLASS_CCC21, + HB_MODIFIED_COMBINING_CLASS_CCC22, + HB_MODIFIED_COMBINING_CLASS_CCC23, + HB_MODIFIED_COMBINING_CLASS_CCC24, + HB_MODIFIED_COMBINING_CLASS_CCC25, + HB_MODIFIED_COMBINING_CLASS_CCC26, + + /* Arabic */ + HB_MODIFIED_COMBINING_CLASS_CCC27, + HB_MODIFIED_COMBINING_CLASS_CCC28, + HB_MODIFIED_COMBINING_CLASS_CCC29, + HB_MODIFIED_COMBINING_CLASS_CCC30, + HB_MODIFIED_COMBINING_CLASS_CCC31, + HB_MODIFIED_COMBINING_CLASS_CCC32, + HB_MODIFIED_COMBINING_CLASS_CCC33, + HB_MODIFIED_COMBINING_CLASS_CCC34, + HB_MODIFIED_COMBINING_CLASS_CCC35, + + /* Syriac */ + HB_MODIFIED_COMBINING_CLASS_CCC36, + + 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, + 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, + 80, 81, 82, 83, + + /* Telugu */ + HB_MODIFIED_COMBINING_CLASS_CCC84, + 85, 86, 87, 88, 89, 90, + HB_MODIFIED_COMBINING_CLASS_CCC91, + 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, + + /* Thai */ + HB_MODIFIED_COMBINING_CLASS_CCC103, + 104, 105, 106, + HB_MODIFIED_COMBINING_CLASS_CCC107, + 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, + + /* Lao */ + HB_MODIFIED_COMBINING_CLASS_CCC118, + 119, 120, 121, + HB_MODIFIED_COMBINING_CLASS_CCC122, + 123, 124, 125, 126, 127, 128, + + /* Tibetan */ + HB_MODIFIED_COMBINING_CLASS_CCC129, + HB_MODIFIED_COMBINING_CLASS_CCC130, + 131, + HB_MODIFIED_COMBINING_CLASS_CCC132, + 133, 134, 135, 136, 137, 138, 139, + + + 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, + 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, + 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, + 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, + 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, + 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, + + 200, /* HB_UNICODE_COMBINING_CLASS_ATTACHED_BELOW_LEFT */ + 201, + 202, /* HB_UNICODE_COMBINING_CLASS_ATTACHED_BELOW */ + 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, + 214, /* HB_UNICODE_COMBINING_CLASS_ATTACHED_ABOVE */ + 215, + 216, /* HB_UNICODE_COMBINING_CLASS_ATTACHED_ABOVE_RIGHT */ + 217, + 218, /* HB_UNICODE_COMBINING_CLASS_BELOW_LEFT */ + 219, + 220, /* HB_UNICODE_COMBINING_CLASS_BELOW */ + 221, + 222, /* HB_UNICODE_COMBINING_CLASS_BELOW_RIGHT */ + 223, + 224, /* HB_UNICODE_COMBINING_CLASS_LEFT */ + 225, + 226, /* HB_UNICODE_COMBINING_CLASS_RIGHT */ + 227, + 228, /* HB_UNICODE_COMBINING_CLASS_ABOVE_LEFT */ + 229, + 230, /* HB_UNICODE_COMBINING_CLASS_ABOVE */ + 231, + 232, /* HB_UNICODE_COMBINING_CLASS_ABOVE_RIGHT */ + 233, /* HB_UNICODE_COMBINING_CLASS_DOUBLE_BELOW */ + 234, /* HB_UNICODE_COMBINING_CLASS_DOUBLE_ABOVE */ + 235, 236, 237, 238, 239, + 240, /* HB_UNICODE_COMBINING_CLASS_IOTA_SUBSCRIPT */ + 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, + 255, /* HB_UNICODE_COMBINING_CLASS_INVALID */ +}; +#endif + + +/* + * Emoji + */ +#ifndef HB_NO_EMOJI_SEQUENCES + +#include "hb-unicode-emoji-table.hh" + +bool +_hb_unicode_is_emoji_Extended_Pictographic (hb_codepoint_t cp) +{ + return _hb_emoji_is_Extended_Pictographic (cp); +} +#endif diff --git a/libs/harfbuzz/src/hb-unicode.h b/libs/harfbuzz/src/hb-unicode.h new file mode 100644 index 00000000..faa8d679 --- /dev/null +++ b/libs/harfbuzz/src/hb-unicode.h @@ -0,0 +1,643 @@ +/* + * Copyright © 2009 Red Hat, Inc. + * Copyright © 2011 Codethink Limited + * Copyright © 2011,2012 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. + * + * Red Hat Author(s): Behdad Esfahbod + * Codethink Author(s): Ryan Lortie + * Google Author(s): Behdad Esfahbod + */ + +#if !defined(HB_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR) +#error "Include instead." +#endif + +#ifndef HB_UNICODE_H +#define HB_UNICODE_H + +#include "hb-common.h" + +HB_BEGIN_DECLS + + +/** + * HB_UNICODE_MAX: + * + * Maximum valid Unicode code point. + * + * Since: 1.9.0 + **/ +#define HB_UNICODE_MAX 0x10FFFFu + + +/** + * hb_unicode_general_category_t: + * @HB_UNICODE_GENERAL_CATEGORY_CONTROL: [Cc] + * @HB_UNICODE_GENERAL_CATEGORY_FORMAT: [Cf] + * @HB_UNICODE_GENERAL_CATEGORY_UNASSIGNED: [Cn] + * @HB_UNICODE_GENERAL_CATEGORY_PRIVATE_USE: [Co] + * @HB_UNICODE_GENERAL_CATEGORY_SURROGATE: [Cs] + * @HB_UNICODE_GENERAL_CATEGORY_LOWERCASE_LETTER: [Ll] + * @HB_UNICODE_GENERAL_CATEGORY_MODIFIER_LETTER: [Lm] + * @HB_UNICODE_GENERAL_CATEGORY_OTHER_LETTER: [Lo] + * @HB_UNICODE_GENERAL_CATEGORY_TITLECASE_LETTER: [Lt] + * @HB_UNICODE_GENERAL_CATEGORY_UPPERCASE_LETTER: [Lu] + * @HB_UNICODE_GENERAL_CATEGORY_SPACING_MARK: [Mc] + * @HB_UNICODE_GENERAL_CATEGORY_ENCLOSING_MARK: [Me] + * @HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK: [Mn] + * @HB_UNICODE_GENERAL_CATEGORY_DECIMAL_NUMBER: [Nd] + * @HB_UNICODE_GENERAL_CATEGORY_LETTER_NUMBER: [Nl] + * @HB_UNICODE_GENERAL_CATEGORY_OTHER_NUMBER: [No] + * @HB_UNICODE_GENERAL_CATEGORY_CONNECT_PUNCTUATION: [Pc] + * @HB_UNICODE_GENERAL_CATEGORY_DASH_PUNCTUATION: [Pd] + * @HB_UNICODE_GENERAL_CATEGORY_CLOSE_PUNCTUATION: [Pe] + * @HB_UNICODE_GENERAL_CATEGORY_FINAL_PUNCTUATION: [Pf] + * @HB_UNICODE_GENERAL_CATEGORY_INITIAL_PUNCTUATION: [Pi] + * @HB_UNICODE_GENERAL_CATEGORY_OTHER_PUNCTUATION: [Po] + * @HB_UNICODE_GENERAL_CATEGORY_OPEN_PUNCTUATION: [Ps] + * @HB_UNICODE_GENERAL_CATEGORY_CURRENCY_SYMBOL: [Sc] + * @HB_UNICODE_GENERAL_CATEGORY_MODIFIER_SYMBOL: [Sk] + * @HB_UNICODE_GENERAL_CATEGORY_MATH_SYMBOL: [Sm] + * @HB_UNICODE_GENERAL_CATEGORY_OTHER_SYMBOL: [So] + * @HB_UNICODE_GENERAL_CATEGORY_LINE_SEPARATOR: [Zl] + * @HB_UNICODE_GENERAL_CATEGORY_PARAGRAPH_SEPARATOR: [Zp] + * @HB_UNICODE_GENERAL_CATEGORY_SPACE_SEPARATOR: [Zs] + * + * Data type for the "General_Category" (gc) property from + * the Unicode Character Database. + **/ + +/* Unicode Character Database property: General_Category (gc) */ +typedef enum +{ + HB_UNICODE_GENERAL_CATEGORY_CONTROL, /* Cc */ + HB_UNICODE_GENERAL_CATEGORY_FORMAT, /* Cf */ + HB_UNICODE_GENERAL_CATEGORY_UNASSIGNED, /* Cn */ + HB_UNICODE_GENERAL_CATEGORY_PRIVATE_USE, /* Co */ + HB_UNICODE_GENERAL_CATEGORY_SURROGATE, /* Cs */ + HB_UNICODE_GENERAL_CATEGORY_LOWERCASE_LETTER, /* Ll */ + HB_UNICODE_GENERAL_CATEGORY_MODIFIER_LETTER, /* Lm */ + HB_UNICODE_GENERAL_CATEGORY_OTHER_LETTER, /* Lo */ + HB_UNICODE_GENERAL_CATEGORY_TITLECASE_LETTER, /* Lt */ + HB_UNICODE_GENERAL_CATEGORY_UPPERCASE_LETTER, /* Lu */ + HB_UNICODE_GENERAL_CATEGORY_SPACING_MARK, /* Mc */ + HB_UNICODE_GENERAL_CATEGORY_ENCLOSING_MARK, /* Me */ + HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK, /* Mn */ + HB_UNICODE_GENERAL_CATEGORY_DECIMAL_NUMBER, /* Nd */ + HB_UNICODE_GENERAL_CATEGORY_LETTER_NUMBER, /* Nl */ + HB_UNICODE_GENERAL_CATEGORY_OTHER_NUMBER, /* No */ + HB_UNICODE_GENERAL_CATEGORY_CONNECT_PUNCTUATION, /* Pc */ + HB_UNICODE_GENERAL_CATEGORY_DASH_PUNCTUATION, /* Pd */ + HB_UNICODE_GENERAL_CATEGORY_CLOSE_PUNCTUATION, /* Pe */ + HB_UNICODE_GENERAL_CATEGORY_FINAL_PUNCTUATION, /* Pf */ + HB_UNICODE_GENERAL_CATEGORY_INITIAL_PUNCTUATION, /* Pi */ + HB_UNICODE_GENERAL_CATEGORY_OTHER_PUNCTUATION, /* Po */ + HB_UNICODE_GENERAL_CATEGORY_OPEN_PUNCTUATION, /* Ps */ + HB_UNICODE_GENERAL_CATEGORY_CURRENCY_SYMBOL, /* Sc */ + HB_UNICODE_GENERAL_CATEGORY_MODIFIER_SYMBOL, /* Sk */ + HB_UNICODE_GENERAL_CATEGORY_MATH_SYMBOL, /* Sm */ + HB_UNICODE_GENERAL_CATEGORY_OTHER_SYMBOL, /* So */ + HB_UNICODE_GENERAL_CATEGORY_LINE_SEPARATOR, /* Zl */ + HB_UNICODE_GENERAL_CATEGORY_PARAGRAPH_SEPARATOR, /* Zp */ + HB_UNICODE_GENERAL_CATEGORY_SPACE_SEPARATOR /* Zs */ +} hb_unicode_general_category_t; + +/** + * hb_unicode_combining_class_t: + * @HB_UNICODE_COMBINING_CLASS_NOT_REORDERED: Spacing and enclosing marks; also many vowel and consonant signs, even if nonspacing + * @HB_UNICODE_COMBINING_CLASS_OVERLAY: Marks which overlay a base letter or symbol + * @HB_UNICODE_COMBINING_CLASS_NUKTA: Diacritic nukta marks in Brahmi-derived scripts + * @HB_UNICODE_COMBINING_CLASS_KANA_VOICING: Hiragana/Katakana voicing marks + * @HB_UNICODE_COMBINING_CLASS_VIRAMA: Viramas + * @HB_UNICODE_COMBINING_CLASS_CCC10: [Hebrew] + * @HB_UNICODE_COMBINING_CLASS_CCC11: [Hebrew] + * @HB_UNICODE_COMBINING_CLASS_CCC12: [Hebrew] + * @HB_UNICODE_COMBINING_CLASS_CCC13: [Hebrew] + * @HB_UNICODE_COMBINING_CLASS_CCC14: [Hebrew] + * @HB_UNICODE_COMBINING_CLASS_CCC15: [Hebrew] + * @HB_UNICODE_COMBINING_CLASS_CCC16: [Hebrew] + * @HB_UNICODE_COMBINING_CLASS_CCC17: [Hebrew] + * @HB_UNICODE_COMBINING_CLASS_CCC18: [Hebrew] + * @HB_UNICODE_COMBINING_CLASS_CCC19: [Hebrew] + * @HB_UNICODE_COMBINING_CLASS_CCC20: [Hebrew] + * @HB_UNICODE_COMBINING_CLASS_CCC21: [Hebrew] + * @HB_UNICODE_COMBINING_CLASS_CCC22: [Hebrew] + * @HB_UNICODE_COMBINING_CLASS_CCC23: [Hebrew] + * @HB_UNICODE_COMBINING_CLASS_CCC24: [Hebrew] + * @HB_UNICODE_COMBINING_CLASS_CCC25: [Hebrew] + * @HB_UNICODE_COMBINING_CLASS_CCC26: [Hebrew] + * @HB_UNICODE_COMBINING_CLASS_CCC27: [Arabic] + * @HB_UNICODE_COMBINING_CLASS_CCC28: [Arabic] + * @HB_UNICODE_COMBINING_CLASS_CCC29: [Arabic] + * @HB_UNICODE_COMBINING_CLASS_CCC30: [Arabic] + * @HB_UNICODE_COMBINING_CLASS_CCC31: [Arabic] + * @HB_UNICODE_COMBINING_CLASS_CCC32: [Arabic] + * @HB_UNICODE_COMBINING_CLASS_CCC33: [Arabic] + * @HB_UNICODE_COMBINING_CLASS_CCC34: [Arabic] + * @HB_UNICODE_COMBINING_CLASS_CCC35: [Arabic] + * @HB_UNICODE_COMBINING_CLASS_CCC36: [Syriac] + * @HB_UNICODE_COMBINING_CLASS_CCC84: [Telugu] + * @HB_UNICODE_COMBINING_CLASS_CCC91: [Telugu] + * @HB_UNICODE_COMBINING_CLASS_CCC103: [Thai] + * @HB_UNICODE_COMBINING_CLASS_CCC107: [Thai] + * @HB_UNICODE_COMBINING_CLASS_CCC118: [Lao] + * @HB_UNICODE_COMBINING_CLASS_CCC122: [Lao] + * @HB_UNICODE_COMBINING_CLASS_CCC129: [Tibetan] + * @HB_UNICODE_COMBINING_CLASS_CCC130: [Tibetan] + * @HB_UNICODE_COMBINING_CLASS_CCC133: [Tibetan] + * @HB_UNICODE_COMBINING_CLASS_ATTACHED_BELOW_LEFT: Marks attached at the bottom left + * @HB_UNICODE_COMBINING_CLASS_ATTACHED_BELOW: Marks attached directly below + * @HB_UNICODE_COMBINING_CLASS_ATTACHED_ABOVE: Marks attached directly above + * @HB_UNICODE_COMBINING_CLASS_ATTACHED_ABOVE_RIGHT: Marks attached at the top right + * @HB_UNICODE_COMBINING_CLASS_BELOW_LEFT: Distinct marks at the bottom left + * @HB_UNICODE_COMBINING_CLASS_BELOW: Distinct marks directly below + * @HB_UNICODE_COMBINING_CLASS_BELOW_RIGHT: Distinct marks at the bottom right + * @HB_UNICODE_COMBINING_CLASS_LEFT: Distinct marks to the left + * @HB_UNICODE_COMBINING_CLASS_RIGHT: Distinct marks to the right + * @HB_UNICODE_COMBINING_CLASS_ABOVE_LEFT: Distinct marks at the top left + * @HB_UNICODE_COMBINING_CLASS_ABOVE: Distinct marks directly above + * @HB_UNICODE_COMBINING_CLASS_ABOVE_RIGHT: Distinct marks at the top right + * @HB_UNICODE_COMBINING_CLASS_DOUBLE_BELOW: Distinct marks subtending two bases + * @HB_UNICODE_COMBINING_CLASS_DOUBLE_ABOVE: Distinct marks extending above two bases + * @HB_UNICODE_COMBINING_CLASS_IOTA_SUBSCRIPT: Greek iota subscript only + * @HB_UNICODE_COMBINING_CLASS_INVALID: Invalid combining class + * + * Data type for the Canonical_Combining_Class (ccc) property + * from the Unicode Character Database. + * + * Note: newer versions of Unicode may add new values. + * Client programs should be ready to handle any value in the 0..254 range + * being returned from hb_unicode_combining_class(). + * + **/ +typedef enum +{ + HB_UNICODE_COMBINING_CLASS_NOT_REORDERED = 0, + HB_UNICODE_COMBINING_CLASS_OVERLAY = 1, + HB_UNICODE_COMBINING_CLASS_NUKTA = 7, + HB_UNICODE_COMBINING_CLASS_KANA_VOICING = 8, + HB_UNICODE_COMBINING_CLASS_VIRAMA = 9, + + /* Hebrew */ + HB_UNICODE_COMBINING_CLASS_CCC10 = 10, + HB_UNICODE_COMBINING_CLASS_CCC11 = 11, + HB_UNICODE_COMBINING_CLASS_CCC12 = 12, + HB_UNICODE_COMBINING_CLASS_CCC13 = 13, + HB_UNICODE_COMBINING_CLASS_CCC14 = 14, + HB_UNICODE_COMBINING_CLASS_CCC15 = 15, + HB_UNICODE_COMBINING_CLASS_CCC16 = 16, + HB_UNICODE_COMBINING_CLASS_CCC17 = 17, + HB_UNICODE_COMBINING_CLASS_CCC18 = 18, + HB_UNICODE_COMBINING_CLASS_CCC19 = 19, + HB_UNICODE_COMBINING_CLASS_CCC20 = 20, + HB_UNICODE_COMBINING_CLASS_CCC21 = 21, + HB_UNICODE_COMBINING_CLASS_CCC22 = 22, + HB_UNICODE_COMBINING_CLASS_CCC23 = 23, + HB_UNICODE_COMBINING_CLASS_CCC24 = 24, + HB_UNICODE_COMBINING_CLASS_CCC25 = 25, + HB_UNICODE_COMBINING_CLASS_CCC26 = 26, + + /* Arabic */ + HB_UNICODE_COMBINING_CLASS_CCC27 = 27, + HB_UNICODE_COMBINING_CLASS_CCC28 = 28, + HB_UNICODE_COMBINING_CLASS_CCC29 = 29, + HB_UNICODE_COMBINING_CLASS_CCC30 = 30, + HB_UNICODE_COMBINING_CLASS_CCC31 = 31, + HB_UNICODE_COMBINING_CLASS_CCC32 = 32, + HB_UNICODE_COMBINING_CLASS_CCC33 = 33, + HB_UNICODE_COMBINING_CLASS_CCC34 = 34, + HB_UNICODE_COMBINING_CLASS_CCC35 = 35, + + /* Syriac */ + HB_UNICODE_COMBINING_CLASS_CCC36 = 36, + + /* Telugu */ + HB_UNICODE_COMBINING_CLASS_CCC84 = 84, + HB_UNICODE_COMBINING_CLASS_CCC91 = 91, + + /* Thai */ + HB_UNICODE_COMBINING_CLASS_CCC103 = 103, + HB_UNICODE_COMBINING_CLASS_CCC107 = 107, + + /* Lao */ + HB_UNICODE_COMBINING_CLASS_CCC118 = 118, + HB_UNICODE_COMBINING_CLASS_CCC122 = 122, + + /* Tibetan */ + HB_UNICODE_COMBINING_CLASS_CCC129 = 129, + HB_UNICODE_COMBINING_CLASS_CCC130 = 130, + HB_UNICODE_COMBINING_CLASS_CCC133 = 132, + + + HB_UNICODE_COMBINING_CLASS_ATTACHED_BELOW_LEFT = 200, + HB_UNICODE_COMBINING_CLASS_ATTACHED_BELOW = 202, + HB_UNICODE_COMBINING_CLASS_ATTACHED_ABOVE = 214, + HB_UNICODE_COMBINING_CLASS_ATTACHED_ABOVE_RIGHT = 216, + HB_UNICODE_COMBINING_CLASS_BELOW_LEFT = 218, + HB_UNICODE_COMBINING_CLASS_BELOW = 220, + HB_UNICODE_COMBINING_CLASS_BELOW_RIGHT = 222, + HB_UNICODE_COMBINING_CLASS_LEFT = 224, + HB_UNICODE_COMBINING_CLASS_RIGHT = 226, + HB_UNICODE_COMBINING_CLASS_ABOVE_LEFT = 228, + HB_UNICODE_COMBINING_CLASS_ABOVE = 230, + HB_UNICODE_COMBINING_CLASS_ABOVE_RIGHT = 232, + HB_UNICODE_COMBINING_CLASS_DOUBLE_BELOW = 233, + HB_UNICODE_COMBINING_CLASS_DOUBLE_ABOVE = 234, + + HB_UNICODE_COMBINING_CLASS_IOTA_SUBSCRIPT = 240, + + HB_UNICODE_COMBINING_CLASS_INVALID = 255 +} hb_unicode_combining_class_t; + + +/* + * hb_unicode_funcs_t + */ + +/** + * hb_unicode_funcs_t: + * + * Data type containing a set of virtual methods used for + * accessing various Unicode character properties. + * + * HarfBuzz provides a default function for each of the + * methods in #hb_unicode_funcs_t. Client programs can implement + * their own replacements for the individual Unicode functions, as + * needed, and replace the default by calling the setter for a + * method. + **/ +typedef struct hb_unicode_funcs_t hb_unicode_funcs_t; + + +/* + * just give me the best implementation you've got there. + */ +HB_EXTERN hb_unicode_funcs_t * +hb_unicode_funcs_get_default (void); + + +HB_EXTERN hb_unicode_funcs_t * +hb_unicode_funcs_create (hb_unicode_funcs_t *parent); + +HB_EXTERN hb_unicode_funcs_t * +hb_unicode_funcs_get_empty (void); + +HB_EXTERN hb_unicode_funcs_t * +hb_unicode_funcs_reference (hb_unicode_funcs_t *ufuncs); + +HB_EXTERN void +hb_unicode_funcs_destroy (hb_unicode_funcs_t *ufuncs); + +HB_EXTERN hb_bool_t +hb_unicode_funcs_set_user_data (hb_unicode_funcs_t *ufuncs, + hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy, + hb_bool_t replace); + + +HB_EXTERN void * +hb_unicode_funcs_get_user_data (const hb_unicode_funcs_t *ufuncs, + hb_user_data_key_t *key); + + +HB_EXTERN void +hb_unicode_funcs_make_immutable (hb_unicode_funcs_t *ufuncs); + +HB_EXTERN hb_bool_t +hb_unicode_funcs_is_immutable (hb_unicode_funcs_t *ufuncs); + +HB_EXTERN hb_unicode_funcs_t * +hb_unicode_funcs_get_parent (hb_unicode_funcs_t *ufuncs); + + +/* + * funcs + */ + +/* typedefs */ + +/** + * hb_unicode_combining_class_func_t: + * @ufuncs: A Unicode-functions structure + * @unicode: The code point to query + * @user_data: User data pointer passed by the caller + * + * A virtual method for the #hb_unicode_funcs_t structure. + * + * This method should retrieve the Canonical Combining Class (ccc) + * property for a specified Unicode code point. + * + * Return value: The #hb_unicode_combining_class_t of @unicode + * + **/ +typedef hb_unicode_combining_class_t (*hb_unicode_combining_class_func_t) (hb_unicode_funcs_t *ufuncs, + hb_codepoint_t unicode, + void *user_data); + +/** + * hb_unicode_general_category_func_t: + * @ufuncs: A Unicode-functions structure + * @unicode: The code point to query + * @user_data: User data pointer passed by the caller + * + * A virtual method for the #hb_unicode_funcs_t structure. + * + * This method should retrieve the General Category property for + * a specified Unicode code point. + * + * Return value: The #hb_unicode_general_category_t of @unicode + * + **/ +typedef hb_unicode_general_category_t (*hb_unicode_general_category_func_t) (hb_unicode_funcs_t *ufuncs, + hb_codepoint_t unicode, + void *user_data); + +/** + * hb_unicode_mirroring_func_t: + * @ufuncs: A Unicode-functions structure + * @unicode: The code point to query + * @user_data: User data pointer passed by the caller + * + * A virtual method for the #hb_unicode_funcs_t structure. + * + * This method should retrieve the Bi-Directional Mirroring Glyph + * code point for a specified Unicode code point. + * + * Note: If a code point does not have a specified + * Bi-Directional Mirroring Glyph defined, the method should + * return the original code point. + * + * Return value: The #hb_codepoint_t of the Mirroring Glyph for @unicode + * + **/ +typedef hb_codepoint_t (*hb_unicode_mirroring_func_t) (hb_unicode_funcs_t *ufuncs, + hb_codepoint_t unicode, + void *user_data); + +/** + * hb_unicode_script_func_t: + * @ufuncs: A Unicode-functions structure + * @unicode: The code point to query + * @user_data: User data pointer passed by the caller + * + * A virtual method for the #hb_unicode_funcs_t structure. + * + * This method should retrieve the Script property for a + * specified Unicode code point. + * + * Return value: The #hb_script_t of @unicode + * + **/ +typedef hb_script_t (*hb_unicode_script_func_t) (hb_unicode_funcs_t *ufuncs, + hb_codepoint_t unicode, + void *user_data); + +/** + * hb_unicode_compose_func_t: + * @ufuncs: A Unicode-functions structure + * @a: The first code point to compose + * @b: The second code point to compose + * @ab: (out): The composed code point + * @user_data: user data pointer passed by the caller + * + * A virtual method for the #hb_unicode_funcs_t structure. + * + * This method should compose a sequence of two input Unicode code + * points by canonical equivalence, returning the composed code + * point in a #hb_codepoint_t output parameter (if successful). + * The method must return an #hb_bool_t indicating the success + * of the composition. + * + * Return value: `true` is @a,@b composed, `false` otherwise + * + **/ +typedef hb_bool_t (*hb_unicode_compose_func_t) (hb_unicode_funcs_t *ufuncs, + hb_codepoint_t a, + hb_codepoint_t b, + hb_codepoint_t *ab, + void *user_data); + +/** + * hb_unicode_decompose_func_t: + * @ufuncs: A Unicode-functions structure + * @ab: The code point to decompose + * @a: (out): The first decomposed code point + * @b: (out): The second decomposed code point + * @user_data: user data pointer passed by the caller + * + * A virtual method for the #hb_unicode_funcs_t structure. + * + * This method should decompose an input Unicode code point, + * returning the two decomposed code points in #hb_codepoint_t + * output parameters (if successful). The method must return an + * #hb_bool_t indicating the success of the composition. + * + * Return value: `true` if @ab decomposed, `false` otherwise + * + **/ +typedef hb_bool_t (*hb_unicode_decompose_func_t) (hb_unicode_funcs_t *ufuncs, + hb_codepoint_t ab, + hb_codepoint_t *a, + hb_codepoint_t *b, + void *user_data); + +/* func setters */ + +/** + * hb_unicode_funcs_set_combining_class_func: + * @ufuncs: A Unicode-functions structure + * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign + * @user_data: Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is not needed anymore + * + * Sets the implementation function for #hb_unicode_combining_class_func_t. + * + * Since: 0.9.2 + **/ +HB_EXTERN void +hb_unicode_funcs_set_combining_class_func (hb_unicode_funcs_t *ufuncs, + hb_unicode_combining_class_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_unicode_funcs_set_general_category_func: + * @ufuncs: A Unicode-functions structure + * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign + * @user_data: Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is not needed anymore + * + * Sets the implementation function for #hb_unicode_general_category_func_t. + * + * Since: 0.9.2 + **/ +HB_EXTERN void +hb_unicode_funcs_set_general_category_func (hb_unicode_funcs_t *ufuncs, + hb_unicode_general_category_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_unicode_funcs_set_mirroring_func: + * @ufuncs: A Unicode-functions structure + * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign + * @user_data: Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is not needed anymore + * + * Sets the implementation function for #hb_unicode_mirroring_func_t. + * + * Since: 0.9.2 + **/ +HB_EXTERN void +hb_unicode_funcs_set_mirroring_func (hb_unicode_funcs_t *ufuncs, + hb_unicode_mirroring_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_unicode_funcs_set_script_func: + * @ufuncs: A Unicode-functions structure + * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign + * @user_data: Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is not needed anymore + * + * Sets the implementation function for #hb_unicode_script_func_t. + * + * Since: 0.9.2 + **/ +HB_EXTERN void +hb_unicode_funcs_set_script_func (hb_unicode_funcs_t *ufuncs, + hb_unicode_script_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_unicode_funcs_set_compose_func: + * @ufuncs: A Unicode-functions structure + * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign + * @user_data: Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is not needed anymore + * + * Sets the implementation function for #hb_unicode_compose_func_t. + * + * Since: 0.9.2 + **/ +HB_EXTERN void +hb_unicode_funcs_set_compose_func (hb_unicode_funcs_t *ufuncs, + hb_unicode_compose_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_unicode_funcs_set_decompose_func: + * @ufuncs: A Unicode-functions structure + * @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign + * @user_data: Data to pass to @func + * @destroy: (nullable): The function to call when @user_data is not needed anymore + * + * Sets the implementation function for #hb_unicode_decompose_func_t. + * + * Since: 0.9.2 + **/ +HB_EXTERN void +hb_unicode_funcs_set_decompose_func (hb_unicode_funcs_t *ufuncs, + hb_unicode_decompose_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/* accessors */ + +/** + * hb_unicode_combining_class: + * @ufuncs: The Unicode-functions structure + * @unicode: The code point to query + * + * Retrieves the Canonical Combining Class (ccc) property + * of code point @unicode. + * + * Return value: The #hb_unicode_combining_class_t of @unicode + * + * Since: 0.9.2 + **/ +HB_EXTERN hb_unicode_combining_class_t +hb_unicode_combining_class (hb_unicode_funcs_t *ufuncs, + hb_codepoint_t unicode); + +/** + * hb_unicode_general_category: + * @ufuncs: The Unicode-functions structure + * @unicode: The code point to query + * + * Retrieves the General Category (gc) property + * of code point @unicode. + * + * Return value: The #hb_unicode_general_category_t of @unicode + * + * Since: 0.9.2 + **/ +HB_EXTERN hb_unicode_general_category_t +hb_unicode_general_category (hb_unicode_funcs_t *ufuncs, + hb_codepoint_t unicode); + +/** + * hb_unicode_mirroring: + * @ufuncs: The Unicode-functions structure + * @unicode: The code point to query + * + * Retrieves the Bi-directional Mirroring Glyph code + * point defined for code point @unicode. + * + * Return value: The #hb_codepoint_t of the Mirroring Glyph for @unicode + * + * Since: 0.9.2 + **/ +HB_EXTERN hb_codepoint_t +hb_unicode_mirroring (hb_unicode_funcs_t *ufuncs, + hb_codepoint_t unicode); + +/** + * hb_unicode_script: + * @ufuncs: The Unicode-functions structure + * @unicode: The code point to query + * + * Retrieves the #hb_script_t script to which code + * point @unicode belongs. + * + * Return value: The #hb_script_t of @unicode + * + * Since: 0.9.2 + **/ +HB_EXTERN hb_script_t +hb_unicode_script (hb_unicode_funcs_t *ufuncs, + hb_codepoint_t unicode); + +HB_EXTERN hb_bool_t +hb_unicode_compose (hb_unicode_funcs_t *ufuncs, + hb_codepoint_t a, + hb_codepoint_t b, + hb_codepoint_t *ab); + +HB_EXTERN hb_bool_t +hb_unicode_decompose (hb_unicode_funcs_t *ufuncs, + hb_codepoint_t ab, + hb_codepoint_t *a, + hb_codepoint_t *b); + +HB_END_DECLS + +#endif /* HB_UNICODE_H */ diff --git a/libs/harfbuzz/src/hb-unicode.hh b/libs/harfbuzz/src/hb-unicode.hh new file mode 100644 index 00000000..39aaee5b --- /dev/null +++ b/libs/harfbuzz/src/hb-unicode.hh @@ -0,0 +1,403 @@ +/* + * Copyright © 2009 Red Hat, Inc. + * Copyright © 2011 Codethink Limited + * Copyright © 2010,2011,2012 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. + * + * Red Hat Author(s): Behdad Esfahbod + * Codethink Author(s): Ryan Lortie + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_UNICODE_HH +#define HB_UNICODE_HH + +#include "hb.hh" + + +extern HB_INTERNAL const uint8_t _hb_modified_combining_class[256]; + +/* + * hb_unicode_funcs_t + */ + +#define HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS \ + HB_UNICODE_FUNC_IMPLEMENT (combining_class) \ + HB_IF_NOT_DEPRECATED (HB_UNICODE_FUNC_IMPLEMENT (eastasian_width)) \ + HB_UNICODE_FUNC_IMPLEMENT (general_category) \ + HB_UNICODE_FUNC_IMPLEMENT (mirroring) \ + HB_UNICODE_FUNC_IMPLEMENT (script) \ + HB_UNICODE_FUNC_IMPLEMENT (compose) \ + HB_UNICODE_FUNC_IMPLEMENT (decompose) \ + HB_IF_NOT_DEPRECATED (HB_UNICODE_FUNC_IMPLEMENT (decompose_compatibility)) \ + /* ^--- Add new callbacks here */ + +/* Simple callbacks are those taking a hb_codepoint_t and returning a hb_codepoint_t */ +#define HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS_SIMPLE \ + HB_UNICODE_FUNC_IMPLEMENT (hb_unicode_combining_class_t, combining_class) \ + HB_IF_NOT_DEPRECATED (HB_UNICODE_FUNC_IMPLEMENT (unsigned int, eastasian_width)) \ + HB_UNICODE_FUNC_IMPLEMENT (hb_unicode_general_category_t, general_category) \ + HB_UNICODE_FUNC_IMPLEMENT (hb_codepoint_t, mirroring) \ + HB_UNICODE_FUNC_IMPLEMENT (hb_script_t, script) \ + /* ^--- Add new simple callbacks here */ + +struct hb_unicode_funcs_t +{ + hb_object_header_t header; + + hb_unicode_funcs_t *parent; + +#define HB_UNICODE_FUNC_IMPLEMENT(return_type, name) \ + return_type name (hb_codepoint_t unicode) { return func.name (this, unicode, user_data.name); } +HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS_SIMPLE +#undef HB_UNICODE_FUNC_IMPLEMENT + + hb_bool_t compose (hb_codepoint_t a, hb_codepoint_t b, + hb_codepoint_t *ab) + { + *ab = 0; + if (unlikely (!a || !b)) return false; + return func.compose (this, a, b, ab, user_data.compose); + } + + hb_bool_t decompose (hb_codepoint_t ab, + hb_codepoint_t *a, hb_codepoint_t *b) + { + *a = ab; *b = 0; + return func.decompose (this, ab, a, b, user_data.decompose); + } + + unsigned int decompose_compatibility (hb_codepoint_t u, + hb_codepoint_t *decomposed) + { +#ifdef HB_DISABLE_DEPRECATED + unsigned int ret = 0; +#else + unsigned int ret = func.decompose_compatibility (this, u, decomposed, user_data.decompose_compatibility); +#endif + if (ret == 1 && u == decomposed[0]) { + decomposed[0] = 0; + return 0; + } + decomposed[ret] = 0; + return ret; + } + + unsigned int + modified_combining_class (hb_codepoint_t u) + { + /* Reorder SAKOT to ensure it comes after any tone marks. */ + if (unlikely (u == 0x1A60u)) return 254; + /* Reorder PADMA to ensure it comes after any vowel marks. */ + if (unlikely (u == 0x0FC6u)) return 254; + /* Reorder TSA -PHRU to reorder before U+0F74 */ + if (unlikely (u == 0x0F39u)) return 127; + + return _hb_modified_combining_class[combining_class (u)]; + } + + static hb_bool_t + is_variation_selector (hb_codepoint_t unicode) + { + /* U+180B..180D, U+180F MONGOLIAN FREE VARIATION SELECTORs are handled in the + * Arabic shaper. No need to match them here. */ + return unlikely (hb_in_ranges (unicode, + 0xFE00u, 0xFE0Fu, /* VARIATION SELECTOR-1..16 */ + 0xE0100u, 0xE01EFu)); /* VARIATION SELECTOR-17..256 */ + } + + /* Default_Ignorable codepoints: + * + * Note: While U+115F, U+1160, U+3164 and U+FFA0 are Default_Ignorable, + * we do NOT want to hide them, as the way Uniscribe has implemented them + * is with regular spacing glyphs, and that's the way fonts are made to work. + * As such, we make exceptions for those four. + * Also ignoring U+1BCA0..1BCA3. https://github.com/harfbuzz/harfbuzz/issues/503 + * + * Unicode 14.0: + * $ grep '; Default_Ignorable_Code_Point ' DerivedCoreProperties.txt | sed 's/;.*#/#/' + * 00AD # Cf SOFT HYPHEN + * 034F # Mn COMBINING GRAPHEME JOINER + * 061C # Cf ARABIC LETTER MARK + * 115F..1160 # Lo [2] HANGUL CHOSEONG FILLER..HANGUL JUNGSEONG FILLER + * 17B4..17B5 # Mn [2] KHMER VOWEL INHERENT AQ..KHMER VOWEL INHERENT AA + * 180B..180D # Mn [3] MONGOLIAN FREE VARIATION SELECTOR ONE..MONGOLIAN FREE VARIATION SELECTOR THREE + * 180E # Cf MONGOLIAN VOWEL SEPARATOR + * 180F # Mn MONGOLIAN FREE VARIATION SELECTOR FOUR + * 200B..200F # Cf [5] ZERO WIDTH SPACE..RIGHT-TO-LEFT MARK + * 202A..202E # Cf [5] LEFT-TO-RIGHT EMBEDDING..RIGHT-TO-LEFT OVERRIDE + * 2060..2064 # Cf [5] WORD JOINER..INVISIBLE PLUS + * 2065 # Cn + * 2066..206F # Cf [10] LEFT-TO-RIGHT ISOLATE..NOMINAL DIGIT SHAPES + * 3164 # Lo HANGUL FILLER + * FE00..FE0F # Mn [16] VARIATION SELECTOR-1..VARIATION SELECTOR-16 + * FEFF # Cf ZERO WIDTH NO-BREAK SPACE + * FFA0 # Lo HALFWIDTH HANGUL FILLER + * FFF0..FFF8 # Cn [9] .. + * 1BCA0..1BCA3 # Cf [4] SHORTHAND FORMAT LETTER OVERLAP..SHORTHAND FORMAT UP STEP + * 1D173..1D17A # Cf [8] MUSICAL SYMBOL BEGIN BEAM..MUSICAL SYMBOL END PHRASE + * E0000 # Cn + * E0001 # Cf LANGUAGE TAG + * E0002..E001F # Cn [30] .. + * E0020..E007F # Cf [96] TAG SPACE..CANCEL TAG + * E0080..E00FF # Cn [128] .. + * E0100..E01EF # Mn [240] VARIATION SELECTOR-17..VARIATION SELECTOR-256 + * E01F0..E0FFF # Cn [3600] .. + */ + static hb_bool_t + is_default_ignorable (hb_codepoint_t ch) + { + hb_codepoint_t plane = ch >> 16; + if (likely (plane == 0)) + { + /* BMP */ + hb_codepoint_t page = ch >> 8; + switch (page) { + case 0x00: return unlikely (ch == 0x00ADu); + case 0x03: return unlikely (ch == 0x034Fu); + case 0x06: return unlikely (ch == 0x061Cu); + case 0x17: return hb_in_range (ch, 0x17B4u, 0x17B5u); + case 0x18: return hb_in_range (ch, 0x180Bu, 0x180Eu); + case 0x20: return hb_in_ranges (ch, 0x200Bu, 0x200Fu, + 0x202Au, 0x202Eu, + 0x2060u, 0x206Fu); + case 0xFE: return hb_in_range (ch, 0xFE00u, 0xFE0Fu) || ch == 0xFEFFu; + case 0xFF: return hb_in_range (ch, 0xFFF0u, 0xFFF8u); + default: return false; + } + } + else + { + /* Other planes */ + switch (plane) { + case 0x01: return hb_in_range (ch, 0x1D173u, 0x1D17Au); + case 0x0E: return hb_in_range (ch, 0xE0000u, 0xE0FFFu); + default: return false; + } + } + } + + /* Space estimates based on: + * https://unicode.org/charts/PDF/U2000.pdf + * https://docs.microsoft.com/en-us/typography/develop/character-design-standards/whitespace + */ + enum space_t { + NOT_SPACE = 0, + SPACE_EM = 1, + SPACE_EM_2 = 2, + SPACE_EM_3 = 3, + SPACE_EM_4 = 4, + SPACE_EM_5 = 5, + SPACE_EM_6 = 6, + SPACE_EM_16 = 16, + SPACE_4_EM_18, /* 4/18th of an EM! */ + SPACE, + SPACE_FIGURE, + SPACE_PUNCTUATION, + SPACE_NARROW, + }; + static space_t + space_fallback_type (hb_codepoint_t u) + { + switch (u) + { + /* All GC=Zs chars that can use a fallback. */ + default: return NOT_SPACE; /* U+1680 OGHAM SPACE MARK */ + case 0x0020u: return SPACE; /* U+0020 SPACE */ + case 0x00A0u: return SPACE; /* U+00A0 NO-BREAK SPACE */ + case 0x2000u: return SPACE_EM_2; /* U+2000 EN QUAD */ + case 0x2001u: return SPACE_EM; /* U+2001 EM QUAD */ + case 0x2002u: return SPACE_EM_2; /* U+2002 EN SPACE */ + case 0x2003u: return SPACE_EM; /* U+2003 EM SPACE */ + case 0x2004u: return SPACE_EM_3; /* U+2004 THREE-PER-EM SPACE */ + case 0x2005u: return SPACE_EM_4; /* U+2005 FOUR-PER-EM SPACE */ + case 0x2006u: return SPACE_EM_6; /* U+2006 SIX-PER-EM SPACE */ + case 0x2007u: return SPACE_FIGURE; /* U+2007 FIGURE SPACE */ + case 0x2008u: return SPACE_PUNCTUATION; /* U+2008 PUNCTUATION SPACE */ + case 0x2009u: return SPACE_EM_5; /* U+2009 THIN SPACE */ + case 0x200Au: return SPACE_EM_16; /* U+200A HAIR SPACE */ + case 0x202Fu: return SPACE_NARROW; /* U+202F NARROW NO-BREAK SPACE */ + case 0x205Fu: return SPACE_4_EM_18; /* U+205F MEDIUM MATHEMATICAL SPACE */ + case 0x3000u: return SPACE_EM; /* U+3000 IDEOGRAPHIC SPACE */ + } + } + + struct { +#define HB_UNICODE_FUNC_IMPLEMENT(name) hb_unicode_##name##_func_t name; + HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_UNICODE_FUNC_IMPLEMENT + } func; + + struct { +#define HB_UNICODE_FUNC_IMPLEMENT(name) void *name; + HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_UNICODE_FUNC_IMPLEMENT + } user_data; + + struct { +#define HB_UNICODE_FUNC_IMPLEMENT(name) hb_destroy_func_t name; + HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_UNICODE_FUNC_IMPLEMENT + } destroy; +}; +DECLARE_NULL_INSTANCE (hb_unicode_funcs_t); + + +/* + * Modified combining marks + */ + +/* Hebrew + * + * We permute the "fixed-position" classes 10-26 into the order + * described in the SBL Hebrew manual: + * + * https://www.sbl-site.org/Fonts/SBLHebrewUserManual1.5x.pdf + * + * (as recommended by: + * https://forum.fontlab.com/archive-old-microsoft-volt-group/vista-and-diacritic-ordering/msg22823/) + * + * More details here: + * https://bugzilla.mozilla.org/show_bug.cgi?id=662055 + */ +#define HB_MODIFIED_COMBINING_CLASS_CCC10 22 /* sheva */ +#define HB_MODIFIED_COMBINING_CLASS_CCC11 15 /* hataf segol */ +#define HB_MODIFIED_COMBINING_CLASS_CCC12 16 /* hataf patah */ +#define HB_MODIFIED_COMBINING_CLASS_CCC13 17 /* hataf qamats */ +#define HB_MODIFIED_COMBINING_CLASS_CCC14 23 /* hiriq */ +#define HB_MODIFIED_COMBINING_CLASS_CCC15 18 /* tsere */ +#define HB_MODIFIED_COMBINING_CLASS_CCC16 19 /* segol */ +#define HB_MODIFIED_COMBINING_CLASS_CCC17 20 /* patah */ +#define HB_MODIFIED_COMBINING_CLASS_CCC18 21 /* qamats & qamats qatan */ +#define HB_MODIFIED_COMBINING_CLASS_CCC19 14 /* holam & holam haser for vav*/ +#define HB_MODIFIED_COMBINING_CLASS_CCC20 24 /* qubuts */ +#define HB_MODIFIED_COMBINING_CLASS_CCC21 12 /* dagesh */ +#define HB_MODIFIED_COMBINING_CLASS_CCC22 25 /* meteg */ +#define HB_MODIFIED_COMBINING_CLASS_CCC23 13 /* rafe */ +#define HB_MODIFIED_COMBINING_CLASS_CCC24 10 /* shin dot */ +#define HB_MODIFIED_COMBINING_CLASS_CCC25 11 /* sin dot */ +#define HB_MODIFIED_COMBINING_CLASS_CCC26 26 /* point varika */ + +/* + * Arabic + * + * Modify to move Shadda (ccc=33) before other marks. See: + * https://unicode.org/faq/normalization.html#8 + * https://unicode.org/faq/normalization.html#9 + */ +#define HB_MODIFIED_COMBINING_CLASS_CCC27 28 /* fathatan */ +#define HB_MODIFIED_COMBINING_CLASS_CCC28 29 /* dammatan */ +#define HB_MODIFIED_COMBINING_CLASS_CCC29 30 /* kasratan */ +#define HB_MODIFIED_COMBINING_CLASS_CCC30 31 /* fatha */ +#define HB_MODIFIED_COMBINING_CLASS_CCC31 32 /* damma */ +#define HB_MODIFIED_COMBINING_CLASS_CCC32 33 /* kasra */ +#define HB_MODIFIED_COMBINING_CLASS_CCC33 27 /* shadda */ +#define HB_MODIFIED_COMBINING_CLASS_CCC34 34 /* sukun */ +#define HB_MODIFIED_COMBINING_CLASS_CCC35 35 /* superscript alef */ + +/* Syriac */ +#define HB_MODIFIED_COMBINING_CLASS_CCC36 36 /* superscript alaph */ + +/* Telugu + * + * Modify Telugu length marks (ccc=84, ccc=91). + * These are the only matras in the main Indic scripts range that have + * a non-zero ccc. That makes them reorder with the Halant (ccc=9). + * Assign 4 and 5, which are otherwise unassigned. + */ +#define HB_MODIFIED_COMBINING_CLASS_CCC84 4 /* length mark */ +#define HB_MODIFIED_COMBINING_CLASS_CCC91 5 /* ai length mark */ + +/* Thai + * + * Modify U+0E38 and U+0E39 (ccc=103) to be reordered before U+0E3A (ccc=9). + * Assign 3, which is unassigned otherwise. + * Uniscribe does this reordering too. + */ +#define HB_MODIFIED_COMBINING_CLASS_CCC103 3 /* sara u / sara uu */ +#define HB_MODIFIED_COMBINING_CLASS_CCC107 107 /* mai * */ + +/* Lao */ +#define HB_MODIFIED_COMBINING_CLASS_CCC118 118 /* sign u / sign uu */ +#define HB_MODIFIED_COMBINING_CLASS_CCC122 122 /* mai * */ + +/* Tibetan + * + * In case of multiple vowel-signs, use u first (but after achung) + * this allows Dzongkha multi-vowel shortcuts to render correctly + */ +#define HB_MODIFIED_COMBINING_CLASS_CCC129 129 /* sign aa */ +#define HB_MODIFIED_COMBINING_CLASS_CCC130 132 /* sign i */ +#define HB_MODIFIED_COMBINING_CLASS_CCC132 131 /* sign u */ + +/* Misc */ + +#define HB_UNICODE_GENERAL_CATEGORY_IS_MARK(gen_cat) \ + (FLAG_UNSAFE (gen_cat) & \ + (FLAG (HB_UNICODE_GENERAL_CATEGORY_SPACING_MARK) | \ + FLAG (HB_UNICODE_GENERAL_CATEGORY_ENCLOSING_MARK) | \ + FLAG (HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK))) + +#define HB_UNICODE_GENERAL_CATEGORY_IS_LETTER(gen_cat) \ + (FLAG_UNSAFE (gen_cat) & \ + (FLAG (HB_UNICODE_GENERAL_CATEGORY_LOWERCASE_LETTER) | \ + FLAG (HB_UNICODE_GENERAL_CATEGORY_MODIFIER_LETTER) | \ + FLAG (HB_UNICODE_GENERAL_CATEGORY_OTHER_LETTER) | \ + FLAG (HB_UNICODE_GENERAL_CATEGORY_TITLECASE_LETTER) | \ + FLAG (HB_UNICODE_GENERAL_CATEGORY_UPPERCASE_LETTER))) + +/* + * Ranges, used for bsearch tables. + */ + +struct hb_unicode_range_t +{ + static int + cmp (const void *_key, const void *_item) + { + hb_codepoint_t cp = *((hb_codepoint_t *) _key); + const hb_unicode_range_t *range = (hb_unicode_range_t *) _item; + + if (cp < range->start) + return -1; + else if (cp <= range->end) + return 0; + else + return +1; + } + + hb_codepoint_t start; + hb_codepoint_t end; +}; + +/* + * Emoji. + */ + +HB_INTERNAL bool +_hb_unicode_is_emoji_Extended_Pictographic (hb_codepoint_t cp); + + +extern "C" HB_INTERNAL hb_unicode_funcs_t *hb_ucd_get_unicode_funcs (); + + +#endif /* HB_UNICODE_HH */ diff --git a/libs/harfbuzz/src/hb-uniscribe.cc b/libs/harfbuzz/src/hb-uniscribe.cc new file mode 100644 index 00000000..50f71ce9 --- /dev/null +++ b/libs/harfbuzz/src/hb-uniscribe.cc @@ -0,0 +1,889 @@ +/* + * Copyright © 2011,2012,2013 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 + */ + +#include "hb.hh" + +#ifdef HAVE_UNISCRIBE + +#ifdef HB_NO_OT_TAG +#error "Cannot compile 'uniscribe' shaper with HB_NO_OT_TAG." +#endif + +#include "hb-shaper-impl.hh" + +#include +#include +#include + +#ifndef E_NOT_SUFFICIENT_BUFFER +#define E_NOT_SUFFICIENT_BUFFER HRESULT_FROM_WIN32 (ERROR_INSUFFICIENT_BUFFER) +#endif + +#include "hb-uniscribe.h" + +#include "hb-ms-feature-ranges.hh" +#include "hb-open-file.hh" +#include "hb-ot-name-table.hh" +#include "hb-ot-layout.h" + + +/** + * SECTION:hb-uniscribe + * @title: hb-uniscribe + * @short_description: Windows integration + * @include: hb-uniscribe.h + * + * Functions for using HarfBuzz with Windows fonts. + **/ + +typedef HRESULT (WINAPI *SIOT) /*ScriptItemizeOpenType*/( + const WCHAR *pwcInChars, + int cInChars, + int cMaxItems, + const SCRIPT_CONTROL *psControl, + const SCRIPT_STATE *psState, + SCRIPT_ITEM *pItems, + OPENTYPE_TAG *pScriptTags, + int *pcItems +); + +typedef HRESULT (WINAPI *SSOT) /*ScriptShapeOpenType*/( + HDC hdc, + SCRIPT_CACHE *psc, + SCRIPT_ANALYSIS *psa, + OPENTYPE_TAG tagScript, + OPENTYPE_TAG tagLangSys, + int *rcRangeChars, + TEXTRANGE_PROPERTIES **rpRangeProperties, + int cRanges, + const WCHAR *pwcChars, + int cChars, + int cMaxGlyphs, + WORD *pwLogClust, + SCRIPT_CHARPROP *pCharProps, + WORD *pwOutGlyphs, + SCRIPT_GLYPHPROP *pOutGlyphProps, + int *pcGlyphs +); + +typedef HRESULT (WINAPI *SPOT) /*ScriptPlaceOpenType*/( + HDC hdc, + SCRIPT_CACHE *psc, + SCRIPT_ANALYSIS *psa, + OPENTYPE_TAG tagScript, + OPENTYPE_TAG tagLangSys, + int *rcRangeChars, + TEXTRANGE_PROPERTIES **rpRangeProperties, + int cRanges, + const WCHAR *pwcChars, + WORD *pwLogClust, + SCRIPT_CHARPROP *pCharProps, + int cChars, + const WORD *pwGlyphs, + const SCRIPT_GLYPHPROP *pGlyphProps, + int cGlyphs, + int *piAdvance, + GOFFSET *pGoffset, + ABC *pABC +); + + +/* Fallback implementations. */ + +static HRESULT WINAPI +hb_ScriptItemizeOpenType( + const WCHAR *pwcInChars, + int cInChars, + int cMaxItems, + const SCRIPT_CONTROL *psControl, + const SCRIPT_STATE *psState, + SCRIPT_ITEM *pItems, + OPENTYPE_TAG *pScriptTags, + int *pcItems +) +{ +{ + return ScriptItemize (pwcInChars, + cInChars, + cMaxItems, + psControl, + psState, + pItems, + pcItems); +} +} + +static HRESULT WINAPI +hb_ScriptShapeOpenType( + HDC hdc, + SCRIPT_CACHE *psc, + SCRIPT_ANALYSIS *psa, + OPENTYPE_TAG tagScript, + OPENTYPE_TAG tagLangSys, + int *rcRangeChars, + TEXTRANGE_PROPERTIES **rpRangeProperties, + int cRanges, + const WCHAR *pwcChars, + int cChars, + int cMaxGlyphs, + WORD *pwLogClust, + SCRIPT_CHARPROP *pCharProps, + WORD *pwOutGlyphs, + SCRIPT_GLYPHPROP *pOutGlyphProps, + int *pcGlyphs +) +{ + SCRIPT_VISATTR *psva = (SCRIPT_VISATTR *) pOutGlyphProps; + return ScriptShape (hdc, + psc, + pwcChars, + cChars, + cMaxGlyphs, + psa, + pwOutGlyphs, + pwLogClust, + psva, + pcGlyphs); +} + +static HRESULT WINAPI +hb_ScriptPlaceOpenType( + HDC hdc, + SCRIPT_CACHE *psc, + SCRIPT_ANALYSIS *psa, + OPENTYPE_TAG tagScript, + OPENTYPE_TAG tagLangSys, + int *rcRangeChars, + TEXTRANGE_PROPERTIES **rpRangeProperties, + int cRanges, + const WCHAR *pwcChars, + WORD *pwLogClust, + SCRIPT_CHARPROP *pCharProps, + int cChars, + const WORD *pwGlyphs, + const SCRIPT_GLYPHPROP *pGlyphProps, + int cGlyphs, + int *piAdvance, + GOFFSET *pGoffset, + ABC *pABC +) +{ + SCRIPT_VISATTR *psva = (SCRIPT_VISATTR *) pGlyphProps; + return ScriptPlace (hdc, + psc, + pwGlyphs, + cGlyphs, + psva, + psa, + piAdvance, + pGoffset, + pABC); +} + + +struct hb_uniscribe_shaper_funcs_t +{ + SIOT ScriptItemizeOpenType; + SSOT ScriptShapeOpenType; + SPOT ScriptPlaceOpenType; + + void init () + { + HMODULE hinstLib; + this->ScriptItemizeOpenType = nullptr; + this->ScriptShapeOpenType = nullptr; + this->ScriptPlaceOpenType = nullptr; + + hinstLib = GetModuleHandle (TEXT ("usp10.dll")); + if (hinstLib) + { +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-function-type" + this->ScriptItemizeOpenType = (SIOT) GetProcAddress (hinstLib, "ScriptItemizeOpenType"); + this->ScriptShapeOpenType = (SSOT) GetProcAddress (hinstLib, "ScriptShapeOpenType"); + this->ScriptPlaceOpenType = (SPOT) GetProcAddress (hinstLib, "ScriptPlaceOpenType"); +#pragma GCC diagnostic pop + } + if (!this->ScriptItemizeOpenType || + !this->ScriptShapeOpenType || + !this->ScriptPlaceOpenType) + { + DEBUG_MSG (UNISCRIBE, nullptr, "OpenType versions of functions not found; falling back."); + this->ScriptItemizeOpenType = hb_ScriptItemizeOpenType; + this->ScriptShapeOpenType = hb_ScriptShapeOpenType; + this->ScriptPlaceOpenType = hb_ScriptPlaceOpenType; + } + } +}; + +static inline void free_static_uniscribe_shaper_funcs (); + +static struct hb_uniscribe_shaper_funcs_lazy_loader_t : hb_lazy_loader_t +{ + static hb_uniscribe_shaper_funcs_t *create () + { + hb_uniscribe_shaper_funcs_t *funcs = (hb_uniscribe_shaper_funcs_t *) hb_calloc (1, sizeof (hb_uniscribe_shaper_funcs_t)); + if (unlikely (!funcs)) + return nullptr; + + funcs->init (); + + hb_atexit (free_static_uniscribe_shaper_funcs); + + return funcs; + } + static void destroy (hb_uniscribe_shaper_funcs_t *p) + { + hb_free ((void *) p); + } + static hb_uniscribe_shaper_funcs_t *get_null () + { + return nullptr; + } +} static_uniscribe_shaper_funcs; + +static inline +void free_static_uniscribe_shaper_funcs () +{ + static_uniscribe_shaper_funcs.free_instance (); +} + +static hb_uniscribe_shaper_funcs_t * +hb_uniscribe_shaper_get_funcs () +{ + return static_uniscribe_shaper_funcs.get_unconst (); +} + + +/* + * shaper face data + */ + +struct hb_uniscribe_face_data_t { + HANDLE fh; + hb_uniscribe_shaper_funcs_t *funcs; + wchar_t face_name[LF_FACESIZE]; +}; + +/* face_name should point to a wchar_t[LF_FACESIZE] object. */ +static void +_hb_generate_unique_face_name (wchar_t *face_name, unsigned int *plen) +{ + /* We'll create a private name for the font from a UUID using a simple, + * somewhat base64-like encoding scheme */ + const char *enc = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-"; + UUID id; + UuidCreate ((UUID*) &id); + static_assert ((2 + 3 * (16/2) < LF_FACESIZE), ""); + unsigned int name_str_len = 0; + face_name[name_str_len++] = 'F'; + face_name[name_str_len++] = '_'; + unsigned char *p = (unsigned char *) &id; + for (unsigned int i = 0; i < 16; i += 2) + { + /* Spread the 16 bits from two bytes of the UUID across three chars of face_name, + * using the bits in groups of 5,5,6 to select chars from enc. + * This will generate 24 characters; with the 'F_' prefix we already provided, + * the name will be 26 chars (plus the NUL terminator), so will always fit within + * face_name (LF_FACESIZE = 32). */ + face_name[name_str_len++] = enc[p[i] >> 3]; + face_name[name_str_len++] = enc[((p[i] << 2) | (p[i + 1] >> 6)) & 0x1f]; + face_name[name_str_len++] = enc[p[i + 1] & 0x3f]; + } + face_name[name_str_len] = 0; + if (plen) + *plen = name_str_len; +} + +/* Destroys blob. */ +static hb_blob_t * +_hb_rename_font (hb_blob_t *blob, wchar_t *new_name) +{ + /* Create a copy of the font data, with the 'name' table replaced by a + * table that names the font with our private F_* name created above. + * For simplicity, we just append a new 'name' table and update the + * sfnt directory; the original table is left in place, but unused. + * + * The new table will contain just 5 name IDs: family, style, unique, + * full, PS. All of them point to the same name data with our unique name. + */ + + blob = hb_sanitize_context_t ().sanitize_blob (blob); + + unsigned int length, new_length, name_str_len; + const char *orig_sfnt_data = hb_blob_get_data (blob, &length); + + _hb_generate_unique_face_name (new_name, &name_str_len); + + static const uint16_t name_IDs[] = { 1, 2, 3, 4, 6 }; + + unsigned int name_table_length = OT::name::min_size + + ARRAY_LENGTH (name_IDs) * OT::NameRecord::static_size + + name_str_len * 2; /* for name data in UTF16BE form */ + unsigned int padded_name_table_length = ((name_table_length + 3) & ~3); + unsigned int name_table_offset = (length + 3) & ~3; + + new_length = name_table_offset + padded_name_table_length; + void *new_sfnt_data = hb_calloc (1, new_length); + if (!new_sfnt_data) + { + hb_blob_destroy (blob); + return nullptr; + } + + memcpy(new_sfnt_data, orig_sfnt_data, length); + + OT::name &name = StructAtOffset (new_sfnt_data, name_table_offset); + name.format = 0; + name.count = ARRAY_LENGTH (name_IDs); + name.stringOffset = name.get_size (); + for (unsigned int i = 0; i < ARRAY_LENGTH (name_IDs); i++) + { + OT::NameRecord &record = name.nameRecordZ[i]; + record.platformID = 3; + record.encodingID = 1; + record.languageID = 0x0409u; /* English */ + record.nameID = name_IDs[i]; + record.length = name_str_len * 2; + record.offset = 0; + } + + /* Copy string data from new_name, converting wchar_t to UTF16BE. */ + unsigned char *p = &StructAfter (name); + for (unsigned int i = 0; i < name_str_len; i++) + { + *p++ = new_name[i] >> 8; + *p++ = new_name[i] & 0xff; + } + + /* Adjust name table entry to point to new name table */ + const OT::OpenTypeFontFile &file = * (OT::OpenTypeFontFile *) (new_sfnt_data); + unsigned int face_count = file.get_face_count (); + for (unsigned int face_index = 0; face_index < face_count; face_index++) + { + /* Note: doing multiple edits (ie. TTC) can be unsafe. There may be + * toe-stepping. But we don't really care. */ + const OT::OpenTypeFontFace &face = file.get_face (face_index); + unsigned int index; + if (face.find_table_index (HB_OT_TAG_name, &index)) + { + OT::TableRecord &record = const_cast (face.get_table (index)); + record.checkSum.set_for_data (&name, padded_name_table_length); + record.offset = name_table_offset; + record.length = name_table_length; + } + else if (face_index == 0) /* Fail if first face doesn't have 'name' table. */ + { + hb_free (new_sfnt_data); + hb_blob_destroy (blob); + return nullptr; + } + } + + /* The checkSumAdjustment field in the 'head' table is now wrong, + * but that doesn't actually seem to cause any problems so we don't + * bother. */ + + hb_blob_destroy (blob); + return hb_blob_create ((const char *) new_sfnt_data, new_length, + HB_MEMORY_MODE_WRITABLE, new_sfnt_data, hb_free); +} + +hb_uniscribe_face_data_t * +_hb_uniscribe_shaper_face_data_create (hb_face_t *face) +{ + hb_uniscribe_face_data_t *data = (hb_uniscribe_face_data_t *) hb_calloc (1, sizeof (hb_uniscribe_face_data_t)); + if (unlikely (!data)) + return nullptr; + + data->funcs = hb_uniscribe_shaper_get_funcs (); + if (unlikely (!data->funcs)) + { + hb_free (data); + return nullptr; + } + + hb_blob_t *blob = hb_face_reference_blob (face); + if (unlikely (!hb_blob_get_length (blob))) + DEBUG_MSG (UNISCRIBE, face, "Face has empty blob"); + + blob = _hb_rename_font (blob, data->face_name); + if (unlikely (!blob)) + { + hb_free (data); + return nullptr; + } + + DWORD num_fonts_installed; + data->fh = AddFontMemResourceEx ((void *) hb_blob_get_data (blob, nullptr), + hb_blob_get_length (blob), + 0, &num_fonts_installed); + if (unlikely (!data->fh)) + { + DEBUG_MSG (UNISCRIBE, face, "Face AddFontMemResourceEx() failed"); + hb_free (data); + return nullptr; + } + + return data; +} + +void +_hb_uniscribe_shaper_face_data_destroy (hb_uniscribe_face_data_t *data) +{ + RemoveFontMemResourceEx (data->fh); + hb_free (data); +} + + +/* + * shaper font data + */ + +struct hb_uniscribe_font_data_t +{ + HDC hdc; + mutable LOGFONTW log_font; + HFONT hfont; + mutable SCRIPT_CACHE script_cache; + double x_mult, y_mult; /* From LOGFONT space to HB space. */ +}; + +static bool +populate_log_font (LOGFONTW *lf, + hb_font_t *font, + unsigned int font_size) +{ + memset (lf, 0, sizeof (*lf)); + lf->lfHeight = - (int) font_size; + lf->lfCharSet = DEFAULT_CHARSET; + + memcpy (lf->lfFaceName, font->face->data.uniscribe->face_name, sizeof (lf->lfFaceName)); + + return true; +} + +hb_uniscribe_font_data_t * +_hb_uniscribe_shaper_font_data_create (hb_font_t *font) +{ + hb_uniscribe_font_data_t *data = (hb_uniscribe_font_data_t *) hb_calloc (1, sizeof (hb_uniscribe_font_data_t)); + if (unlikely (!data)) + return nullptr; + + int font_size = font->face->get_upem (); /* Default... */ + /* No idea if the following is even a good idea. */ + if (font->y_ppem) + font_size = font->y_ppem; + + if (font_size < 0) + font_size = -font_size; + data->x_mult = (double) font->x_scale / font_size; + data->y_mult = (double) font->y_scale / font_size; + + data->hdc = GetDC (nullptr); + + if (unlikely (!populate_log_font (&data->log_font, font, font_size))) { + DEBUG_MSG (UNISCRIBE, font, "Font populate_log_font() failed"); + _hb_uniscribe_shaper_font_data_destroy (data); + return nullptr; + } + + data->hfont = CreateFontIndirectW (&data->log_font); + if (unlikely (!data->hfont)) { + DEBUG_MSG (UNISCRIBE, font, "Font CreateFontIndirectW() failed"); + _hb_uniscribe_shaper_font_data_destroy (data); + return nullptr; + } + + if (!SelectObject (data->hdc, data->hfont)) { + DEBUG_MSG (UNISCRIBE, font, "Font SelectObject() failed"); + _hb_uniscribe_shaper_font_data_destroy (data); + return nullptr; + } + + return data; +} + +void +_hb_uniscribe_shaper_font_data_destroy (hb_uniscribe_font_data_t *data) +{ + if (data->hdc) + ReleaseDC (nullptr, data->hdc); + if (data->hfont) + DeleteObject (data->hfont); + if (data->script_cache) + ScriptFreeCache (&data->script_cache); + hb_free (data); +} + +/** + * hb_uniscribe_font_get_logfontw: + * @font: The #hb_font_t to work upon + * + * Fetches the LOGFONTW structure that corresponds to the + * specified #hb_font_t font. + * + * Return value: a pointer to the LOGFONTW retrieved + * + **/ +LOGFONTW * +hb_uniscribe_font_get_logfontw (hb_font_t *font) +{ + const hb_uniscribe_font_data_t *data = font->data.uniscribe; + return data ? &data->log_font : nullptr; +} + +/** + * hb_uniscribe_font_get_hfont: + * @font: The #hb_font_t to work upon + * + * Fetches the HFONT handle that corresponds to the + * specified #hb_font_t font. + * + * Return value: the HFONT retreieved + * + **/ +HFONT +hb_uniscribe_font_get_hfont (hb_font_t *font) +{ + const hb_uniscribe_font_data_t *data = font->data.uniscribe; + return data ? data->hfont : nullptr; +} + + +/* + * shaper + */ + + +hb_bool_t +_hb_uniscribe_shape (hb_shape_plan_t *shape_plan, + hb_font_t *font, + hb_buffer_t *buffer, + const hb_feature_t *features, + unsigned int num_features) +{ + hb_face_t *face = font->face; + const hb_uniscribe_face_data_t *face_data = face->data.uniscribe; + const hb_uniscribe_font_data_t *font_data = font->data.uniscribe; + hb_uniscribe_shaper_funcs_t *funcs = face_data->funcs; + +#define FAIL(...) \ + HB_STMT_START { \ + DEBUG_MSG (UNISCRIBE, nullptr, __VA_ARGS__); \ + return false; \ + } HB_STMT_END + + HRESULT hr; + +retry: + + unsigned int scratch_size; + hb_buffer_t::scratch_buffer_t *scratch = buffer->get_scratch_buffer (&scratch_size); + +#define ALLOCATE_ARRAY(Type, name, len) \ + Type *name = (Type *) scratch; \ + do { \ + unsigned int _consumed = DIV_CEIL ((len) * sizeof (Type), sizeof (*scratch)); \ + assert (_consumed <= scratch_size); \ + scratch += _consumed; \ + scratch_size -= _consumed; \ + } while (0) + +#define utf16_index() var1.u32 + + ALLOCATE_ARRAY (WCHAR, pchars, buffer->len * 2); + + unsigned int chars_len = 0; + for (unsigned int i = 0; i < buffer->len; i++) + { + hb_codepoint_t c = buffer->info[i].codepoint; + buffer->info[i].utf16_index() = chars_len; + if (likely (c <= 0xFFFFu)) + pchars[chars_len++] = c; + else if (unlikely (c > 0x10FFFFu)) + pchars[chars_len++] = 0xFFFDu; + else { + pchars[chars_len++] = 0xD800u + ((c - 0x10000u) >> 10); + pchars[chars_len++] = 0xDC00u + ((c - 0x10000u) & ((1u << 10) - 1)); + } + } + + ALLOCATE_ARRAY (WORD, log_clusters, chars_len); + ALLOCATE_ARRAY (SCRIPT_CHARPROP, char_props, chars_len); + + if (num_features) + { + /* Need log_clusters to assign features. */ + chars_len = 0; + for (unsigned int i = 0; i < buffer->len; i++) + { + hb_codepoint_t c = buffer->info[i].codepoint; + unsigned int cluster = buffer->info[i].cluster; + log_clusters[chars_len++] = cluster; + if (hb_in_range (c, 0x10000u, 0x10FFFFu)) + log_clusters[chars_len++] = cluster; /* Surrogates. */ + } + } + + /* The -2 in the following is to compensate for possible + * alignment needed after the WORD array. sizeof(WORD) == 2. */ + unsigned int glyphs_size = (scratch_size * sizeof (int) - 2) + / (sizeof (WORD) + + sizeof (SCRIPT_GLYPHPROP) + + sizeof (int) + + sizeof (GOFFSET) + + sizeof (uint32_t)); + + ALLOCATE_ARRAY (WORD, glyphs, glyphs_size); + ALLOCATE_ARRAY (SCRIPT_GLYPHPROP, glyph_props, glyphs_size); + ALLOCATE_ARRAY (int, advances, glyphs_size); + ALLOCATE_ARRAY (GOFFSET, offsets, glyphs_size); + ALLOCATE_ARRAY (uint32_t, vis_clusters, glyphs_size); + + /* Note: + * We can't touch the contents of glyph_props. Our fallback + * implementations of Shape and Place functions use that buffer + * by casting it to a different type. It works because they + * both agree about it, but if we want to access it here we + * need address that issue first. + */ + +#undef ALLOCATE_ARRAY + +#define MAX_ITEMS 256 + + SCRIPT_ITEM items[MAX_ITEMS + 1]; + SCRIPT_CONTROL bidi_control = {0}; + SCRIPT_STATE bidi_state = {0}; + ULONG script_tags[MAX_ITEMS]; + int item_count; + + /* MinGW32 doesn't define fMergeNeutralItems, so we bruteforce */ + //bidi_control.fMergeNeutralItems = true; + *(uint32_t*)&bidi_control |= 1u<<24; + + bidi_state.uBidiLevel = HB_DIRECTION_IS_FORWARD (buffer->props.direction) ? 0 : 1; + bidi_state.fOverrideDirection = 1; + + hr = funcs->ScriptItemizeOpenType (pchars, + chars_len, + MAX_ITEMS, + &bidi_control, + &bidi_state, + items, + script_tags, + &item_count); + if (unlikely (FAILED (hr))) + FAIL ("ScriptItemizeOpenType() failed: 0x%08lx", hr); + +#undef MAX_ITEMS + + hb_tag_t lang_tag; + unsigned int lang_count = 1; + hb_ot_tags_from_script_and_language (buffer->props.script, + buffer->props.language, + nullptr, nullptr, + &lang_count, &lang_tag); + OPENTYPE_TAG language_tag = hb_uint32_swap (lang_count ? lang_tag : HB_TAG_NONE); + + /* + * Set up features. + */ + static_assert ((sizeof (TEXTRANGE_PROPERTIES) == sizeof (hb_ms_features_t)), ""); + static_assert ((sizeof (OPENTYPE_FEATURE_RECORD) == sizeof (hb_ms_feature_t)), ""); + hb_vector_t feature_records; + hb_vector_t range_records; + bool has_features = false; + if (num_features) + has_features = hb_ms_setup_features (features, + num_features, + feature_records, + range_records); + + hb_vector_t range_properties; + hb_vector_t range_char_counts; + + unsigned int glyphs_offset = 0; + unsigned int glyphs_len; + bool backward = HB_DIRECTION_IS_BACKWARD (buffer->props.direction); + for (int i = 0; i < item_count; i++) + { + unsigned int chars_offset = items[i].iCharPos; + unsigned int item_chars_len = items[i + 1].iCharPos - chars_offset; + + if (has_features) + hb_ms_make_feature_ranges (feature_records, + range_records, + item_chars_len, + chars_offset, + log_clusters, + range_properties, + range_char_counts); + + /* Asking for glyphs in logical order circumvents at least + * one bug in Uniscribe. */ + items[i].a.fLogicalOrder = true; + + retry_shape: + hr = funcs->ScriptShapeOpenType (font_data->hdc, + &font_data->script_cache, + &items[i].a, + script_tags[i], + language_tag, + (int *) range_char_counts.arrayZ, + (TEXTRANGE_PROPERTIES**) range_properties.arrayZ, + range_properties.length, + pchars + chars_offset, + item_chars_len, + glyphs_size - glyphs_offset, + /* out */ + log_clusters + chars_offset, + char_props + chars_offset, + glyphs + glyphs_offset, + glyph_props + glyphs_offset, + (int *) &glyphs_len); + + if (unlikely (items[i].a.fNoGlyphIndex)) + FAIL ("ScriptShapeOpenType() set fNoGlyphIndex"); + if (unlikely (hr == E_OUTOFMEMORY || hr == E_NOT_SUFFICIENT_BUFFER)) + { + if (unlikely (!buffer->ensure (buffer->allocated * 2))) + FAIL ("Buffer resize failed"); + goto retry; + } + if (unlikely (hr == USP_E_SCRIPT_NOT_IN_FONT)) + { + if (items[i].a.eScript == SCRIPT_UNDEFINED) + FAIL ("ScriptShapeOpenType() failed: Font doesn't support script"); + items[i].a.eScript = SCRIPT_UNDEFINED; + goto retry_shape; + } + if (unlikely (FAILED (hr))) + { + FAIL ("ScriptShapeOpenType() failed: 0x%08lx", hr); + } + + for (unsigned int j = chars_offset; j < chars_offset + item_chars_len; j++) + log_clusters[j] += glyphs_offset; + + hr = funcs->ScriptPlaceOpenType (font_data->hdc, + &font_data->script_cache, + &items[i].a, + script_tags[i], + language_tag, + (int *) range_char_counts.arrayZ, + (TEXTRANGE_PROPERTIES**) range_properties.arrayZ, + range_properties.length, + pchars + chars_offset, + log_clusters + chars_offset, + char_props + chars_offset, + item_chars_len, + glyphs + glyphs_offset, + glyph_props + glyphs_offset, + glyphs_len, + /* out */ + advances + glyphs_offset, + offsets + glyphs_offset, + nullptr); + if (unlikely (FAILED (hr))) + FAIL ("ScriptPlaceOpenType() failed: 0x%08lx", hr); + + if (DEBUG_ENABLED (UNISCRIBE)) + fprintf (stderr, "Item %d RTL %d LayoutRTL %d LogicalOrder %d ScriptTag %c%c%c%c\n", + i, + items[i].a.fRTL, + items[i].a.fLayoutRTL, + items[i].a.fLogicalOrder, + HB_UNTAG (hb_uint32_swap (script_tags[i]))); + + glyphs_offset += glyphs_len; + } + glyphs_len = glyphs_offset; + + /* Ok, we've got everything we need, now compose output buffer, + * very, *very*, carefully! */ + + /* Calculate visual-clusters. That's what we ship. */ + for (unsigned int i = 0; i < glyphs_len; i++) + vis_clusters[i] = (uint32_t) -1; + for (unsigned int i = 0; i < buffer->len; i++) { + uint32_t *p = &vis_clusters[log_clusters[buffer->info[i].utf16_index()]]; + *p = hb_min (*p, buffer->info[i].cluster); + } + for (unsigned int i = 1; i < glyphs_len; i++) + if (vis_clusters[i] == (uint32_t) -1) + vis_clusters[i] = vis_clusters[i - 1]; + +#undef utf16_index + + if (unlikely (!buffer->ensure (glyphs_len))) + FAIL ("Buffer in error"); + +#undef FAIL + + /* Set glyph infos */ + buffer->len = 0; + for (unsigned int i = 0; i < glyphs_len; i++) + { + hb_glyph_info_t *info = &buffer->info[buffer->len++]; + + info->codepoint = glyphs[i]; + info->cluster = vis_clusters[i]; + + /* The rest is crap. Let's store position info there for now. */ + info->mask = advances[i]; + info->var1.i32 = offsets[i].du; + info->var2.i32 = offsets[i].dv; + } + + /* Set glyph positions */ + buffer->clear_positions (); + double x_mult = font_data->x_mult, y_mult = font_data->y_mult; + for (unsigned int i = 0; i < glyphs_len; i++) + { + hb_glyph_info_t *info = &buffer->info[i]; + hb_glyph_position_t *pos = &buffer->pos[i]; + + /* TODO vertical */ + pos->x_advance = x_mult * (int32_t) info->mask; + pos->x_offset = x_mult * (backward ? -info->var1.i32 : info->var1.i32); + pos->y_offset = y_mult * info->var2.i32; + } + + if (backward) + hb_buffer_reverse (buffer); + + buffer->clear_glyph_flags (); + buffer->unsafe_to_break (); + + /* Wow, done! */ + return true; +} + + +#endif diff --git a/libs/harfbuzz/src/hb-uniscribe.h b/libs/harfbuzz/src/hb-uniscribe.h new file mode 100644 index 00000000..4e4ef998 --- /dev/null +++ b/libs/harfbuzz/src/hb-uniscribe.h @@ -0,0 +1,46 @@ +/* + * 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 HB_UNISCRIBE_H +#define HB_UNISCRIBE_H + +#include "hb.h" + +#include + +HB_BEGIN_DECLS + + +HB_EXTERN LOGFONTW * +hb_uniscribe_font_get_logfontw (hb_font_t *font); + +HB_EXTERN HFONT +hb_uniscribe_font_get_hfont (hb_font_t *font); + + +HB_END_DECLS + +#endif /* HB_UNISCRIBE_H */ diff --git a/libs/harfbuzz/src/hb-utf.hh b/libs/harfbuzz/src/hb-utf.hh new file mode 100644 index 00000000..ff5712d1 --- /dev/null +++ b/libs/harfbuzz/src/hb-utf.hh @@ -0,0 +1,453 @@ +/* + * Copyright © 2011,2012,2014 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 HB_UTF_HH +#define HB_UTF_HH + +#include "hb.hh" + +#include "hb-open-type.hh" + + +struct hb_utf8_t +{ + typedef uint8_t codepoint_t; + + static const codepoint_t * + next (const codepoint_t *text, + const codepoint_t *end, + hb_codepoint_t *unicode, + hb_codepoint_t replacement) + { + /* Written to only accept well-formed sequences. + * Based on ideas from ICU's U8_NEXT. + * Generates one "replacement" for each ill-formed byte. */ + + hb_codepoint_t c = *text++; + + if (c > 0x7Fu) + { + if (hb_in_range (c, 0xC2u, 0xDFu)) /* Two-byte */ + { + unsigned int t1; + if (likely (text < end && + (t1 = text[0] - 0x80u) <= 0x3Fu)) + { + c = ((c&0x1Fu)<<6) | t1; + text++; + } + else + goto error; + } + else if (hb_in_range (c, 0xE0u, 0xEFu)) /* Three-byte */ + { + unsigned int t1, t2; + if (likely (1 < end - text && + (t1 = text[0] - 0x80u) <= 0x3Fu && + (t2 = text[1] - 0x80u) <= 0x3Fu)) + { + c = ((c&0xFu)<<12) | (t1<<6) | t2; + if (unlikely (c < 0x0800u || hb_in_range (c, 0xD800u, 0xDFFFu))) + goto error; + text += 2; + } + else + goto error; + } + else if (hb_in_range (c, 0xF0u, 0xF4u)) /* Four-byte */ + { + unsigned int t1, t2, t3; + if (likely (2 < end - text && + (t1 = text[0] - 0x80u) <= 0x3Fu && + (t2 = text[1] - 0x80u) <= 0x3Fu && + (t3 = text[2] - 0x80u) <= 0x3Fu)) + { + c = ((c&0x7u)<<18) | (t1<<12) | (t2<<6) | t3; + if (unlikely (!hb_in_range (c, 0x10000u, 0x10FFFFu))) + goto error; + text += 3; + } + else + goto error; + } + else + goto error; + } + + *unicode = c; + return text; + + error: + *unicode = replacement; + return text; + } + + static const codepoint_t * + prev (const codepoint_t *text, + const codepoint_t *start, + hb_codepoint_t *unicode, + hb_codepoint_t replacement) + { + const codepoint_t *end = text--; + while (start < text && (*text & 0xc0) == 0x80 && end - text < 4) + text--; + + if (likely (next (text, end, unicode, replacement) == end)) + return text; + + *unicode = replacement; + return end - 1; + } + + static unsigned int + strlen (const codepoint_t *text) + { return ::strlen ((const char *) text); } + + static unsigned int + encode_len (hb_codepoint_t unicode) + { + if (unicode < 0x0080u) return 1; + if (unicode < 0x0800u) return 2; + if (unicode < 0x10000u) return 3; + if (unicode < 0x110000u) return 4; + return 3; + } + + static codepoint_t * + encode (codepoint_t *text, + const codepoint_t *end, + hb_codepoint_t unicode) + { + if (unlikely (unicode >= 0xD800u && (unicode <= 0xDFFFu || unicode > 0x10FFFFu))) + unicode = 0xFFFDu; + if (unicode < 0x0080u) + *text++ = unicode; + else if (unicode < 0x0800u) + { + if (end - text >= 2) + { + *text++ = 0xC0u + (0x1Fu & (unicode >> 6)); + *text++ = 0x80u + (0x3Fu & (unicode )); + } + } + else if (unicode < 0x10000u) + { + if (end - text >= 3) + { + *text++ = 0xE0u + (0x0Fu & (unicode >> 12)); + *text++ = 0x80u + (0x3Fu & (unicode >> 6)); + *text++ = 0x80u + (0x3Fu & (unicode )); + } + } + else + { + if (end - text >= 4) + { + *text++ = 0xF0u + (0x07u & (unicode >> 18)); + *text++ = 0x80u + (0x3Fu & (unicode >> 12)); + *text++ = 0x80u + (0x3Fu & (unicode >> 6)); + *text++ = 0x80u + (0x3Fu & (unicode )); + } + } + return text; + } +}; + + +template +struct hb_utf16_xe_t +{ + static_assert (sizeof (TCodepoint) == 2, ""); + typedef TCodepoint codepoint_t; + + static const codepoint_t * + next (const codepoint_t *text, + const codepoint_t *end, + hb_codepoint_t *unicode, + hb_codepoint_t replacement) + { + hb_codepoint_t c = *text++; + + if (likely (!hb_in_range (c, 0xD800u, 0xDFFFu))) + { + *unicode = c; + return text; + } + + if (likely (c <= 0xDBFFu && text < end)) + { + /* High-surrogate in c */ + hb_codepoint_t l = *text; + if (likely (hb_in_range (l, 0xDC00u, 0xDFFFu))) + { + /* Low-surrogate in l */ + *unicode = (c << 10) + l - ((0xD800u << 10) - 0x10000u + 0xDC00u); + text++; + return text; + } + } + + /* Lonely / out-of-order surrogate. */ + *unicode = replacement; + return text; + } + + static const codepoint_t * + prev (const codepoint_t *text, + const codepoint_t *start, + hb_codepoint_t *unicode, + hb_codepoint_t replacement) + { + hb_codepoint_t c = *--text; + + if (likely (!hb_in_range (c, 0xD800u, 0xDFFFu))) + { + *unicode = c; + return text; + } + + if (likely (c >= 0xDC00u && start < text)) + { + /* Low-surrogate in c */ + hb_codepoint_t h = text[-1]; + if (likely (hb_in_range (h, 0xD800u, 0xDBFFu))) + { + /* High-surrogate in h */ + *unicode = (h << 10) + c - ((0xD800u << 10) - 0x10000u + 0xDC00u); + text--; + return text; + } + } + + /* Lonely / out-of-order surrogate. */ + *unicode = replacement; + return text; + } + + + static unsigned int + strlen (const codepoint_t *text) + { + unsigned int l = 0; + while (*text++) l++; + return l; + } + + static unsigned int + encode_len (hb_codepoint_t unicode) + { + return unicode < 0x10000 ? 1 : 2; + } + + static codepoint_t * + encode (codepoint_t *text, + const codepoint_t *end, + hb_codepoint_t unicode) + { + if (unlikely (unicode >= 0xD800u && (unicode <= 0xDFFFu || unicode > 0x10FFFFu))) + unicode = 0xFFFDu; + if (unicode < 0x10000u) + *text++ = unicode; + else if (end - text >= 2) + { + unicode -= 0x10000u; + *text++ = 0xD800u + (unicode >> 10); + *text++ = 0xDC00u + (unicode & 0x03FFu); + } + return text; + } +}; + +typedef hb_utf16_xe_t hb_utf16_t; +typedef hb_utf16_xe_t hb_utf16_be_t; + + +template +struct hb_utf32_xe_t +{ + static_assert (sizeof (TCodepoint) == 4, ""); + typedef TCodepoint codepoint_t; + + static const TCodepoint * + next (const TCodepoint *text, + const TCodepoint *end HB_UNUSED, + hb_codepoint_t *unicode, + hb_codepoint_t replacement) + { + hb_codepoint_t c = *unicode = *text++; + if (validate && unlikely (c >= 0xD800u && (c <= 0xDFFFu || c > 0x10FFFFu))) + *unicode = replacement; + return text; + } + + static const TCodepoint * + prev (const TCodepoint *text, + const TCodepoint *start HB_UNUSED, + hb_codepoint_t *unicode, + hb_codepoint_t replacement) + { + hb_codepoint_t c = *unicode = *--text; + if (validate && unlikely (c >= 0xD800u && (c <= 0xDFFFu || c > 0x10FFFFu))) + *unicode = replacement; + return text; + } + + static unsigned int + strlen (const TCodepoint *text) + { + unsigned int l = 0; + while (*text++) l++; + return l; + } + + static unsigned int + encode_len (hb_codepoint_t unicode HB_UNUSED) + { + return 1; + } + + static codepoint_t * + encode (codepoint_t *text, + const codepoint_t *end HB_UNUSED, + hb_codepoint_t unicode) + { + if (validate && unlikely (unicode >= 0xD800u && (unicode <= 0xDFFFu || unicode > 0x10FFFFu))) + unicode = 0xFFFDu; + *text++ = unicode; + return text; + } +}; + +typedef hb_utf32_xe_t hb_utf32_t; +typedef hb_utf32_xe_t hb_utf32_novalidate_t; + + +struct hb_latin1_t +{ + typedef uint8_t codepoint_t; + + static const codepoint_t * + next (const codepoint_t *text, + const codepoint_t *end HB_UNUSED, + hb_codepoint_t *unicode, + hb_codepoint_t replacement HB_UNUSED) + { + *unicode = *text++; + return text; + } + + static const codepoint_t * + prev (const codepoint_t *text, + const codepoint_t *start HB_UNUSED, + hb_codepoint_t *unicode, + hb_codepoint_t replacement HB_UNUSED) + { + *unicode = *--text; + return text; + } + + static unsigned int + strlen (const codepoint_t *text) + { + unsigned int l = 0; + while (*text++) l++; + return l; + } + + static unsigned int + encode_len (hb_codepoint_t unicode HB_UNUSED) + { + return 1; + } + + static codepoint_t * + encode (codepoint_t *text, + const codepoint_t *end HB_UNUSED, + hb_codepoint_t unicode) + { + if (unlikely (unicode >= 0x0100u)) + unicode = '?'; + *text++ = unicode; + return text; + } +}; + + +struct hb_ascii_t +{ + typedef uint8_t codepoint_t; + + static const codepoint_t * + next (const codepoint_t *text, + const codepoint_t *end HB_UNUSED, + hb_codepoint_t *unicode, + hb_codepoint_t replacement HB_UNUSED) + { + *unicode = *text++; + if (*unicode >= 0x0080u) + *unicode = replacement; + return text; + } + + static const codepoint_t * + prev (const codepoint_t *text, + const codepoint_t *start HB_UNUSED, + hb_codepoint_t *unicode, + hb_codepoint_t replacement) + { + *unicode = *--text; + if (*unicode >= 0x0080u) + *unicode = replacement; + return text; + } + + static unsigned int + strlen (const codepoint_t *text) + { + unsigned int l = 0; + while (*text++) l++; + return l; + } + + static unsigned int + encode_len (hb_codepoint_t unicode HB_UNUSED) + { + return 1; + } + + static codepoint_t * + encode (codepoint_t *text, + const codepoint_t *end HB_UNUSED, + hb_codepoint_t unicode) + { + if (unlikely (unicode >= 0x0080u)) + unicode = '?'; + *text++ = unicode; + return text; + } +}; + +#endif /* HB_UTF_HH */ diff --git a/libs/harfbuzz/src/hb-vector.hh b/libs/harfbuzz/src/hb-vector.hh new file mode 100644 index 00000000..a6d9f6b3 --- /dev/null +++ b/libs/harfbuzz/src/hb-vector.hh @@ -0,0 +1,464 @@ +/* + * Copyright © 2017,2018 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 HB_VECTOR_HH +#define HB_VECTOR_HH + +#include "hb.hh" +#include "hb-array.hh" +#include "hb-meta.hh" +#include "hb-null.hh" + + +template +struct hb_vector_t : std::conditional, hb_empty_t>::type +{ + typedef Type item_t; + static constexpr unsigned item_size = hb_static_size (Type); + using array_t = typename std::conditional, hb_array_t>::type; + using c_array_t = typename std::conditional, hb_array_t>::type; + + hb_vector_t () = default; + hb_vector_t (std::initializer_list lst) : hb_vector_t () + { + alloc (lst.size ()); + for (auto&& item : lst) + push (item); + } + template + hb_vector_t (const Iterable &o) : hb_vector_t () + { + if (hb_iter (o).is_random_access_iterator) + alloc (hb_len (hb_iter (o))); + hb_copy (o, *this); + } + hb_vector_t (const hb_vector_t &o) : hb_vector_t () + { + alloc (o.length); + if (unlikely (in_error ())) return; + copy_vector (o); + } + hb_vector_t (hb_vector_t &&o) + { + allocated = o.allocated; + length = o.length; + arrayZ = o.arrayZ; + o.init (); + } + ~hb_vector_t () { fini (); } + + public: + int allocated = 0; /* == -1 means allocation failed. */ + unsigned int length = 0; + public: + Type *arrayZ = nullptr; + + void init () + { + allocated = length = 0; + arrayZ = nullptr; + } + + void fini () + { + shrink_vector (0); + hb_free (arrayZ); + init (); + } + + void reset () + { + if (unlikely (in_error ())) + allocated = length; // Big hack! + resize (0); + } + + friend void swap (hb_vector_t& a, hb_vector_t& b) + { + hb_swap (a.allocated, b.allocated); + hb_swap (a.length, b.length); + hb_swap (a.arrayZ, b.arrayZ); + } + + hb_vector_t& operator = (const hb_vector_t &o) + { + reset (); + alloc (o.length); + if (unlikely (in_error ())) return *this; + + copy_vector (o); + + return *this; + } + hb_vector_t& operator = (hb_vector_t &&o) + { + hb_swap (*this, o); + return *this; + } + + hb_bytes_t as_bytes () const + { return hb_bytes_t ((const char *) arrayZ, length * item_size); } + + bool operator == (const hb_vector_t &o) const { return as_array () == o.as_array (); } + bool operator != (const hb_vector_t &o) const { return !(*this == o); } + uint32_t hash () const { return as_array ().hash (); } + + Type& operator [] (int i_) + { + unsigned int i = (unsigned int) i_; + if (unlikely (i >= length)) + return Crap (Type); + return arrayZ[i]; + } + const Type& operator [] (int i_) const + { + unsigned int i = (unsigned int) i_; + if (unlikely (i >= length)) + return Null (Type); + return arrayZ[i]; + } + + Type& tail () { return (*this)[length - 1]; } + const Type& tail () const { return (*this)[length - 1]; } + + explicit operator bool () const { return length; } + unsigned get_size () const { return length * item_size; } + + /* Sink interface. */ + template + hb_vector_t& operator << (T&& v) { push (std::forward (v)); return *this; } + + array_t as_array () { return hb_array (arrayZ, length); } + c_array_t as_array () const { return hb_array (arrayZ, length); } + + /* Iterator. */ + typedef c_array_t iter_t; + typedef array_t writer_t; + iter_t iter () const { return as_array (); } + writer_t writer () { return as_array (); } + operator iter_t () const { return iter (); } + operator writer_t () { return writer (); } + + c_array_t sub_array (unsigned int start_offset, unsigned int count) const + { return as_array ().sub_array (start_offset, count); } + c_array_t sub_array (unsigned int start_offset, unsigned int *count = nullptr /* IN/OUT */) const + { return as_array ().sub_array (start_offset, count); } + array_t sub_array (unsigned int start_offset, unsigned int count) + { return as_array ().sub_array (start_offset, count); } + array_t sub_array (unsigned int start_offset, unsigned int *count = nullptr /* IN/OUT */) + { return as_array ().sub_array (start_offset, count); } + + hb_sorted_array_t as_sorted_array () + { return hb_sorted_array (arrayZ, length); } + hb_sorted_array_t as_sorted_array () const + { return hb_sorted_array (arrayZ, length); } + + template explicit operator T * () { return arrayZ; } + template explicit operator const T * () const { return arrayZ; } + + Type * operator + (unsigned int i) { return arrayZ + i; } + const Type * operator + (unsigned int i) const { return arrayZ + i; } + + Type *push () + { + if (unlikely (!resize (length + 1))) + return &Crap (Type); + return std::addressof (arrayZ[length - 1]); + } + template ::value && + std::is_copy_assignable::value)> + Type *push (T&& v) + { + Type *p = push (); + if (p == &Crap (Type)) + // If push failed to allocate then don't copy v, since this may cause + // the created copy to leak memory since we won't have stored a + // reference to it. + return p; + *p = std::forward (v); + return p; + } + template ::value)> + Type *push (T&& v) + { + if (unlikely (!alloc (length + 1))) + // If push failed to allocate then don't copy v, since this may cause + // the created copy to leak memory since we won't have stored a + // reference to it. + return &Crap (Type); + + /* Emplace. */ + length++; + Type *p = std::addressof (arrayZ[length - 1]); + return new (p) Type (std::forward (v)); + } + + bool in_error () const { return allocated < 0; } + + template + Type * + realloc_vector (unsigned new_allocated) + { + return (Type *) hb_realloc (arrayZ, new_allocated * sizeof (Type)); + } + template + Type * + realloc_vector (unsigned new_allocated) + { + Type *new_array = (Type *) hb_malloc (new_allocated * sizeof (Type)); + if (likely (new_array)) + { + for (unsigned i = 0; i < length; i++) + new (std::addressof (new_array[i])) Type (); + for (unsigned i = 0; i < (unsigned) length; i++) + new_array[i] = std::move (arrayZ[i]); + unsigned old_length = length; + shrink_vector (0); + length = old_length; + hb_free (arrayZ); + } + return new_array; + } + + template + void + grow_vector (unsigned size) + { + memset (arrayZ + length, 0, (size - length) * sizeof (*arrayZ)); + length = size; + } + template + void + grow_vector (unsigned size) + { + while (length < size) + { + length++; + new (std::addressof (arrayZ[length - 1])) Type (); + } + } + + template + void + copy_vector (const hb_vector_t &other) + { + length = other.length; + hb_memcpy ((void *) arrayZ, (const void *) other.arrayZ, length * item_size); + } + template ::value)> + void + copy_vector (const hb_vector_t &other) + { + length = 0; + while (length < other.length) + { + length++; + new (std::addressof (arrayZ[length - 1])) Type (other.arrayZ[length - 1]); + } + } + template ::value && + std::is_default_constructible::value && + std::is_copy_assignable::value)> + void + copy_vector (const hb_vector_t &other) + { + length = 0; + while (length < other.length) + { + length++; + new (std::addressof (arrayZ[length - 1])) Type (); + arrayZ[length - 1] = other.arrayZ[length - 1]; + } + } + + template + void + shrink_vector (unsigned size) + { + length = size; + } + template + void + shrink_vector (unsigned size) + { + while ((unsigned) length > size) + { + arrayZ[(unsigned) length - 1].~Type (); + length--; + } + } + + template + void + shift_down_vector (unsigned i) + { + memmove (static_cast (&arrayZ[i - 1]), + static_cast (&arrayZ[i]), + (length - i) * sizeof (Type)); + } + template + void + shift_down_vector (unsigned i) + { + for (; i < length; i++) + arrayZ[i - 1] = std::move (arrayZ[i]); + } + + /* Allocate for size but don't adjust length. */ + bool alloc (unsigned int size) + { + if (unlikely (in_error ())) + return false; + + if (likely (size <= (unsigned) allocated)) + return true; + + /* Reallocate */ + + unsigned int new_allocated = allocated; + while (size >= new_allocated) + new_allocated += (new_allocated >> 1) + 8; + + Type *new_array = nullptr; + bool overflows = + (int) in_error () || + (new_allocated < (unsigned) allocated) || + hb_unsigned_mul_overflows (new_allocated, sizeof (Type)); + if (likely (!overflows)) + new_array = realloc_vector (new_allocated); + + if (unlikely (!new_array)) + { + allocated = -1; + return false; + } + + arrayZ = new_array; + allocated = new_allocated; + + return true; + } + + bool resize (int size_) + { + unsigned int size = size_ < 0 ? 0u : (unsigned int) size_; + if (!alloc (size)) + return false; + + if (size > length) + grow_vector (size); + else if (size < length) + shrink_vector (size); + + length = size; + return true; + } + + Type pop () + { + if (!length) return Null (Type); + Type v = arrayZ[length - 1]; + arrayZ[length - 1].~Type (); + length--; + return v; + } + + void remove (unsigned int i) + { + if (unlikely (i >= length)) + return; + shift_down_vector (i + 1); + arrayZ[length - 1].~Type (); + length--; + } + + void shrink (int size_) + { + unsigned int size = size_ < 0 ? 0u : (unsigned int) size_; + if (size >= length) + return; + + shrink_vector (size); + } + + + /* Sorting API. */ + void qsort (int (*cmp)(const void*, const void*)) + { as_array ().qsort (cmp); } + void qsort (unsigned int start = 0, unsigned int end = (unsigned int) -1) + { as_array ().qsort (start, end); } + + /* Unsorted search API. */ + template + Type *lsearch (const T &x, Type *not_found = nullptr) + { return as_array ().lsearch (x, not_found); } + template + const Type *lsearch (const T &x, const Type *not_found = nullptr) const + { return as_array ().lsearch (x, not_found); } + template + bool lfind (const T &x, unsigned *pos = nullptr) const + { return as_array ().lfind (x, pos); } + + /* Sorted search API. */ + template + Type *bsearch (const T &x, Type *not_found = nullptr) + { return as_array ().bsearch (x, not_found); } + template + const Type *bsearch (const T &x, const Type *not_found = nullptr) const + { return as_array ().bsearch (x, not_found); } + template + bool bfind (const T &x, unsigned int *i = nullptr, + hb_not_found_t not_found = HB_NOT_FOUND_DONT_STORE, + unsigned int to_store = (unsigned int) -1) const + { return as_array ().bfind (x, i, not_found, to_store); } +}; + +template +using hb_sorted_vector_t = hb_vector_t; + +#endif /* HB_VECTOR_HH */ diff --git a/libs/harfbuzz/src/hb-version.h b/libs/harfbuzz/src/hb-version.h new file mode 100644 index 00000000..8d6ec66e --- /dev/null +++ b/libs/harfbuzz/src/hb-version.h @@ -0,0 +1,95 @@ +/* + * 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 + */ + +#if !defined(HB_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR) +#error "Include instead." +#endif + +#ifndef HB_VERSION_H +#define HB_VERSION_H + +#include "hb-common.h" + +HB_BEGIN_DECLS + + +/** + * HB_VERSION_MAJOR: + * + * The major component of the library version available at compile-time. + */ +#define HB_VERSION_MAJOR 5 +/** + * HB_VERSION_MINOR: + * + * The minor component of the library version available at compile-time. + */ +#define HB_VERSION_MINOR 3 +/** + * HB_VERSION_MICRO: + * + * The micro component of the library version available at compile-time. + */ +#define HB_VERSION_MICRO 1 + +/** + * HB_VERSION_STRING: + * + * A string literal containing the library version available at compile-time. + */ +#define HB_VERSION_STRING "5.3.1" + +/** + * HB_VERSION_ATLEAST: + * @major: the major component of the version number + * @minor: the minor component of the version number + * @micro: the micro component of the version number + * + * Tests the library version at compile-time against a minimum value, + * as three integer components. + */ +#define HB_VERSION_ATLEAST(major,minor,micro) \ + ((major)*10000+(minor)*100+(micro) <= \ + HB_VERSION_MAJOR*10000+HB_VERSION_MINOR*100+HB_VERSION_MICRO) + + +HB_EXTERN void +hb_version (unsigned int *major, + unsigned int *minor, + unsigned int *micro); + +HB_EXTERN const char * +hb_version_string (void); + +HB_EXTERN hb_bool_t +hb_version_atleast (unsigned int major, + unsigned int minor, + unsigned int micro); + + +HB_END_DECLS + +#endif /* HB_VERSION_H */ diff --git a/libs/harfbuzz/src/hb-version.h.in b/libs/harfbuzz/src/hb-version.h.in new file mode 100644 index 00000000..abcb73f4 --- /dev/null +++ b/libs/harfbuzz/src/hb-version.h.in @@ -0,0 +1,95 @@ +/* + * 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 + */ + +#if !defined(HB_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR) +#error "Include instead." +#endif + +#ifndef HB_VERSION_H +#define HB_VERSION_H + +#include "hb-common.h" + +HB_BEGIN_DECLS + + +/** + * HB_VERSION_MAJOR: + * + * The major component of the library version available at compile-time. + */ +#define HB_VERSION_MAJOR @HB_VERSION_MAJOR@ +/** + * HB_VERSION_MINOR: + * + * The minor component of the library version available at compile-time. + */ +#define HB_VERSION_MINOR @HB_VERSION_MINOR@ +/** + * HB_VERSION_MICRO: + * + * The micro component of the library version available at compile-time. + */ +#define HB_VERSION_MICRO @HB_VERSION_MICRO@ + +/** + * HB_VERSION_STRING: + * + * A string literal containing the library version available at compile-time. + */ +#define HB_VERSION_STRING "@HB_VERSION@" + +/** + * HB_VERSION_ATLEAST: + * @major: the major component of the version number + * @minor: the minor component of the version number + * @micro: the micro component of the version number + * + * Tests the library version at compile-time against a minimum value, + * as three integer components. + */ +#define HB_VERSION_ATLEAST(major,minor,micro) \ + ((major)*10000+(minor)*100+(micro) <= \ + HB_VERSION_MAJOR*10000+HB_VERSION_MINOR*100+HB_VERSION_MICRO) + + +HB_EXTERN void +hb_version (unsigned int *major, + unsigned int *minor, + unsigned int *micro); + +HB_EXTERN const char * +hb_version_string (void); + +HB_EXTERN hb_bool_t +hb_version_atleast (unsigned int major, + unsigned int minor, + unsigned int micro); + + +HB_END_DECLS + +#endif /* HB_VERSION_H */ diff --git a/libs/harfbuzz/src/hb.h b/libs/harfbuzz/src/hb.h new file mode 100644 index 00000000..360686ca --- /dev/null +++ b/libs/harfbuzz/src/hb.h @@ -0,0 +1,50 @@ +/* + * Copyright © 2009 Red Hat, 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. + * + * Red Hat Author(s): Behdad Esfahbod + */ + +#ifndef HB_H +#define HB_H +#define HB_H_IN + +#include "hb-blob.h" +#include "hb-buffer.h" +#include "hb-common.h" +#include "hb-deprecated.h" +#include "hb-draw.h" +#include "hb-face.h" +#include "hb-font.h" +#include "hb-map.h" +#include "hb-set.h" +#include "hb-shape.h" +#include "hb-shape-plan.h" +#include "hb-style.h" +#include "hb-unicode.h" +#include "hb-version.h" + +HB_BEGIN_DECLS +HB_END_DECLS + +#undef HB_H_IN +#endif /* HB_H */ diff --git a/libs/harfbuzz/src/hb.hh b/libs/harfbuzz/src/hb.hh new file mode 100644 index 00000000..8ec638a2 --- /dev/null +++ b/libs/harfbuzz/src/hb.hh @@ -0,0 +1,486 @@ +/* + * Copyright © 2007,2008,2009 Red Hat, Inc. + * Copyright © 2011,2012 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. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_HH +#define HB_HH + +#ifndef HB_NO_PRAGMA_GCC_DIAGNOSTIC +#ifdef _MSC_VER +#pragma warning( disable: 4068 ) /* Unknown pragma */ +#endif +#if defined(__GNUC__) || defined(__clang__) +/* Rules: + * + * - All pragmas are declared GCC even if they are clang ones. Otherwise GCC + * nags, even though we instruct it to ignore -Wunknown-pragmas. ¯\_(ツ)_/¯ + * + * - Within each category, keep sorted. + * + * - Warnings whose scope can be expanded in future compiler versions shall + * be declared as "warning". Otherwise, either ignored or error. + */ + +/* Setup. Don't sort order within this category. */ +#ifndef HB_NO_PRAGMA_GCC_DIAGNOSTIC_WARNING +#pragma GCC diagnostic warning "-Wall" +#pragma GCC diagnostic warning "-Wextra" +#endif +#ifndef HB_NO_PRAGMA_GCC_DIAGNOSTIC_IGNORED +#pragma GCC diagnostic ignored "-Wpragmas" +#pragma GCC diagnostic ignored "-Wunknown-pragmas" +#pragma GCC diagnostic ignored "-Wunknown-warning-option" +#endif +#ifndef HB_NO_PRAGMA_GCC_DIAGNOSTIC_WARNING +//#pragma GCC diagnostic warning "-Weverything" +#endif + +/* Error. Should never happen. */ +#ifndef HB_NO_PRAGMA_GCC_DIAGNOSTIC_ERROR +#pragma GCC diagnostic error "-Wbitwise-instead-of-logical" +#pragma GCC diagnostic error "-Wcast-align" +#pragma GCC diagnostic error "-Wcast-function-type" +#pragma GCC diagnostic error "-Wcomma" +#pragma GCC diagnostic error "-Wdelete-non-virtual-dtor" +#pragma GCC diagnostic error "-Wembedded-directive" +#pragma GCC diagnostic error "-Wextra-semi-stmt" +#pragma GCC diagnostic error "-Wformat-security" +#pragma GCC diagnostic error "-Wimplicit-function-declaration" +#pragma GCC diagnostic error "-Winit-self" +#pragma GCC diagnostic error "-Winjected-class-name" +#pragma GCC diagnostic error "-Wmissing-braces" +#pragma GCC diagnostic error "-Wmissing-declarations" +#pragma GCC diagnostic error "-Wmissing-prototypes" +#pragma GCC diagnostic error "-Wnarrowing" +#pragma GCC diagnostic error "-Wnested-externs" +#pragma GCC diagnostic error "-Wold-style-definition" +#pragma GCC diagnostic error "-Wpointer-arith" +#pragma GCC diagnostic error "-Wredundant-decls" +#pragma GCC diagnostic error "-Wreorder" +#pragma GCC diagnostic error "-Wsign-compare" +#pragma GCC diagnostic error "-Wstrict-prototypes" +#pragma GCC diagnostic error "-Wstring-conversion" +#pragma GCC diagnostic error "-Wswitch-enum" +#pragma GCC diagnostic error "-Wtautological-overlap-compare" +#pragma GCC diagnostic error "-Wunneeded-internal-declaration" +#pragma GCC diagnostic error "-Wunused" +#pragma GCC diagnostic error "-Wunused-local-typedefs" +#pragma GCC diagnostic error "-Wunused-value" +#pragma GCC diagnostic error "-Wunused-variable" +#pragma GCC diagnostic error "-Wvla" +#pragma GCC diagnostic error "-Wwrite-strings" +#endif + +/* Warning. To be investigated if happens. */ +#ifndef HB_NO_PRAGMA_GCC_DIAGNOSTIC_WARNING +#pragma GCC diagnostic warning "-Wbuiltin-macro-redefined" +#pragma GCC diagnostic warning "-Wdeprecated" +#pragma GCC diagnostic warning "-Wdeprecated-declarations" +#pragma GCC diagnostic warning "-Wdisabled-optimization" +#pragma GCC diagnostic warning "-Wdouble-promotion" +#pragma GCC diagnostic warning "-Wformat=2" +#pragma GCC diagnostic warning "-Wignored-pragma-optimize" +#pragma GCC diagnostic warning "-Wlogical-op" +#pragma GCC diagnostic warning "-Wmaybe-uninitialized" +#pragma GCC diagnostic warning "-Wmissing-format-attribute" +#pragma GCC diagnostic warning "-Wundef" +#pragma GCC diagnostic warning "-Wunused-but-set-variable" +#endif + +/* Ignored currently, but should be fixed at some point. */ +#ifndef HB_NO_PRAGMA_GCC_DIAGNOSTIC_IGNORED +#pragma GCC diagnostic ignored "-Wconversion" // TODO fix +#pragma GCC diagnostic ignored "-Wformat-signedness" // TODO fix +#pragma GCC diagnostic ignored "-Wshadow" // TODO fix +#pragma GCC diagnostic ignored "-Wunsafe-loop-optimizations" // TODO fix +#pragma GCC diagnostic ignored "-Wunused-parameter" // TODO fix +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic ignored "-Wunused-result" // TODO fix +#endif +#endif + +/* Ignored intentionally. */ +#ifndef HB_NO_PRAGMA_GCC_DIAGNOSTIC_IGNORED +#pragma GCC diagnostic ignored "-Wclass-memaccess" +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#pragma GCC diagnostic ignored "-Wformat-zero-length" +#pragma GCC diagnostic ignored "-Wmissing-field-initializers" +#pragma GCC diagnostic ignored "-Wpacked" // Erratic impl in clang +#pragma GCC diagnostic ignored "-Wrange-loop-analysis" // https://github.com/harfbuzz/harfbuzz/issues/2834 +#pragma GCC diagnostic ignored "-Wstrict-aliasing" +#pragma GCC diagnostic ignored "-Wtype-limits" +#pragma GCC diagnostic ignored "-Wc++11-compat" // only gcc raises it +#endif + +#endif +#endif + + +#include "hb-config.hh" + + +/* + * Following added based on what AC_USE_SYSTEM_EXTENSIONS adds to + * config.h.in. Copied here for the convenience of those embedding + * HarfBuzz and not using our build system. + */ +/* Enable extensions on AIX 3, Interix. */ +#ifndef _ALL_SOURCE +# define _ALL_SOURCE 1 +#endif +/* Enable GNU extensions on systems that have them. */ +#ifndef _GNU_SOURCE +# define _GNU_SOURCE 1 +#endif +/* Enable threading extensions on Solaris. */ +#ifndef _POSIX_PTHREAD_SEMANTICS +# define _POSIX_PTHREAD_SEMANTICS 1 +#endif +/* Enable extensions on HP NonStop. */ +#ifndef _TANDEM_SOURCE +# define _TANDEM_SOURCE 1 +#endif +/* Enable general extensions on Solaris. */ +#ifndef __EXTENSIONS__ +# define __EXTENSIONS__ 1 +#endif + +#if defined (_MSC_VER) && defined (HB_DLL_EXPORT) +#define HB_EXTERN __declspec (dllexport) extern +#endif + +#include "hb.h" +#define HB_H_IN +#include "hb-ot.h" +#define HB_OT_H_IN +#include "hb-aat.h" +#define HB_AAT_H_IN + +#include +#include +#include +#if defined(_MSC_VER) && !defined(_USE_MATH_DEFINES) +# define _USE_MATH_DEFINES +#endif +#include +#include +#include +#include +#include +#include + +#if (defined(_MSC_VER) && _MSC_VER >= 1500) || defined(__MINGW32__) +#ifdef __MINGW32_VERSION +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN 1 +#endif +#else +#include +#endif +#endif + +#ifdef _WIN32 +#include +#include +#endif + +#define HB_PASTE1(a,b) a##b +#define HB_PASTE(a,b) HB_PASTE1(a,b) + + +/* Compile-time custom allocator support. */ + +#if !defined(HB_CUSTOM_MALLOC) \ + && defined(hb_malloc_impl) \ + && defined(hb_calloc_impl) \ + && defined(hb_realloc_impl) \ + && defined(hb_free_impl) +#define HB_CUSTOM_MALLOC +#endif + +#ifdef HB_CUSTOM_MALLOC +extern "C" void* hb_malloc_impl(size_t size); +extern "C" void* hb_calloc_impl(size_t nmemb, size_t size); +extern "C" void* hb_realloc_impl(void *ptr, size_t size); +extern "C" void hb_free_impl(void *ptr); +#define hb_malloc hb_malloc_impl +#define hb_calloc hb_calloc_impl +#define hb_realloc hb_realloc_impl +#define hb_free hb_free_impl +#else +#define hb_malloc malloc +#define hb_calloc calloc +#define hb_realloc realloc +#define hb_free free +#endif + + +/* + * Compiler attributes + */ + +#if (defined(__GNUC__) || defined(__clang__)) && defined(__OPTIMIZE__) +#define likely(expr) (__builtin_expect (!!(expr), 1)) +#define unlikely(expr) (__builtin_expect (!!(expr), 0)) +#else +#define likely(expr) (expr) +#define unlikely(expr) (expr) +#endif + +#if !defined(__GNUC__) && !defined(__clang__) +#undef __attribute__ +#define __attribute__(x) +#endif + +#if defined(__GNUC__) && (__GNUC__ >= 3) +#define HB_PRINTF_FUNC(format_idx, arg_idx) __attribute__((__format__ (__printf__, format_idx, arg_idx))) +#else +#define HB_PRINTF_FUNC(format_idx, arg_idx) +#endif +#if defined(__GNUC__) && (__GNUC__ >= 4) || (__clang__) +#define HB_UNUSED __attribute__((unused)) +#elif defined(_MSC_VER) /* https://github.com/harfbuzz/harfbuzz/issues/635 */ +#define HB_UNUSED __pragma(warning(suppress: 4100 4101)) +#else +#define HB_UNUSED +#endif + +#ifndef HB_INTERNAL +# if !defined(HB_NO_VISIBILITY) && !defined(__MINGW32__) && !defined(__CYGWIN__) && !defined(_MSC_VER) && !defined(__SUNPRO_CC) +# define HB_INTERNAL __attribute__((__visibility__("hidden"))) +# elif defined(__MINGW32__) + /* We use -export-symbols on mingw32, since it does not support visibility attributes. */ +# define HB_INTERNAL +# elif defined (_MSC_VER) && defined (HB_DLL_EXPORT) + /* We do not try to export internal symbols on Visual Studio */ +# define HB_INTERNAL +#else +# define HB_INTERNAL +# define HB_NO_VISIBILITY 1 +# endif +#endif + +/* https://github.com/harfbuzz/harfbuzz/issues/1651 */ +#if defined(__clang__) && __clang_major__ < 10 +#define static_const static +#else +#define static_const static const +#endif + +#if defined(__GNUC__) && (__GNUC__ >= 3) +#define HB_FUNC __PRETTY_FUNCTION__ +#elif defined(_MSC_VER) +#define HB_FUNC __FUNCSIG__ +#else +#define HB_FUNC __func__ +#endif + +#if defined(__SUNPRO_CC) && (__SUNPRO_CC < 0x5140) +/* https://github.com/harfbuzz/harfbuzz/issues/630 */ +#define __restrict +#endif + +/* + * Borrowed from https://bugzilla.mozilla.org/show_bug.cgi?id=1215411 + * HB_FALLTHROUGH is an annotation to suppress compiler warnings about switch + * cases that fall through without a break or return statement. HB_FALLTHROUGH + * is only needed on cases that have code: + * + * switch (foo) { + * case 1: // These cases have no code. No fallthrough annotations are needed. + * case 2: + * case 3: + * foo = 4; // This case has code, so a fallthrough annotation is needed: + * HB_FALLTHROUGH; + * default: + * return foo; + * } + */ +#if defined(__clang__) && __cplusplus >= 201103L + /* clang's fallthrough annotations are only available starting in C++11. */ +# define HB_FALLTHROUGH [[clang::fallthrough]] +#elif defined(__GNUC__) && (__GNUC__ >= 7) + /* GNU fallthrough attribute is available from GCC7 */ +# define HB_FALLTHROUGH __attribute__((fallthrough)) +#elif defined(_MSC_VER) + /* + * MSVC's __fallthrough annotations are checked by /analyze (Code Analysis): + * https://msdn.microsoft.com/en-us/library/ms235402%28VS.80%29.aspx + */ +# include +# define HB_FALLTHROUGH __fallthrough +#else +# define HB_FALLTHROUGH /* FALLTHROUGH */ +#endif + +/* A tag to enforce use of return value for a function */ +#if __cplusplus >= 201703L +# define HB_NODISCARD [[nodiscard]] +#elif defined(__GNUC__) || defined(__clang__) +# define HB_NODISCARD __attribute__((warn_unused_result)) +#elif defined(_MSC_VER) +# define HB_NODISCARD _Check_return_ +#else +# define HB_NODISCARD +#endif + +/* https://github.com/harfbuzz/harfbuzz/issues/1852 */ +#if defined(__clang__) && !(defined(_AIX) && (defined(__IBMCPP__) || defined(__ibmxl__))) +/* Disable certain sanitizer errors. */ +/* https://github.com/harfbuzz/harfbuzz/issues/1247 */ +#define HB_NO_SANITIZE_SIGNED_INTEGER_OVERFLOW __attribute__((no_sanitize("signed-integer-overflow"))) +#else +#define HB_NO_SANITIZE_SIGNED_INTEGER_OVERFLOW +#endif + + +#ifdef _WIN32 + /* We need Windows Vista for both Uniscribe backend and for + * MemoryBarrier. We don't support compiling on Windows XP, + * though we run on it fine. */ +# if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0600 +# undef _WIN32_WINNT +# endif +# ifndef _WIN32_WINNT +# if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +# define _WIN32_WINNT 0x0600 +# endif +# endif +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN 1 +# endif +# ifndef STRICT +# define STRICT 1 +# endif + +# if defined(_WIN32_WCE) + /* Some things not defined on Windows CE. */ +# define vsnprintf _vsnprintf +# ifndef HB_NO_GETENV +# define HB_NO_GETENV +# endif +# if _WIN32_WCE < 0x800 +# define HB_NO_SETLOCALE +# define HB_NO_ERRNO +# endif +# elif !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +# ifndef HB_NO_GETENV +# define HB_NO_GETENV +# endif +# endif +# if defined(_MSC_VER) && _MSC_VER < 1900 +# define snprintf _snprintf +# endif +#endif + +#ifdef HB_NO_GETENV +#define getenv(Name) nullptr +#endif + +#ifndef HB_NO_ERRNO +# include +#else +static int HB_UNUSED _hb_errno = 0; +# undef errno +# define errno _hb_errno +#endif + +#define HB_STMT_START do +#define HB_STMT_END while (0) + +#if defined(HAVE_ATEXIT) && !defined(HB_USE_ATEXIT) +/* atexit() is only safe to be called from shared libraries on certain + * platforms. Whitelist. + * https://bugs.freedesktop.org/show_bug.cgi?id=82246 */ +# if defined(__linux) && defined(__GLIBC_PREREQ) +# if __GLIBC_PREREQ(2,3) +/* From atexit() manpage, it's safe with glibc 2.2.3 on Linux. */ +# define HB_USE_ATEXIT 1 +# endif +# elif defined(_MSC_VER) || defined(__MINGW32__) +/* For MSVC: + * https://msdn.microsoft.com/en-us/library/tze57ck3.aspx + * https://msdn.microsoft.com/en-us/library/zk17ww08.aspx + * mingw32 headers say atexit is safe to use in shared libraries. + */ +# define HB_USE_ATEXIT 1 +# elif defined(__ANDROID__) +/* This is available since Android NKD r8 or r8b: + * https://issuetracker.google.com/code/p/android/issues/detail?id=6455 + */ +# define HB_USE_ATEXIT 1 +# elif defined(__APPLE__) +/* For macOS and related platforms, the atexit man page indicates + * that it will be invoked when the library is unloaded, not only + * at application exit. + */ +# define HB_USE_ATEXIT 1 +# endif +#endif /* defined(HAVE_ATEXIT) && !defined(HB_USE_ATEXIT) */ +#ifdef HB_NO_ATEXIT +# undef HB_USE_ATEXIT +#endif +#ifndef HB_USE_ATEXIT +# define HB_USE_ATEXIT 0 +#endif +#ifndef hb_atexit +#if !HB_USE_ATEXIT +# define hb_atexit(_) HB_STMT_START { if (0) (_) (); } HB_STMT_END +#else /* HB_USE_ATEXIT */ +# ifdef HAVE_ATEXIT +# define hb_atexit atexit +# else + template struct hb_atexit_t { ~hb_atexit_t () { function (); } }; +# define hb_atexit(f) static hb_atexit_t _hb_atexit_##__LINE__; +# endif +#endif +#endif + +/* Lets assert int types. Saves trouble down the road. */ +static_assert ((sizeof (hb_codepoint_t) == 4), ""); +static_assert ((sizeof (hb_position_t) == 4), ""); +static_assert ((sizeof (hb_mask_t) == 4), ""); +static_assert ((sizeof (hb_var_int_t) == 4), ""); + + +/* Headers we include for everyone. Keep topologically sorted by dependency. + * They express dependency amongst themselves, but no other file should include + * them directly.*/ +#include "hb-cplusplus.hh" +#include "hb-meta.hh" +#include "hb-mutex.hh" +#include "hb-number.hh" +#include "hb-atomic.hh" // Requires: hb-meta +#include "hb-null.hh" // Requires: hb-meta +#include "hb-algs.hh" // Requires: hb-meta hb-null hb-number +#include "hb-iter.hh" // Requires: hb-algs hb-meta +#include "hb-debug.hh" // Requires: hb-algs hb-atomic +#include "hb-array.hh" // Requires: hb-algs hb-iter hb-null +#include "hb-vector.hh" // Requires: hb-array hb-null +#include "hb-object.hh" // Requires: hb-atomic hb-mutex hb-vector + +#endif /* HB_HH */ diff --git a/libs/harfbuzz/src/main.cc b/libs/harfbuzz/src/main.cc new file mode 100644 index 00000000..99e1528d --- /dev/null +++ b/libs/harfbuzz/src/main.cc @@ -0,0 +1,531 @@ +/* + * Copyright © 2007,2008,2009 Red Hat, Inc. + * Copyright © 2018,2019,2020 Ebrahim Byagowi + * Copyright © 2018 Khaled Hosny + * + * 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. + * + * Red Hat Author(s): Behdad Esfahbod + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "hb.h" +#include "hb-ot.h" + +#include +#include +#include +#include + +#ifdef HB_NO_OPEN +#define hb_blob_create_from_file_or_fail(x) hb_blob_get_empty () +#endif + +#if !defined(HB_NO_COLOR) && !defined(HB_NO_DRAW) +static void +svg_dump (hb_face_t *face, unsigned face_index) +{ + unsigned glyph_count = hb_face_get_glyph_count (face); + + for (unsigned glyph_id = 0; glyph_id < glyph_count; ++glyph_id) + { + hb_blob_t *blob = hb_ot_color_glyph_reference_svg (face, glyph_id); + + if (hb_blob_get_length (blob) == 0) continue; + + unsigned length; + const char *data = hb_blob_get_data (blob, &length); + + char output_path[255]; + sprintf (output_path, "out/svg-%u-%u.svg%s", + glyph_id, + face_index, + // append "z" if the content is gzipped, https://stackoverflow.com/a/6059405 + (length > 2 && (data[0] == '\x1F') && (data[1] == '\x8B')) ? "z" : ""); + + FILE *f = fopen (output_path, "wb"); + fwrite (data, 1, length, f); + fclose (f); + + hb_blob_destroy (blob); + } +} + +/* _png API is so easy to use unlike the below code, don't get confused */ +static void +png_dump (hb_face_t *face, unsigned face_index) +{ + unsigned glyph_count = hb_face_get_glyph_count (face); + hb_font_t *font = hb_font_create (face); + + /* scans the font for strikes */ + unsigned sample_glyph_id; + /* we don't care about different strikes for different glyphs at this point */ + for (sample_glyph_id = 0; sample_glyph_id < glyph_count; ++sample_glyph_id) + { + hb_blob_t *blob = hb_ot_color_glyph_reference_png (font, sample_glyph_id); + unsigned blob_length = hb_blob_get_length (blob); + hb_blob_destroy (blob); + if (blob_length != 0) + break; + } + + unsigned upem = hb_face_get_upem (face); + unsigned blob_length = 0; + unsigned strike = 0; + for (unsigned ppem = 1; ppem < upem; ++ppem) + { + hb_font_set_ppem (font, ppem, ppem); + hb_blob_t *blob = hb_ot_color_glyph_reference_png (font, sample_glyph_id); + unsigned new_blob_length = hb_blob_get_length (blob); + hb_blob_destroy (blob); + if (new_blob_length != blob_length) + { + for (unsigned glyph_id = 0; glyph_id < glyph_count; ++glyph_id) + { + hb_blob_t *blob = hb_ot_color_glyph_reference_png (font, glyph_id); + + if (hb_blob_get_length (blob) == 0) continue; + + unsigned length; + const char *data = hb_blob_get_data (blob, &length); + + char output_path[255]; + sprintf (output_path, "out/png-%u-%u-%u.png", glyph_id, strike, face_index); + + FILE *f = fopen (output_path, "wb"); + fwrite (data, 1, length, f); + fclose (f); + + hb_blob_destroy (blob); + } + + strike++; + blob_length = new_blob_length; + } + } + + hb_font_destroy (font); +} + +struct draw_data_t +{ + FILE *f; + hb_position_t ascender; +}; + +static void +move_to (hb_draw_funcs_t *, draw_data_t *draw_data, + hb_draw_state_t *, + float to_x, float to_y, + void *) +{ + fprintf (draw_data->f, "M%g,%g", to_x, draw_data->ascender - to_y); +} + +static void +line_to (hb_draw_funcs_t *, draw_data_t *draw_data, + hb_draw_state_t *, + float to_x, float to_y, + void *) +{ + fprintf (draw_data->f, "L%g,%g", to_x, draw_data->ascender - to_y); +} + +static void +quadratic_to (hb_draw_funcs_t *, draw_data_t *draw_data, + hb_draw_state_t *, + float control_x, float control_y, + float to_x, float to_y, + void *) +{ + fprintf (draw_data->f, "Q%g,%g %g,%g", control_x, draw_data->ascender - control_y, + to_x, draw_data->ascender - to_y); +} + +static void +cubic_to (hb_draw_funcs_t *, draw_data_t *draw_data, + hb_draw_state_t *, + float control1_x, float control1_y, + float control2_x, float control2_y, + float to_x, float to_y, + void *) +{ + fprintf (draw_data->f, "C%g,%g %g,%g %g,%g", control1_x, draw_data->ascender - control1_y, + control2_x, draw_data->ascender - control2_y, + to_x, draw_data->ascender - to_y); +} + +static void +close_path (hb_draw_funcs_t *, draw_data_t *draw_data, + hb_draw_state_t *, + void *) +{ + fprintf (draw_data->f, "Z"); +} + +static void +layered_glyph_dump (hb_font_t *font, hb_draw_funcs_t *funcs, unsigned face_index) +{ + hb_face_t *face = hb_font_get_face (font); + unsigned palette_count = hb_ot_color_palette_get_count (face); + for (unsigned palette = 0; palette < palette_count; ++palette) + { + unsigned num_colors = hb_ot_color_palette_get_colors (face, palette, 0, nullptr, nullptr); + if (!num_colors) continue; + + hb_color_t *colors = (hb_color_t*) calloc (num_colors, sizeof (hb_color_t)); + hb_ot_color_palette_get_colors (face, palette, 0, &num_colors, colors); + if (!num_colors) + { + free (colors); + continue; + } + + unsigned num_glyphs = hb_face_get_glyph_count (face); + for (hb_codepoint_t gid = 0; gid < num_glyphs; ++gid) + { + unsigned num_layers = hb_ot_color_glyph_get_layers (face, gid, 0, nullptr, nullptr); + if (!num_layers) continue; + + hb_ot_color_layer_t *layers = (hb_ot_color_layer_t*) malloc (num_layers * sizeof (hb_ot_color_layer_t)); + + hb_ot_color_glyph_get_layers (face, gid, 0, &num_layers, layers); + if (num_layers) + { + hb_font_extents_t font_extents; + hb_font_get_extents_for_direction (font, HB_DIRECTION_LTR, &font_extents); + hb_glyph_extents_t extents = {0}; + if (!hb_font_get_glyph_extents (font, gid, &extents)) + { + printf ("Skip gid: %d\n", gid); + continue; + } + + char output_path[255]; + sprintf (output_path, "out/colr-%u-%u-%u.svg", gid, palette, face_index); + FILE *f = fopen (output_path, "wb"); + fprintf (f, "\n", + extents.x_bearing, 0, + extents.x_bearing + extents.width, -extents.height); + draw_data_t draw_data; + draw_data.ascender = extents.y_bearing; + draw_data.f = f; + + for (unsigned layer = 0; layer < num_layers; ++layer) + { + hb_color_t color = 0x000000FF; + if (layers[layer].color_index != 0xFFFF) + color = colors[layers[layer].color_index]; + fprintf (f, "\n"); + } + + fprintf (f, ""); + fclose (f); + } + free (layers); + } + + free (colors); + } +} + +static void +dump_glyphs (hb_font_t *font, hb_draw_funcs_t *funcs, unsigned face_index) +{ + unsigned num_glyphs = hb_face_get_glyph_count (hb_font_get_face (font)); + for (unsigned gid = 0; gid < num_glyphs; ++gid) + { + hb_font_extents_t font_extents; + hb_font_get_extents_for_direction (font, HB_DIRECTION_LTR, &font_extents); + hb_glyph_extents_t extents = {0}; + if (!hb_font_get_glyph_extents (font, gid, &extents)) + { + printf ("Skip gid: %d\n", gid); + continue; + } + + char output_path[255]; + sprintf (output_path, "out/%u-%u.svg", face_index, gid); + FILE *f = fopen (output_path, "wb"); + fprintf (f, ""); + fclose (f); + } +} + +static void +dump_glyphs (hb_blob_t *blob, const char *font_name) +{ + FILE *font_name_file = fopen ("out/.dumped_font_name", "r"); + if (font_name_file) + { + fprintf (stderr, "Purge or rename ./out folder if you like to run a glyph dump,\n" + "run it like `rm -rf out && mkdir out && src/main font-file.ttf`\n"); + return; + } + + font_name_file = fopen ("out/.dumped_font_name", "w"); + if (!font_name_file) + { + fprintf (stderr, "./out is not accessible as a folder, create it please\n"); + return; + } + fwrite (font_name, 1, strlen (font_name), font_name_file); + fclose (font_name_file); + + hb_draw_funcs_t *funcs = hb_draw_funcs_create (); + hb_draw_funcs_set_move_to_func (funcs, (hb_draw_move_to_func_t) move_to, nullptr, nullptr); + hb_draw_funcs_set_line_to_func (funcs, (hb_draw_line_to_func_t) line_to, nullptr, nullptr); + hb_draw_funcs_set_quadratic_to_func (funcs, (hb_draw_quadratic_to_func_t) quadratic_to, nullptr, nullptr); + hb_draw_funcs_set_cubic_to_func (funcs, (hb_draw_cubic_to_func_t) cubic_to, nullptr, nullptr); + hb_draw_funcs_set_close_path_func (funcs, (hb_draw_close_path_func_t) close_path, nullptr, nullptr); + + unsigned num_faces = hb_face_count (blob); + for (unsigned face_index = 0; face_index < num_faces; ++face_index) + { + hb_face_t *face = hb_face_create (blob, face_index); + hb_font_t *font = hb_font_create (face); + + if (hb_ot_color_has_png (face)) + printf ("Dumping png (CBDT/sbix)...\n"); + png_dump (face, face_index); + + if (hb_ot_color_has_svg (face)) + printf ("Dumping svg (SVG )...\n"); + svg_dump (face, face_index); + + if (hb_ot_color_has_layers (face) && hb_ot_color_has_palettes (face)) + printf ("Dumping layered color glyphs (COLR/CPAL)...\n"); + layered_glyph_dump (font, funcs, face_index); + + dump_glyphs (font, funcs, face_index); + + hb_font_destroy (font); + hb_face_destroy (face); + } + + hb_draw_funcs_destroy (funcs); +} +#endif + +#ifndef MAIN_CC_NO_PRIVATE_API +/* Only this part of this mini app uses private API */ +#include "hb-static.cc" +#include "hb-open-file.hh" +#include "hb-ot-layout-gdef-table.hh" +#include "hb-ot-layout-gsubgpos.hh" + +using namespace OT; + +static void +print_layout_info_using_private_api (hb_blob_t *blob) +{ + const char *font_data = hb_blob_get_data (blob, nullptr); + hb_blob_t *font_blob = hb_sanitize_context_t ().sanitize_blob (blob); + const OpenTypeFontFile* sanitized = font_blob->as (); + if (!font_blob->data) + { + printf ("Sanitization of the file wasn't successful. Exit"); + exit (1); + } + const OpenTypeFontFile& ot = *sanitized; + + switch (ot.get_tag ()) + { + case OpenTypeFontFile::TrueTypeTag: + printf ("OpenType font with TrueType outlines\n"); + break; + case OpenTypeFontFile::CFFTag: + printf ("OpenType font with CFF (Type1) outlines\n"); + break; + case OpenTypeFontFile::TTCTag: + printf ("TrueType Collection of OpenType fonts\n"); + break; + case OpenTypeFontFile::TrueTag: + printf ("Obsolete Apple TrueType font\n"); + break; + case OpenTypeFontFile::Typ1Tag: + printf ("Obsolete Apple Type1 font in SFNT container\n"); + break; + case OpenTypeFontFile::DFontTag: + printf ("DFont Mac Resource Fork\n"); + break; + default: + printf ("Unknown font format\n"); + break; + } + + unsigned num_faces = hb_face_count (blob); + printf ("%d font(s) found in file\n", num_faces); + for (unsigned n_font = 0; n_font < num_faces; ++n_font) + { + const OpenTypeFontFace &font = ot.get_face (n_font); + printf ("Font %d of %d:\n", n_font, num_faces); + + unsigned num_tables = font.get_table_count (); + printf (" %d table(s) found in font\n", num_tables); + for (unsigned n_table = 0; n_table < num_tables; ++n_table) + { + const OpenTypeTable &table = font.get_table (n_table); + printf (" Table %2d of %2d: %.4s (0x%08x+0x%08x)\n", n_table, num_tables, + (const char *) table.tag, + (unsigned) table.offset, + (unsigned) table.length); + + switch (table.tag) + { + + case HB_OT_TAG_GSUB: + case HB_OT_TAG_GPOS: + { + + const GSUBGPOS &g = *reinterpret_cast (font_data + table.offset); + + unsigned num_scripts = g.get_script_count (); + printf (" %d script(s) found in table\n", num_scripts); + for (unsigned n_script = 0; n_script < num_scripts; ++n_script) + { + const Script &script = g.get_script (n_script); + printf (" Script %2d of %2d: %.4s\n", n_script, num_scripts, + (const char *) g.get_script_tag (n_script)); + + if (!script.has_default_lang_sys ()) + printf (" No default language system\n"); + int num_langsys = script.get_lang_sys_count (); + printf (" %d language system(s) found in script\n", num_langsys); + for (int n_langsys = script.has_default_lang_sys () ? -1 : 0; n_langsys < num_langsys; ++n_langsys) + { + const LangSys &langsys = n_langsys == -1 + ? script.get_default_lang_sys () + : script.get_lang_sys (n_langsys); + if (n_langsys == -1) + printf (" Default Language System\n"); + else + printf (" Language System %2d of %2d: %.4s\n", n_langsys, num_langsys, + (const char *) script.get_lang_sys_tag (n_langsys)); + if (!langsys.has_required_feature ()) + printf (" No required feature\n"); + else + printf (" Required feature index: %d\n", + langsys.get_required_feature_index ()); + + unsigned num_features = langsys.get_feature_count (); + printf (" %d feature(s) found in language system\n", num_features); + for (unsigned n_feature = 0; n_feature < num_features; ++n_feature) + { + printf (" Feature index %2d of %2d: %d\n", n_feature, num_features, + langsys.get_feature_index (n_feature)); + } + } + } + + unsigned num_features = g.get_feature_count (); + printf (" %d feature(s) found in table\n", num_features); + for (unsigned n_feature = 0; n_feature < num_features; ++n_feature) + { + const Feature &feature = g.get_feature (n_feature); + unsigned num_lookups = feature.get_lookup_count (); + printf (" Feature %2d of %2d: %c%c%c%c\n", n_feature, num_features, + HB_UNTAG (g.get_feature_tag (n_feature))); + + printf (" %d lookup(s) found in feature\n", num_lookups); + for (unsigned n_lookup = 0; n_lookup < num_lookups; ++n_lookup) { + printf (" Lookup index %2d of %2d: %d\n", n_lookup, num_lookups, + feature.get_lookup_index (n_lookup)); + } + } + + unsigned num_lookups = g.get_lookup_count (); + printf (" %d lookup(s) found in table\n", num_lookups); + for (unsigned n_lookup = 0; n_lookup < num_lookups; ++n_lookup) + { + const Lookup &lookup = g.get_lookup (n_lookup); + printf (" Lookup %2d of %2d: type %d, props 0x%04X\n", n_lookup, num_lookups, + lookup.get_type (), lookup.get_props ()); + } + + } + break; + + case GDEF::tableTag: + { + + const GDEF &gdef = *reinterpret_cast (font_data + table.offset); + + printf (" Has %sglyph classes\n", + gdef.has_glyph_classes () ? "" : "no "); + printf (" Has %smark attachment types\n", + gdef.has_mark_attachment_types () ? "" : "no "); + printf (" Has %sattach list\n", + gdef.has_attach_list () ? "" : "no "); + printf (" Has %slig carets\n", + gdef.has_lig_carets () ? "" : "no "); + printf (" Has %smark glyph sets\n", + gdef.has_mark_glyph_sets () ? "" : "no "); + break; + } + } + } + } +} +/* end of private API use */ +#endif + +int +main (int argc, char **argv) +{ + if (argc != 2) + { + fprintf (stderr, "usage: %s font-file.ttf\n", argv[0]); + exit (1); + } + + hb_blob_t *blob = hb_blob_create_from_file_or_fail (argv[1]); + assert (blob); + printf ("Opened font file %s: %d bytes long\n", argv[1], hb_blob_get_length (blob)); +#ifndef MAIN_CC_NO_PRIVATE_API + print_layout_info_using_private_api (blob); +#endif +#if !defined(HB_NO_COLOR) && !defined(HB_NO_DRAW) + dump_glyphs (blob, argv[1]); +#endif + hb_blob_destroy (blob); + + return 0; +} diff --git a/libs/harfbuzz/src/meson.build b/libs/harfbuzz/src/meson.build new file mode 100644 index 00000000..ba3470ff --- /dev/null +++ b/libs/harfbuzz/src/meson.build @@ -0,0 +1,811 @@ +hb_version_h = configure_file( + command: [find_program('gen-hb-version.py'), meson.project_version(), '@OUTPUT@', '@INPUT@'], + input: 'hb-version.h.in', + output: 'hb-version.h', + install: true, + install_dir: get_option('includedir') / meson.project_name()) + +# Base and default-included sources and headers +hb_base_sources = files( + 'hb-aat-layout-ankr-table.hh', + 'hb-aat-layout-bsln-table.hh', + 'hb-aat-layout-common.hh', + 'hb-aat-layout-feat-table.hh', + 'hb-aat-layout-just-table.hh', + 'hb-aat-layout-kerx-table.hh', + 'hb-aat-layout-morx-table.hh', + 'hb-aat-layout-opbd-table.hh', + 'hb-aat-layout-trak-table.hh', + 'hb-aat-layout.cc', + 'hb-aat-layout.hh', + 'hb-aat-ltag-table.hh', + 'hb-aat-map.cc', + 'hb-aat-map.hh', + 'hb-algs.hh', + 'hb-array.hh', + 'hb-atomic.hh', + 'hb-bimap.hh', + 'hb-bit-page.hh', + 'hb-blob.cc', + 'hb-blob.hh', + 'hb-buffer-serialize.cc', + 'hb-buffer-verify.cc', + 'hb-buffer.cc', + 'hb-buffer.hh', + 'hb-cache.hh', + 'hb-cff-interp-common.hh', + 'hb-cff-interp-cs-common.hh', + 'hb-cff-interp-dict-common.hh', + 'hb-cff1-interp-cs.hh', + 'hb-cff2-interp-cs.hh', + 'hb-common.cc', + 'hb-config.hh', + 'hb-debug.hh', + 'hb-dispatch.hh', + 'hb-draw.cc', + 'hb-draw.hh', + 'hb-face.cc', + 'hb-face.hh', + 'hb-fallback-shape.cc', + 'hb-font.cc', + 'hb-font.hh', + 'hb-iter.hh', + 'hb-kern.hh', + 'hb-machinery.hh', + 'hb-map.cc', + 'hb-map.hh', + 'hb-meta.hh', + 'hb-ms-feature-ranges.hh', + 'hb-mutex.hh', + 'hb-null.hh', + 'hb-number.cc', + 'hb-number.hh', + 'hb-object.hh', + 'hb-open-file.hh', + 'hb-open-type.hh', + 'hb-ot-cff-common.hh', + 'hb-ot-cff1-std-str.hh', + 'hb-ot-cff1-table.cc', + 'hb-ot-cff1-table.hh', + 'hb-ot-cff2-table.cc', + 'hb-ot-cff2-table.hh', + 'hb-ot-cmap-table.hh', + 'hb-ot-color-cbdt-table.hh', + 'hb-ot-color-colr-table.hh', + 'hb-ot-color-cpal-table.hh', + 'hb-ot-color-sbix-table.hh', + 'hb-ot-color-svg-table.hh', + 'hb-ot-color.cc', + 'hb-ot-face-table-list.hh', + 'hb-ot-face.cc', + 'hb-ot-face.hh', + 'hb-ot-font.cc', + 'hb-ot-gasp-table.hh', + 'hb-ot-glyf-table.hh', + 'hb-ot-hdmx-table.hh', + 'hb-ot-head-table.hh', + 'hb-ot-hhea-table.hh', + 'hb-ot-hmtx-table.hh', + 'hb-ot-kern-table.hh', + 'hb-ot-layout-base-table.hh', + 'hb-ot-layout-common.hh', + 'hb-ot-layout-gdef-table.hh', + 'hb-ot-layout-gpos-table.hh', + 'hb-ot-layout-gsub-table.hh', + 'OT/glyf/glyf.hh', + 'OT/glyf/glyf-helpers.hh', + 'OT/glyf/loca.hh', + 'OT/glyf/path-builder.hh', + 'OT/glyf/Glyph.hh', + 'OT/glyf/GlyphHeader.hh', + 'OT/glyf/SimpleGlyph.hh', + 'OT/glyf/CompositeGlyph.hh', + 'OT/glyf/SubsetGlyph.hh', + 'OT/Layout/types.hh', + 'OT/Layout/Common/Coverage.hh', + 'OT/Layout/Common/CoverageFormat1.hh', + 'OT/Layout/Common/CoverageFormat2.hh', + 'OT/Layout/Common/RangeRecord.hh', + 'OT/Layout/GPOS/AnchorFormat1.hh', + 'OT/Layout/GPOS/AnchorFormat2.hh', + 'OT/Layout/GPOS/AnchorFormat3.hh', + 'OT/Layout/GPOS/Anchor.hh', + 'OT/Layout/GPOS/AnchorMatrix.hh', + 'OT/Layout/GPOS/ChainContextPos.hh', + 'OT/Layout/GPOS/Common.hh', + 'OT/Layout/GPOS/ContextPos.hh', + 'OT/Layout/GPOS/CursivePosFormat1.hh', + 'OT/Layout/GPOS/CursivePos.hh', + 'OT/Layout/GPOS/ExtensionPos.hh', + 'OT/Layout/GPOS/GPOS.hh', + 'OT/Layout/GPOS/LigatureArray.hh', + 'OT/Layout/GPOS/MarkArray.hh', + 'OT/Layout/GPOS/MarkBasePosFormat1.hh', + 'OT/Layout/GPOS/MarkBasePos.hh', + 'OT/Layout/GPOS/MarkLigPosFormat1.hh', + 'OT/Layout/GPOS/MarkLigPos.hh', + 'OT/Layout/GPOS/MarkMarkPosFormat1.hh', + 'OT/Layout/GPOS/MarkMarkPos.hh', + 'OT/Layout/GPOS/MarkRecord.hh', + 'OT/Layout/GPOS/PairPosFormat1.hh', + 'OT/Layout/GPOS/PairPosFormat2.hh', + 'OT/Layout/GPOS/PairPos.hh', + 'OT/Layout/GPOS/PairSet.hh', + 'OT/Layout/GPOS/PairValueRecord.hh', + 'OT/Layout/GPOS/PosLookup.hh', + 'OT/Layout/GPOS/PosLookupSubTable.hh', + 'OT/Layout/GPOS/SinglePosFormat1.hh', + 'OT/Layout/GPOS/SinglePosFormat2.hh', + 'OT/Layout/GPOS/SinglePos.hh', + 'OT/Layout/GPOS/ValueFormat.hh', + 'OT/Layout/GSUB/AlternateSet.hh', + 'OT/Layout/GSUB/AlternateSubstFormat1.hh', + 'OT/Layout/GSUB/AlternateSubst.hh', + 'OT/Layout/GSUB/ChainContextSubst.hh', + 'OT/Layout/GSUB/Common.hh', + 'OT/Layout/GSUB/ContextSubst.hh', + 'OT/Layout/GSUB/ExtensionSubst.hh', + 'OT/Layout/GSUB/GSUB.hh', + 'OT/Layout/GSUB/Ligature.hh', + 'OT/Layout/GSUB/LigatureSet.hh', + 'OT/Layout/GSUB/LigatureSubstFormat1.hh', + 'OT/Layout/GSUB/LigatureSubst.hh', + 'OT/Layout/GSUB/MultipleSubstFormat1.hh', + 'OT/Layout/GSUB/MultipleSubst.hh', + 'OT/Layout/GSUB/ReverseChainSingleSubstFormat1.hh', + 'OT/Layout/GSUB/ReverseChainSingleSubst.hh', + 'OT/Layout/GSUB/Sequence.hh', + 'OT/Layout/GSUB/SingleSubstFormat1.hh', + 'OT/Layout/GSUB/SingleSubstFormat2.hh', + 'OT/Layout/GSUB/SingleSubst.hh', + 'OT/Layout/GSUB/SubstLookup.hh', + 'OT/Layout/GSUB/SubstLookupSubTable.hh', + 'hb-ot-layout-gsubgpos.hh', + 'hb-ot-layout-jstf-table.hh', + 'hb-ot-layout.cc', + 'hb-ot-layout.hh', + 'hb-ot-map.cc', + 'hb-ot-map.hh', + 'hb-ot-math-table.hh', + 'hb-ot-math.cc', + 'hb-ot-maxp-table.hh', + 'hb-ot-meta-table.hh', + 'hb-ot-meta.cc', + 'hb-ot-metrics.cc', + 'hb-ot-metrics.hh', + 'hb-ot-name-language-static.hh', + 'hb-ot-name-language.hh', + 'hb-ot-name-table.hh', + 'hb-ot-name.cc', + 'hb-ot-os2-table.hh', + 'hb-ot-os2-unicode-ranges.hh', + 'hb-ot-post-macroman.hh', + 'hb-ot-post-table.hh', + 'hb-ot-shaper-arabic-fallback.hh', + 'hb-ot-shaper-arabic-joining-list.hh', + 'hb-ot-shaper-arabic-pua.hh', + 'hb-ot-shaper-arabic-table.hh', + 'hb-ot-shaper-arabic-win1256.hh', + 'hb-ot-shaper-arabic.cc', + 'hb-ot-shaper-arabic.hh', + 'hb-ot-shaper-default.cc', + 'hb-ot-shaper-hangul.cc', + 'hb-ot-shaper-hebrew.cc', + 'hb-ot-shaper-indic-table.cc', + 'hb-ot-shaper-indic.cc', + 'hb-ot-shaper-indic.hh', + 'hb-ot-shaper-khmer.cc', + 'hb-ot-shaper-myanmar.cc', + 'hb-ot-shaper-syllabic.cc', + 'hb-ot-shaper-syllabic.hh', + 'hb-ot-shaper-thai.cc', + 'hb-ot-shaper-use-table.hh', + 'hb-ot-shaper-use.cc', + 'hb-ot-shaper-vowel-constraints.cc', + 'hb-ot-shaper-vowel-constraints.hh', + 'hb-ot-shaper.hh', + 'hb-ot-shape-fallback.cc', + 'hb-ot-shape-fallback.hh', + 'hb-ot-shape-normalize.cc', + 'hb-ot-shape-normalize.hh', + 'hb-ot-shape.cc', + 'hb-ot-shape.hh', + 'hb-ot-stat-table.hh', + 'hb-ot-tag-table.hh', + 'hb-ot-tag.cc', + 'hb-ot-var-avar-table.hh', + 'hb-ot-var-common.hh', + 'hb-ot-var-fvar-table.hh', + 'hb-ot-var-gvar-table.hh', + 'hb-ot-var-hvar-table.hh', + 'hb-ot-var-mvar-table.hh', + 'hb-ot-var.cc', + 'hb-ot-vorg-table.hh', + 'hb-pool.hh', + 'hb-sanitize.hh', + 'hb-serialize.hh', + 'hb-set-digest.hh', + 'hb-set.cc', + 'hb-set.hh', + 'hb-shape-plan.cc', + 'hb-shape-plan.hh', + 'hb-shape.cc', + 'hb-shaper-impl.hh', + 'hb-shaper-list.hh', + 'hb-shaper.cc', + 'hb-shaper.hh', + 'hb-static.cc', + 'hb-string-array.hh', + 'hb-style.cc', + 'hb-ucd-table.hh', + 'hb-ucd.cc', + 'hb-unicode-emoji-table.hh', + 'hb-unicode.cc', + 'hb-unicode.hh', + 'hb-utf.hh', + 'hb-vector.hh', + 'hb.hh', +) + +hb_base_ragel_generated_sources = files( + 'hb-buffer-deserialize-json.hh', + 'hb-buffer-deserialize-text.hh', + 'hb-number-parser.hh', + 'hb-ot-shaper-indic-machine.hh', + 'hb-ot-shaper-khmer-machine.hh', + 'hb-ot-shaper-myanmar-machine.hh', + 'hb-ot-shaper-use-machine.hh', +) +hb_base_ragel_sources = [ + 'hb-buffer-deserialize-json.rl', + 'hb-buffer-deserialize-text.rl', + 'hb-number-parser.rl', + 'hb-ot-shaper-indic-machine.rl', + 'hb-ot-shaper-khmer-machine.rl', + 'hb-ot-shaper-myanmar-machine.rl', + 'hb-ot-shaper-use-machine.rl', +] + +hb_base_headers = files( + 'hb-aat-layout.h', + 'hb-aat.h', + 'hb-blob.h', + 'hb-buffer.h', + 'hb-common.h', + 'hb-cplusplus.hh', + 'hb-deprecated.h', + 'hb-draw.h', + 'hb-face.h', + 'hb-font.h', + 'hb-map.h', + 'hb-ot-color.h', + 'hb-ot-deprecated.h', + 'hb-ot-font.h', + 'hb-ot-layout.h', + 'hb-ot-math.h', + 'hb-ot-meta.h', + 'hb-ot-metrics.h', + 'hb-ot-name.h', + 'hb-ot-shape.h', + 'hb-ot-var.h', + 'hb-ot.h', + 'hb-set.h', + 'hb-shape-plan.h', + 'hb-shape.h', + 'hb-style.h', + 'hb-unicode.h', + 'hb.h', +) +hb_base_headers += hb_version_h + +# Optional Sources and Headers with external deps + +hb_ft_sources = files('hb-ft.cc') +hb_ft_headers = files('hb-ft.h') + +hb_glib_sources = files('hb-glib.cc') +hb_glib_headers = files('hb-glib.h') + +hb_graphite2_sources = files('hb-graphite2.cc') +hb_graphite2_headers = files('hb-graphite2.h') + +# System-dependent sources and headers + +hb_coretext_sources = files('hb-coretext.cc') +hb_coretext_headers = files('hb-coretext.h') + +hb_directwrite_sources = files('hb-directwrite.cc') +hb_directwrite_headers = files('hb-directwrite.h') + +hb_gdi_sources = files('hb-gdi.cc') +hb_gdi_headers = files('hb-gdi.h') + +hb_uniscribe_sources = files('hb-uniscribe.cc') +hb_uniscribe_headers = files('hb-uniscribe.h') + +# Sources for libharfbuzz-gobject and libharfbuzz-icu +hb_icu_sources = files('hb-icu.cc') +hb_icu_headers = files('hb-icu.h') + +# Sources for libharfbuzz-subset +hb_subset_sources = files( + 'hb-number.cc', + 'hb-number.hh', + 'hb-ot-cff1-table.cc', + 'hb-ot-cff2-table.cc', + 'hb-static.cc', + 'hb-subset-accelerator.hh', + 'hb-subset-cff-common.cc', + 'hb-subset-cff-common.hh', + 'hb-subset-cff1.cc', + 'hb-subset-cff1.hh', + 'hb-subset-cff2.cc', + 'hb-subset-cff2.hh', + 'hb-subset-input.cc', + 'hb-subset-input.hh', + 'hb-subset-plan.cc', + 'hb-subset-plan.hh', + 'hb-subset-repacker.cc', + 'graph/gsubgpos-context.cc', + 'graph/gsubgpos-context.hh', + 'graph/gsubgpos-graph.hh', + 'graph/pairpos-graph.hh', + 'graph/markbasepos-graph.hh', + 'graph/coverage-graph.hh', + 'graph/classdef-graph.hh', + 'graph/split-helpers.hh', + 'hb-subset.cc', + 'hb-subset.hh', +) + +hb_subset_headers = files( + 'hb-subset.h', + 'hb-subset-repacker.h' +) + +hb_gobject_sources = files( + 'hb-gobject-structs.cc' +) + +hb_gobject_headers = files( + 'hb-gobject.h', + 'hb-gobject-structs.h', +) + +ragel = find_program('ragel', version: '6.10', required: false) +has_ragel = ragel.found() +if not has_ragel and get_option('ragel_subproject') + ragel = subproject('ragel').get_variable('ragel') + has_ragel = true +endif +if not has_ragel + if not meson.is_subproject() + warning('You have to install ragel if you are going to develop HarfBuzz itself') + endif +else + ragel_helper = find_program('gen-ragel-artifacts.py') + foreach rl : hb_base_ragel_sources + hh = rl.split('.')[0] + '.hh' + custom_target('@0@'.format(hh), + build_by_default: true, + input: rl, + output: hh, + command: [ragel_helper, ragel, '@OUTPUT@', meson.current_source_dir(), '@INPUT@'], + ) + endforeach +endif + +custom_target('harfbuzz.cc', + build_by_default: true, + output: 'harfbuzz.cc', + input: hb_base_sources + hb_glib_sources + hb_ft_sources + + hb_graphite2_sources + hb_uniscribe_sources + hb_gdi_sources + + hb_directwrite_sources + hb_coretext_sources, + command: [find_program('gen-harfbuzzcc.py'), + '@OUTPUT@', meson.current_source_dir(), '@INPUT@'], +) + +incsrc = include_directories('.') + +hb_sources = hb_base_sources + hb_base_ragel_generated_sources +hb_headers = hb_base_headers + +harfbuzz_deps = [thread_dep, m_dep] + harfbuzz_extra_deps + +libharfbuzz_link_language = 'c' + +if conf.get('HAVE_FREETYPE', 0) == 1 + hb_sources += hb_ft_sources + hb_headers += hb_ft_headers + harfbuzz_deps += [freetype_dep] +endif + +if conf.get('HAVE_GDI', 0) == 1 + hb_sources += hb_gdi_sources + hb_headers += hb_gdi_headers + harfbuzz_deps += gdi_uniscribe_deps +endif + +if conf.get('HAVE_GRAPHITE2', 0) == 1 + hb_sources += hb_graphite2_sources + hb_headers += hb_graphite2_headers + harfbuzz_deps += [graphite2_dep, graphite_dep] +endif + +if conf.get('HAVE_GLIB', 0) == 1 + hb_sources += hb_glib_sources + hb_headers += hb_glib_headers + harfbuzz_deps += [glib_dep] +endif + +if conf.get('HAVE_UNISCRIBE', 0) == 1 + hb_sources += hb_uniscribe_sources + hb_headers += hb_uniscribe_headers +endif + +if conf.get('HAVE_DIRECTWRITE', 0) == 1 + hb_sources += hb_directwrite_sources + hb_headers += hb_directwrite_headers + # hb-directwrite needs a C++ linker + libharfbuzz_link_language = 'cpp' +endif + +if conf.get('HAVE_CORETEXT', 0) == 1 + hb_sources += hb_coretext_sources + hb_headers += hb_coretext_headers + harfbuzz_deps += coretext_deps +endif + +have_icu = conf.get('HAVE_ICU', 0) == 1 +have_icu_builtin = conf.get('HAVE_ICU_BUILTIN', 0) == 1 + +if have_icu and have_icu_builtin + hb_sources += hb_icu_sources + hb_headers += hb_icu_headers + harfbuzz_deps += [icu_dep] +endif + +# harfbuzz +gen_def = find_program('gen-def.py') + +harfbuzz_def_command_args = [gen_def, '@OUTPUT@', '@INPUT@'] +if get_option('experimental_api') + harfbuzz_def_command_args += '--experimental-api' +endif + +harfbuzz_def = custom_target('harfbuzz.def', + command: harfbuzz_def_command_args, + input: hb_headers, + output: 'harfbuzz.def') +defs_list = [harfbuzz_def] + +version = '0.@0@.0'.format(hb_version_int) + +extra_hb_cpp_args = [] +if cpp.get_argument_syntax() == 'msvc' + if get_option('default_library') != 'static' + extra_hb_cpp_args += '-DHB_DLL_EXPORT' + endif + hb_so_version = '' +else + hb_so_version = '0' +endif + +if get_option('fuzzer_ldflags') != '' + extra_hb_cpp_args += ['-DHB_CUSTOM_MALLOC'] + hb_sources += 'failing-alloc.c' + hb_subset_sources += 'failing-alloc.c' + hb_icu_sources += 'failing-alloc.c' + hb_gobject_sources += 'failing-alloc.c' +endif + +darwin_versions = [hb_version_int, '@0@.0.0'.format(hb_version_int)] + +libharfbuzz = library('harfbuzz', hb_sources, + include_directories: incconfig, + dependencies: harfbuzz_deps, + cpp_args: cpp_args + extra_hb_cpp_args, + soversion: hb_so_version, + version: version, + install: true, + darwin_versions: darwin_versions, + link_language: libharfbuzz_link_language, +) + +libharfbuzz_dep = declare_dependency( + link_with: libharfbuzz, + include_directories: incsrc, + dependencies: harfbuzz_deps) + +# harfbuzz-subset +harfbuzz_subset_def = custom_target('harfbuzz-subset.def', + command: [gen_def, '@OUTPUT@', '@INPUT@'], + input: hb_subset_headers, + output: 'harfbuzz-subset.def') +defs_list += [harfbuzz_subset_def] + +libharfbuzz_subset = library('harfbuzz-subset', hb_subset_sources, + include_directories: incconfig, + dependencies: [m_dep], + link_with: [libharfbuzz], + cpp_args: cpp_args + extra_hb_cpp_args, + soversion: hb_so_version, + version: version, + install: true, + darwin_versions: darwin_versions, + link_language: 'c', +) + +custom_target('harfbuzz-subset.cc', + build_by_default: true, + output: 'harfbuzz-subset.cc', + input: hb_base_sources + hb_subset_sources, + command: [find_program('gen-harfbuzzcc.py'), + '@OUTPUT@', meson.current_source_dir(), '@INPUT@'], +) + +libharfbuzz_subset_dep = declare_dependency( + link_with: libharfbuzz_subset, + include_directories: incsrc, + dependencies: [m_dep]) + +if get_option('tests').enabled() + # TODO: MSVC gives the following, + # error LNK2019: unresolved external symbol "unsigned __int64 const * const _hb_NullPool" + if cpp.get_argument_syntax() != 'msvc' + noinst_programs = { + 'main': 'main.cc', + 'test-basics': 'test.cc', + 'test-buffer-serialize': 'test-buffer-serialize.cc', + 'test-ot-meta': 'test-ot-meta.cc', + 'test-ot-name': 'test-ot-name.cc', + 'test-ot-glyphname': 'test-ot-glyphname.cc', + 'test-ot-gpos-size-params': 'test-gpos-size-params.cc', + 'test-ot-gsub-would-substitute': 'test-gsub-would-substitute.cc', + 'test-use-table': 'test-use-table.cc', + } + foreach name, source : noinst_programs + executable(name, source, + include_directories: incconfig, + cpp_args: cpp_args, + dependencies: libharfbuzz_dep, + install: false, + ) + endforeach + endif + + compiled_tests = { + 'test-algs': ['test-algs.cc', 'hb-static.cc'], + 'test-array': ['test-array.cc'], + 'test-iter': ['test-iter.cc', 'hb-static.cc'], + 'test-machinery': ['test-machinery.cc', 'hb-static.cc'], + 'test-map': ['test-map.cc', 'hb-static.cc'], + 'test-number': ['test-number.cc', 'hb-number.cc'], + 'test-ot-tag': ['hb-ot-tag.cc'], + 'test-priority-queue': ['test-priority-queue.cc', 'hb-static.cc'], + 'test-repacker': ['test-repacker.cc', 'hb-static.cc', 'graph/gsubgpos-context.cc'], + 'test-classdef-graph': ['graph/test-classdef-graph.cc', 'hb-static.cc', 'graph/gsubgpos-context.cc'], + 'test-set': ['test-set.cc', 'hb-static.cc'], + 'test-serialize': ['test-serialize.cc', 'hb-static.cc'], + 'test-unicode-ranges': ['test-unicode-ranges.cc'], + 'test-vector': ['test-vector.cc', 'hb-static.cc'], + 'test-bimap': ['test-bimap.cc', 'hb-static.cc'], + } + foreach name, source : compiled_tests + if cpp.get_argument_syntax() == 'msvc' and source.contains('hb-static.cc') + # TODO: MSVC doesn't like tests having hb-static.cc, fix them + continue + endif + test(name, executable(name, source, + include_directories: incconfig, + cpp_args: cpp_args + ['-DMAIN', '-UNDEBUG'], + dependencies: libharfbuzz_dep, + install: false, + ), suite: ['src']) + endforeach +endif + +pkgmod.generate(libharfbuzz, + description: 'HarfBuzz text shaping library', + subdirs: [meson.project_name()], + version: meson.project_version(), +) + +pkgmod.generate(libharfbuzz_subset, + description: 'HarfBuzz font subsetter', + requires: ['harfbuzz = @0@'.format(meson.project_version())], + subdirs: [meson.project_name()], + version: meson.project_version(), +) + +libharfbuzz_icu_dep = null_dep +if have_icu and not have_icu_builtin + harfbuzz_icu_def = custom_target('harfbuzz-icu.def', + command: [gen_def, '@OUTPUT@', '@INPUT@'], + input: [hb_icu_headers], + output: 'harfbuzz-icu.def') + defs_list += [harfbuzz_icu_def] + + libharfbuzz_icu = library('harfbuzz-icu', [hb_icu_sources, hb_icu_headers], + include_directories: incconfig, + dependencies: icu_dep, + link_with: [libharfbuzz], + cpp_args: cpp_args + extra_hb_cpp_args, + soversion: hb_so_version, + version: version, + install: true, + darwin_versions: darwin_versions, + # ICU links to stdc++ anyway so the default linker is good + # link_language: 'c', + ) + + libharfbuzz_icu_dep = declare_dependency( + link_with: libharfbuzz_icu, + include_directories: incsrc, + dependencies: icu_dep) + + pkgmod.generate(libharfbuzz_icu, + description: 'HarfBuzz text shaping library ICU integration', + requires: ['harfbuzz = @0@'.format(meson.project_version())], + subdirs: [meson.project_name()], + version: meson.project_version(), + ) + + install_headers(hb_icu_headers, subdir: meson.project_name()) +endif + +have_gobject = conf.get('HAVE_GOBJECT', 0) == 1 + +cmake_config = configuration_data() +cmake_config.set('libdir', '${prefix}/@0@'.format(get_option('libdir'))) +cmake_config.set('includedir', '${prefix}/@0@'.format(get_option('includedir'))) +cmake_config.set('HB_LIBTOOL_VERSION_INFO', hb_libtool_version_info) +cmake_config.set('have_gobject', '@0@'.format(have_gobject)) +configure_file(input: 'harfbuzz-config.cmake.in', + output: 'harfbuzz-config.cmake', + configuration: cmake_config, + install_dir: get_option('libdir') / 'cmake' / 'harfbuzz', +) + +libharfbuzz_gobject_dep = null_dep +if have_gobject + gnome = import('gnome') + + h_templ = configure_file( + input: 'hb-gobject-enums.h.tmpl', + output: 'hb-gobject-enums-tmp.h.tmpl', + copy: true) + + cc_templ = configure_file( + input: 'hb-gobject-enums.cc.tmpl', + output: 'hb-gobject-enums-tmp.cc.tmpl', + copy: true) + + enums = gnome.mkenums('hb-gobject', + sources: hb_headers, + h_template: h_templ, + c_template: cc_templ, + identifier_prefix: 'hb_', + symbol_prefix: 'hb_gobject', + ) + + enum_c = custom_target('hb-gobject-enums.cc', + input: enums[0], + output: 'hb-gobject-enums.cc', + command: [find_program('fix_get_types.py'), '@INPUT@', '@OUTPUT@'] + ) + + enum_h = custom_target('hb-gobject-enums.h', + input: enums[1], + output: 'hb-gobject-enums.h', + command: [find_program('fix_get_types.py'), '@INPUT@', '@OUTPUT@'], + install: true, + install_dir: get_option('prefix') / get_option('includedir') / meson.project_name(), + ) + + hb_gobject_sources += [enum_c] + + harfbuzz_gobject_def = custom_target('harfbuzz-gobject.def', + command: [gen_def, '@OUTPUT@', '@INPUT@'], + input: [hb_gobject_headers, enum_h], + output: 'harfbuzz-gobject.def') + defs_list += [harfbuzz_gobject_def] + + libharfbuzz_gobject = library('harfbuzz-gobject', [hb_gobject_sources, enum_c, enum_h], + include_directories: incconfig, + dependencies: [glib_dep, gobject_dep], + link_with: [libharfbuzz], + cpp_args: cpp_args + extra_hb_cpp_args, + soversion: hb_so_version, + version: version, + install: true, + darwin_versions: darwin_versions, + link_language: 'c', + ) + + gir = find_program('g-ir-scanner', required: get_option('introspection')) + build_gir = gir.found() and (not meson.is_cross_build() or get_option('introspection').enabled()) + + build_gir = build_gir and get_option('default_library') != 'static' + if not build_gir and get_option('introspection').enabled() + error('Introspection support is requested but the default library option should be shared or both') + endif + + if build_gir + conf.set('HAVE_INTROSPECTION', 1) + hb_gen_files_gir = gnome.generate_gir(libharfbuzz_gobject, + sources: [hb_headers, hb_sources, hb_gobject_headers, hb_gobject_sources, enum_h], + dependencies: libharfbuzz_dep, + namespace: 'HarfBuzz', + nsversion: '0.0', + identifier_prefix: 'hb_', + symbol_prefix: ['hb', 'hb_gobject'], + includes: ['GObject-2.0', 'freetype2-2.0'], + export_packages: ['harfbuzz-gobject'], + header: 'hb-gobject.h', + install: true, + extra_args: ['--cflags-begin', + '-DHB_NO_SINGLE_HEADER_ERROR', + '-DHAVE_GOBJECT', + '-DHB_EXTERN=', + '--cflags-end']) + endif + + libharfbuzz_gobject_dep = declare_dependency( + link_with: libharfbuzz_gobject, + include_directories: incsrc, + sources: build_gir ? hb_gen_files_gir : hb_gobject_sources, + dependencies: [glib_dep, gobject_dep]) + + pkgmod.generate(libharfbuzz_gobject, + description: 'HarfBuzz text shaping library GObject integration', + requires: ['harfbuzz = @0@'.format(meson.project_version()), 'glib-2.0', 'gobject-2.0'], + subdirs: [meson.project_name()], + version: meson.project_version(), + ) + + install_headers(hb_gobject_headers, subdir: meson.project_name()) +else + if get_option('introspection').enabled() + error('introspection requires gobject to be enabled') + endif +endif + +if get_option('tests').enabled() + dist_check_script = [ + 'check-c-linkage-decls', + 'check-externs', + 'check-header-guards', + 'check-includes', + ] + + env = environment() + env.set('srcdir', meson.current_source_dir()) + env.set('base_srcdir', meson.source_root()) + env.set('builddir', meson.current_build_dir()) + env.set('libs', meson.current_build_dir()) # TODO: Merge this with builddir after autotools removal + HBSOURCES = [] + foreach f : hb_sources + HBSOURCES += '@0@'.format(f) + endforeach + env.set('HBSOURCES', ' '.join(HBSOURCES)) + HBHEADERS = [] + foreach f : hb_headers + HBHEADERS += '@0@'.format(f) + endforeach + env.set('HBHEADERS', ' '.join(HBHEADERS)) + + if cpp.get_argument_syntax() != 'msvc' and not meson.is_cross_build() # ensure the local tools are usable + dist_check_script += ['check-libstdc++', 'check-static-inits', 'check-symbols'] + endif + + foreach name : dist_check_script + test(name, find_program(name + '.py'), + env: env, + depends: name == 'check-symbols' ? defs_list : [], + suite: ['src'], + ) + endforeach +endif + +install_headers(hb_headers + hb_subset_headers, subdir: meson.project_name()) diff --git a/libs/harfbuzz/src/ms-use/COPYING b/libs/harfbuzz/src/ms-use/COPYING new file mode 100644 index 00000000..9e841e7a --- /dev/null +++ b/libs/harfbuzz/src/ms-use/COPYING @@ -0,0 +1,21 @@ + MIT License + + Copyright (c) Microsoft Corporation. + + 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/libs/harfbuzz/src/ms-use/IndicPositionalCategory-Additional.txt b/libs/harfbuzz/src/ms-use/IndicPositionalCategory-Additional.txt new file mode 100644 index 00000000..884fee59 --- /dev/null +++ b/libs/harfbuzz/src/ms-use/IndicPositionalCategory-Additional.txt @@ -0,0 +1,120 @@ +# Override values For Indic_Positional_Category +# Not derivable +# Initial version based on Unicode 7.0 by Andrew Glass 2014-03-17 +# Updated for Unicode 10.0 by Andrew Glass 2017-07-25 +# Ammended for Unicode 10.0 by Andrew Glass 2018-09-21 +# Updated for L2/19-083 by Andrew Glass 2019-05-06 +# Updated for Unicode 12.1 by Andrew Glass 2019-05-30 +# Updated for Unicode 13.0 by Andrew Glass 2020-07-28 +# Updated for Unicode 14.0 by Andrew Glass 2021-09-28 +# Updated for Unicode 15.0 by Andrew Glass 2022-09-16 + +# ================================================ +# ================================================ +# OVERRIDES TO ASSIGNED VALUES +# ================================================ +# ================================================ + +# Indic_Positional_Category=Bottom +0F72 ; Bottom # Mn TIBETAN VOWEL SIGN I # Not really below, but need to override to fit into Universal model +0F7A..0F7D ; Bottom # Mn [4] TIBETAN VOWEL SIGN E..TIBETAN VOWEL SIGN OO # Not really below, but need to override to fit into Universal model +0F80 ; Bottom # Mn TIBETAN VOWEL SIGN REVERSED I # Not really below, but need to override to fit into Universal model +A9BF ; Bottom # Mc JAVANESE CONSONANT SIGN CAKRA +10A38 ; Bottom # Mn KHAROSHTHI SIGN BAR ABOVE # Overriden, ccc controls order USE issue #26 +11127..11129 ; Bottom # Mn [3] CHAKMA VOWEL SIGN A..CHAKMA VOWEL SIGN II +1112D ; Bottom # Mn CHAKMA VOWEL SIGN AI +11130 ; Bottom # Mn CHAKMA VOWEL SIGN OI +1BF2..1BF3 ; Bottom # Mc [2] BATAK PANGOLAT..BATAK PANONGONAN # see USE issue #20 + + +# ================================================ + +# Indic_Positional_Category=Left +1C29 ; Left # Mc LEPCHA VOWEL SIGN OO # Reduced from Top_And_Left + +# ================================================ + + +# Indic_Positional_Category=Right +A9BE ; Right # Mc JAVANESE CONSONANT SIGN PENGKAL # Reduced from Bottom_And_Right +10A0C ; Right # Mn KHAROSHTHI VOWEL LENGTH MARK # Follows vowels and precedes vowel modifiers +11942 ; Right # Mc DIVES AKURU MEDIAL RA # Reduced from Bottom_And_Right + +# ================================================ + +# Indic_Positional_Category=Top +0F74 ; Top # Mn TIBETAN VOWEL SIGN U # Not really above, but need to override to fit into Universal model +1A18 ; Top # Mn BUGINESE VOWEL SIGN U # Workaround to allow below to occur before above by treating all below marks as above +AA35   ; Top # Mn       CHAM CONSONANT SIGN +1112A..1112B ; Top # Mn [2] CHAKMA VOWEL SIGN U..CHAKMA VOWEL SIGN UU # see USE issue #25 +11131..11132 ; Top # Mn [2] CHAKMA O MARK..CHAKMA AU MARK # see USE issue #25 +1E4EC..1E4EF ; Top # Mn [4] NAG MUNDARI SIGN MUHOR..NAG MUNDARI SIGN SUTUH # 1E4EE is below, but made to for ccc + +# ================================================ + +# Indic_Positional_Category=Top_And_Right +0E33 ; Top_And_Right # Lo THAI CHARACTER SARA AM # IPC has Right, which seems to be a mistake. +0EB3 ; Top_And_Right # Lo LAO VOWEL SIGN AM # IPC has Right, which seems to be a mistake. + +# ================================================ +# ================================================ +# VALUES NOT ASSIGNED IN Indic_Positional_Category +# ================================================ +# ================================================ + +# Indic_Positional_Category=Bottom +0859..085B ; Bottom # Mn [3] MANDAIC AFFRICATION MARK..MANDAIC GEMINATION MARK +18A9 ; Bottom # Mn MONGOLIAN LETTER ALI GALI DAGALGA +10AE5 ; Bottom # Mn MANICHAEAN ABBREVIATION MARK ABOVE # Overriden, ccc controls order +10AE6 ; Bottom # Mn MANICHAEAN ABBREVIATION MARK BELOW +10F46..10F47 ; Bottom # Mn [2] SOGDIAN COMBINING DOT BELOW..SOGDIAN COMBINING TWO DOTS BELOW +10F48..10F4A ; Bottom # Mn [3] SOGDIAN COMBINING DOT ABOVE..SOGDIAN COMBINING CURVE ABOVE # Overriden, ccc controls order +10F4B ; Bottom # Mn SOGDIAN COMBINING CURVE BELOW +10F4C ; Bottom # Mn SOGDIAN COMBINING HOOK ABOVE # Overriden, ccc controls order +10F4D..10F50 ; Bottom # Mn [4] SOGDIAN COMBINING HOOK BELOW..SOGDIAN COMBINING STROKE BELOW +10F82 ; Bottom # Mn OLD UYGHUR COMBINING DOT ABOVE # Overriden, ccc controls order +10F83 ; Bottom # Mn OLD UYGHUR COMBINING DOT BELOW +10F84 ; Bottom # Mn OLD UYGHUR COMBINING TWO DOTS ABOVE # Overriden, ccc controls order +10F85 ; Bottom # Mn OLD UYGHUR COMBINING TWO DOTS BELOW +16F4F ; Bottom # Mn MIAO SIGN CONSONANT MODIFIER BAR +16F51..16F87 ; Bottom # Mc [55] MIAO SIGN ASPIRATION..MIAO VOWEL SIGN UI +16F8F..16F92 ; Bottom # Mn [4] MIAO TONE RIGHT..MIAO TONE BELOW +#HIEROGLYPHS defined here while ISC is being used as a proxy for dedicated Hieroglyph cluster +13440 ; Bottom # Mn EGYPTIAN HIEROGLYPH MIRROR HORIZONTALLY +13447..13455 ; Bottom # Mn [15] EGYPTIAN HIEROGLYPH MODIFIER DAMAGED AT TOP START..EGYPTIAN HIEROGLYPH MODIFIER DAMAGED + +# ================================================ + +# Indic_Positional_Category=Left +103C ; Left # Mc MYANMAR CONSONANT SIGN MEDIAL RA + +# ================================================ + +# Indic_Positional_Category=Top +07EB..07F3 ; Top # Mn [9] NKO COMBINING SHORT HIGH TONE..NKO COMBINING DOUBLE DOT ABOVE +07FD ; Top # Mn NKO DANTAYALAN # Not really top, but assigned here to allow ccc to control mark order +1885..1886 ; Top # Mn [2] MONGOLIAN LETTER ALI GALI BALUDA..MONGOLIAN LETTER ALI GALI THREE BALUDA +1CF8..1CF9 ; Top # Mn [2] VEDIC TONE RING ABOVE..VEDIC TONE DOUBLE RING ABOVE +10D24..10D27 ; Top # Mn [4] HANIFI ROHINGYA SIGN HARBAHAY..HANIFI ROHINGYA SIGN TASSI +10EAB..10EAC ; Top # Mn [2] YEZIDI COMBINING HAMZA MARK..YEZIDI COMBINING MADDA MARK +16B30..16B36 ; Top # Mn [7] PAHAWH HMONG MARK CIM TUB..PAHAWH HMONG MARK CIM TAUM +1E130..1E136 ; Top # Mn [7] NYIAKENG PUACHUE HMONG TONE-B..NYIAKENG PUACHUE HMONG TONE-D +1E2AE ; Top # Mn TOTO SIGN RISING TONE +1E2EC..1E2EF ; Top # Mn [4] WANCHO TONE TUP..WANCHO TONE KOINI +1E944..1E94A ; Top # Mn [7] ADLAM ALIF LENGTHENER..ADLAM NUKTA + +# ================================================ + +# Indic_Positional_Category=Overstruck +1BC9D..1BC9E ; Overstruck # Mn [2] DUPLOYAN THICK LETTER SELECTOR..DUPLOYAN DOUBLE MARK + +# ================================================ +# ================================================ +# Deliberately suppressed +# ================================================ +# ================================================ + +# Indic_Positional_Category=NA +180B..180D ; NA # Mn [3] MONGOLIAN FREE VARIATION SELECTOR ONE..MONGOLIAN FREE VARIATION SELECTOR THREE +180F ; NA # Mn MONGOLIAN FREE VARIATION SELECTOR FOUR +2D7F ; NA # Mn TIFINAGH CONSONANT JOINER diff --git a/libs/harfbuzz/src/ms-use/IndicShapingInvalidCluster.txt b/libs/harfbuzz/src/ms-use/IndicShapingInvalidCluster.txt new file mode 100644 index 00000000..9e0edd34 --- /dev/null +++ b/libs/harfbuzz/src/ms-use/IndicShapingInvalidCluster.txt @@ -0,0 +1,113 @@ +# IndicShapingInvalidCluster.txt +# Date: 2015-03-12, 21:17:00 GMT [AG] +# Date: 2019-11-08, 23:22:00 GMT [AG] +# +# This file defines the following property: +# +# Indic_Shaping_Invalid_Cluster +# +# Scope: This file enumerates sequences of characters that should be treated as invalid clusters + + 0905 0946 ; # DEVANAGARI LETTER A, DEVANAGARI VOWEL SIGN SHORT E + 0905 093E ; # DEVANAGARI LETTER A, DEVANAGARI VOWEL SIGN AA + 0930 094D 0907 ; # DEVANAGARI LETTER RA, DEVANAGARI SIGN VIRAMA, DEVANAGARI LETTER I + 0909 0941 ; # DEVANAGARI LETTER U, DEVANAGARI VOWEL SIGN U + 090F 0945 ; # DEVANAGARI LETTER E, DEVANAGARI VOWEL SIGN CANDRA E + 090F 0946 ; # DEVANAGARI LETTER E, DEVANAGARI VOWEL SIGN SHORT E + 090F 0947 ; # DEVANAGARI LETTER E, DEVANAGARI VOWEL SIGN E + 0905 0949 ; # DEVANAGARI LETTER A, DEVANAGARI VOWEL SIGN CANDRA O + 0906 0945 ; # DEVANAGARI LETTER AA, DEVANAGARI VOWEL SIGN CANDRA E + 0905 094A ; # DEVANAGARI LETTER A, DEVANAGARI VOWEL SIGN SHORT O + 0906 0946 ; # DEVANAGARI LETTER AA, DEVANAGARI VOWEL SIGN SHORT E + 0905 094B ; # DEVANAGARI LETTER A, DEVANAGARI VOWEL SIGN O + 0906 0947 ; # DEVANAGARI LETTER AA, DEVANAGARI VOWEL SIGN E + 0905 094C ; # DEVANAGARI LETTER A, DEVANAGARI VOWEL SIGN AU + 0906 0948 ; # DEVANAGARI LETTER AA, DEVANAGARI VOWEL SIGN AI + 0905 0945 ; # DEVANAGARI LETTER A, DEVANAGARI VOWEL SIGN CANDRA E + 0905 093A ; # DEVANAGARI LETTER A, DEVANAGARI VOWEL SIGN OE + 0905 093B ; # DEVANAGARI LETTER A, DEVANAGARI VOWEL SIGN OOE + 0906 093A ; # DEVANAGARI LETTER AA, DEVANAGARI VOWEL SIGN OE + 0905 094F ; # DEVANAGARI LETTER A, DEVANAGARI VOWEL SIGN AW + 0905 0956 ; # DEVANAGARI LETTER A, DEVANAGARI VOWEL SIGN UE + 0905 0957 ; # DEVANAGARI LETTER A, DEVANAGARI VOWEL SIGN UUE + 0985 09BE ; # BENGALI LETTER A, BENGALI VOWEL SIGN AA + 098B 09C3 ; # BENGALI LETTER VOCALIC R, BENGALI VOWEL SIGN VOCALIC R + 098C 09E2 ; # BENGALI LETTER VOCALIC L, BENGALI VOWEL SIGN VOCALIC L + 0A05 0A3E ; # GURMUKHI LETTER A, GURMUKHI VOWEL SIGN AA + 0A72 0A3F ; # GURMUKHI IRI, GURMUKHI VOWEL SIGN I + 0A72 0A40 ; # GURMUKHI IRI, GURMUKHI VOWEL SIGN II + 0A73 0A41 ; # GURMUKHI URA, GURMUKHI VOWEL SIGN U + 0A73 0A42 ; # GURMUKHI URA, GURMUKHI VOWEL SIGN UU + 0A72 0A47 ; # GURMUKHI IRI, GURMUKHI VOWEL SIGN EE + 0A05 0A48 ; # GURMUKHI LETTER A, GURMUKHI VOWEL SIGN AI + 0A73 0A4B ; # GURMUKHI URA, GURMUKHI VOWEL SIGN OO + 0A05 0A4C ; # GURMUKHI LETTER A, GURMUKHI VOWEL SIGN AU + 0A85 0ABE ; # GUJARATI LETTER A, GUJARATI VOWEL SIGN AA + 0A85 0AC5 ; # GUJARATI LETTER A, GUJARATI VOWEL SIGN CANDRA E + 0A85 0AC7 ; # GUJARATI LETTER A, GUJARATI VOWEL SIGN E + 0A85 0AC8 ; # GUJARATI LETTER A, GUJARATI VOWEL SIGN AI + 0A85 0AC9 ; # GUJARATI LETTER A, GUJARATI VOWEL SIGN CANDRA O + 0A85 0ACB ; # GUJARATI LETTER A, GUJARATI VOWEL SIGN O + 0A85 0ABE 0AC5 ; # GUJARATI LETTER A, GUJARATI VOWEL SIGN AA, GUJARATI VOWEL SIGN CANDRA E + 0A85 0ACC ; # GUJARATI LETTER A, GUJARATI VOWEL SIGN AU + 0A85 0ABE 0AC8 ; # GUJARATI LETTER A, GUJARATI VOWEL SIGN AA, GUJARATI VOWEL SIGN AI + 0AC5 0ABE ; # GUJARATI VOWEL SIGN CANDRA E, GUJARATI VOWEL SIGN AA + 0B05 0B3E ; # ORIYA LETTER A, ORIYA VOWEL SIGN AA + 0B0F 0B57 ; # ORIYA LETTER E, ORIYA AU LENGTH MARK + 0B13 0B57 ; # ORIYA LETTER O, ORIYA AU LENGTH MARK + 0B85 0BC2 ; # TAMIL LETTER A, TAMIL VOWEL SIGN UU + 0C12 0C55 ; # TELUGU LETTER O, TELUGU LENGTH MARK + 0C12 0C4C ; # TELUGU LETTER O, TELUGU VOWEL SIGN AU + 0C3F 0C55 ; # TELUGU VOWEL SIGN I, TELUGU LENGTH MARK + 0C46 0C55 ; # TELUGU VOWEL SIGN E, TELUGU LENGTH MARK + 0C4A 0C55 ; # TELUGU VOWEL SIGN O, TELUGU LENGTH MARK + 0C89 0CBE ; # KANNADA LETTER U, KANNADA VOWEL SIGN AA + 0C92 0CCC ; # KANNADA LETTER O, KANNADA VOWEL SIGN AU + 0C8B 0CBE ; # KANNADA LETTER VOCALIC R, KANNADA VOWEL SIGN AA + 0D07 0D57 ; # MALAYALAM LETTER I, MALAYALAM AU LENGTH MARK + 0D09 0D57 ; # MALAYALAM LETTER U, MALAYALAM AU LENGTH MARK + 0D0E 0D46 ; # MALAYALAM LETTER E, MALAYALAM VOWEL SIGN E + 0D12 0D3E ; # MALAYALAM LETTER O, MALAYALAM VOWEL SIGN AA + 0D12 0D57 ; # MALAYALAM LETTER O, MALAYALAM AU LENGTH MARK + 0D85 0DCF ; # SINHALA LETTER AYANNA, SINHALA VOWEL SIGN AELA-PILLA + 0D85 0DD0 ; # SINHALA LETTER AYANNA, SINHALA VOWEL SIGN KETTI AEDA-PILLA + 0D85 0DD1 ; # SINHALA LETTER AYANNA, SINHALA VOWEL SIGN DIGA AEDA-PILLA + 0D8B 0DDF ; # SINHALA LETTER UYANNA, SINHALA VOWEL SIGN GAYANUKITTA + 0D8D 0DD8 ; # SINHALA LETTER IRUYANNA, SINHALA VOWEL SIGN GAETTA-PILLA + 0D8F 0DDF ; # SINHALA LETTER ILUYANNA, SINHALA VOWEL SIGN GAYANUKITTA + 0D91 0DCA ; # SINHALA LETTER EYANNA, SINHALA SIGN AL-LAKUNA + 0D91 0DD9 ; # SINHALA LETTER EYANNA, SINHALA VOWEL SIGN KOMBUVA + 0D91 0DDA ; # SINHALA LETTER EYANNA, SINHALA VOWEL SIGN DIGA KOMBUVA + 0D91 0DDC ; # SINHALA LETTER EYANNA, SINHALA VOWEL SIGN KOMBUVA HAA AELA-PILLA + 0D91 0DDD ; # SINHALA LETTER EYANNA, SINHALA VOWEL SIGN KOMBUVA HAA DIGA AELA-PILLA + 0D91 0DDE ; # SINHALA LETTER EYANNA, SINHALA VOWEL SIGN KOMBUVA HAA GAYANUKITTA + 0D94 0DDF ; # SINHALA LETTER OYANNA, SINHALA VOWEL SIGN GAYANUKITTA + 11005 11038 ; # BRAHMI LETTER A, BRAHMI VOWEL SIGN AA + 1100B 1103E ; # BRAHMI LETTER VOCALIC R, BRAHMI VOWEL SIGN VOCALIC R + 1100F 11042 ; # BRAHMI LETTER E, BRAHMI VOWEL SIGN E + 11680 116AD ; # TAKRI LETTER A, TAKRI VOWEL SIGN AA + 11686 116B2 ; # TAKRI LETTER E, TAKRI VOWEL SIGN E + 11680 116B4 ; # TAKRI LETTER A, TAKRI VOWEL SIGN O + 11680 116B5 ; # TAKRI LETTER A, TAKRI VOWEL SIGN AU + 11200 1122C ; # KHOJKI LETTER A, KHOJKI VOWEL SIGN AA + 11240 1122E ; # KHOJKI LETTER SHORT I, KHOJKI VOWEL SIGN II + 11206 1122C ; # KHOJKI LETTER O, KHOJKI VOWEL SIGN AA + 11200 11231 ; # KHOJKI LETTER A, KHOJKI VOWEL SIGN AI + 11200 11233 ; # KHOJKI LETTER A, KHOJKI VOWEL SIGN AU + 11200 1122C 11231 ; # KHOJKI LETTER A, KHOJKI VOWEL SIGN AA, KHOJKI VOWEL SIGN AI + 1122C 11230 ; # KHOJKI VOWEL SIGN AA, KHOJKI VOWEL SIGN E + 1122C 11231 ; # KHOJKI VOWEL SIGN AA, KHOJKI VOWEL SIGN AI + 112B0 112E0 ; # KHUDAWADI LETTER A, KHUDAWADI VOWEL SIGN AA + 112B0 112E5 ; # KHUDAWADI LETTER A, KHUDAWADI VOWEL SIGN E + 112B0 112E6 ; # KHUDAWADI LETTER A, KHUDAWADI VOWEL SIGN AI + 112B0 112E7 ; # KHUDAWADI LETTER A, KHUDAWADI VOWEL SIGN O + 112B0 112E8 ; # KHUDAWADI LETTER A, KHUDAWADI VOWEL SIGN AU + 11481 114B0 ; # TIRHUTA LETTER A, TIRHUTA VOWEL SIGN AA + 114AA 114B5 ; # TIRHUTA LETTER LA, TIRHUTA VOWEL SIGN VOCALIC R + 114AA 114B6 ; # TIRHUTA LETTER LA, TIRHUTA VOWEL SIGN VOCALIC RR + 1148B 114BA ; # TIRHUTA LETTER E, TIRHUTA VOWEL SIGN SHORT E + 1148D 114BA ; # TIRHUTA LETTER O, TIRHUTA VOWEL SIGN SHORT E + 11600 11639 ; # MODI LETTER A, MODI VOWEL SIGN E + 11600 1163A ; # MODI LETTER A, MODI VOWEL SIGN AI + 11601 11639 ; # MODI LETTER AA, MODI VOWEL SIGN E + 11601 1163A ; # MODI LETTER AA, MODI VOWEL SIGN AI diff --git a/libs/harfbuzz/src/ms-use/IndicSyllabicCategory-Additional.txt b/libs/harfbuzz/src/ms-use/IndicSyllabicCategory-Additional.txt new file mode 100644 index 00000000..0ca2aad1 --- /dev/null +++ b/libs/harfbuzz/src/ms-use/IndicSyllabicCategory-Additional.txt @@ -0,0 +1,268 @@ +# Override values For Indic_Syllabic_Category +# Not derivable +# Initial version based on Unicode 7.0 by Andrew Glass 2014-03-17 +# Updated for Unicode 10.0 by Andrew Glass 2017-07-25 +# Updated for Unicode 12.1 by Andrew Glass 2019-05-24 +# Updated for Unicode 13.0 by Andrew Glass 2020-07-28 +# Updated for Unicode 14.0 by Andrew Glass 2021-09-25 +# Updated for Unicode 15.0 by Andrew Glass 2022-09-16 + +# ================================================ +# OVERRIDES TO ASSIGNED VALUES +# ================================================ + +# Indic_Syllabic_Category=Bindu +193A ; Bindu # Mn LIMBU SIGN KEMPHRENG +AA29 ; Bindu # Mn  CHAM VOWEL SIGN AA +10A0D ; Bindu # Mn KHAROSHTHI SIGN DOUBLE RING BELOW + +# ================================================ + +# Indic_Syllabic_Category=Consonant +19C1..19C7 ; Consonant # Lo [7] NEW TAI LUE LETTER FINAL V..NEW TAI LUE LETTER FINAL B # Reassigned to avoid clustering with a base consonant +25CC ; Consonant # So DOTTED CIRCLE #Reassigned to allow it to cluster as a generic base + +# ================================================ + +# Indic_Syllabic_Category=Consonant_Dead +0F7F ; Consonant_Dead # Mc TIBETAN SIGN RNAM BCAD # reassigned so that visarga can form an independent cluster, but see #19 + +# ================================================ + +# Indic_Syllabic_Category=Consonant_Final_Modifier +1C36 ; Consonant_Final_Modifier # Mn LEPCHA SIGN RAN + +# ================================================ + +# Indic_Syllabic_Category=Gemination_Mark +11134 ; Gemination_Mark # Mc CHAKMA MAAYYAA + +# ================================================ + +# Indic_Syllabic_Category=Nukta +0F71 ; Nukta # Mn TIBETAN VOWEL SIGN AA # Reassigned to get this before an above vowel, but see #22 +1BF2..1BF3 ; Nukta # Mc [2] BATAK PANGOLAT..BATAK PANONGONAN # see USE issue #20 + +# ================================================ + +# Indic_Syllabic_Category=Tone_Mark +1A7B..1A7C ; Tone_Mark # Mn [2] TAI THAM SIGN MAI SAM..TAI THAM SIGN KHUEN-LUE KARAN +1A7F ; Tone_Mark # Mn TAI THAM COMBINING CRYPTOGRAMMIC DOT + +# ================================================ + +# Indic_Syllabic_Category=Vowel_Independent +AAB1 ; Vowel_Independent # Lo TAI VIET VOWEL AA +AABA ; Vowel_Independent # Lo TAI VIET VOWEL UA +AABD ; Vowel_Independent # Lo TAI VIET VOWEL AN + +# ================================================ +# ================================================ +# VALUES NOT ASSIGNED IN Indic_Syllabic_Category +# ================================================ +# ================================================ + +# Indic_Syllabic_Category=Consonant +0800..0815 ; Consonant # Lo [22] SAMARITAN LETTER ALAF..SAMARITAN LETTER TAAF +0840..0858 ; Consonant # Lo [25] MANDAIC LETTER HALQA..MANDAIC LETTER AIN +0F00..0F01 ; Consonant # Lo [2] TIBETAN SYLLABLE OM..TIBETAN MARK GTER YIG MGO TRUNCATED +0F04..0F06 ; Consonant # Po TIBETAN MARK INITIAL YIG MGO MDUN MA..TIBETAN MARK CARET YIG MGO PHUR SHAD MA +1800 ; Consonant # Po MONGOLIAN BIRGA # Reassigned so that legacy Birga + MFVS sequences still work +1807 ; Consonant # Po MONGOLIAN SIBE SYLLABLE BOUNDARY MARKER +180A ; Consonant # Po MONGOLIAN NIRUGU +1820..1878 ; Consonant # Lo [88] MONGOLIAN LETTER A..MONGOLIAN LETTER CHA WITH TWO DOTS +1843 ; Consonant # Lm MONGOLIAN LETTER TODO LONG VOWEL SIGN +2D30..2D67 ; Consonant # Lo [56] TIFINAGH LETTER YA..TIFINAGH LETTER YO +2D6F ; Consonant # Lm TIFINAGH MODIFIER LETTER LABIALIZATION MARK +10570..1057A ; Consonant # Lo [11] VITHKUQI CAPITAL LETTER A..VITHKUQI CAPITAL LETTER GA +1057C..1058A ; Consonant # Lo [15] VITHKUQI CAPITAL LETTER HA..VITHKUQI CAPITAL LETTER RE +1058C..10592 ; Consonant # Lo [7] VITHKUQI CAPITAL LETTER SE..VITHKUQI CAPITAL LETTER XE +10594..10595 ; Consonant # Lo [2] VITHKUQI CAPITAL LETTER Y..VITHKUQI CAPITAL LETTER ZE +10597..105A1 ; Consonant # Lo [11] VITHKUQI SMALL LETTER A..VITHKUQI SMALL LETTER GA +105A3..105B1 ; Consonant # Lo [15] VITHKUQI SMALL LETTER HA..VITHKUQI SMALL LETTER RE +105B3..105B9 ; Consonant # Lo [7] VITHKUQI SMALL LETTER SE..VITHKUQI SMALL LETTER XE +105BB..105BC ; Consonant # Lo [2] VITHKUQI SMALL LETTER Y..VITHKUQI SMALL LETTER ZE +10AC0..10AC7 ; Consonant # Lo [8] MANICHAEAN LETTER ALEPH..MANICHAEAN LETTER WAW +10AC9..10AE4 ; Consonant # Lo [28] MANICHAEAN LETTER ZAYIN..MANICHAEAN LETTER TAW +10D00..10D23 ; Consonant # Lo [36] HANIFI ROHINGYA LETTER A..HANIFI ROHINGYA MARK NA KHONNA +10E80..10EA9 ; Consonant # Lo [42] YEZIDI LETTER ELIF..YEZIDI LETTER ET +10EB0..10EB1 ; Consonant # Lo [2] YEZIDI LETTER LAM WITH DOT ABOVE..YEZIDI LETTER YOT WITH CIRCUMFLEX ABOVE +10F30..10F45 ; Consonant # Lo [22] SOGDIAN LETTER ALEPH..SOGDIAN INDEPENDENT SHIN +10F70..10F81 ; Consonant # Lo [18] OLD UYGHUR LETTER ALEPH..OLD UYGHUR LETTER LESH +111DA ; Consonant # Lo SHARADA EKAM +#HIEROGLYPHS to be moved to new category +13000..1342F ; Consonant # Lo [1071] EGYPTIAN HIEROGLYPH A001..EGYPTIAN HIEROGLYPH V011D +#For the Begin and End segment to be handled fully correctly, the cluster model needs to be modified. +13437..13438 ; Consonant # Lo [2] EGYPTIAN HIEROGLYPH BEGIN SEGMENT..EGYPTIAN HIEROGLYPH END SEGMENT +13441..13446 ; Consonant # Lo [6] EGYPTIAN HIEROGLYPH FULL BLANK..HIEROGLYPH WIDE LOST SIGN +16B00..16B2F ; Consonant # Lo [48] PAHAWH HMONG VOWEL KEEB..PAHAWH HMONG CONSONANT CAU +16F00..16F4A ; Consonant # Lo [75] MIAO LETTER PA..MIAO LETTER RTE +16FE4 ; Consonant # Mn KHITAN SMALL SCRIPT FILLER # Avoids Mn pushing this into VOWEL class +18B00..18CD5 ; Consonant # Lo [470] KHITAN SMALL SCRIPT CHARACTER-18B00..KHITAN SMALL SCRIPT CHARACTER-18CD5 +1BC00..1BC6A ; Consonant # Lo [107] DUPLOYAN LETTER H..DUPLOYAN LETTER VOCALIC M +1BC70..1BC7C ; Consonant # Lo [13] DUPLOYAN AFFIX LEFT HORIZONTAL SECANT..DUPLOYAN AFFIX ATTACHED TANGENT HOOK +1BC80..1BC88 ; Consonant # Lo [9] DUPLOYAN AFFIX HIGH ACUTE..DUPLOYAN AFFIX HIGH VERTICAL +1BC90..1BC99 ; Consonant # Lo [10] DUPLOYAN AFFIX LOW ACUTE..DUPLOYAN AFFIX LOW ARROW +1E100..1E12C ; Consonant # Lo [45] NYIAKENG PUACHUE HMONG LETTER MA..NYIAKENG PUACHUE HMONG LETTER W +1E137..1E13D ; Consonant # Lm [7] NYIAKENG PUACHUE HMONG SIGN FOR PERSON..NYIAKENG PUACHUE HMONG SYLLABLE LENGTHENER +1E14E ; Consonant # Lo NYIAKENG PUACHUE HMONG LOGOGRAM NYAJ +1E14F ; Consonant # So NYIAKENG PUACHUE HMONG CIRCLED CA +1E290..1E2AD ; Consonant # Lo [30] TOTO LETTER PA..TOTO LETTER A +1E2C0..1E2EB ; Consonant # Lo [44] WANCHO LETTER AA..WANCHO LETTER YIH +1E4D0..1E4EA ; Consonant # Lo [27] NAG MUNDARI LETTER O..NAG MUNDARI LETTER ELL +1E4EB ; Consonant # Lm NAG MUNDARI SIGN OJOD +1E900..1E921 ; Consonant # Lu [34] ADLAM CAPITAL LETTER ALIF..ADLAM CAPITAL LETTER SHA +1E922..1E943 ; Consonant # Ll [34] ADLAM SMALL LETTER ALIF..ADLAM SMALL LETTER SHA +1E94B ; Consonant # Lm ADLAM NASALIZATION MARK + +# ================================================ + +# Indic_Syllabic_Category=Consonant_Placeholder +1880..1884 ; Consonant_Placeholder # Lo [5] MONGOLIAN LETTER ALI GALI ANUSVARA ONE..MONGOLIAN LETTER ALI GALI INVERTED UBADAMA + +# ================================================ + +# Indic_Syllabic_Category=Gemination_Mark +10D27 ; Gemination_Mark # Mn HANIFI ROHINGYA SIGN TASSI + +# ================================================ + +# Indic_Syllabic_Category=Modifying_Letter +FE00..FE0F ; Modifying_Letter # Mn [16] VARIATION SELECTOR-1..VARIATION SELECTOR-16# Need to treat them as isolated bases so they don't merge with a cluster in invalid scenarios +16F50 ; Modifying_Letter # Lo MIAO LETTER NASALIZATION + +# ================================================ + +# Indic_Syllabic_Category=Nukta +0859..085B ; Nukta # Mn [3] MANDAIC AFFRICATION MARK..MANDAIC GEMINATION MARK +0F39 ; Nukta # Mn TIBETAN MARK TSA -PHRU # NOW IN UNICODE 10.0 +1885..1886 ; Nukta # Mn [2] MONGOLIAN LETTER ALI GALI BALUDA..MONGOLIAN LETTER ALI GALI THREE BALUDA +18A9 ; Nukta # Mn MONGOLIAN LETTER ALI GALI DAGALGA +10AE5..10AE6 ; Nukta # Mn [2] MANICHAEAN ABBREVIATION MARK ABOVE..MANICHAEAN ABBREVIATION MARK BELOW +16F4F ; Nukta # Mn MIAO SIGN CONSONANT MODIFIER BAR +1BC9D..1BC9E ; Nukta # Mn [2] DUPLOYAN THICK LETTER SELECTOR..DUPLOYAN DOUBLE MARK +1E944..1E94A ; Nukta # Mn [7] ADLAM ALIF LENGTHENER..ADLAM NUKTA +10F82..10F85 ; Nukta # Mn [4] OLD UYGHUR COMBINING DOT ABOVE..OLD UYGHUR COMBINING TWO DOTS BELOW + +# ================================================ + +# Indic_Syllabic_Category=Number +10D30..10D39 ; Number # Nd [10] HANIFI ROHINGYA DIGIT ZERO..HANIFI ROHINGYA DIGIT NINE +10F51..10F54 ; Number # No [4] SOGDIAN NUMBER ONE..SOGDIAN NUMBER ONE HUNDRED +16AC0..16AC9 ; Number # Nd [10] TANGSA DIGIT ZERO..TANGSA DIGIT NINE +1E140..1E149 ; Number # Nd [10] NYIAKENG PUACHUE HMONG DIGIT ZERO..NYIAKENG PUACHUE HMONG DIGIT NINE +1E2F0..1E2F9 ; Number # Nd [10] WANCHO DIGIT ZERO..WANCHO DIGIT NINE +1E4F0..1E4F9 ; Number # Nd [10] NAG MUNDARI DIGIT ZERO..NAG MUNDARI DIGIT NINE +1E950..1E959 ; Number # Nd [10] ADLAM DIGIT ZERO..ADLAM DIGIT NINE + +# ================================================ + +# Indic_Syllabic_Category=Tone_Mark +07EB..07F3 ; Tone_Mark # Mn [9] NKO COMBINING SHORT HIGH TONE..NKO COMBINING DOUBLE DOT ABOVE +07FD ; Tone_Mark # Mn NKO DANTAYALAN +0F86..0F87 ; Tone_Mark # Mn [2] TIBETAN SIGN LCI RTAGS..TIBETAN SIGN YANG RTAGS +17CF ; Tone_Mark # Mn KHMER SIGN AHSDA +10D24..10D26 ; Tone_Mark # Mn [3] HANIFI ROHINGYA SIGN HARBAHAY..HANIFI ROHINGYA SIGN TANA +10F46..10F50 ; Tone_Mark # Mn [11] SOGDIAN COMBINING DOT BELOW..SOGDIAN COMBINING STROKE BELOW +16B30..16B36 ; Tone_Mark # Mn [7] PAHAWH HMONG MARK CIM TUB..PAHAWH HMONG MARK CIM TAUM +16F8F..16F92 ; Tone_Mark # Mn [4] MIAO TONE RIGHT..MIAO TONE BELOW +1E130..1E136 ; Tone_Mark # Mn [7] NYIAKENG PUACHUE HMONG TONE-B..NYIAKENG PUACHUE HMONG TONE-D +1E2AE ; Tone_Mark # Mn TOTO SIGN RISING TONE +1E2EC..1E2EF ; Tone_Mark # Mn [4] WANCHO TONE TUP..WANCHO TONE KOINI + +# ================================================ + +# Indic_Syllabic_Category=Virama +2D7F ; Virama # Mn TIFINAGH CONSONANT JOINER +#HIEROGLYPHS to be moved to new category +13430..13436 ; Virama # Cf [7] EGYPTIAN HIEROGLYPH VERTICAL JOINER..EGYPTIAN HIEROGLYPH OVERLAY MIDDLE +13439..1343B ; Virama # Cf [3] EGYPTIAN HIEROGLYPH INSERT AT MIDDLE..EGYPTIAN HIEROGLYPH INSERT AT BOTTOM + +# ================================================ + +# Indic_Syllabic_Category=Vowel_Independent +AAB1 ; Vowel_Independent # Lo TAI VIET VOWEL AA +AABA ; Vowel_Independent # Lo TAI VIET VOWEL UA +AABD ; Vowel_Independent # Lo TAI VIET VOWEL AN + +# ================================================ + +# Indic_Syllabic_Category=Vowel_Dependent +0B55 ; Vowel_Dependent # Mn ORIYA SIGN OVERLINE +10EAB..10EAC ; Vowel_Dependent # Mn [2] YEZIDI COMBINING HAMZA MARK..YEZIDI COMBINING MADDA MARK +16F51..16F87 ; Vowel_Dependent # Mc [55] MIAO SIGN ASPIRATION..MIAO VOWEL SIGN UI +1E4EC..1E4EF ; Vowel_Dependent # Mn [4] NAG MUNDARI SIGN MUHOR..NAG MUNDARI SIGN SUTUH + +# ================================================ + +# Indic_Syllabic_Category=Cantillation_Mark + +1CF8..1CF9 ; Cantillation_Mark # Mn [2] VEDIC TONE RING ABOVE..VEDIC TONE DOUBLE RING ABOVE +#HIEROGLYPHS to be moved to new category +13440 ; Cantillation_Mark # Mn EGYPTIAN HIEROGLYPH MIRROR HORIZONTALLY +13447..13455 ; Cantillation_Mark # Mn [15] EGYPTIAN HIEROGLYPH MODIFIER DAMAGED AT TOP START..EGYPTIAN HIEROGLYPH MODIFIER DAMAGED + +# ================================================ + +# Indic_Syllabic_Category=Symbol_Modifier +1B6B..1B73 ; Symbol_Modifier # Mn [9] BALINESE MUSICAL SYMBOL COMBINING TEGEH..BALINESE MUSICAL SYMBOL COMBINING GONG + +# ================================================ +# ================================================ +# PROPERTIES NOT ASSIGNED IN Indic_Syllabic_Category +# ================================================ +# ================================================ + +# USE, Extended_Syllabic_Category=Hieroglyph +# 13000..1342F ; Hieroglyph # Lo [1072] EGYPTIAN HIEROGLYPH A001..EGYPTIAN HIEROGLYPH V011D +# 13441..13446 ; Hieroglyph # Lo [6] EGYPTIAN HIEROGLYPH FULL BLANK..HIEROGLYPH WIDE LOST SIGN + +# ================================================ + +# USE, Extended_Syllabic_Category=Hieroglyph_Joiner +# 13430..13436 ; Hieroglyph_Joiner # Cf [7] EGYPTIAN HIEROGLYPH VERTICAL JOINER..EGYPTIAN HIEROGLYPH OVERLAY MIDDLE +# 13439..1343B ; Hieroglyph_Joiner # Cf [3] EGYPTIAN HIEROGLYPH INSERT AT MIDDLE..EGYPTIAN HIEROGLYPH INSERT AT BOTTOM + +# ================================================ + +# USE, Extended_Syllabic_Category=Hieroglyph_Mark_Begin +# 005B ; Hieroglyph_Mark_Begin # Ps LEFT SQUARE BRACKET +# 007B ; Hieroglyph_Mark_Begin # Ps LEFT CURLY BRACKET +# 27E6 ; Hieroglyph_Mark_Begin # Ps MATHEMATICAL LEFT WHITE SQUARE BRACKET +# 27E8 ; Hieroglyph_Mark_Begin # Ps MATHEMATICAL LEFT ANGLE BRACKET +# 2E22 ; Hieroglyph_Mark_Begin # Ps TOP LEFT HALF BRACKET +# 2E24 ; Hieroglyph_Mark_Begin # Ps BOTTOM LEFT HALF BRACKET + +# ================================================ + +# USE, Extended_Syllabic_Category=Hieroglyph_Mark_End +# 005D ; Hieroglyph_Mark_Begin # Pe RIGHT SQUARE BRACKET +# 007D ; Hieroglyph_Mark_Begin # Pe RIGHT CURLY BRACKET +# 27E7 ; Hieroglyph_Mark_Begin # Pe MATHEMATICAL RIGHT WHITE SQUARE BRACKET +# 27E9 ; Hieroglyph_Mark_Begin # Pe MATHEMATICAL RIGHT ANGLE BRACKET +# 2E23 ; Hieroglyph_Mark_Begin # Pe TOP RIGHT HALF BRACKET +# 2E25 ; Hieroglyph_Mark_Begin # Pe BOTTOM RIGHT HALF BRACKET + +# ================================================ + +# USE, Extended_Syllabic_Category=Hieroglyph_Segment_Begin +# 13437 ; Hieroglyph_Segment_Begin # Cf EGYPTIAN HIEROGLYPH BEGIN SEGMENT + +# ================================================ + +# USE, Extended_Syllabic_Category=Hieroglyph_Segment_End +# 13438 ; Hieroglyph_Segment_End # Cf EGYPTIAN HIEROGLYPH END SEGMENT + +# ================================================ + +# USE, Extended_Syllabic_Category=Hieroglyph_Mirror +# 13440 ; Hieroglyph_Mirror # Mn EGYPTIAN HIEROGLYPH MIRROR HORIZONTALLY + +# ================================================ + +# USE, Extended_Syllabic_Category=Hieroglyph_Modifier +# 13447..13455 ; Hieroglyph_Modifier # Mn [15] EGYPTIAN HIEROGLYPH MODIFIER DAMAGED AT TOP START..EGYPTIAN HIEROGLYPH MODIFIER DAMAGED + +# ================================================ + +# eof diff --git a/libs/harfbuzz/src/sample.py b/libs/harfbuzz/src/sample.py new file mode 100644 index 00000000..fd850480 --- /dev/null +++ b/libs/harfbuzz/src/sample.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python3 + +import sys +import array +from gi.repository import HarfBuzz as hb +from gi.repository import GLib + +fontdata = open (sys.argv[1], 'rb').read () +text = sys.argv[2] +# Need to create GLib.Bytes explicitly until this bug is fixed: +# https://bugzilla.gnome.org/show_bug.cgi?id=729541 +blob = hb.glib_blob_create (GLib.Bytes.new (fontdata)) +face = hb.face_create (blob, 0) +del blob +font = hb.font_create (face) +upem = hb.face_get_upem (face) +del face +hb.font_set_scale (font, upem, upem) +#hb.ft_font_set_funcs (font) +hb.ot_font_set_funcs (font) + +buf = hb.buffer_create () +class Debugger (object): + def message (self, buf, font, msg, data, _x_what_is_this): + print (msg) + return True +debugger = Debugger () +hb.buffer_set_message_func (buf, debugger.message, 1, 0) + +## +## Add text to buffer +## +# +# See https://github.com/harfbuzz/harfbuzz/pull/271 +# +# If you do not care about cluster values reflecting Python +# string indices, then this is quickest way to add text to +# buffer: +# hb.buffer_add_utf8 (buf, text.encode('utf-8'), 0, -1) +# Otherwise, then following handles both narrow and wide +# Python builds (the first item in the array is BOM, so we skip it): +if sys.maxunicode == 0x10FFFF: + hb.buffer_add_utf32 (buf, array.array ('I', text.encode ('utf-32'))[1:], 0, -1) +else: + hb.buffer_add_utf16 (buf, array.array ('H', text.encode ('utf-16'))[1:], 0, -1) + + +hb.buffer_guess_segment_properties (buf) + +hb.shape (font, buf, []) +del font + +infos = hb.buffer_get_glyph_infos (buf) +positions = hb.buffer_get_glyph_positions (buf) + +for info, pos in zip (infos, positions): + gid = info.codepoint + cluster = info.cluster + x_advance = pos.x_advance + x_offset = pos.x_offset + y_offset = pos.y_offset + + print ("gid%d=%d@%d,%d+%d" % (gid, cluster, x_advance, x_offset, y_offset)) diff --git a/libs/harfbuzz/src/test-algs.cc b/libs/harfbuzz/src/test-algs.cc new file mode 100644 index 00000000..450a7c43 --- /dev/null +++ b/libs/harfbuzz/src/test-algs.cc @@ -0,0 +1,110 @@ +/* + * Copyright © 2019 Facebook, 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. + * + * Facebook Author(s): Behdad Esfahbod + */ + +#include "hb.hh" +#include "hb-algs.hh" +#include "hb-set.hh" + + +static char * +test_func (int a, char **b) +{ + return b ? b[a] : nullptr; +} + +struct A +{ + void a () {} +}; + +int +main (int argc, char **argv) +{ + int i = 1; + auto p = hb_pair (1, i); + + p.second = 2; + assert (i == 2); + + const int c = 3; + auto pc = hb_pair (1, c); + assert (pc.second == 3); + + auto q = p; + assert (&q != &p); + q.second = 4; + assert (i == 4); + + hb_invoke (test_func, 0, nullptr); + + A a; + hb_invoke (&A::a, a); + + assert (1 == hb_min (8, 1)); + assert (8 == hb_max (8, 1)); + + int x = 1, y = 2; + hb_min (x, 3); + hb_min (3, x); + hb_min (x, 4 + 3); + int &z = hb_min (x, y); + z = 3; + assert (x == 3); + + hb_pair_t xp = hb_pair_t (nullptr, 0); + xp = hb_pair_t (nullptr, 1); + xp = hb_pair_t (nullptr, 1); + + assert (3 == hb_partial (hb_min, 3) (4)); + assert (3 == hb_partial<1> (hb_min, 4) (3)); + + auto M0 = hb_partial<2> (hb_max, 0); + assert (M0 (-2) == 0); + assert (M0 (+2) == 2); + + assert (hb_add (2) (5) == 7); + assert (hb_add (5) (2) == 7); + + x = 1; + assert (++hb_inc (x) == 3); + assert (x == 3); + + hb_set_t set1 {1}; + hb_set_t set2 {2}; + + assert (hb_hash (set1) != hb_hash (set2)); + assert (hb_hash (set1) == hb_hash (hb_set_t {1})); + assert (hb_hash (set1) != hb_hash (hb_set_t {})); + assert (hb_hash (set1) != hb_hash (hb_set_t {2})); + assert (hb_hash (set2) == hb_hash (hb_set_t {2})); + + /* hb_hash, unlike std::hash, dereferences pointers. */ + assert (hb_hash (set1) == hb_hash (&set1)); + assert (hb_hash (set1) == hb_hash (hb::shared_ptr {hb_set_reference (&set1)})); + assert (hb_hash (set1) == hb_hash (hb::unique_ptr {hb_set_reference (&set1)})); + + return 0; +} diff --git a/libs/harfbuzz/src/test-array.cc b/libs/harfbuzz/src/test-array.cc new file mode 100644 index 00000000..6c888138 --- /dev/null +++ b/libs/harfbuzz/src/test-array.cc @@ -0,0 +1,76 @@ +/* + * Copyright © 2020 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): Garret Rieger + */ + +#include "hb.hh" +#include "hb-array.hh" + +static void +test_reverse () +{ + int values[] = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + hb_array_t a (values, 9); + a.reverse(); + + int expected_values[] = {9, 8, 7, 6, 5, 4, 3, 2, 1}; + hb_array_t expected (expected_values, 9); + assert (a == expected); +} + +static void +test_reverse_range () +{ + int values[] = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + hb_array_t a (values, 9); + a.reverse(2, 6); + + int expected_values[] = {1, 2, 6, 5, 4, 3, 7, 8, 9}; + hb_array_t expected (expected_values, 9); + assert (a == expected); +} + +static void +test_reverse_invalid () +{ + int values[] = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + hb_array_t a (values, 9); + + a.reverse(4, 3); + a.reverse(2, 3); + a.reverse(5, 5); + a.reverse(12, 15); + + int expected_values[] = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + hb_array_t expected (expected_values, 9); + assert (a == expected); +} + +int +main (int argc, char **argv) +{ + test_reverse (); + test_reverse_range (); + test_reverse_invalid (); +} diff --git a/libs/harfbuzz/src/test-bimap.cc b/libs/harfbuzz/src/test-bimap.cc new file mode 100644 index 00000000..1253d0c1 --- /dev/null +++ b/libs/harfbuzz/src/test-bimap.cc @@ -0,0 +1,76 @@ +/* + * Copyright © 2019 Adobe, 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. + * + * Adobe Author(s): Michiharu Ariza + */ + +#include "hb.hh" +#include "hb-bimap.hh" + +int +main (int argc, char **argv) +{ + hb_bimap_t bm; + + assert (bm.is_empty () == true); + bm.set (1, 4); + bm.set (2, 5); + bm.set (3, 6); + assert (bm.get_population () == 3); + assert (bm.has (1) == true); + assert (bm.has (4) == false); + assert (bm[2] == 5); + assert (bm.backward (6) == 3); + bm.del (1); + assert (bm.has (1) == false); + assert (bm.has (3) == true); + bm.clear (); + assert (bm.get_population () == 0); + + hb_inc_bimap_t ibm; + + assert (ibm.add (13) == 0); + assert (ibm.add (8) == 1); + assert (ibm.add (10) == 2); + assert (ibm.add (8) == 1); + assert (ibm.add (7) == 3); + assert (ibm.get_population () == 4); + assert (ibm[7] == 3); + + ibm.sort (); + assert (ibm.get_population () == 4); + assert (ibm[7] == 0); + assert (ibm[13] == 3); + + ibm.identity (3); + assert (ibm.get_population () == 3); + assert (ibm[0] == 0); + assert (ibm[1] == 1); + assert (ibm[2] == 2); + assert (ibm.backward (0) == 0); + assert (ibm.backward (1) == 1); + assert (ibm.backward (2) == 2); + assert (ibm.has (4) == false); + + return 0; +} diff --git a/libs/harfbuzz/src/test-buffer-serialize.cc b/libs/harfbuzz/src/test-buffer-serialize.cc new file mode 100644 index 00000000..bbebcbe7 --- /dev/null +++ b/libs/harfbuzz/src/test-buffer-serialize.cc @@ -0,0 +1,97 @@ +/* + * Copyright © 2010,2011,2013 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 + */ + +#include "hb.hh" + +#include "hb.h" +#include "hb-ot.h" +#ifdef HAVE_FREETYPE +#include "hb-ft.h" +#endif + +#ifdef HB_NO_OPEN +#define hb_blob_create_from_file_or_fail(x) hb_blob_get_empty () +#endif + +int +main (int argc, char **argv) +{ + bool ret = true; + +#ifndef HB_NO_BUFFER_SERIALIZE + + if (argc != 2) { + fprintf (stderr, "usage: %s font-file\n", argv[0]); + exit (1); + } + + hb_blob_t *blob = hb_blob_create_from_file_or_fail (argv[1]); + assert (blob); + hb_face_t *face = hb_face_create (blob, 0 /* first face */); + hb_blob_destroy (blob); + blob = nullptr; + + unsigned int upem = hb_face_get_upem (face); + hb_font_t *font = hb_font_create (face); + hb_face_destroy (face); + hb_font_set_scale (font, upem, upem); + hb_ot_font_set_funcs (font); +#ifdef HAVE_FREETYPE + //hb_ft_font_set_funcs (font); +#endif + + hb_buffer_t *buf; + buf = hb_buffer_create (); + + char line[BUFSIZ], out[BUFSIZ]; + while (fgets (line, sizeof(line), stdin)) + { + hb_buffer_clear_contents (buf); + + const char *p = line; + while (hb_buffer_deserialize_glyphs (buf, + p, -1, &p, + font, + HB_BUFFER_SERIALIZE_FORMAT_TEXT)) + ; + if (*p && *p != '\n') + ret = false; + + hb_buffer_serialize_glyphs (buf, 0, hb_buffer_get_length (buf), + out, sizeof (out), nullptr, + font, HB_BUFFER_SERIALIZE_FORMAT_TEXT, + HB_BUFFER_SERIALIZE_FLAG_GLYPH_FLAGS); + puts (out); + } + + hb_buffer_destroy (buf); + + hb_font_destroy (font); + +#endif + + return !ret; +} diff --git a/libs/harfbuzz/src/test-gpos-size-params.cc b/libs/harfbuzz/src/test-gpos-size-params.cc new file mode 100644 index 00000000..b96381dd --- /dev/null +++ b/libs/harfbuzz/src/test-gpos-size-params.cc @@ -0,0 +1,62 @@ +/* + * Copyright © 2010,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 + */ + +#include "hb.hh" + +#include "hb.h" +#include "hb-ot.h" + +#ifdef HB_NO_OPEN +#define hb_blob_create_from_file_or_fail(x) hb_blob_get_empty () +#endif + +int +main (int argc, char **argv) +{ + if (argc != 2) { + fprintf (stderr, "usage: %s font-file\n", argv[0]); + exit (1); + } + + /* Create the face */ + hb_blob_t *blob = hb_blob_create_from_file_or_fail (argv[1]); + assert (blob); + hb_face_t *face = hb_face_create (blob, 0 /* first face */); + hb_blob_destroy (blob); + blob = nullptr; + + bool ret = true; + +#ifndef HB_NO_LAYOUT_FEATURE_PARAMS + unsigned int p[5]; + ret = hb_ot_layout_get_size_params (face, p, p+1, (p+2), p+3, p+4); + printf ("%g %u %u %g %g\n", p[0]/10., p[1], p[2], p[3]/10., p[4]/10.); +#endif + + hb_face_destroy (face); + + return !ret; +} diff --git a/libs/harfbuzz/src/test-gsub-would-substitute.cc b/libs/harfbuzz/src/test-gsub-would-substitute.cc new file mode 100644 index 00000000..87123030 --- /dev/null +++ b/libs/harfbuzz/src/test-gsub-would-substitute.cc @@ -0,0 +1,67 @@ +/* + * Copyright © 2010,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 + */ + +#include "hb.hh" + +#include "hb.h" +#include "hb-ot.h" + +#ifdef HAVE_FREETYPE +#include "hb-ft.h" +#endif + +#ifdef HB_NO_OPEN +#define hb_blob_create_from_file_or_fail(x) hb_blob_get_empty () +#endif + +int +main (int argc, char **argv) +{ + if (argc != 4 && argc != 5) { + fprintf (stderr, "usage: %s font-file lookup-index first-glyph [second-glyph]\n", argv[0]); + exit (1); + } + + /* Create the face */ + hb_blob_t *blob = hb_blob_create_from_file_or_fail (argv[1]); + assert (blob); + hb_face_t *face = hb_face_create (blob, 0 /* first face */); + hb_blob_destroy (blob); + blob = nullptr; + + hb_font_t *font = hb_font_create (face); +#ifdef HAVE_FREETYPE + hb_ft_font_set_funcs (font); +#endif + + unsigned int len = argc - 3; + hb_codepoint_t glyphs[2]; + if (!hb_font_glyph_from_string (font, argv[3], -1, &glyphs[0]) || + (argc > 4 && + !hb_font_glyph_from_string (font, argv[4], -1, &glyphs[1]))) + return 2; + return !hb_ot_layout_lookup_would_substitute (face, strtol (argv[2], nullptr, 0), glyphs, len, false); +} diff --git a/libs/harfbuzz/src/test-iter.cc b/libs/harfbuzz/src/test-iter.cc new file mode 100644 index 00000000..c37d1e74 --- /dev/null +++ b/libs/harfbuzz/src/test-iter.cc @@ -0,0 +1,359 @@ +/* + * Copyright © 2018 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 + */ + +#include "hb.hh" +#include "hb-iter.hh" + +#include "hb-array.hh" +#include "hb-set.hh" +#include "hb-ot-layout-common.hh" + +template +struct array_iter_t : hb_iter_with_fallback_t, T&> +{ + array_iter_t (hb_array_t arr_) : arr (arr_) {} + + typedef T& __item_t__; + static constexpr bool is_random_access_iterator = true; + T& __item_at__ (unsigned i) const { return arr[i]; } + void __forward__ (unsigned n) { arr += n; } + void __rewind__ (unsigned n) { arr -= n; } + unsigned __len__ () const { return arr.length; } + bool operator != (const array_iter_t& o) { return arr != o.arr; } + + private: + hb_array_t arr; +}; + +template +struct some_array_t +{ + some_array_t (hb_array_t arr_) : arr (arr_) {} + + typedef array_iter_t iter_t; + array_iter_t iter () { return array_iter_t (arr); } + operator array_iter_t () { return iter (); } + operator hb_iter_t> () { return iter (); } + + private: + hb_array_t arr; +}; + + +template +static void +test_iterator_non_default_constructable (Iter it) +{ + /* Iterate over a copy of it. */ + for (auto c = it.iter (); c; c++) + *c; + + /* Same. */ + for (auto c = +it; c; c++) + *c; + + /* Range-based for over a copy. */ + for (auto _ : +it) + (void) _; + + it += it.len (); + it = it + 10; + it = 10 + it; + + assert (*it == it[0]); + + static_assert (true || it.is_random_access_iterator, ""); + static_assert (true || it.is_sorted_iterator, ""); +} + +template +static void +test_iterator (Iter it) +{ + Iter default_constructed; + assert (!default_constructed); + + test_iterator_non_default_constructable (it); +} + +template +static void +test_iterable (const Iterable &lst = Null (Iterable)) +{ + for (auto _ : lst) + (void) _; + + // Test that can take iterator from. + test_iterator (lst.iter ()); +} + +template +static void check_sequential (It it) +{ + int i = 1; + for (int v : +it) { + assert (v == i++); + } +} + +static void test_concat () +{ + hb_vector_t a = {1, 2, 3}; + hb_vector_t b = {4, 5}; + + hb_vector_t c = {}; + hb_vector_t d = {1, 2, 3, 4, 5}; + + auto it1 = hb_concat (a, b); + assert (it1.len () == 5); + assert (it1.is_random_access_iterator); + auto it2 = hb_concat (c, d); + assert (it2.len () == 5); + auto it3 = hb_concat (d, c); + assert (it3.len () == 5); + for (int i = 0; i < 5; i++) { + assert(it1[i] == i + 1); + assert(it2[i] == i + 1); + assert(it3[i] == i + 1); + } + + check_sequential (it1); + check_sequential (it2); + check_sequential (it3); + + auto it4 = +it1; + it4 += 0; + assert (*it4 == 1); + + it4 += 2; + assert (*it4 == 3); + assert (it4); + assert (it4.len () == 3); + + it4 += 2; + assert (*it4 == 5); + assert (it4); + assert (it4.len () == 1); + + it4++; + assert (!it4); + assert (it4.len () == 0); + + auto it5 = +it1; + it5 += 3; + assert (*it5 == 4); + + hb_set_t s_a = {1, 2, 3}; + hb_set_t s_b = {4, 5}; + auto it6 = hb_concat (s_a, s_b); + assert (!it6.is_random_access_iterator); + check_sequential (it6); + assert (it6.len () == 5); + + it6 += 0; + assert (*it6 == 1); + + it6 += 3; + assert (*it6 == 4); + assert (it6); + assert (it6.len () == 2); +} + +int +main (int argc, char **argv) +{ + const int src[10] = {}; + int dst[20]; + hb_vector_t v; + + array_iter_t s (src); /* Implicit conversion from static array. */ + array_iter_t s2 (v); /* Implicit conversion from vector. */ + array_iter_t t (dst); + + static_assert (array_iter_t::is_random_access_iterator, ""); + + some_array_t a (src); + + s2 = s; + + hb_iter (src); + hb_iter (src, 2); + + hb_fill (t, 42); + hb_copy (s, t); + hb_copy (a.iter (), t); + + test_iterable (v); + hb_set_t st; + st << 1 << 15 << 43; + test_iterable (st); + hb_sorted_array_t sa; + (void) static_cast, hb_sorted_array_t::item_t>&> (sa); + (void) static_cast, hb_sorted_array_t::__item_t__>&> (sa); + (void) static_cast, int&>&>(sa); + (void) static_cast>&>(sa); + (void) static_cast, int&>&> (sa); + test_iterable (sa); + + test_iterable> (); + test_iterable> (); + test_iterable> (); + test_iterable (); + test_iterable> (); + + test_iterator (hb_zip (st, v)); + test_iterator_non_default_constructable (hb_enumerate (st)); + test_iterator_non_default_constructable (hb_enumerate (st, -5)); + test_iterator_non_default_constructable (hb_enumerate (hb_iter (st))); + test_iterator_non_default_constructable (hb_enumerate (hb_iter (st) + 1)); + test_iterator_non_default_constructable (hb_iter (st) | hb_filter ()); + test_iterator_non_default_constructable (hb_iter (st) | hb_map (hb_lidentity)); + + assert (true == hb_all (st)); + assert (false == hb_all (st, 42u)); + assert (true == hb_any (st)); + assert (false == hb_any (st, 14u)); + assert (true == hb_any (st, 14u, [] (unsigned _) { return _ - 1u; })); + assert (true == hb_any (st, [] (unsigned _) { return _ == 15u; })); + assert (true == hb_any (st, 15u)); + assert (false == hb_none (st)); + assert (false == hb_none (st, 15u)); + assert (true == hb_none (st, 17u)); + + hb_array_t> pa; + pa->as_array (); + + hb_map_t m; + + hb_iter (st); + hb_iter (&st); + + + hb_iter (src) + | hb_map (m) + | hb_map (&m) + | hb_filter () + | hb_filter (st) + | hb_filter (&st) + | hb_filter (hb_bool) + | hb_filter (hb_bool, hb_identity) + | hb_sink (st) + ; + + + hb_iter (src) + | hb_sink (hb_array (dst)) + ; + + + hb_iter (src) + | hb_apply (&st) + ; + + + hb_iter (src) + | hb_map ([] (int i) { return 1; }) + | hb_reduce ([=] (int acc, int value) { return acc; }, 2) + ; + + using map_pair_t = hb_item_type; + + hb_iter (m) + | hb_map ([] (map_pair_t p) { return p.first * p.second; }) + ; + + m.keys (); + using map_key_t = decltype (*m.keys()); + + hb_iter (m.keys ()) + | hb_filter ([] (map_key_t k) { return k < 42; }) + | hb_drain + ; + + m.values (); + using map_value_t = decltype (*m.values()); + + hb_iter (m.values ()) + | hb_filter ([] (map_value_t k) { return k < 42; }) + | hb_drain + ; + + unsigned int temp1 = 10; + unsigned int temp2 = 0; + hb_map_t *result = + + hb_iter (src) + | hb_map ([&] (int i) -> hb_set_t * + { + hb_set_t *set = hb_set_create (); + for (unsigned int i = 0; i < temp1; ++i) + hb_set_add (set, i); + temp1++; + return set; + }) + | hb_reduce ([&] (hb_map_t *acc, hb_set_t *value) -> hb_map_t * + { + hb_map_set (acc, temp2++, hb_set_get_population (value)); + /* This is not a memory managed language, take care! */ + hb_set_destroy (value); + return acc; + }, hb_map_create ()) + ; + /* The result should be something like 0->10, 1->11, ..., 9->19 */ + assert (hb_map_get (result, 9) == 19); + + unsigned int temp3 = 0; + + hb_iter(src) + | hb_map([&] (int i) { return ++temp3; }) + | hb_reduce([&] (float acc, int value) { return acc + value; }, 0) + ; + hb_map_destroy (result); + + + hb_iter (src) + | hb_drain + ; + + t << 1; + long vl; + s >> vl; + + hb_iota (); + hb_iota (3); + hb_iota (3, 2); + assert ((&vl) + 1 == *++hb_iota (&vl, hb_inc)); + hb_range (); + hb_repeat (7u); + hb_repeat (nullptr); + hb_repeat (vl) | hb_chop (3); + assert (hb_len (hb_range (10) | hb_take (3)) == 3); + assert (hb_range (9).len () == 9); + assert (hb_range (2, 9).len () == 7); + assert (hb_range (2, 9, 3).len () == 3); + assert (hb_range (2, 8, 3).len () == 2); + assert (hb_range (2, 7, 3).len () == 2); + assert (hb_range (-2, -9, -3).len () == 3); + assert (hb_range (-2, -8, -3).len () == 2); + assert (hb_range (-2, -7, -3).len () == 2); + + test_concat (); + + return 0; +} diff --git a/libs/harfbuzz/src/test-machinery.cc b/libs/harfbuzz/src/test-machinery.cc new file mode 100644 index 00000000..7fc9c24b --- /dev/null +++ b/libs/harfbuzz/src/test-machinery.cc @@ -0,0 +1,46 @@ +/* + * Copyright © 2022 Behdad Esfahbod + * + * 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. + */ + +#include "hb.hh" +#include "hb-machinery.hh" + +struct hb_intp_lazy_loader_t : hb_lazy_loader_t +{ + static int* create () { return nullptr; } + static void destroy (int* l) {} + static int* get_null () { return nullptr; } +}; + +struct hb_void_lazy_loader_t : hb_lazy_loader_t +{ + static void* create () { return nullptr; } + static void destroy (void* l) {} + static void* get_null () { return nullptr; } +}; + +int +main (int argc, char **argv) +{ + return 0; +} diff --git a/libs/harfbuzz/src/test-map.cc b/libs/harfbuzz/src/test-map.cc new file mode 100644 index 00000000..357a8b01 --- /dev/null +++ b/libs/harfbuzz/src/test-map.cc @@ -0,0 +1,244 @@ +/* + * Copyright © 2021 Behdad Esfahbod + * + * 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. + */ + +#include "hb.hh" +#include "hb-map.hh" +#include "hb-set.hh" +#include + +int +main (int argc, char **argv) +{ + + /* Test copy constructor. */ + { + hb_map_t v1; + v1.set (1, 2); + hb_map_t v2 {v1}; + assert (v1.get_population () == 1); + assert (v2.get_population () == 1); + assert (v1[1] == 2); + assert (v2[1] == 2); + } + + /* Test copy assignment. */ + { + hb_map_t v1; + v1.set (1, 2); + hb_map_t v2 = v1; + assert (v1.get_population () == 1); + assert (v2.get_population () == 1); + assert (v1[1] == 2); + assert (v2[1] == 2); + } + + /* Test move constructor. */ + { + hb_map_t s {}; + s.set (1, 2); + hb_map_t v (std::move (s)); + assert (s.get_population () == 0); + assert (v.get_population () == 1); + } + + /* Test move assignment. */ + { + hb_map_t s {}; + s.set (1, 2); + hb_map_t v; + v = std::move (s); + assert (s.get_population () == 0); + assert (v.get_population () == 1); + } + + /* Test initializing from iterable. */ + { + hb_map_t s; + + s.set (1, 2); + s.set (3, 4); + + hb_vector_t> v (s); + hb_map_t v0 (v); + hb_map_t v1 (s); + hb_map_t v2 (std::move (s)); + + assert (s.get_population () == 0); + assert (v0.get_population () == 2); + assert (v1.get_population () == 2); + assert (v2.get_population () == 2); + } + + /* Test call fini() twice. */ + { + hb_map_t s; + for (int i = 0; i < 16; i++) + s.set(i, i+1); + s.fini(); + } + + /* Test initializing from iterator. */ + { + hb_map_t s; + + s.set (1, 2); + s.set (3, 4); + + hb_map_t v (hb_iter (s)); + + assert (v.get_population () == 2); + } + + /* Test initializing from initializer list and swapping. */ + { + using pair_t = hb_pair_t; + hb_map_t v1 {pair_t{1,2}, pair_t{4,5}}; + hb_map_t v2 {pair_t{3,4}}; + hb_swap (v1, v2); + assert (v1.get_population () == 1); + assert (v2.get_population () == 2); + } + + /* Test class key / value types. */ + { + hb_hashmap_t m1; + hb_hashmap_t m2; + hb_hashmap_t m3; + assert (m1.get_population () == 0); + assert (m2.get_population () == 0); + assert (m3.get_population () == 0); + } + + { + hb_hashmap_t m0; + hb_hashmap_t m1; + hb_hashmap_t m2; + hb_hashmap_t m3; + + std::string s; + for (unsigned i = 1; i < 1000; i++) + { + s += "x"; + m0.set (i, i); + m1.set (s, i); + m2.set (i, s); + m3.set (s, s); + } + } + + /* Test hashing maps. */ + { + using pair = hb_pair_t; + + hb_hashmap_t m1; + + m1.set (hb_map_t (), hb_map_t {}); + m1.set (hb_map_t (), hb_map_t {pair (1u, 2u)}); + m1.set (hb_map_t {pair (1u, 2u)}, hb_map_t {pair (2u, 3u)}); + + assert (m1.get (hb_map_t ()) == hb_map_t {pair (1u, 2u)}); + assert (m1.get (hb_map_t {pair (1u, 2u)}) == hb_map_t {pair (2u, 3u)}); + } + + /* Test hashing sets. */ + { + hb_hashmap_t m1; + + m1.set (hb_set_t (), hb_set_t ()); + m1.set (hb_set_t (), hb_set_t {1}); + m1.set (hb_set_t {1, 1000}, hb_set_t {2}); + + assert (m1.get (hb_set_t ()) == hb_set_t {1}); + assert (m1.get (hb_set_t {1000, 1}) == hb_set_t {2}); + } + + /* Test hashing vectors. */ + { + using vector_t = hb_vector_t; + + hb_hashmap_t m1; + + m1.set (vector_t (), vector_t ()); + m1.set (vector_t (), vector_t {1}); + m1.set (vector_t {1}, vector_t {2}); + + assert (m1.get (vector_t ()) == vector_t {1}); + assert (m1.get (vector_t {1}) == vector_t {2}); + } + + /* Test hb::shared_ptr. */ + hb_hash (hb::shared_ptr ()); + { + hb_hashmap_t, hb::shared_ptr> m; + + m.get (hb::shared_ptr ()); + m.get (hb::shared_ptr (hb_set_get_empty ())); + m.iter (); + m.keys (); + m.values (); + m.iter_ref (); + m.keys_ref (); + m.values_ref (); + } + /* Test hb::unique_ptr. */ + hb_hash (hb::unique_ptr ()); + { + hb_hashmap_t, hb::unique_ptr> m; + + m.get (hb::unique_ptr ()); + m.get (hb::unique_ptr (hb_set_get_empty ())); + m.iter_ref (); + m.keys_ref (); + m.values_ref (); + } + /* Test more complex unique_ptr's. */ + { + hb_hashmap_t>> m; + + m.get (0); + const hb::unique_ptr> *v1; + m.has (0, &v1); + hb::unique_ptr> *v2; + m.has (0, &v2); + } + /* Test hashmap with complex shared_ptrs as keys. */ + { + hb_hashmap_t, unsigned> m; + + hb_map_t *m1 = hb_map_create (); + hb_map_t *m2 = hb_map_create (); + m1->set (1,3); + m2->set (1,3); + + hb::shared_ptr p1 {m1}; + hb::shared_ptr p2 {m2}; + m.set (p1,1); + + assert (m.has (p2)); + + m1->set (2,4); + assert (!m.has (p2)); + } + return 0; +} diff --git a/libs/harfbuzz/src/test-number.cc b/libs/harfbuzz/src/test-number.cc new file mode 100644 index 00000000..57835288 --- /dev/null +++ b/libs/harfbuzz/src/test-number.cc @@ -0,0 +1,224 @@ +/* + * Copyright © 2019 Ebrahim Byagowi + * + * 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. + * + */ + +#include "hb.hh" +#include "hb-number.hh" + + +int +main (int argc, char **argv) +{ + { + const char str[] = "123"; + const char *pp = str; + const char *end = str + 3; + + int pv; + assert (hb_parse_int (&pp, end, &pv)); + assert (pv == 123); + assert (pp - str == 3); + assert (end - pp == 0); + assert (!*end); + } + + { + const char str[] = "123"; + const char *pp = str; + const char *end = str + strlen (str); + + unsigned int pv; + assert (hb_parse_uint (&pp, end, &pv)); + assert (pv == 123); + assert (pp - str == 3); + assert (end - pp == 0); + assert (!*end); + } + + { + const char str[] = "12F"; + const char *pp = str; + const char *end = str + 3; + + unsigned int pv; + assert (hb_parse_uint (&pp, end, &pv, true, 16)); + assert (pv == 0x12F); + assert (pp - str == 3); + assert (end - pp == 0); + assert (!*end); + } + + { + const char str[] = "12Fq"; + const char *pp = str; + const char *end = str + 4; + + unsigned int pv; + assert (!hb_parse_uint (&pp, end, &pv, true, 16)); + assert (hb_parse_uint (&pp, end, &pv, false, 16)); + assert (pv == 0x12F); + assert (pp - str == 3); + assert (end - pp == 1); + assert (!*end); + } + + { + const char str[] = "-123"; + const char *pp = str; + const char *end = str + 4; + + int pv; + assert (hb_parse_int (&pp, end, &pv)); + assert (pv == -123); + assert (pp - str == 4); + assert (end - pp == 0); + assert (!*end); + } + + { + const char str[] = "123"; + const char *pp = str; + assert (ARRAY_LENGTH (str) == 4); + const char *end = str + ARRAY_LENGTH (str); + + unsigned int pv; + assert (hb_parse_uint (&pp, end, &pv)); + assert (pv == 123); + assert (pp - str == 3); + assert (end - pp == 1); + } + + { + const char str[] = "123\0"; + const char *pp = str; + assert (ARRAY_LENGTH (str) == 5); + const char *end = str + ARRAY_LENGTH (str); + + unsigned int pv; + assert (hb_parse_uint (&pp, end, &pv)); + assert (pv == 123); + assert (pp - str == 3); + assert (end - pp == 2); + } + + { + const char str[] = "123V"; + const char *pp = str; + assert (ARRAY_LENGTH (str) == 5); + const char *end = str + ARRAY_LENGTH (str); + + unsigned int pv; + assert (hb_parse_uint (&pp, end, &pv)); + assert (pv == 123); + assert (pp - str == 3); + assert (end - pp == 2); + } + + { + const char str[] = ".123"; + const char *pp = str; + const char *end = str + ARRAY_LENGTH (str); + + double pv; + assert (hb_parse_double (&pp, end, &pv)); + assert ((int) roundf (pv * 1000.) == 123); + assert (pp - str == 4); + assert (end - pp == 1); + } + + { + const char str[] = "0.123"; + const char *pp = str; + const char *end = str + ARRAY_LENGTH (str) - 1; + + double pv; + assert (hb_parse_double (&pp, end, &pv)); + assert ((int) roundf (pv * 1000.) == 123); + assert (pp - str == 5); + assert (end - pp == 0); + } + + { + const char str[] = "0.123e0"; + const char *pp = str; + const char *end = str + ARRAY_LENGTH (str) - 1; + + double pv; + assert (hb_parse_double (&pp, end, &pv)); + assert ((int) roundf (pv * 1000.) == 123); + assert (pp - str == 7); + assert (end - pp == 0); + } + + { + const char str[] = "123e-3"; + const char *pp = str; + const char *end = str + ARRAY_LENGTH (str) - 1; + + double pv; + assert (hb_parse_double (&pp, end, &pv)); + assert ((int) roundf (pv * 1000.) == 123); + assert (pp - str == 6); + assert (end - pp == 0); + } + + { + const char str[] = ".000123e+3"; + const char *pp = str; + const char *end = str + ARRAY_LENGTH (str) - 1; + + double pv; + assert (hb_parse_double (&pp, end, &pv)); + assert ((int) roundf (pv * 1000.) == 123); + assert (pp - str == 10); + assert (end - pp == 0); + } + + { + const char str[] = "-.000000123e6"; + const char *pp = str; + const char *end = str + ARRAY_LENGTH (str) - 1; + + double pv; + assert (hb_parse_double (&pp, end, &pv)); + assert ((int) roundf (pv * 1000.) == -123); + assert (pp - str == 13); + assert (end - pp == 0); + + } + + { + const char str[] = "-1.23E-1"; + const char *pp = str; + const char *end = str + ARRAY_LENGTH (str) - 1; + + double pv; + assert (hb_parse_double (&pp, end, &pv)); + assert ((int) roundf (pv * 1000.) == -123); + assert (pp - str == 8); + assert (end - pp == 0); + } + + return 0; +} diff --git a/libs/harfbuzz/src/test-ot-glyphname.cc b/libs/harfbuzz/src/test-ot-glyphname.cc new file mode 100644 index 00000000..ec6e149a --- /dev/null +++ b/libs/harfbuzz/src/test-ot-glyphname.cc @@ -0,0 +1,89 @@ +/* + * Copyright © 2019 Adobe, 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. + * + * Adobe Author(s): Michiharu Ariza + */ + +#include "hb.hh" +#include "hb-ot.h" + +#ifdef HB_NO_OPEN +#define hb_blob_create_from_file_or_fail(x) hb_blob_get_empty () +#endif + +int +main (int argc, char **argv) +{ + if (argc != 2) { + fprintf (stderr, "usage: %s font-file\n", argv[0]); + exit (1); + } + + hb_blob_t *blob = hb_blob_create_from_file_or_fail (argv[1]); + assert (blob); + hb_face_t *face = hb_face_create (blob, 0 /* first face */); + hb_font_t *font = hb_font_create (face); + hb_blob_destroy (blob); + blob = nullptr; + + + const unsigned int num_glyphs = hb_face_get_glyph_count (face); + int result = 1; + + for (hb_codepoint_t gid = 0; gid < num_glyphs; gid++) + { + char buf[64]; + unsigned int buf_size = sizeof (buf); + if (hb_font_get_glyph_name (font, gid, buf, buf_size)) + { + hb_codepoint_t gid_inv; + if (hb_font_get_glyph_from_name(font, buf, strlen (buf), &gid_inv)) + { + if (gid == gid_inv) + { + printf ("%u <-> %s\n", gid, buf); + } + else + { + printf ("%u -> %s -> %u\n", gid, buf, gid_inv); + result = 0; + } + } + else + { + printf ("%u -> %s -> ?\n", gid, buf); + result = 0; + } + } + else + { + printf ("%u -> ?\n", gid); + result = 0; + } + } + + hb_font_destroy (font); + hb_face_destroy (face); + + return !result; +} diff --git a/libs/harfbuzz/src/test-ot-meta.cc b/libs/harfbuzz/src/test-ot-meta.cc new file mode 100644 index 00000000..7cf69dbc --- /dev/null +++ b/libs/harfbuzz/src/test-ot-meta.cc @@ -0,0 +1,68 @@ +/* + * Copyright © 2019 Ebrahim Byagowi + * + * 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. + */ + +#include "hb.hh" +#include "hb-ot.h" + +#ifdef HB_NO_OPEN +#define hb_blob_create_from_file_or_fail(x) hb_blob_get_empty () +#endif + +int +main (int argc, char **argv) +{ + if (argc != 2) { + fprintf (stderr, "usage: %s font-file\n", argv[0]); + exit (1); + } + + hb_blob_t *blob = hb_blob_create_from_file_or_fail (argv[1]); + assert (blob); + hb_face_t *face = hb_face_create (blob, 0 /* first face */); + hb_blob_destroy (blob); + blob = nullptr; + + unsigned int count = 0; + +#ifndef HB_NO_META + count = hb_ot_meta_get_entry_tags (face, 0, nullptr, nullptr); + + hb_ot_meta_tag_t *tags = (hb_ot_meta_tag_t *) + malloc (sizeof (hb_ot_meta_tag_t) * count); + hb_ot_meta_get_entry_tags (face, 0, &count, tags); + for (unsigned i = 0; i < count; ++i) + { + hb_blob_t *entry = hb_ot_meta_reference_entry (face, tags[i]); + printf ("%c%c%c%c, size: %d: %.*s\n", + HB_UNTAG (tags[i]), hb_blob_get_length (entry), + hb_blob_get_length (entry), hb_blob_get_data (entry, nullptr)); + hb_blob_destroy (entry); + } + free (tags); +#endif + + hb_face_destroy (face); + + return !count; +} diff --git a/libs/harfbuzz/src/test-ot-name.cc b/libs/harfbuzz/src/test-ot-name.cc new file mode 100644 index 00000000..bfa654a7 --- /dev/null +++ b/libs/harfbuzz/src/test-ot-name.cc @@ -0,0 +1,74 @@ +/* + * Copyright © 2018 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 + */ + +#include "hb.hh" +#include "hb-ot.h" + +#ifdef HB_NO_OPEN +#define hb_blob_create_from_file_or_fail(x) hb_blob_get_empty () +#endif + +int +main (int argc, char **argv) +{ + if (argc != 2) { + fprintf (stderr, "usage: %s font-file\n", argv[0]); + exit (1); + } + + hb_blob_t *blob = hb_blob_create_from_file_or_fail (argv[1]); + assert (blob); + hb_face_t *face = hb_face_create (blob, 0 /* first face */); + hb_blob_destroy (blob); + blob = nullptr; + + unsigned int count = 0; + +#ifndef HB_NO_NAME + const hb_ot_name_entry_t *entries = hb_ot_name_list_names (face, &count); + + for (unsigned int i = 0; i < count; i++) + { + printf ("%u %s ", + entries[i].name_id, + hb_language_to_string (entries[i].language)); + + char buf[64]; + unsigned int buf_size = sizeof (buf); + hb_ot_name_get_utf8 (face, + entries[i].name_id, + entries[i].language, + &buf_size, + buf); + + printf ("%s\n", buf); + } +#endif + + hb_face_destroy (face); + + return count ? 0 : 1; +} diff --git a/libs/harfbuzz/src/test-priority-queue.cc b/libs/harfbuzz/src/test-priority-queue.cc new file mode 100644 index 00000000..e83d72c7 --- /dev/null +++ b/libs/harfbuzz/src/test-priority-queue.cc @@ -0,0 +1,81 @@ +/* + * Copyright © 2020 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): Garret Rieger + */ + +#include "hb.hh" +#include "hb-priority-queue.hh" + +static void +test_insert () +{ + hb_priority_queue_t queue; + assert (queue.is_empty ()); + + queue.insert (10, 0); + assert (!queue.is_empty ()); + assert (queue.minimum () == hb_pair (10, 0)); + + queue.insert (20, 1); + assert (queue.minimum () == hb_pair (10, 0)); + + queue.insert (5, 2); + assert (queue.minimum () == hb_pair (5, 2)); + + queue.insert (15, 3); + assert (queue.minimum () == hb_pair (5, 2)); + + queue.insert (1, 4); + assert (queue.minimum () == hb_pair (1, 4)); +} + +static void +test_extract () +{ + hb_priority_queue_t queue; + queue.insert (0, 0); + queue.insert (60, 6); + queue.insert (30, 3); + queue.insert (40 ,4); + queue.insert (20, 2); + queue.insert (50, 5); + queue.insert (70, 7); + queue.insert (10, 1); + + for (int i = 0; i < 8; i++) + { + assert (!queue.is_empty ()); + assert (queue.minimum () == hb_pair (i * 10, i)); + assert (queue.pop_minimum () == hb_pair (i * 10, i)); + } + + assert (queue.is_empty ()); +} + +int +main (int argc, char **argv) +{ + test_insert (); + test_extract (); +} diff --git a/libs/harfbuzz/src/test-repacker.cc b/libs/harfbuzz/src/test-repacker.cc new file mode 100644 index 00000000..cd8789f1 --- /dev/null +++ b/libs/harfbuzz/src/test-repacker.cc @@ -0,0 +1,2137 @@ +/* + * Copyright © 2020 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): Garret Rieger + */ + +#include + +#include "hb-repacker.hh" +#include "hb-open-type.hh" +#include "graph/serialize.hh" + +static void extend (const char* value, + unsigned len, + hb_serialize_context_t* c) +{ + char* obj = c->allocate_size (len); + memcpy (obj, value, len); +} + +static void start_object(const char* tag, + unsigned len, + hb_serialize_context_t* c) +{ + c->push (); + extend (tag, len, c); +} + +static unsigned add_object(const char* tag, + unsigned len, + hb_serialize_context_t* c) +{ + start_object (tag, len, c); + return c->pop_pack (false); +} + + +static void add_offset (unsigned id, + hb_serialize_context_t* c) +{ + OT::Offset16* offset = c->start_embed (); + c->extend_min (offset); + c->add_link (*offset, id); +} + +static void add_24_offset (unsigned id, + hb_serialize_context_t* c) +{ + OT::Offset24* offset = c->start_embed (); + c->extend_min (offset); + c->add_link (*offset, id); +} + +static void add_wide_offset (unsigned id, + hb_serialize_context_t* c) +{ + OT::Offset32* offset = c->start_embed (); + c->extend_min (offset); + c->add_link (*offset, id); +} + +static void add_gsubgpos_header (unsigned lookup_list, + hb_serialize_context_t* c) +{ + char header[] = { + 0, 1, // major + 0, 0, // minor + 0, 0, // script list + 0, 0, // feature list + }; + + start_object (header, 8, c); + add_offset (lookup_list, c); + c->pop_pack (false); +} + +static unsigned add_lookup_list (const unsigned* lookups, + char count, + hb_serialize_context_t* c) +{ + char lookup_count[] = {0, count}; + start_object ((char *) &lookup_count, 2, c); + + for (int i = 0; i < count; i++) + add_offset (lookups[i], c); + + return c->pop_pack (false); +} + +static void start_lookup (int8_t type, + int8_t num_subtables, + hb_serialize_context_t* c) +{ + char lookup[] = { + 0, (char)type, // type + 0, 0, // flag + 0, (char)num_subtables, // num subtables + }; + + start_object (lookup, 6, c); +} + +static unsigned finish_lookup (hb_serialize_context_t* c) +{ + char filter[] = {0, 0}; + extend (filter, 2, c); + return c->pop_pack (false); +} + +static unsigned add_extension (unsigned child, + uint8_t type, + hb_serialize_context_t* c) +{ + char ext[] = { + 0, 1, + 0, (char) type, + }; + + start_object (ext, 4, c); + add_wide_offset (child, c); + + return c->pop_pack (false); + +} + +// Adds coverage table fro [start, end] +static unsigned add_coverage (unsigned start, unsigned end, + hb_serialize_context_t* c) +{ + if (end - start == 1) + { + uint8_t coverage[] = { + 0, 1, // format + 0, 2, // count + + (uint8_t) ((start >> 8) & 0xFF), + (uint8_t) (start & 0xFF), // glyph[0] + + (uint8_t) ((end >> 8) & 0xFF), + (uint8_t) (end & 0xFF), // glyph[1] + }; + return add_object ((char*) coverage, 8, c); + } + + uint8_t coverage[] = { + 0, 2, // format + 0, 1, // range count + + (uint8_t) ((start >> 8) & 0xFF), + (uint8_t) (start & 0xFF), // start + + (uint8_t) ((end >> 8) & 0xFF), + (uint8_t) (end & 0xFF), // end + + 0, 0, + }; + return add_object ((char*) coverage, 10, c); +} + + +template +static unsigned add_coverage (It it, + hb_serialize_context_t* c) +{ + c->push (); + OT::Layout::Common::Coverage_serialize (c, it); + return c->pop_pack (false); +} + +// Adds a class that maps glyphs from [start_glyph, end_glyph) +// to classes 1...n +static unsigned add_class_def (uint16_t start_glyph, + uint16_t end_glyph, + hb_serialize_context_t* c) +{ + unsigned count = end_glyph - start_glyph; + uint8_t header[] = { + 0, 1, // format + + (uint8_t) ((start_glyph >> 8) & 0xFF), + (uint8_t) (start_glyph & 0xFF), // start_glyph + + (uint8_t) ((count >> 8) & 0xFF), + (uint8_t) (count & 0xFF), // count + }; + + start_object ((char*) header, 6, c); + for (uint16_t i = 1; i <= count; i++) + { + uint8_t class_value[] = { + (uint8_t) ((i >> 8) & 0xFF), + (uint8_t) (i & 0xFF), // count + }; + extend ((char*) class_value, 2, c); + } + + return c->pop_pack (false); +} + +static unsigned add_pair_pos_1 (unsigned* pair_sets, + char count, + unsigned coverage, + hb_serialize_context_t* c) +{ + char format[] = { + 0, 1 + }; + + start_object (format, 2, c); + add_offset (coverage, c); + + char value_format[] = { + 0, 0, + 0, 0, + 0, count, + }; + extend (value_format, 6, c); + + for (char i = 0; i < count; i++) + add_offset (pair_sets[(unsigned) i], c); + + return c->pop_pack (false); +} + +static unsigned add_pair_pos_2 (unsigned starting_class, + unsigned coverage, + unsigned class_def_1, uint16_t class_def_1_count, + unsigned class_def_2, uint16_t class_def_2_count, + unsigned* device_tables, + hb_serialize_context_t* c) +{ + uint8_t format[] = { + 0, 2 + }; + + start_object ((char*) format, 2, c); + add_offset (coverage, c); + + unsigned num_values = 4; + uint8_t format1 = 0x01 | 0x02 | 0x08; + uint8_t format2 = 0x04; + if (device_tables) { + format2 |= 0x20; + num_values += 1; + } + uint8_t value_format[] = { + 0, format1, + 0, format2, + }; + + extend ((char*) value_format, 4, c); + + add_offset (class_def_1, c); + add_offset (class_def_2, c); + + uint8_t class_counts[] = { + (uint8_t) ((class_def_1_count >> 8) & 0xFF), + (uint8_t) (class_def_1_count & 0xFF), + (uint8_t) ((class_def_2_count >> 8) & 0xFF), + (uint8_t) (class_def_2_count & 0xFF), + }; + extend ((char*) class_counts, 4, c); + + unsigned num_bytes_per_record = class_def_2_count * num_values * 2; + uint8_t* record = (uint8_t*) calloc (1, num_bytes_per_record); + int device_index = 0; + for (uint16_t i = 0; i < class_def_1_count; i++) + { + + for (uint16_t j = 0; j < class_def_2_count; j++) + { + for (int k = 0; k < 4; k++) { + uint8_t value[] = { + (uint8_t) (i + starting_class), + (uint8_t) (i + starting_class), + }; + extend ((char*) value, 2, c); + } + + if (device_tables) { + add_offset (device_tables[device_index++], c); + } + } + } + free (record); + + return c->pop_pack (false); +} + +static unsigned add_mark_base_pos_1 (unsigned mark_coverage, + unsigned base_coverage, + unsigned mark_array, + unsigned base_array, + unsigned class_count, + hb_serialize_context_t* c) +{ + uint8_t format[] = { + 0, 1 + }; + + start_object ((char*) format, 2, c); + add_offset (mark_coverage, c); + add_offset (base_coverage, c); + + uint8_t count[] = { + (uint8_t) ((class_count >> 8) & 0xFF), + (uint8_t) (class_count & 0xFF), + }; + extend ((char*) count, 2, c); + + add_offset (mark_array, c); + add_offset (base_array, c); + + return c->pop_pack (false); +} + +template +struct MarkBasePosBuffers +{ + unsigned base_anchors[class_count * base_count]; + unsigned mark_anchors[mark_count]; + uint8_t anchor_buffers[class_count * base_count + 100]; + uint8_t class_buffer[class_count * 2]; + + MarkBasePosBuffers(hb_serialize_context_t* c) + { + for (unsigned i = 0; i < sizeof(anchor_buffers) / 2; i++) + { + OT::HBUINT16* value = (OT::HBUINT16*) (&anchor_buffers[2*i]); + *value = i; + } + + for (unsigned i = 0; i < class_count * base_count; i++) + { + base_anchors[i] = add_object ((char*) &anchor_buffers[i], 100, c); + if (i < class_count) { + class_buffer[i*2] = (uint8_t) ((i >> 8) & 0xFF); + class_buffer[i*2 + 1] = (uint8_t) (i & 0xFF); + } + } + + for (unsigned i = 0; i < mark_count; i++) + { + mark_anchors[i] = add_object ((char*) &anchor_buffers[i], 4, c); + } + } + + unsigned create_mark_base_pos_1 (unsigned table_index, hb_serialize_context_t* c) + { + unsigned class_per_table = class_count / table_count; + unsigned mark_per_class = mark_count / class_count; + unsigned start_class = class_per_table * table_index; + unsigned end_class = class_per_table * (table_index + 1) - 1; + + // baseArray + uint8_t base_count_buffer[] = { + (uint8_t) ((base_count >> 8) & 0xFF), + (uint8_t) (base_count & 0xFF), + + }; + start_object ((char*) base_count_buffer, 2, c); + for (unsigned base = 0; base < base_count; base++) + { + for (unsigned klass = start_class; klass <= end_class; klass++) + { + unsigned i = base * class_count + klass; + add_offset (base_anchors[i], c); + } + } + unsigned base_array = c->pop_pack (false); + + // markArray + unsigned num_marks = class_per_table * mark_per_class; + uint8_t mark_count_buffer[] = { + (uint8_t) ((num_marks >> 8) & 0xFF), + (uint8_t) (num_marks & 0xFF), + }; + start_object ((char*) mark_count_buffer, 2, c); + for (unsigned mark = 0; mark < mark_count; mark++) + { + unsigned klass = mark % class_count; + if (klass < start_class || klass > end_class) continue; + klass -= start_class; + + extend ((char*) &class_buffer[2 * klass], 2, c); + add_offset (mark_anchors[mark], c); + } + unsigned mark_array = c->pop_pack (false); + + // markCoverage + auto it = + + hb_range ((hb_codepoint_t) mark_count) + | hb_filter ([&] (hb_codepoint_t mark) { + unsigned klass = mark % class_count; + return klass >= class_per_table * table_index && + klass < class_per_table * (table_index + 1); + }) + ; + unsigned mark_coverage = add_coverage (it, c); + + // baseCoverage + unsigned base_coverage = add_coverage (10, 10 + base_count - 1, c); + + return add_mark_base_pos_1 (mark_coverage, + base_coverage, + mark_array, + base_array, + class_per_table, + c); + } +}; + + + + + +static void run_resolve_overflow_test (const char* name, + hb_serialize_context_t& overflowing, + hb_serialize_context_t& expected, + unsigned num_iterations = 0, + bool recalculate_extensions = false, + hb_tag_t tag = HB_TAG ('G', 'S', 'U', 'B')) +{ + printf (">>> Testing overflowing resolution for %s\n", + name); + + graph_t graph (overflowing.object_graph ()); + graph_t expected_graph (expected.object_graph ()); + if (graph::will_overflow (expected_graph)) + { + expected_graph.assign_spaces (); + expected_graph.sort_shortest_distance (); + } + + // Check that overflow resolution succeeds + assert (overflowing.offset_overflow ()); + assert (hb_resolve_graph_overflows (tag, + num_iterations, + recalculate_extensions, + graph)); + + // Check the graphs can be serialized. + hb_blob_t* out = graph::serialize (graph); + assert (out); + hb_blob_destroy (out); + out = graph::serialize (expected_graph); + assert (out); + hb_blob_destroy (out); + + // Check the graphs are equivalent + graph.normalize (); + expected_graph.normalize (); + assert (graph == expected_graph); +} + +static void add_virtual_offset (unsigned id, + hb_serialize_context_t* c) +{ + c->add_virtual_link (id); +} + +static void +populate_serializer_simple (hb_serialize_context_t* c) +{ + c->start_serialize (); + + unsigned obj_1 = add_object ("ghi", 3, c); + unsigned obj_2 = add_object ("def", 3, c); + + start_object ("abc", 3, c); + add_offset (obj_2, c); + add_offset (obj_1, c); + c->pop_pack (false); + + c->end_serialize(); +} + +static void +populate_serializer_with_overflow (hb_serialize_context_t* c) +{ + std::string large_string(50000, 'a'); + c->start_serialize (); + + unsigned obj_1 = add_object (large_string.c_str(), 10000, c); + unsigned obj_2 = add_object (large_string.c_str(), 20000, c); + unsigned obj_3 = add_object (large_string.c_str(), 50000, c); + + start_object ("abc", 3, c); + add_offset (obj_3, c); + add_offset (obj_2, c); + add_offset (obj_1, c); + c->pop_pack (false); + + c->end_serialize(); +} + +static void +populate_serializer_with_priority_overflow (hb_serialize_context_t* c) +{ + std::string large_string(50000, 'a'); + c->start_serialize (); + + unsigned obj_e = add_object ("e", 1, c); + unsigned obj_d = add_object ("d", 1, c); + + start_object (large_string.c_str (), 50000, c); + add_offset (obj_e, c); + unsigned obj_c = c->pop_pack (false); + + start_object (large_string.c_str (), 20000, c); + add_offset (obj_d, c); + unsigned obj_b = c->pop_pack (false); + + start_object ("a", 1, c); + add_offset (obj_b, c); + add_offset (obj_c, c); + c->pop_pack (false); + + c->end_serialize(); +} + +static void +populate_serializer_with_priority_overflow_expected (hb_serialize_context_t* c) +{ + std::string large_string(50000, 'a'); + c->start_serialize (); + + unsigned obj_e = add_object ("e", 1, c); + + start_object (large_string.c_str (), 50000, c); + add_offset (obj_e, c); + unsigned obj_c = c->pop_pack (false); + + unsigned obj_d = add_object ("d", 1, c); + + start_object (large_string.c_str (), 20000, c); + add_offset (obj_d, c); + unsigned obj_b = c->pop_pack (false); + + start_object ("a", 1, c); + add_offset (obj_b, c); + add_offset (obj_c, c); + c->pop_pack (false); + + c->end_serialize(); +} + + +static void +populate_serializer_with_dedup_overflow (hb_serialize_context_t* c) +{ + std::string large_string(70000, 'a'); + c->start_serialize (); + + unsigned obj_1 = add_object ("def", 3, c); + + start_object (large_string.c_str(), 60000, c); + add_offset (obj_1, c); + unsigned obj_2 = c->pop_pack (false); + + start_object (large_string.c_str(), 10000, c); + add_offset (obj_2, c); + add_offset (obj_1, c); + c->pop_pack (false); + + c->end_serialize(); +} + +static void +populate_serializer_with_isolation_overflow (hb_serialize_context_t* c) +{ + std::string large_string(70000, 'a'); + c->start_serialize (); + + unsigned obj_4 = add_object ("4", 1, c); + + start_object (large_string.c_str(), 60000, c); + add_offset (obj_4, c); + unsigned obj_3 = c->pop_pack (false); + + start_object (large_string.c_str(), 10000, c); + add_offset (obj_4, c); + unsigned obj_2 = c->pop_pack (false); + + start_object ("1", 1, c); + add_wide_offset (obj_3, c); + add_offset (obj_2, c); + c->pop_pack (false); + + c->end_serialize(); +} + +static void +populate_serializer_with_isolation_overflow_complex (hb_serialize_context_t* c) +{ + std::string large_string(70000, 'a'); + c->start_serialize (); + + unsigned obj_f = add_object ("f", 1, c); + + start_object ("e", 1, c); + add_offset (obj_f, c); + unsigned obj_e = c->pop_pack (false); + + start_object ("c", 1, c); + add_offset (obj_e, c); + unsigned obj_c = c->pop_pack (false); + + start_object ("d", 1, c); + add_offset (obj_e, c); + unsigned obj_d = c->pop_pack (false); + + start_object (large_string.c_str(), 60000, c); + add_offset (obj_d, c); + unsigned obj_h = c->pop_pack (false); + + start_object (large_string.c_str(), 60000, c); + add_offset (obj_c, c); + add_offset (obj_h, c); + unsigned obj_b = c->pop_pack (false); + + start_object (large_string.c_str(), 10000, c); + add_offset (obj_d, c); + unsigned obj_g = c->pop_pack (false); + + start_object (large_string.c_str(), 11000, c); + add_offset (obj_d, c); + unsigned obj_i = c->pop_pack (false); + + start_object ("a", 1, c); + add_wide_offset (obj_b, c); + add_offset (obj_g, c); + add_offset (obj_i, c); + c->pop_pack (false); + + c->end_serialize(); +} + +static void +populate_serializer_with_isolation_overflow_complex_expected (hb_serialize_context_t* c) +{ + std::string large_string(70000, 'a'); + c->start_serialize (); + + + // space 1 + + unsigned obj_f_prime = add_object ("f", 1, c); + + start_object ("e", 1, c); + add_offset (obj_f_prime, c); + unsigned obj_e_prime = c->pop_pack (false); + + start_object ("d", 1, c); + add_offset (obj_e_prime, c); + unsigned obj_d_prime = c->pop_pack (false); + + start_object (large_string.c_str(), 60000, c); + add_offset (obj_d_prime, c); + unsigned obj_h = c->pop_pack (false); + + start_object ("c", 1, c); + add_offset (obj_e_prime, c); + unsigned obj_c = c->pop_pack (false); + + start_object (large_string.c_str(), 60000, c); + add_offset (obj_c, c); + add_offset (obj_h, c); + unsigned obj_b = c->pop_pack (false); + + // space 0 + + unsigned obj_f = add_object ("f", 1, c); + + start_object ("e", 1, c); + add_offset (obj_f, c); + unsigned obj_e = c->pop_pack (false); + + + start_object ("d", 1, c); + add_offset (obj_e, c); + unsigned obj_d = c->pop_pack (false); + + start_object (large_string.c_str(), 11000, c); + add_offset (obj_d, c); + unsigned obj_i = c->pop_pack (false); + + start_object (large_string.c_str(), 10000, c); + add_offset (obj_d, c); + unsigned obj_g = c->pop_pack (false); + + start_object ("a", 1, c); + add_wide_offset (obj_b, c); + add_offset (obj_g, c); + add_offset (obj_i, c); + c->pop_pack (false); + + c->end_serialize(); +} + +static void +populate_serializer_with_isolation_overflow_spaces (hb_serialize_context_t* c) +{ + std::string large_string(70000, 'a'); + c->start_serialize (); + + unsigned obj_d = add_object ("f", 1, c); + unsigned obj_e = add_object ("f", 1, c); + + start_object (large_string.c_str(), 60000, c); + add_offset (obj_d, c); + unsigned obj_b = c->pop_pack (); + + start_object (large_string.c_str(), 60000, c); + add_offset (obj_e, c); + unsigned obj_c = c->pop_pack (); + + + start_object ("a", 1, c); + add_wide_offset (obj_b, c); + add_wide_offset (obj_c, c); + c->pop_pack (); + + c->end_serialize(); +} + +static void +populate_serializer_spaces (hb_serialize_context_t* c, bool with_overflow) +{ + std::string large_string(70000, 'a'); + c->start_serialize (); + + unsigned obj_i; + + if (with_overflow) + obj_i = add_object ("i", 1, c); + + // Space 2 + unsigned obj_h = add_object ("h", 1, c); + + start_object (large_string.c_str(), 30000, c); + add_offset (obj_h, c); + unsigned obj_e = c->pop_pack (false); + + start_object ("b", 1, c); + add_offset (obj_e, c); + unsigned obj_b = c->pop_pack (false); + + // Space 1 + if (!with_overflow) + obj_i = add_object ("i", 1, c); + + start_object (large_string.c_str(), 30000, c); + add_offset (obj_i, c); + unsigned obj_g = c->pop_pack (false); + + start_object (large_string.c_str(), 30000, c); + add_offset (obj_i, c); + unsigned obj_f = c->pop_pack (false); + + start_object ("d", 1, c); + add_offset (obj_g, c); + unsigned obj_d = c->pop_pack (false); + + start_object ("c", 1, c); + add_offset (obj_f, c); + unsigned obj_c = c->pop_pack (false); + + start_object ("a", 1, c); + add_wide_offset (obj_b, c); + add_wide_offset (obj_c, c); + add_wide_offset (obj_d, c); + c->pop_pack (false); + + c->end_serialize(); +} + +static void +populate_serializer_spaces_16bit_connection (hb_serialize_context_t* c) +{ + std::string large_string(70000, 'a'); + c->start_serialize (); + + unsigned obj_g = add_object ("g", 1, c); + unsigned obj_h = add_object ("h", 1, c); + + start_object (large_string.c_str (), 40000, c); + add_offset (obj_g, c); + unsigned obj_e = c->pop_pack (false); + + start_object (large_string.c_str (), 40000, c); + add_offset (obj_h, c); + unsigned obj_f = c->pop_pack (false); + + start_object ("c", 1, c); + add_offset (obj_e, c); + unsigned obj_c = c->pop_pack (false); + + start_object ("d", 1, c); + add_offset (obj_f, c); + unsigned obj_d = c->pop_pack (false); + + start_object ("b", 1, c); + add_offset (obj_e, c); + add_offset (obj_h, c); + unsigned obj_b = c->pop_pack (false); + + start_object ("a", 1, c); + add_offset (obj_b, c); + add_wide_offset (obj_c, c); + add_wide_offset (obj_d, c); + c->pop_pack (false); + + c->end_serialize(); +} + +static void +populate_serializer_spaces_16bit_connection_expected (hb_serialize_context_t* c) +{ + std::string large_string(70000, 'a'); + c->start_serialize (); + + unsigned obj_g_prime = add_object ("g", 1, c); + + start_object (large_string.c_str (), 40000, c); + add_offset (obj_g_prime, c); + unsigned obj_e_prime = c->pop_pack (false); + + start_object ("c", 1, c); + add_offset (obj_e_prime, c); + unsigned obj_c = c->pop_pack (false); + + unsigned obj_h_prime = add_object ("h", 1, c); + + start_object (large_string.c_str (), 40000, c); + add_offset (obj_h_prime, c); + unsigned obj_f = c->pop_pack (false); + + start_object ("d", 1, c); + add_offset (obj_f, c); + unsigned obj_d = c->pop_pack (false); + + unsigned obj_g = add_object ("g", 1, c); + + start_object (large_string.c_str (), 40000, c); + add_offset (obj_g, c); + unsigned obj_e = c->pop_pack (false); + + unsigned obj_h = add_object ("h", 1, c); + + start_object ("b", 1, c); + add_offset (obj_e, c); + add_offset (obj_h, c); + unsigned obj_b = c->pop_pack (false); + + start_object ("a", 1, c); + add_offset (obj_b, c); + add_wide_offset (obj_c, c); + add_wide_offset (obj_d, c); + c->pop_pack (false); + + c->end_serialize (); +} + +static void +populate_serializer_short_and_wide_subgraph_root (hb_serialize_context_t* c) +{ + std::string large_string(70000, 'a'); + c->start_serialize (); + + unsigned obj_e = add_object ("e", 1, c); + + start_object (large_string.c_str (), 40000, c); + add_offset (obj_e, c); + unsigned obj_c = c->pop_pack (false); + + start_object (large_string.c_str (), 40000, c); + add_offset (obj_c, c); + unsigned obj_d = c->pop_pack (false); + + start_object ("b", 1, c); + add_offset (obj_c, c); + add_offset (obj_e, c); + unsigned obj_b = c->pop_pack (false); + + start_object ("a", 1, c); + add_offset (obj_b, c); + add_wide_offset (obj_c, c); + add_wide_offset (obj_d, c); + c->pop_pack (false); + + c->end_serialize(); +} + +static void +populate_serializer_short_and_wide_subgraph_root_expected (hb_serialize_context_t* c) +{ + std::string large_string(70000, 'a'); + c->start_serialize (); + + unsigned obj_e_prime = add_object ("e", 1, c); + + start_object (large_string.c_str (), 40000, c); + add_offset (obj_e_prime, c); + unsigned obj_c_prime = c->pop_pack (false); + + start_object (large_string.c_str (), 40000, c); + add_offset (obj_c_prime, c); + unsigned obj_d = c->pop_pack (false); + + unsigned obj_e = add_object ("e", 1, c); + + start_object (large_string.c_str (), 40000, c); + add_offset (obj_e, c); + unsigned obj_c = c->pop_pack (false); + + + start_object ("b", 1, c); + add_offset (obj_c, c); + add_offset (obj_e, c); + unsigned obj_b = c->pop_pack (false); + + start_object ("a", 1, c); + add_offset (obj_b, c); + add_wide_offset (obj_c_prime, c); + add_wide_offset (obj_d, c); + c->pop_pack (false); + + c->end_serialize(); +} + +static void +populate_serializer_with_split_spaces (hb_serialize_context_t* c) +{ + // Overflow needs to be resolved by splitting the single space + std::string large_string(70000, 'a'); + c->start_serialize (); + + unsigned obj_f = add_object ("f", 1, c); + + start_object (large_string.c_str(), 40000, c); + add_offset (obj_f, c); + unsigned obj_d = c->pop_pack (false); + + start_object (large_string.c_str(), 40000, c); + add_offset (obj_f, c); + unsigned obj_e = c->pop_pack (false); + + start_object ("b", 1, c); + add_offset (obj_d, c); + unsigned obj_b = c->pop_pack (false); + + start_object ("c", 1, c); + add_offset (obj_e, c); + unsigned obj_c = c->pop_pack (false); + + start_object ("a", 1, c); + add_wide_offset (obj_b, c); + add_wide_offset (obj_c, c); + c->pop_pack (false); + + c->end_serialize(); +} + +static void +populate_serializer_with_split_spaces_2 (hb_serialize_context_t* c) +{ + // Overflow needs to be resolved by splitting the single space + std::string large_string(70000, 'a'); + c->start_serialize (); + + unsigned obj_f = add_object ("f", 1, c); + + start_object (large_string.c_str(), 40000, c); + add_offset (obj_f, c); + unsigned obj_d = c->pop_pack (false); + + start_object (large_string.c_str(), 40000, c); + add_offset (obj_f, c); + unsigned obj_e = c->pop_pack (false); + + start_object ("b", 1, c); + add_offset (obj_d, c); + unsigned obj_b = c->pop_pack (false); + + start_object ("c", 1, c); + add_offset (obj_e, c); + unsigned obj_c = c->pop_pack (false); + + start_object ("a", 1, c); + add_offset (obj_b, c); + add_wide_offset (obj_b, c); + add_wide_offset (obj_c, c); + c->pop_pack (false); + + c->end_serialize(); +} + +static void +populate_serializer_with_split_spaces_expected (hb_serialize_context_t* c) +{ + // Overflow needs to be resolved by splitting the single space + + std::string large_string(70000, 'a'); + c->start_serialize (); + + unsigned obj_f_prime = add_object ("f", 1, c); + + start_object (large_string.c_str(), 40000, c); + add_offset (obj_f_prime, c); + unsigned obj_d = c->pop_pack (false); + + start_object ("b", 1, c); + add_offset (obj_d, c); + unsigned obj_b = c->pop_pack (false); + + unsigned obj_f = add_object ("f", 1, c); + + start_object (large_string.c_str(), 40000, c); + add_offset (obj_f, c); + unsigned obj_e = c->pop_pack (false); + + start_object ("c", 1, c); + add_offset (obj_e, c); + unsigned obj_c = c->pop_pack (false); + + start_object ("a", 1, c); + add_wide_offset (obj_b, c); + add_wide_offset (obj_c, c); + c->pop_pack (false); + + c->end_serialize(); +} + +static void +populate_serializer_with_split_spaces_expected_2 (hb_serialize_context_t* c) +{ + // Overflow needs to be resolved by splitting the single space + + std::string large_string(70000, 'a'); + c->start_serialize (); + + // Space 2 + + unsigned obj_f_double_prime = add_object ("f", 1, c); + + start_object (large_string.c_str(), 40000, c); + add_offset (obj_f_double_prime, c); + unsigned obj_d_prime = c->pop_pack (false); + + start_object ("b", 1, c); + add_offset (obj_d_prime, c); + unsigned obj_b_prime = c->pop_pack (false); + + // Space 1 + + unsigned obj_f_prime = add_object ("f", 1, c); + + start_object (large_string.c_str(), 40000, c); + add_offset (obj_f_prime, c); + unsigned obj_e = c->pop_pack (false); + + start_object ("c", 1, c); + add_offset (obj_e, c); + unsigned obj_c = c->pop_pack (false); + + // Space 0 + + unsigned obj_f = add_object ("f", 1, c); + + start_object (large_string.c_str(), 40000, c); + add_offset (obj_f, c); + unsigned obj_d = c->pop_pack (false); + + start_object ("b", 1, c); + add_offset (obj_d, c); + unsigned obj_b = c->pop_pack (false); + + // Root + start_object ("a", 1, c); + add_offset (obj_b, c); + add_wide_offset (obj_b_prime, c); + add_wide_offset (obj_c, c); + c->pop_pack (false); + + c->end_serialize(); +} + +static void +populate_serializer_complex_2 (hb_serialize_context_t* c) +{ + c->start_serialize (); + + unsigned obj_5 = add_object ("mn", 2, c); + + unsigned obj_4 = add_object ("jkl", 3, c); + + start_object ("ghi", 3, c); + add_offset (obj_4, c); + unsigned obj_3 = c->pop_pack (false); + + start_object ("def", 3, c); + add_offset (obj_3, c); + unsigned obj_2 = c->pop_pack (false); + + start_object ("abc", 3, c); + add_offset (obj_2, c); + add_offset (obj_4, c); + add_offset (obj_5, c); + c->pop_pack (false); + + c->end_serialize(); +} + +static void +populate_serializer_complex_3 (hb_serialize_context_t* c) +{ + c->start_serialize (); + + unsigned obj_6 = add_object ("opqrst", 6, c); + + unsigned obj_5 = add_object ("mn", 2, c); + + start_object ("jkl", 3, c); + add_offset (obj_6, c); + unsigned obj_4 = c->pop_pack (false); + + start_object ("ghi", 3, c); + add_offset (obj_4, c); + unsigned obj_3 = c->pop_pack (false); + + start_object ("def", 3, c); + add_offset (obj_3, c); + unsigned obj_2 = c->pop_pack (false); + + start_object ("abc", 3, c); + add_offset (obj_2, c); + add_offset (obj_4, c); + add_offset (obj_5, c); + c->pop_pack (false); + + c->end_serialize(); +} + +static void +populate_serializer_virtual_link (hb_serialize_context_t* c) +{ + c->start_serialize (); + + unsigned obj_d = add_object ("d", 1, c); + + start_object ("b", 1, c); + add_offset (obj_d, c); + unsigned obj_b = c->pop_pack (false); + + start_object ("e", 1, c); + add_virtual_offset (obj_b, c); + unsigned obj_e = c->pop_pack (false); + + start_object ("c", 1, c); + add_offset (obj_e, c); + unsigned obj_c = c->pop_pack (false); + + start_object ("a", 1, c); + add_offset (obj_b, c); + add_offset (obj_c, c); + c->pop_pack (false); + + c->end_serialize(); +} + +static void +populate_serializer_with_24_and_32_bit_offsets (hb_serialize_context_t* c) +{ + std::string large_string(60000, 'a'); + c->start_serialize (); + + unsigned obj_f = add_object ("f", 1, c); + unsigned obj_g = add_object ("g", 1, c); + unsigned obj_j = add_object ("j", 1, c); + unsigned obj_k = add_object ("k", 1, c); + + start_object (large_string.c_str (), 40000, c); + add_offset (obj_f, c); + unsigned obj_c = c->pop_pack (false); + + start_object (large_string.c_str (), 40000, c); + add_offset (obj_g, c); + unsigned obj_d = c->pop_pack (false); + + start_object (large_string.c_str (), 40000, c); + add_offset (obj_j, c); + unsigned obj_h = c->pop_pack (false); + + start_object (large_string.c_str (), 40000, c); + add_offset (obj_k, c); + unsigned obj_i = c->pop_pack (false); + + start_object ("e", 1, c); + add_wide_offset (obj_h, c); + add_wide_offset (obj_i, c); + unsigned obj_e = c->pop_pack (false); + + start_object ("b", 1, c); + add_24_offset (obj_c, c); + add_24_offset (obj_d, c); + add_24_offset (obj_e, c); + unsigned obj_b = c->pop_pack (false); + + start_object ("a", 1, c); + add_24_offset (obj_b, c); + c->pop_pack (false); + + c->end_serialize(); +} + +static void +populate_serializer_with_extension_promotion (hb_serialize_context_t* c, + int num_extensions = 0) +{ + constexpr int num_lookups = 5; + constexpr int num_subtables = num_lookups * 2; + unsigned int lookups[num_lookups]; + unsigned int subtables[num_subtables]; + unsigned int extensions[num_subtables]; + + std::string large_string(60000, 'a'); + c->start_serialize (); + + + for (int i = num_subtables - 1; i >= 0; i--) + subtables[i] = add_object(large_string.c_str (), 15000, c); + + for (int i = num_subtables - 1; + i >= (num_lookups - num_extensions) * 2; + i--) + { + unsigned ext_index = i - (num_lookups - num_extensions) * 2; + unsigned subtable_index = num_subtables - ext_index - 1; + extensions[i] = add_extension (subtables[subtable_index], 5, c); + } + + for (int i = num_lookups - 1; i >= 0; i--) + { + bool is_ext = (i >= (num_lookups - num_extensions)); + + start_lookup (is_ext ? (char) 7 : (char) 5, + 2, + c); + + if (is_ext) { + add_offset (extensions[i * 2], c); + add_offset (extensions[i * 2 + 1], c); + } else { + add_offset (subtables[i * 2], c); + add_offset (subtables[i * 2 + 1], c); + } + + lookups[i] = finish_lookup (c); + } + + unsigned lookup_list = add_lookup_list (lookups, num_lookups, c); + + add_gsubgpos_header (lookup_list, c); + + c->end_serialize(); +} + +template +static void +populate_serializer_with_large_pair_pos_1 (hb_serialize_context_t* c, + bool as_extension = false) +{ + std::string large_string(60000, 'a'); + c->start_serialize (); + + constexpr int total_pair_set = num_pair_pos_1 * num_pair_set; + unsigned pair_set[total_pair_set]; + unsigned coverage[num_pair_pos_1]; + unsigned pair_pos_1[num_pair_pos_1]; + + for (int i = num_pair_pos_1 - 1; i >= 0; i--) + { + for (int j = (i + 1) * num_pair_set - 1; j >= i * num_pair_set; j--) + pair_set[j] = add_object (large_string.c_str (), 30000 + j, c); + + coverage[i] = add_coverage (i * num_pair_set, + (i + 1) * num_pair_set - 1, c); + + pair_pos_1[i] = add_pair_pos_1 (&pair_set[i * num_pair_set], + num_pair_set, + coverage[i], + c); + } + + unsigned pair_pos_2 = add_object (large_string.c_str(), 200, c); + + if (as_extension) { + pair_pos_2 = add_extension (pair_pos_2, 2, c); + for (int i = num_pair_pos_1 - 1; i >= 0; i--) + pair_pos_1[i] = add_extension (pair_pos_1[i], 2, c); + } + + start_lookup (as_extension ? 9 : 2, 1 + num_pair_pos_1, c); + + for (int i = 0; i < num_pair_pos_1; i++) + add_offset (pair_pos_1[i], c); + add_offset (pair_pos_2, c); + + unsigned lookup = finish_lookup (c); + + unsigned lookup_list = add_lookup_list (&lookup, 1, c); + + add_gsubgpos_header (lookup_list, c); + + c->end_serialize(); +} + +template +static void +populate_serializer_with_large_pair_pos_2 (hb_serialize_context_t* c, + bool as_extension = false, + bool with_device_tables = false, + bool extra_table = true) +{ + std::string large_string(100000, 'a'); + c->start_serialize (); + + unsigned coverage[num_pair_pos_2]; + unsigned class_def_1[num_pair_pos_2]; + unsigned class_def_2[num_pair_pos_2]; + unsigned pair_pos_2[num_pair_pos_2]; + + unsigned* device_tables = (unsigned*) calloc (num_pair_pos_2 * num_class_1 * num_class_2, + sizeof(unsigned)); + + // Total glyphs = num_class_1 * num_pair_pos_2 + for (int i = num_pair_pos_2 - 1; i >= 0; i--) + { + unsigned start_glyph = 5 + i * num_class_1; + if (num_class_2 >= num_class_1) + { + class_def_2[i] = add_class_def (11, + 10 + num_class_2, c); + class_def_1[i] = add_class_def (start_glyph + 1, + start_glyph + num_class_1, + c); + } else { + class_def_1[i] = add_class_def (start_glyph + 1, + start_glyph + num_class_1, + c); + class_def_2[i] = add_class_def (11, + 10 + num_class_2, c); + } + + coverage[i] = add_coverage (start_glyph, + start_glyph + num_class_1 - 1, + c); + + if (with_device_tables) + { + for(int j = (i + 1) * num_class_1 * num_class_2 - 1; + j >= i * num_class_1 * num_class_2; + j--) + { + uint8_t table[] = { + (uint8_t) ((j >> 8) & 0xFF), + (uint8_t) (j & 0xFF), + }; + device_tables[j] = add_object ((char*) table, 2, c); + } + } + + pair_pos_2[i] = add_pair_pos_2 (1 + i * num_class_1, + coverage[i], + class_def_1[i], num_class_1, + class_def_2[i], num_class_2, + with_device_tables + ? &device_tables[i * num_class_1 * num_class_2] + : nullptr, + c); + } + + + unsigned pair_pos_1 = 0; + if (extra_table) pair_pos_1 = add_object (large_string.c_str(), 100000, c); + + if (as_extension) { + for (int i = num_pair_pos_2 - 1; i >= 0; i--) + pair_pos_2[i] = add_extension (pair_pos_2[i], 2, c); + + if (extra_table) + pair_pos_1 = add_extension (pair_pos_1, 2, c); + } + + start_lookup (as_extension ? 9 : 2, 1 + num_pair_pos_2, c); + + if (extra_table) + add_offset (pair_pos_1, c); + + for (int i = 0; i < num_pair_pos_2; i++) + add_offset (pair_pos_2[i], c); + + unsigned lookup = finish_lookup (c); + + unsigned lookup_list = add_lookup_list (&lookup, 1, c); + + add_gsubgpos_header (lookup_list, c); + + c->end_serialize(); + + free (device_tables); +} + +template +static void +populate_serializer_with_large_mark_base_pos_1 (hb_serialize_context_t* c) +{ + c->start_serialize (); + + MarkBasePosBuffers buffers (c); + + unsigned mark_base_pos[table_count]; + for (unsigned i = 0; i < table_count; i++) + mark_base_pos[i] = buffers.create_mark_base_pos_1 (i, c); + + for (int i = 0; i < table_count; i++) + mark_base_pos[i] = add_extension (mark_base_pos[i], 4, c); + + start_lookup (9, table_count, c); + + for (int i = 0; i < table_count; i++) + add_offset (mark_base_pos[i], c); + + unsigned lookup = finish_lookup (c); + + unsigned lookup_list = add_lookup_list (&lookup, 1, c); + + add_gsubgpos_header (lookup_list, c); + + c->end_serialize(); +} + +static void test_sort_shortest () +{ + size_t buffer_size = 100; + void* buffer = malloc (buffer_size); + hb_serialize_context_t c (buffer, buffer_size); + populate_serializer_complex_2 (&c); + + graph_t graph (c.object_graph ()); + graph.sort_shortest_distance (); + + assert(strncmp (graph.object (4).head, "abc", 3) == 0); + assert(graph.object (4).real_links.length == 3); + assert(graph.object (4).real_links[0].objidx == 2); + assert(graph.object (4).real_links[1].objidx == 0); + assert(graph.object (4).real_links[2].objidx == 3); + + assert(strncmp (graph.object (3).head, "mn", 2) == 0); + assert(graph.object (3).real_links.length == 0); + + assert(strncmp (graph.object (2).head, "def", 3) == 0); + assert(graph.object (2).real_links.length == 1); + assert(graph.object (2).real_links[0].objidx == 1); + + assert(strncmp (graph.object (1).head, "ghi", 3) == 0); + assert(graph.object (1).real_links.length == 1); + assert(graph.object (1).real_links[0].objidx == 0); + + assert(strncmp (graph.object (0).head, "jkl", 3) == 0); + assert(graph.object (0).real_links.length == 0); + + free (buffer); +} + +static void test_duplicate_leaf () +{ + size_t buffer_size = 100; + void* buffer = malloc (buffer_size); + hb_serialize_context_t c (buffer, buffer_size); + populate_serializer_complex_2 (&c); + + graph_t graph (c.object_graph ()); + graph.duplicate (4, 1); + + assert(strncmp (graph.object (5).head, "abc", 3) == 0); + assert(graph.object (5).real_links.length == 3); + assert(graph.object (5).real_links[0].objidx == 3); + assert(graph.object (5).real_links[1].objidx == 4); + assert(graph.object (5).real_links[2].objidx == 0); + + assert(strncmp (graph.object (4).head, "jkl", 3) == 0); + assert(graph.object (4).real_links.length == 0); + + assert(strncmp (graph.object (3).head, "def", 3) == 0); + assert(graph.object (3).real_links.length == 1); + assert(graph.object (3).real_links[0].objidx == 2); + + assert(strncmp (graph.object (2).head, "ghi", 3) == 0); + assert(graph.object (2).real_links.length == 1); + assert(graph.object (2).real_links[0].objidx == 1); + + assert(strncmp (graph.object (1).head, "jkl", 3) == 0); + assert(graph.object (1).real_links.length == 0); + + assert(strncmp (graph.object (0).head, "mn", 2) == 0); + assert(graph.object (0).real_links.length == 0); + + free (buffer); +} + +static void test_duplicate_interior () +{ + size_t buffer_size = 100; + void* buffer = malloc (buffer_size); + hb_serialize_context_t c (buffer, buffer_size); + populate_serializer_complex_3 (&c); + + graph_t graph (c.object_graph ()); + graph.duplicate (3, 2); + + assert(strncmp (graph.object (6).head, "abc", 3) == 0); + assert(graph.object (6).real_links.length == 3); + assert(graph.object (6).real_links[0].objidx == 4); + assert(graph.object (6).real_links[1].objidx == 2); + assert(graph.object (6).real_links[2].objidx == 1); + + assert(strncmp (graph.object (5).head, "jkl", 3) == 0); + assert(graph.object (5).real_links.length == 1); + assert(graph.object (5).real_links[0].objidx == 0); + + assert(strncmp (graph.object (4).head, "def", 3) == 0); + assert(graph.object (4).real_links.length == 1); + assert(graph.object (4).real_links[0].objidx == 3); + + assert(strncmp (graph.object (3).head, "ghi", 3) == 0); + assert(graph.object (3).real_links.length == 1); + assert(graph.object (3).real_links[0].objidx == 5); + + assert(strncmp (graph.object (2).head, "jkl", 3) == 0); + assert(graph.object (2).real_links.length == 1); + assert(graph.object (2).real_links[0].objidx == 0); + + assert(strncmp (graph.object (1).head, "mn", 2) == 0); + assert(graph.object (1).real_links.length == 0); + + assert(strncmp (graph.object (0).head, "opqrst", 6) == 0); + assert(graph.object (0).real_links.length == 0); + + free (buffer); +} + +static void +test_serialize () +{ + size_t buffer_size = 100; + void* buffer_1 = malloc (buffer_size); + hb_serialize_context_t c1 (buffer_1, buffer_size); + populate_serializer_simple (&c1); + hb_bytes_t expected = c1.copy_bytes (); + + graph_t graph (c1.object_graph ()); + hb_blob_t* out = graph::serialize (graph); + free (buffer_1); + + hb_bytes_t actual = out->as_bytes (); + assert (actual == expected); + expected.fini (); + hb_blob_destroy (out); +} + +static void test_will_overflow_1 () +{ + size_t buffer_size = 100; + void* buffer = malloc (buffer_size); + hb_serialize_context_t c (buffer, buffer_size); + populate_serializer_complex_2 (&c); + graph_t graph (c.object_graph ()); + + assert (!graph::will_overflow (graph, nullptr)); + + free (buffer); +} + +static void test_will_overflow_2 () +{ + size_t buffer_size = 160000; + void* buffer = malloc (buffer_size); + hb_serialize_context_t c (buffer, buffer_size); + populate_serializer_with_overflow (&c); + graph_t graph (c.object_graph ()); + + assert (graph::will_overflow (graph, nullptr)); + + free (buffer); +} + +static void test_will_overflow_3 () +{ + size_t buffer_size = 160000; + void* buffer = malloc (buffer_size); + hb_serialize_context_t c (buffer, buffer_size); + populate_serializer_with_dedup_overflow (&c); + graph_t graph (c.object_graph ()); + + assert (graph::will_overflow (graph, nullptr)); + + free (buffer); +} + +static void test_resolve_overflows_via_sort () +{ + size_t buffer_size = 160000; + void* buffer = malloc (buffer_size); + hb_serialize_context_t c (buffer, buffer_size); + populate_serializer_with_overflow (&c); + graph_t graph (c.object_graph ()); + + hb_blob_t* out = hb_resolve_overflows (c.object_graph (), HB_TAG_NONE); + assert (out); + hb_bytes_t result = out->as_bytes (); + assert (result.length == (80000 + 3 + 3 * 2)); + + free (buffer); + hb_blob_destroy (out); +} + +static void test_resolve_overflows_via_duplication () +{ + size_t buffer_size = 160000; + void* buffer = malloc (buffer_size); + hb_serialize_context_t c (buffer, buffer_size); + populate_serializer_with_dedup_overflow (&c); + graph_t graph (c.object_graph ()); + + hb_blob_t* out = hb_resolve_overflows (c.object_graph (), HB_TAG_NONE); + assert (out); + hb_bytes_t result = out->as_bytes (); + assert (result.length == (10000 + 2 * 2 + 60000 + 2 + 3 * 2)); + + free (buffer); + hb_blob_destroy (out); +} + +static void test_resolve_overflows_via_space_assignment () +{ + size_t buffer_size = 160000; + void* buffer = malloc (buffer_size); + hb_serialize_context_t c (buffer, buffer_size); + populate_serializer_spaces (&c, true); + + void* expected_buffer = malloc (buffer_size); + hb_serialize_context_t e (expected_buffer, buffer_size); + populate_serializer_spaces (&e, false); + + run_resolve_overflow_test ("test_resolve_overflows_via_space_assignment", + c, + e); + + free (buffer); + free (expected_buffer); +} + +static void test_resolve_overflows_via_isolation () +{ + size_t buffer_size = 160000; + void* buffer = malloc (buffer_size); + hb_serialize_context_t c (buffer, buffer_size); + populate_serializer_with_isolation_overflow (&c); + graph_t graph (c.object_graph ()); + + assert (c.offset_overflow ()); + hb_blob_t* out = hb_resolve_overflows (c.object_graph (), HB_TAG ('G', 'S', 'U', 'B'), 0); + assert (out); + hb_bytes_t result = out->as_bytes (); + assert (result.length == (1 + 10000 + 60000 + 1 + 1 + + 4 + 3 * 2)); + + free (buffer); + hb_blob_destroy (out); +} + +static void test_resolve_overflows_via_isolation_with_recursive_duplication () +{ + size_t buffer_size = 160000; + void* buffer = malloc (buffer_size); + hb_serialize_context_t c (buffer, buffer_size); + populate_serializer_with_isolation_overflow_complex (&c); + + void* expected_buffer = malloc (buffer_size); + hb_serialize_context_t e (expected_buffer, buffer_size); + populate_serializer_with_isolation_overflow_complex_expected (&e); + + run_resolve_overflow_test ("test_resolve_overflows_via_isolation_with_recursive_duplication", + c, + e); + free (buffer); + free (expected_buffer); +} + +static void test_resolve_overflows_via_isolating_16bit_space () +{ + size_t buffer_size = 160000; + void* buffer = malloc (buffer_size); + hb_serialize_context_t c (buffer, buffer_size); + populate_serializer_spaces_16bit_connection (&c); + + void* expected_buffer = malloc (buffer_size); + hb_serialize_context_t e (expected_buffer, buffer_size); + populate_serializer_spaces_16bit_connection_expected (&e); + + run_resolve_overflow_test ("test_resolve_overflows_via_isolating_16bit_space", + c, + e); + + free (buffer); + free (expected_buffer); +} + +static void test_resolve_overflows_via_isolating_16bit_space_2 () +{ + size_t buffer_size = 160000; + void* buffer = malloc (buffer_size); + hb_serialize_context_t c (buffer, buffer_size); + populate_serializer_short_and_wide_subgraph_root (&c); + + void* expected_buffer = malloc (buffer_size); + hb_serialize_context_t e (expected_buffer, buffer_size); + populate_serializer_short_and_wide_subgraph_root_expected (&e); + + run_resolve_overflow_test ("test_resolve_overflows_via_isolating_16bit_space_2", + c, + e); + + free (buffer); + free (expected_buffer); +} + +static void test_resolve_overflows_via_isolation_spaces () +{ + size_t buffer_size = 160000; + void* buffer = malloc (buffer_size); + hb_serialize_context_t c (buffer, buffer_size); + populate_serializer_with_isolation_overflow_spaces (&c); + graph_t graph (c.object_graph ()); + + assert (c.offset_overflow ()); + hb_blob_t* out = hb_resolve_overflows (c.object_graph (), HB_TAG ('G', 'S', 'U', 'B'), 0); + assert (out); + hb_bytes_t result = out->as_bytes (); + + unsigned expected_length = 3 + 2 * 60000; // objects + expected_length += 2 * 4 + 2 * 2; // links + assert (result.length == expected_length); + + free (buffer); + hb_blob_destroy (out); +} + +static void test_resolve_mixed_overflows_via_isolation_spaces () +{ + size_t buffer_size = 200000; + void* buffer = malloc (buffer_size); + hb_serialize_context_t c (buffer, buffer_size); + populate_serializer_with_24_and_32_bit_offsets (&c); + graph_t graph (c.object_graph ()); + + assert (c.offset_overflow ()); + hb_blob_t* out = hb_resolve_overflows (c.object_graph (), HB_TAG ('G', 'S', 'U', 'B'), 0); + assert (out); + hb_bytes_t result = out->as_bytes (); + + unsigned expected_length = + // Objects + 7 + + 4 * 40000; + + expected_length += + // Links + 2 * 4 + // 32 + 4 * 3 + // 24 + 4 * 2; // 16 + + assert (result.length == expected_length); + + free (buffer); + hb_blob_destroy (out); +} + +static void test_resolve_with_extension_promotion () +{ + size_t buffer_size = 200000; + void* buffer = malloc (buffer_size); + assert (buffer); + hb_serialize_context_t c (buffer, buffer_size); + populate_serializer_with_extension_promotion (&c); + + void* expected_buffer = malloc (buffer_size); + assert (expected_buffer); + hb_serialize_context_t e (expected_buffer, buffer_size); + populate_serializer_with_extension_promotion (&e, 3); + + run_resolve_overflow_test ("test_resolve_with_extension_promotion", + c, + e, + 20, + true); + free (buffer); + free (expected_buffer); +} + +static void test_resolve_with_basic_pair_pos_1_split () +{ + size_t buffer_size = 200000; + void* buffer = malloc (buffer_size); + assert (buffer); + hb_serialize_context_t c (buffer, buffer_size); + populate_serializer_with_large_pair_pos_1 <1, 4>(&c); + + void* expected_buffer = malloc (buffer_size); + assert (expected_buffer); + hb_serialize_context_t e (expected_buffer, buffer_size); + populate_serializer_with_large_pair_pos_1 <2, 2>(&e, true); + + run_resolve_overflow_test ("test_resolve_with_basic_pair_pos_1_split", + c, + e, + 20, + true, + HB_TAG('G', 'P', 'O', 'S')); + free (buffer); + free (expected_buffer); +} + +static void test_resolve_with_extension_pair_pos_1_split () +{ + size_t buffer_size = 200000; + void* buffer = malloc (buffer_size); + assert (buffer); + hb_serialize_context_t c (buffer, buffer_size); + populate_serializer_with_large_pair_pos_1 <1, 4>(&c, true); + + void* expected_buffer = malloc (buffer_size); + assert (expected_buffer); + hb_serialize_context_t e (expected_buffer, buffer_size); + populate_serializer_with_large_pair_pos_1 <2, 2>(&e, true); + + run_resolve_overflow_test ("test_resolve_with_extension_pair_pos_1_split", + c, + e, + 20, + true, + HB_TAG('G', 'P', 'O', 'S')); + free (buffer); + free (expected_buffer); +} + +static void test_resolve_with_basic_pair_pos_2_split () +{ + size_t buffer_size = 300000; + void* buffer = malloc (buffer_size); + assert (buffer); + hb_serialize_context_t c (buffer, buffer_size); + populate_serializer_with_large_pair_pos_2 <1, 4, 3000>(&c); + + void* expected_buffer = malloc (buffer_size); + assert (expected_buffer); + hb_serialize_context_t e (expected_buffer, buffer_size); + populate_serializer_with_large_pair_pos_2 <2, 2, 3000>(&e, true); + + run_resolve_overflow_test ("test_resolve_with_basic_pair_pos_2_split", + c, + e, + 20, + true, + HB_TAG('G', 'P', 'O', 'S')); + free (buffer); + free (expected_buffer); +} + +static void test_resolve_with_close_to_limit_pair_pos_2_split () +{ + size_t buffer_size = 300000; + void* buffer = malloc (buffer_size); + assert (buffer); + hb_serialize_context_t c (buffer, buffer_size); + populate_serializer_with_large_pair_pos_2 <1, 1596, 10>(&c, true, false, false); + + void* expected_buffer = malloc (buffer_size); + assert (expected_buffer); + hb_serialize_context_t e (expected_buffer, buffer_size); + populate_serializer_with_large_pair_pos_2 <2, 798, 10>(&e, true, false, false); + + run_resolve_overflow_test ("test_resolve_with_close_to_limit_pair_pos_2_split", + c, + e, + 20, + true, + HB_TAG('G', 'P', 'O', 'S')); + free (buffer); + free (expected_buffer); +} + +static void test_resolve_with_pair_pos_2_split_with_device_tables () +{ + size_t buffer_size = 300000; + void* buffer = malloc (buffer_size); + assert (buffer); + hb_serialize_context_t c (buffer, buffer_size); + populate_serializer_with_large_pair_pos_2 <1, 4, 2000>(&c, false, true); + + void* expected_buffer = malloc (buffer_size); + assert (expected_buffer); + hb_serialize_context_t e (expected_buffer, buffer_size); + populate_serializer_with_large_pair_pos_2 <2, 2, 2000>(&e, true, true); + + run_resolve_overflow_test ("test_resolve_with_pair_pos_2_split_with_device_tables", + c, + e, + 20, + true, + HB_TAG('G', 'P', 'O', 'S')); + free (buffer); + free (expected_buffer); +} + +static void test_resolve_with_basic_mark_base_pos_1_split () +{ + size_t buffer_size = 200000; + void* buffer = malloc (buffer_size); + assert (buffer); + hb_serialize_context_t c (buffer, buffer_size); + populate_serializer_with_large_mark_base_pos_1 <40, 10, 110, 1>(&c); + + void* expected_buffer = malloc (buffer_size); + assert (expected_buffer); + hb_serialize_context_t e (expected_buffer, buffer_size); + populate_serializer_with_large_mark_base_pos_1 <40, 10, 110, 2>(&e); + + run_resolve_overflow_test ("test_resolve_with_basic_mark_base_pos_1_split", + c, + e, + 20, + true, + HB_TAG('G', 'P', 'O', 'S')); + free (buffer); + free (expected_buffer); +} + +static void test_resolve_overflows_via_splitting_spaces () +{ + size_t buffer_size = 160000; + void* buffer = malloc (buffer_size); + hb_serialize_context_t c (buffer, buffer_size); + populate_serializer_with_split_spaces (&c); + + void* expected_buffer = malloc (buffer_size); + hb_serialize_context_t e (expected_buffer, buffer_size); + populate_serializer_with_split_spaces_expected (&e); + + run_resolve_overflow_test ("test_resolve_overflows_via_splitting_spaces", + c, + e, + 1); + + free (buffer); + free (expected_buffer); + +} + +static void test_resolve_overflows_via_splitting_spaces_2 () +{ + size_t buffer_size = 160000; + void* buffer = malloc (buffer_size); + hb_serialize_context_t c (buffer, buffer_size); + populate_serializer_with_split_spaces_2 (&c); + + void* expected_buffer = malloc (buffer_size); + hb_serialize_context_t e (expected_buffer, buffer_size); + populate_serializer_with_split_spaces_expected_2 (&e); + + run_resolve_overflow_test ("test_resolve_overflows_via_splitting_spaces_2", + c, + e, + 1); + free (buffer); + free (expected_buffer); +} + +static void test_resolve_overflows_via_priority () +{ + size_t buffer_size = 160000; + void* buffer = malloc (buffer_size); + hb_serialize_context_t c (buffer, buffer_size); + populate_serializer_with_priority_overflow (&c); + + void* expected_buffer = malloc (buffer_size); + hb_serialize_context_t e (expected_buffer, buffer_size); + populate_serializer_with_priority_overflow_expected (&e); + + run_resolve_overflow_test ("test_resolve_overflows_via_priority", + c, + e, + 3); + free (buffer); + free (expected_buffer); +} + + +static void test_virtual_link () +{ + size_t buffer_size = 100; + void* buffer = malloc (buffer_size); + hb_serialize_context_t c (buffer, buffer_size); + populate_serializer_virtual_link (&c); + + hb_blob_t* out = hb_resolve_overflows (c.object_graph (), HB_TAG_NONE); + assert (out); + + hb_bytes_t result = out->as_bytes (); + assert (result.length == 5 + 4 * 2); + assert (result[0] == 'a'); + assert (result[5] == 'c'); + assert (result[8] == 'e'); + assert (result[9] == 'b'); + assert (result[12] == 'd'); + + free (buffer); + hb_blob_destroy (out); +} + +static void +test_shared_node_with_virtual_links () +{ + size_t buffer_size = 100; + void* buffer = malloc (buffer_size); + hb_serialize_context_t c (buffer, buffer_size); + + c.start_serialize (); + + unsigned obj_b = add_object ("b", 1, &c); + unsigned obj_c = add_object ("c", 1, &c); + + start_object ("d", 1, &c); + add_virtual_offset (obj_b, &c); + unsigned obj_d_1 = c.pop_pack (); + + start_object ("d", 1, &c); + add_virtual_offset (obj_c, &c); + unsigned obj_d_2 = c.pop_pack (); + + assert (obj_d_1 == obj_d_2); + + start_object ("a", 1, &c); + add_offset (obj_b, &c); + add_offset (obj_c, &c); + add_offset (obj_d_1, &c); + add_offset (obj_d_2, &c); + c.pop_pack (); + c.end_serialize (); + + assert(c.object_graph() [obj_d_1]->virtual_links.length == 2); + assert(c.object_graph() [obj_d_1]->virtual_links[0].objidx == obj_b); + assert(c.object_graph() [obj_d_1]->virtual_links[1].objidx == obj_c); + free(buffer); +} + + +// TODO(garretrieger): update will_overflow tests to check the overflows array. +// TODO(garretrieger): add tests for priority raising. + +int +main (int argc, char **argv) +{ + test_serialize (); + test_sort_shortest (); + test_will_overflow_1 (); + test_will_overflow_2 (); + test_will_overflow_3 (); + test_resolve_overflows_via_sort (); + test_resolve_overflows_via_duplication (); + test_resolve_overflows_via_priority (); + test_resolve_overflows_via_space_assignment (); + test_resolve_overflows_via_isolation (); + test_resolve_overflows_via_isolation_with_recursive_duplication (); + test_resolve_overflows_via_isolation_spaces (); + test_resolve_overflows_via_isolating_16bit_space (); + test_resolve_overflows_via_isolating_16bit_space_2 (); + test_resolve_overflows_via_splitting_spaces (); + test_resolve_overflows_via_splitting_spaces_2 (); + test_resolve_mixed_overflows_via_isolation_spaces (); + test_duplicate_leaf (); + test_duplicate_interior (); + test_virtual_link (); + test_shared_node_with_virtual_links (); + test_resolve_with_extension_promotion (); + test_resolve_with_basic_pair_pos_1_split (); + test_resolve_with_extension_pair_pos_1_split (); + test_resolve_with_basic_pair_pos_2_split (); + test_resolve_with_pair_pos_2_split_with_device_tables (); + test_resolve_with_close_to_limit_pair_pos_2_split (); + test_resolve_with_basic_mark_base_pos_1_split (); + + // TODO(grieger): have run overflow tests compare graph equality not final packed binary. + // TODO(grieger): split test where multiple subtables in one lookup are split to test link ordering. + // TODO(grieger): split test where coverage table in subtable that is being split is shared. + // TODO(grieger): test with extensions already mixed in as well. + // TODO(grieger): test two layer ext promotion setup. + // TODO(grieger): test sorting by subtables per byte in ext. promotion. +} diff --git a/libs/harfbuzz/src/test-serialize.cc b/libs/harfbuzz/src/test-serialize.cc new file mode 100644 index 00000000..4c90abb1 --- /dev/null +++ b/libs/harfbuzz/src/test-serialize.cc @@ -0,0 +1,52 @@ +/* + * Copyright © 2022 Behdad Esfahbod + * + * 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. + * + */ + +#include "hb.hh" +#include "hb-serialize.hh" +#include "hb-ot-layout-common.hh" + +using OT::Layout::Common::Coverage; + +int +main (int argc, char **argv) +{ + char buf[16384]; + + hb_serialize_context_t s (buf, sizeof (buf)); + + hb_sorted_vector_t v{1, 2, 5}; + + auto c = s.start_serialize (); + + c->serialize (&s, hb_iter (v)); + + s.end_serialize (); + + hb_bytes_t bytes = s.copy_bytes (); + assert (bytes.length == 10); + bytes.fini (); + + return 0; +} diff --git a/libs/harfbuzz/src/test-set.cc b/libs/harfbuzz/src/test-set.cc new file mode 100644 index 00000000..983a1591 --- /dev/null +++ b/libs/harfbuzz/src/test-set.cc @@ -0,0 +1,106 @@ +/* + * Copyright © 2021 Behdad Esfahbod + * + * 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. + */ + +#include "hb.hh" +#include "hb-set.hh" + +int +main (int argc, char **argv) +{ + + /* Test copy constructor. */ + { + hb_set_t v1 {1, 2}; + hb_set_t v2 {v1}; + assert (v1.get_population () == 2); + assert (v2.get_population () == 2); + } + + /* Test copy assignment. */ + { + hb_set_t v1 {1, 2}; + hb_set_t v2; + v2 = v1; + assert (v1.get_population () == 2); + assert (v2.get_population () == 2); + } + + /* Test move constructor. */ + { + hb_set_t s {1, 2}; + hb_set_t v (std::move (s)); + assert (s.get_population () == 0); + assert (v.get_population () == 2); + } + + /* Test move assignment. */ + { + hb_set_t s = hb_set_t {1, 2}; + hb_set_t v; + v = std::move (s); + assert (s.get_population () == 0); + assert (v.get_population () == 2); + } + + /* Test initializing from iterable. */ + { + hb_set_t s; + + s.add (18); + s.add (12); + + hb_vector_t v (s); + hb_set_t v0 (v); + hb_set_t v1 (s); + hb_set_t v2 (std::move (s)); + + assert (s.get_population () == 0); + assert (v0.get_population () == 2); + assert (v1.get_population () == 2); + assert (v2.get_population () == 2); + } + + /* Test initializing from iterator. */ + { + hb_set_t s; + + s.add (18); + s.add (12); + + hb_set_t v (hb_iter (s)); + + assert (v.get_population () == 2); + } + + /* Test initializing from initializer list and swapping. */ + { + hb_set_t v1 {1, 2, 3}; + hb_set_t v2 {4, 5}; + hb_swap (v1, v2); + assert (v1.get_population () == 2); + assert (v2.get_population () == 3); + } + + return 0; +} diff --git a/libs/harfbuzz/src/test-unicode-ranges.cc b/libs/harfbuzz/src/test-unicode-ranges.cc new file mode 100644 index 00000000..33cac6b7 --- /dev/null +++ b/libs/harfbuzz/src/test-unicode-ranges.cc @@ -0,0 +1,66 @@ +/* + * Copyright © 2018 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): Garret Rieger + */ + +#include "hb.hh" +#include "hb-ot-os2-unicode-ranges.hh" + +static void +test (hb_codepoint_t cp, unsigned int bit) +{ + if (OT::_hb_ot_os2_get_unicode_range_bit (cp) != bit) + { + fprintf (stderr, "got incorrect bit (%d) for cp 0x%X. Should have been %d.", + OT::_hb_ot_os2_get_unicode_range_bit (cp), + cp, + bit); + abort(); + } +} + +static void +test_get_unicode_range_bit () +{ + test (0x0000, 0); + test (0x0042, 0); + test (0x007F, 0); + test (0x0080, 1); + + test (0x30A0, 50); + test (0x30B1, 50); + test (0x30FF, 50); + + test (0x10FFFD, 90); + + test (0x30000, -1); + test (0x110000, -1); +} + +int +main () +{ + test_get_unicode_range_bit (); + return 0; +} diff --git a/libs/harfbuzz/src/test-use-table.cc b/libs/harfbuzz/src/test-use-table.cc new file mode 100644 index 00000000..70d2d41f --- /dev/null +++ b/libs/harfbuzz/src/test-use-table.cc @@ -0,0 +1,18 @@ +#include "hb-ot-shaper-use-table.hh" + +int main (int argc, char **argv) +{ + if (argc != 2) + { + for (unsigned u = 0; u < 0x10FFFFu; u++) + printf ("U+%04X %d\n", u, hb_use_get_category (u)); + return 0; + } + + hb_codepoint_t u; + sscanf (argv[1], "%x", &u); + + printf ("%d\n", hb_use_get_category (u)); + + return 0; +} diff --git a/libs/harfbuzz/src/test-vector.cc b/libs/harfbuzz/src/test-vector.cc new file mode 100644 index 00000000..37bec309 --- /dev/null +++ b/libs/harfbuzz/src/test-vector.cc @@ -0,0 +1,167 @@ +/* + * Copyright © 2021 Behdad Esfahbod + * + * 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. + * + */ + +#include "hb.hh" +#include "hb-vector.hh" +#include "hb-set.hh" +#include + + +int +main (int argc, char **argv) +{ + + /* Test copy constructor. */ + { + hb_vector_t v1 {1, 2}; + hb_vector_t v2 {v1}; + hb_vector_t V2 {v1}; + assert (v1.length == 2); + assert (v1[0] == 1); + assert (v1[1] == 2); + assert (v2.length == 2); + assert (v2[0] == 1); + assert (v2[1] == 2); + } + + /* Test copy assignment. */ + { + hb_vector_t v1 {1, 2}; + hb_vector_t v2 = v1; + hb_vector_t V2 = v1; + assert (v1.length == 2); + assert (v1[0] == 1); + assert (v1[1] == 2); + assert (v2.length == 2); + assert (v2[0] == 1); + assert (v2[1] == 2); + } + + /* Test move constructor. */ + { + hb_vector_t s {1, 2}; + hb_sorted_vector_t S {1, 2}; + hb_vector_t v (std::move (s)); + hb_sorted_vector_t V (std::move (S)); + assert (s.length == 0); + assert (S.length == 0); + assert (v.length == 2); + assert (v[0] == 1); + assert (v[1] == 2); + } + + /* Test move assignment. */ + { + hb_vector_t s {1, 2}; + hb_sorted_vector_t S {1, 2}; + hb_vector_t v; + hb_sorted_vector_t V; + v = std::move (s); + V = std::move (S); + assert (s.length == 0); + assert (S.length == 0); + assert (v.length == 2); + assert (V.length == 2); + assert (v[0] == 1); + assert (v[1] == 2); + } + + /* Test initializing from iterable. */ + { + hb_set_t s; + + s.add (18); + s.add (12); + + hb_vector_t v (s); + hb_sorted_vector_t V (s); + + assert (v.length == 2); + assert (V.length == 2); + assert (v[0] == 12); + assert (V[0] == 12); + assert (v[1] == 18); + assert (V[1] == 18); + } + + /* Test initializing from iterator. */ + { + hb_set_t s; + + s.add (18); + s.add (12); + + hb_vector_t v (hb_iter (s)); + hb_vector_t V (hb_iter (s)); + + assert (v.length == 2); + assert (V.length == 2); + assert (v[0] == 12); + assert (V[0] == 12); + assert (v[1] == 18); + assert (V[1] == 18); + } + + /* Test initializing from initializer list and swapping. */ + { + hb_vector_t v1 {1, 2, 3}; + hb_vector_t v2 {4, 5}; + hb_swap (v1, v2); + assert (v1.length == 2); + assert (v1[0] == 4); + assert (v2.length == 3); + assert (v2[2] == 3); + } + + /* Test initializing sorted-vector from initializer list and swapping. */ + { + hb_sorted_vector_t v1 {1, 2, 3}; + hb_sorted_vector_t v2 {4, 5}; + hb_swap (v1, v2); + assert (v1.length == 2); + assert (v1[0] == 4); + assert (v2.length == 3); + assert (v2[2] == 3); + } + + { + hb_vector_t v; + + std::string s; + for (unsigned i = 1; i < 100; i++) + { + s += "x"; + v.push (s); + } + + hb_vector_t v2; + + v2 = v; + + v2.remove (50); + } + + return 0; +} diff --git a/libs/harfbuzz/src/test.cc b/libs/harfbuzz/src/test.cc new file mode 100644 index 00000000..d848cf10 --- /dev/null +++ b/libs/harfbuzz/src/test.cc @@ -0,0 +1,95 @@ +/* + * Copyright © 2010,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 + */ + +#include "hb.hh" + +#ifdef HAVE_FREETYPE +#include "hb-ft.h" +#endif + +#ifdef HB_NO_OPEN +#define hb_blob_create_from_file_or_fail(x) hb_blob_get_empty () +#endif + +int +main (int argc, char **argv) +{ + if (argc != 2) { + fprintf (stderr, "usage: %s font-file.ttf\n", argv[0]); + exit (1); + } + + hb_blob_t *blob = hb_blob_create_from_file_or_fail (argv[1]); + assert (blob); + printf ("Opened font file %s: %u bytes long\n", argv[1], hb_blob_get_length (blob)); + + /* Create the face */ + hb_face_t *face = hb_face_create (blob, 0 /* first face */); + hb_blob_destroy (blob); + blob = nullptr; + unsigned int upem = hb_face_get_upem (face); + + hb_font_t *font = hb_font_create (face); + hb_font_set_scale (font, upem, upem); + +#ifdef HAVE_FREETYPE + hb_ft_font_set_funcs (font); +#endif + + hb_buffer_t *buffer = hb_buffer_create (); + + hb_buffer_add_utf8 (buffer, "\xe0\xa4\x95\xe0\xa5\x8d\xe0\xa4\xb0\xe0\xa5\x8d\xe0\xa4\x95", -1, 0, -1); + hb_buffer_guess_segment_properties (buffer); + + hb_shape (font, buffer, nullptr, 0); + + unsigned int count = hb_buffer_get_length (buffer); + hb_glyph_info_t *infos = hb_buffer_get_glyph_infos (buffer, nullptr); + hb_glyph_position_t *positions = hb_buffer_get_glyph_positions (buffer, nullptr); + + for (unsigned int i = 0; i < count; i++) + { + hb_glyph_info_t *info = &infos[i]; + hb_glyph_position_t *pos = &positions[i]; + + printf ("cluster %d glyph 0x%x at (%d,%d)+(%d,%d)\n", + info->cluster, + info->codepoint, + pos->x_offset, + pos->y_offset, + pos->x_advance, + pos->y_advance); + + } + + hb_buffer_destroy (buffer); + hb_font_destroy (font); + hb_face_destroy (face); + + return 0; +} + + diff --git a/libs/harfbuzz/src/update-unicode-tables.make b/libs/harfbuzz/src/update-unicode-tables.make new file mode 100644 index 00000000..6f5b490f --- /dev/null +++ b/libs/harfbuzz/src/update-unicode-tables.make @@ -0,0 +1,49 @@ +#!/usr/bin/env -S make -f + +all: packtab \ + hb-ot-shaper-arabic-joining-list.hh \ + hb-ot-shaper-arabic-table.hh hb-unicode-emoji-table.hh \ + hb-ot-shaper-indic-table.cc hb-ot-tag-table.hh \ + hb-ucd-table.hh hb-ot-shaper-use-table.hh \ + hb-ot-shaper-vowel-constraints.cc + +.PHONY: all clean packtab + +hb-ot-shaper-arabic-joining-list.hh: gen-arabic-joining-list.py ArabicShaping.txt Scripts.txt + ./$^ > $@ || ($(RM) $@; false) +hb-ot-shaper-arabic-table.hh: gen-arabic-table.py ArabicShaping.txt UnicodeData.txt Blocks.txt + ./$^ > $@ || ($(RM) $@; false) +hb-unicode-emoji-table.hh: gen-emoji-table.py emoji-data.txt emoji-test.txt + ./$^ > $@ || ($(RM) $@; false) +hb-ot-shaper-indic-table.cc: gen-indic-table.py IndicSyllabicCategory.txt IndicPositionalCategory.txt Blocks.txt + ./$^ > $@ || ($(RM) $@; false) +hb-ot-tag-table.hh: gen-tag-table.py languagetags language-subtag-registry + ./$^ > $@ || ($(RM) $@; false) +hb-ucd-table.hh: gen-ucd-table.py ucd.nounihan.grouped.zip hb-common.h + ./$^ > $@ || ($(RM) $@; false) +hb-ot-shaper-use-table.hh: gen-use-table.py IndicSyllabicCategory.txt IndicPositionalCategory.txt ArabicShaping.txt DerivedCoreProperties.txt UnicodeData.txt Blocks.txt Scripts.txt ms-use/IndicSyllabicCategory-Additional.txt ms-use/IndicPositionalCategory-Additional.txt + ./$^ > $@ || ($(RM) $@; false) +hb-ot-shaper-vowel-constraints.cc: gen-vowel-constraints.py ms-use/IndicShapingInvalidCluster.txt Scripts.txt + ./$^ > $@ || ($(RM) $@; false) + +packtab: + /usr/bin/env python3 -c "import packTab" 2>/dev/null || /usr/bin/env python3 -m pip install git+https://github.com/harfbuzz/packtab + +ArabicShaping.txt Blocks.txt DerivedCoreProperties.txt IndicPositionalCategory.txt IndicSyllabicCategory.txt Scripts.txt UnicodeData.txt: + curl -O https://unicode.org/Public/UCD/latest/ucd/$@ +emoji-data.txt: + curl -O https://www.unicode.org/Public/UCD/latest/ucd/emoji/emoji-data.txt +emoji-test.txt: + curl -O https://www.unicode.org/Public/emoji/latest/emoji-test.txt +languagetags: + curl -O https://learn.microsoft.com/en-us/typography/opentype/spec/languagetags +language-subtag-registry: + curl -O https://www.iana.org/assignments/language-subtag-registry/language-subtag-registry +ucd.nounihan.grouped.zip: + curl -O https://unicode.org/Public/UCD/latest/ucdxml/ucd.nounihan.grouped.zip + +clean: + $(RM) \ + ArabicShaping.txt UnicodeData.txt Blocks.txt emoji-data.txt \ + IndicSyllabicCategory.txt IndicPositionalCategory.txt \ + languagetags language-subtag-registry ucd.nounihan.grouped.zip Scripts.txt diff --git a/libs/harfbuzz/subprojects/.gitignore b/libs/harfbuzz/subprojects/.gitignore new file mode 100644 index 00000000..9cdd6880 --- /dev/null +++ b/libs/harfbuzz/subprojects/.gitignore @@ -0,0 +1,6 @@ +/freetype2 +/glib +/packagecache +/benchmark-* +/cairo +/ragel-6.10 diff --git a/libs/harfbuzz/subprojects/cairo.wrap b/libs/harfbuzz/subprojects/cairo.wrap new file mode 100644 index 00000000..afb7e742 --- /dev/null +++ b/libs/harfbuzz/subprojects/cairo.wrap @@ -0,0 +1,8 @@ +[wrap-git] +directory=cairo +url=https://gitlab.freedesktop.org/cairo/cairo.git +depth=1 +revision=c90faeb7492b1b778d18a796afe5c2e4b32a6356 + +[provide] +dependency_names = cairo, cairo-ft diff --git a/libs/harfbuzz/subprojects/freetype2.wrap b/libs/harfbuzz/subprojects/freetype2.wrap new file mode 100644 index 00000000..43ab8fa4 --- /dev/null +++ b/libs/harfbuzz/subprojects/freetype2.wrap @@ -0,0 +1,8 @@ +[wrap-git] +directory=freetype +url=https://gitlab.freedesktop.org/freetype/freetype.git +revision=VER-2-11-0 + +[provide] +freetype2 = freetype_dep +freetype = freetype_dep diff --git a/libs/harfbuzz/subprojects/glib.wrap b/libs/harfbuzz/subprojects/glib.wrap new file mode 100644 index 00000000..91430907 --- /dev/null +++ b/libs/harfbuzz/subprojects/glib.wrap @@ -0,0 +1,10 @@ +[wrap-git] +directory=glib +url=https://gitlab.gnome.org/GNOME/glib.git +depth=1 +push-url=git@gitlab.gnome.org:GNOME/glib.git +revision=2.58.1 + +[provide] +glib-2.0 = libglib_dep +gobject-2.0 = libgobject_dep diff --git a/libs/harfbuzz/subprojects/google-benchmark.wrap b/libs/harfbuzz/subprojects/google-benchmark.wrap new file mode 100644 index 00000000..268ddf92 --- /dev/null +++ b/libs/harfbuzz/subprojects/google-benchmark.wrap @@ -0,0 +1,11 @@ +[wrap-file] +directory = benchmark-1.6.0 +source_url = https://github.com/google/benchmark/archive/refs/tags/v1.6.0.tar.gz +source_filename = benchmark-1.6.0.tar.gz +source_hash = 1f71c72ce08d2c1310011ea6436b31e39ccab8c2db94186d26657d41747c85d6 +patch_filename = google-benchmark_1.6.0-1_patch.zip +patch_url = https://wrapdb.mesonbuild.com/v2/google-benchmark_1.6.0-1/get_patch +patch_hash = f2feff0641a5ebf6b893a4f49ea0bff566ba866ee993c5c3aef62ff40cdc6b14 + +[provide] +benchmark = google_benchmark_dep diff --git a/libs/harfbuzz/subprojects/packagefiles/ragel/meson.build b/libs/harfbuzz/subprojects/packagefiles/ragel/meson.build new file mode 100644 index 00000000..cd317c20 --- /dev/null +++ b/libs/harfbuzz/subprojects/packagefiles/ragel/meson.build @@ -0,0 +1,58 @@ +project('ragel', 'c', 'cpp', + version : '6.10' +) + +conf = configuration_data() +conf.set_quoted('PACKAGE', meson.project_name()) +conf.set_quoted('VERSION', meson.project_version()) +configure_file( + output : 'config.h', + configuration : conf +) + +ragel_sources = files( + 'ragel/buffer.h', 'ragel/cdcodegen.cpp', 'ragel/cdcodegen.h', + 'ragel/cdfflat.cpp', 'ragel/cdfflat.h', 'ragel/cdfgoto.cpp', + 'ragel/cdfgoto.h', 'ragel/cdflat.cpp', 'ragel/cdflat.h', + 'ragel/cdftable.cpp', 'ragel/cdftable.h', 'ragel/cdgoto.cpp', + 'ragel/cdgoto.h', 'ragel/cdipgoto.cpp', 'ragel/cdipgoto.h', + 'ragel/cdsplit.cpp', 'ragel/cdsplit.h', 'ragel/cdtable.cpp', + 'ragel/cdtable.h', 'ragel/common.cpp', 'ragel/common.h', + 'ragel/cscodegen.cpp', 'ragel/cscodegen.h', 'ragel/csfflat.cpp', + 'ragel/csfflat.h', 'ragel/csfgoto.cpp', 'ragel/csfgoto.h', + 'ragel/csflat.cpp', 'ragel/csflat.h', 'ragel/csftable.cpp', + 'ragel/csftable.h', 'ragel/csgoto.cpp', 'ragel/csgoto.h', + 'ragel/csipgoto.cpp', 'ragel/csipgoto.h', 'ragel/cssplit.cpp', + 'ragel/cssplit.h', 'ragel/cstable.cpp', 'ragel/cstable.h', + 'ragel/dotcodegen.cpp', 'ragel/dotcodegen.h', 'ragel/fsmap.cpp', + 'ragel/fsmattach.cpp', 'ragel/fsmbase.cpp', 'ragel/fsmgraph.cpp', + 'ragel/fsmgraph.h', 'ragel/fsmmin.cpp', 'ragel/fsmstate.cpp', + 'ragel/gendata.cpp', 'ragel/gendata.h', 'ragel/gocodegen.cpp', + 'ragel/gocodegen.h', 'ragel/gofflat.cpp', 'ragel/gofflat.h', + 'ragel/gofgoto.cpp', 'ragel/gofgoto.h', 'ragel/goflat.cpp', 'ragel/goflat.h', + 'ragel/goftable.cpp', 'ragel/goftable.h', 'ragel/gogoto.cpp', + 'ragel/gogoto.h', 'ragel/goipgoto.cpp', 'ragel/goipgoto.h', + 'ragel/gotable.cpp', 'ragel/gotable.h', 'ragel/gotablish.cpp', + 'ragel/gotablish.h', 'ragel/inputdata.cpp', 'ragel/inputdata.h', + 'ragel/javacodegen.cpp', 'ragel/javacodegen.h', 'ragel/main.cpp', + 'ragel/mlcodegen.cpp', 'ragel/mlcodegen.h', 'ragel/mlfflat.cpp', + 'ragel/mlfflat.h', 'ragel/mlfgoto.cpp', 'ragel/mlfgoto.h', + 'ragel/mlflat.cpp', 'ragel/mlflat.h', 'ragel/mlftable.cpp', + 'ragel/mlftable.h', 'ragel/mlgoto.cpp', 'ragel/mlgoto.h', + 'ragel/mltable.cpp', 'ragel/mltable.h', 'ragel/parsedata.cpp', + 'ragel/parsedata.h', 'ragel/parsetree.cpp', 'ragel/parsetree.h', + 'ragel/pcheck.h', 'ragel/ragel.h', 'ragel/rbxgoto.cpp', 'ragel/rbxgoto.h', + 'ragel/redfsm.cpp', 'ragel/redfsm.h', 'ragel/rlparse.cpp', 'ragel/rlparse.h', + 'ragel/rlscan.cpp', 'ragel/rlscan.h', 'ragel/rubycodegen.cpp', + 'ragel/rubycodegen.h', 'ragel/rubyfflat.cpp', 'ragel/rubyfflat.h', + 'ragel/rubyflat.cpp', 'ragel/rubyflat.h', 'ragel/rubyftable.cpp', + 'ragel/rubyftable.h', 'ragel/rubytable.cpp', 'ragel/rubytable.h', + 'ragel/version.h', 'ragel/xmlcodegen.cpp', 'ragel/xmlcodegen.h', +) + +ragel = executable( + meson.project_name(), + ragel_sources, + include_directories : ['aapl', 'ragel'], + install : true, +) diff --git a/libs/harfbuzz/subprojects/ragel.wrap b/libs/harfbuzz/subprojects/ragel.wrap new file mode 100644 index 00000000..1dabcbc1 --- /dev/null +++ b/libs/harfbuzz/subprojects/ragel.wrap @@ -0,0 +1,11 @@ +[wrap-file] +directory = ragel-6.10 +source_url = https://www.colm.net/files/ragel/ragel-6.10.tar.gz +source_filename = ragel-6.10.tar.gz +source_hash = 5f156edb65d20b856d638dd9ee2dfb43285914d9aa2b6ec779dac0270cd56c3f +patch_directory = ragel + +[provide] +ragel = ragel + + diff --git a/libs/harfbuzz/util/Makefile.am b/libs/harfbuzz/util/Makefile.am new file mode 100644 index 00000000..cabb9eff --- /dev/null +++ b/libs/harfbuzz/util/Makefile.am @@ -0,0 +1,80 @@ +# Process this file with automake to produce Makefile.in + +NULL = +EXTRA_DIST = +CLEANFILES = +DISTCLEANFILES = +MAINTAINERCLEANFILES = + +EXTRA_DIST += meson.build + +include Makefile.sources + +# Convenience targets: +lib: + @$(MAKE) $(AM_MAKEFLAGS) -C $(top_builddir)/src lib +libs: + @$(MAKE) $(AM_MAKEFLAGS) -C $(top_builddir)/src libs + +bin_PROGRAMS = + +AM_CPPFLAGS = \ + -DHB_DISABLE_DEPRECATED \ + -I$(top_srcdir)/src/ \ + -I$(top_builddir)/src/ \ + $(GLIB_CFLAGS) \ + $(FREETYPE_CFLAGS) \ + $(CAIRO_FT_CFLAGS) \ + $(CHAFA_CFLAGS) \ + $(NULL) +LDADD = \ + $(top_builddir)/src/libharfbuzz.la \ + -lm \ + $(GLIB_LIBS) \ + $(FREETYPE_LIBS) \ + $(NULL) + +if HAVE_GLIB + +if HAVE_FREETYPE +if HAVE_CAIRO_FT +hb_view_SOURCES = $(HB_VIEW_sources) +hb_view_LDADD = \ + $(LDADD) \ + $(CAIRO_LIBS) \ + $(CAIRO_FT_LIBS) \ + $(CHAFA_LIBS) \ + $(NULL) +bin_PROGRAMS += hb-view +endif # HAVE_CAIRO_FT +endif # HAVE_FREETYPE + +hb_shape_SOURCES = $(HB_SHAPE_sources) +bin_PROGRAMS += hb-shape + +hb_subset_SOURCES = $(HB_SUBSET_CLI_sources) +hb_subset_LDADD = \ + $(top_builddir)/src/libharfbuzz-subset.la \ + $(LDADD) + +bin_PROGRAMS += hb-subset + +hb_ot_shape_closure_SOURCES = $(HB_OT_SHAPE_CLOSURE_sources) +bin_PROGRAMS += hb-ot-shape-closure + +endif # HAVE_GLIB + +#if HAVE_FONTCONFIG +#hb_fc_list_SOURCES = \ +# hb-fc.cc \ +# hb-fc.h \ +# hb-fc-list.c \ +# $(NULL) +#hb_fc_list_LDADD = \ +# $(LDADD) \ +# $(FONTCONFIG_LIBS) \ +# $(NULL) +#bin_PROGRAMS += hb-fc-list +#endif # HAVE_FONTCONFIG + +-include $(top_srcdir)/git.mk diff --git a/libs/harfbuzz/util/Makefile.sources b/libs/harfbuzz/util/Makefile.sources new file mode 100644 index 00000000..628de050 --- /dev/null +++ b/libs/harfbuzz/util/Makefile.sources @@ -0,0 +1,52 @@ +HB_VIEW_sources = \ + ansi-print.hh \ + face-options.hh \ + font-options.hh \ + hb-view.cc \ + helper-cairo-ansi.hh \ + helper-cairo-ft.hh \ + helper-cairo-user.hh \ + helper-cairo.hh \ + main-font-text.hh \ + options.hh \ + output-options.hh \ + shape-consumer.hh \ + shape-options.hh \ + text-options.hh \ + view-cairo.hh \ + view-options.hh \ + $(NULL) + +HB_SHAPE_sources = \ + batch.hh \ + face-options.hh \ + font-options.hh \ + hb-shape.cc \ + main-font-text.hh \ + options.hh \ + output-options.hh \ + shape-consumer.hh \ + shape-format.hh \ + shape-options.hh \ + shape-output.hh \ + text-options.hh \ + $(NULL) + +HB_SUBSET_CLI_sources = \ + batch.hh \ + face-options.hh \ + hb-subset.cc \ + main-font-text.hh \ + options.hh \ + output-options.hh \ + text-options.hh \ + $(NULL) + +HB_OT_SHAPE_CLOSURE_sources = \ + face-options.hh \ + font-options.hh \ + hb-ot-shape-closure.cc \ + main-font-text.hh \ + options.hh \ + text-options.hh \ + $(NULL) diff --git a/libs/harfbuzz/util/ansi-print.hh b/libs/harfbuzz/util/ansi-print.hh new file mode 100644 index 00000000..0715f492 --- /dev/null +++ b/libs/harfbuzz/util/ansi-print.hh @@ -0,0 +1,426 @@ +/* + * Copyright © 2012 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 ANSI_PRINT_HH +#define ANSI_PRINT_HH + +#include "hb.hh" + +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include /* for isatty() */ +#endif + +#if defined (_MSC_VER) && (_MSC_VER < 1800) +static inline long int +lround (double x) +{ + if (x >= 0) + return floor (x + 0.5); + else + return ceil (x - 0.5); +} +#endif + +#define ESC_E (char)27 + +#define CELL_W 8 +#define CELL_H (2 * CELL_W) + +struct color_diff_t +{ + int dot (const color_diff_t &o) + { return v[0]*o.v[0] + v[1]*o.v[1] + v[2]*o.v[2] + v[3]*o.v[3]; } + + int v[4]; +}; + +struct color_t +{ + static color_t from_ansi (unsigned int x) + { + color_t c = {(0xFFu<<24) | ((0xFFu*(x&1))<<16) | ((0xFFu*((x >> 1)&1))<<8) | (0xFFu*((x >> 2)&1))}; + return c; + } + unsigned int to_ansi () + { + return ((v >> 23) & 1) | ((v >> 14)&2) | ((v >> 5)&4); + } + + color_diff_t diff (const color_t &o) + { + color_diff_t d; + for (unsigned int i = 0; i < 4; i++) + d.v[i] = (int) ((v >> (i*8))&0xFF) - (int) ((o.v >> (i*8))&0xFF); + return d; + } + + uint32_t v; +}; + +struct image_t +{ + public: + + image_t (unsigned int width_, + unsigned int height_, + const uint32_t *data_, + unsigned int stride_) : + width (width_), + height (height_), + own_data (false), + data ((color_t *) data_), + stride (stride_) {} + image_t (unsigned int width_, + unsigned int height_) : + width (width_), + height (height_), + own_data (true), + data ((color_t *) malloc (sizeof (data[0]) * width * height)), + stride (width) {} + ~image_t () + { if (own_data) free (data); } + + color_t &operator () (unsigned int x, unsigned int y) + { return data[x + y * stride]; } + + color_t operator () (unsigned int x, unsigned int y) const + { return data[x + y * stride]; } + + void + copy_sub_image (const image_t &s, + unsigned int x, unsigned int y, + unsigned int w, unsigned int h) + { + assert (x < width); + assert (y < height); + for (unsigned int row = 0; row < h; row++) { + color_t *p = data + x + hb_min (y + row, height - 1) * stride; + color_t *q = s.data + row * s.stride; + if (x + w <= width) + for (unsigned int col = 0; col < w; col++) + *q++ = *p++; + else { + unsigned int limit = width - x; + for (unsigned int col = 0; col < limit; col++) + *q++ = *p++; + p--; + for (unsigned int col = limit; col < w; col++) + *q++ = *p; + } + } + } + + const unsigned int width; + const unsigned int height; + + private: + bool own_data; + color_t * const data; + const unsigned int stride; +}; + +struct biimage_t +{ + public: + + biimage_t (unsigned int width, unsigned int height) : + width (width), + height (height), + bg (0), fg (0), unicolor (true), + data ((uint8_t *) malloc (sizeof (data[0]) * width * height)) {} + ~biimage_t () + { free (data); } + + void set (const image_t &image) + { + assert (image.width == width); + assert (image.height == height); + int freq[8] = {0}; + for (unsigned int y = 0; y < height; y++) + for (unsigned int x = 0; x < width; x++) { + color_t c = image (x, y); + freq[c.to_ansi ()]++; + } + bg = 0; + for (unsigned int i = 1; i < 8; i++) + if (freq[bg] < freq[i]) + bg = i; + fg = 8; + for (unsigned int i = 0; i < 8; i++) + if (i != bg && (fg == 8 || freq[fg] < freq[i])) + fg = i; + if (freq[fg] == 0) { + fg = bg; + unicolor = true; + } + else + unicolor = false; + + /* Set the data... */ + + if (unicolor) { + memset (data, 0, sizeof (data[0]) * width * height); + return; + } + + color_t bgc = color_t::from_ansi (bg); + color_t fgc = color_t::from_ansi (fg); + color_diff_t diff = fgc.diff (bgc); + double dd = sqrt (diff.dot (diff)); + for (unsigned int y = 0; y < height; y++) + for (unsigned int x = 0; x < width; x++) { + double d = sqrt (diff.dot (image (x, y).diff (bgc))); + (*this)(x, y) = d <= 0 ? 0 : d >= dd ? 255 : lround (d / dd * 255.); + } + } + + uint8_t &operator () (unsigned int x, unsigned int y) + { return data[x + y * width]; } + + uint8_t operator () (unsigned int x, unsigned int y) const + { return data[x + y * width]; } + + const unsigned int width; + const unsigned int height; + unsigned int bg; + unsigned int fg; + bool unicolor; + + private: + uint8_t * const data; +}; + +static const char * +block_best (const biimage_t &bi, bool *inverse) +{ + assert (bi.width <= CELL_W); + assert (bi.height <= CELL_H); + + unsigned int score = UINT_MAX; + unsigned int row_sum[CELL_H] = {0}; + unsigned int col_sum[CELL_W] = {0}; + unsigned int row_sum_i[CELL_H] = {0}; + unsigned int col_sum_i[CELL_W] = {0}; + unsigned int quad[2][2] = {{0}}; + unsigned int quad_i[2][2] = {{0}}; + unsigned int total = 0; + unsigned int total_i = 0; + for (unsigned int y = 0; y < bi.height; y++) + for (unsigned int x = 0; x < bi.width; x++) { + unsigned int c = bi (x, y); + unsigned int c_i = 255 - c; + row_sum[y] += c; + row_sum_i[y] += c_i; + col_sum[x] += c; + col_sum_i[x] += c_i; + quad[2 * y / bi.height][2 * x / bi.width] += c; + quad_i[2 * y / bi.height][2 * x / bi.width] += c_i; + total += c; + total_i += c_i; + } + + /* Make the sums cumulative */ + for (unsigned int i = 1; i < bi.height; i++) { + row_sum[i] += row_sum[i - 1]; + row_sum_i[i] += row_sum_i[i - 1]; + } + for (unsigned int i = 1; i < bi.width; i++) { + col_sum[i] += col_sum[i - 1]; + col_sum_i[i] += col_sum_i[i - 1]; + } + + const char *best_c = " "; + + /* Maybe empty is better! */ + if (total < score) { + score = total; + *inverse = false; + best_c = " "; + } + /* Maybe full is better! */ + if (total_i < score) { + score = total_i; + *inverse = true; + best_c = " "; + } + + /* Find best lower line */ + if (1) { + unsigned int best_s = UINT_MAX; + bool best_inv = false; + int best_i = 0; + for (unsigned int i = 0; i < bi.height - 1; i++) + { + unsigned int s; + s = row_sum[i] + total_i - row_sum_i[i]; + if (s < best_s) { + best_s = s; + best_i = i; + best_inv = false; + } + s = row_sum_i[i] + total - row_sum[i]; + if (s < best_s) { + best_s = s; + best_i = i; + best_inv = true; + } + } + if (best_s < score) { + static const char *lower[7] = {"▁", "▂", "▃", "▄", "▅", "▆", "▇"}; + unsigned int which = lround ((double) ((best_i + 1) * 8) / bi.height); + if (1 <= which && which <= 7) { + score = best_s; + *inverse = best_inv; + best_c = lower[7 - which]; + } + } + } + + /* Find best left line */ + if (1) { + unsigned int best_s = UINT_MAX; + bool best_inv = false; + int best_i = 0; + for (unsigned int i = 0; i < bi.width - 1; i++) + { + unsigned int s; + s = col_sum[i] + total_i - col_sum_i[i]; + if (s < best_s) { + best_s = s; + best_i = i; + best_inv = true; + } + s = col_sum_i[i] + total - col_sum[i]; + if (s < best_s) { + best_s = s; + best_i = i; + best_inv = false; + } + } + if (best_s < score) { + static const char *left [7] = {"▏", "▎", "▍", "▌", "▋", "▊", "▉"}; + unsigned int which = lround ((double) ((best_i + 1) * 8) / bi.width); + if (1 <= which && which <= 7) { + score = best_s; + *inverse = best_inv; + best_c = left[which - 1]; + } + } + } + + /* Find best quadrant */ + if (1) { + unsigned int q = 0; + unsigned int qs = 0; + for (unsigned int i = 0; i < 2; i++) + for (unsigned int j = 0; j < 2; j++) + if (quad[i][j] > quad_i[i][j]) { + q += 1 << (2 * i + j); + qs += quad_i[i][j]; + } else + qs += quad[i][j]; + if (qs < score) { + const char *c = nullptr; + bool inv = false; + switch (q) { + case 1: c = "▟"; inv = true; break; + case 2: c = "▙"; inv = true; break; + case 4: c = "▖"; inv = false; break; + case 6: c = "▞"; inv = false; break; + case 7: c = "▛"; inv = false; break; + case 8: c = "▗"; inv = false; break; + case 9: c = "▚"; inv = false; break; + case 11: c = "▜"; inv = false; break; + case 13: c = "▙"; inv = false; break; + case 14: c = "▟"; inv = false; break; + } + if (c) { + score = qs; + *inverse = inv; + best_c = c; + } + } + } + + return best_c; +} + +static inline void +ansi_print_image_rgb24 (const uint32_t *data, + unsigned int width, + unsigned int height, + unsigned int stride) +{ + image_t image (width, height, data, stride); + + unsigned int rows = (height + CELL_H - 1) / CELL_H; + unsigned int cols = (width + CELL_W - 1) / CELL_W; + image_t cell (CELL_W, CELL_H); + biimage_t bi (CELL_W, CELL_H); + unsigned int last_bg = -1, last_fg = -1; + for (unsigned int row = 0; row < rows; row++) { + for (unsigned int col = 0; col < cols; col++) { + image.copy_sub_image (cell, col * CELL_W, row * CELL_H, CELL_W, CELL_H); + bi.set (cell); + if (bi.unicolor) { + if (last_bg != bi.bg) { + printf ("%c[%dm", ESC_E, 40 + bi.bg); + last_bg = bi.bg; + } + printf (" "); + } else { + /* Figure out the closest character to the biimage */ + bool inverse = false; + const char *c = block_best (bi, &inverse); + if (inverse) { + if (last_bg != bi.fg || last_fg != bi.bg) { + printf ("%c[%d;%dm", ESC_E, 30 + bi.bg, 40 + bi.fg); + last_bg = bi.fg; + last_fg = bi.bg; + } + } else { + if (last_bg != bi.bg || last_fg != bi.fg) { + printf ("%c[%d;%dm", ESC_E, 40 + bi.bg, 30 + bi.fg); + last_bg = bi.bg; + last_fg = bi.fg; + } + } + printf ("%s", c); + } + } + printf ("%c[0m\n", ESC_E); /* Reset */ + last_bg = last_fg = -1; + } +} + +#endif diff --git a/libs/harfbuzz/util/batch.hh b/libs/harfbuzz/util/batch.hh new file mode 100644 index 00000000..8c0217d8 --- /dev/null +++ b/libs/harfbuzz/util/batch.hh @@ -0,0 +1,75 @@ +/* + * Copyright © 2021 Behdad Esfahbod + * + * 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. + */ + +#ifndef BATCH_HH +#define BATCH_HH + +#include "options.hh" + +typedef int (*main_func_t) (int argc, char **argv); + +template +int +batch_main (int argc, char **argv) +{ + if (argc == 2 && !strcmp (argv[1], "--batch")) + { + int ret = 0; + char buf[4092]; + while (fgets (buf, sizeof (buf), stdin)) + { + size_t l = strlen (buf); + if (l && buf[l - 1] == '\n') buf[l - 1] = '\0'; + + char *args[64]; + argc = 0; + args[argc++] = argv[0]; + char *p = buf, *e; + args[argc++] = p; + while ((e = strchr (p, ';')) && argc < (int) ARRAY_LENGTH (args)) + { + *e++ = '\0'; + while (*e == ';') + e++; + args[argc++] = p = e; + } + + int result = main_t () (argc, args); + + if (report_status) + fprintf (stdout, result == 0 ? "success\n" : "failure\n"); + fflush (stdout); + + ret = MAX (ret, result); + } + return ret; + } + + int ret = main_t () (argc, argv); + if (report_status && ret != 0) + fprintf (stdout, "error: Operation failed. Probably a bug. File github issue.\n"); + return ret; +} + +#endif diff --git a/libs/harfbuzz/util/face-options.hh b/libs/harfbuzz/util/face-options.hh new file mode 100644 index 00000000..f680e05c --- /dev/null +++ b/libs/harfbuzz/util/face-options.hh @@ -0,0 +1,142 @@ +/* + * 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 FACE_OPTIONS_HH +#define FACE_OPTIONS_HH + +#include "options.hh" + +struct face_options_t +{ + ~face_options_t () + { + g_free (font_file); + } + + void set_face (hb_face_t *face_) + { face = face_; } + + void add_options (option_parser_t *parser); + + void post_parse (GError **error); + + static struct cache_t + { + ~cache_t () + { + g_free (font_path); + hb_blob_destroy (blob); + hb_face_destroy (face); + } + + char *font_path = nullptr; + hb_blob_t *blob = nullptr; + unsigned face_index = (unsigned) -1; + hb_face_t *face = nullptr; + } cache; + + char *font_file = nullptr; + unsigned face_index = 0; + + hb_blob_t *blob = nullptr; + hb_face_t *face = nullptr; +}; + + +face_options_t::cache_t face_options_t::cache {}; + +void +face_options_t::post_parse (GError **error) +{ + if (!font_file) + { + g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_FAILED, + "No font file set"); + return; + } + + assert (font_file); + + const char *font_path = font_file; + + if (0 == strcmp (font_path, "-")) + { +#if defined(_WIN32) || defined(__CYGWIN__) + setmode (fileno (stdin), O_BINARY); + font_path = "STDIN"; +#else + font_path = "/dev/stdin"; +#endif + } + + if (!cache.font_path || 0 != strcmp (cache.font_path, font_path)) + { + hb_blob_destroy (cache.blob); + cache.blob = hb_blob_create_from_file_or_fail (font_path); + + free ((char *) cache.font_path); + cache.font_path = g_strdup (font_path); + + if (!cache.blob) + { + g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_FAILED, + "%s: Failed reading file", font_path); + return; + } + + hb_face_destroy (cache.face); + cache.face = nullptr; + cache.face_index = (unsigned) -1; + } + + if (cache.face_index != face_index) + { + hb_face_destroy (cache.face); + cache.face = hb_face_create (cache.blob, face_index); + cache.face_index = face_index; + } + + blob = cache.blob; + face = cache.face; +} + +void +face_options_t::add_options (option_parser_t *parser) +{ + GOptionEntry entries[] = + { + {"font-file", 0, 0, G_OPTION_ARG_STRING, &this->font_file, "Set font file-name", "filename"}, + {"face-index", 0, 0, G_OPTION_ARG_INT, &this->face_index, "Set face index (default: 0)", "index"}, + {nullptr} + }; + parser->add_group (entries, + "face", + "Font-face options:", + "Options for the font face", + this); +} + +#endif diff --git a/libs/harfbuzz/util/font-options.hh b/libs/harfbuzz/util/font-options.hh new file mode 100644 index 00000000..331efd7d --- /dev/null +++ b/libs/harfbuzz/util/font-options.hh @@ -0,0 +1,329 @@ +/* + * 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 FONT_OPTIONS_HH +#define FONT_OPTIONS_HH + +#include "face-options.hh" + +#ifdef HAVE_FREETYPE +#include +#endif +#include + +#define FONT_SIZE_UPEM 0x7FFFFFFF +#define FONT_SIZE_NONE 0 + +extern const unsigned DEFAULT_FONT_SIZE; +extern const unsigned SUBPIXEL_BITS; + +struct font_options_t : face_options_t +{ + ~font_options_t () + { +#ifndef HB_NO_VAR + free (variations); +#endif + g_free (font_funcs); + hb_font_destroy (font); + } + + void add_options (option_parser_t *parser); + + void post_parse (GError **error); + + hb_bool_t sub_font = false; +#ifndef HB_NO_VAR + hb_variation_t *variations = nullptr; + unsigned int num_variations = 0; +#endif + int x_ppem = 0; + int y_ppem = 0; + double ptem = 0.; + double slant = 0.; + unsigned int subpixel_bits = SUBPIXEL_BITS; + mutable double font_size_x = DEFAULT_FONT_SIZE; + mutable double font_size_y = DEFAULT_FONT_SIZE; + char *font_funcs = nullptr; + int ft_load_flags = 2; + + hb_font_t *font = nullptr; +}; + + +static struct supported_font_funcs_t { + char name[4]; + void (*func) (hb_font_t *); +} supported_font_funcs[] = +{ + {"ot", hb_ot_font_set_funcs}, +#ifdef HAVE_FREETYPE + {"ft", hb_ft_font_set_funcs}, +#endif +}; + + +void +font_options_t::post_parse (GError **error) +{ + assert (!font); + font = hb_font_create (face); + + if (font_size_x == FONT_SIZE_UPEM) + font_size_x = hb_face_get_upem (face); + if (font_size_y == FONT_SIZE_UPEM) + font_size_y = hb_face_get_upem (face); + + hb_font_set_ppem (font, x_ppem, y_ppem); + hb_font_set_ptem (font, ptem); + + hb_font_set_synthetic_slant (font, slant); + + int scale_x = (int) scalbnf (font_size_x, subpixel_bits); + int scale_y = (int) scalbnf (font_size_y, subpixel_bits); + hb_font_set_scale (font, scale_x, scale_y); + +#ifndef HB_NO_VAR + hb_font_set_variations (font, variations, num_variations); +#endif + + void (*set_font_funcs) (hb_font_t *) = nullptr; + if (!font_funcs) + { + set_font_funcs = supported_font_funcs[0].func; + } + else + { + for (unsigned int i = 0; i < ARRAY_LENGTH (supported_font_funcs); i++) + if (0 == g_ascii_strcasecmp (font_funcs, supported_font_funcs[i].name)) + { + set_font_funcs = supported_font_funcs[i].func; + break; + } + if (!set_font_funcs) + { + GString *s = g_string_new (nullptr); + for (unsigned int i = 0; i < ARRAY_LENGTH (supported_font_funcs); i++) + { + if (i) + g_string_append_c (s, '/'); + g_string_append (s, supported_font_funcs[i].name); + } + g_string_append_c (s, '\n'); + char *p = g_string_free (s, FALSE); + g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, + "Unknown font function implementation `%s'; supported values are: %s; default is %s", + font_funcs, + p, + supported_font_funcs[0].name); + free (p); + return; + } + } + set_font_funcs (font); +#ifdef HAVE_FREETYPE + hb_ft_font_set_load_flags (font, ft_load_flags); +#endif + + if (sub_font) + { + hb_font_t *old_font = font; + font = hb_font_create_sub_font (old_font); + hb_font_set_scale (old_font, scale_x * 2, scale_y * 2); + hb_font_destroy (old_font); + } +} + + +#ifndef HB_NO_VAR +static gboolean +parse_variations (const char *name G_GNUC_UNUSED, + const char *arg, + gpointer data, + GError **error G_GNUC_UNUSED) +{ + font_options_t *font_opts = (font_options_t *) data; + char *s = (char *) arg; + char *p; + + font_opts->num_variations = 0; + g_free (font_opts->variations); + font_opts->variations = nullptr; + + if (!*s) + return true; + + /* count the variations first, so we can allocate memory */ + p = s; + do { + font_opts->num_variations++; + p = strpbrk (p, ", "); + if (p) + p++; + } while (p); + + font_opts->variations = (hb_variation_t *) calloc (font_opts->num_variations, sizeof (*font_opts->variations)); + if (!font_opts->variations) + return false; + + /* now do the actual parsing */ + p = s; + font_opts->num_variations = 0; + while (p && *p) { + char *end = strpbrk (p, ", "); + if (hb_variation_from_string (p, end ? end - p : -1, &font_opts->variations[font_opts->num_variations])) + font_opts->num_variations++; + p = end ? end + 1 : nullptr; + } + + return true; +} +#endif + +static gboolean +parse_font_size (const char *name G_GNUC_UNUSED, + const char *arg, + gpointer data, + GError **error G_GNUC_UNUSED) +{ + font_options_t *font_opts = (font_options_t *) data; + if (0 == strcmp (arg, "upem")) + { + font_opts->font_size_y = font_opts->font_size_x = FONT_SIZE_UPEM; + return true; + } + switch (sscanf (arg, "%lf%*[ ,]%lf", &font_opts->font_size_x, &font_opts->font_size_y)) { + case 1: font_opts->font_size_y = font_opts->font_size_x; HB_FALLTHROUGH; + case 2: return true; + default: + g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, + "%s argument should be one or two space-separated numbers", + name); + return false; + } +} + +static gboolean +parse_font_ppem (const char *name G_GNUC_UNUSED, + const char *arg, + gpointer data, + GError **error G_GNUC_UNUSED) +{ + font_options_t *font_opts = (font_options_t *) data; + switch (sscanf (arg, "%d%*[ ,]%d", &font_opts->x_ppem, &font_opts->y_ppem)) { + case 1: font_opts->y_ppem = font_opts->x_ppem; HB_FALLTHROUGH; + case 2: return true; + default: + g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, + "%s argument should be one or two space-separated numbers", + name); + return false; + } +} + +void +font_options_t::add_options (option_parser_t *parser) +{ + face_options_t::add_options (parser); + + char *text = nullptr; + + { + static_assert ((ARRAY_LENGTH_CONST (supported_font_funcs) > 0), + "No supported font-funcs found."); + GString *s = g_string_new (nullptr); + g_string_printf (s, "Set font functions implementation to use (default: %s)\n\n Supported font function implementations are: %s", + supported_font_funcs[0].name, + supported_font_funcs[0].name); + for (unsigned int i = 1; i < ARRAY_LENGTH (supported_font_funcs); i++) + { + g_string_append_c (s, '/'); + g_string_append (s, supported_font_funcs[i].name); + } + text = g_string_free (s, FALSE); + parser->free_later (text); + } + + char *font_size_text; + if (DEFAULT_FONT_SIZE == FONT_SIZE_UPEM) + font_size_text = (char *) "Font size (default: upem)"; + else + { + font_size_text = g_strdup_printf ("Font size (default: %d)", DEFAULT_FONT_SIZE); + parser->free_later (font_size_text); + } + + int font_size_flags = DEFAULT_FONT_SIZE == FONT_SIZE_NONE ? G_OPTION_FLAG_HIDDEN : 0; + GOptionEntry entries[] = + { + {"font-size", 0, font_size_flags, + G_OPTION_ARG_CALLBACK, (gpointer) &parse_font_size, font_size_text, "1/2 integers or 'upem'"}, + {"font-ppem", 0, font_size_flags, + G_OPTION_ARG_CALLBACK, (gpointer) &parse_font_ppem, "Set x,y pixels per EM (default: 0; disabled)", "1/2 integers"}, + {"font-ptem", 0, 0, + G_OPTION_ARG_DOUBLE, &this->ptem, "Set font point-size (default: 0; disabled)", "point-size"}, + {"font-slant", 0, 0, + G_OPTION_ARG_DOUBLE, &this->slant, "Set synthetic slant (default: 0)", "slant ratio; eg. 0.2"}, + {"font-funcs", 0, 0, G_OPTION_ARG_STRING, &this->font_funcs, text, "impl"}, + {"sub-font", 0, G_OPTION_FLAG_HIDDEN, + G_OPTION_ARG_NONE, &this->sub_font, "Create a sub-font (default: false)", "boolean"}, + {"ft-load-flags", 0, 0, G_OPTION_ARG_INT, &this->ft_load_flags, "Set FreeType load-flags (default: 2)", "integer"}, + {nullptr} + }; + parser->add_group (entries, + "font", + "Font-instance options:", + "Options for the font instance", + this, + false /* We add below. */); + +#ifndef HB_NO_VAR + const gchar *variations_help = "Comma-separated list of font variations\n" + "\n" + " Variations are set globally. The format for specifying variation settings\n" + " follows. All valid CSS font-variation-settings values other than 'normal'\n" + " and 'inherited' are also accepted, though, not documented below.\n" + "\n" + " The format is a tag, optionally followed by an equals sign, followed by a\n" + " number. For example:\n" + "\n" + " \"wght=500\"\n" + " \"slnt=-7.5\""; + + GOptionEntry entries2[] = + { + {"variations", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_variations, variations_help, "list"}, + {nullptr} + }; + parser->add_group (entries2, + "variations", + "Variations options:", + "Options for font variations used", + this); +#endif +} + +#endif diff --git a/libs/harfbuzz/util/hb-fc-list.c b/libs/harfbuzz/util/hb-fc-list.c new file mode 100644 index 00000000..40c6b32b --- /dev/null +++ b/libs/harfbuzz/util/hb-fc-list.c @@ -0,0 +1,222 @@ +/* + * Copyright © 2002 Keith Packard + * Copyright © 2014 Google, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of the author(s) not be used in + * advertising or publicity pertaining to distribution of the software without + * specific, written prior permission. The authors make no + * representations about the suitability of this software for any purpose. It + * is provided "as is" without express or implied warranty. + * + * THE AUTHOR(S) DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + * + * Google Author(s): Behdad Esfahbod + */ + +#define HAVE_GETOPT_LONG 1 /* XXX */ + +#include "hb-fc.h" + +#include +#include +#include +#include +#include +#ifdef HAVE_CONFIG_H +#include +#else +#ifdef linux +#define HAVE_GETOPT_LONG 1 +#endif +#define HAVE_GETOPT 1 +#endif + +#ifndef HAVE_GETOPT +#define HAVE_GETOPT 0 +#endif +#ifndef HAVE_GETOPT_LONG +#define HAVE_GETOPT_LONG 0 +#endif + +#if HAVE_GETOPT_LONG +#undef _GNU_SOURCE +#define _GNU_SOURCE +#include +const struct option longopts[] = { + {"verbose", 0, 0, 'v'}, + {"format", 1, 0, 'f'}, + {"quiet", 0, 0, 'q'}, + {"version", 0, 0, 'V'}, + {"help", 0, 0, 'h'}, + {NULL,0,0,0}, +}; +#else +#if HAVE_GETOPT +extern char *optarg; +extern int optind, opterr, optopt; +#endif +#endif + +static void +usage (char *program, int error) +{ + FILE *file = error ? stderr : stdout; +#if HAVE_GETOPT_LONG + fprintf (file, "usage: %s [-vqVh] [-f FORMAT] [--verbose] [--format=FORMAT] [--quiet] [--version] [--help] text [pattern] {element ...} \n", + program); +#else + fprintf (file, "usage: %s [-vqVh] [-f FORMAT] text [pattern] {element ...} \n", + program); +#endif + fprintf (file, "List fonts matching [pattern] that can render [text]\n"); + fprintf (file, "\n"); +#if HAVE_GETOPT_LONG + fprintf (file, " -v, --verbose display entire font pattern verbosely\n"); + fprintf (file, " -f, --format=FORMAT use the given output format\n"); + fprintf (file, " -q, --quiet suppress all normal output, exit 1 if no fonts matched\n"); + fprintf (file, " -V, --version display font config version and exit\n"); + fprintf (file, " -h, --help display this help and exit\n"); +#else + fprintf (file, " -v (verbose) display entire font pattern verbosely\n"); + fprintf (file, " -f FORMAT (format) use the given output format\n"); + fprintf (file, " -q, (quiet) suppress all normal output, exit 1 if no fonts matched\n"); + fprintf (file, " -V (version) display HarfBuzz version and exit\n"); + fprintf (file, " -h (help) display this help and exit\n"); +#endif + exit (error); +} + +int +main (int argc, char **argv) +{ + int verbose = 0; + int quiet = 0; + const FcChar8 *format = NULL; + int nfont = 0; + int i; + FcObjectSet *os = 0; + FcFontSet *fs; + FcPattern *pat; + const char *text; +#if HAVE_GETOPT_LONG || HAVE_GETOPT + int c; + +#if HAVE_GETOPT_LONG + while ((c = getopt_long (argc, argv, "vf:qVh", longopts, NULL)) != -1) +#else + while ((c = getopt (argc, argv, "vf:qVh")) != -1) +#endif + { + switch (c) { + case 'v': + verbose = 1; + break; + case 'f': + format = (FcChar8 *) strdup (optarg); + break; + case 'q': + quiet = 1; + break; + case 'V': + fprintf (stderr, "fontconfig version %d.%d.%d\n", + FC_MAJOR, FC_MINOR, FC_REVISION); + exit (0); + case 'h': + usage (argv[0], 0); + default: + usage (argv[0], 1); + } + } + i = optind; +#else + i = 1; +#endif + + if (!argv[i]) + usage (argv[0], 1); + + text = argv[i]; + i++; + + if (argv[i]) + { + pat = FcNameParse ((FcChar8 *) argv[i]); + if (!pat) + { + fputs ("Unable to parse the pattern\n", stderr); + return 1; + } + while (argv[++i]) + { + if (!os) + os = FcObjectSetCreate (); + FcObjectSetAdd (os, argv[i]); + } + } + else + pat = FcPatternCreate (); + if (quiet && !os) + os = FcObjectSetCreate (); + if (!verbose && !format && !os) + os = FcObjectSetBuild (FC_FAMILY, FC_STYLE, FC_FILE, (char *) 0); + FcObjectSetAdd (os, FC_CHARSET); + if (!format) + format = (const FcChar8 *) "%{=fclist}\n"; + fs = FcFontList (0, pat, os); + if (os) + FcObjectSetDestroy (os); + if (pat) + FcPatternDestroy (pat); + + if (!quiet && fs) + { + int j; + + for (j = 0; j < fs->nfont; j++) + { + hb_font_t *font = hb_fc_font_create (fs->fonts[j]); + hb_bool_t can_render = hb_fc_can_render (font, text); + hb_font_destroy (font); + + if (!can_render) + continue; + + FcPatternDel (fs->fonts[j], FC_CHARSET); + + if (verbose) + { + FcPatternPrint (fs->fonts[j]); + } + else + { + FcChar8 *s; + + s = FcPatternFormat (fs->fonts[j], format); + if (s) + { + printf ("%s", s); + FcStrFree (s); + } + } + } + } + + if (fs) { + nfont = fs->nfont; + FcFontSetDestroy (fs); + } + + FcFini (); + + return quiet ? (nfont == 0 ? 1 : 0) : 0; +} diff --git a/libs/harfbuzz/util/hb-fc.cc b/libs/harfbuzz/util/hb-fc.cc new file mode 100644 index 00000000..90423468 --- /dev/null +++ b/libs/harfbuzz/util/hb-fc.cc @@ -0,0 +1,149 @@ +/* + * Copyright © 2014 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 + */ + +#include +#include + +#include "hb-fc.h" + +static hb_bool_t +hb_fc_get_glyph (hb_font_t *font /*HB_UNUSED*/, + void *font_data, + hb_codepoint_t unicode, + hb_codepoint_t variation_selector, + hb_codepoint_t *glyph, + void *user_data /*HB_UNUSED*/) + +{ + FcCharSet *cs = (FcCharSet *) font_data; + + if (variation_selector) + { + /* Fontconfig doesn't cache cmap-14 info. However: + * 1. If the font maps the variation_selector, assume it's + * supported, + * 2. If the font doesn't map it, still say it's supported, + * but return 0. This way, the caller will see the zero + * and reject. If we return unsupported here, then the + * variation selector will be hidden and ignored. + */ + if (FcCharSetHasChar (cs, unicode) && + FcCharSetHasChar (cs, variation_selector)) + { + unsigned int var_num = 0; + if (variation_selector - 0xFE00u < 16) + var_num = variation_selector - 0xFE00 + 1; + else if (variation_selector - 0xE0100u < (256 - 16)) + var_num = variation_selector - 0xE0100 + 17; + *glyph = (var_num << 21) | unicode; + } + else + { + *glyph = 0; + } + return true; + } + + *glyph = FcCharSetHasChar (cs, unicode) ? unicode : 0; + return *glyph != 0; +} + +static hb_font_funcs_t * +_hb_fc_get_font_funcs () +{ + static const hb_font_funcs_t *fc_ffuncs; + + const hb_font_funcs_t *ffuncs; + + if (!(ffuncs = fc_ffuncs)) + { + hb_font_funcs_t *newfuncs = hb_font_funcs_create (); + + hb_font_funcs_set_glyph_func (newfuncs, hb_fc_get_glyph, nullptr, nullptr); + + /* XXX MT-unsafe */ + if (fc_ffuncs) + hb_font_funcs_destroy (newfuncs); + else + fc_ffuncs = ffuncs = newfuncs; + } + + return const_cast (fc_ffuncs); +} + + +hb_font_t * +hb_fc_font_create (FcPattern *fcfont) +{ + static hb_face_t *face; + hb_font_t *font; + + FcCharSet *cs; + if (FcResultMatch != FcPatternGetCharSet (fcfont, FC_CHARSET, 0, &cs)) + return hb_font_get_empty (); + + if (!face) /* XXX MT-unsafe */ + face = hb_face_create (hb_blob_get_empty (), 0); + + font = hb_font_create (face); + + hb_font_set_funcs (font, + _hb_fc_get_font_funcs (), + FcCharSetCopy (cs), + (hb_destroy_func_t) FcCharSetDestroy); + + return font; +} + +hb_bool_t +hb_fc_can_render (hb_font_t *font, const char *text) +{ + static const char *ot[] = {"ot", nullptr}; + + hb_buffer_t *buffer = hb_buffer_create (); + hb_buffer_add_utf8 (buffer, text, -1, 0, -1); + + /* XXX Do we need this? I think Arabic and Hangul shapers are the + * only one that make any use of this. The Hangul case is not really + * needed, and for Arabic we'll miss a very narrow set of fonts. + * Might be better to force generic shaper perhaps. */ + hb_buffer_guess_segment_properties (buffer); + + if (!hb_shape_full (font, buffer, nullptr, 0, ot)) + abort (); /* hb-ot shaper not enabled? */ + + unsigned int len; + hb_glyph_info_t *info = hb_buffer_get_glyph_infos (buffer, &len); + for (unsigned int i = 0; i < len; i++) + { + if (!info[i].codepoint) + { + return false; + } + } + + return true; +} diff --git a/libs/harfbuzz/util/hb-fc.h b/libs/harfbuzz/util/hb-fc.h new file mode 100644 index 00000000..bb2f78a8 --- /dev/null +++ b/libs/harfbuzz/util/hb-fc.h @@ -0,0 +1,46 @@ +/* + * Copyright © 2014 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 HB_FC_H +#define HB_FC_H + +#include "hb.h" + +#include + +HB_BEGIN_DECLS + + +hb_font_t * +hb_fc_font_create (FcPattern *font); + +hb_bool_t +hb_fc_can_render (hb_font_t *font, const char *text); + + +HB_END_DECLS + +#endif /* HB_FC_H */ diff --git a/libs/harfbuzz/util/hb-ot-shape-closure.cc b/libs/harfbuzz/util/hb-ot-shape-closure.cc new file mode 100644 index 00000000..3b6751c3 --- /dev/null +++ b/libs/harfbuzz/util/hb-ot-shape-closure.cc @@ -0,0 +1,122 @@ +/* + * Copyright © 2012 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 + */ + +#include "batch.hh" +#include "font-options.hh" +#include "main-font-text.hh" +#include "shape-options.hh" +#include "text-options.hh" + +const unsigned DEFAULT_FONT_SIZE = FONT_SIZE_NONE; +const unsigned SUBPIXEL_BITS = 0; + +struct shape_closure_consumer_t +{ + void add_options (struct option_parser_t *parser) + { + parser->set_summary ("Find glyph set from input text under shaping closure."); + shaper.add_options (parser); + + GOptionEntry entries[] = + { + {"no-glyph-names", 0, G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE, &this->show_glyph_names, "Use glyph indices instead of names", nullptr}, + {nullptr} + }; + parser->add_group (entries, + "format", + "Format options:", + "Options controlling output formatting", + this); + } + + void init (const font_options_t *font_opts) + { + glyphs = hb_set_create (); + font = hb_font_reference (font_opts->font); + failed = false; + buffer = hb_buffer_create (); + } + template + bool consume_line (text_options_type &text_opts) + { + unsigned int text_len; + const char *text; + if (!(text = text_opts.get_line (&text_len))) + return false; + + hb_set_clear (glyphs); + shaper.shape_closure (text, text_len, font, buffer, glyphs); + + if (hb_set_is_empty (glyphs)) + return true; + + /* Print it out! */ + bool first = true; + for (hb_codepoint_t i = -1; hb_set_next (glyphs, &i);) + { + if (first) + first = false; + else + printf (" "); + if (show_glyph_names) + { + char glyph_name[64]; + hb_font_glyph_to_string (font, i, glyph_name, sizeof (glyph_name)); + printf ("%s", glyph_name); + } else + printf ("%u", i); + } + + return true; + } + void finish (const font_options_t *font_opts) + { + printf ("\n"); + hb_font_destroy (font); + font = nullptr; + hb_set_destroy (glyphs); + glyphs = nullptr; + hb_buffer_destroy (buffer); + buffer = nullptr; + } + + bool failed; + + protected: + shape_options_t shaper; + hb_bool_t show_glyph_names = true; + + hb_set_t *glyphs = nullptr; + hb_font_t *font = nullptr; + hb_buffer_t *buffer = nullptr; +}; + +int +main (int argc, char **argv) +{ + using main_t = main_font_text_t; + return batch_main (argc, argv); +} diff --git a/libs/harfbuzz/util/hb-shape.cc b/libs/harfbuzz/util/hb-shape.cc new file mode 100644 index 00000000..9eec5636 --- /dev/null +++ b/libs/harfbuzz/util/hb-shape.cc @@ -0,0 +1,43 @@ +/* + * Copyright © 2010 Behdad Esfahbod + * Copyright © 2011,2012 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 + */ + +#include "batch.hh" +#include "font-options.hh" +#include "main-font-text.hh" +#include "shape-consumer.hh" +#include "shape-output.hh" +#include "text-options.hh" + +const unsigned DEFAULT_FONT_SIZE = FONT_SIZE_UPEM; +const unsigned SUBPIXEL_BITS = 0; + +int +main (int argc, char **argv) +{ + using main_t = main_font_text_t, font_options_t, shape_text_options_t>; + return batch_main (argc, argv); +} diff --git a/libs/harfbuzz/util/hb-subset.cc b/libs/harfbuzz/util/hb-subset.cc new file mode 100644 index 00000000..f2606c76 --- /dev/null +++ b/libs/harfbuzz/util/hb-subset.cc @@ -0,0 +1,973 @@ +/* + * Copyright © 2010 Behdad Esfahbod + * Copyright © 2011,2012 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): Garret Rieger, Rod Sheeter + */ + +#include "batch.hh" +#include "face-options.hh" +#include "main-font-text.hh" +#include "output-options.hh" + +#include + +static hb_face_t* preprocess_face(hb_face_t* face) +{ + #ifdef HB_EXPERIMENTAL_API + return hb_subset_preprocess (face); + #else + return hb_face_reference(face); + #endif +} + +/* + * Command line interface to the harfbuzz font subsetter. + */ + +struct subset_main_t : option_parser_t, face_options_t, output_options_t +{ + subset_main_t () + : input (hb_subset_input_create_or_fail ()) + {} + ~subset_main_t () + { + hb_subset_input_destroy (input); + } + + void parse_face (int argc, const char * const *argv) + { + option_parser_t parser; + face_options_t face_opts; + + face_opts.add_options (&parser); + + GOptionEntry entries[] = + { + {G_OPTION_REMAINING, 0, G_OPTION_FLAG_IN_MAIN, + G_OPTION_ARG_CALLBACK, (gpointer) &collect_face, nullptr, "[FONT-FILE] [TEXT]"}, + {nullptr} + }; + parser.add_main_group (entries, &face_opts); + parser.add_options (); + + g_option_context_set_ignore_unknown_options (parser.context, true); + g_option_context_set_help_enabled (parser.context, false); + + char **args = (char **) +#if GLIB_CHECK_VERSION (2, 68, 0) + g_memdup2 +#else + g_memdup +#endif + (argv, argc * sizeof (*argv)); + parser.parse (&argc, &args); + g_free (args); + + set_face (face_opts.face); + } + + void parse (int argc, char **argv) + { + bool help = false; + for (auto i = 1; i < argc; i++) + if (!strncmp ("--help", argv[i], 6)) + { + help = true; + break; + } + + if (likely (!help)) + { + /* Do a preliminary parse to load font-face, such that we can use it + * during main option parsing. */ + parse_face (argc, argv); + } + + add_options (); + option_parser_t::parse (&argc, &argv); + } + + int operator () (int argc, char **argv) + { + parse (argc, argv); + + hb_face_t* orig_face = face; + if (preprocess) + orig_face = preprocess_face (face); + + hb_face_t *new_face = nullptr; + for (unsigned i = 0; i < num_iterations; i++) + { + hb_face_destroy (new_face); + new_face = hb_subset_or_fail (face, input); + } + + bool success = new_face; + if (success) + { + hb_blob_t *result = hb_face_reference_blob (new_face); + write_file (output_file, result); + hb_blob_destroy (result); + } + + hb_face_destroy (new_face); + if (preprocess) + hb_face_destroy (orig_face); + + return success ? 0 : 1; + } + + bool + write_file (const char *output_file, hb_blob_t *blob) + { + assert (out_fp); + + unsigned int size; + const char* data = hb_blob_get_data (blob, &size); + + while (size) + { + size_t ret = fwrite (data, 1, size, out_fp); + size -= ret; + data += ret; + if (size && ferror (out_fp)) + fail (false, "Failed to write output: %s", strerror (errno)); + } + + return true; + } + + void add_options (); + + protected: + static gboolean + collect_face (const char *name, + const char *arg, + gpointer data, + GError **error); + static gboolean + collect_rest (const char *name, + const char *arg, + gpointer data, + GError **error); + + public: + + unsigned num_iterations = 1; + gboolean preprocess; + hb_subset_input_t *input = nullptr; +}; + +static gboolean +parse_gids (const char *name G_GNUC_UNUSED, + const char *arg, + gpointer data, + GError **error) +{ + subset_main_t *subset_main = (subset_main_t *) data; + hb_bool_t is_remove = (name[strlen (name) - 1] == '-'); + hb_bool_t is_add = (name[strlen (name) - 1] == '+'); + hb_set_t *gids = hb_subset_input_glyph_set (subset_main->input); + + if (!is_remove && !is_add) hb_set_clear (gids); + + if (0 == strcmp (arg, "*")) + { + hb_set_clear (gids); + if (!is_remove) + hb_set_invert (gids); + return true; + } + + char *s = (char *) arg; + char *p; + + while (s && *s) + { + while (*s && strchr (", ", *s)) + s++; + if (!*s) + break; + + errno = 0; + hb_codepoint_t start_code = strtoul (s, &p, 10); + if (s[0] == '-' || errno || s == p) + { + g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, + "Failed parsing glyph-index at: '%s'", s); + return false; + } + + if (p && p[0] == '-') // ranges + { + s = ++p; + hb_codepoint_t end_code = strtoul (s, &p, 10); + if (s[0] == '-' || errno || s == p) + { + g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, + "Failed parsing glyph-index at: '%s'", s); + return false; + } + + if (end_code < start_code) + { + g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, + "Invalid glyph-index range %u-%u", start_code, end_code); + return false; + } + if (!is_remove) + hb_set_add_range (gids, start_code, end_code); + else + hb_set_del_range (gids, start_code, end_code); + } + else + { + if (!is_remove) + hb_set_add (gids, start_code); + else + hb_set_del (gids, start_code); + } + + s = p; + } + + return true; +} + +static gboolean +parse_glyphs (const char *name G_GNUC_UNUSED, + const char *arg, + gpointer data, + GError **error G_GNUC_UNUSED) +{ + subset_main_t *subset_main = (subset_main_t *) data; + hb_bool_t is_remove = (name[strlen (name) - 1] == '-'); + hb_bool_t is_add = (name[strlen (name) - 1] == '+'); + hb_set_t *gids = hb_subset_input_glyph_set (subset_main->input); + + if (!is_remove && !is_add) hb_set_clear (gids); + + if (0 == strcmp (arg, "*")) + { + hb_set_clear (gids); + if (!is_remove) + hb_set_invert (gids); + return true; + } + + const char *p = arg; + const char *p_end = arg + strlen (arg); + + hb_font_t *font = hb_font_create (subset_main->face); + while (p < p_end) + { + while (p < p_end && (*p == ' ' || *p == ',')) + p++; + + const char *end = p; + while (end < p_end && *end != ' ' && *end != ',') + end++; + + if (p < end) + { + hb_codepoint_t gid; + if (!hb_font_get_glyph_from_name (font, p, end - p, &gid)) + { + g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, + "Failed parsing glyph name: '%s'", p); + return false; + } + + if (!is_remove) + hb_set_add (gids, gid); + else + hb_set_del (gids, gid); + } + + p = end + 1; + } + hb_font_destroy (font); + + return true; +} + +static gboolean +parse_text (const char *name G_GNUC_UNUSED, + const char *arg, + gpointer data, + GError **error G_GNUC_UNUSED) +{ + subset_main_t *subset_main = (subset_main_t *) data; + hb_bool_t is_remove = (name[strlen (name) - 1] == '-'); + hb_bool_t is_add = (name[strlen (name) - 1] == '+'); + hb_set_t *unicodes = hb_subset_input_unicode_set (subset_main->input); + + if (!is_remove && !is_add) hb_set_clear (unicodes); + + if (0 == strcmp (arg, "*")) + { + hb_set_clear (unicodes); + if (!is_remove) + hb_set_invert (unicodes); + return true; + } + + for (gchar *c = (gchar *) arg; + *c; + c = g_utf8_find_next_char(c, nullptr)) + { + gunichar cp = g_utf8_get_char(c); + if (!is_remove) + hb_set_add (unicodes, cp); + else + hb_set_del (unicodes, cp); + } + return true; +} + +static gboolean +parse_unicodes (const char *name G_GNUC_UNUSED, + const char *arg, + gpointer data, + GError **error) +{ + subset_main_t *subset_main = (subset_main_t *) data; + hb_bool_t is_remove = (name[strlen (name) - 1] == '-'); + hb_bool_t is_add = (name[strlen (name) - 1] == '+'); + hb_set_t *unicodes = hb_subset_input_unicode_set (subset_main->input); + + if (!is_remove && !is_add) hb_set_clear (unicodes); + + if (0 == strcmp (arg, "*")) + { + hb_set_clear (unicodes); + if (!is_remove) + hb_set_invert (unicodes); + return true; + } + + // XXX TODO Ranges +#define DELIMITERS "<+->{},;&#\\xXuUnNiI\n\t\v\f\r " + + char *s = (char *) arg; + char *p; + + while (s && *s) + { + while (*s && strchr (DELIMITERS, *s)) + s++; + if (!*s) + break; + + errno = 0; + hb_codepoint_t start_code = strtoul (s, &p, 16); + if (errno || s == p) + { + g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, + "Failed parsing Unicode at: '%s'", s); + return false; + } + + if (p && p[0] == '-') // ranges + { + s = ++p; + hb_codepoint_t end_code = strtoul (s, &p, 16); + if (s[0] == '-' || errno || s == p) + { + g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, + "Failed parsing Unicode at: '%s'", s); + return false; + } + + if (end_code < start_code) + { + g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, + "Invalid Unicode range %u-%u", start_code, end_code); + return false; + } + if (!is_remove) + hb_set_add_range (unicodes, start_code, end_code); + else + hb_set_del_range (unicodes, start_code, end_code); + } + else + { + if (!is_remove) + hb_set_add (unicodes, start_code); + else + hb_set_del (unicodes, start_code); + } + + s = p; + } + + return true; +} + +static gboolean +parse_nameids (const char *name, + const char *arg, + gpointer data, + GError **error) +{ + subset_main_t *subset_main = (subset_main_t *) data; + hb_bool_t is_remove = (name[strlen (name) - 1] == '-'); + hb_bool_t is_add = (name[strlen (name) - 1] == '+'); + hb_set_t *name_ids = hb_subset_input_set (subset_main->input, HB_SUBSET_SETS_NAME_ID); + + + if (!is_remove && !is_add) hb_set_clear (name_ids); + + if (0 == strcmp (arg, "*")) + { + hb_set_clear (name_ids); + if (!is_remove) + hb_set_invert (name_ids); + return true; + } + + char *s = (char *) arg; + char *p; + + while (s && *s) + { + while (*s && strchr (", ", *s)) + s++; + if (!*s) + break; + + errno = 0; + hb_codepoint_t u = strtoul (s, &p, 10); + if (errno || s == p) + { + g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, + "Failed parsing nameID at: '%s'", s); + return false; + } + + if (!is_remove) + { + hb_set_add (name_ids, u); + } else { + hb_set_del (name_ids, u); + } + + s = p; + } + + return true; +} + +static gboolean +parse_name_languages (const char *name, + const char *arg, + gpointer data, + GError **error) +{ + subset_main_t *subset_main = (subset_main_t *) data; + hb_bool_t is_remove = (name[strlen (name) - 1] == '-'); + hb_bool_t is_add = (name[strlen (name) - 1] == '+'); + hb_set_t *name_languages = hb_subset_input_set (subset_main->input, HB_SUBSET_SETS_NAME_LANG_ID); + + if (!is_remove && !is_add) hb_set_clear (name_languages); + + if (0 == strcmp (arg, "*")) + { + hb_set_clear (name_languages); + if (!is_remove) + hb_set_invert (name_languages); + return true; + } + + char *s = (char *) arg; + char *p; + + while (s && *s) + { + while (*s && strchr (", ", *s)) + s++; + if (!*s) + break; + + errno = 0; + hb_codepoint_t u = strtoul (s, &p, 10); + if (errno || s == p) + { + g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, + "Failed parsing name-language code at: '%s'", s); + return false; + } + + if (!is_remove) + { + hb_set_add (name_languages, u); + } else { + hb_set_del (name_languages, u); + } + + s = p; + } + + return true; +} + +template +static gboolean +set_flag (const char *name, + const char *arg, + gpointer data, + GError **error G_GNUC_UNUSED) +{ + subset_main_t *subset_main = (subset_main_t *) data; + + hb_subset_input_set_flags (subset_main->input, + hb_subset_input_get_flags (subset_main->input) | flag); + + return true; +} + +static gboolean +parse_layout_tag_list (hb_subset_sets_t set_type, + const char *name, + const char *arg, + gpointer data, + GError **error G_GNUC_UNUSED) +{ + subset_main_t *subset_main = (subset_main_t *) data; + hb_bool_t is_remove = (name[strlen (name) - 1] == '-'); + hb_bool_t is_add = (name[strlen (name) - 1] == '+'); + hb_set_t *layout_tags = hb_subset_input_set (subset_main->input, set_type); + + if (!is_remove && !is_add) hb_set_clear (layout_tags); + + if (0 == strcmp (arg, "*")) + { + hb_set_clear (layout_tags); + if (!is_remove) + hb_set_invert (layout_tags); + return true; + } + + char *s = strtok((char *) arg, ", "); + while (s) + { + if (strlen (s) > 4) // tags are at most 4 bytes + { + g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, + "Failed parsing table tag at: '%s'", s); + return false; + } + + hb_tag_t tag = hb_tag_from_string (s, strlen (s)); + + if (!is_remove) + hb_set_add (layout_tags, tag); + else + hb_set_del (layout_tags, tag); + + s = strtok(nullptr, ", "); + } + + return true; +} + +static gboolean +parse_layout_features (const char *name, + const char *arg, + gpointer data, + GError **error) + +{ + return parse_layout_tag_list (HB_SUBSET_SETS_LAYOUT_FEATURE_TAG, + name, + arg, + data, + error); +} + +static gboolean +parse_layout_scripts (const char *name, + const char *arg, + gpointer data, + GError **error) + +{ + return parse_layout_tag_list (HB_SUBSET_SETS_LAYOUT_SCRIPT_TAG, + name, + arg, + data, + error); +} + +static gboolean +parse_drop_tables (const char *name, + const char *arg, + gpointer data, + GError **error) +{ + subset_main_t *subset_main = (subset_main_t *) data; + hb_bool_t is_remove = (name[strlen (name) - 1] == '-'); + hb_bool_t is_add = (name[strlen (name) - 1] == '+'); + hb_set_t *drop_tables = hb_subset_input_set (subset_main->input, HB_SUBSET_SETS_DROP_TABLE_TAG); + + if (!is_remove && !is_add) hb_set_clear (drop_tables); + + if (0 == strcmp (arg, "*")) + { + hb_set_clear (drop_tables); + if (!is_remove) + hb_set_invert (drop_tables); + return true; + } + + char *s = strtok((char *) arg, ", "); + while (s) + { + if (strlen (s) > 4) // Table tags are at most 4 bytes. + { + g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, + "Failed parsing table tag at: '%s'", s); + return false; + } + + hb_tag_t tag = hb_tag_from_string (s, strlen (s)); + + if (!is_remove) + hb_set_add (drop_tables, tag); + else + hb_set_del (drop_tables, tag); + + s = strtok(nullptr, ", "); + } + + return true; +} + +#ifdef HB_EXPERIMENTAL_API +#ifndef HB_NO_VAR +static gboolean +parse_instance (const char *name, + const char *arg, + gpointer data, + GError **error) +{ + subset_main_t *subset_main = (subset_main_t *) data; + + char *s = strtok((char *) arg, "="); + while (s) + { + unsigned len = strlen (s); + if (len > 4) //Axis tags are 4 bytes. + { + g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, + "Failed parsing axis tag at: '%s'", s); + return false; + } + + hb_tag_t axis_tag = hb_tag_from_string (s, len); + + s = strtok(nullptr, ", "); + if (!s) + { + g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, + "Value not specified for axis: %c%c%c%c", HB_UNTAG (axis_tag)); + return false; + } + + if (strcmp (s, "drop") == 0) + { + if (!hb_subset_input_pin_axis_to_default (subset_main->input, subset_main->face, axis_tag)) + { + g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, + "Cannot pin axis: '%c%c%c%c', not present in fvar", HB_UNTAG (axis_tag)); + return false; + } + } + else + { + errno = 0; + char *p; + float axis_value = strtof (s, &p); + if (errno || s == p) + { + g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, + "Failed parsing axis value at: '%s'", s); + return false; + } + + if (!hb_subset_input_pin_axis_location (subset_main->input, subset_main->face, axis_tag, axis_value)) + { + g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, + "Cannot pin axis: '%c%c%c%c', not present in fvar", HB_UNTAG (axis_tag)); + return false; + } + } + s = strtok(nullptr, "="); + } + + return true; +} +#endif +#endif + +template +static gboolean +parse_file_for (const char *name, + const char *arg, + gpointer data, + GError **error) +{ + FILE *fp = nullptr; + if (0 != strcmp (arg, "-")) + fp = fopen (arg, "r"); + else + fp = stdin; + + if (!fp) + { + g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_FAILED, + "Failed opening file `%s': %s", + arg, strerror (errno)); + return false; + } + + GString *gs = g_string_new (nullptr); + do + { + g_string_set_size (gs, 0); + char buf[BUFSIZ]; + while (fgets (buf, sizeof (buf), fp)) + { + unsigned bytes = strlen (buf); + if (bytes && buf[bytes - 1] == '\n') + { + bytes--; + g_string_append_len (gs, buf, bytes); + break; + } + g_string_append_len (gs, buf, bytes); + } + if (ferror (fp)) + { + g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_FAILED, + "Failed reading file `%s': %s", + arg, strerror (errno)); + return false; + } + g_string_append_c (gs, '\0'); + + if (allow_comments) + { + char *comment = strchr (gs->str, '#'); + if (comment) + *comment = '\0'; + } + + line_parser ("+", gs->str, data, error); + + if (*error) + break; + } + while (!feof (fp)); + + g_string_free (gs, false); + + return true; +} + +gboolean +subset_main_t::collect_face (const char *name, + const char *arg, + gpointer data, + GError **error) +{ + face_options_t *thiz = (face_options_t *) data; + + if (!thiz->font_file) + { + thiz->font_file = g_strdup (arg); + return true; + } + + return true; +} + +gboolean +subset_main_t::collect_rest (const char *name, + const char *arg, + gpointer data, + GError **error) +{ + subset_main_t *thiz = (subset_main_t *) data; + + if (!thiz->font_file) + { + thiz->font_file = g_strdup (arg); + return true; + } + + parse_text (name, arg, data, error); + return true; +} + +void +subset_main_t::add_options () +{ + set_summary ("Subset fonts to specification."); + + face_options_t::add_options (this); + + GOptionEntry glyphset_entries[] = + { + {"gids", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_gids, + "Specify glyph IDs or ranges to include in the subset.\n" + " " + "Use --gids-=... to subtract codepoints from the current set.", "list of glyph indices/ranges or *"}, + {"gids-", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, (gpointer) &parse_gids, "Specify glyph IDs or ranges to remove from the subset", "list of glyph indices/ranges or *"}, + {"gids+", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, (gpointer) &parse_gids, "Specify glyph IDs or ranges to include in the subset", "list of glyph indices/ranges or *"}, + {"gids-file", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_file_for, "Specify file to read glyph IDs or ranges from", "filename"}, + {"glyphs", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_glyphs, "Specify glyph names to include in the subset. Use --glyphs-=... to subtract glyphs from the current set.", "list of glyph names or *"}, + {"glyphs+", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, (gpointer) &parse_glyphs, "Specify glyph names to include in the subset", "list of glyph names"}, + {"glyphs-", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, (gpointer) &parse_glyphs, "Specify glyph names to remove from the subset", "list of glyph names"}, + + + {"glyphs-file", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_file_for, "Specify file to read glyph names from", "filename"}, + + {"text", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_text, "Specify text to include in the subset. Use --text-=... to subtract codepoints from the current set.", "string"}, + {"text-", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, (gpointer) &parse_text, "Specify text to remove from the subset", "string"}, + {"text+", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, (gpointer) &parse_text, "Specify text to include in the subset", "string"}, + + + {"text-file", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_file_for,"Specify file to read text from", "filename"}, + {"unicodes", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_unicodes, + "Specify Unicode codepoints or ranges to include in the subset. Use * to include all codepoints.\n" + " " + "--unicodes-=... can be used to subtract codepoints from the current set.\n" + " " + "For example: --unicodes=* --unicodes-=41,42,43 would create a subset with all codepoints\n" + " " + "except for 41, 42, 43.", + "list of hex numbers/ranges or *"}, + {"unicodes-", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, (gpointer) &parse_unicodes, "Specify Unicode codepoints or ranges to remove from the subset", "list of hex numbers/ranges or *"}, + {"unicodes+", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, (gpointer) &parse_unicodes, "Specify Unicode codepoints or ranges to include in the subset", "list of hex numbers/ranges or *"}, + + {"unicodes-file", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_file_for,"Specify file to read Unicode codepoints or ranges from", "filename"}, + {nullptr} + }; + add_group (glyphset_entries, + "subset-glyphset", + "Subset glyph-set option:", + "Subsetting glyph-set options", + this); + + GOptionEntry other_entries[] = + { + {"name-IDs", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_nameids, "Subset specified nameids. Use --name-IDs-=... to subtract from the current set.", "list of int numbers or *"}, + {"name-IDs-", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, (gpointer) &parse_nameids, "Subset specified nameids", "list of int numbers or *"}, + {"name-IDs+", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, (gpointer) &parse_nameids, "Subset specified nameids", "list of int numbers or *"}, + {"name-languages", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_name_languages, "Subset nameRecords with specified language IDs. Use --name-languages-=... to subtract from the current set.", "list of int numbers or *"}, + {"name-languages-", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, (gpointer) &parse_name_languages, "Subset nameRecords with specified language IDs", "list of int numbers or *"}, + {"name-languages+", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, (gpointer) &parse_name_languages, "Subset nameRecords with specified language IDs", "list of int numbers or *"}, + + {"layout-features", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_layout_features, "Specify set of layout feature tags that will be preserved. Use --layout-features-=... to subtract from the current set.", "list of string table tags or *"}, + {"layout-features+",0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, (gpointer) &parse_layout_features, "Specify set of layout feature tags that will be preserved", "list of string tags or *"}, + {"layout-features-",0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, (gpointer) &parse_layout_features, "Specify set of layout feature tags that will be preserved", "list of string tags or *"}, + + {"layout-scripts", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_layout_scripts, "Specify set of layout script tags that will be preserved. Use --layout-scripts-=... to subtract from the current set.", "list of string table tags or *"}, + {"layout-scripts+",0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, (gpointer) &parse_layout_scripts, "Specify set of layout script tags that will be preserved", "list of string tags or *"}, + {"layout-scripts-",0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, (gpointer) &parse_layout_scripts, "Specify set of layout script tags that will be preserved", "list of string tags or *"}, + + {"drop-tables", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_drop_tables, "Drop the specified tables. Use --drop-tables-=... to subtract from the current set.", "list of string table tags or *"}, + {"drop-tables+", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, (gpointer) &parse_drop_tables, "Drop the specified tables.", "list of string table tags or *"}, + {"drop-tables-", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, (gpointer) &parse_drop_tables, "Drop the specified tables.", "list of string table tags or *"}, +#ifdef HB_EXPERIMENTAL_API +#ifndef HB_NO_VAR + {"instance", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_instance, + "(Partially|Fully) Instantiate a variable font. A location consists of the tag of a variation axis, followed by '=', followed by a\n" + "number or the literal string 'drop'\n" + " " + "For example: --instance=\"wdth=100 wght=200\" or --instance=\"wdth=drop\"\n" + "Note: currently only fully instancing to the default location is supported\n", + "list of comma separated axis-locations"}, +#endif +#endif + {nullptr} + }; + add_group (other_entries, + "subset-other", + "Subset other option:", + "Subsetting other options", + this); + + GOptionEntry flag_entries[] = + { + {"no-hinting", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, (gpointer) &set_flag, "Whether to drop hints", nullptr}, + {"retain-gids", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, (gpointer) &set_flag, "If set don't renumber glyph ids in the subset.", nullptr}, + {"desubroutinize", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, (gpointer) &set_flag, "Remove CFF/CFF2 use of subroutines", nullptr}, + {"name-legacy", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, (gpointer) &set_flag, "Keep legacy (non-Unicode) 'name' table entries", nullptr}, + {"set-overlaps-flag", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, (gpointer) &set_flag, "Set the overlaps flag on each glyph.", nullptr}, + {"notdef-outline", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, (gpointer) &set_flag, "Keep the outline of \'.notdef\' glyph", nullptr}, + {"no-prune-unicode-ranges", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, (gpointer) &set_flag, "Don't change the 'OS/2 ulUnicodeRange*' bits.", nullptr}, + {"glyph-names", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, (gpointer) &set_flag, "Keep PS glyph names in TT-flavored fonts. ", nullptr}, + {"passthrough-tables", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, (gpointer) &set_flag, "Do not drop tables that the tool does not know how to subset.", nullptr}, + {"preprocess-face", 0, 0, G_OPTION_ARG_NONE, &this->preprocess, + "If set preprocesses the face with the add accelerator option before actually subsetting.", nullptr}, + {nullptr} + }; + add_group (flag_entries, + "subset-flags", + "Subset boolean option:", + "Subsetting boolean options", + this); + + GOptionEntry app_entries[] = + { + {"num-iterations", 'n', G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_INT, + &this->num_iterations, + "Run subsetter N times (default: 1)", "N"}, + {nullptr} + }; + add_group (app_entries, + "subset-app", + "Subset app option:", + "Subsetting application options", + this); + + output_options_t::add_options (this); + + GOptionEntry entries[] = + { + {G_OPTION_REMAINING, 0, G_OPTION_FLAG_IN_MAIN, + G_OPTION_ARG_CALLBACK, (gpointer) &collect_rest, nullptr, "[FONT-FILE] [TEXT]"}, + {nullptr} + }; + add_main_group (entries, this); + option_parser_t::add_options (); +} + +int +main (int argc, char **argv) +{ + return batch_main (argc, argv); +} diff --git a/libs/harfbuzz/util/hb-view.cc b/libs/harfbuzz/util/hb-view.cc new file mode 100644 index 00000000..82761fbf --- /dev/null +++ b/libs/harfbuzz/util/hb-view.cc @@ -0,0 +1,43 @@ +/* + * Copyright © 2010 Behdad Esfahbod + * Copyright © 2011,2012 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 + */ + +#include "batch.hh" +#include "font-options.hh" +#include "main-font-text.hh" +#include "shape-consumer.hh" +#include "text-options.hh" +#include "view-cairo.hh" + +const unsigned DEFAULT_FONT_SIZE = 256; +const unsigned SUBPIXEL_BITS = 6; + +int +main (int argc, char **argv) +{ + using main_t = main_font_text_t, font_options_t, shape_text_options_t>; + return batch_main (argc, argv); +} diff --git a/libs/harfbuzz/util/helper-cairo-ansi.hh b/libs/harfbuzz/util/helper-cairo-ansi.hh new file mode 100644 index 00000000..9708ee9e --- /dev/null +++ b/libs/harfbuzz/util/helper-cairo-ansi.hh @@ -0,0 +1,223 @@ +/* + * Copyright © 2012 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 HELPER_CAIRO_ANSI_HH +#define HELPER_CAIRO_ANSI_HH + +#include "hb.hh" + +#include + +#include "ansi-print.hh" + +#ifdef HAVE_CHAFA +# include + +/* Similar to ansi-print.cc */ +# define CELL_W 8 +# define CELL_H (2 * CELL_W) + +static void +chafa_print_image_rgb24 (const void *data, int width, int height, int stride, int level) +{ + ChafaTermInfo *term_info; + ChafaSymbolMap *symbol_map; + ChafaCanvasConfig *config; + ChafaCanvas *canvas; + GString *gs; + unsigned int cols = (width + CELL_W - 1) / CELL_W; + unsigned int rows = (height + CELL_H - 1) / CELL_H; + gchar **environ; + ChafaCanvasMode mode; + ChafaPixelMode pixel_mode; + + /* Adapt to terminal; use sixels if available, and fall back to symbols + * with as many colors as are supported */ + + environ = g_get_environ (); + term_info = chafa_term_db_detect (chafa_term_db_get_default (), + environ); + + pixel_mode = CHAFA_PIXEL_MODE_SYMBOLS; + + if (chafa_term_info_have_seq (term_info, CHAFA_TERM_SEQ_BEGIN_SIXELS)) + { + pixel_mode = CHAFA_PIXEL_MODE_SIXELS; + mode = CHAFA_CANVAS_MODE_TRUECOLOR; + } +// else if (chafa_term_info_have_seq (term_info, CHAFA_TERM_SEQ_SET_COLOR_FGBG_DIRECT)) +// mode = CHAFA_CANVAS_MODE_TRUECOLOR; + else if (chafa_term_info_have_seq (term_info, CHAFA_TERM_SEQ_SET_COLOR_FGBG_256)) + mode = CHAFA_CANVAS_MODE_INDEXED_240; + else if (chafa_term_info_have_seq (term_info, CHAFA_TERM_SEQ_SET_COLOR_FGBG_16)) + mode = CHAFA_CANVAS_MODE_INDEXED_16; + else if (chafa_term_info_have_seq (term_info, CHAFA_TERM_SEQ_INVERT_COLORS)) + mode = CHAFA_CANVAS_MODE_FGBG_BGFG; + else + mode = CHAFA_CANVAS_MODE_FGBG; + + /* Create the configuration */ + + symbol_map = chafa_symbol_map_new (); + chafa_symbol_map_add_by_tags (symbol_map, + (ChafaSymbolTags) (CHAFA_SYMBOL_TAG_BLOCK + | CHAFA_SYMBOL_TAG_SPACE + | (level >= 2 ? CHAFA_SYMBOL_TAG_WEDGE : 0) + | (level >= 3 ? CHAFA_SYMBOL_TAG_ALL : 0) + )); + + config = chafa_canvas_config_new (); + chafa_canvas_config_set_canvas_mode (config, mode); + chafa_canvas_config_set_pixel_mode (config, pixel_mode); + chafa_canvas_config_set_cell_geometry (config, CELL_W, CELL_H); + chafa_canvas_config_set_geometry (config, cols, rows); + chafa_canvas_config_set_symbol_map (config, symbol_map); + chafa_canvas_config_set_color_extractor (config, CHAFA_COLOR_EXTRACTOR_MEDIAN); + chafa_canvas_config_set_work_factor (config, 1.0f); + + /* Create canvas, draw to it and render output string */ + + canvas = chafa_canvas_new (config); + chafa_canvas_draw_all_pixels (canvas, + /* Cairo byte order is host native */ + G_BYTE_ORDER == G_LITTLE_ENDIAN + ? CHAFA_PIXEL_BGRA8_PREMULTIPLIED + : CHAFA_PIXEL_ARGB8_PREMULTIPLIED, + (const guint8 *) data, + width, + height, + stride); + gs = chafa_canvas_print (canvas, term_info); + + /* Print the string */ + + fwrite (gs->str, sizeof (char), gs->len, stdout); + + if (pixel_mode != CHAFA_PIXEL_MODE_SIXELS) + fputc ('\n', stdout); + + /* Free resources */ + + g_string_free (gs, TRUE); + chafa_canvas_unref (canvas); + chafa_canvas_config_unref (config); + chafa_symbol_map_unref (symbol_map); + chafa_term_info_unref (term_info); + g_strfreev (environ); +} + +#endif /* HAVE_CHAFA */ + +static inline cairo_status_t +helper_cairo_surface_write_to_ansi_stream (cairo_surface_t *surface, + cairo_write_func_t write_func, + void *closure) +{ + unsigned int width = cairo_image_surface_get_width (surface); + unsigned int height = cairo_image_surface_get_height (surface); + if (cairo_image_surface_get_format (surface) != CAIRO_FORMAT_RGB24) { + cairo_surface_t *new_surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, height); + cairo_t *cr = cairo_create (new_surface); + if (cairo_image_surface_get_format (surface) == CAIRO_FORMAT_A8) { + cairo_set_source_rgb (cr, 0., 0., 0.); + cairo_paint (cr); + cairo_set_source_rgb (cr, 1., 1., 1.); + cairo_mask_surface (cr, surface, 0, 0); + } else { + cairo_set_source_rgb (cr, 1., 1., 1.); + cairo_paint (cr); + cairo_set_source_surface (cr, surface, 0, 0); + cairo_paint (cr); + } + cairo_destroy (cr); + surface = new_surface; + } else + cairo_surface_reference (surface); + + unsigned int stride = cairo_image_surface_get_stride (surface); + const uint32_t *data = (uint32_t *) (void *) cairo_image_surface_get_data (surface); + + /* We don't have rows to spare on the terminal window... + * Find the tight image top/bottom and only print in between. */ + + /* Use corner color as background color. */ + uint32_t bg_color = data ? * (uint32_t *) data : 0; + + /* Drop first row while empty */ + auto orig_data = data; + while (height) + { + unsigned int i; + for (i = 0; i < width; i++) + if (data[i] != bg_color) + break; + if (i < width) + break; + data += stride / 4; + height--; + } + if (orig_data < data) + { + data -= stride / 4; + height++; /* Add one first blank row for padding. */ + } + + /* Drop last row while empty */ + auto orig_height = height; + while (height) + { + const uint32_t *row = data + (height - 1) * stride / 4; + unsigned int i; + for (i = 0; i < width; i++) + if (row[i] != bg_color) + break; + if (i < width) + break; + height--; + } + if (height < orig_height) + height++; /* Add one last blank row for padding. */ + + if (width && height) + { +#ifdef HAVE_CHAFA + const char *env = getenv ("HB_CHAFA"); + int chafa_level = 1; + if (env) + chafa_level = atoi (env); + if (chafa_level) + chafa_print_image_rgb24 (data, width, height, stride, chafa_level); + else +#endif + ansi_print_image_rgb24 (data, width, height, stride / 4); + } + + cairo_surface_destroy (surface); + return CAIRO_STATUS_SUCCESS; +} + + +#endif diff --git a/libs/harfbuzz/util/helper-cairo-ft.hh b/libs/harfbuzz/util/helper-cairo-ft.hh new file mode 100644 index 00000000..5152c7ca --- /dev/null +++ b/libs/harfbuzz/util/helper-cairo-ft.hh @@ -0,0 +1,120 @@ +/* + * Copyright © 2022 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 HELPER_CAIRO_FT_HH +#define HELPER_CAIRO_FT_HH + +#include "font-options.hh" + +#include +#include +#include FT_MULTIPLE_MASTERS_H + +static FT_Library ft_library; + +#ifdef HAVE_ATEXIT +static inline +void free_ft_library () +{ + FT_Done_FreeType (ft_library); +} +#endif + +static inline cairo_font_face_t * +helper_cairo_create_ft_font_face (const font_options_t *font_opts) +{ + cairo_font_face_t *cairo_face; + /* We cannot use the FT_Face from hb_font_t, as doing so will confuse hb_font_t because + * cairo will reset the face size. As such, create new face... + * TODO Perhaps add API to hb-ft to encapsulate this code. */ + FT_Face ft_face = nullptr;//hb_ft_font_get_face (font); + if (!ft_face) + { + if (!ft_library) + { + FT_Init_FreeType (&ft_library); +#ifdef HAVE_ATEXIT + atexit (free_ft_library); +#endif + } + + unsigned int blob_length; + const char *blob_data = hb_blob_get_data (font_opts->blob, &blob_length); + + if (FT_New_Memory_Face (ft_library, + (const FT_Byte *) blob_data, + blob_length, + font_opts->face_index, + &ft_face)) + fail (false, "FT_New_Memory_Face fail"); + } + if (!ft_face) + { + /* This allows us to get some boxes at least... */ + cairo_face = cairo_toy_font_face_create ("@cairo:sans", + CAIRO_FONT_SLANT_NORMAL, + CAIRO_FONT_WEIGHT_NORMAL); + } + else + { +#if !defined(HB_NO_VAR) && defined(HAVE_FT_SET_VAR_BLEND_COORDINATES) + unsigned int num_coords; + const float *coords = hb_font_get_var_coords_design (font_opts->font, &num_coords); + if (num_coords) + { + FT_Fixed *ft_coords = (FT_Fixed *) calloc (num_coords, sizeof (FT_Fixed)); + if (ft_coords) + { + for (unsigned int i = 0; i < num_coords; i++) + ft_coords[i] = coords[i] * 65536.f; + FT_Set_Var_Design_Coordinates (ft_face, num_coords, ft_coords); + free (ft_coords); + } + } +#endif + + cairo_face = cairo_ft_font_face_create_for_ft_face (ft_face, font_opts->ft_load_flags); + } + return cairo_face; +} + +static inline bool +helper_cairo_ft_scaled_font_has_color (cairo_scaled_font_t *scaled_font) +{ + bool ret = false; +#ifdef FT_HAS_COLOR + FT_Face ft_face = cairo_ft_scaled_font_lock_face (scaled_font); + if (ft_face) + { + if (FT_HAS_COLOR (ft_face)) + ret = true; + cairo_ft_scaled_font_unlock_face (scaled_font); + } +#endif + return ret; +} + +#endif diff --git a/libs/harfbuzz/util/helper-cairo-user.hh b/libs/harfbuzz/util/helper-cairo-user.hh new file mode 100644 index 00000000..8c1f9f22 --- /dev/null +++ b/libs/harfbuzz/util/helper-cairo-user.hh @@ -0,0 +1,316 @@ +/* + * Copyright © 2022 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 HELPER_CAIRO_USER_HH +#define HELPER_CAIRO_USER_HH + +#include "font-options.hh" + +#include +#include + +#include "hb-blob.hh" + +static void +move_to (hb_draw_funcs_t *dfuncs, + cairo_t *cr, + hb_draw_state_t *st, + float to_x, float to_y, + void *) +{ + cairo_move_to (cr, + (double) to_x, (double) to_y); +} + +static void +line_to (hb_draw_funcs_t *dfuncs, + cairo_t *cr, + hb_draw_state_t *st, + float to_x, float to_y, + void *) +{ + cairo_line_to (cr, + (double) to_x, (double) to_y); +} + +static void +cubic_to (hb_draw_funcs_t *dfuncs, + cairo_t *cr, + hb_draw_state_t *st, + float control1_x, float control1_y, + float control2_x, float control2_y, + float to_x, float to_y, + void *) +{ + cairo_curve_to (cr, + (double) control1_x, (double) control1_y, + (double) control2_x, (double) control2_y, + (double) to_x, (double) to_y); +} + +static void +close_path (hb_draw_funcs_t *dfuncs, + cairo_t *cr, + hb_draw_state_t *st, + void *) +{ + cairo_close_path (cr); +} + + +static hb_draw_funcs_t * +get_cairo_draw_funcs () +{ + static hb_draw_funcs_t *funcs; + + if (!funcs) + { + funcs = hb_draw_funcs_create (); + hb_draw_funcs_set_move_to_func (funcs, (hb_draw_move_to_func_t) move_to, nullptr, nullptr); + hb_draw_funcs_set_line_to_func (funcs, (hb_draw_line_to_func_t) line_to, nullptr, nullptr); + hb_draw_funcs_set_cubic_to_func (funcs, (hb_draw_cubic_to_func_t) cubic_to, nullptr, nullptr); + hb_draw_funcs_set_close_path_func (funcs, (hb_draw_close_path_func_t) close_path, nullptr, nullptr); + } + + return funcs; +} + +static const cairo_user_data_key_t _hb_font_cairo_user_data_key = {0}; + +static cairo_status_t +render_glyph (cairo_scaled_font_t *scaled_font, + unsigned long glyph, + cairo_t *cr, + cairo_text_extents_t *extents) +{ + hb_font_t *font = (hb_font_t *) (cairo_font_face_get_user_data (cairo_scaled_font_get_font_face (scaled_font), + &_hb_font_cairo_user_data_key)); + + hb_position_t x_scale, y_scale; + hb_font_get_scale (font, &x_scale, &y_scale); + cairo_scale (cr, +1./x_scale, -1./y_scale); + + hb_font_get_glyph_shape (font, glyph, get_cairo_draw_funcs (), cr); + cairo_fill (cr); + + return CAIRO_STATUS_SUCCESS; +} + +#ifdef HAVE_CAIRO_USER_FONT_FACE_SET_RENDER_COLOR_GLYPH_FUNC + +#ifdef CAIRO_HAS_PNG_FUNCTIONS +static inline cairo_status_t +_hb_bytes_read_func (hb_bytes_t *src, + char *data, + unsigned length) +{ + if (unlikely (src->length < length)) + return CAIRO_STATUS_READ_ERROR; + + memcpy (data, src->arrayZ, length); + *src += length; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +render_color_glyph_png (cairo_scaled_font_t *scaled_font, + unsigned long glyph, + cairo_t *cr, + cairo_text_extents_t *extents) +{ + hb_font_t *font = (hb_font_t *) (cairo_font_face_get_user_data (cairo_scaled_font_get_font_face (scaled_font), + &_hb_font_cairo_user_data_key)); + + hb_blob_t *blob = hb_ot_color_glyph_reference_png (font, glyph); + if (blob == hb_blob_get_empty ()) + return CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED; + + hb_position_t x_scale, y_scale; + hb_font_get_scale (font, &x_scale, &y_scale); + cairo_scale (cr, +1./x_scale, -1./y_scale); + + /* Draw PNG. */ + hb_bytes_t bytes = blob->as_bytes (); + cairo_surface_t *surface = cairo_image_surface_create_from_png_stream ((cairo_read_func_t) _hb_bytes_read_func, + std::addressof (bytes)); + hb_blob_destroy (blob); + + if (unlikely (cairo_surface_status (surface)) != CAIRO_STATUS_SUCCESS) + { + cairo_surface_destroy (surface); + return CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED; + } + + int width = cairo_image_surface_get_width (surface); + int height = cairo_image_surface_get_width (surface); + + hb_glyph_extents_t hb_extents; + if (unlikely (!hb_font_get_glyph_extents (font, glyph, &hb_extents))) + { + cairo_surface_destroy (surface); + return CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED; + } + + cairo_pattern_t *pattern = cairo_pattern_create_for_surface (surface); + cairo_pattern_set_extend (pattern, CAIRO_EXTEND_PAD); + + cairo_matrix_t matrix = {(double) width, 0, 0, (double) height, 0, 0}; + cairo_pattern_set_matrix (pattern, &matrix); + + cairo_translate (cr, hb_extents.x_bearing, hb_extents.y_bearing); + cairo_scale (cr, hb_extents.width, hb_extents.height); + cairo_set_source (cr, pattern); + + cairo_rectangle (cr, 0, 0, 1, 1); + cairo_fill (cr); + cairo_pattern_destroy (pattern); + + cairo_surface_destroy (surface); + return CAIRO_STATUS_SUCCESS; +} +#endif + +static cairo_status_t +render_color_glyph_layers (cairo_scaled_font_t *scaled_font, + unsigned long glyph, + cairo_t *cr, + cairo_text_extents_t *extents) +{ + hb_font_t *font = (hb_font_t *) (cairo_font_face_get_user_data (cairo_scaled_font_get_font_face (scaled_font), + &_hb_font_cairo_user_data_key)); + hb_face_t *face = hb_font_get_face (font); + + unsigned count = hb_ot_color_glyph_get_layers (face, glyph, 0, nullptr, nullptr); + if (!count) + return CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED; + + hb_ot_color_layer_t layers[16]; + unsigned offset = 0, len; + do { + len = ARRAY_LENGTH (layers); + hb_ot_color_glyph_get_layers (face, glyph, + offset, + &len, + layers); + for (unsigned i = 0; i < len; i++) + { + hb_color_t color; + unsigned clen = 1; + unsigned color_index = layers[i].color_index; + bool is_foreground = color_index == 65535; + + if (!is_foreground) + { + hb_ot_color_palette_get_colors (face, + 0/*palette_index*/, + color_index/*start_offset*/, + &clen/*color_count*/, + &color); + if (clen < 1) + continue; + } + + cairo_save (cr); + { + if (!is_foreground) + cairo_set_source_rgba (cr, + hb_color_get_red (color) / 255., + hb_color_get_green (color) / 255., + hb_color_get_blue (color) / 255., + hb_color_get_alpha (color) / 255.); + + cairo_status_t ret = render_glyph (scaled_font, layers[i].glyph, cr, extents); + if (ret != CAIRO_STATUS_SUCCESS) + return ret; + } + cairo_restore (cr); + } + } + while (len == ARRAY_LENGTH (layers)); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +render_color_glyph (cairo_scaled_font_t *scaled_font, + unsigned long glyph, + cairo_t *cr, + cairo_text_extents_t *extents) +{ + cairo_status_t ret = CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED; + +#ifdef CAIRO_HAS_PNG_FUNCTIONS + ret = render_color_glyph_png (scaled_font, glyph, cr, extents); + if (ret != CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED) + return ret; +#endif + + ret = render_color_glyph_layers (scaled_font, glyph, cr, extents); + if (ret != CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED) + return ret; + + return render_glyph (scaled_font, glyph, cr, extents); +} + +#endif + +static inline cairo_font_face_t * +helper_cairo_create_user_font_face (const font_options_t *font_opts) +{ + cairo_font_face_t *cairo_face = cairo_user_font_face_create (); + + cairo_font_face_set_user_data (cairo_face, + &_hb_font_cairo_user_data_key, + hb_font_reference (font_opts->font), + (cairo_destroy_func_t) hb_font_destroy); + + cairo_user_font_face_set_render_glyph_func (cairo_face, render_glyph); +#ifdef HAVE_CAIRO_USER_FONT_FACE_SET_RENDER_COLOR_GLYPH_FUNC + hb_face_t *face = hb_font_get_face (font_opts->font); + if (hb_ot_color_has_png (face) || hb_ot_color_has_layers (face)) + cairo_user_font_face_set_render_color_glyph_func (cairo_face, render_color_glyph); +#endif + + return cairo_face; +} + +static inline bool +helper_cairo_user_font_face_has_data (cairo_font_face_t *font_face) +{ + return cairo_font_face_get_user_data (font_face, &_hb_font_cairo_user_data_key); +} + +static inline bool +helper_cairo_user_scaled_font_has_color (cairo_scaled_font_t *scaled_font) +{ + /* Ignoring SVG for now, since we cannot render it. */ + hb_font_t *font = (hb_font_t *) (cairo_font_face_get_user_data (cairo_scaled_font_get_font_face (scaled_font), + &_hb_font_cairo_user_data_key)); + hb_face_t *face = hb_font_get_face (font); + return hb_ot_color_has_png (face) || hb_ot_color_has_layers (face); +} + +#endif diff --git a/libs/harfbuzz/util/helper-cairo.hh b/libs/harfbuzz/util/helper-cairo.hh new file mode 100644 index 00000000..6df92e56 --- /dev/null +++ b/libs/harfbuzz/util/helper-cairo.hh @@ -0,0 +1,656 @@ +/* + * 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 HELPER_CAIRO_HH +#define HELPER_CAIRO_HH + +#include "view-options.hh" +#include "output-options.hh" +#include "helper-cairo-ft.hh" +#include "helper-cairo-user.hh" + +#include +#include + +#include "helper-cairo-ansi.hh" +#ifdef CAIRO_HAS_SVG_SURFACE +# include +#endif +#ifdef CAIRO_HAS_PDF_SURFACE +# include +#endif +#ifdef CAIRO_HAS_PS_SURFACE +# include +# if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1,6,0) +# define HAS_EPS 1 + +static cairo_surface_t * +_cairo_eps_surface_create_for_stream (cairo_write_func_t write_func, + void *closure, + double width, + double height) +{ + cairo_surface_t *surface; + + surface = cairo_ps_surface_create_for_stream (write_func, closure, width, height); + cairo_ps_surface_set_eps (surface, true); + + return surface; +} + +# else +# undef HAS_EPS +# endif +#endif + +static inline bool +helper_cairo_use_hb_draw (const font_options_t *font_opts) +{ + const char *env = getenv ("HB_DRAW"); + if (!env) + return cairo_version () >= CAIRO_VERSION_ENCODE (1, 17, 5); + return atoi (env); +} + +static inline cairo_scaled_font_t * +helper_cairo_create_scaled_font (const font_options_t *font_opts) +{ + bool use_hb_draw = helper_cairo_use_hb_draw (font_opts); + hb_font_t *font = hb_font_reference (font_opts->font); + + cairo_font_face_t *cairo_face; + if (use_hb_draw) + cairo_face = helper_cairo_create_user_font_face (font_opts); + else + cairo_face = helper_cairo_create_ft_font_face (font_opts); + + cairo_matrix_t ctm, font_matrix; + cairo_font_options_t *font_options; + + cairo_matrix_init_identity (&ctm); + cairo_matrix_init_scale (&font_matrix, + font_opts->font_size_x, + font_opts->font_size_y); + if (use_hb_draw) + font_matrix.xy = -font_opts->slant * font_opts->font_size_x; + + font_options = cairo_font_options_create (); + cairo_font_options_set_hint_style (font_options, CAIRO_HINT_STYLE_NONE); + cairo_font_options_set_hint_metrics (font_options, CAIRO_HINT_METRICS_OFF); + + cairo_scaled_font_t *scaled_font = cairo_scaled_font_create (cairo_face, + &font_matrix, + &ctm, + font_options); + + cairo_font_options_destroy (font_options); + cairo_font_face_destroy (cairo_face); + + static cairo_user_data_key_t key; + if (cairo_scaled_font_set_user_data (scaled_font, + &key, + (void *) font, + (cairo_destroy_func_t) hb_font_destroy)) + hb_font_destroy (font); + + return scaled_font; +} + +static inline bool +helper_cairo_scaled_font_has_color (cairo_scaled_font_t *scaled_font) +{ + if (helper_cairo_user_font_face_has_data (cairo_scaled_font_get_font_face (scaled_font))) + return helper_cairo_user_scaled_font_has_color (scaled_font); + else + return helper_cairo_ft_scaled_font_has_color (scaled_font); +} + + +enum class image_protocol_t { + NONE = 0, + ITERM2, + KITTY, +}; + +struct finalize_closure_t { + void (*callback)(finalize_closure_t *); + cairo_surface_t *surface; + cairo_write_func_t write_func; + void *closure; + image_protocol_t protocol; +}; +static cairo_user_data_key_t finalize_closure_key; + + +static void +finalize_ansi (finalize_closure_t *closure) +{ + cairo_status_t status; + status = helper_cairo_surface_write_to_ansi_stream (closure->surface, + closure->write_func, + closure->closure); + if (status != CAIRO_STATUS_SUCCESS) + fail (false, "Failed to write output: %s", + cairo_status_to_string (status)); +} + +static cairo_surface_t * +_cairo_ansi_surface_create_for_stream (cairo_write_func_t write_func, + void *closure, + double width, + double height, + cairo_content_t content, + image_protocol_t protocol HB_UNUSED) +{ + cairo_surface_t *surface; + int w = ceil (width); + int h = ceil (height); + + switch (content) { + case CAIRO_CONTENT_ALPHA: + surface = cairo_image_surface_create (CAIRO_FORMAT_A8, w, h); + break; + default: + case CAIRO_CONTENT_COLOR: + surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, w, h); + break; + case CAIRO_CONTENT_COLOR_ALPHA: + surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, w, h); + break; + } + cairo_status_t status = cairo_surface_status (surface); + if (status != CAIRO_STATUS_SUCCESS) + fail (false, "Failed to create cairo surface: %s", + cairo_status_to_string (status)); + + finalize_closure_t *ansi_closure = g_new0 (finalize_closure_t, 1); + ansi_closure->callback = finalize_ansi; + ansi_closure->surface = surface; + ansi_closure->write_func = write_func; + ansi_closure->closure = closure; + + if (cairo_surface_set_user_data (surface, + &finalize_closure_key, + (void *) ansi_closure, + (cairo_destroy_func_t) g_free)) + g_free ((void *) closure); + + return surface; +} + + +#ifdef CAIRO_HAS_PNG_FUNCTIONS + +static cairo_status_t +byte_array_write_func (void *closure, + const unsigned char *data, + unsigned int size) +{ + g_byte_array_append ((GByteArray *) closure, data, size); + return CAIRO_STATUS_SUCCESS; +} + +static void +finalize_png (finalize_closure_t *closure) +{ + cairo_status_t status; + GByteArray *bytes = nullptr; + GString *string; + gchar *base64; + size_t base64_len; + + if (closure->protocol == image_protocol_t::NONE) + { + status = cairo_surface_write_to_png_stream (closure->surface, + closure->write_func, + closure->closure); + } + else + { + bytes = g_byte_array_new (); + status = cairo_surface_write_to_png_stream (closure->surface, + byte_array_write_func, + bytes); + } + + if (status != CAIRO_STATUS_SUCCESS) + fail (false, "Failed to write output: %s", + cairo_status_to_string (status)); + + if (closure->protocol == image_protocol_t::NONE) + return; + + base64 = g_base64_encode (bytes->data, bytes->len); + base64_len = strlen (base64); + + string = g_string_new (NULL); + if (closure->protocol == image_protocol_t::ITERM2) + { + /* https://iterm2.com/documentation-images.html */ + g_string_printf (string, "\033]1337;File=inline=1;size=%zu:%s\a\n", + base64_len, base64); + } + else if (closure->protocol == image_protocol_t::KITTY) + { +#define CHUNK_SIZE 4096 + /* https://sw.kovidgoyal.net/kitty/graphics-protocol.html */ + for (size_t pos = 0; pos < base64_len; pos += CHUNK_SIZE) + { + size_t len = base64_len - pos; + + if (pos == 0) + g_string_append (string, "\033_Ga=T,f=100,m="); + else + g_string_append (string, "\033_Gm="); + + if (len > CHUNK_SIZE) + { + g_string_append (string, "1;"); + g_string_append_len (string, base64 + pos, CHUNK_SIZE); + } + else + { + g_string_append (string, "0;"); + g_string_append_len (string, base64 + pos, len); + } + + g_string_append (string, "\033\\"); + } + g_string_append (string, "\n"); +#undef CHUNK_SIZE + } + + closure->write_func (closure->closure, (unsigned char *) string->str, string->len); + + g_byte_array_unref (bytes); + g_free (base64); + g_string_free (string, TRUE); +} + +static cairo_surface_t * +_cairo_png_surface_create_for_stream (cairo_write_func_t write_func, + void *closure, + double width, + double height, + cairo_content_t content, + image_protocol_t protocol) +{ + cairo_surface_t *surface; + int w = ceil (width); + int h = ceil (height); + + switch (content) { + case CAIRO_CONTENT_ALPHA: + surface = cairo_image_surface_create (CAIRO_FORMAT_A8, w, h); + break; + default: + case CAIRO_CONTENT_COLOR: + surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, w, h); + break; + case CAIRO_CONTENT_COLOR_ALPHA: + surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, w, h); + break; + } + cairo_status_t status = cairo_surface_status (surface); + if (status != CAIRO_STATUS_SUCCESS) + fail (false, "Failed to create cairo surface: %s", + cairo_status_to_string (status)); + + finalize_closure_t *png_closure = g_new0 (finalize_closure_t, 1); + png_closure->callback = finalize_png; + png_closure->surface = surface; + png_closure->write_func = write_func; + png_closure->closure = closure; + png_closure->protocol = protocol; + + if (cairo_surface_set_user_data (surface, + &finalize_closure_key, + (void *) png_closure, + (cairo_destroy_func_t) g_free)) + g_free ((void *) closure); + + return surface; +} + +#endif + +static cairo_status_t +stdio_write_func (void *closure, + const unsigned char *data, + unsigned int size) +{ + FILE *fp = (FILE *) closure; + + while (size) { + size_t ret = fwrite (data, 1, size, fp); + size -= ret; + data += ret; + if (size && ferror (fp)) + fail (false, "Failed to write output: %s", strerror (errno)); + } + + return CAIRO_STATUS_SUCCESS; +} + +static const char *helper_cairo_supported_formats[] = +{ + "ansi", + #ifdef CAIRO_HAS_PNG_FUNCTIONS + "png", + #endif + #ifdef CAIRO_HAS_SVG_SURFACE + "svg", + #endif + #ifdef CAIRO_HAS_PDF_SURFACE + "pdf", + #endif + #ifdef CAIRO_HAS_PS_SURFACE + "ps", + #ifdef HAS_EPS + "eps", + #endif + #endif + nullptr +}; + +template +static inline cairo_t * +helper_cairo_create_context (double w, double h, + view_options_t *view_opts, + output_options_type *out_opts, + cairo_content_t content) +{ + cairo_surface_t *(*constructor) (cairo_write_func_t write_func, + void *closure, + double width, + double height) = nullptr; + cairo_surface_t *(*constructor2) (cairo_write_func_t write_func, + void *closure, + double width, + double height, + cairo_content_t content, + image_protocol_t protocol) = nullptr; + + image_protocol_t protocol = image_protocol_t::NONE; + const char *extension = out_opts->output_format; + if (!extension) { +#if HAVE_ISATTY + if (isatty (fileno (out_opts->out_fp))) + { +#ifdef CAIRO_HAS_PNG_FUNCTIONS + const char *name; + /* https://gitlab.com/gnachman/iterm2/-/issues/7154 */ + if ((name = getenv ("LC_TERMINAL")) != nullptr && + 0 == g_ascii_strcasecmp (name, "iTerm2")) + { + extension = "png"; + protocol = image_protocol_t::ITERM2; + } + else if ((name = getenv ("TERM_PROGRAM")) != nullptr && + 0 == g_ascii_strcasecmp (name, "WezTerm")) + { + extension = "png"; + protocol = image_protocol_t::ITERM2; + } + else if ((name = getenv ("TERM")) != nullptr && + 0 == g_ascii_strcasecmp (name, "xterm-kitty")) + { + extension = "png"; + protocol = image_protocol_t::KITTY; + } + else + extension = "ansi"; +#else + extension = "ansi"; +#endif + } + else +#endif + { +#ifdef CAIRO_HAS_PNG_FUNCTIONS + extension = "png"; +#else + extension = "ansi"; +#endif + } + } + if (0) + ; + else if (0 == g_ascii_strcasecmp (extension, "ansi")) + constructor2 = _cairo_ansi_surface_create_for_stream; + #ifdef CAIRO_HAS_PNG_FUNCTIONS + else if (0 == g_ascii_strcasecmp (extension, "png")) + constructor2 = _cairo_png_surface_create_for_stream; + #endif + #ifdef CAIRO_HAS_SVG_SURFACE + else if (0 == g_ascii_strcasecmp (extension, "svg")) + constructor = cairo_svg_surface_create_for_stream; + #endif + #ifdef CAIRO_HAS_PDF_SURFACE + else if (0 == g_ascii_strcasecmp (extension, "pdf")) + constructor = cairo_pdf_surface_create_for_stream; + #endif + #ifdef CAIRO_HAS_PS_SURFACE + else if (0 == g_ascii_strcasecmp (extension, "ps")) + constructor = cairo_ps_surface_create_for_stream; + #ifdef HAS_EPS + else if (0 == g_ascii_strcasecmp (extension, "eps")) + constructor = _cairo_eps_surface_create_for_stream; + #endif + #endif + + + unsigned int fr, fg, fb, fa, br, bg, bb, ba; + const char *color; + br = bg = bb = 0; ba = 255; + color = view_opts->back ? view_opts->back : DEFAULT_BACK; + sscanf (color + (*color=='#'), "%2x%2x%2x%2x", &br, &bg, &bb, &ba); + fr = fg = fb = 0; fa = 255; + color = view_opts->fore ? view_opts->fore : DEFAULT_FORE; + sscanf (color + (*color=='#'), "%2x%2x%2x%2x", &fr, &fg, &fb, &fa); + + if (content == CAIRO_CONTENT_ALPHA) + { + if (view_opts->annotate || + br != bg || bg != bb || + fr != fg || fg != fb) + content = CAIRO_CONTENT_COLOR; + } + if (ba != 255) + content = CAIRO_CONTENT_COLOR_ALPHA; + + cairo_surface_t *surface; + FILE *f = out_opts->out_fp; + if (constructor) + surface = constructor (stdio_write_func, f, w, h); + else if (constructor2) + surface = constructor2 (stdio_write_func, f, w, h, content, protocol); + else + fail (false, "Unknown output format `%s'; supported formats are: %s%s", + extension, + g_strjoinv ("/", const_cast (helper_cairo_supported_formats)), + out_opts->explicit_output_format ? "" : + "\nTry setting format using --output-format"); + + cairo_t *cr = cairo_create (surface); + content = cairo_surface_get_content (surface); + + switch (content) { + case CAIRO_CONTENT_ALPHA: + cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); + cairo_set_source_rgba (cr, 1., 1., 1., br / 255.); + cairo_paint (cr); + cairo_set_source_rgba (cr, 1., 1., 1., + (fr / 255.) * (fa / 255.) + (br / 255) * (1 - (fa / 255.))); + break; + default: + case CAIRO_CONTENT_COLOR: + case CAIRO_CONTENT_COLOR_ALPHA: + cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); + cairo_set_source_rgba (cr, br / 255., bg / 255., bb / 255., ba / 255.); + cairo_paint (cr); + cairo_set_operator (cr, CAIRO_OPERATOR_OVER); + cairo_set_source_rgba (cr, fr / 255., fg / 255., fb / 255., fa / 255.); + break; + } + + cairo_surface_destroy (surface); + return cr; +} + +static inline void +helper_cairo_destroy_context (cairo_t *cr) +{ + finalize_closure_t *closure = (finalize_closure_t *) + cairo_surface_get_user_data (cairo_get_target (cr), + &finalize_closure_key); + if (closure) + closure->callback (closure); + + cairo_status_t status = cairo_status (cr); + if (status != CAIRO_STATUS_SUCCESS) + fail (false, "Failed: %s", + cairo_status_to_string (status)); + cairo_destroy (cr); +} + + +struct helper_cairo_line_t { + cairo_glyph_t *glyphs; + unsigned int num_glyphs; + char *utf8; + unsigned int utf8_len; + cairo_text_cluster_t *clusters; + unsigned int num_clusters; + cairo_text_cluster_flags_t cluster_flags; + + void finish () { + if (glyphs) + cairo_glyph_free (glyphs); + if (clusters) + cairo_text_cluster_free (clusters); + if (utf8) + g_free (utf8); + } + + void get_advance (double *x_advance, double *y_advance) { + *x_advance = glyphs[num_glyphs].x; + *y_advance = glyphs[num_glyphs].y; + } +}; + +static inline void +helper_cairo_line_from_buffer (helper_cairo_line_t *l, + hb_buffer_t *buffer, + const char *text, + unsigned int text_len, + int scale_bits, + hb_bool_t utf8_clusters) +{ + memset (l, 0, sizeof (*l)); + + l->num_glyphs = hb_buffer_get_length (buffer); + hb_glyph_info_t *hb_glyph = hb_buffer_get_glyph_infos (buffer, nullptr); + hb_glyph_position_t *hb_position = hb_buffer_get_glyph_positions (buffer, nullptr); + l->glyphs = cairo_glyph_allocate (l->num_glyphs + 1); + + if (text) { + l->utf8 = g_strndup (text, text_len); + l->utf8_len = text_len; + l->num_clusters = l->num_glyphs ? 1 : 0; + for (unsigned int i = 1; i < l->num_glyphs; i++) + if (hb_glyph[i].cluster != hb_glyph[i-1].cluster) + l->num_clusters++; + l->clusters = cairo_text_cluster_allocate (l->num_clusters); + } + + if ((l->num_glyphs && !l->glyphs) || + (l->utf8_len && !l->utf8) || + (l->num_clusters && !l->clusters)) + { + l->finish (); + return; + } + + hb_position_t x = 0, y = 0; + int i; + for (i = 0; i < (int) l->num_glyphs; i++) + { + l->glyphs[i].index = hb_glyph[i].codepoint; + l->glyphs[i].x = scalbn ((double) hb_position->x_offset + x, scale_bits); + l->glyphs[i].y = scalbn ((double) -hb_position->y_offset + y, scale_bits); + x += hb_position->x_advance; + y += -hb_position->y_advance; + + hb_position++; + } + l->glyphs[i].index = -1; + l->glyphs[i].x = scalbn ((double) x, scale_bits); + l->glyphs[i].y = scalbn ((double) y, scale_bits); + + if (l->num_clusters) { + memset ((void *) l->clusters, 0, l->num_clusters * sizeof (l->clusters[0])); + hb_bool_t backward = HB_DIRECTION_IS_BACKWARD (hb_buffer_get_direction (buffer)); + l->cluster_flags = backward ? CAIRO_TEXT_CLUSTER_FLAG_BACKWARD : (cairo_text_cluster_flags_t) 0; + unsigned int cluster = 0; + const char *start = l->utf8, *end; + l->clusters[cluster].num_glyphs++; + if (backward) { + for (i = l->num_glyphs - 2; i >= 0; i--) { + if (hb_glyph[i].cluster != hb_glyph[i+1].cluster) { + g_assert (hb_glyph[i].cluster > hb_glyph[i+1].cluster); + if (utf8_clusters) + end = start + hb_glyph[i].cluster - hb_glyph[i+1].cluster; + else + end = g_utf8_offset_to_pointer (start, hb_glyph[i].cluster - hb_glyph[i+1].cluster); + l->clusters[cluster].num_bytes = end - start; + start = end; + cluster++; + } + l->clusters[cluster].num_glyphs++; + } + l->clusters[cluster].num_bytes = l->utf8 + text_len - start; + } else { + for (i = 1; i < (int) l->num_glyphs; i++) { + if (hb_glyph[i].cluster != hb_glyph[i-1].cluster) { + g_assert (hb_glyph[i].cluster > hb_glyph[i-1].cluster); + if (utf8_clusters) + end = start + hb_glyph[i].cluster - hb_glyph[i-1].cluster; + else + end = g_utf8_offset_to_pointer (start, hb_glyph[i].cluster - hb_glyph[i-1].cluster); + l->clusters[cluster].num_bytes = end - start; + start = end; + cluster++; + } + l->clusters[cluster].num_glyphs++; + } + l->clusters[cluster].num_bytes = l->utf8 + text_len - start; + } + } +} + +#endif diff --git a/libs/harfbuzz/util/main-font-text.hh b/libs/harfbuzz/util/main-font-text.hh new file mode 100644 index 00000000..ca39c5a4 --- /dev/null +++ b/libs/harfbuzz/util/main-font-text.hh @@ -0,0 +1,104 @@ +/* + * Copyright © 2011,2012 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 HB_MAIN_FONT_TEXT_HH +#define HB_MAIN_FONT_TEXT_HH + +#include "options.hh" + +/* main() body for utilities taking font and processing text.*/ + +template +struct main_font_text_t : + option_parser_t, + font_options_type, + text_options_type, + consumer_t +{ + int operator () (int argc, char **argv) + { + add_options (); + parse (&argc, &argv); + + this->init (this); + + while (this->consume_line (*this)) + ; + + this->finish (this); + + return this->failed ? 1 : 0; + } + + protected: + + void add_options () + { + font_options_type::add_options (this); + text_options_type::add_options (this); + consumer_t::add_options (this); + + GOptionEntry entries[] = + { + {G_OPTION_REMAINING, 0, G_OPTION_FLAG_IN_MAIN, + G_OPTION_ARG_CALLBACK, (gpointer) &collect_rest, nullptr, "[FONT-FILE] [TEXT]"}, + {nullptr} + }; + add_main_group (entries, this); + option_parser_t::add_options (); + } + + private: + + static gboolean + collect_rest (const char *name G_GNUC_UNUSED, + const char *arg, + gpointer data, + GError **error) + { + main_font_text_t *thiz = (main_font_text_t *) data; + + if (!thiz->font_file) + { + thiz->font_file = g_strdup (arg); + return true; + } + + if (!thiz->text && !thiz->text_file) + { + thiz->text = g_strdup (arg); + return true; + } + + g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_FAILED, + "Too many arguments on the command line"); + return false; + } +}; + +#endif diff --git a/libs/harfbuzz/util/meson.build b/libs/harfbuzz/util/meson.build new file mode 100644 index 00000000..fdab620f --- /dev/null +++ b/libs/harfbuzz/util/meson.build @@ -0,0 +1,58 @@ +hb_view_sources = [ + 'hb-view.cc', +] + +hb_shape_sources = [ + 'hb-shape.cc', +] + +hb_ot_shape_closure_sources = [ + 'hb-ot-shape-closure.cc', +] + +hb_subset_cli_sources = [ + 'hb-subset.cc', +] + +util_deps = [freetype_dep, cairo_dep, cairo_ft_dep, glib_dep] + +if conf.get('HAVE_GLIB', 0) == 1 + if conf.get('HAVE_FREETYPE', 0) == 1 and conf.get('HAVE_CAIRO_FT', 0) == 1 + + hb_view = executable('hb-view', hb_view_sources, + cpp_args: cpp_args, + include_directories: [incconfig, incsrc], + dependencies: [util_deps, chafa_dep], + link_with: [libharfbuzz], + install: true, + ) + endif + + hb_shape = executable('hb-shape', hb_shape_sources, + cpp_args: cpp_args, + include_directories: [incconfig, incsrc], + dependencies: util_deps, + link_with: [libharfbuzz], + install: true, + ) + + hb_subset = executable('hb-subset', hb_subset_cli_sources, + cpp_args: cpp_args, + include_directories: [incconfig, incsrc], + dependencies: util_deps, + link_with: [libharfbuzz, libharfbuzz_subset], + install: true, + ) + + hb_ot_shape_closure = executable('hb-ot-shape-closure', hb_ot_shape_closure_sources, + cpp_args: cpp_args, + include_directories: [incconfig, incsrc], + dependencies: util_deps, + link_with: [libharfbuzz], + install: true, + ) +else + # Disable tests that use this + hb_shape = disabler() + hb_subset = disabler() +endif diff --git a/libs/harfbuzz/util/options.hh b/libs/harfbuzz/util/options.hh new file mode 100644 index 00000000..790650bf --- /dev/null +++ b/libs/harfbuzz/util/options.hh @@ -0,0 +1,244 @@ +/* + * 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 OPTIONS_HH +#define OPTIONS_HH + +#include "hb.hh" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include /* for isatty() */ +#endif +#if defined(_WIN32) || defined(__CYGWIN__) +#include /* for setmode() under Windows */ +#endif + +#include +#include +#include +#include + + +static inline void fail (hb_bool_t suggest_help, const char *format, ...) G_GNUC_NORETURN G_GNUC_PRINTF (2, 3); + +static inline void +fail (hb_bool_t suggest_help, const char *format, ...) +{ + const char *msg; + + va_list vap; + va_start (vap, format); + msg = g_strdup_vprintf (format, vap); + va_end (vap); + const char *prgname = g_get_prgname (); + g_printerr ("%s: %s\n", prgname, msg); + if (suggest_help) + g_printerr ("Try `%s --help' for more information.\n", prgname); + + exit (1); +} + +struct option_parser_t +{ + option_parser_t (const char *parameter_string = nullptr) + : context (g_option_context_new (parameter_string)), + to_free (g_ptr_array_new ()) + {} + + static void _g_free_g_func (void *p, void * G_GNUC_UNUSED) { g_free (p); } + + ~option_parser_t () + { + g_option_context_free (context); + g_ptr_array_foreach (to_free, _g_free_g_func, nullptr); + g_ptr_array_free (to_free, TRUE); + } + + void add_options (); + + static void + post_parse_ (void *thiz, GError **error) {} + template + static auto + post_parse_ (Type *thiz, GError **error) -> decltype (thiz->post_parse (error)) + { thiz->post_parse (error); } + template + static gboolean + post_parse (GOptionContext *context G_GNUC_UNUSED, + GOptionGroup *group G_GNUC_UNUSED, + gpointer data, + GError **error) + { + option_parser_t::post_parse_ (static_cast (data), error); + return !*error; + } + + template + void add_group (GOptionEntry *entries, + const gchar *name, + const gchar *description, + const gchar *help_description, + Type *closure, + bool add_parse_hooks = true) + { + GOptionGroup *group = g_option_group_new (name, description, help_description, + static_cast(closure), nullptr); + g_option_group_add_entries (group, entries); + if (add_parse_hooks) + g_option_group_set_parse_hooks (group, nullptr, post_parse); + g_option_context_add_group (context, group); + } + + template + void add_main_group (GOptionEntry *entries, + Type *closure) + { + GOptionGroup *group = g_option_group_new (nullptr, nullptr, nullptr, + static_cast(closure), nullptr); + g_option_group_add_entries (group, entries); + /* https://gitlab.gnome.org/GNOME/glib/-/issues/2460 */ + //g_option_group_set_parse_hooks (group, nullptr, post_parse); + g_option_context_set_main_group (context, group); + } + + void set_summary (const char *summary) + { + g_option_context_set_summary (context, summary); + } + void set_description (const char *description) + { + g_option_context_set_description (context, description); + } + + void free_later (char *p) { + g_ptr_array_add (to_free, p); + } + + bool parse (int *argc, char ***argv, bool ignore_error = false); + + GOptionContext *context; + protected: + GPtrArray *to_free; +}; + + +static inline gchar * +shapers_to_string () +{ + GString *shapers = g_string_new (nullptr); + const char **shaper_list = hb_shape_list_shapers (); + + for (; *shaper_list; shaper_list++) { + g_string_append (shapers, *shaper_list); + g_string_append_c (shapers, ','); + } + g_string_truncate (shapers, MAX (0, (gint)shapers->len - 1)); + + return g_string_free (shapers, false); +} + +static G_GNUC_NORETURN gboolean +show_version (const char *name G_GNUC_UNUSED, + const char *arg G_GNUC_UNUSED, + gpointer data G_GNUC_UNUSED, + GError **error G_GNUC_UNUSED) +{ + g_printf ("%s (%s) %s\n", g_get_prgname (), PACKAGE_NAME, PACKAGE_VERSION); + + char *shapers = shapers_to_string (); + g_printf ("Available shapers: %s\n", shapers); + g_free (shapers); + if (strcmp (HB_VERSION_STRING, hb_version_string ())) + g_printf ("Linked HarfBuzz library has a different version: %s\n", hb_version_string ()); + + exit(0); +} + +inline void +option_parser_t::add_options () +{ + GOptionEntry entries[] = + { + {"version", 0, G_OPTION_FLAG_NO_ARG, + G_OPTION_ARG_CALLBACK, (gpointer) &show_version, "Show version numbers", nullptr}, + {nullptr} + }; + g_option_context_add_main_entries (context, entries, nullptr); +} + +inline bool +option_parser_t::parse (int *argc, char ***argv, bool ignore_error) +{ + setlocale (LC_ALL, ""); + + GError *parse_error = nullptr; + if (!g_option_context_parse (context, argc, argv, &parse_error)) + { + if (parse_error) + { + if (!ignore_error) + fail (true, "%s", parse_error->message); + g_error_free (parse_error); + } + else + { + if (!ignore_error) + fail (true, "Option parse error"); + } + return false; + } + return true; +} + + +/* fallback implementation for scalbn()/scalbnf() for pre-2013 MSVC */ +#if defined (_MSC_VER) && (_MSC_VER < 1800) + +#ifndef FLT_RADIX +#define FLT_RADIX 2 +#endif + +__inline long double scalbn (long double x, int exp) +{ + return x * (pow ((long double) FLT_RADIX, exp)); +} + +__inline float scalbnf (float x, int exp) +{ + return x * (pow ((float) FLT_RADIX, exp)); +} +#endif + +#endif diff --git a/libs/harfbuzz/util/output-options.hh b/libs/harfbuzz/util/output-options.hh new file mode 100644 index 00000000..270e9f1e --- /dev/null +++ b/libs/harfbuzz/util/output-options.hh @@ -0,0 +1,117 @@ +/* + * 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 OUTPUT_OPTIONS_HH +#define OUTPUT_OPTIONS_HH + +#include "options.hh" + +template +struct output_options_t +{ + ~output_options_t () + { + g_free (output_file); + g_free (output_format); + if (out_fp && out_fp != stdout) + fclose (out_fp); + } + + void add_options (option_parser_t *parser, + const char **supported_formats = nullptr) + { + const char *text = nullptr; + + if (supported_formats) + { + char *items = g_strjoinv ("/", const_cast (supported_formats)); + text = g_strdup_printf ("Set output format\n\n Supported output formats are: %s", items); + g_free (items); + parser->free_later ((char *) text); + } + + GOptionEntry entries[] = + { + {"output-file", 'o', 0, G_OPTION_ARG_STRING, &this->output_file, "Set output file-name (default: stdout)","filename"}, + {"output-format", 'O', supported_formats ? 0 : G_OPTION_FLAG_HIDDEN, + G_OPTION_ARG_STRING, &this->output_format, text, "format"}, + {nullptr} + }; + parser->add_group (entries, + "output", + "Output destination & format options:", + "Options for the destination & form of the output", + this); + } + + void post_parse (GError **error) + { + if (output_format) + explicit_output_format = true; + + if (output_file && !output_format) + { + output_format = strrchr (output_file, '.'); + if (output_format) + { + output_format++; /* skip the dot */ + output_format = g_strdup (output_format); + } + } + + if (output_file && 0 != strcmp (output_file, "-")) + out_fp = fopen (output_file, "wb"); + else + { + if (!default_stdout && !output_file) + { + g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_FAILED, + "No output file was specified"); + return; + } + +#if defined(_WIN32) || defined(__CYGWIN__) + setmode (fileno (stdout), O_BINARY); +#endif + out_fp = stdout; + } + if (!out_fp) + { + g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_FAILED, + "Cannot open output file `%s': %s", + g_filename_display_name (output_file), strerror (errno)); + return; + } + } + + char *output_file = nullptr; + char *output_format = nullptr; + + bool explicit_output_format = false; + FILE *out_fp = nullptr; +}; + +#endif diff --git a/libs/harfbuzz/util/shape-consumer.hh b/libs/harfbuzz/util/shape-consumer.hh new file mode 100644 index 00000000..21151890 --- /dev/null +++ b/libs/harfbuzz/util/shape-consumer.hh @@ -0,0 +1,101 @@ +/* + * Copyright © 2011,2012 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 HB_SHAPE_CONSUMER_HH +#define HB_SHAPE_CONSUMER_HH + +#include "font-options.hh" +#include "shape-options.hh" +#include "text-options.hh" + + +template +struct shape_consumer_t : shape_options_t +{ + void add_options (option_parser_t *parser) + { + shape_options_t::add_options (parser); + output.add_options (parser); + } + + template + void init (const app_t *app) + { + failed = false; + buffer = hb_buffer_create (); + + output.init (buffer, app); + } + template + bool consume_line (app_t &app) + { + unsigned int text_len; + const char *text; + if (!(text = app.get_line (&text_len))) + return false; + + output.new_line (); + + for (unsigned int n = num_iterations; n; n--) + { + const char *error = nullptr; + + populate_buffer (buffer, text, text_len, app.text_before, app.text_after); + if (n == 1) + output.consume_text (buffer, text, text_len, utf8_clusters); + if (!shape (app.font, buffer, &error)) + { + failed = true; + output.error (error); + if (hb_buffer_get_content_type (buffer) == HB_BUFFER_CONTENT_TYPE_GLYPHS) + break; + else + return true; + } + } + + output.consume_glyphs (buffer, text, text_len, utf8_clusters); + return true; + } + template + void finish (const app_t *app) + { + output.finish (buffer, app); + hb_buffer_destroy (buffer); + buffer = nullptr; + } + + public: + bool failed = false; + + protected: + output_t output; + + hb_buffer_t *buffer = nullptr; +}; + + +#endif diff --git a/libs/harfbuzz/util/shape-format.hh b/libs/harfbuzz/util/shape-format.hh new file mode 100644 index 00000000..18b0b96e --- /dev/null +++ b/libs/harfbuzz/util/shape-format.hh @@ -0,0 +1,214 @@ +/* + * 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 SHAPE_FORMAT_OPTIONS_HH +#define SHAPE_FORMAT_OPTIONS_HH + +#include "options.hh" + + +struct shape_format_options_t +{ + void add_options (option_parser_t *parser); + + void serialize (hb_buffer_t *buffer, + hb_font_t *font, + hb_buffer_serialize_format_t format, + hb_buffer_serialize_flags_t flags, + GString *gs); + void serialize_line_no (unsigned int line_no, + GString *gs); + void serialize_buffer_of_text (hb_buffer_t *buffer, + unsigned int line_no, + const char *text, + unsigned int text_len, + hb_font_t *font, + GString *gs); + void serialize_message (unsigned int line_no, + const char *type, + const char *msg, + GString *gs); + void serialize_buffer_of_glyphs (hb_buffer_t *buffer, + unsigned int line_no, + const char *text, + unsigned int text_len, + hb_font_t *font, + hb_buffer_serialize_format_t output_format, + hb_buffer_serialize_flags_t format_flags, + GString *gs); + + + hb_bool_t show_glyph_names = true; + hb_bool_t show_positions = true; + hb_bool_t show_advances = true; + hb_bool_t show_clusters = true; + hb_bool_t show_text = false; + hb_bool_t show_unicode = false; + hb_bool_t show_line_num = false; + hb_bool_t show_extents = false; + hb_bool_t show_flags = false; + hb_bool_t trace = false; +}; + + +static gboolean +parse_verbose (const char *name G_GNUC_UNUSED, + const char *arg G_GNUC_UNUSED, + gpointer data G_GNUC_UNUSED, + GError **error G_GNUC_UNUSED) +{ + shape_format_options_t *format_opts = (shape_format_options_t *) data; + format_opts->show_text = format_opts->show_unicode = format_opts->show_line_num = true; + return true; +} + +static gboolean +parse_ned (const char *name G_GNUC_UNUSED, + const char *arg G_GNUC_UNUSED, + gpointer data G_GNUC_UNUSED, + GError **error G_GNUC_UNUSED) +{ + shape_format_options_t *format_opts = (shape_format_options_t *) data; + format_opts->show_clusters = format_opts->show_advances = false; + return true; +} + +inline void +shape_format_options_t::serialize (hb_buffer_t *buffer, + hb_font_t *font, + hb_buffer_serialize_format_t output_format, + hb_buffer_serialize_flags_t flags, + GString *gs) +{ + unsigned int num_glyphs = hb_buffer_get_length (buffer); + unsigned int start = 0; + + while (start < num_glyphs) + { + char buf[32768]; + unsigned int consumed; + start += hb_buffer_serialize (buffer, start, num_glyphs, + buf, sizeof (buf), &consumed, + font, output_format, flags); + if (!consumed) + break; + g_string_append (gs, buf); + } +} + +inline void +shape_format_options_t::serialize_line_no (unsigned int line_no, + GString *gs) +{ + if (show_line_num) + g_string_append_printf (gs, "%d: ", line_no); +} +inline void +shape_format_options_t::serialize_buffer_of_text (hb_buffer_t *buffer, + unsigned int line_no, + const char *text, + unsigned int text_len, + hb_font_t *font, + GString *gs) +{ + if (show_text) + { + serialize_line_no (line_no, gs); + g_string_append_c (gs, '('); + g_string_append_len (gs, text, text_len); + g_string_append_c (gs, ')'); + g_string_append_c (gs, '\n'); + } + + if (show_unicode) + { + serialize_line_no (line_no, gs); + serialize (buffer, font, HB_BUFFER_SERIALIZE_FORMAT_TEXT, HB_BUFFER_SERIALIZE_FLAG_DEFAULT, gs); + g_string_append_c (gs, '\n'); + } +} +inline void +shape_format_options_t::serialize_message (unsigned int line_no, + const char *type, + const char *msg, + GString *gs) +{ + serialize_line_no (line_no, gs); + g_string_append_printf (gs, "%s: %s", type, msg); + g_string_append_c (gs, '\n'); +} +inline void +shape_format_options_t::serialize_buffer_of_glyphs (hb_buffer_t *buffer, + unsigned int line_no, + const char *text, + unsigned int text_len, + hb_font_t *font, + hb_buffer_serialize_format_t output_format, + hb_buffer_serialize_flags_t format_flags, + GString *gs) +{ + serialize_line_no (line_no, gs); + serialize (buffer, font, output_format, format_flags, gs); + g_string_append_c (gs, '\n'); +} + + +void +shape_format_options_t::add_options (option_parser_t *parser) +{ + GOptionEntry entries[] = + { + {"show-text", 0, 0, G_OPTION_ARG_NONE, &this->show_text, "Prefix each line of output with its corresponding input text", nullptr}, + {"show-unicode", 0, 0, G_OPTION_ARG_NONE, &this->show_unicode, "Prefix each line of output with its corresponding input codepoint(s)", nullptr}, + {"show-line-num", 0, 0, G_OPTION_ARG_NONE, &this->show_line_num, "Prefix each line of output with its corresponding input line number", nullptr}, + {"verbose", 'v', G_OPTION_FLAG_NO_ARG, + G_OPTION_ARG_CALLBACK, (gpointer) &parse_verbose, "Prefix each line of output with all of the above", nullptr}, + {"no-glyph-names", 0, G_OPTION_FLAG_REVERSE, + G_OPTION_ARG_NONE, &this->show_glyph_names, "Output glyph indices instead of names", nullptr}, + {"no-positions", 0, G_OPTION_FLAG_REVERSE, + G_OPTION_ARG_NONE, &this->show_positions, "Do not output glyph positions", nullptr}, + {"no-advances", 0, G_OPTION_FLAG_REVERSE, + G_OPTION_ARG_NONE, &this->show_advances, "Do not output glyph advances", nullptr}, + {"no-clusters", 0, G_OPTION_FLAG_REVERSE, + G_OPTION_ARG_NONE, &this->show_clusters, "Do not output cluster indices", nullptr}, + {"show-extents", 0, 0, G_OPTION_ARG_NONE, &this->show_extents, "Output glyph extents", nullptr}, + {"show-flags", 0, 0, G_OPTION_ARG_NONE, &this->show_flags, "Output glyph flags", nullptr}, + {"ned", 'v', G_OPTION_FLAG_NO_ARG, + G_OPTION_ARG_CALLBACK, (gpointer) &parse_ned, "No Extra Data; Do not output clusters or advances", nullptr}, + {"trace", 'V', 0, G_OPTION_ARG_NONE, &this->trace, "Output interim shaping results", nullptr}, + {nullptr} + }; + parser->add_group (entries, + "output-syntax", + "Output syntax:\n" + " text: [=@,+,|...]\n" + " json: [{\"g\": , \"ax\": , \"ay\": , \"dx\": , \"dy\": , \"cl\": }, ...]\n" + "\nOutput syntax options:", + "Options for the syntax of the output", + this); +} + +#endif diff --git a/libs/harfbuzz/util/shape-options.hh b/libs/harfbuzz/util/shape-options.hh new file mode 100644 index 00000000..e24f4f8e --- /dev/null +++ b/libs/harfbuzz/util/shape-options.hh @@ -0,0 +1,329 @@ +/* + * 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 SHAPE_OPTIONS_HH +#define SHAPE_OPTIONS_HH + +#include "options.hh" + +struct shape_options_t +{ + ~shape_options_t () + { + g_free (direction); + g_free (language); + g_free (script); + free (features); + g_strfreev (shapers); + } + + void add_options (option_parser_t *parser); + + void setup_buffer (hb_buffer_t *buffer) + { + hb_buffer_set_direction (buffer, hb_direction_from_string (direction, -1)); + hb_buffer_set_script (buffer, hb_script_from_string (script, -1)); + hb_buffer_set_language (buffer, hb_language_from_string (language, -1)); + hb_buffer_set_flags (buffer, (hb_buffer_flags_t) + (HB_BUFFER_FLAG_DEFAULT | + (bot ? HB_BUFFER_FLAG_BOT : 0) | + (eot ? HB_BUFFER_FLAG_EOT : 0) | + (verify ? HB_BUFFER_FLAG_VERIFY : 0) | + (unsafe_to_concat ? HB_BUFFER_FLAG_PRODUCE_UNSAFE_TO_CONCAT : 0) | + (safe_to_insert_tatweel ? HB_BUFFER_FLAG_PRODUCE_SAFE_TO_INSERT_TATWEEL : 0) | + (preserve_default_ignorables ? HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES : 0) | + (remove_default_ignorables ? HB_BUFFER_FLAG_REMOVE_DEFAULT_IGNORABLES : 0) | + 0)); + hb_buffer_set_invisible_glyph (buffer, invisible_glyph); + hb_buffer_set_not_found_glyph (buffer, not_found_glyph); + hb_buffer_set_cluster_level (buffer, cluster_level); + hb_buffer_guess_segment_properties (buffer); + } + + void populate_buffer (hb_buffer_t *buffer, const char *text, int text_len, + const char *text_before, const char *text_after) + { + hb_buffer_clear_contents (buffer); + if (text_before) { + unsigned int len = strlen (text_before); + hb_buffer_add_utf8 (buffer, text_before, len, len, 0); + } + hb_buffer_add_utf8 (buffer, text, text_len, 0, text_len); + if (text_after) { + hb_buffer_add_utf8 (buffer, text_after, -1, 0, 0); + } + + if (!utf8_clusters) { + /* Reset cluster values to refer to Unicode character index + * instead of UTF-8 index. */ + unsigned int num_glyphs = hb_buffer_get_length (buffer); + hb_glyph_info_t *info = hb_buffer_get_glyph_infos (buffer, nullptr); + for (unsigned int i = 0; i < num_glyphs; i++) + { + info->cluster = i; + info++; + } + } + + setup_buffer (buffer); + } + + hb_bool_t shape (hb_font_t *font, hb_buffer_t *buffer, const char **error=nullptr) + { + if (!hb_shape_full (font, buffer, features, num_features, shapers)) + { + if (error) + *error = "Shaping failed."; + goto fail; + } + + if (normalize_glyphs) + hb_buffer_normalize_glyphs (buffer); + + return true; + + fail: + return false; + } + + void shape_closure (const char *text, int text_len, + hb_font_t *font, hb_buffer_t *buffer, + hb_set_t *glyphs) + { + hb_buffer_reset (buffer); + hb_buffer_add_utf8 (buffer, text, text_len, 0, text_len); + setup_buffer (buffer); + hb_ot_shape_glyphs_closure (font, buffer, features, num_features, glyphs); + } + + /* Buffer properties */ + char *direction = nullptr; + char *language = nullptr; + char *script = nullptr; + + /* Buffer flags */ + hb_bool_t bot = false; + hb_bool_t eot = false; + hb_bool_t preserve_default_ignorables = false; + hb_bool_t remove_default_ignorables = false; + + hb_feature_t *features = nullptr; + unsigned int num_features = 0; + char **shapers = nullptr; + hb_bool_t utf8_clusters = false; + hb_codepoint_t invisible_glyph = 0; + hb_codepoint_t not_found_glyph = 0; + hb_buffer_cluster_level_t cluster_level = HB_BUFFER_CLUSTER_LEVEL_DEFAULT; + hb_bool_t normalize_glyphs = false; + hb_bool_t verify = false; + hb_bool_t unsafe_to_concat = false; + hb_bool_t safe_to_insert_tatweel = false; + unsigned int num_iterations = 1; +}; + + +static gboolean +parse_shapers (const char *name G_GNUC_UNUSED, + const char *arg, + gpointer data, + GError **error) +{ + shape_options_t *shape_opts = (shape_options_t *) data; + char **shapers = g_strsplit (arg, ",", 0); + + for (char **shaper = shapers; *shaper; shaper++) + { + bool found = false; + for (const char **hb_shaper = hb_shape_list_shapers (); *hb_shaper; hb_shaper++) { + if (strcmp (*shaper, *hb_shaper) == 0) + { + found = true; + break; + } + } + if (!found) + { + g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, + "Unknown or unsupported shaper: %s", *shaper); + g_strfreev (shapers); + return false; + } + } + + g_strfreev (shape_opts->shapers); + shape_opts->shapers = shapers; + return true; +} + +static G_GNUC_NORETURN gboolean +list_shapers (const char *name G_GNUC_UNUSED, + const char *arg G_GNUC_UNUSED, + gpointer data G_GNUC_UNUSED, + GError **error G_GNUC_UNUSED) +{ + for (const char **shaper = hb_shape_list_shapers (); *shaper; shaper++) + g_printf ("%s\n", *shaper); + + exit(0); +} + + +static gboolean +parse_features (const char *name G_GNUC_UNUSED, + const char *arg, + gpointer data, + GError **error G_GNUC_UNUSED) +{ + shape_options_t *shape_opts = (shape_options_t *) data; + char *s = (char *) arg; + size_t l = strlen (s); + char *p; + + shape_opts->num_features = 0; + g_free (shape_opts->features); + shape_opts->features = nullptr; + + /* if the string is quoted, strip the quotes */ + if (s[0] == s[l - 1] && (s[0] == '\"' || s[0] == '\'')) + { + s[l - 1] = '\0'; + s++; + } + + if (!*s) + return true; + + /* count the features first, so we can allocate memory */ + p = s; + do { + shape_opts->num_features++; + p = strpbrk (p, ", "); + if (p) + p++; + } while (p); + + shape_opts->features = (hb_feature_t *) calloc (shape_opts->num_features, sizeof (*shape_opts->features)); + if (!shape_opts->features) + return false; + + /* now do the actual parsing */ + p = s; + shape_opts->num_features = 0; + while (p && *p) { + char *end = strpbrk (p, ", "); + if (hb_feature_from_string (p, end ? end - p : -1, &shape_opts->features[shape_opts->num_features])) + shape_opts->num_features++; + p = end ? end + 1 : nullptr; + } + + return true; +} + +void +shape_options_t::add_options (option_parser_t *parser) +{ + GOptionEntry entries[] = + { + {"list-shapers", 0, G_OPTION_FLAG_NO_ARG, + G_OPTION_ARG_CALLBACK, (gpointer) &list_shapers, "List available shapers and quit", nullptr}, + {"shaper", 0, G_OPTION_FLAG_HIDDEN, + G_OPTION_ARG_CALLBACK, (gpointer) &parse_shapers, "Hidden duplicate of --shapers", nullptr}, + {"shapers", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_shapers, "Set comma-separated list of shapers to try","list"}, + {"direction", 0, 0, G_OPTION_ARG_STRING, &this->direction, "Set text direction (default: auto)", "ltr/rtl/ttb/btt"}, + {"language", 0, 0, G_OPTION_ARG_STRING, &this->language, "Set text language (default: $LANG)", "BCP 47 tag"}, + {"script", 0, 0, G_OPTION_ARG_STRING, &this->script, "Set text script (default: auto)", "ISO-15924 tag"}, + {"bot", 0, 0, G_OPTION_ARG_NONE, &this->bot, "Treat text as beginning-of-paragraph", nullptr}, + {"eot", 0, 0, G_OPTION_ARG_NONE, &this->eot, "Treat text as end-of-paragraph", nullptr}, + {"preserve-default-ignorables",0, 0, G_OPTION_ARG_NONE, &this->preserve_default_ignorables, "Preserve Default-Ignorable characters", nullptr}, + {"remove-default-ignorables",0, 0, G_OPTION_ARG_NONE, &this->remove_default_ignorables, "Remove Default-Ignorable characters", nullptr}, + {"invisible-glyph", 0, 0, G_OPTION_ARG_INT, &this->invisible_glyph, "Glyph value to replace Default-Ignorables with", nullptr}, + {"not-found-glyph", 0, 0, G_OPTION_ARG_INT, &this->not_found_glyph, "Glyph value to replace not-found characters with", nullptr}, + {"utf8-clusters", 0, 0, G_OPTION_ARG_NONE, &this->utf8_clusters, "Use UTF8 byte indices, not char indices", nullptr}, + {"cluster-level", 0, 0, G_OPTION_ARG_INT, &this->cluster_level, "Cluster merging level (default: 0)", "0/1/2"}, + {"normalize-glyphs",0, 0, G_OPTION_ARG_NONE, &this->normalize_glyphs, "Rearrange glyph clusters in nominal order", nullptr}, + {"unsafe-to-concat",0, 0, G_OPTION_ARG_NONE, &this->unsafe_to_concat, "Produce unsafe-to-concat glyph flag", nullptr}, + {"safe-to-insert-tatweel",0, 0, G_OPTION_ARG_NONE, &this->safe_to_insert_tatweel, "Produce safe-to-insert-tatweel glyph flag", nullptr}, + {"verify", 0, 0, G_OPTION_ARG_NONE, &this->verify, "Perform sanity checks on shaping results", nullptr}, + {"num-iterations", 'n', G_OPTION_FLAG_IN_MAIN, + G_OPTION_ARG_INT, &this->num_iterations, "Run shaper N times (default: 1)", "N"}, + {nullptr} + }; + parser->add_group (entries, + "shape", + "Shape options:", + "Options for the shaping process", + this); + + const gchar *features_help = "Comma-separated list of font features\n" + "\n" + " Features can be enabled or disabled, either globally or limited to\n" + " specific character ranges. The format for specifying feature settings\n" + " follows. All valid CSS font-feature-settings values other than 'normal'\n" + " and the global values are also accepted, though not documented below.\n" + " CSS string escapes are not supported." + "\n" + " The range indices refer to the positions between Unicode characters,\n" + " unless the --utf8-clusters is provided, in which case range indices\n" + " refer to UTF-8 byte indices. The position before the first character\n" + " is always 0.\n" + "\n" + " The format is Python-esque. Here is how it all works:\n" + "\n" + " Syntax: Value: Start: End:\n" + "\n" + " Setting value:\n" + " \"kern\" 1 0 ∞ # Turn feature on\n" + " \"+kern\" 1 0 ∞ # Turn feature on\n" + " \"-kern\" 0 0 ∞ # Turn feature off\n" + " \"kern=0\" 0 0 ∞ # Turn feature off\n" + " \"kern=1\" 1 0 ∞ # Turn feature on\n" + " \"aalt=2\" 2 0 ∞ # Choose 2nd alternate\n" + "\n" + " Setting index:\n" + " \"kern[]\" 1 0 ∞ # Turn feature on\n" + " \"kern[:]\" 1 0 ∞ # Turn feature on\n" + " \"kern[5:]\" 1 5 ∞ # Turn feature on, partial\n" + " \"kern[:5]\" 1 0 5 # Turn feature on, partial\n" + " \"kern[3:5]\" 1 3 5 # Turn feature on, range\n" + " \"kern[3]\" 1 3 3+1 # Turn feature on, single char\n" + "\n" + " Mixing it all:\n" + "\n" + " \"aalt[3:5]=2\" 2 3 5 # Turn 2nd alternate on for range"; + + GOptionEntry entries2[] = + { + {"features", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_features, features_help, "list"}, + {nullptr} + }; + parser->add_group (entries2, + "features", + "Features options:", + "Options for font features used", + this); +} + +#endif diff --git a/libs/harfbuzz/util/shape-output.hh b/libs/harfbuzz/util/shape-output.hh new file mode 100644 index 00000000..8d208050 --- /dev/null +++ b/libs/harfbuzz/util/shape-output.hh @@ -0,0 +1,159 @@ +/* + * Copyright © 2010 Behdad Esfahbod + * Copyright © 2011,2012 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 HB_SHAPE_OUTPUT_HH +#define HB_SHAPE_OUTPUT_HH + +#include "shape-format.hh" +#include "output-options.hh" + + +struct shape_output_t : output_options_t<> +{ + void add_options (option_parser_t *parser) + { + parser->set_summary ("Shape text with given font."); + output_options_t::add_options (parser, hb_buffer_serialize_list_formats ()); + format.add_options (parser); + } + + void init (hb_buffer_t *buffer, const font_options_t *font_opts) + { + gs = g_string_new (nullptr); + line_no = 0; + font = hb_font_reference (font_opts->font); + + if (!output_format) + serialize_format = HB_BUFFER_SERIALIZE_FORMAT_TEXT; + else + serialize_format = hb_buffer_serialize_format_from_string (output_format, -1); + /* An empty "output_format" parameter basically skips output generating. + * Useful for benchmarking. */ + if ((!output_format || *output_format) && + !hb_buffer_serialize_format_to_string (serialize_format)) + { + if (explicit_output_format) + fail (false, "Unknown output format `%s'; supported formats are: %s", + output_format, + g_strjoinv ("/", const_cast (hb_buffer_serialize_list_formats ()))); + else + /* Just default to TEXT if not explicitly requested and the + * file extension is not recognized. */ + serialize_format = HB_BUFFER_SERIALIZE_FORMAT_TEXT; + } + + unsigned int flags = HB_BUFFER_SERIALIZE_FLAG_DEFAULT; + if (!format.show_glyph_names) + flags |= HB_BUFFER_SERIALIZE_FLAG_NO_GLYPH_NAMES; + if (!format.show_clusters) + flags |= HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS; + if (!format.show_positions) + flags |= HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS; + if (!format.show_advances) + flags |= HB_BUFFER_SERIALIZE_FLAG_NO_ADVANCES; + if (format.show_extents) + flags |= HB_BUFFER_SERIALIZE_FLAG_GLYPH_EXTENTS; + if (format.show_flags) + flags |= HB_BUFFER_SERIALIZE_FLAG_GLYPH_FLAGS; + serialize_flags = (hb_buffer_serialize_flags_t) flags; + + if (format.trace) + hb_buffer_set_message_func (buffer, message_func, this, nullptr); + } + void new_line () { line_no++; } + void consume_text (hb_buffer_t *buffer, + const char *text, + unsigned int text_len, + hb_bool_t utf8_clusters) + { + g_string_set_size (gs, 0); + format.serialize_buffer_of_text (buffer, line_no, text, text_len, font, gs); + fprintf (out_fp, "%s", gs->str); + } + void error (const char *message) + { + g_string_set_size (gs, 0); + format.serialize_message (line_no, "error", message, gs); + fprintf (out_fp, "%s", gs->str); + } + void consume_glyphs (hb_buffer_t *buffer, + const char *text, + unsigned int text_len, + hb_bool_t utf8_clusters) + { + g_string_set_size (gs, 0); + format.serialize_buffer_of_glyphs (buffer, line_no, text, text_len, font, + serialize_format, serialize_flags, gs); + fprintf (out_fp, "%s", gs->str); + } + void finish (hb_buffer_t *buffer, const font_options_t *font_opts) + { + hb_buffer_set_message_func (buffer, nullptr, nullptr, nullptr); + hb_font_destroy (font); + g_string_free (gs, true); + gs = nullptr; + font = nullptr; + } + + static hb_bool_t + message_func (hb_buffer_t *buffer, + hb_font_t *font, + const char *message, + void *user_data) + { + shape_output_t *that = (shape_output_t *) user_data; + that->trace (buffer, font, message); + return true; + } + + void + trace (hb_buffer_t *buffer, + hb_font_t *font, + const char *message) + { + g_string_set_size (gs, 0); + format.serialize_line_no (line_no, gs); + g_string_append_printf (gs, "trace: %s buffer: ", message); + format.serialize (buffer, font, serialize_format, serialize_flags, gs); + g_string_append_c (gs, '\n'); + fprintf (out_fp, "%s", gs->str); + } + + + protected: + + shape_format_options_t format; + + GString *gs = nullptr; + unsigned int line_no = 0; + hb_font_t *font = nullptr; + hb_buffer_serialize_format_t serialize_format = HB_BUFFER_SERIALIZE_FORMAT_INVALID; + hb_buffer_serialize_flags_t serialize_flags = HB_BUFFER_SERIALIZE_FLAG_DEFAULT; +}; + + +#endif diff --git a/libs/harfbuzz/util/text-options.hh b/libs/harfbuzz/util/text-options.hh new file mode 100644 index 00000000..3043efc4 --- /dev/null +++ b/libs/harfbuzz/util/text-options.hh @@ -0,0 +1,370 @@ +/* + * 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 TEXT_OPTIONS_HH +#define TEXT_OPTIONS_HH + +#include "options.hh" + +struct text_options_t +{ + text_options_t () + : gs (g_string_new (nullptr)) + {} + ~text_options_t () + { + g_free (text); + g_free (text_file); + if (gs) + g_string_free (gs, true); + if (in_fp && in_fp != stdin) + fclose (in_fp); + } + + void add_options (option_parser_t *parser); + + void post_parse (GError **error G_GNUC_UNUSED) + { + if (!text && !text_file) + text_file = g_strdup ("-"); + + if (text && text_file) + { + g_set_error (error, + G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, + "Only one of text and text-file can be set"); + return; + } + + if (text_file) + { + if (0 != strcmp (text_file, "-")) + in_fp = fopen (text_file, "r"); + else + in_fp = stdin; + + if (!in_fp) + g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_FAILED, + "Failed opening text file `%s': %s", + text_file, strerror (errno)); + } + } + + const char *get_line (unsigned int *len); + + int text_len = -1; + char *text = nullptr; + char *text_file = nullptr; + + private: + FILE *in_fp = nullptr; + GString *gs = nullptr; + char *line = nullptr; + unsigned line_len = UINT_MAX; + hb_bool_t single_par = false; +}; + +struct shape_text_options_t : text_options_t +{ + ~shape_text_options_t () + { + g_free (text_before); + g_free (text_after); + } + + void add_options (option_parser_t *parser); + + char *text_before = nullptr; + char *text_after = nullptr; +}; + + +static gboolean +parse_text (const char *name G_GNUC_UNUSED, + const char *arg, + gpointer data, + GError **error G_GNUC_UNUSED) +{ + text_options_t *text_opts = (text_options_t *) data; + + if (text_opts->text) + { + g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, + "Either --text or --unicodes can be provided but not both"); + return false; + } + + text_opts->text_len = -1; + text_opts->text = g_strdup (arg); + return true; +} + +static bool +encode_unicodes (const char *unicodes, + GString *gs, + GError **error) +{ +#define DELIMITERS "<+-|>{},;&#\\xXuUnNiI\n\t\v\f\r " + + char *s = (char *) unicodes; + char *p; + + while (s && *s) + { + while (*s && strchr (DELIMITERS, *s)) + s++; + if (!*s) + break; + + errno = 0; + hb_codepoint_t u = strtoul (s, &p, 16); + if (errno || s == p) + { + g_string_free (gs, TRUE); + g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, + "Failed parsing Unicode value at: '%s'", s); + return false; + } + + g_string_append_unichar (gs, u); + + s = p; + } + +#undef DELIMITERS + + return true; +} + +static gboolean +parse_unicodes (const char *name G_GNUC_UNUSED, + const char *arg, + gpointer data, + GError **error G_GNUC_UNUSED) +{ + text_options_t *text_opts = (text_options_t *) data; + + if (text_opts->text) + { + g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, + "Either --text or --unicodes can be provided but not both"); + return false; + } + + GString *gs = g_string_new (nullptr); + if (0 == strcmp (arg, "*")) + g_string_append_c (gs, '*'); + else + if (!encode_unicodes (arg, gs, error)) + return false; + + text_opts->text_len = gs->len; + text_opts->text = g_string_free (gs, FALSE); + return true; +} + +static gboolean +parse_text_before (const char *name G_GNUC_UNUSED, + const char *arg, + gpointer data, + GError **error) +{ + auto *opts = (shape_text_options_t *) data; + + if (opts->text_before) + { + g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, + "Either --text-before or --unicodes-before can be provided but not both"); + return false; + } + + opts->text_before = g_strdup (arg); + fprintf(stderr, "%s\n", opts->text_before); + return true; +} + +static gboolean +parse_unicodes_before (const char *name G_GNUC_UNUSED, + const char *arg, + gpointer data, + GError **error) +{ + auto *opts = (shape_text_options_t *) data; + + if (opts->text_before) + { + g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, + "Either --text-before or --unicodes-before can be provided but not both"); + return false; + } + + GString *gs = g_string_new (nullptr); + if (!encode_unicodes (arg, gs, error)) + return false; + + opts->text_before = g_string_free (gs, FALSE); + return true; +} + +static gboolean +parse_text_after (const char *name G_GNUC_UNUSED, + const char *arg, + gpointer data, + GError **error) +{ + auto *opts = (shape_text_options_t *) data; + + if (opts->text_after) + { + g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, + "Either --text-after or --unicodes-after can be provided but not both"); + return false; + } + + opts->text_after = g_strdup (arg); + return true; +} + +static gboolean +parse_unicodes_after (const char *name G_GNUC_UNUSED, + const char *arg, + gpointer data, + GError **error) +{ + auto *opts = (shape_text_options_t *) data; + + if (opts->text_after) + { + g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, + "Either --text-after or --unicodes-after can be provided but not both"); + return false; + } + + GString *gs = g_string_new (nullptr); + if (!encode_unicodes (arg, gs, error)) + return false; + + opts->text_after = g_string_free (gs, FALSE); + return true; +} + +const char * +text_options_t::get_line (unsigned int *len) +{ + if (text) + { + if (!line) + { + line = text; + line_len = text_len; + } + if (line_len == UINT_MAX) + line_len = strlen (line); + + if (!line_len) + { + *len = 0; + return nullptr; + } + + const char *ret = line; + const char *p = single_par ? nullptr : (const char *) memchr (line, '\n', line_len); + unsigned int ret_len; + if (!p) + { + ret_len = line_len; + line += ret_len; + line_len = 0; + } + else + { + ret_len = p - ret; + line += ret_len + 1; + line_len -= ret_len + 1; + } + + *len = ret_len; + return ret; + } + + g_string_set_size (gs, 0); + char buf[BUFSIZ]; + while (fgets (buf, sizeof (buf), in_fp)) + { + unsigned bytes = strlen (buf); + if (!single_par && bytes && buf[bytes - 1] == '\n') + { + bytes--; + g_string_append_len (gs, buf, bytes); + break; + } + g_string_append_len (gs, buf, bytes); + } + if (ferror (in_fp)) + fail (false, "Failed reading text: %s", strerror (errno)); + *len = gs->len; + return !*len && feof (in_fp) ? nullptr : gs->str; +} + +void +text_options_t::add_options (option_parser_t *parser) +{ + GOptionEntry entries[] = + { + {"text", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_text, "Set input text", "string"}, + {"text-file", 0, 0, G_OPTION_ARG_STRING, &this->text_file, "Set input text file-name", "filename"}, + {"unicodes", 'u', 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_unicodes, "Set input Unicode codepoints", "list of hex numbers"}, + {"single-par", 0, 0, G_OPTION_ARG_NONE, &this->single_par, "Treat text as single paragraph", nullptr}, + {nullptr} + }; + parser->add_group (entries, + "text", + "Text options:\n\nIf no text is provided, standard input is used for input.\n", + "Options for the input text", + this); +} + +void +shape_text_options_t::add_options (option_parser_t *parser) +{ + text_options_t::add_options (parser); + + GOptionEntry entries[] = + { + {"text-before", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_text_before, "Set text context before each line", "string"}, + {"text-after", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_text_after, "Set text context after each line", "string"}, + {"unicodes-before", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_unicodes_before, "Set Unicode codepoints context before each line", "list of hex numbers"}, + {"unicodes-after", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_unicodes_after, "Set Unicode codepoints context after each line", "list of hex numbers"}, + {nullptr} + }; + parser->add_group (entries, + "text-context", + "Textual context options:", + "Options for the input context text", + this); +} + +#endif diff --git a/libs/harfbuzz/util/view-cairo.hh b/libs/harfbuzz/util/view-cairo.hh new file mode 100644 index 00000000..1578f13e --- /dev/null +++ b/libs/harfbuzz/util/view-cairo.hh @@ -0,0 +1,206 @@ +/* + * 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 VIEW_CAIRO_HH +#define VIEW_CAIRO_HH + +#include "view-options.hh" +#include "output-options.hh" +#include "helper-cairo.hh" + +struct view_cairo_t : view_options_t, output_options_t<> +{ + ~view_cairo_t () + { + cairo_debug_reset_static_data (); + } + + void add_options (option_parser_t *parser) + { + parser->set_summary ("View text with given font."); + view_options_t::add_options (parser); + output_options_t::add_options (parser, helper_cairo_supported_formats); + } + + void init (hb_buffer_t *buffer, const font_options_t *font_opts) + { + lines = g_array_new (false, false, sizeof (helper_cairo_line_t)); + scale_bits = - (int) font_opts->subpixel_bits; + } + void new_line () {} + void consume_text (hb_buffer_t *buffer, + const char *text, + unsigned int text_len, + hb_bool_t utf8_clusters) {} + void error (const char *message) + { g_printerr ("%s: %s\n", g_get_prgname (), message); } + void consume_glyphs (hb_buffer_t *buffer, + const char *text, + unsigned int text_len, + hb_bool_t utf8_clusters) + { + direction = hb_buffer_get_direction (buffer); + helper_cairo_line_t l; + helper_cairo_line_from_buffer (&l, buffer, text, text_len, scale_bits, utf8_clusters); + g_array_append_val (lines, l); + } + void finish (hb_buffer_t *buffer, const font_options_t *font_opts) + { + render (font_opts); + + for (unsigned int i = 0; i < lines->len; i++) { + helper_cairo_line_t &line = g_array_index (lines, helper_cairo_line_t, i); + line.finish (); + } +#if GLIB_CHECK_VERSION (2, 22, 0) + g_array_unref (lines); +#else + g_array_free (lines, TRUE); +#endif + } + + protected: + + void render (const font_options_t *font_opts); + + hb_direction_t direction = HB_DIRECTION_INVALID; // Remove this, make segment_properties accessible + GArray *lines = nullptr; + int scale_bits = 0; +}; + +inline void +view_cairo_t::render (const font_options_t *font_opts) +{ + bool vertical = HB_DIRECTION_IS_VERTICAL (direction); + int vert = vertical ? 1 : 0; + int horiz = vertical ? 0 : 1; + + int x_sign = font_opts->font_size_x < 0 ? -1 : +1; + int y_sign = font_opts->font_size_y < 0 ? -1 : +1; + + hb_font_t *font = font_opts->font; + + if (!have_font_extents) + { + hb_font_extents_t hb_extents; + hb_font_get_extents_for_direction (font, direction, &hb_extents); + font_extents.ascent = scalbn ((double) hb_extents.ascender, scale_bits); + font_extents.descent = -scalbn ((double) hb_extents.descender, scale_bits); + font_extents.line_gap = scalbn ((double) hb_extents.line_gap, scale_bits); + have_font_extents = true; + } + + double ascent = y_sign * font_extents.ascent; + double descent = y_sign * font_extents.descent; + double line_gap = y_sign * font_extents.line_gap + line_space; + double leading = ascent + descent + line_gap; + + /* Calculate surface size. */ + double w = 0, h = 0; + (vertical ? w : h) = (int) lines->len * leading - (font_extents.line_gap + line_space); + (vertical ? h : w) = 0; + for (unsigned int i = 0; i < lines->len; i++) { + helper_cairo_line_t &line = g_array_index (lines, helper_cairo_line_t, i); + double x_advance, y_advance; + line.get_advance (&x_advance, &y_advance); + if (vertical) + h = MAX (h, y_sign * y_advance); + else + w = MAX (w, x_sign * x_advance); + } + + cairo_scaled_font_t *scaled_font = helper_cairo_create_scaled_font (font_opts); + + /* See if font needs color. */ + cairo_content_t content = CAIRO_CONTENT_ALPHA; + if (helper_cairo_scaled_font_has_color (scaled_font)) + content = CAIRO_CONTENT_COLOR; + + /* Create surface. */ + cairo_t *cr = helper_cairo_create_context (w + margin.l + margin.r, + h + margin.t + margin.b, + this, + this, + content); + cairo_set_scaled_font (cr, scaled_font); + + /* Setup coordinate system. */ + cairo_translate (cr, margin.l, margin.t); + if (vertical) + cairo_translate (cr, + w - ascent, /* We currently always stack lines right to left */ + y_sign < 0 ? h : 0); + else + { + cairo_translate (cr, + x_sign < 0 ? w : 0, + y_sign < 0 ? descent : ascent); + } + + /* Draw. */ + cairo_translate (cr, +vert * leading, -horiz * leading); + for (unsigned int i = 0; i < lines->len; i++) + { + helper_cairo_line_t &l = g_array_index (lines, helper_cairo_line_t, i); + + cairo_translate (cr, -vert * leading, +horiz * leading); + + if (annotate) { + cairo_save (cr); + + /* Draw actual glyph origins */ + cairo_set_source_rgba (cr, 1., 0., 0., .5); + cairo_set_line_width (cr, 5); + cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND); + for (unsigned i = 0; i < l.num_glyphs; i++) { + cairo_move_to (cr, l.glyphs[i].x, l.glyphs[i].y); + cairo_rel_line_to (cr, 0, 0); + } + cairo_stroke (cr); + + cairo_restore (cr); + } + + if (0 && cairo_surface_get_type (cairo_get_target (cr)) == CAIRO_SURFACE_TYPE_IMAGE) { + /* cairo_show_glyphs() doesn't support subpixel positioning */ + cairo_glyph_path (cr, l.glyphs, l.num_glyphs); + cairo_fill (cr); + } else if (l.num_clusters) + cairo_show_text_glyphs (cr, + l.utf8, l.utf8_len, + l.glyphs, l.num_glyphs, + l.clusters, l.num_clusters, + l.cluster_flags); + else + cairo_show_glyphs (cr, l.glyphs, l.num_glyphs); + } + + /* Clean up. */ + helper_cairo_destroy_context (cr); + cairo_scaled_font_destroy (scaled_font); +} + +#endif diff --git a/libs/harfbuzz/util/view-options.hh b/libs/harfbuzz/util/view-options.hh new file mode 100644 index 00000000..322009a1 --- /dev/null +++ b/libs/harfbuzz/util/view-options.hh @@ -0,0 +1,123 @@ +/* + * 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 VIEW_OPTIONS_HH +#define VIEW_OPTIONS_HH + +#include "options.hh" + +#define DEFAULT_MARGIN 16 +#define DEFAULT_FORE "#000000" +#define DEFAULT_BACK "#FFFFFF" + +struct view_options_t +{ + ~view_options_t () + { + g_free (fore); + g_free (back); + } + + void add_options (option_parser_t *parser); + + hb_bool_t annotate = false; + char *fore = nullptr; + char *back = nullptr; + double line_space = 0; + bool have_font_extents = false; + struct font_extents_t { + double ascent, descent, line_gap; + } font_extents = {0., 0., 0.}; + struct margin_t { + double t, r, b, l; + } margin = {DEFAULT_MARGIN, DEFAULT_MARGIN, DEFAULT_MARGIN, DEFAULT_MARGIN}; +}; + + +static gboolean +parse_font_extents (const char *name G_GNUC_UNUSED, + const char *arg, + gpointer data, + GError **error G_GNUC_UNUSED) +{ + view_options_t *view_opts = (view_options_t *) data; + view_options_t::font_extents_t &e = view_opts->font_extents; + switch (sscanf (arg, "%lf%*[ ,]%lf%*[ ,]%lf", &e.ascent, &e.descent, &e.line_gap)) { + case 1: HB_FALLTHROUGH; + case 2: HB_FALLTHROUGH; + case 3: + view_opts->have_font_extents = true; + return true; + default: + g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, + "%s argument should be one to three space-separated numbers", + name); + return false; + } +} + +static gboolean +parse_margin (const char *name G_GNUC_UNUSED, + const char *arg, + gpointer data, + GError **error G_GNUC_UNUSED) +{ + view_options_t *view_opts = (view_options_t *) data; + view_options_t::margin_t &m = view_opts->margin; + switch (sscanf (arg, "%lf%*[ ,]%lf%*[ ,]%lf%*[ ,]%lf", &m.t, &m.r, &m.b, &m.l)) { + case 1: m.r = m.t; HB_FALLTHROUGH; + case 2: m.b = m.t; HB_FALLTHROUGH; + case 3: m.l = m.r; HB_FALLTHROUGH; + case 4: return true; + default: + g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, + "%s argument should be one to four space-separated numbers", + name); + return false; + } +} + +void +view_options_t::add_options (option_parser_t *parser) +{ + GOptionEntry entries[] = + { + {"annotate", 0, 0, G_OPTION_ARG_NONE, &this->annotate, "Annotate output rendering", nullptr}, + {"background", 0, 0, G_OPTION_ARG_STRING, &this->back, "Set background color (default: " DEFAULT_BACK ")", "rrggbb/rrggbbaa"}, + {"foreground", 0, 0, G_OPTION_ARG_STRING, &this->fore, "Set foreground color (default: " DEFAULT_FORE ")", "rrggbb/rrggbbaa"}, + {"line-space", 0, 0, G_OPTION_ARG_DOUBLE, &this->line_space, "Set space between lines (default: 0)", "units"}, + {"font-extents", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_font_extents, "Set font ascent/descent/line-gap (default: auto)","one to three numbers"}, + {"margin", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_margin, "Margin around output (default: " G_STRINGIFY(DEFAULT_MARGIN) ")","one to four numbers"}, + {nullptr} + }; + parser->add_group (entries, + "view", + "View options:", + "Options for output rendering", + this); +} + +#endif