diff --git a/.ci/deploy-docs.sh b/.ci/deploy-docs.sh index e4dbad44d..a8a852331 100755 --- a/.ci/deploy-docs.sh +++ b/.ci/deploy-docs.sh @@ -3,6 +3,8 @@ set -x set -o errexit -o nounset +if test "x$TRAVIS_SECURE_ENV_VARS" != xtrue; then exit; fi + BRANCH="$TRAVIS_BRANCH" if test "x$BRANCH" != xmaster; then exit; fi @@ -16,18 +18,19 @@ mkdir $DOCSDIR cd $DOCSDIR cp ../docs/html/* . +#cp ../docs/CNAME . git init git config user.name "Travis CI" git config user.email "travis@harfbuzz.org" set +x -echo "git remote add upstream \"https://\$GH_TOKEN@github.com/$TRAVIS_REPO_SLUG.git\"" -git remote add upstream "https://$GH_TOKEN@github.com/$TRAVIS_REPO_SLUG.git" +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/gh-pages +git reset upstream/master touch . git add -A . -git commit -m "Rebuild docs for $REVISION" -git push -q upstream HEAD:gh-pages +git commit -m "Rebuild docs for https://github.com/harfbuzz/harfbuzz/commit/$REVISION" +git push -q upstream HEAD:master diff --git a/.ci/fail.sh b/.ci/fail.sh new file mode 100755 index 000000000..4e0069e3f --- /dev/null +++ b/.ci/fail.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +for f in $(find . -name '*.log' -not -name 'config.log'); do + last=$(tail -1 $f) + if [[ $last = FAIL* ]]; then + echo '====' $f '====' + cat $f + elif [[ $last = PASS* ]]; then + # Do nothing. + true + else + # Travis Linux images has an old automake that does not match the + # patterns above, so in case of doubt just print the file. + cat $f + fi +done + +exit 1 diff --git a/.ci/run-coveralls.sh b/.ci/run-coveralls.sh new file mode 100755 index 000000000..58b83114b --- /dev/null +++ b/.ci/run-coveralls.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +set -x +set -o errexit -o nounset + +if test "x$TRAVIS_SLUG" != x"harfbuzz/harfbuzz"; then exit; fi + +pip install --user nose +pip install --user cpp-coveralls +export PATH=$HOME/.local/bin:$PATH + +rm -f src/.libs/NONE.gcov +touch src/NONE +coveralls -e docs diff --git a/.ci/trigger-coverity.sh b/.ci/trigger-coverity.sh new file mode 100644 index 000000000..e2416922e --- /dev/null +++ b/.ci/trigger-coverity.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +set -x +set -o errexit -o nounset + +if test "x$TRAVIS_EVENT_TYPE" != x"cron"; then exit; fi + +BRANCH="$TRAVIS_BRANCH" +if test "x$BRANCH" != xmaster; then exit; fi + +git fetch --unshallow +git remote add upstream "https://$GH_TOKEN@github.com/harfbuzz/harfbuzz.git" +git push -q upstream master:coverity_scan diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 000000000..d804afce9 --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,190 @@ +version: 2 + +jobs: + + distcheck: + docker: + - image: ubuntu:17.10 + steps: + - checkout + - run: apt update && apt install -y ninja-build binutils libtool autoconf automake make cmake gcc g++ pkg-config ragel gtk-doc-tools libfreetype6-dev libglib2.0-dev libcairo2-dev libicu-dev libgraphite2-dev python python-pip + - run: pip install fonttools + - run: ./autogen.sh + - run: make + - run: make distcheck || .ci/fail.sh + - run: rm -rf harfbuzz-* + - run: make distdir && cd harfbuzz-* && cmake -DHB_CHECK=ON -Bbuild -H. -GNinja && ninja -Cbuild && CTEST_OUTPUT_ON_FAILURE=1 ninja -Cbuild test && ninja -Cbuild install + + alpine-O3: + docker: + - image: alpine + steps: + - checkout + - run: apk update && apk add ragel make pkgconfig libtool autoconf automake gettext gcc g++ glib-dev freetype-dev cairo-dev + # C??FLAGS are not needed for a regular build + - run: CFLAGS="-O3" CXXFLAGS="-O3" ./autogen.sh + - run: make + - run: make check || .ci/fail.sh + + archlinux-debug-O0: + docker: + - image: base/devel + steps: + - checkout + - run: pacman --noconfirm -Syu freetype2 cairo icu gettext gobject-introspection gcc gcc-libs glib2 graphite pkg-config ragel python + # C??FLAGS are not needed for a regular build + - run: CFLAGS="-O0" CXXFLAGS="-O0" CPPFLAGS="-DHB_DEBUG" ./autogen.sh --with-freetype --with-glib --with-gobject --with-cairo --with-icu --with-graphite2 + - run: make + - run: make check || .ci/fail.sh + + clang-O3-O0: + docker: + - image: multiarch/crossbuild + steps: + - checkout + - run: apt update && apt install -y ragel libfreetype6-dev libglib2.0-dev libcairo2-dev libicu-dev libgraphite2-dev python python-pip + - run: pip install fonttools + - run: wget http://download.savannah.gnu.org/releases/freetype/freetype-2.9.tar.bz2 && tar xf freetype-2.9.tar.bz2 && cd freetype-2.9 && ./autogen.sh && ./configure && make -j4 && cd .. + - run: CFLAGS="-O3" CXXFLAGS="-O3" CC=clang CXX=clang++ ./autogen.sh --with-freetype --with-glib --with-cairo --with-icu --with-graphite2 + - run: make + - run: LD_LIBRARY_PATH="$PWD/freetype-2.9/objs/.libs" make check || .ci/fail.sh + - run: CFLAGS="-O0" CXXFLAGS="-O0" CC=clang CXX=clang++ ./autogen.sh --with-freetype --with-glib --with-cairo --with-icu --with-graphite2 + - run: make + - run: LD_LIBRARY_PATH="$PWD/freetype-2.9/objs/.libs" make check || .ci/fail.sh + + fedora-outoftreebuild: + docker: + - image: fedora + steps: + - checkout + - run: dnf install -y pkg-config ragel gcc gcc-c++ automake autoconf libtool make which glib2-devel freetype-devel cairo-devel libicu-devel gobject-introspection-devel graphite2-devel redhat-rpm-config python || true + - run: NOCONFIGURE=1 ./autogen.sh --with-freetype --with-glib --with-gobject --with-cairo --with-icu --with-graphite2 + - run: mkdir build && cd build && ../configure && make && (make check || ../.ci/fail.sh) + + cmake-gcc: + docker: + - image: ubuntu:17.10 + steps: + - checkout + - run: apt update && apt install -y ninja-build binutils cmake gcc g++ pkg-config ragel gtk-doc-tools libfreetype6-dev libglib2.0-dev libcairo2-dev libicu-dev libgraphite2-dev python python-pip + - run: pip install fonttools + - run: cmake -DHB_CHECK=ON -Bbuild -H. -GNinja + - run: ninja -Cbuild + - run: CTEST_OUTPUT_ON_FAILURE=1 ninja -Cbuild test + - run: ninja -Cbuild install + + cmake-oracledeveloperstudio: + docker: + - image: fedora + steps: + - checkout + - run: dnf install -y gcc ragel cmake make which glib2-devel freetype-devel cairo-devel libicu-devel graphite2-devel wget tar bzip2 python || true + - run: wget http://$ODSUSER:$ODSPASS@behdad.org/harfbuzz-private/OracleDeveloperStudio12.6-linux-x86-bin.tar.bz2 && tar xf OracleDeveloperStudio12.6-linux-x86-bin.tar.bz2 --owner root --group root --no-same-owner + - run: CC=/root/project/OracleDeveloperStudio12.6-linux-x86-bin/developerstudio12.6/bin/suncc CXX=/root/project/OracleDeveloperStudio12.6-linux-x86-bin/developerstudio12.6/bin/sunCC cmake -DHB_HAVE_GRAPHITE2=ON -DHB_BUILTIN_UCDN=ON -DHB_HAVE_GLIB=ON -DHB_HAVE_ICU=ON -DHB_HAVE_FREETYPE=ON -Bbuild -H. + - run: make -Cbuild + - run: CTEST_OUTPUT_ON_FAILURE=1 make -Cbuild test + - run: make -Cbuild install + + crosscompile-notest-djgpp: + docker: + - image: quay.io/ebraminio/djgpp + steps: + - checkout + - run: apt update && apt install -y ragel pkg-config libtool autoconf + - run: CFLAGS="-Wno-attributes" CXXFLAGS="-Wno-attributes" ./autogen.sh --prefix=/usr/local/djgpp --host=i586-pc-msdosdjgpp + - run: make + + crosscompile-notest-freebsd9: + docker: + - image: donbowman/freebsd-cross-build + steps: + - checkout + - run: apt update && apt install -y pkg-config ragel + - run: ./autogen.sh --prefix=/freebsd --host=x86_64-pc-freebsd9 + - run: make + + crosscompile-notest-psvita: + docker: + - image: dockcross/base + steps: + - checkout + - run: apt update && apt install ragel + - run: git clone https://github.com/vitasdk/vdpm && cd vdpm && ./bootstrap-vitasdk.sh + - run: ./autogen.sh --prefix=/usr/local/vitasdk/arm-vita-eabi --host=arm-vita-eabi + - run: make + + crosscompile-cmake-notest-android-arm: + docker: + - image: dockcross/android-arm + steps: + - checkout + - run: apt update && apt install ragel + - run: cmake -Bbuild -H. -GNinja + - run: ninja -Cbuild + + crosscompile-cmake-notest-browser-asmjs: + docker: + - image: dockcross/browser-asmjs + steps: + - checkout + - run: apt update && apt install ragel + - run: cmake -Bbuild -H. -GNinja + - run: ninja -Cbuild + + crosscompile-cmake-notest-linux-arm64: + docker: + - image: dockcross/linux-arm64 + steps: + - checkout + - run: apt update && apt install ragel + - run: cmake -Bbuild -H. -GNinja + - run: ninja -Cbuild + + crosscompile-cmake-notest-linux-mips: + docker: + - image: dockcross/linux-mips + steps: + - checkout + - run: apt update && apt install ragel + - run: cmake -Bbuild -H. -GNinja + - run: ninja -Cbuild + + crosscompile-cmake-notest-windows-x64: + docker: + - image: dockcross/windows-x64 + steps: + - checkout + - run: apt update && apt install ragel + - run: cmake -Bbuild -H. -GNinja + - run: ninja -Cbuild + +workflows: + version: 2 + build: + jobs: + # both autotools and cmake + - distcheck + + # autotools based builds + - alpine-O3 + - archlinux-debug-O0 + - clang-O3-O0 + - fedora-outoftreebuild + + # cmake based builds + - cmake-gcc + - cmake-oracledeveloperstudio + + # crosscompiles + # they can't be test thus are without tests + ## autotools + - crosscompile-notest-djgpp + - crosscompile-notest-freebsd9 + - crosscompile-notest-psvita + + ## cmake + - crosscompile-cmake-notest-android-arm + - crosscompile-cmake-notest-browser-asmjs + - crosscompile-cmake-notest-linux-arm64 + - crosscompile-cmake-notest-linux-mips + - crosscompile-cmake-notest-windows-x64 diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..708188ad8 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,18 @@ +root = true + +[*] +charset = utf-8 +trim_trailing_whitespace = true +end_of_line = lf +insert_final_newline = true + +[*.{c,cc,h,hh}] +indent_size = 2 +indent_style = space +tab_width = 8 + +[*.{py,sh}] +indent_style = tab + +[{CMakeLists.txt,*.cmake}] +indent_size = 2 diff --git a/.travis.yml b/.travis.yml index f37b4b228..02accdc94 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,40 +1,68 @@ # Build Configuration for Travis -sudo: required # For Trusty beta -os: - - linux - - osx dist: trusty + language: cpp -compiler: - - clang - - gcc + env: global: - CPPFLAGS="" - - CFLAGS="-Werror --coverage" - - CXXFLAGS="-Werror -Wno-deprecated-register --coverage" # glib uses register and clang raises a warning + - CFLAGS="-Werror -Werror=unused-function --coverage" + - CXXFLAGS="-Werror -Werror=unused-function -Wno-deprecated-register --coverage" # glib uses register and clang raises a warning - LDFLAGS="--coverage" -install: - - if [ "$TRAVIS_OS_NAME" == "linux" ]; then pip install --user nose; fi - - if [ "$TRAVIS_OS_NAME" == "linux" ]; then pip install --user cpp-coveralls; fi # for coveralls.io code coverage tracking - - if [ "$TRAVIS_OS_NAME" == "linux" ]; then export PATH=$HOME/.local/bin:$PATH; fi # Make sure we can find the above Python packages - - if [ "$TRAVIS_OS_NAME" == "osx" ]; then brew update; fi; - - if [ "$TRAVIS_OS_NAME" == "osx" ]; then brew install ragel freetype glib gobject-introspection cairo icu4c graphite2; fi - - if [ "$TRAVIS_OS_NAME" == "osx" ]; then brew link --force icu4c; fi # icu4c is keg-only -script: - - NOCONFIGURE=1 ./autogen.sh - - export CONFIGURE_OPTS="--with-freetype --with-glib --with-gobject --with-cairo --with-icu --with-graphite2" - - if [ "$TRAVIS_OS_NAME" == "linux" -a "$CC" == "gcc" ]; then export CONFIGURE_OPTS="$CONFIGURE_OPTS --enable-gtk-doc"; fi - - if [ "$TRAVIS_OS_NAME" == "osx" ]; then export CONFIGURE_OPTS="$CONFIGURE_OPTS --with-coretext"; fi - - ./configure $CONFIGURE_OPTS - - make - - make check - - if [ "$TRAVIS_OS_NAME" == "linux" -a "$CC" == "gcc" -a "$TRAVIS_SLUG" == "behdad/harfbuzz" ]; then rm -f src/.libs/NONE.gcov; touch src/NONE; coveralls -e docs; fi -after_success: - - if [ "$TRAVIS_OS_NAME" == "linux" -a "$CC" == "gcc" -a "$TRAVIS_SECURE_ENV_VARS" == "true" ]; then bash .ci/deploy-docs.sh; fi + - CONFIGURE_OPTS="--with-freetype --with-glib --with-gobject --with-cairo --with-icu --with-graphite2" + - NOCONFIGURE=1 + # COVERITY_SCAN_TOKEN + - secure: "Vw1UUHsAr4t3xuvOqqBsFAORptmNcQRrcGJnzSX7jDODBIRNfnU2LIBTagrPNKfSUhyQgCHqt1gX9iWNWvVfy3sDOXr2MXZeoqmF6Y1mS35J0rA/EPJgRHsdkxygkmFnXVeQkEuI55BINkaSoOpAeunmXKJNw1p9JVw368Fm/tU=" + +matrix: + include: + - os: linux + compiler: gcc + script: + # Remove these two lines when Travis updated its distro + - wget http://download.savannah.gnu.org/releases/freetype/freetype-2.9.tar.bz2 && tar xf freetype-2.9.tar.bz2 && cd freetype-2.9 && ./autogen.sh && ./configure && make -j4 && cd .. + - export LD_LIBRARY_PATH="$PWD/freetype-2.9/objs/.libs" + + - ./autogen.sh + - ./configure $CONFIGURE_OPTS --enable-gtk-doc + - make + - make check || .ci/fail.sh + after_success: + - bash .ci/run-coveralls.sh # for coveralls.io code coverage tracking + - bash .ci/deploy-docs.sh + - bash .ci/trigger-coverity.sh + + - os: linux + compiler: clang + script: + # Remove these two lines when Travis updated its distro + - wget http://download.savannah.gnu.org/releases/freetype/freetype-2.9.tar.bz2 && tar xf freetype-2.9.tar.bz2 && cd freetype-2.9 && ./autogen.sh && ./configure && make -j4 && cd .. + - export LD_LIBRARY_PATH="$PWD/freetype-2.9/objs/.libs" + + - ./autogen.sh + - ./configure $CONFIGURE_OPTS + - make + - make check || .ci/fail.sh + + - os: osx + compiler: clang + install: + # https://github.com/harfbuzz/harfbuzz/issues/345 + - export CXXFLAGS="$CXXFLAGS -Wno-deprecated-declarations" + - brew update; + # Workaround Travis/brew bug + - brew uninstall libtool && brew install libtool + - brew install ragel freetype glib gobject-introspection cairo icu4c graphite2 + - brew link --force icu4c # icu4c is keg-only + script: + - ./autogen.sh + - ./configure $CONFIGURE_OPTS --with-coretext + - make + - make check || .ci/fail.sh + notifications: irc: "irc.freenode.org#harfbuzz" - email: harfbuzz@lists.freedesktop.org + email: harfbuzz-bots-chatter@googlegroups.com addons: apt: @@ -47,4 +75,14 @@ addons: - libcairo2-dev # for utils - libicu-dev # for extra unicode functions - libgraphite2-dev # for extra shapers - - # libgirepository1.0-dev # for gobject-introspection + #- libgirepository1.0-dev # for gobject-introspection + + coverity_scan: + project: + name: HarfBuzz + version: 1.0 + description: HarfBuzz OpenType text shaping engine + notification_email: harfbuzz-bots-chatter@googlegroups.com + build_command_prepend: ./autogen.sh --with-freetype --with-glib --with-gobject --with-cairo --with-icu --with-graphite2 + build_command: make + branch_pattern: coverity_scan diff --git a/Android.mk b/Android.mk deleted file mode 100644 index 654f0e0fc..000000000 --- a/Android.mk +++ /dev/null @@ -1,107 +0,0 @@ -## -## Copyright (C) 2012 The Android Open Source Project -## -## Licensed under the Apache License, Version 2.0 (the "License"); -## you may not use this file except in compliance with the License. -## You may obtain a copy of the License at -## -## http://www.apache.org/licenses/LICENSE-2.0 -## -## Unless required by applicable law or agreed to in writing, software -## distributed under the License is distributed on an "AS IS" BASIS, -## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -## See the License for the specific language governing permissions and -## limitations under the License. -## - -############################################################# -# Note: -# -# This file is used to build HarfBuzz within the Android -# platform itself. If you need to compile HarfBuzz to -# ship with your Android NDK app, you can use the autotools -# build system to do so. To do that you need to install a -# "standalone" toolchain with the NDK, eg: -# -# ndk/build/tools/make-standalone-toolchain.sh \ -# --platform=android-18 \ -# --install-dir=/prefix -# -# Set PLATFORM_PREFIX eng var to that prefix and make sure -# the cross-compile tools from PLATFORM_PREFIX are in path. -# Configure and install HarfBuzz: -# -# ./configure --host=arm-linux-androideabi \ -# --prefix=$PLATFORM_PREFIX \ -# --enable-static \ -# --with-freetype \ -# PKG_CONFIG_LIBDIR=$PLATFORM_PREFIX/lib/pkgconfig -# make install -# -# You can first build FreeType the same way: -# -# ./configure --host=arm-linux-androideabi \ -# --prefix=$PLATFORM_PREFIX \ -# --enable-static \ -# --without-png \ -# PKG_CONFIG_LIBDIR=$PLATFORM_PREFIX/lib/pkgconfig -# make install -# - -LOCAL_PATH:= $(call my-dir) - -HARFBUZZ_SRC_FILES = \ - src/hb-blob.cc \ - src/hb-buffer-serialize.cc \ - src/hb-buffer.cc \ - src/hb-common.cc \ - src/hb-face.cc \ - src/hb-font.cc \ - src/hb-ot-tag.cc \ - src/hb-set.cc \ - src/hb-shape.cc \ - src/hb-shape-plan.cc \ - src/hb-shaper.cc \ - src/hb-unicode.cc \ - src/hb-warning.cc \ - src/hb-ot-font.cc \ - src/hb-ot-layout.cc \ - src/hb-ot-map.cc \ - src/hb-ot-shape.cc \ - src/hb-ot-shape-complex-arabic.cc \ - src/hb-ot-shape-complex-default.cc \ - src/hb-ot-shape-complex-hangul.cc \ - src/hb-ot-shape-complex-hebrew.cc \ - src/hb-ot-shape-complex-indic.cc \ - src/hb-ot-shape-complex-indic-table.cc \ - src/hb-ot-shape-complex-myanmar.cc \ - src/hb-ot-shape-complex-thai.cc \ - src/hb-ot-shape-complex-tibetan.cc \ - src/hb-ot-shape-complex-use.cc \ - src/hb-ot-shape-complex-use-table.cc \ - src/hb-ot-shape-normalize.cc \ - src/hb-ot-shape-fallback.cc \ - $(NULL) - -############################################################# -# build the harfbuzz shared library -# -include $(CLEAR_VARS) -LOCAL_ARM_MODE := arm -LOCAL_MODULE_TAGS := optional -LOCAL_SRC_FILES:= \ - $(HARFBUZZ_SRC_FILES) \ - src/hb-icu.cc -LOCAL_CPP_EXTENSION := .cc -LOCAL_SHARED_LIBRARIES := \ - libcutils \ - libicuuc \ - libicui18n \ - libutils \ - liblog -LOCAL_C_INCLUDES += \ - $(LOCAL_PATH)/src -LOCAL_CFLAGS += -DHB_NO_MT -DHAVE_OT -DHAVE_ICU -DHAVE_ICU_BUILTIN \ - -Wno-unused-parameter -Wno-missing-field-initializers -LOCAL_MODULE:= libharfbuzz_ng -include $(BUILD_SHARED_LIBRARY) diff --git a/BUILD.md b/BUILD.md index 7518c2e42..8a6b5695a 100644 --- a/BUILD.md +++ b/BUILD.md @@ -1,36 +1,50 @@ On Linux, install the development packages for FreeType, Cairo, and GLib. For example, on Ubuntu / Debian, you would do: -* sudo apt-get install gcc g++ libfreetype6-dev libglib2.0-dev libcairo2-dev + + sudo apt-get install gcc g++ libfreetype6-dev libglib2.0-dev libcairo2-dev whereas on Fedora, RHEL, CentOS, and other Red Hat based systems you would do: -* sudo yum install gcc gcc-c++ freetype-devel glib2-devel cairo-devel -on the Mac, using MacPorts: -* sudo port install freetype glib2 cairo + sudo yum install gcc gcc-c++ freetype-devel glib2-devel cairo-devel + +on Windows, consider using [vcpkg](https://github.com/Microsoft/vcpkg), +provided by Microsoft, for building HarfBuzz and other open-source libraries +but if you need to build harfbuzz from source, put ragel binary on your +PATH and follow appveyor CI's cmake +[build steps](https://github.com/harfbuzz/harfbuzz/blob/master/appveyor.yml). + +on macOS, using MacPorts: + + sudo port install freetype glib2 cairo or using Homebrew: -* brew install freetype glib cairo + + brew install freetype glib cairo If you are using a tarball, you can now proceed to running configure and make as with any other standard package. That should leave you with a shared -library in src/, and a few utility programs including hb-view and hb-shape -under util/. From the tarball, NMake Makefiles are also provided in win32/, -which supports building HarfBuzz using Visual Studio, with a README.txt that -gives instructions on building using NMake. +library in `src/`, and a few utility programs including `hb-view` and `hb-shape` +under `util/`. + If you are bootstraping from git, you need a few more tools before you can -run autogen.sh for the first time. Namely, pkg-config and ragel. Again, -on Ubuntu / Debian: -* sudo apt-get install autoconf automake libtool pkg-config ragel gtk-doc-tools +run `autogen.sh` for the first time. Namely, `pkg-config` and `ragel`. + +Again, on Ubuntu / Debian: + + sudo apt-get install autoconf automake libtool pkg-config ragel gtk-doc-tools and on Fedora, RHEL, CentOS: -* sudo yum install autoconf automake libtool pkgconfig ragel gtk-doc + + sudo yum install autoconf automake libtool pkgconfig ragel gtk-doc on the Mac, using MacPorts: -* sudo port install autoconf automake libtool pkgconfig ragel gtk-doc + + sudo port install autoconf automake libtool pkgconfig ragel gtk-doc or using Homebrew: -* brew install autoconf automake libtool pkgconfig ragel gtk-doc + + brew install autoconf automake libtool pkgconfig ragel gtk-doc To build the Python bindings, you also need: -* brew install pygobject3 + brew install pygobject3 diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 000000000..660da5a1f --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,860 @@ +cmake_minimum_required(VERSION 2.8.0) +project(harfbuzz) + +enable_testing() + +## 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_BUILTIN_UCDN "Use HarfBuzz provided UCDN" ON) +option(HB_HAVE_GLIB "Enable glib unicode functions" OFF) +option(HB_HAVE_ICU "Enable icu unicode functions" OFF) +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_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_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 () + +option(HB_DISABLE_TEST_PROGS OFF "Do not build some of the test programs, useful for continuous builds") +option(HB_CHECK OFF "Do a configuration suitable for testing (shared library and enable all options)") +if (HB_CHECK) + set (BUILD_SHARED_LIBS ON) + set (HB_BUILD_UTILS ON) + set (HB_BUILTIN_UCDN ON) + set (HB_HAVE_ICU) + set (HB_HAVE_GLIB ON) + #set (HB_HAVE_GOBJECT ON) + #set (HB_HAVE_INTROSPECTION ON) + set (HB_HAVE_FREETYPE ON) + set (HB_HAVE_GRAPHITE2 ON) + if (WIN32) + set (HB_HAVE_UNISCRIBE ON) + set (HB_HAVE_DIRECTWRITE ON) + elseif (APPLE) + set (HB_HAVE_CORETEXT ON) + endif () +endif () + +include_directories(AFTER + ${PROJECT_SOURCE_DIR}/src + ${PROJECT_BINARY_DIR}/src +) + +add_definitions(-DHAVE_OT) +add_definitions(-DHAVE_FALLBACK) + + +## Functions and headers +include (CheckFunctionExists) +include (CheckIncludeFile) +macro (check_funcs) # Similar to AC_CHECK_FUNCS of autotools + foreach (func_name ${ARGN}) + string(TOUPPER ${func_name} definiton_to_add) + check_function_exists(${func_name} HAVE_${definiton_to_add}) + if (${HAVE_${definiton_to_add}}) + add_definitions(-DHAVE_${definiton_to_add}) + endif () + endforeach () +endmacro () +check_funcs(atexit mprotect sysconf getpagesize mmap isatty newlocale strtod_l) +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(xlocale.h HAVE_XLOCALE_H) +if (${HAVE_XLOCALE_H}) + add_definitions(-DHAVE_XLOCALE_H) +endif () + + +if (MSVC) + add_definitions(-wd4244 -wd4267 -D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_WARNINGS) +endif () + +if (BUILD_SHARED_LIBS) + if (WIN32 AND NOT MINGW) + add_definitions("-DHB_EXTERN=__declspec(dllexport) extern") + else () + set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility-inlines-hidden") + endif () +endif () + + +## Detect if we are running inside a distribution or regular repository folder +set (IN_HB_DIST FALSE) +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 () + +# http://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) +file(READ ${PROJECT_SOURCE_DIR}/src/hb-ucdn/Makefile.sources UCDNSOURCES) + +extract_make_variable(HB_BASE_sources ${SRCSOURCES}) +add_prefix_to_list(HB_BASE_sources "${PROJECT_SOURCE_DIR}/src/") +extract_make_variable(HB_BASE_headers ${SRCSOURCES}) +add_prefix_to_list(HB_BASE_headers "${PROJECT_SOURCE_DIR}/src/") +extract_make_variable(HB_FALLBACK_sources ${SRCSOURCES}) +add_prefix_to_list(HB_FALLBACK_sources "${PROJECT_SOURCE_DIR}/src/") +extract_make_variable(HB_OT_sources ${SRCSOURCES}) +add_prefix_to_list(HB_OT_sources "${PROJECT_SOURCE_DIR}/src/") +extract_make_variable(HB_OT_headers ${SRCSOURCES}) +add_prefix_to_list(HB_OT_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}) +extract_make_variable(HB_OT_RAGEL_GENERATED_sources ${SRCSOURCES}) +#if (IN_HB_DIST) + add_prefix_to_list(HB_BASE_RAGEL_GENERATED_sources "${PROJECT_SOURCE_DIR}/src/") + add_prefix_to_list(HB_OT_RAGEL_GENERATED_sources "${PROJECT_SOURCE_DIR}/src/") +#else () +# add_prefix_to_list(HB_BASE_RAGEL_GENERATED_sources "${PROJECT_BINARY_DIR}/src/") +# add_prefix_to_list(HB_OT_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/") + +extract_make_variable(LIBHB_UCDN_sources ${UCDNSOURCES}) +add_prefix_to_list(LIBHB_UCDN_sources "${PROJECT_SOURCE_DIR}/src/hb-ucdn/") + + +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 ragel tasks +if (NOT IN_HB_DIST) + find_program(RAGEL "ragel" CMAKE_FIND_ROOT_PATH_BOTH) + + if (RAGEL) + message(STATUS "ragel found at: ${RAGEL}") + else () + message(FATAL_ERROR "ragel not found, get it here -- http://www.complang.org/ragel/ or, use harfbuzz releases https://github.com/harfbuzz/harfbuzz/releases") + endif () + + foreach (ragel_output IN ITEMS ${HB_BASE_RAGEL_GENERATED_sources} ${HB_OT_RAGEL_GENERATED_sources}) + string(REGEX MATCH "([^/]+)\\.hh" temp ${ragel_output}) + set (target_name ${CMAKE_MATCH_1}) + add_custom_command(OUTPUT ${ragel_output} + COMMAND ${RAGEL} -G2 -o ${ragel_output} ${PROJECT_SOURCE_DIR}/src/${target_name}.rl -I ${PROJECT_SOURCE_DIR} ${ARGN} + DEPENDS ${PROJECT_SOURCE_DIR}/src/${target_name}.rl + ) + add_custom_target(harfbuzz_${target_name} DEPENDS ${PROJECT_BINARY_DIR}/src/${target_name}) + endforeach () + + mark_as_advanced(RAGEL) +endif () + + +## Generate hb-version.h +#if (NOT IN_HB_DIST) +# set (HB_VERSION_H_IN "${PROJECT_SOURCE_DIR}/src/hb-version.h.in") +# set (HB_VERSION_H "${PROJECT_BINARY_DIR}/src/hb-version.h") +# set_source_files_properties("${HB_VERSION_H}" PROPERTIES GENERATED true) +# configure_file("${HB_VERSION_H_IN}" "${HB_VERSION_H}.tmp" @ONLY) +# execute_process(COMMAND "${CMAKE_COMMAND}" -E copy_if_different +# "${HB_VERSION_H}.tmp" +# "${HB_VERSION_H}" +# ) +# file(REMOVE "${HB_VERSION_H}.tmp") +#endif () + + +## Define sources and headers of the project +set (project_sources + ${HB_BASE_sources} + ${HB_BASE_RAGEL_GENERATED_sources} + + ${HB_FALLBACK_sources} + ${HB_OT_sources} + ${HB_OT_RAGEL_GENERATED_sources} +) + +set (subset_project_sources + ${HB_SUBSET_sources} +) + +set (project_extra_sources) + +set (project_headers + #${HB_VERSION_H} + + ${HB_BASE_headers} + ${HB_OT_headers} +) + +set (subset_project_headers + ${HB_SUBSET_headers} +) + + +## Find and include needed header folders and libraries +if (HB_HAVE_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_sources ${PROJECT_SOURCE_DIR}/src/hb-ft.cc) + 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_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_sources ${PROJECT_SOURCE_DIR}/src/hb-graphite2.cc) + list(APPEND project_headers ${PROJECT_SOURCE_DIR}/src/hb-graphite2.h) + + list(APPEND THIRD_PARTY_LIBS ${GRAPHITE2_LIBRARY}) + + mark_as_advanced(GRAPHITE2_INCLUDE_DIR GRAPHITE2_LIBRARY) +endif () + +if (HB_BUILTIN_UCDN) + include_directories(src/hb-ucdn) + add_definitions(-DHAVE_UCDN) + + list(APPEND project_sources ${PROJECT_SOURCE_DIR}/src/hb-ucdn.cc) + list(APPEND project_extra_sources ${LIBHB_UCDN_sources}) +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_sources ${PROJECT_SOURCE_DIR}/src/hb-glib.cc) + list(APPEND project_headers ${PROJECT_SOURCE_DIR}/src/hb-glib.h) + + list(APPEND THIRD_PARTY_LIBS ${GLIB_LIBRARIES}) + + 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/master/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_sources ${PROJECT_SOURCE_DIR}/src/hb-icu.cc) + 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_sources ${PROJECT_SOURCE_DIR}/src/hb-coretext.cc) + list(APPEND project_headers ${PROJECT_SOURCE_DIR}/src/hb-coretext.h) + + find_library(APPLICATION_SERVICES_FRAMEWORK ApplicationServices) + if (APPLICATION_SERVICES_FRAMEWORK) + list(APPEND THIRD_PARTY_LIBS ${APPLICATION_SERVICES_FRAMEWORK}) + endif (APPLICATION_SERVICES_FRAMEWORK) + + mark_as_advanced(APPLICATION_SERVICES_FRAMEWORK) +endif () + +if (WIN32 AND HB_HAVE_UNISCRIBE) + add_definitions(-DHAVE_UNISCRIBE) + + list(APPEND project_sources ${PROJECT_SOURCE_DIR}/src/hb-uniscribe.cc) + list(APPEND project_headers ${PROJECT_SOURCE_DIR}/src/hb-uniscribe.h) + + list(APPEND THIRD_PARTY_LIBS usp10 gdi32 rpcrt4) +endif () + +if (WIN32 AND HB_HAVE_DIRECTWRITE) + add_definitions(-DHAVE_DIRECTWRITE) + + list(APPEND project_sources ${PROJECT_SOURCE_DIR}/src/hb-directwrite.cc) + list(APPEND project_headers ${PROJECT_SOURCE_DIR}/src/hb-directwrite.h) + + list(APPEND THIRD_PARTY_LIBS dwrite rpcrt4) +endif () + +if (HB_HAVE_GOBJECT) + include (FindPythonInterp) + 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 () + + +## Atomic ops availability detection +file(WRITE "${PROJECT_BINARY_DIR}/try_compile_intel_atomic_primitives.c" +" void memory_barrier (void) { __sync_synchronize (); } + int atomic_add (int *i) { return __sync_fetch_and_add (i, 1); } + int mutex_trylock (int *m) { return __sync_lock_test_and_set (m, 1); } + void mutex_unlock (int *m) { __sync_lock_release (m); } + int main () { return 0; } +") +try_compile(HB_HAVE_INTEL_ATOMIC_PRIMITIVES + ${PROJECT_BINARY_DIR}/try_compile_intel_atomic_primitives + ${PROJECT_BINARY_DIR}/try_compile_intel_atomic_primitives.c) +if (HB_HAVE_INTEL_ATOMIC_PRIMITIVES) + add_definitions(-DHAVE_INTEL_ATOMIC_PRIMITIVES) +endif () + +file(WRITE "${PROJECT_BINARY_DIR}/try_compile_solaris_atomic_ops.c" +" #include + /* This requires Solaris Studio 12.2 or newer: */ + #include + void memory_barrier (void) { __machine_rw_barrier (); } + int atomic_add (volatile unsigned *i) { return atomic_add_int_nv (i, 1); } + void *atomic_ptr_cmpxchg (volatile void **target, void *cmp, void *newval) { return atomic_cas_ptr (target, cmp, newval); } + int main () { return 0; } +") +try_compile(HB_HAVE_SOLARIS_ATOMIC_OPS + ${PROJECT_BINARY_DIR}/try_compile_solaris_atomic_ops + ${PROJECT_BINARY_DIR}/try_compile_solaris_atomic_ops.c) +if (HB_HAVE_SOLARIS_ATOMIC_OPS) + add_definitions(-DHAVE_SOLARIS_ATOMIC_OPS) +endif () + + +## Define harfbuzz library +add_library(harfbuzz ${project_sources} ${project_extra_sources} ${project_headers}) +target_link_libraries(harfbuzz ${THIRD_PARTY_LIBS}) + +## Define harfbuzz-subset library +add_library(harfbuzz-subset ${subset_project_sources} ${subset_project_headers}) +add_dependencies(harfbuzz-subset harfbuzz) +target_link_libraries(harfbuzz-subset harfbuzz ${THIRD_PARTY_LIBS}) + +if (UNIX OR MINGW) + # Make symbols link locally + link_libraries(-Bsymbolic-functions) + + if (CMAKE_CXX_COMPILER_ID STREQUAL "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) + set_target_properties(harfbuzz-subset PROPERTIES LINKER_LANGUAGE C) + + # No threadsafe statics as we do it ourselves + set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-threadsafe-statics") + 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}) +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 () + + # Finally, build the introspection files... + add_custom_command( + TARGET harfbuzz-gobject + POST_BUILD + COMMAND ${G_IR_SCANNER_CMD} + --warn-all --no-libtool --verbose + -n hb + --namespace=HarfBuzz + --nsversion=0.0 + --identifier-prefix=hb_ + --include GObject-2.0 + --pkg-export=harfbuzz + --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_GOBJECT_H + -DHB_GOBJECT_H_IN + -DHB_EXTERN= + --cflags-end + --library=harfbuzz-gobject + --library=harfbuzz + -L${hb_libpath} + ${extra_libs} + ${introspected_sources} + -o ${hb_libpath}/HarfBuzz-0.0.gir + DEPENDS harfbuzz-gobject harfbuzz + ) + + 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 () + +if (NOT SKIP_INSTALL_LIBRARIES AND NOT SKIP_INSTALL_ALL) + install(TARGETS harfbuzz + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + FRAMEWORK DESTINATION Library/Frameworks + ) + if (HB_BUILD_UTILS) + install(TARGETS hb-view + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + ) + install(TARGETS hb-view + 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} + ) + 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 () + +if (UNIX AND CMAKE_GENERATOR STREQUAL "Ninja") + if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fcolor-diagnostics") + set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fcolor-diagnostics") + endif () + if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fdiagnostics-color") + set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fdiagnostics-color") + endif () +endif () + + +## src/ executables +if (NOT HB_DISABLE_TEST_PROGS) + foreach (prog main test test-would-substitute test-size-params test-buffer-serialize hb-ot-tag test-unicode-ranges) + set (prog_name ${prog}) + if (${prog_name} STREQUAL "test") + # test can not be used as a valid executable name on cmake, lets special case it + set (prog_name test-test) + endif () + add_executable(${prog_name} ${PROJECT_SOURCE_DIR}/src/${prog}.cc) + target_link_libraries(${prog_name} harfbuzz ${THIRD_PARTY_LIBS}) + endforeach () + set_target_properties(hb-ot-tag PROPERTIES COMPILE_FLAGS "-DMAIN") +endif () + +## Tests +if (UNIX OR MINGW) + if (BUILD_SHARED_LIBS) + # generate harfbuzz.def after build completion + string(REPLACE ";" " " space_separated_headers "${project_headers}") + add_custom_command(TARGET harfbuzz POST_BUILD + COMMAND ${CMAKE_COMMAND} -E env "headers=${space_separated_headers}" python ${PROJECT_SOURCE_DIR}/src/gen-def.py ${PROJECT_BINARY_DIR}/harfbuzz.def + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/src) + + add_test(NAME check-static-inits.sh + COMMAND ${PROJECT_SOURCE_DIR}/src/check-static-inits.sh + WORKING_DIRECTORY ${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/harfbuzz.dir/src # ugly hack + ) + add_test(NAME check-libstdc++.sh COMMAND ${PROJECT_SOURCE_DIR}/src/check-libstdc++.sh) + add_test(NAME check-symbols.sh COMMAND ${PROJECT_SOURCE_DIR}/src/check-symbols.sh) + + set_tests_properties( + check-static-inits.sh check-libstdc++.sh check-symbols.sh + PROPERTIES + ENVIRONMENT "libs=.;srcdir=${PROJECT_SOURCE_DIR}/src" + SKIP_RETURN_CODE 77) + endif () + + add_test(NAME check-c-linkage-decls.sh COMMAND ./check-c-linkage-decls.sh) + add_test(NAME check-header-guards.sh COMMAND ./check-header-guards.sh) + add_test(NAME check-externs.sh COMMAND ./check-externs.sh) + add_test(NAME check-includes.sh COMMAND ./check-includes.sh) + set_tests_properties( + check-c-linkage-decls.sh check-header-guards.sh check-externs.sh check-includes.sh + PROPERTIES + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/src + SKIP_RETURN_CODE 77) +endif () + +# Needs to come last so that variables defined above are passed to +# subdirectories. +add_subdirectory(test) diff --git a/Makefile.am b/Makefile.am index d56a151a3..fde525642 100644 --- a/Makefile.am +++ b/Makefile.am @@ -4,14 +4,16 @@ NULL = ACLOCAL_AMFLAGS = -I m4 -SUBDIRS = src util test docs win32 +SUBDIRS = src util test docs EXTRA_DIST = \ autogen.sh \ harfbuzz.doap \ - Android.mk \ README.python \ BUILD.md \ + RELEASING.md \ + CMakeLists.txt \ + replace-enum-strings.cmake \ $(NULL) MAINTAINERCLEANFILES = \ diff --git a/NEWS b/NEWS index 992c64bcc..8ff1fe613 100644 --- a/NEWS +++ b/NEWS @@ -1,7 +1,376 @@ -Overview of changes leading to 1.2.8 -Not yet released +======= +Overview of changes leading to 1.7.5 +Tuesday, January 30, 2018 ==================================== -- Implemented 'CPAL' table in hb-ot-color. + +- 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 @@ -109,7 +478,7 @@ Tuesday, February 23, 2016 - CoreText: Drastically speed up font initialization. - CoreText: Fix tiny leak. - Group ZWJ/ZWNJ with previous syllable under cluster-level=0. - https://github.com/behdad/harfbuzz/issues/217 + https://github.com/harfbuzz/harfbuzz/issues/217 - Add test/shaping/README.md about how to add tests to the suite. @@ -125,8 +494,8 @@ Friday, February 19, 2016 - 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/behdad/harfbuzz/issues/211 - https://github.com/behdad/harfbuzz/commit/86c68c7a2c971efe8e35b1f1bd99401dc8b688d2 + 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. @@ -167,7 +536,7 @@ Wednesday, November 26, 2015 ==================================== - Fix badly-broken fallback shaper that affected terminology. - https://github.com/behdad/harfbuzz/issues/187 + 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) @@ -189,11 +558,11 @@ Wednesday, November 18, 2015 ==================================== - Implement 'stch' stretch feature for Syriac Abbreviation Mark. - https://github.com/behdad/harfbuzz/issues/141 + 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/behdad/harfbuzz/issues/153 + 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: @@ -216,7 +585,7 @@ Thursday, October 15, 2015 - 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/behdad/harfbuzz/issues/143 + https://github.com/harfbuzz/harfbuzz/issues/143 Overview of changes leading to 1.0.5 @@ -224,7 +593,7 @@ Tuesday, October 13, 2015 ==================================== - Fix multiple memory access bugs discovered using libFuzzer. - https://github.com/behdad/harfbuzz/issues/139 + 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. @@ -495,7 +864,7 @@ Wednesday, July 16, 2014 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 overriden though. See below. + 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() diff --git a/README b/README index 3fcdfb4c3..6e2132237 100644 --- a/README +++ b/README @@ -1,5 +1,8 @@ -[![Build Status](https://travis-ci.org/behdad/harfbuzz.svg)](https://travis-ci.org/behdad/harfbuzz) -[![Coverage Status](https://img.shields.io/coveralls/behdad/harfbuzz.svg)](https://coveralls.io/r/behdad/harfbuzz) +[![Build Status](https://travis-ci.org/harfbuzz/harfbuzz.svg)](https://travis-ci.org/harfbuzz/harfbuzz) +[![Build status](https://ci.appveyor.com/api/projects/status/0t0flrxpstj9lb9w?svg=true)](https://ci.appveyor.com/project/harfbuzz/harfbuzz) +[![CircleCI](https://circleci.com/gh/harfbuzz/harfbuzz.svg?style=svg)](https://circleci.com/gh/harfbuzz/harfbuzz) +[![Coverity](https://img.shields.io/coverity/scan/5450.svg)](https://scan.coverity.com/projects/behdad-harfbuzz) +[![Coverage Status](https://img.shields.io/coveralls/harfbuzz/harfbuzz.svg)](https://coveralls.io/r/harfbuzz/harfbuzz) [ABI Tracker](http://abi-laboratory.pro/tracker/timeline/harfbuzz/) This is HarfBuzz, a text shaping library. @@ -9,3 +12,5 @@ For bug reports, mailing list, and other information please visit: http://harfbuzz.org/ For license information, see the file COPYING. + +Documentation: https://harfbuzz.github.io diff --git a/RELEASING.md b/RELEASING.md new file mode 100644 index 000000000..dedcca8f9 --- /dev/null +++ b/RELEASING.md @@ -0,0 +1,99 @@ +HarfBuzz release walk-through checklist: + +1. Open gitk and review changes since last release. + + * `git diff $(git describe | sed 's/-.*//').. src/*.h` prints all public API + changes. + + Document them in NEWS. All API and API semantic changes should be clearly + marked as API additions, API changes, or API deletions. Document + deprecations. + + If there's a backward-incompatible API change (including deletions for API + used anywhere), that's a release blocker. Do NOT release. + +2. Based on severity of changes, decide whether it's a minor or micro release + number bump, + +3. Make sure you have correct date and new version at the top of NEWS file, + +4. Bump version in configure.ac line 3, + +5. Do "make distcheck", if it passes, you get a tarball. + Otherwise, fix things and commit them separately before making release, + +6. "make release-files". Enter your GPG password. This creates a sha256 hash + and signs it. + +7. Now that you have release files built, commit NEWS and configure.ac changes. + The commit message is simply the release number. Eg. "1.4.7" + +8. Tag the release and sign it: Eg. "git tag -s 1.4.7 -m 1.4.7". Enter your + GPG password again. + +9. Build win32 bundle. + + a. Put contents of [this](https://drive.google.com/open?id=0B3_fQkxDZZXXbWltRGd5bjVrUDQ) on your `~/.local/i686-w64-mingw32`, + + b. Run `../mingw32.sh --with-uniscribe` script (available below) to configure harfbuzz with mingw in a subdirector (eg. winbuild/), + + c. make + + d. Back in the parent directory, run `./UPDATE.sh` (available below) to build win32 bundle. + +10. Copy all artefacts to users.freedesktop.org and move them into + `/srv/www.freedesktop.org/www/software/harfbuzz/release` There should be four + files. Eg.: + ``` +-rw-r--r-- 1 behdad eng 1592693 Jul 18 11:25 harfbuzz-1.4.7.tar.bz2 +-rw-r--r-- 1 behdad eng 89 Jul 18 11:34 harfbuzz-1.4.7.tar.bz2.sha256 +-rw-r--r-- 1 behdad eng 339 Jul 18 11:34 harfbuzz-1.4.7.tar.bz2.sha256.asc +-rw-r--r-- 1 behdad eng 2895619 Jul 18 11:34 harfbuzz-1.4.7-win32.zip +``` + +11. While doing that, quickly double-check the size of the .tar.bz2 and .zip + files against their previous releases to make sure nothing bad happened. + They should be in the ballpark, perhaps slightly larger. Sometimes they + do shrink, that's not by itself a stopper. + +12. Push the commit and tag out: "git push --follow-tags". Make sure it's + pushed both to freedesktop repo and github. + +13. Go to GitHub release page [here](https://github.com/harfbuzz/harfbuzz/releases), + edit the tag, upload artefacts and NEWS entry and save. + + +## UPDATE.sh +```bash +#!/bin/bash + +v=$1 + +if test "x$v" = x; then + echo "usage: UPDATE.sh micro-version" + exit 1 +fi + +dir_prefix=harfbuzz-1.4. +dir_suffix=-win32 +dir=$dir_prefix$v$dir_suffix +dir_old=$dir_prefix$((v-1))$dir_suffix +if test -d "$dir"; then + echo "New dir $dir exists; not overwriting" + exit 1 +fi +if ! test -d "$dir_old"; then + echo "Old dir $dir_old does NOT exist; aborting" + exit 1 +fi +set -ex +cp -a "$dir_old" "$dir.tmp" +rm -f "$dir.tmp"/GDX32.dll +rm -f "$dir.tmp"/usp10.dll +cp ../winbuild/src/.libs/libharfbuzz-0.dll{,.def} $dir.tmp/ +cp ../winbuild/util/.libs/hb-{shape,view}.exe $dir.tmp/ +i686-w64-mingw32-strip $dir.tmp/{hb-shape.exe,hb-view.exe,libharfbuzz-0.dll} +mv $dir.tmp $dir +zip -r $dir.zip $dir +echo Bundle $dir.zip ready +``` diff --git a/TODO b/TODO index 4f37f605b..53ffbe9d0 100644 --- a/TODO +++ b/TODO @@ -1,24 +1,14 @@ General fixes: ============= -- AAT 'morx' implementation. - -- Return "safe-to-break" bit from shaping. - - Implement 'rand' feature. -- mask propagation? (when ligation, "or" the masks). - API issues: =========== - API to accept a list of languages? -- Add init_func to font_funcs. Adjust ft. - -- 'const' for getter APIs? (use mutable internally) - - Remove hb_ot_shape_glyphs_closure()? @@ -39,7 +29,7 @@ API additions - Add query / enumeration API for aalt-like features? -- SFNT api? get_num_faces? get_table_tags? (there's something in stash) +- SFNT api? get_num_faces? - Add segmentation API @@ -50,20 +40,3 @@ hb-view / hb-shape enhancements: =============================== - Add --width, --height, --auto-size, --ink-box, --align, etc? - - -Tests to write: -============== - -- ot-layout enumeration API (needs font) - -- Finish test-shape.c, grep for TODO - -- Finish test-unicode.c, grep for TODO - -- GObject, FreeType, etc - -- hb_cache_t and relatives - -- hb_feature_to/from_string -- hb_buffer_[sg]et_contents diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 000000000..5971337cd --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,61 @@ +platform: x64 + +environment: + matrix: + - compiler: msvc + generator: Visual Studio 14 + platform: Win32 + configuration: Debug + triplet: x86-windows + - compiler: msvc + generator: Visual Studio 14 Win64 + platform: x64 + configuration: Debug + triplet: x64-windows + + - compiler: msvc + generator: Visual Studio 14 ARM + platform: ARM + configuration: Debug + + + - compiler: msys2 + MINGW_PREFIX: /c/msys2/mingw64/ + MINGW_CHOST: x86_64-w64-mingw32 + MSYS2_ARCH: x86_64 + - compiler: msys2 + MINGW_PREFIX: /c/msys2/mingw32/ + MINGW_CHOST: i686-w64-mingw32 + MSYS2_ARCH: i686 + +install: + - C:\msys64\usr\bin\bash -lc "pacman --noconfirm -S mingw-w64-x86_64-ragel" + +build_script: + - 'if "%compiler%"=="msvc" if not "%platform%"=="ARM" vcpkg install glib:%triplet% freetype:%triplet% cairo:%triplet%' + - 'if "%compiler%"=="msvc" md build' + - 'if "%compiler%"=="msvc" cd build' + - 'if "%compiler%"=="msvc" set PATH=%PATH%;C:\Program Files (x86)\MSBuild\14.0\Bin;c:\msys64\mingw64\bin' # msys2 is added just for having "ragel" on PATH + + - 'if "%compiler%"=="msvc" if "%platform%"=="ARM" cmake -DHB_HAVE_UNISCRIBE=ON -DHB_HAVE_DIRECTWRITE=ON -G "%generator%" ../' + - 'if "%compiler%"=="msvc" if not "%platform%"=="ARM" cmake -DHB_HAVE_UNISCRIBE=ON -DHB_HAVE_DIRECTWRITE=ON -DHB_HAVE_GLIB=ON -DHB_HAVE_FREETYPE=ON -DHB_BUILD_UTILS=ON -G "%generator%" -DCMAKE_TOOLCHAIN_FILE=c:/tools/vcpkg/scripts/buildsystems/vcpkg.cmake ../' + + - 'if "%compiler%"=="msvc" msbuild harfbuzz.sln /p:Configuration=%configuration% /p:Platform=%platform%' + - 'if "%compiler%"=="msvc" if not "%platform%"=="ARM" ctest --output-on-failure -C %configuration%' + + - 'if "%compiler%"=="msys2" C:\msys64\usr\bin\bash -lc "pacman --noconfirm -S mingw-w64-$MSYS2_ARCH-{freetype,cairo,icu,gettext,gobject-introspection,gcc,gcc-libs,glib2,graphite2,pkg-config,python2}"' + - 'if "%compiler%"=="msys2" C:\msys64\usr\bin\bash -lc "cd $APPVEYOR_BUILD_FOLDER; PATH=$PATH:/mingw64/bin:/mingw32/bin; ./autogen.sh --with-uniscribe --with-freetype --with-glib --with-gobject --with-cairo --with-icu --with-graphite2 --build=%MINGW_CHOST% --host=%MINGW_CHOST% --prefix=%MINGW_PREFIX%; make; make check || .ci/fail.sh"' + +cache: + - c:\tools\vcpkg\installed\ + +notifications: + - provider: Email + to: + - harfbuzz-bots-chatter@googlegroups.com + on_build_success: false + on_build_failure: true + on_build_status_changed: true + +# disable automatic tests +test: off diff --git a/autogen.sh b/autogen.sh index ff1b0c0c9..fd5c1983c 100755 --- a/autogen.sh +++ b/autogen.sh @@ -7,11 +7,11 @@ test -n "$srcdir" || srcdir=. olddir=`pwd` cd $srcdir -echo -n "checking for ragel... " -which ragel || { - echo "You need to install ragel... See http://www.complang.org/ragel/" - exit 1 -} +#echo -n "checking for ragel... " +#which ragel || { +# echo "You need to install ragel... See http://www.complang.org/ragel/" +# exit 1 +#} echo -n "checking for pkg-config... " which pkg-config || { @@ -42,5 +42,7 @@ echo "running autoreconf --force --install --verbose" autoreconf --force --install --verbose || exit $? cd $olddir -echo "running configure $@" -test -n "$NOCONFIGURE" || "$srcdir/configure" "$@" +test -n "$NOCONFIGURE" || { + echo "running configure $@" + "$srcdir/configure" "$@" +} diff --git a/configure.ac b/configure.ac index 740beb896..eee2568dc 100644 --- a/configure.ac +++ b/configure.ac @@ -1,7 +1,7 @@ AC_PREREQ([2.64]) AC_INIT([HarfBuzz], - [1.2.7], - [https://github.com/behdad/harfbuzz/issues/new], + [1.7.5], + [https://github.com/harfbuzz/harfbuzz/issues/new], [harfbuzz], [http://harfbuzz.org/]) @@ -9,8 +9,7 @@ AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_SRCDIR([src/harfbuzz.pc.in]) AC_CONFIG_HEADERS([config.h]) -AM_INIT_AUTOMAKE([1.11.1 gnits tar-ustar dist-bzip2 no-dist-gzip -Wall no-define color-tests -Wno-portability]) -AM_CONDITIONAL(AUTOMAKE_OLDER_THAN_1_13, test $am__api_version = 1.11 -o $am__api_version = 1.12) +AM_INIT_AUTOMAKE([1.13.0 gnits tar-ustar dist-bzip2 no-dist-gzip -Wall no-define color-tests -Wno-portability]) AM_SILENT_RULES([yes]) # Initialize libtool @@ -19,9 +18,13 @@ LT_PREREQ([2.2]) LT_INIT([disable-static]) # Check for programs +AC_USE_SYSTEM_EXTENSIONS AC_PROG_CC +AC_PROG_CC_C99 AM_PROG_CC_C_O AC_PROG_CXX +dnl AX_CXX_COMPILE_STDCXX(11, noext, optional) +AC_SYS_LARGEFILE PKG_PROG_PKG_CONFIG([0.20]) AM_MISSING_PROG([RAGEL], [ragel]) AM_MISSING_PROG([GIT], [git]) @@ -55,6 +58,13 @@ m4_define([hb_libtool_current], HB_LIBTOOL_VERSION_INFO=hb_libtool_current:hb_libtool_revision:hb_libtool_age 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], [ @@ -66,9 +76,9 @@ GTK_DOC_CHECK([1.15],[--flavour no-tmpl]) AM_CONDITIONAL([ENABLE_GTK_DOC], false) ]) -# Functions and headers -AC_CHECK_FUNCS(atexit mprotect sysconf getpagesize mmap isatty) -AC_CHECK_HEADERS(unistd.h sys/mman.h) +# Functions, and headers +AC_CHECK_FUNCS(atexit mprotect sysconf getpagesize mmap isatty newlocale strtod_l setlinebuf) +AC_CHECK_HEADERS(unistd.h sys/mman.h xlocale.h) # Compiler flags AC_CANONICAL_HOST @@ -78,9 +88,6 @@ if test "x$GCC" = "xyes"; then # Make symbols link locally LDFLAGS="$LDFLAGS -Bsymbolic-functions" - # Make sure we don't link to libstdc++ - CXXFLAGS="$CXXFLAGS -fno-rtti -fno-exceptions" - # Assorted warnings CXXFLAGS="$CXXFLAGS -Wcast-align" @@ -145,7 +152,7 @@ AC_ARG_WITH(glib, [Use glib @<:@default=auto@:>@])],, [with_glib=auto]) have_glib=false -GLIB_DEPS="glib-2.0 >= 2.16" +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, :) @@ -162,7 +169,7 @@ dnl =========================================================================== AC_ARG_WITH(gobject, [AS_HELP_STRING([--with-gobject=@<:@yes/no/auto@:>@], - [Use gobject @<:@default=auto@:>@])],, + [Use gobject @<:@default=no@:>@])],, [with_gobject=no]) have_gobject=false if test "x$with_gobject" = "xyes" -o "x$with_gobject" = "xauto"; then @@ -177,6 +184,7 @@ if $have_gobject; then AC_SUBST(GLIB_MKENUMS) fi AM_CONDITIONAL(HAVE_GOBJECT, $have_gobject) +AC_SUBST(have_gobject) dnl =========================================================================== @@ -287,9 +295,13 @@ AM_CONDITIONAL(HAVE_ICU_BUILTIN, $have_icu && test "x$with_icu" = "xbuiltin") dnl =========================================================================== -have_ucdn=true -if $have_glib || $have_icu && test "x$with_icu" = "xbuiltin"; then - have_ucdn=false +AC_ARG_WITH(ucdn, + [AS_HELP_STRING([--with-ucdn=@<:@yes/no@:>@], + [Use builtin UCDN library @<:@default=yes@:>@])],, + [with_ucdn=yes]) +have_ucdn=false +if test "x$with_ucdn" = "xyes"; then + have_ucdn=true fi if $have_ucdn; then AC_DEFINE(HAVE_UCDN, 1, [Have UCDN Unicode functions]) @@ -307,6 +319,16 @@ GRAPHITE2_DEPS="graphite2" 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]) @@ -334,6 +356,10 @@ if test "x$with_freetype" = "xyes" -a "x$have_freetype" != "xtrue"; then 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) + LIBS=$save_libs fi AM_CONDITIONAL(HAVE_FREETYPE, $have_freetype) @@ -401,12 +427,13 @@ if test "x$with_coretext" = "xyes" -o "x$with_coretext" = "xauto"; then else # On iOS CoreText and CoreGraphics are stand-alone frameworks if test "x$have_coretext" != "xtrue"; then - AC_CHECK_TYPE(CTFontRef, have_coretext=true,, [#include ]) + # 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" + CORETEXT_LIBS="-framework CoreText -framework CoreGraphics -framework CoreFoundation" AC_SUBST(CORETEXT_CFLAGS) AC_SUBST(CORETEXT_LIBS) fi @@ -465,16 +492,20 @@ AC_CONFIG_FILES([ Makefile src/Makefile src/hb-version.h +src/harfbuzz-config.cmake src/hb-ucdn/Makefile util/Makefile test/Makefile test/api/Makefile test/fuzzing/Makefile test/shaping/Makefile +test/shaping/data/Makefile +test/shaping/data/in-house/Makefile +test/shaping/data/text-rendering-tests/Makefile +test/subset/Makefile +test/subset/data/Makefile docs/Makefile docs/version.xml -win32/Makefile -win32/config.h.win32 ]) AC_OUTPUT @@ -484,18 +515,18 @@ AC_MSG_NOTICE([ Build configuration: Unicode callbacks (you want at least one): + Builtin (UCDN): ${have_ucdn} Glib: ${have_glib} ICU: ${have_icu} - UCDN: ${have_ucdn} -Font callbacks (the more the better): +Font callbacks (the more the merrier): FreeType: ${have_freetype} Tools used for command-line utilities: Cairo: ${have_cairo} Fontconfig: ${have_fontconfig} -Additional shapers (the more the better): +Additional shapers (the more the merrier): Graphite2: ${have_graphite2} Platform shapers (not normally needed): @@ -504,7 +535,7 @@ Platform shapers (not normally needed): DirectWrite: ${have_directwrite} Other features: - Documentation: ${have_gtk_doc} + Documentation: ${enable_gtk_doc} GObject bindings: ${have_gobject} Introspection: ${have_introspection} ]) diff --git a/docs/HarfBuzz.png b/docs/HarfBuzz.png index d58d9fc5a..771d955d0 100644 Binary files a/docs/HarfBuzz.png and b/docs/HarfBuzz.png differ diff --git a/docs/HarfBuzz.svg b/docs/HarfBuzz.svg new file mode 100644 index 000000000..4e2df2541 --- /dev/null +++ b/docs/HarfBuzz.svg @@ -0,0 +1,277 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/Makefile.am b/docs/Makefile.am index 3916801ae..a9935385b 100644 --- a/docs/Makefile.am +++ b/docs/Makefile.am @@ -67,7 +67,8 @@ EXTRA_HFILES=$(top_builddir)/src/hb-version.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.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 @@ -110,7 +111,7 @@ EXTRA_DIST += version.xml.in # for --rebuild-sections in $(SCAN_OPTIONS) e.g. $(DOC_MODULE)-sections.txt #DISTCLEANFILES += -# Comment this out if you want 'make check' to test you doc status +# 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) && \ diff --git a/docs/harfbuzz-docs.xml b/docs/harfbuzz-docs.xml index 2c43c4687..9452a92af 100644 --- a/docs/harfbuzz-docs.xml +++ b/docs/harfbuzz-docs.xml @@ -22,7 +22,7 @@ source tree is available here. Also available on - github. + github. See for release tarballs. @@ -60,7 +60,7 @@ Reference manual - Harfbuzz API + HarfBuzz API @@ -80,6 +80,7 @@ + @@ -175,6 +176,30 @@ Index of new symbols in 1.1.3 + + Index of new symbols in 1.2.3 + + + + Index of new symbols in 1.3.3 + + + + Index of new symbols in 1.4.2 + + + + Index of new symbols in 1.4.3 + + + + Index of new symbols in 1.5.0 + + + + Index of new symbols in 1.6.0 + + Index of deprecated API diff --git a/docs/harfbuzz-sections.txt b/docs/harfbuzz-sections.txt index e0dc23d9c..91faa0b75 100644 --- a/docs/harfbuzz-sections.txt +++ b/docs/harfbuzz-sections.txt @@ -9,6 +9,7 @@ HB_EXTERN hb-blob hb_blob_create hb_blob_create_sub_blob +hb_blob_copy_writable_or_fail hb_blob_destroy hb_blob_get_data hb_blob_get_data_writable @@ -41,6 +42,7 @@ 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 @@ -77,9 +79,12 @@ hb_buffer_serialize_format_to_string hb_buffer_serialize_list_formats hb_segment_properties_equal hb_segment_properties_hash +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 @@ -87,6 +92,7 @@ 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 @@ -144,13 +150,18 @@ uint8_t 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_set_invert
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
@@ -161,6 +172,7 @@ 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 @@ -193,7 +205,6 @@ 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_func hb_font_funcs_set_glyph_h_advance_func hb_font_funcs_set_glyph_h_kerning_func hb_font_funcs_set_glyph_h_origin_func @@ -201,7 +212,9 @@ hb_font_funcs_set_glyph_name_func hb_font_funcs_set_glyph_v_advance_func hb_font_funcs_set_glyph_v_kerning_func hb_font_funcs_set_glyph_v_origin_func +hb_font_funcs_set_nominal_glyph_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 @@ -216,7 +229,6 @@ 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_func_t hb_font_get_glyph_h_advance hb_font_get_glyph_h_advance_func_t hb_font_get_glyph_h_kerning @@ -235,20 +247,35 @@ hb_font_get_glyph_v_kerning hb_font_get_glyph_v_kerning_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_parent hb_font_get_ppem +hb_font_get_ptem hb_font_get_scale hb_font_get_user_data +hb_font_get_variation_glyph +hb_font_get_variation_glyph_func_t +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_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_user_data +hb_variation_t +hb_variation_from_string +hb_variation_to_string +hb_font_set_variations +hb_font_set_var_coords_design +hb_font_set_var_coords_normalized hb_font_subtract_glyph_origin_for_direction hb_font_t hb_reference_table_func_t @@ -260,7 +287,6 @@ 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_set_parent
@@ -270,6 +296,7 @@ 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_set_load_flags hb_ft_font_get_load_flags @@ -289,6 +316,7 @@ hb_glib_blob_create HB_GOBJECT_TYPE_BLOB HB_GOBJECT_TYPE_BUFFER HB_GOBJECT_TYPE_BUFFER_CONTENT_TYPE +HB_GOBJECT_TYPE_BUFFER_DIFF_FLAGS HB_GOBJECT_TYPE_BUFFER_FLAGS HB_GOBJECT_TYPE_BUFFER_SERIALIZE_FLAGS HB_GOBJECT_TYPE_BUFFER_SERIALIZE_FORMAT @@ -296,8 +324,14 @@ HB_GOBJECT_TYPE_DIRECTION HB_GOBJECT_TYPE_FACE HB_GOBJECT_TYPE_FONT HB_GOBJECT_TYPE_FONT_FUNCS +HB_GOBJECT_TYPE_GLYPH_FLAGS HB_GOBJECT_TYPE_MEMORY_MODE HB_GOBJECT_TYPE_OT_LAYOUT_GLYPH_CLASS +HB_GOBJECT_TYPE_OT_MATH_CONSTANT +HB_GOBJECT_TYPE_OT_MATH_GLYPH_PART +HB_GOBJECT_TYPE_OT_MATH_GLYPH_PART_FLAGS +HB_GOBJECT_TYPE_OT_MATH_GLYPH_VARIANT +HB_GOBJECT_TYPE_OT_MATH_KERN HB_GOBJECT_TYPE_SCRIPT HB_GOBJECT_TYPE_SHAPE_PLAN HB_GOBJECT_TYPE_UNICODE_COMBINING_CLASS @@ -312,6 +346,7 @@ HB_GOBJECT_TYPE_SET HB_GOBJECT_TYPE_USER_DATA_KEY hb_gobject_blob_get_type hb_gobject_buffer_content_type_get_type +hb_gobject_buffer_diff_flags_get_type hb_gobject_buffer_flags_get_type hb_gobject_buffer_get_type hb_gobject_buffer_serialize_flags_get_type @@ -320,8 +355,14 @@ hb_gobject_direction_get_type hb_gobject_face_get_type hb_gobject_font_funcs_get_type hb_gobject_font_get_type +hb_gobject_glyph_flags_get_type hb_gobject_memory_mode_get_type hb_gobject_ot_layout_glyph_class_get_type +hb_gobject_ot_math_constant_get_type +hb_gobject_ot_math_glyph_part_get_type +hb_gobject_ot_math_glyph_part_flags_get_type +hb_gobject_ot_math_glyph_variant_get_type +hb_gobject_ot_math_kern_get_type hb_gobject_script_get_type hb_gobject_shape_plan_get_type hb_gobject_unicode_combining_class_get_type @@ -378,12 +419,14 @@ hb_ot_shape_glyphs_closure 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_GDEF HB_OT_TAG_GPOS HB_OT_TAG_GSUB HB_OT_TAG_JSTF hb_ot_layout_collect_lookups hb_ot_layout_feature_get_lookups +hb_ot_layout_feature_with_variations_get_lookups hb_ot_layout_get_attach_points hb_ot_layout_get_glyph_class hb_ot_layout_get_glyphs_in_class @@ -404,6 +447,7 @@ hb_ot_layout_lookup_would_substitute hb_ot_layout_script_find_language hb_ot_layout_script_get_language_tags hb_ot_layout_table_choose_script +hb_ot_layout_table_find_feature_variations hb_ot_layout_table_find_script hb_ot_layout_table_get_feature_tags hb_ot_layout_table_get_script_tags @@ -416,6 +460,43 @@ Xhb_ot_layout_lookup_position Xhb_ot_layout_lookup_substitute
+
+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_NO_AXIS_INDEX +hb_ot_var_axis_t +hb_ot_var_has_data +hb_ot_var_find_axis +hb_ot_var_get_axis_count +hb_ot_var_get_axes +hb_ot_var_normalize_variations +hb_ot_var_normalize_coords +
+ +
+hb-ot-math +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 +
+
hb-ot-tag HB_OT_TAG_DEFAULT_LANGUAGE @@ -444,11 +525,12 @@ hb_set_get_population hb_set_get_user_data hb_set_has hb_set_intersect -hb_set_invert hb_set_is_empty hb_set_is_equal hb_set_next +hb_set_previous hb_set_next_range +hb_set_previous_range hb_set_reference hb_set_set hb_set_set_user_data @@ -460,8 +542,8 @@ hb_set_union
hb-shape -hb_feature_from_string hb_feature_t +hb_feature_from_string hb_feature_to_string hb_shape hb_shape_full @@ -472,6 +554,8 @@ 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 @@ -526,6 +610,8 @@ hb_unicode_script_func_t hb-uniscribe hb_uniscribe_font_get_hfont hb_uniscribe_font_get_logfontw + +hb_directwrite_shape_experimental_width
diff --git a/docs/usermanual-buffers-language-script-and-direction.xml b/docs/usermanual-buffers-language-script-and-direction.xml index 3a26c553c..9eddb71a9 100644 --- a/docs/usermanual-buffers-language-script-and-direction.xml +++ b/docs/usermanual-buffers-language-script-and-direction.xml @@ -1,7 +1,7 @@ Buffers, language, script and direction - The input to Harfbuzz is a series of Unicode characters, stored in a + The input to HarfBuzz 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 then customize the properties of the buffer. @@ -15,7 +15,7 @@ default values and ready to accept your Unicode strings. - Harfbuzz manages the memory of objects that it creates (such as + HarfBuzz manages the memory of objects that it creates (such as buffers), so you don't have to. When you have finished working on a buffer, you can call hb_buffer_destroy(): @@ -27,7 +27,7 @@ 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 + 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: @@ -53,8 +53,8 @@ void somefunc(hb_buffer_t *buffer) {
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 + 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 codepoints, but your input string is probably in one of the standard Unicode character encodings (UTF-8, UTF-16, UTF-32) diff --git a/docs/usermanual-clusters.xml b/docs/usermanual-clusters.xml index 8b64bdee3..608371b00 100644 --- a/docs/usermanual-clusters.xml +++ b/docs/usermanual-clusters.xml @@ -290,11 +290,11 @@ 0 ,3,2,4 - There's no way to differentitate between these two scenarios based + There's no way to differentiate between these two scenarios based on the cluster numbers alone. - Another problem appens with ligatures under level 2 if the + Another problem happens with ligatures under level 2 if the direction of the text is forced to opposite of its natural direction (e.g. left-to-right Arabic). But that's too much of a corner case to worry about. diff --git a/docs/usermanual-fonts-and-faces.xml b/docs/usermanual-fonts-and-faces.xml index 01fcdc99f..7de0f051a 100644 --- a/docs/usermanual-fonts-and-faces.xml +++ b/docs/usermanual-fonts-and-faces.xml @@ -6,7 +6,7 @@
- Using Harfbuzz's native OpenType implementation + Using HarfBuzz's native OpenType implementation
diff --git a/docs/usermanual-hello-harfbuzz.xml b/docs/usermanual-hello-harfbuzz.xml index 34db017ae..716b2f2dd 100644 --- a/docs/usermanual-hello-harfbuzz.xml +++ b/docs/usermanual-hello-harfbuzz.xml @@ -1,7 +1,7 @@ - Hello, Harfbuzz + Hello, HarfBuzz - Here's the simplest Harfbuzz that can possibly work. We will improve + Here's the simplest HarfBuzz that can possibly work. We will improve it later. @@ -91,23 +91,23 @@ hb_font_destroy(hb_ft_font);
- What Harfbuzz doesn't do + What HarfBuzz doesn't do The code above will take a UTF8 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. + extent of HarfBuzz's responsibility. If you are implementing a text layout engine you may have other - responsibilities, that Harfbuzz will not help you with: + responsibilities, that HarfBuzz will not help you with: - Harfbuzz won't help you with bidirectionality. If you want to + HarfBuzz won't help you with bidirectionality. If you want to lay out text with mixed Hebrew and English, you will need to - ensure that the buffer provided to Harfbuzz has those + ensure that the buffer provided to HarfBuzz has those characters in the correct layout order. This will be different from the logical order in which the Unicode text is stored. In other words, the user will hit the keys in the following @@ -127,30 +127,30 @@ ABC אבג DEF ("bidi" is short for bidirectional), and there's an algorithm as an annex to the Unicode Standard which tells you how to reorder a string from logical order into presentation order. - Before sending your string to Harfbuzz, you may need to apply the + 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 + 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, you will need to send three - strings to Harfbuzz: a, in your Roman font; + strings to HarfBuzz: a, in your Roman font; huge using your italic font; and breakfast using your Roman font again. Similarly if you change font, font size, script, language or direction within your string, you will need to shape each run - independently and then output them independently. Harfbuzz + independently and then output them independently. HarfBuzz expects to shape a run of characters sharing the same properties. - Harfbuzz won't help you with line breaking, hyphenation or + HarfBuzz won't help you with line breaking, hyphenation or justification. As mentioned above, it lays out the string along a single line of, notionally, infinite length. If you want to find out where the potential @@ -158,12 +158,12 @@ ABC אבג DEF could use the ICU library's break iterator functions. - Harfbuzz can tell you how wide a shaped piece of text is, which is + 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. If you want to layout text in paragraphs, you will probably want to send - each word of your text to Harfbuzz to determine its shaped width + each word of your text to HarfBuzz to determine its shaped width after glyph substitutions, then work out how many words will fit on a line, and then finally output each word of the line separated by a space of the correct size to fully justify the paragraph. @@ -171,12 +171,12 @@ ABC אבג DEF - As a layout engine implementor, Harfbuzz will help you with the + 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. The example we saw above enough to get us - started using Harfbuzz. Now we are going to use the remainder of - Harfbuzz's API to refine that example and improve our text shaping + started using HarfBuzz. Now we are going to use the remainder of + HarfBuzz's API to refine that example and improve our text shaping capabilities.
diff --git a/docs/usermanual-install-harfbuzz.xml b/docs/usermanual-install-harfbuzz.xml index be8ac8d12..899cc5bd6 100644 --- a/docs/usermanual-install-harfbuzz.xml +++ b/docs/usermanual-install-harfbuzz.xml @@ -1,5 +1,5 @@ - Install Harfbuzz + Install HarfBuzz
Download @@ -12,7 +12,7 @@ The canonical source tree is available here. - Also available on github. + Also available on github. The API that comes with hb.h will @@ -50,7 +50,7 @@ and hb-shape under util/. - If you are bootstraping from git, you need a few more tools before you + If you are bootstrapping from git, you need a few more tools before you can run autogen.sh for the first time. Namely, pkg-config and ragel. Again, on Ubuntu / Debian: diff --git a/docs/usermanual-what-is-harfbuzz.xml b/docs/usermanual-what-is-harfbuzz.xml index 3574d75ab..38f40cf11 100644 --- a/docs/usermanual-what-is-harfbuzz.xml +++ b/docs/usermanual-what-is-harfbuzz.xml @@ -1,7 +1,7 @@ - What is Harfbuzz? + What is HarfBuzz? - Harfbuzz is a text shaping engine. It solves + HarfBuzz is a text shaping engine. It solves the problem of selecting and positioning glyphs from a font given a Unicode string. @@ -9,17 +9,17 @@ Why do I need it? Text shaping is an integral part of preparing text for display. It - is a fairly low level operation; Harfbuzz is used directly by + is a fairly low level operation; HarfBuzz is used directly by graphic rendering libraries such as Pango, and 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 higher level + you will probably not need to use HarfBuzz - normally higher level libraries will turn text into glyphs for you. However, if you are writing a layout engine or graphics library yourself, you will need to perform text - shaping, and this is where Harfbuzz can help you. Here are some + shaping, and this is where HarfBuzz can help you. Here are some reasons why you need it: @@ -95,20 +95,20 @@ If this is something that you need to do, then you need a text shaping engine: you could use Uniscribe if you are using Windows; - you could use CoreText on OS X; or you could use Harfbuzz. In the + you could use CoreText on OS X; or you could use HarfBuzz. In the rest of this manual, we are going to assume that you are the implementor of a text layout engine.
- Why is it called Harfbuzz? + Why is it called HarfBuzz? - Harfbuzz began its life as text shaping code within the FreeType + 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 abstracted out to its own project. This project is maintained by - Behdad Esfahbod, and named Harfbuzz. Originally, it was a shaping - engine for OpenType fonts - "Harfbuzz" is the Persian + Behdad Esfahbod, and named HarfBuzz. Originally, it was a shaping + engine for OpenType fonts - "HarfBuzz" is the Persian for "open type".
diff --git a/git.mk b/git.mk index bd39ae10d..6e2708f2d 100644 --- a/git.mk +++ b/git.mk @@ -48,7 +48,7 @@ GIT_MK_URL = https://raw.githubusercontent.com/behdad/git.mk/master/git.mk # # This file knows how to handle autoconf, automake, libtool, gtk-doc, # gnome-doc-utils, yelp.m4, mallard, intltool, gsettings, dejagnu, appdata, -# appstream. +# appstream, hotdoc. # # This makefile provides the following targets: # @@ -86,6 +86,7 @@ GITIGNORE_MAINTAINERCLEANFILES_TOPLEVEL = \ ar-lib \ compile \ config.guess \ + config.rpath \ config.sub \ depcomp \ install-sh \ @@ -120,6 +121,47 @@ GITIGNORE_MAINTAINERCLEANFILES_M4_LIBTOOL = \ 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` @@ -208,6 +250,15 @@ $(srcdir)/.gitignore: Makefile.am $(top_srcdir)/git.mk "*/*.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 \ @@ -235,6 +286,7 @@ $(srcdir)/.gitignore: Makefile.am $(top_srcdir)/git.mk 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 \ @@ -243,6 +295,7 @@ $(srcdir)/.gitignore: Makefile.am $(top_srcdir)/git.mk po/POTFILES \ po/Rules-quot \ po/stamp-it \ + po/stamp-po \ po/.intltool-merge-cache \ "po/*.gmo" \ "po/*.header" \ @@ -274,7 +327,7 @@ $(srcdir)/.gitignore: Makefile.am $(top_srcdir)/git.mk if test "x$(am__dirstamp)" = x; then :; else \ echo "$(am__dirstamp)"; \ fi; \ - if test "x$(LTCOMPILE)" = x -a "x$(LTCXXCOMPILE)" = x -a "x$(GTKDOC_RUN)" = x; then :; else \ + 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" \ diff --git a/harfbuzz.doap b/harfbuzz.doap index d2896ebef..07699697f 100644 --- a/harfbuzz.doap +++ b/harfbuzz.doap @@ -13,7 +13,7 @@ + rdf:resource="https://github.com/harfbuzz/harfbuzz/issues" /> diff --git a/m4/ax_cxx_compile_stdcxx.m4 b/m4/ax_cxx_compile_stdcxx.m4 new file mode 100644 index 000000000..5032bba80 --- /dev/null +++ b/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, ""); + + } + + // http://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/m4/ax_pthread.m4 b/m4/ax_pthread.m4 index d90de34d1..5fbf9fe0d 100644 --- a/m4/ax_pthread.m4 +++ b/m4/ax_pthread.m4 @@ -1,5 +1,5 @@ # =========================================================================== -# http://www.gnu.org/software/autoconf-archive/ax_pthread.html +# https://www.gnu.org/software/autoconf-archive/ax_pthread.html # =========================================================================== # # SYNOPSIS @@ -19,10 +19,10 @@ # 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 link it with them as well. e.g. you should link with +# 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 threads programs, you may wish to use these +# 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" @@ -30,8 +30,8 @@ # CC="$PTHREAD_CC" # # In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant -# has a nonstandard name, defines PTHREAD_CREATE_JOINABLE to that name -# (e.g. PTHREAD_CREATE_UNDETACHED on AIX). +# 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 @@ -67,7 +67,7 @@ # Public License for more details. # # You should have received a copy of the GNU General Public License along -# with this program. If not, see . +# 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 @@ -82,35 +82,40 @@ # modified version of the Autoconf Macro, you may extend this special # exception to the GPL to apply to your modified version as well. -#serial 18 +#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 True64 or Sequent). +# 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_LIBS$PTHREAD_CFLAGS" != x; then - save_CFLAGS="$CFLAGS" +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" - save_LIBS="$LIBS" LIBS="$PTHREAD_LIBS $LIBS" - AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS]) - AC_TRY_LINK_FUNC(pthread_join, ax_pthread_ok=yes) - AC_MSG_RESULT($ax_pthread_ok) - if test x"$ax_pthread_ok" = xno; then + 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 - LIBS="$save_LIBS" - CFLAGS="$save_CFLAGS" + 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 @@ -123,7 +128,7 @@ fi # 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 -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config" +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: @@ -132,68 +137,225 @@ ax_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mt # 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) -# -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able) -# lthread: LinuxThreads port on FreeBSD (also preferred to -pthread) -# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads) -# -pthreads: Solaris/gcc -# -mthreads: Mingw32/gcc, Lynx/gcc +# -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 too; -# also defines -D_REENTRANT) -# ... -mt is also the pthreads flag for HP/aCC +# 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 +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. (We need to link with -pthreads/-mt/ - # -lpthread.) (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 just look for -pthreads and -lpthread first: + # 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="-pthreads pthread -mt -pthread $ax_pthread_flags" - ;; - - darwin*) - ax_pthread_flags="-pthread $ax_pthread_flags" + ax_pthread_flags="-mt,pthread pthread $ax_pthread_flags" ;; esac -if test x"$ax_pthread_ok" = xno; then -for flag in $ax_pthread_flags; do +# GCC generally uses -pthread, or -pthreads on some platforms (e.g. SPARC) - case $flag in +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 $flag]) - PTHREAD_CFLAGS="$flag" + 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) - if test x"$ax_pthread_config" = xno; then continue; fi + 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$flag]) - PTHREAD_LIBS="-l$flag" + AC_MSG_CHECKING([for the pthreads library -l$ax_pthread_try_flag]) + PTHREAD_LIBS="-l$ax_pthread_try_flag" ;; esac - save_LIBS="$LIBS" - save_CFLAGS="$CFLAGS" - LIBS="$PTHREAD_LIBS $LIBS" + 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 @@ -204,7 +366,11 @@ for flag in $ax_pthread_flags; do # 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; @@ -213,16 +379,14 @@ for flag in $ax_pthread_flags; do pthread_attr_init(&attr); pthread_cleanup_push(routine, 0); pthread_cleanup_pop(0) /* ; */])], - [ax_pthread_ok=yes], - []) + [ax_pthread_ok=yes], + []) - LIBS="$save_LIBS" - CFLAGS="$save_CFLAGS" + CFLAGS="$ax_pthread_save_CFLAGS" + LIBS="$ax_pthread_save_LIBS" - AC_MSG_RESULT($ax_pthread_ok) - if test "x$ax_pthread_ok" = xyes; then - break; - fi + AC_MSG_RESULT([$ax_pthread_ok]) + AS_IF([test "x$ax_pthread_ok" = "xyes"], [break]) PTHREAD_LIBS="" PTHREAD_CFLAGS="" @@ -230,76 +394,88 @@ done fi # Various other checks: -if test "x$ax_pthread_ok" = xyes; then - save_LIBS="$LIBS" - LIBS="$PTHREAD_LIBS $LIBS" - save_CFLAGS="$CFLAGS" +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_MSG_CHECKING([for joinable pthread attribute]) - attr_name=unknown - for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do - AC_LINK_IFELSE([AC_LANG_PROGRAM([#include ], - [int attr = $attr; return attr /* ; */])], - [attr_name=$attr; break], - []) - done - AC_MSG_RESULT($attr_name) - if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then - AC_DEFINE_UNQUOTED(PTHREAD_CREATE_JOINABLE, $attr_name, - [Define to necessary symbol if this constant - uses a non-standard name on your system.]) - fi + 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_MSG_CHECKING([if more special flags are required for pthreads]) - flag=no - case ${host_os} in - aix* | freebsd* | darwin*) flag="-D_THREAD_SAFE";; - osf* | hpux*) flag="-D_REENTRANT";; - solaris*) - if test "$GCC" = "yes"; then - flag="-D_REENTRANT" - else - flag="-mt -D_REENTRANT" - fi - ;; - esac - AC_MSG_RESULT(${flag}) - if test "x$flag" != xno; then - PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS" - fi + 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]) + [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"], - AC_DEFINE([HAVE_PTHREAD_PRIO_INHERIT], 1, [Have PTHREAD_PRIO_INHERIT.])) + 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 + ]) - LIBS="$save_LIBS" - CFLAGS="$save_CFLAGS" + CFLAGS="$ax_pthread_save_CFLAGS" + LIBS="$ax_pthread_save_LIBS" - # More AIX lossage: must compile with xlc_r or cc_r - if test x"$GCC" != xyes; then - AC_CHECK_PROGS(PTHREAD_CC, xlc_r cc_r, ${CC}) - else - PTHREAD_CC=$CC + # 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 -else - PTHREAD_CC="$CC" fi -AC_SUBST(PTHREAD_LIBS) -AC_SUBST(PTHREAD_CFLAGS) -AC_SUBST(PTHREAD_CC) +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]) +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 diff --git a/mingw32.sh b/mingw32.sh new file mode 100755 index 000000000..67744051c --- /dev/null +++ b/mingw32.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +target=i686-w64-mingw32 + +unset CC +unset CXX +unset CPP +unset LD +unset LDFLAGS +unset CFLAGS +unset CXXFLAGS +unset PKG_CONFIG_PATH + +# Removed -static from the following +export CFLAGS="-static-libgcc" +export CXXFLAGS="-static-libgcc -static-libstdc++" +export CPPFLAGS="-I$HOME/.local/$target/include -O2" +export LDFLAGS=-L$HOME/.local/$target/lib +export PKG_CONFIG_LIBDIR=$HOME/.local/$target/lib/pkgconfig +export PATH=$HOME/.local/$target/bin:$PATH + +../configure --build=`../config.guess` --host=$target --prefix=$HOME/.local/$target "$@" diff --git a/mingw64.sh b/mingw64.sh new file mode 100755 index 000000000..49a143179 --- /dev/null +++ b/mingw64.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +target=x86_64-w64-mingw32 + +unset CC +unset CXX +unset CPP +unset LD +unset LDFLAGS +unset CFLAGS +unset CXXFLAGS +unset PKG_CONFIG_PATH + +# Removed -static from the following +export CFLAGS="-static-libgcc" +export CXXFLAGS="-static-libgcc -static-libstdc++" +export CPPFLAGS="-I$HOME/.local/$target/include -O2" +export LDFLAGS=-L$HOME/.local/$target/lib +export PKG_CONFIG_LIBDIR=$HOME/.local/$target/lib/pkgconfig +export PATH=$HOME/.local/$target/bin:$PATH + +../configure --build=`../config.guess` --host=$target --prefix=$HOME/.local/$target "$@" diff --git a/replace-enum-strings.cmake b/replace-enum-strings.cmake new file mode 100644 index 000000000..42fdbff90 --- /dev/null +++ b/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/src/Makefile.am b/src/Makefile.am index 49e349bf5..2dcaa7fb9 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,5 +1,6 @@ # Process this file with automake to produce Makefile.in +NULL = SUBDIRS = DIST_SUBDIRS = BUILT_SOURCES = @@ -8,12 +9,14 @@ CLEANFILES = DISTCLEANFILES = MAINTAINERCLEANFILES = DISTCHECK_CONFIGURE_FLAGS = --enable-introspection +TESTS = +check_PROGRAMS = # The following warning options are useful for debugging: -Wpadded #AM_CXXFLAGS = # Convenience targets: -lib: $(BUILT_SOURCES) libharfbuzz.la +lib: $(BUILT_SOURCES) libharfbuzz.la libharfbuzz-subset.la fuzzing: $(BUILT_SOURCES) libharfbuzz-fuzzing.la lib_LTLIBRARIES = libharfbuzz.la @@ -25,11 +28,21 @@ HBLIBS = HBNONPCLIBS = HBDEPS = HBSOURCES = $(HB_BASE_sources) +HBSOURCES += $(HB_BASE_RAGEL_GENERATED_sources) HBHEADERS = $(HB_BASE_headers) -HBNODISTHEADERS = $(HB_NODIST_headers) + +if WITH_LIBSTDCXX +HBNOLIBCXXCFLAGS = +else +# Make sure we don't link to libstdc++ +# No threadsafe statics in C++ as we do it ourselves +HBCFLAGS += -fno-exceptions +HBNOLIBCXXFLAGS = -fno-threadsafe-statics -fno-rtti +endif if HAVE_OT HBSOURCES += $(HB_OT_sources) +HBSOURCES += $(HB_OT_RAGEL_GENERATED_sources) HBHEADERS += $(HB_OT_headers) endif @@ -97,6 +110,9 @@ SUBDIRS += hb-ucdn HBCFLAGS += -I$(srcdir)/hb-ucdn HBLIBS += hb-ucdn/libhb-ucdn.la HBSOURCES += $(HB_UCDN_sources) +hb-ucdn/libhb-ucdn.la: ucdn +ucdn: + @$(MAKE) $(AM_MAKEFLAGS) -C hb-ucdn endif DIST_SUBDIRS += hb-ucdn @@ -108,40 +124,69 @@ HBLIBS += $(HBNONPCLIBS) if OS_WIN32 export_symbols = -export-symbols harfbuzz.def harfbuzz_def_dependency = harfbuzz.def -libharfbuzz_la_LINK = $(CXXLINK) $(libharfbuzz_la_LDFLAGS) +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 -# Use a C linker for GCC, not C++; Don't link to libstdc++ if HAVE_GCC -libharfbuzz_la_LINK = $(LINK) $(libharfbuzz_la_LDFLAGS) +# Use a C linker for GCC, not C++; Don't link to libstdc++ +chosen_linker = $(LINK) else -libharfbuzz_la_LINK = $(CXXLINK) $(libharfbuzz_la_LDFLAGS) +chosen_linker = $(CXXLINK) +endif endif endif -libharfbuzz_la_SOURCES = $(HBSOURCES) $(HBHEADERS) $(HBNODISTHEADERS) -libharfbuzz_la_CPPFLAGS = $(HBCFLAGS) -libharfbuzz_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(HB_LIBTOOL_VERSION_INFO) $(export_symbols) -no-undefined +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) $(HBNOLIBCXXFLAGS) +libharfbuzz_la_LDFLAGS = $(base_link_flags) $(export_symbols) libharfbuzz_la_LIBADD = $(HBLIBS) EXTRA_libharfbuzz_la_DEPENDENCIES = $(harfbuzz_def_dependency) pkginclude_HEADERS = $(HBHEADERS) -nodist_pkginclude_HEADERS = $(HBNODISTHEADERS) +nodist_pkginclude_HEADERS = pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = harfbuzz.pc -EXTRA_DIST += harfbuzz.pc.in +cmakedir = $(libdir)/cmake/harfbuzz +cmake_DATA = harfbuzz-config.cmake +EXTRA_DIST += harfbuzz.pc.in harfbuzz-config.cmake.in -FUZZING_CPPFLAGS= \ +lib_LTLIBRARIES += libharfbuzz-subset.la +libharfbuzz_subset_la_SOURCES = $(HB_SUBSET_sources) +libharfbuzz_subset_la_CPPFLAGS = $(HBCFLAGS) +libharfbuzz_subset_la_LDFLAGS = $(base_link_flags) $(export_symbols_subset) +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 + +FUZZING_CPPFLAGS = \ -DHB_NDEBUG \ -DHB_MAX_NESTING_LEVEL=3 \ -DHB_SANITIZE_MAX_EDITS=3 \ - -DHB_BUFFER_MAX_EXPANSION_FACTOR=3 \ + -DHB_SANITIZE_MAX_OPS_FACTOR=3 \ + -DHB_SANITIZE_MAX_OPS_MIN=128 \ + -DHB_BUFFER_MAX_LEN_FACTOR=3 \ -DHB_BUFFER_MAX_LEN_MIN=8 \ -DHB_BUFFER_MAX_LEN_DEFAULT=128 \ + -DHB_BUFFER_MAX_OPS_FACTOR=8 \ + -DHB_BUFFER_MAX_OPS_MIN=64 \ + -DHB_BUFFER_MAX_OPS_DEFAULT=1024 \ $(NULL) EXTRA_LTLIBRARIES = libharfbuzz-fuzzing.la -libharfbuzz_fuzzing_la_LINK = $(libharfbuzz_la_LINK) +libharfbuzz_fuzzing_la_LINK = $(chosen_linker) $(libharfbuzz_fuzzing_la_LDFLAGS) libharfbuzz_fuzzing_la_SOURCES = $(libharfbuzz_la_SOURCES) -libharfbuzz_fuzzing_la_CPPFLAGS = $(libharfbuzz_la_CPPFLAGS) $(FUZZING_CPPFLAGS) -libharfbuzz_fuzzing_la_LDFLAGS = $(libharfbuzz_la_LDFLAGS) +libharfbuzz_fuzzing_la_CPPFLAGS = $(HBCFLAGS) $(FUZZING_CPPFLAGS) +libharfbuzz_fuzzing_la_LDFLAGS = $(AM_LDFLAGS) libharfbuzz_fuzzing_la_LIBADD = $(libharfbuzz_la_LIBADD) EXTRA_libharfbuzz_fuzzing_la_DEPENDENCIES = $(EXTRA_libharfbuzz_la_DEPENDENCIES) CLEANFILES += libharfbuzz-fuzzing.la @@ -155,9 +200,10 @@ HBHEADERS += $(HB_ICU_headers) else lib_LTLIBRARIES += libharfbuzz-icu.la libharfbuzz_icu_la_SOURCES = $(HB_ICU_sources) -libharfbuzz_icu_la_CPPFLAGS = $(ICU_CFLAGS) -libharfbuzz_icu_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(HB_LIBTOOL_VERSION_INFO) -no-undefined +libharfbuzz_icu_la_CPPFLAGS = $(HBCFLAGS) $(ICU_CFLAGS) +libharfbuzz_icu_la_LDFLAGS = $(base_link_flags) $(export_symbols_icu) 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 @@ -166,13 +212,15 @@ EXTRA_DIST += harfbuzz-icu.pc.in if HAVE_GOBJECT lib_LTLIBRARIES += libharfbuzz-gobject.la -libharfbuzz_gobject_la_SOURCES = $(HB_GOBJECT_sources) -nodist_libharfbuzz_gobject_la_SOURCES = $(HB_GOBJECT_ENUM_sources) -libharfbuzz_gobject_la_CPPFLAGS = $(GOBJECT_CFLAGS) -libharfbuzz_gobject_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(HB_LIBTOOL_VERSION_INFO) -no-undefined +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) $(HBNOLIBCXXFLAGS) $(GOBJECT_CFLAGS) +libharfbuzz_gobject_la_LDFLAGS = $(base_link_flags) libharfbuzz_gobject_la_LIBADD = $(GOBJECT_LIBS) libharfbuzz.la -pkginclude_HEADERS += $(HB_GOBJECT_headers) -nodist_pkginclude_HEADERS += $(HB_GOBJECT_ENUM_headers) +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 += \ @@ -212,23 +260,27 @@ EXTRA_DIST += \ CLEANFILES += $(pkgconfig_DATA) -CLEANFILES += harfbuzz.def +DEF_FILES = harfbuzz.def harfbuzz-subset.def harfbuzz-icu.def +if HAVE_GOBJECT +DEF_FILES += harfbuzz-gobject.def +endif +check: $(DEF_FILES) # For check-symbols.sh +CLEANFILES += $(DEF_FILES) harfbuzz.def: $(HBHEADERS) $(HBNODISTHEADERS) - $(AM_V_GEN) (echo EXPORTS; \ - (cat $^ || echo 'hb_ERROR ()' ) | \ - $(EGREP) '^hb_.* \(' | \ - sed -e 's/ (.*//' | \ - LANG=C sort; \ - echo LIBRARY libharfbuzz-0.dll; \ - ) >"$@" - @ ! grep -q hb_ERROR "$@" \ - || ($(RM) "$@"; false) + $(AM_V_GEN) headers="$^" $(srcdir)/gen-def.py $@ +harfbuzz-subset.def: $(HB_SUBSET_headers) + $(AM_V_GEN) headers="$^" $(srcdir)/gen-def.py $@ +harfbuzz-icu.def: $(HB_ICU_headers) + $(AM_V_GEN) headers="$^" $(srcdir)/gen-def.py $@ +harfbuzz-gobject.def: $(HB_GOBJECT_headers) + $(AM_V_GEN) headers="$^" $(srcdir)/gen-def.py $@ GENERATORS = \ gen-arabic-table.py \ gen-indic-table.py \ gen-use-table.py \ + gen-def.py \ $(NULL) EXTRA_DIST += $(GENERATORS) @@ -251,21 +303,16 @@ built-sources: $(BUILT_SOURCES) .PHONY: unicode-tables arabic-table indic-table use-table built-sources RAGEL_GENERATED = \ - $(srcdir)/hb-buffer-deserialize-json.hh \ - $(srcdir)/hb-buffer-deserialize-text.hh \ - $(srcdir)/hb-ot-shape-complex-indic-machine.hh \ - $(srcdir)/hb-ot-shape-complex-myanmar-machine.hh \ - $(srcdir)/hb-ot-shape-complex-use-machine.hh \ + $(patsubst %,$(srcdir)/%,$(HB_BASE_RAGEL_GENERATED_sources)) \ + $(patsubst %,$(srcdir)/%,$(HB_OT_RAGEL_GENERATED_sources)) \ $(NULL) BUILT_SOURCES += $(RAGEL_GENERATED) EXTRA_DIST += \ - hb-buffer-deserialize-json.rl \ - hb-buffer-deserialize-text.rl \ - hb-ot-shape-complex-indic-machine.rl \ - hb-ot-shape-complex-myanmar-machine.rl \ - hb-ot-shape-complex-use-machine.rl \ + $(HB_BASE_RAGEL_sources) \ + $(HB_OT_RAGEL_sources) \ $(NULL) -MAINTAINERCLEANFILES += $(RAGEL_GENERATED) +# 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) @@ -301,30 +348,55 @@ test_buffer_serialize_LDADD = libharfbuzz.la $(HBLIBS) dist_check_SCRIPTS = \ check-c-linkage-decls.sh \ - check-defs.sh \ + check-externs.sh \ check-header-guards.sh \ check-includes.sh \ - check-libstdc++.sh \ check-static-inits.sh \ check-symbols.sh \ $(NULL) +TESTS += $(dist_check_SCRIPTS) -check_PROGRAMS = \ - test-ot-tag \ +if !WITH_LIBSTDCXX +dist_check_SCRIPTS += \ + check-libstdc++.sh \ $(NULL) -test_ot_color_SOURCES = hb-ot-color.cc -test_ot_color_CPPFLAGS = $(HBCFLAGS) -DMAIN -test_ot_color_LDADD = libharfbuzz.la $(HBLIBS) +endif + +check_PROGRAMS += \ + dump-indic-data \ + dump-khmer-data \ + dump-myanmar-data \ + dump-use-data \ + $(NULL) + +dump_indic_data_SOURCES = dump-indic-data.cc hb-ot-shape-complex-indic-table.cc +dump_indic_data_CPPFLAGS = $(HBCFLAGS) +dump_indic_data_LDADD = libharfbuzz.la $(HBLIBS) +dump_khmer_data_SOURCES = dump-khmer-data.cc hb-ot-shape-complex-indic-table.cc +dump_khmer_data_CPPFLAGS = $(HBCFLAGS) +dump_khmer_data_LDADD = libharfbuzz.la $(HBLIBS) +dump_myanmar_data_SOURCES = dump-myanmar-data.cc hb-ot-shape-complex-indic-table.cc +dump_myanmar_data_CPPFLAGS = $(HBCFLAGS) +dump_myanmar_data_LDADD = libharfbuzz.la $(HBLIBS) +dump_use_data_SOURCES = dump-use-data.cc hb-ot-shape-complex-use-table.cc +dump_use_data_CPPFLAGS = $(HBCFLAGS) +dump_use_data_LDADD = libharfbuzz.la $(HBLIBS) + +check_PROGRAMS += test-ot-tag test-unicode-ranges +TESTS += test-ot-tag test-unicode-ranges + test_ot_tag_SOURCES = hb-ot-tag.cc test_ot_tag_CPPFLAGS = $(HBCFLAGS) -DMAIN test_ot_tag_LDADD = libharfbuzz.la $(HBLIBS) -TESTS = $(dist_check_SCRIPTS) $(check_PROGRAMS) +test_unicode_ranges_SOURCES = test-unicode-ranges.cc +test_unicode_ranges_LDADD = libharfbuzz.la $(HBLIBS) + TESTS_ENVIRONMENT = \ srcdir="$(srcdir)" \ MAKE="$(MAKE) $(AM_MAKEFLAGS)" \ HBSOURCES="$(HBSOURCES)" \ - HBHEADERS="$(HBHEADERS) $(HBNODISTHEADERS)" \ + HBHEADERS="$(HBHEADERS)" \ $(NULL) if HAVE_INTROSPECTION @@ -354,12 +426,9 @@ HarfBuzz_0_0_gir_LIBS = \ $(NULL) HarfBuzz_0_0_gir_FILES = \ $(HBHEADERS) \ - $(HBNODISTHEADERS) \ $(HBSOURCES) \ - $(HB_GOBJECT_ENUM_sources) \ - $(HB_GOBJECT_ENUM_headers) \ $(HB_GOBJECT_sources) \ - $(HB_GOBJECT_STRUCTS_headers) \ + $(HB_GOBJECT_headers) \ $(NULL) girdir = $(datadir)/gir-1.0 diff --git a/src/Makefile.sources b/src/Makefile.sources index a40de858a..fc3383b80 100644 --- a/src/Makefile.sources +++ b/src/Makefile.sources @@ -1,17 +1,14 @@ -NULL = - # Base and default-included sources and headers HB_BASE_sources = \ hb-atomic-private.hh \ hb-blob.cc \ - hb-buffer-deserialize-json.hh \ - hb-buffer-deserialize-text.hh \ hb-buffer-private.hh \ hb-buffer-serialize.cc \ hb-buffer.cc \ - hb-cache-private.hh \ hb-common.cc \ + hb-debug.hh \ + hb-dsalgs.hh \ hb-face-private.hh \ hb-face.cc \ hb-font-private.hh \ @@ -20,17 +17,23 @@ HB_BASE_sources = \ hb-object-private.hh \ hb-open-file-private.hh \ hb-open-type-private.hh \ + hb-ot-cbdt-table.hh \ hb-ot-cmap-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-maxp-table.hh \ hb-ot-name-table.hh \ hb-ot-os2-table.hh \ + hb-ot-os2-unicode-ranges.hh \ + hb-ot-post-macroman.hh \ hb-ot-post-table.hh \ hb-ot-tag.cc \ hb-private.hh \ + hb-set-digest-private.hh \ hb-set-private.hh \ hb-set.cc \ hb-shape.cc \ @@ -40,12 +43,22 @@ HB_BASE_sources = \ hb-shaper-impl-private.hh \ hb-shaper-private.hh \ hb-shaper.cc \ + hb-string-array.hh \ hb-unicode-private.hh \ hb-unicode.cc \ hb-utf-private.hh \ hb-warning.cc \ $(NULL) +HB_BASE_RAGEL_GENERATED_sources = \ + hb-buffer-deserialize-json.hh \ + hb-buffer-deserialize-text.hh \ + $(NULL) +HB_BASE_RAGEL_sources = \ + hb-buffer-deserialize-json.rl \ + hb-buffer-deserialize-text.rl \ + $(NULL) + HB_BASE_headers = \ hb.h \ hb-blob.h \ @@ -58,19 +71,24 @@ HB_BASE_headers = \ hb-shape.h \ hb-shape-plan.h \ hb-unicode.h \ - $(NULL) - -HB_NODIST_headers = \ hb-version.h \ $(NULL) -HB_FALLBACK_sources = hb-fallback-shape.cc +HB_FALLBACK_sources = \ + hb-fallback-shape.cc \ + $(NULL) HB_OT_sources = \ - hb-ot-color.cc \ - hb-ot-cpal-table.hh \ + hb-aat-layout.cc \ + hb-aat-layout-common-private.hh \ + hb-aat-layout-ankr-table.hh \ + hb-aat-layout-kerx-table.hh \ + hb-aat-layout-morx-table.hh \ + hb-aat-layout-trak-table.hh \ + hb-aat-layout-private.hh \ hb-ot-font.cc \ hb-ot-layout.cc \ + hb-ot-layout-base-table.hh \ hb-ot-layout-common-private.hh \ hb-ot-layout-gdef-table.hh \ hb-ot-layout-gpos-table.hh \ @@ -78,8 +96,12 @@ HB_OT_sources = \ hb-ot-layout-gsub-table.hh \ hb-ot-layout-jstf-table.hh \ hb-ot-layout-private.hh \ + hb-ot-color.cc \ + hb-ot-cpal-table.hh \ hb-ot-map.cc \ hb-ot-map-private.hh \ + hb-ot-math.cc \ + hb-ot-math-table.hh \ hb-ot-shape.cc \ hb-ot-shape-complex-arabic.cc \ hb-ot-shape-complex-arabic-fallback.hh \ @@ -90,15 +112,15 @@ HB_OT_sources = \ hb-ot-shape-complex-hangul.cc \ hb-ot-shape-complex-hebrew.cc \ hb-ot-shape-complex-indic.cc \ - hb-ot-shape-complex-indic-machine.hh \ hb-ot-shape-complex-indic-private.hh \ hb-ot-shape-complex-indic-table.cc \ + hb-ot-shape-complex-khmer-private.hh \ + hb-ot-shape-complex-khmer.cc \ + hb-ot-shape-complex-myanmar-private.hh \ hb-ot-shape-complex-myanmar.cc \ - hb-ot-shape-complex-myanmar-machine.hh \ hb-ot-shape-complex-thai.cc \ hb-ot-shape-complex-tibetan.cc \ hb-ot-shape-complex-use.cc \ - hb-ot-shape-complex-use-machine.hh \ hb-ot-shape-complex-use-private.hh \ hb-ot-shape-complex-use-table.cc \ hb-ot-shape-complex-private.hh \ @@ -107,6 +129,24 @@ HB_OT_sources = \ hb-ot-shape-fallback-private.hh \ hb-ot-shape-fallback.cc \ hb-ot-shape-private.hh \ + hb-ot-var.cc \ + hb-ot-var-avar-table.hh \ + hb-ot-var-fvar-table.hh \ + hb-ot-var-hvar-table.hh \ + hb-ot-var-mvar-table.hh \ + $(NULL) + +HB_OT_RAGEL_GENERATED_sources = \ + hb-ot-shape-complex-indic-machine.hh \ + hb-ot-shape-complex-khmer-machine.hh \ + hb-ot-shape-complex-myanmar-machine.hh \ + hb-ot-shape-complex-use-machine.hh \ + $(NULL) +HB_OT_RAGEL_sources = \ + hb-ot-shape-complex-indic-machine.rl \ + hb-ot-shape-complex-khmer-machine.rl \ + hb-ot-shape-complex-myanmar-machine.rl \ + hb-ot-shape-complex-use-machine.rl \ $(NULL) HB_OT_headers = \ @@ -114,8 +154,11 @@ HB_OT_headers = \ hb-ot-color.h \ hb-ot-font.h \ hb-ot-layout.h \ + hb-ot-math.h \ + hb-ot-base.h \ hb-ot-shape.h \ hb-ot-tag.h \ + hb-ot-var.h \ $(NULL) # Optional Sources and Headers with external deps @@ -147,8 +190,26 @@ HB_UCDN_sources = hb-ucdn.cc HB_ICU_sources = hb-icu.cc HB_ICU_headers = hb-icu.h -HB_GOBJECT_sources = hb-gobject-structs.cc -HB_GOBJECT_STRUCTS_headers = hb-gobject-structs.h -HB_GOBJECT_headers = hb-gobject.h $(HB_GOBJECT_STRUCTS_headers) +# Sources for libharfbuzz-subset +HB_SUBSET_sources = \ + hb-subset.cc \ + hb-subset-glyf.cc \ + hb-subset-input.cc \ + hb-subset-plan.cc \ + $(NULL) + +HB_SUBSET_headers = \ + hb-subset.h \ + hb-subset-glyf.hh \ + hb-subset-plan.hh \ + hb-subset-private.hh \ + $(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/src/check-c-linkage-decls.sh b/src/check-c-linkage-decls.sh index b10310f53..8234abc45 100755 --- a/src/check-c-linkage-decls.sh +++ b/src/check-c-linkage-decls.sh @@ -7,18 +7,17 @@ test -z "$srcdir" && srcdir=. stat=0 test "x$HBHEADERS" = x && HBHEADERS=`cd "$srcdir"; find . -maxdepth 1 -name 'hb*.h'` -test "x$HBSOURCES" = x && HBSOURCES=`cd "$srcdir"; find . -maxdepth 1 -name 'hb*.h'` - +test "x$HBSOURCES" = x && HBSOURCES=`cd "$srcdir"; find . -maxdepth 1 -name 'hb*.cc'` for x in $HBHEADERS; do - test -f $srcdir/$x && x=$srcdir/$x + test -f "$srcdir/$x" -a ! -f "$x" && x="$srcdir/$x" if ! grep -q HB_BEGIN_DECLS "$x" || ! grep -q HB_END_DECLS "$x"; then echo "Ouch, file $x does not have HB_BEGIN_DECLS / HB_END_DECLS, but it should" stat=1 fi done for x in $HBSOURCES; do - test -f $srcdir/$x && x=$srcdir/$x + test -f "$srcdir/$x" -a ! -f "$x" && x="$srcdir/$x" if grep -q HB_BEGIN_DECLS "$x" || grep -q HB_END_DECLS "$x"; then echo "Ouch, file $x has HB_BEGIN_DECLS / HB_END_DECLS, but it shouldn't" stat=1 diff --git a/src/check-defs.sh b/src/check-defs.sh deleted file mode 100755 index 65a24670e..000000000 --- a/src/check-defs.sh +++ /dev/null @@ -1,44 +0,0 @@ -#!/bin/sh - -LC_ALL=C -export LC_ALL - -test -z "$srcdir" && srcdir=. -test -z "$MAKE" && MAKE=make -stat=0 - -if which nm 2>/dev/null >/dev/null; then - : -else - echo "check-defs.sh: 'nm' not found; skipping test" - exit 77 -fi - -defs="harfbuzz.def" -$MAKE $defs > /dev/null -tested=false -for def in $defs; do - lib=`echo "$def" | sed 's/[.]def$//;s@.*/@@'` - so=.libs/lib${lib}.so - - EXPORTED_SYMBOLS="`nm "$so" | grep ' [BCDGINRSTVW] ' | grep -v ' _fini\>\| _init\>\| _fdata\>\| _ftext\>\| _fbss\>\| __bss_start\>\| __bss_start__\>\| __bss_end__\>\| _edata\>\| _end\>\| _bss_end__\>\| __end__\>\| __gcov_flush\>\| llvm_' | cut -d' ' -f3`" - - if test -f "$so"; then - - echo "Checking that $so has the same symbol list as $def" - { - echo EXPORTS - echo "$EXPORTED_SYMBOLS" - # cheat: copy the last line from the def file! - tail -n1 "$def" - } | diff "$def" - >&2 || stat=1 - - tested=true - fi -done -if ! $tested; then - echo "check-defs.sh: libharfbuzz shared library not found; skipping test" - exit 77 -fi - -exit $stat diff --git a/src/check-externs.sh b/src/check-externs.sh new file mode 100755 index 000000000..a6de37535 --- /dev/null +++ b/src/check-externs.sh @@ -0,0 +1,22 @@ +#!/bin/sh + +LC_ALL=C +export LC_ALL + +test -z "$srcdir" && srcdir=. +stat=0 + +test "x$HBHEADERS" = x && HBHEADERS=`cd "$srcdir"; find . -maxdepth 1 -name 'hb*.h'` +test "x$EGREP" = x && EGREP='grep -E' + + +echo 'Checking that all public symbols are exported with HB_EXTERN' + +for x in $HBHEADERS; do + test -f "$srcdir/$x" -a ! -f "$x" && x="$srcdir/$x" + $EGREP -B1 -n '^hb_' /dev/null "$x" | + $EGREP -v '(^--|:hb_|-HB_EXTERN )' -A1 +done | +grep . >&2 && stat=1 + +exit $stat diff --git a/src/check-header-guards.sh b/src/check-header-guards.sh index 09c5ea8b2..b67640fc1 100755 --- a/src/check-header-guards.sh +++ b/src/check-header-guards.sh @@ -6,11 +6,11 @@ export LC_ALL test -z "$srcdir" && srcdir=. stat=0 -test "x$HBHEADERS" = x && HBHEADERS=`cd "$srcdir"; find . -maxdepth 1 -name 'hb*.h'` +test "x$HBHEADERS" = x && HBHEADERS=`cd "$srcdir"; find . -maxdepth 1 -name 'hb*.h' ! -name 'hb-gobject-structs.h'` test "x$HBSOURCES" = x && HBSOURCES=`cd "$srcdir"; find . -maxdepth 1 -name 'hb-*.cc' -or -name 'hb-*.hh'` for x in $HBHEADERS $HBSOURCES; do - test -f "$srcdir/$x" && x="$srcdir/$x" + test -f "$srcdir/$x" -a ! -f "$x" && x="$srcdir/$x" echo "$x" | grep -q '[^h]$' && continue; xx=`echo "$x" | sed 's@.*/@@'` tag=`echo "$xx" | tr 'a-z.-' 'A-Z_'` diff --git a/src/check-includes.sh b/src/check-includes.sh index 902f2357e..fd565da53 100755 --- a/src/check-includes.sh +++ b/src/check-includes.sh @@ -13,7 +13,7 @@ test "x$HBSOURCES" = x && HBSOURCES=`cd "$srcdir"; find . -maxdepth 1 -name 'hb- echo 'Checking that public header files #include "hb-common.h" or "hb.h" first (or none)' for x in $HBHEADERS; do - test -f "$srcdir/$x" && x="$srcdir/$x" + test -f "$srcdir/$x" -a ! -f "$x" && x="$srcdir/$x" grep '#.*\' "$x" /dev/null | head -n 1 done | grep -v '"hb-common[.]h"' | @@ -26,7 +26,7 @@ grep . >&2 && stat=1 echo 'Checking that source files #include "hb-*private.hh" first (or none)' for x in $HBSOURCES; do - test -f "$srcdir/$x" && x="$srcdir/$x" + test -f "$srcdir/$x" -a ! -f "$x" && x="$srcdir/$x" grep '#.*\' "$x" /dev/null | grep -v 'include _' | head -n 1 done | grep -v '"hb-.*private[.]hh"' | @@ -34,7 +34,7 @@ grep -v 'hb-private[.]hh:' | grep . >&2 && stat=1 -echo 'Checking that there is no #include ' +echo 'Checking that there is no #include ' for x in $HBHEADERS $HBSOURCES; do test -f "$srcdir/$x" && x="$srcdir/$x" grep '#.*\.*<.*hb' "$x" /dev/null >&2 && stat=1 diff --git a/src/check-libstdc++.sh b/src/check-libstdc++.sh index b541828bc..ce0bdab75 100755 --- a/src/check-libstdc++.sh +++ b/src/check-libstdc++.sh @@ -4,27 +4,37 @@ LC_ALL=C export LC_ALL test -z "$srcdir" && srcdir=. +test -z "$libs" && libs=.libs stat=0 if which ldd 2>/dev/null >/dev/null; then - : + LDD=ldd else - echo "check-libstdc++.sh: 'ldd' not found; skipping test" - exit 77 + # macOS specific tool + if which otool 2>/dev/null >/dev/null; then + LDD="otool -L" + else + echo "check-libstdc++.sh: 'ldd' not found; skipping test" + exit 77 + fi fi tested=false -for suffix in so dylib; do - so=.libs/libharfbuzz.$suffix - if ! test -f "$so"; then continue; fi +# harfbuzz-icu links to libstdc++ because icu does. +# harfbuzz-subset uses libstdc++. +for soname in harfbuzz harfbuzz-gobject; do + for suffix in so dylib; do + so=$libs/lib$soname.$suffix + if ! test -f "$so"; then continue; fi - echo "Checking that we are not linking to libstdc++ or libc++" - if ldd $so | grep 'libstdc[+][+]\|libc[+][+]'; then - echo "Ouch, linked to libstdc++ or libc++" - stat=1 - fi - tested=true + echo "Checking that we are not linking to libstdc++ or libc++ in $so" + if $LDD $so | grep 'libstdc[+][+]\|libc[+][+]'; then + echo "Ouch, linked to libstdc++ or libc++" + stat=1 + fi + tested=true + done done if ! $tested; then echo "check-libstdc++.sh: libharfbuzz shared library not found; skipping test" diff --git a/src/check-static-inits.sh b/src/check-static-inits.sh index 1446fa734..71551cbd4 100755 --- a/src/check-static-inits.sh +++ b/src/check-static-inits.sh @@ -4,6 +4,7 @@ LC_ALL=C export LC_ALL test -z "$srcdir" && srcdir=. +test -z "$libs" && libs=.libs stat=0 @@ -14,7 +15,7 @@ else exit 77 fi -OBJS=.libs/*.o +OBJS=$libs/*.o if test "x`echo $OBJS`" = "x$OBJS" 2>/dev/null >/dev/null; then echo "check-static-inits.sh: object files not found; skipping test" exit 77 diff --git a/src/check-symbols.sh b/src/check-symbols.sh index b2bf43fce..bfc93b349 100755 --- a/src/check-symbols.sh +++ b/src/check-symbols.sh @@ -4,8 +4,10 @@ LC_ALL=C export LC_ALL test -z "$srcdir" && srcdir=. +test -z "$libs" && libs=.libs stat=0 +IGNORED_SYMBOLS='_fini\|_init\|_fdata\|_ftext\|_fbss\|__bss_start\|__bss_start__\|__bss_end__\|_edata\|_end\|_bss_end__\|__end__\|__gcov_flush\|llvm_.*' if which nm 2>/dev/null >/dev/null; then : @@ -14,29 +16,46 @@ else exit 77 fi -echo "Checking that we are not exposing internal symbols" tested=false -for suffix in so dylib; do - so=.libs/libharfbuzz.$suffix - if ! test -f "$so"; then continue; fi - - EXPORTED_SYMBOLS="`nm "$so" | grep ' [BCDGINRSTVW] ' | grep -v ' _fini\>\| _init\>\| _fdata\>\| _ftext\>\| _fbss\>\| __bss_start\>\| __bss_start__\>\| __bss_end__\>\| _edata\>\| _end\>\| _bss_end__\>\| __end__\>\| __gcov_flush\>\| llvm_' | cut -d' ' -f3`" +for soname in harfbuzz harfbuzz-subset harfbuzz-icu harfbuzz-gobject; do + for suffix in so dylib; do + so=$libs/lib$soname.$suffix + if ! test -f "$so"; then continue; fi - prefix=`basename "$so" | sed 's/libharfbuzz/hb/; s/-/_/g; s/[.].*//'` + # On macOS, C symbols are prefixed with _ + symprefix= + if test $suffix = dylib; then symprefix=_; fi - # On mac, C symbols are prefixed with _ - if test $suffix = dylib; then prefix="_$prefix"; fi + EXPORTED_SYMBOLS="`nm "$so" | grep ' [BCDGINRSTVW] .' | grep -v " $symprefix\\($IGNORED_SYMBOLS\\>\\)" | cut -d' ' -f3 | c++filt`" - echo "Processing $so" - if echo "$EXPORTED_SYMBOLS" | grep -v "^${prefix}_"; then - echo "Ouch, internal symbols exposed" - stat=1 - fi + prefix=$symprefix`basename "$so" | sed 's/libharfbuzz/hb/; s/-/_/g; s/[.].*//'` - tested=true + echo + echo "Checking that $so does not expose internal symbols" + if echo "$EXPORTED_SYMBOLS" | grep -v "^${prefix}\(_\|$\)"; then + echo "Ouch, internal symbols exposed" + stat=1 + fi + + def=$soname.def + if ! test -f "$def"; then + echo "'$def' not found; skipping" + else + echo + echo "Checking that $so has the same symbol list as $def" + { + echo EXPORTS + echo "$EXPORTED_SYMBOLS" | sed -e "s/^${symprefix}hb/hb/g" + # cheat: copy the last line from the def file! + tail -n1 "$def" + } | c++filt | diff "$def" - >&2 || stat=1 + fi + + tested=true + done done if ! $tested; then - echo "check-symbols.sh: no shared library found; skipping test" + echo "check-symbols.sh: no shared libraries found; skipping test" exit 77 fi diff --git a/src/dev-run.sh b/src/dev-run.sh new file mode 100755 index 000000000..9c82db0b8 --- /dev/null +++ b/src/dev-run.sh @@ -0,0 +1,84 @@ +#!/bin/bash +# Suggested setup to use the script: +# (on the root of the project) +# $ NOCONFIGURE=1 ./autogen.sh --with-freetype --with-glib --with-gobject --with-cairo +# $ mkdir build && cd build && ../configure && make -j5 && cd .. +# $ src/dev-run.sh [FONT-FILE] [TEXT] +# +# Or, using cmake: +# $ cmake -DHB_CHECK=ON -Bbuild -H. -GNinja && ninja -Cbuild +# $ src/dev-run.sh [FONT-FILE] [TEXT] +# +# If you are using iTerm2, issue the script like this: +# $ src/dev-run.sh img [FONT-FILE] [TEXT] +# + +[ $# = 0 ] && echo Usage: "src/dev-run.sh [FONT-FILE] [TEXT]" && exit +command -v entr >/dev/null 2>&1 || { echo >&2 "This script needs `entr` be installed"; exit 1; } + + +GDB=gdb +# if gdb doesn't exist, hopefully lldb exist +command -v $GDB >/dev/null 2>&1 || export GDB="lldb" + + +[ $1 = "img" ] && img=1 && shift +# http://iterm2.com/documentation-images.html +osc="\033]" +if [[ $TERM == screen* ]]; then osc="\033Ptmux;\033\033]"; fi +st="\a" +if [[ $TERM == screen* ]]; then st="\a"; fi + + +tmp=$(mktemp) +[ -f 'build/build.ninja' ] && CMAKENINJA=TRUE +# or "fswatch -0 . -e build/ -e .git" +find src/ | entr printf '\0' | while read -d ""; do + clear + echo '====================================================' + if [[ $CMAKENINJA ]]; then + ninja -Cbuild hb-shape hb-view && { + build/hb-shape $@ + if [ $img ]; then + build/hb-view $@ -O png -o $tmp + printf "\n${osc}1337;File=;inline=1:`cat $tmp | base64`${st}\n" + else + build/hb-view $@ + fi + } + else + make -Cbuild/src -j5 -s lib && { + build/util/hb-shape $@ + if [ $img ]; then + build/util/hb-view $@ -O png -o $tmp + printf "\n${osc}1337;File=;inline=1:`cat $tmp | base64`${st}\n" + else + build/util/hb-view $@ + fi + } + fi +done + +read -n 1 -p "[C]heck, [D]ebug, [R]estart, [Q]uit? " answer +case "$answer" in +c|C ) + if [[ $CMAKENINJA ]]; then + CTEST_OUTPUT_ON_FAILURE=1 CTEST_PARALLEL_LEVEL=5 ninja -Cbuild test + else + make -Cbuild -j5 check && .ci/fail.sh + fi +;; +d|D ) + if [[ $CMAKENINJA ]]; then + echo "Not supported on cmake builds yet" + else + build/libtool --mode=execute $GDB -- build/util/hb-shape $@ + fi +;; +r|R ) + src/dev-run.sh $@ +;; +* ) + exit +;; +esac diff --git a/src/dump-indic-data.cc b/src/dump-indic-data.cc new file mode 100644 index 000000000..d57413884 --- /dev/null +++ b/src/dump-indic-data.cc @@ -0,0 +1,43 @@ +/* + * 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-shape-complex-indic-private.hh" + +int +main (void) +{ + for (hb_codepoint_t u = 0; u <= 0x10FFFF; u++) + { + hb_glyph_info_t info; + info.codepoint = u; + set_indic_properties (info); + if (info.indic_category() != INDIC_SYLLABIC_CATEGORY_OTHER || + info.indic_position() != INDIC_MATRA_CATEGORY_NOT_APPLICABLE) + printf("U+%04X %u %u\n", u, + info.indic_category(), + info.indic_position()); + } +} diff --git a/src/dump-khmer-data.cc b/src/dump-khmer-data.cc new file mode 100644 index 000000000..7dd09b2b5 --- /dev/null +++ b/src/dump-khmer-data.cc @@ -0,0 +1,43 @@ +/* + * 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-shape-complex-khmer-private.hh" + +int +main (void) +{ + for (hb_codepoint_t u = 0; u <= 0x10FFFF; u++) + { + hb_glyph_info_t info; + info.codepoint = u; + set_khmer_properties (info); + if (info.khmer_category() != INDIC_SYLLABIC_CATEGORY_OTHER || + info.khmer_position() != INDIC_MATRA_CATEGORY_NOT_APPLICABLE) + printf("U+%04X %u %u\n", u, + info.khmer_category(), + info.khmer_position()); + } +} diff --git a/src/dump-myanmar-data.cc b/src/dump-myanmar-data.cc new file mode 100644 index 000000000..2df9cd987 --- /dev/null +++ b/src/dump-myanmar-data.cc @@ -0,0 +1,43 @@ +/* + * 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-shape-complex-myanmar-private.hh" + +int +main (void) +{ + for (hb_codepoint_t u = 0; u <= 0x10FFFF; u++) + { + hb_glyph_info_t info; + info.codepoint = u; + set_myanmar_properties (info); + if (info.myanmar_category() != INDIC_SYLLABIC_CATEGORY_OTHER || + info.myanmar_position() != INDIC_MATRA_CATEGORY_NOT_APPLICABLE) + printf("U+%04X %u %u\n", u, + info.myanmar_category(), + info.myanmar_position()); + } +} diff --git a/src/dump-use-data.cc b/src/dump-use-data.cc new file mode 100644 index 000000000..0e64688f1 --- /dev/null +++ b/src/dump-use-data.cc @@ -0,0 +1,38 @@ +/* + * 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-shape-complex-use-private.hh" + +int +main (void) +{ + for (hb_codepoint_t u = 0; u <= 0x10FFFF; u++) + { + unsigned int category = hb_use_get_category (u); + if (category != USE_O) + printf("U+%04X %u\n", u, category); + } +} diff --git a/src/gen-arabic-table.py b/src/gen-arabic-table.py index 308435f99..59bd76012 100755 --- a/src/gen-arabic-table.py +++ b/src/gen-arabic-table.py @@ -134,7 +134,7 @@ def print_joining_table(f): 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 " 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:" diff --git a/src/gen-def.py b/src/gen-def.py new file mode 100755 index 000000000..1673537c9 --- /dev/null +++ b/src/gen-def.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python + +from __future__ import print_function + +import io, os, re, sys + +headers_content = [] +for h in os.environ["headers"].split (' '): + if h.endswith (".h"): + with io.open(h, encoding='utf8') as f: headers_content.append (f.read ()) + +result = """EXPORTS +%s +LIBRARY lib%s-0.dll""" % ( + "\n".join (sorted (re.findall (r"^hb_\w+(?= \()", "\n".join (headers_content), re.M))), + sys.argv[1].replace ('.def', '') +) + +with open (sys.argv[1], "w") as f: f.write (result) diff --git a/src/gen-indic-table.py b/src/gen-indic-table.py index 3016cd07b..735b9015e 100755 --- a/src/gen-indic-table.py +++ b/src/gen-indic-table.py @@ -229,13 +229,13 @@ print " {" pages = set([u>>page_bits for u in starts+ends+singles.keys()]) for p in sorted(pages): print " case 0x%0Xu:" % p - 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) 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:" diff --git a/src/gen-unicode-ranges.py b/src/gen-unicode-ranges.py new file mode 100644 index 000000000..3b59cd862 --- /dev/null +++ b/src/gen-unicode-ranges.py @@ -0,0 +1,52 @@ +# -*- coding: utf-8 -*- + +# Generates the code for a sorted unicode range array as used in hb-ot-os2-unicode-ranges.hh +# Input is a tab seperated list of unicode ranges from the otspec +# (https://docs.microsoft.com/en-us/typography/opentype/spec/os2#ulunicoderange1). + +import io +import re +import sys + +reload(sys) +sys.setdefaultencoding('utf-8') + +print (u"""static Range os2UnicodeRangesSorted[] = +{""") + +args = sys.argv[1:] +input_file = args[0] + +with io.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 Error("bad input :(.") + + name = fields[0] + ranges = re.split("-", fields[1]) + if len(ranges) != 2: + raise Error("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 (u"""};"""); diff --git a/src/gen-use-table.py b/src/gen-use-table.py index a922c92fa..06817255c 100755 --- a/src/gen-use-table.py +++ b/src/gen-use-table.py @@ -44,6 +44,7 @@ defaults = ('Other', 'Not_Applicable', 'Cn', 'No_Block') # TODO Characters that are not in Unicode Indic files, but used in USE data[0][0x034F] = defaults[0] data[0][0x2060] = defaults[0] +data[0][0x20F0] = defaults[0] for u in range (0xFE00, 0xFE0F + 1): data[0][u] = defaults[0] @@ -117,6 +118,7 @@ property_names = [ 'Top_And_Right', 'Top_And_Left', 'Top_And_Left_And_Right', + 'Bottom_And_Left', 'Bottom_And_Right', 'Top_And_Bottom_And_Right', 'Overstruck', @@ -153,7 +155,7 @@ def is_BASE(U, UISC, UGC): def is_BASE_IND(U, UISC, UGC): #SPEC-DRAFT return (UISC in [Consonant_Dead, Modifying_Letter] or UGC == Po) return (UISC in [Consonant_Dead, Modifying_Letter] or - (UGC == Po and not U in [0x104E, 0x2022]) or + (UGC == Po and not U in [0x104E, 0x2022, 0x11A3F, 0x11A45]) or False # SPEC-DRAFT-OUTDATED! U == 0x002D ) def is_BASE_NUM(U, UISC, UGC): @@ -177,6 +179,8 @@ def is_CONS_MOD(U, UISC, UGC): def is_CONS_SUB(U, UISC, UGC): #SPEC-DRAFT return UISC == Consonant_Subjoined return UISC == Consonant_Subjoined and UGC != Lo +def is_CONS_WITH_STACKER(U, UISC, UGC): + return UISC == Consonant_With_Stacker def is_HALANT(U, UISC, UGC): return UISC in [Virama, Invisible_Stacker] def is_HALANT_NUM(U, UISC, UGC): @@ -198,9 +202,7 @@ def is_OTHER(U, UISC, UGC): def is_Reserved(U, UISC, UGC): return UGC == 'Cn' def is_REPHA(U, UISC, UGC): - #return UISC == Consonant_Preceding_Repha - #SPEC-OUTDATED hack to categorize Consonant_With_Stacker and Consonant_Prefixed - return UISC in [Consonant_Preceding_Repha, Consonant_With_Stacker, Consonant_Prefixed] + return UISC in [Consonant_Preceding_Repha, Consonant_Prefixed] def is_SYM(U, UISC, UGC): if U == 0x25CC: return False #SPEC-DRAFT #SPEC-DRAFT return UGC in [So, Sc] or UISC == Symbol_Letter @@ -210,11 +212,13 @@ def is_SYM_MOD(U, UISC, UGC): def is_VARIATION_SELECTOR(U, UISC, UGC): return 0xFE00 <= U <= 0xFE0F def is_VOWEL(U, UISC, UGC): + # https://github.com/roozbehp/unicode-data/issues/6 return (UISC == Pure_Killer or - (UGC != Lo and UISC in [Vowel, Vowel_Dependent])) + (UGC != Lo and UISC in [Vowel, Vowel_Dependent] and U not in [0xAA29])) def is_VOWEL_MOD(U, UISC, UGC): + # https://github.com/roozbehp/unicode-data/issues/6 return (UISC in [Tone_Mark, Cantillation_Mark, Register_Shifter, Visarga] or - (UGC != Lo and UISC == Bindu)) + (UGC != Lo and (UISC == Bindu or U in [0xAA29]))) use_mapping = { 'B': is_BASE, @@ -227,6 +231,7 @@ use_mapping = { 'M': is_CONS_MED, 'CM': is_CONS_MOD, 'SUB': is_CONS_SUB, + 'CS': is_CONS_WITH_STACKER, 'H': is_HALANT, 'HN': is_HALANT_NUM, 'ZWNJ': is_ZWNJ, @@ -250,7 +255,7 @@ use_positions = { }, 'M': { 'Abv': [Top], - 'Blw': [Bottom], + 'Blw': [Bottom, Bottom_And_Left], 'Pst': [Right], 'Pre': [Left], }, @@ -292,12 +297,23 @@ def map_to_use(data): if U == 0x17DD: UISC = Vowel_Dependent if 0x1CE2 <= U <= 0x1CE8: UISC = Cantillation_Mark + # TODO: https://github.com/harfbuzz/harfbuzz/pull/627 + if 0x1BF2 <= U <= 0x1BF3: UISC = Nukta; UIPC = Bottom + # 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 - evals = [(k, v(U,UISC,UGC)) for k,v in items] - values = [k for k,v in evals if v] + # TODO: https://github.com/harfbuzz/harfbuzz/issues/525 + if U == 0x1A7F: UISC = Consonant_Final; UIPC = Bottom + + # TODO: https://github.com/harfbuzz/harfbuzz/pull/609 + if U == 0x20F0: UISC = Cantillation_Mark; UIPC = Top + + # TODO: https://github.com/harfbuzz/harfbuzz/pull/626 + if U == 0xA8B4: UISC = Consonant_Medial + + values = [k for k,v in items if v(U,UISC,UGC)] assert len(values) == 1, "%s %s %s %s" % (hex(U), UISC, UGC, values) USE = values[0] @@ -336,12 +352,6 @@ def map_to_use(data): defaults = ('O', 'No_Block') data = map_to_use(data) -# Remove the outliers -singles = {} -for u in [0x034F, 0x25CC, 0x1107F]: - singles[u] = data[u] - del data[u] - print "/* == Start of generated table == */" print "/*" print " * The following table is generated by running:" @@ -439,20 +449,17 @@ page_bits = 12 print "}; /* Table items: %d; occupancy: %d%% */" % (offset, occupancy) print print "USE_TABLE_ELEMENT_TYPE" -print "hb_use_get_categories (hb_codepoint_t u)" +print "hb_use_get_category (hb_codepoint_t u)" print "{" print " switch (u >> %d)" % page_bits print " {" -pages = set([u>>page_bits for u in starts+ends+singles.keys()]) +pages = set([u>>page_bits for u in starts+ends]) for p in sorted(pages): print " case 0x%0Xu:" % p for (start,end) in zip (starts, ends): if p not in [start>>page_bits, end>>page_bits]: continue offset = "use_offset_0x%04xu" % start - print " if (hb_in_range (u, 0x%04Xu, 0x%04Xu)) return use_table[u - 0x%04Xu + %s];" % (start, end-1, start, offset) - for u,d in singles.items (): - if p != u>>page_bits: continue - print " if (unlikely (u == 0x%04Xu)) return %s;" % (u, d[0]) + print " if (hb_in_range (u, 0x%04Xu, 0x%04Xu)) return use_table[u - 0x%04Xu + %s];" % (start, end-1, start, offset) print " break;" print "" print " default:" diff --git a/src/harfbuzz-config.cmake.in b/src/harfbuzz-config.cmake.in new file mode 100644 index 000000000..87b15721a --- /dev/null +++ b/src/harfbuzz-config.cmake.in @@ -0,0 +1,82 @@ +# 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) + get_filename_component(_harfbuzz_libdir_iter "${_harfbuzz_libdir_iter}" DIRECTORY) + 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/src/harfbuzz-subset.pc.in b/src/harfbuzz-subset.pc.in new file mode 100644 index 000000000..5da64b3f1 --- /dev/null +++ b/src/harfbuzz-subset.pc.in @@ -0,0 +1,12 @@ +prefix=%prefix% +exec_prefix=%exec_prefix% +libdir=%libdir% +includedir=%includedir% + +Name: harfbuzz +Description: HarfBuzz font subsetter +Version: %VERSION% + +Requires: harfbuzz +Libs: -L${libdir} -lharfbuzz-subset +Cflags: -I${includedir}/harfbuzz diff --git a/src/harfbuzz.pc.in b/src/harfbuzz.pc.in index b3e124aa8..661251c2d 100644 --- a/src/harfbuzz.pc.in +++ b/src/harfbuzz.pc.in @@ -8,6 +8,6 @@ Description: HarfBuzz text shaping library Version: %VERSION% Libs: -L${libdir} -lharfbuzz -Libs.private: %libs_private% +Libs.private: -lm %libs_private% Requires.private: %requires_private% Cflags: -I${includedir}/harfbuzz diff --git a/src/hb-aat-layout-ankr-table.hh b/src/hb-aat-layout-ankr-table.hh new file mode 100644 index 000000000..d0453bd88 --- /dev/null +++ b/src/hb-aat-layout-ankr-table.hh @@ -0,0 +1,80 @@ +/* + * 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-private.hh" + +#define HB_AAT_TAG_ankr HB_TAG('a','n','k','r') + + +namespace AAT { + + +/* + * ankr -- Anchor point + */ + +struct Anchor +{ + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + FWORD xCoordinate; + FWORD yCoordinate; + public: + DEFINE_SIZE_STATIC (4); +}; + +struct ankr +{ + static const hb_tag_t tableTag = HB_AAT_TAG_ankr; + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && version == 0 && + lookupTable.sanitize (c, this) && + anchors.sanitize (c, this)); + } + + protected: + HBUINT16 version; /* Version number (set to zero) */ + HBUINT16 flags; /* Flags (currently unused; set to zero) */ + LOffsetTo > lookupTable; /* Offset to the table's lookup table */ + LOffsetTo > + anchors; /* Offset to the glyph data table */ + + public: + DEFINE_SIZE_STATIC (12); +}; + +} /* namespace AAT */ + + +#endif /* HB_AAT_LAYOUT_ANKR_TABLE_HH */ diff --git a/src/hb-aat-layout-common-private.hh b/src/hb-aat-layout-common-private.hh new file mode 100644 index 000000000..7c0dfa8b0 --- /dev/null +++ b/src/hb-aat-layout-common-private.hh @@ -0,0 +1,728 @@ +/* + * 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_PRIVATE_HH +#define HB_AAT_LAYOUT_COMMON_PRIVATE_HH + +#include "hb-aat-layout-private.hh" + + +namespace AAT { + +using namespace OT; + + +/* + * Binary Searching Tables + */ + +struct BinSearchHeader +{ + + inline 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 BinSearchArrayOf +{ + inline const Type& operator [] (unsigned int i) const + { + if (unlikely (i >= header.nUnits)) return Null(Type); + return StructAtOffset (bytes, i * header.unitSize); + } + inline Type& operator [] (unsigned int i) + { + return StructAtOffset (bytes, i * header.unitSize); + } + inline unsigned int get_size (void) const + { return header.static_size + header.nUnits * header.unitSize; } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (unlikely (!sanitize_shallow (c))) return_trace (false); + + /* Note: for structs that do not reference other structs, + * we do not need to call their sanitize() as we already did + * a bound check on the aggregate array size. We just include + * a small unreachable expression to make sure the structs + * pointed to do have a simple sanitize(), ie. they do not + * reference other structs via offsets. + */ + (void) (false && StructAtOffset (bytes, 0).sanitize (c)); + + return_trace (true); + } + inline bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + if (unlikely (!sanitize_shallow (c))) return_trace (false); + unsigned int count = header.nUnits; + for (unsigned int i = 0; i < count; i++) + if (unlikely (!(*this)[i].sanitize (c, base))) + return_trace (false); + return_trace (true); + } + + template + inline const Type *bsearch (const T &key) const + { + unsigned int size = header.unitSize; + int min = 0, max = (int) header.nUnits - 1; + while (min <= max) + { + int mid = (min + max) / 2; + const Type *p = (const Type *) (((const char *) bytes) + (mid * size)); + int c = p->cmp (key); + if (c < 0) + max = mid - 1; + else if (c > 0) + min = mid + 1; + else + return p; + } + return nullptr; + } + + private: + inline bool sanitize_shallow (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (header.sanitize (c) && + Type::static_size >= header.unitSize && + c->check_array (bytes, header.unitSize, header.nUnits)); + } + + protected: + BinSearchHeader header; + HBUINT8 bytes[VAR]; + public: + DEFINE_SIZE_ARRAY (10, bytes); +}; + + +/* TODO Move this to hb-open-type-private.hh and use it in ArrayOf, HeadlessArrayOf, + * and other places around the code base?? */ +template +struct UnsizedArrayOf +{ + inline const Type& operator [] (unsigned int i) const { return arrayZ[i]; } + inline Type& operator [] (unsigned int i) { return arrayZ[i]; } + + inline bool sanitize (hb_sanitize_context_t *c, unsigned int count) const + { + TRACE_SANITIZE (this); + if (unlikely (!sanitize_shallow (c, count))) return_trace (false); + + /* Note: for structs that do not reference other structs, + * we do not need to call their sanitize() as we already did + * a bound check on the aggregate array size. We just include + * a small unreachable expression to make sure the structs + * pointed to do have a simple sanitize(), ie. they do not + * reference other structs via offsets. + */ + (void) (false && arrayZ[0].sanitize (c)); + + return_trace (true); + } + inline bool sanitize (hb_sanitize_context_t *c, unsigned int count, const void *base) const + { + TRACE_SANITIZE (this); + if (unlikely (!sanitize_shallow (c, count))) return_trace (false); + for (unsigned int i = 0; i < count; i++) + if (unlikely (!arrayZ[i].sanitize (c, base))) + return_trace (false); + return_trace (true); + } + template + inline bool sanitize (hb_sanitize_context_t *c, unsigned int count, const void *base, T user_data) const + { + TRACE_SANITIZE (this); + if (unlikely (!sanitize_shallow (c, count))) return_trace (false); + for (unsigned int i = 0; i < count; i++) + if (unlikely (!arrayZ[i].sanitize (c, base, user_data))) + return_trace (false); + return_trace (true); + } + + private: + inline bool sanitize_shallow (hb_sanitize_context_t *c, unsigned int count) const + { + TRACE_SANITIZE (this); + return_trace (c->check_array (arrayZ, arrayZ[0].static_size, count)); + } + + public: + Type arrayZ[VAR]; + public: + DEFINE_SIZE_ARRAY (0, arrayZ); +}; + +/* Unsized array of offset's */ +template +struct UnsizedOffsetArrayOf : UnsizedArrayOf > {}; + +/* Unsized array of offsets relative to the beginning of the array itself. */ +template +struct UnsizedOffsetListOf : UnsizedOffsetArrayOf +{ + inline const Type& operator [] (unsigned int i) const + { + return this+this->arrayZ[i]; + } + + inline bool sanitize (hb_sanitize_context_t *c, unsigned int count) const + { + TRACE_SANITIZE (this); + return_trace ((UnsizedOffsetArrayOf::sanitize (c, count, this))); + } + template + inline bool sanitize (hb_sanitize_context_t *c, unsigned int count, T user_data) const + { + TRACE_SANITIZE (this); + return_trace ((UnsizedOffsetArrayOf::sanitize (c, count, this, user_data))); + } +}; + + +/* + * Lookup Table + */ + +template struct Lookup; + +template +struct LookupFormat0 +{ + friend struct Lookup; + + private: + inline 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]; + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (arrayZ.sanitize (c, c->num_glyphs)); + } + + protected: + HBUINT16 format; /* Format identifier--format = 0 */ + UnsizedArrayOf + arrayZ; /* Array of lookup values, indexed by glyph index. */ + public: + DEFINE_SIZE_ARRAY (2, arrayZ); +}; + + +template +struct LookupSegmentSingle +{ + inline int cmp (hb_codepoint_t g) const { + return g < first ? -1 : g <= last ? 0 : +1 ; + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && value.sanitize (c)); + } + + GlyphID last; /* Last GlyphID in this segment */ + GlyphID 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: + inline const T* get_value (hb_codepoint_t glyph_id) const + { + const LookupSegmentSingle *v = segments.bsearch (glyph_id); + return v ? &v->value : nullptr; + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (segments.sanitize (c)); + } + + protected: + HBUINT16 format; /* Format identifier--format = 2 */ + BinSearchArrayOf > + 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 +{ + inline 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; + } + + inline int cmp (hb_codepoint_t g) const { + return g < first ? -1 : g <= last ? 0 : +1 ; + } + + inline 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)); + } + + GlyphID last; /* Last GlyphID in this segment */ + GlyphID first; /* First GlyphID in this segment */ + OffsetTo > + 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: + inline 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; + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (segments.sanitize (c, this)); + } + + protected: + HBUINT16 format; /* Format identifier--format = 2 */ + BinSearchArrayOf > + 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 +{ + inline int cmp (hb_codepoint_t g) const { return glyph.cmp (g); } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && value.sanitize (c)); + } + + GlyphID glyph; /* Last GlyphID */ + T value; /* The lookup value (only one) */ + public: + DEFINE_SIZE_STATIC (4 + T::static_size); +}; + +template +struct LookupFormat6 +{ + friend struct Lookup; + + private: + inline const T* get_value (hb_codepoint_t glyph_id) const + { + const LookupSingle *v = entries.bsearch (glyph_id); + return v ? &v->value : nullptr; + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (entries.sanitize (c)); + } + + protected: + HBUINT16 format; /* Format identifier--format = 6 */ + BinSearchArrayOf > + entries; /* The actual entries, sorted by glyph index. */ + public: + DEFINE_SIZE_ARRAY (8, entries); +}; + +template +struct LookupFormat8 +{ + friend struct Lookup; + + private: + inline const T* get_value (hb_codepoint_t glyph_id) const + { + return firstGlyph <= glyph_id && glyph_id - firstGlyph < glyphCount ? &valueArrayZ[glyph_id - firstGlyph] : nullptr; + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && valueArrayZ.sanitize (c, glyphCount)); + } + + protected: + HBUINT16 format; /* Format identifier--format = 6 */ + GlyphID 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 Lookup +{ + inline 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; + } + } + + inline 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)); + default:return_trace (true); + } + } + + protected: + union { + HBUINT16 format; /* Format identifier */ + LookupFormat0 format0; + LookupFormat2 format2; + LookupFormat4 format4; + LookupFormat6 format6; + LookupFormat8 format8; + } u; + public: + DEFINE_SIZE_UNION (2, format); +}; + + +/* + * Extended State Table + */ + +template +struct Entry +{ + inline 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. */ + 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 +{ + inline bool sanitize (hb_sanitize_context_t *c, unsigned int count) 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 +{ + inline unsigned int get_class (hb_codepoint_t glyph_id, unsigned int num_glyphs) const + { + const HBUINT16 *v = (this+classTable).get_value (glyph_id, num_glyphs); + return v ? *v : 1; + } + + inline const Entry *get_entries () const + { + return (this+entryTable).arrayZ; + } + + inline const Entry *get_entryZ (unsigned int state, unsigned int klass) const + { + if (unlikely (klass >= nClasses)) return nullptr; + + const HBUINT16 *states = (this+stateArrayTable).arrayZ; + const Entry *entries = (this+entryTable).arrayZ; + + unsigned int entry = states[state * nClasses + klass]; + + return &entries[entry]; + } + + inline bool sanitize (hb_sanitize_context_t *c, + unsigned int *num_entries_out = nullptr) const + { + TRACE_SANITIZE (this); + if (unlikely (!(c->check_struct (this) && + classTable.sanitize (c, this)))) return_trace (false); + + const HBUINT16 *states = (this+stateArrayTable).arrayZ; + const Entry *entries = (this+entryTable).arrayZ; + + unsigned int num_states = 1; + unsigned int num_entries = 0; + + unsigned int state = 0; + unsigned int entry = 0; + while (state < num_states) + { + if (unlikely (!c->check_array (states, + states[0].static_size * nClasses, + num_states))) + return_trace (false); + { /* Sweep new states. */ + const HBUINT16 *stop = &states[num_states * nClasses]; + for (const HBUINT16 *p = &states[state * nClasses]; p < stop; p++) + num_entries = MAX (num_entries, *p + 1); + state = num_states; + } + + if (unlikely (!c->check_array (entries, + entries[0].static_size, + num_entries))) + return_trace (false); + { /* Sweep new entries. */ + const Entry *stop = &entries[num_entries]; + for (const Entry *p = &entries[entry]; p < stop; p++) + num_states = MAX (num_states, p->newState + 1); + entry = num_entries; + } + } + + if (num_entries_out) + *num_entries_out = num_entries; + + return_trace (true); + } + + protected: + HBUINT32 nClasses; /* Number of classes, which is the number of indices + * in a single line in the state array. */ + OffsetTo, HBUINT32> + classTable; /* Offset to the class table. */ + OffsetTo, HBUINT32> + stateArrayTable;/* Offset to the state array. */ + OffsetTo >, HBUINT32> + entryTable; /* Offset to the entry array. */ + + public: + DEFINE_SIZE_STATIC (16); +}; + +template +struct StateTableDriver +{ + inline StateTableDriver (const StateTable &machine_, + hb_buffer_t *buffer_, + hb_face_t *face_) : + machine (machine_), + buffer (buffer_), + num_glyphs (face_->get_num_glyphs ()) {} + + template + inline void drive (context_t *c) + { + hb_glyph_info_t *info = buffer->info; + + if (!c->in_place) + buffer->clear_output (); + + unsigned int state = 0; + bool last_was_dont_advance = false; + for (buffer->idx = 0;;) + { + unsigned int klass = buffer->idx < buffer->len ? + machine.get_class (info[buffer->idx].codepoint, num_glyphs) : + 0 /* End of text */; + const Entry *entry = machine.get_entryZ (state, klass); + if (unlikely (!entry)) + break; + + /* Unsafe-to-break before this if not in state 0, as things might + * go differently if we start from state 0 here. */ + if (state && buffer->idx) + { + /* If there's no action and we're just epsilon-transitioning to state 0, + * safe to break. */ + if (c->is_actionable (this, entry) || + !(entry->newState == 0 && entry->flags == context_t::DontAdvance)) + buffer->unsafe_to_break (buffer->idx - 1, buffer->idx + 1); + } + + /* Unsafe-to-break if end-of-text would kick in here. */ + if (buffer->idx + 2 <= buffer->len) + { + const Entry *end_entry = machine.get_entryZ (state, 0); + if (c->is_actionable (this, end_entry)) + buffer->unsafe_to_break (buffer->idx, buffer->idx + 2); + } + + if (unlikely (!c->transition (this, entry))) + break; + + last_was_dont_advance = (entry->flags & context_t::DontAdvance) && buffer->max_ops-- > 0; + + state = entry->newState; + + if (buffer->idx == buffer->len) + break; + + if (!last_was_dont_advance) + buffer->next_glyph (); + } + + if (!c->in_place) + { + for (; buffer->idx < buffer->len;) + buffer->next_glyph (); + buffer->swap_buffers (); + } + } + + public: + const StateTable &machine; + hb_buffer_t *buffer; + unsigned int num_glyphs; +}; + + + +struct hb_aat_apply_context_t : + hb_dispatch_context_t +{ + inline const char *get_name (void) { return "APPLY"; } + template + inline return_t dispatch (const T &obj) { return obj.apply (this); } + static return_t default_return_value (void) { return false; } + bool stop_sublookup_iteration (return_t r) const { return r; } + + hb_font_t *font; + hb_face_t *face; + hb_buffer_t *buffer; + hb_sanitize_context_t sanitizer; + + /* Unused. For debug tracing only. */ + unsigned int lookup_index; + unsigned int debug_depth; + + inline hb_aat_apply_context_t (hb_font_t *font_, + hb_buffer_t *buffer_, + hb_blob_t *table) : + font (font_), face (font->face), buffer (buffer_), + sanitizer (), lookup_index (0), debug_depth (0) + { + sanitizer.init (table); + sanitizer.num_glyphs = face->get_num_glyphs (); + sanitizer.start_processing (); + } + + inline void set_lookup_index (unsigned int i) { lookup_index = i; } + + inline ~hb_aat_apply_context_t (void) + { + sanitizer.end_processing (); + } +}; + + +} /* namespace AAT */ + + +#endif /* HB_AAT_LAYOUT_COMMON_PRIVATE_HH */ diff --git a/src/hb-aat-layout-kerx-table.hh b/src/hb-aat-layout-kerx-table.hh new file mode 100644 index 000000000..505e5084b --- /dev/null +++ b/src/hb-aat-layout-kerx-table.hh @@ -0,0 +1,384 @@ +/* + * Copyright © 2018 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_AAT_LAYOUT_KERX_TABLE_HH +#define HB_AAT_LAYOUT_KERX_TABLE_HH + +#include "hb-aat-layout-common-private.hh" + +namespace AAT { + + +/* + * kerx -- Kerning + */ + +#define HB_AAT_TAG_kerx HB_TAG('k','e','r','x') + +struct hb_glyph_pair_t +{ + hb_codepoint_t left; + hb_codepoint_t right; +}; + +struct KerxPair +{ + inline int get_kerning (void) const + { return value; } + + inline int cmp (const hb_glyph_pair_t &o) const + { + int ret = left.cmp (o.left); + if (ret) return ret; + return right.cmp (o.right); + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + protected: + GlyphID left; + GlyphID right; + FWORD value; + HBUINT16 pad; + public: + DEFINE_SIZE_STATIC (8); +}; + +struct KerxSubTableFormat0 +{ + inline int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const + { + //hb_glyph_pair_t pair = {left, right}; + //int i = pairs.bsearch (pair); + //if (i == -1) + return 0; + //return pairs[i].get_kerning (); + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (pairs.sanitize (c)); + } + + protected: + BinSearchArrayOf pairs; /* Array of kerning pairs. */ + //FIXME: BinSearchArrayOf and its BinSearchHeader should be + //modified in a way to accept uint32s + public: + //DEFINE_SIZE_ARRAY (16, pairs); +}; + +struct KerxAction +{ + HBUINT16 index; +}; + +struct KerxSubTableFormat1 +{ + inline bool sanitize (hb_sanitize_context_t *c) const + { + //TRACE_SANITIZE (this); + //return_trace (stateHeader.sanitize (c)); + return false; + } + + protected: + StateTable stateHeader; + OffsetTo, HBUINT32> valueTable; + + public: + //DEFINE_SIZE_MIN (4); +}; + +//FIXME: Maybe this can be replaced with Lookup? +struct KerxClassTable +{ + inline unsigned int get_class (hb_codepoint_t g) const { return classes[g - firstGlyph]; } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (firstGlyph.sanitize (c) && classes.sanitize (c)); + } + + protected: + HBUINT16 firstGlyph; /* First glyph in class range. */ + ArrayOf classes; /* Glyph classes. */ + public: + DEFINE_SIZE_ARRAY (4, classes); +}; + +struct KerxSubTableFormat2 +{ + inline int get_kerning (hb_codepoint_t left, hb_codepoint_t right, const char *end) const + { + unsigned int l = (this+leftClassTable).get_class (left); + unsigned int r = (this+leftClassTable).get_class (left); + unsigned int offset = l * rowWidth + r * sizeof (FWORD); + const FWORD *arr = &(this+array); + if (unlikely ((const void *) arr < (const void *) this || (const void *) arr >= (const void *) end)) + return 0; + const FWORD *v = &StructAtOffset (arr, offset); + if (unlikely ((const void *) v < (const void *) arr || (const void *) (v + 1) > (const void *) end)) + return 0; + return *v; + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (rowWidth.sanitize (c) && + leftClassTable.sanitize (c, this) && + rightClassTable.sanitize (c, this) && + array.sanitize (c, this)); + } + + protected: + HBUINT32 rowWidth; /* The width, in bytes, of a row in the table. */ + LOffsetTo + leftClassTable; /* Offset from beginning of this subtable to + * left-hand class table. */ + LOffsetTo + rightClassTable;/* Offset from beginning of this subtable to + * right-hand class table. */ + LOffsetTo + array; /* Offset from beginning of this subtable to + * the start of the kerning array. */ + public: + DEFINE_SIZE_MIN (16); +}; + +struct KerxSubTableFormat4 +{ + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (rowWidth.sanitize (c) && + leftClassTable.sanitize (c, this) && + rightClassTable.sanitize (c, this) && + array.sanitize (c, this)); + } + + protected: + HBUINT32 rowWidth; /* The width, in bytes, of a row in the table. */ + LOffsetTo + leftClassTable; /* Offset from beginning of this subtable to + * left-hand class table. */ + LOffsetTo + rightClassTable;/* Offset from beginning of this subtable to + * right-hand class table. */ + LOffsetTo + array; /* Offset from beginning of this subtable to + * the start of the kerning array. */ + public: + DEFINE_SIZE_MIN (16); +}; + +struct KerxSubTableFormat6 +{ + inline bool sanitize (hb_sanitize_context_t *c) const + { + //TRACE_SANITIZE (this); + //return_trace ; + return false; + } + + protected: + HBUINT32 flags; + HBUINT16 rowCount; + HBUINT16 columnCount; + HBUINT32 rowIndexTableOffset; + HBUINT32 columnIndexTableOffset; + HBUINT32 kerningArrayOffset; + HBUINT32 kerningVectorOffset; + + public: + DEFINE_SIZE_MIN (24); +}; + +struct KerxSubTable +{ + inline int get_kerning (hb_codepoint_t left, hb_codepoint_t right, const char *end, unsigned int format) const + { + switch (format) { + case 0: return u.format0.get_kerning (left, right); + case 2: return u.format2.get_kerning (left, right, end); + default:return 0; + } + } + + inline bool sanitize (hb_sanitize_context_t *c, unsigned int format) const + { + TRACE_SANITIZE (this); + switch (format) { + case 0: return_trace (u.format0.sanitize (c)); + case 2: return_trace (u.format2.sanitize (c)); + default:return_trace (true); + } + } + + protected: + union { + KerxSubTableFormat0 format0; + KerxSubTableFormat2 format2; + KerxSubTableFormat4 format4; + KerxSubTableFormat6 format6; + } u; + public: + DEFINE_SIZE_MIN (0); +}; + + + +struct kerx +{ + static const hb_tag_t tableTag = HB_AAT_TAG_kerx; + + inline bool apply (hb_aat_apply_context_t *c, const AAT::ankr *ankr) const + { + TRACE_APPLY (this); + /* TODO */ + return_trace (false); + } + + struct SubTableWrapper + { + enum coverage_flags_t { + COVERAGE_VERTICAL_FLAG = 0x8000u, + COVERAGE_CROSSSTREAM_FLAG = 0x4000u, + COVERAGE_VARIATION_FLAG = 0x2000u, + + COVERAGE_OVERRIDE_FLAG = 0x0000u, /* Not supported. */ + + COVERAGE_CHECK_FLAGS = 0x0700u, //FIXME: Where these two come from? + COVERAGE_CHECK_HORIZONTAL = 0x0100u + }; + + protected: + HBUINT32 length; /* Length of the subtable (including this header). */ + HBUINT16 coverage; /* Coverage bits. */ + HBUINT16 format; /* Subtable format. */ + HBUINT32 tupleIndex; /* The tuple index (used for variations fonts). + * This value specifies which tuple this subtable covers. */ + KerxSubTable subtable; /* Subtable data. */ + public: + inline bool is_horizontal (void) const + { return (coverage & COVERAGE_CHECK_FLAGS) == COVERAGE_CHECK_HORIZONTAL; } + + inline bool is_override (void) const + { return bool (coverage & COVERAGE_OVERRIDE_FLAG); } + + inline int get_kerning (hb_codepoint_t left, hb_codepoint_t right, const char *end) const + { return subtable.get_kerning (left, right, end, format); } + + inline int get_h_kerning (hb_codepoint_t left, hb_codepoint_t right, const char *end) const + { return is_horizontal () ? get_kerning (left, right, end) : 0; } + + inline unsigned int get_size (void) const { return length; } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + length >= min_size && + c->check_array (this, 1, length) && + subtable.sanitize (c, format)); + } + DEFINE_SIZE_MIN (12); + }; + + inline int get_h_kerning (hb_codepoint_t left, hb_codepoint_t right, unsigned int table_length) const + { + int v = 0; + const SubTableWrapper *st = (SubTableWrapper *) data; + unsigned int count = nTables; + for (unsigned int i = 0; i < count; i++) + { + if (st->is_override ()) + v = 0; + v += st->get_h_kerning (left, right, table_length + (const char *) this); + st = (SubTableWrapper *) st; + } + return v; + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + const SubTableWrapper *st = (SubTableWrapper *) data; + unsigned int count = nTables; + for (unsigned int i = 0; i < count; i++) + { + if (unlikely (!st->sanitize (c))) + return_trace (false); + st = (SubTableWrapper *) st; + } + + return_trace (true); + } + + struct accelerator_t + { + inline void init (hb_face_t *face) + { + blob = Sanitizer().sanitize (face->reference_table (HB_AAT_TAG_kerx)); + table = Sanitizer::lock_instance (blob); + table_length = hb_blob_get_length (blob); + } + inline void fini (void) + { + hb_blob_destroy (blob); + } + + inline int get_h_kerning (hb_codepoint_t left, hb_codepoint_t right) const + { return table->get_h_kerning (left, right, table_length); } + + private: + hb_blob_t *blob; + const kerx *table; + unsigned int table_length; + }; + + protected: + HBUINT16 version; + HBUINT16 padding; + HBUINT32 nTables; /* Number of subtables in the kerning table. */ + HBUINT8 data[VAR]; + //ArrayOf subtableGlyphCoverageArray; + public: + DEFINE_SIZE_ARRAY (8, data); +}; + +} /* namespace AAT */ + + +#endif /* HB_AAT_LAYOUT_KERX_TABLE_HH */ diff --git a/src/hb-aat-layout-morx-table.hh b/src/hb-aat-layout-morx-table.hh new file mode 100644 index 000000000..4cc282421 --- /dev/null +++ b/src/hb-aat-layout-morx-table.hh @@ -0,0 +1,728 @@ +/* + * 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-private.hh" +#include "hb-aat-layout-common-private.hh" + +#define HB_AAT_TAG_MORX HB_TAG('m','o','r','x') + + +namespace AAT { + +using namespace OT; + + +struct RearrangementSubtable +{ + typedef void EntryData; + + struct driver_context_t + { + static const 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. */ + }; + + inline driver_context_t (const RearrangementSubtable *table) : + ret (false), + start (0), end (0) {} + + inline bool is_actionable (StateTableDriver *driver, + const Entry *entry) + { + return (entry->flags & Verb) && start < end; + } + inline bool 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 = 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 = MIN (2, m >> 4); + unsigned int r = MIN (2, m & 0x0F); + bool reverse_l = 3 == (m >> 4); + bool reverse_r = 3 == (m & 0x0F); + + if (end - start >= l + r) + { + buffer->merge_clusters (start, 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]; + } + } + } + + return true; + } + + public: + bool ret; + private: + unsigned int start; + unsigned int end; + }; + + inline 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); + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (machine.sanitize (c)); + } + + protected: + StateTable machine; + public: + DEFINE_SIZE_STATIC (16); +}; + +struct ContextualSubtable +{ + 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 const 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. */ + }; + + inline driver_context_t (const ContextualSubtable *table) : + ret (false), + mark_set (false), + mark (0), + subs (table+table->substitutionTables) {} + + inline 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; + } + inline bool 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 true; + + if (entry->data.markIndex != 0xFFFF) + { + const Lookup &lookup = subs[entry->data.markIndex]; + hb_glyph_info_t *info = buffer->info; + const GlyphID *replacement = lookup.get_value (info[mark].codepoint, driver->num_glyphs); + if (replacement) + { + buffer->unsafe_to_break (mark, MIN (buffer->idx + 1, buffer->len)); + info[mark].codepoint = *replacement; + ret = true; + } + } + if (entry->data.currentIndex != 0xFFFF) + { + unsigned int idx = MIN (buffer->idx, buffer->len - 1); + const Lookup &lookup = subs[entry->data.currentIndex]; + hb_glyph_info_t *info = buffer->info; + const GlyphID *replacement = lookup.get_value (info[idx].codepoint, driver->num_glyphs); + if (replacement) + { + info[idx].codepoint = *replacement; + ret = true; + } + } + + if (entry->flags & SetMark) + { + mark_set = true; + mark = buffer->idx; + } + + return true; + } + + public: + bool ret; + private: + bool mark_set; + unsigned int mark; + const UnsizedOffsetListOf, HBUINT32> &subs; + }; + + inline 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); + } + + inline 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); + + 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 = MAX (num_lookups, 1 + data.markIndex); + if (data.currentIndex != 0xFFFF) + num_lookups = MAX (num_lookups, 1 + data.currentIndex); + } + + return_trace (substitutionTables.sanitize (c, this, num_lookups)); + } + + protected: + StateTable machine; + OffsetTo, HBUINT32>, HBUINT32> + substitutionTables; + public: + DEFINE_SIZE_STATIC (20); +}; + +struct LigatureSubtable +{ + struct EntryData + { + HBUINT16 ligActionIndex; /* Index to the first ligActionTable entry + * for processing this group, if indicated + * by the flags. */ + public: + DEFINE_SIZE_STATIC (2); + }; + + struct driver_context_t + { + static const bool in_place = false; + 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. */ + }; + 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. */ + }; + + inline driver_context_t (const LigatureSubtable *table, + hb_aat_apply_context_t *c_) : + ret (false), + c (c_), + ligAction (table+table->ligAction), + component (table+table->component), + ligature (table+table->ligature), + match_length (0) {} + + inline bool is_actionable (StateTableDriver *driver, + const Entry *entry) + { + return !!(entry->flags & PerformAction); + } + inline bool transition (StateTableDriver *driver, + const Entry *entry) + { + hb_buffer_t *buffer = driver->buffer; + unsigned int flags = entry->flags; + + if (flags & SetComponent) + { + if (unlikely (match_length >= ARRAY_LENGTH (match_positions))) + return false; + + /* Never mark same index twice, in case DontAdvance was used... */ + if (match_length && match_positions[match_length - 1] == buffer->out_len) + match_length--; + + match_positions[match_length++] = buffer->out_len; + } + + if (flags & PerformAction) + { + unsigned int end = buffer->out_len; + unsigned int action_idx = entry->data.ligActionIndex; + unsigned int action; + unsigned int ligature_idx = 0; + do + { + if (unlikely (!match_length)) + return false; + + buffer->move_to (match_positions[--match_length]); + + const HBUINT32 &actionData = ligAction[action_idx]; + if (unlikely (!actionData.sanitize (&c->sanitizer))) return false; + action = actionData; + + uint32_t uoffset = action & LigActionOffset; + if (uoffset & 0x20000000) + uoffset += 0xC0000000; + int32_t offset = (int32_t) uoffset; + unsigned int component_idx = buffer->cur().codepoint + offset; + + const HBUINT16 &componentData = component[component_idx]; + if (unlikely (!componentData.sanitize (&c->sanitizer))) return false; + ligature_idx += componentData; + + if (action & (LigActionStore | LigActionLast)) + { + const GlyphID &ligatureData = ligature[ligature_idx]; + if (unlikely (!ligatureData.sanitize (&c->sanitizer))) return false; + hb_codepoint_t lig = ligatureData; + + match_positions[match_length++] = buffer->out_len; + buffer->replace_glyph (lig); + + //ligature_idx = 0; // XXX Yes or no? + } + else + { + buffer->skip_glyph (); + end--; + } + /* TODO merge_clusters / unsafe_to_break */ + + action_idx++; + } + while (!(action & LigActionLast)); + buffer->move_to (end); + } + + return true; + } + + public: + bool ret; + private: + hb_aat_apply_context_t *c; + const UnsizedArrayOf &ligAction; + const UnsizedArrayOf &component; + const UnsizedArrayOf &ligature; + unsigned int match_length; + unsigned int match_positions[HB_MAX_CONTEXT_LENGTH]; + }; + + inline 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); + } + + inline 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; + OffsetTo, HBUINT32> + ligAction; /* Offset to the ligature action table. */ + OffsetTo, HBUINT32> + component; /* Offset to the component table. */ + OffsetTo, HBUINT32> + ligature; /* Offset to the actual ligature lists. */ + public: + DEFINE_SIZE_STATIC (28); +}; + +struct NoncontextualSubtable +{ + inline bool apply (hb_aat_apply_context_t *c) const + { + TRACE_APPLY (this); + + 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 GlyphID *replacement = substitute.get_value (info[i].codepoint, num_glyphs); + if (replacement) + { + info[i].codepoint = *replacement; + ret = true; + } + } + + return_trace (ret); + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (substitute.sanitize (c)); + } + + protected: + Lookup substitute; + public: + DEFINE_SIZE_MIN (2); +}; + +struct InsertionSubtable +{ + inline bool apply (hb_aat_apply_context_t *c) const + { + TRACE_APPLY (this); + /* TODO */ + return_trace (false); + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + /* TODO */ + return_trace (true); + } +}; + + +struct Feature +{ + inline 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); +}; + + +struct ChainSubtable +{ + friend struct Chain; + + inline unsigned int get_size (void) const { return length; } + inline unsigned int get_type (void) const { return coverage & 0xFF; } + + enum Type { + Rearrangement = 0, + Contextual = 1, + Ligature = 2, + Noncontextual = 4, + Insertion = 5 + }; + + inline void apply (hb_aat_apply_context_t *c) const + { + dispatch (c); + } + + template + inline typename context_t::return_t dispatch (context_t *c) const + { + unsigned int subtable_type = get_type (); + TRACE_DISPATCH (this, subtable_type); + switch (subtable_type) { + case Rearrangement: return_trace (c->dispatch (u.rearrangement)); + case Contextual: return_trace (c->dispatch (u.contextual)); + case Ligature: return_trace (c->dispatch (u.ligature)); + case Noncontextual: return_trace (c->dispatch (u.noncontextual)); + case Insertion: return_trace (c->dispatch (u.insertion)); + default: return_trace (c->default_return_value ()); + } + } + + inline 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); + + return_trace (dispatch (c)); + } + + protected: + HBUINT32 length; /* Total subtable length, including this header. */ + HBUINT32 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 (12); +}; + +struct Chain +{ + inline void apply (hb_aat_apply_context_t *c) const + { + const ChainSubtable *subtable = &StructAtOffset (featureZ, featureZ[0].static_size * featureCount); + unsigned int count = subtableCount; + for (unsigned int i = 0; i < count; i++) + { + if (!c->buffer->message (c->font, "start chain subtable %d", c->lookup_index)) + { + c->set_lookup_index (c->lookup_index + 1); + continue; + } + + subtable->apply (c); + subtable = &StructAfter (*subtable); + + (void) c->buffer->message (c->font, "end chain subtable %d", c->lookup_index); + + c->set_lookup_index (c->lookup_index + 1); + } + } + + inline unsigned int get_size (void) const { return length; } + + inline bool sanitize (hb_sanitize_context_t *c, unsigned int major) const + { + TRACE_SANITIZE (this); + if (!length.sanitize (c) || + length < min_size || + !c->check_range (this, length)) + return_trace (false); + + if (!c->check_array (featureZ, featureZ[0].static_size, featureCount)) + return_trace (false); + + const ChainSubtable *subtable = &StructAtOffset (featureZ, featureZ[0].static_size * 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. */ + HBUINT32 featureCount; /* Number of feature subtable entries. */ + HBUINT32 subtableCount; /* The number of subtables in the chain. */ + + Feature featureZ[VAR]; /* Features. */ + ChainSubtable subtableX[VAR]; /* Subtables. */ + // subtableGlyphCoverageArray if major == 3 + + public: + DEFINE_SIZE_MIN (16); +}; + + +/* + * The 'mort'/'morx' Tables + */ + +struct morx +{ + static const hb_tag_t tableTag = HB_AAT_TAG_MORX; + + inline void apply (hb_aat_apply_context_t *c) const + { + c->set_lookup_index (0); + const Chain *chain = chains; + unsigned int count = chainCount; + for (unsigned int i = 0; i < count; i++) + { + chain->apply (c); + chain = &StructAfter (*chain); + } + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (!version.sanitize (c) || + (version.major >> (sizeof (HBUINT32) == 4 ? 1 : 0)) != 1 || + !chainCount.sanitize (c)) + return_trace (false); + + const Chain *chain = chains; + unsigned int count = chainCount; + for (unsigned int i = 0; i < count; i++) + { + if (!chain->sanitize (c, version.major)) + return_trace (false); + chain = &StructAfter (*chain); + } + + return_trace (true); + } + + protected: + FixedVersion<>version; /* Version number of the glyph metamorphosis table. + * 1 for mort, 2 or 3 for morx. */ + HBUINT32 chainCount; /* Number of metamorphosis chains contained in this + * table. */ + Chain chains[VAR]; /* Chains. */ + + public: + DEFINE_SIZE_MIN (8); +}; + +} /* namespace AAT */ + + +#endif /* HB_AAT_LAYOUT_MORX_TABLE_HH */ diff --git a/src/hb-aat-layout-private.hh b/src/hb-aat-layout-private.hh new file mode 100644 index 000000000..ce75c8e71 --- /dev/null +++ b/src/hb-aat-layout-private.hh @@ -0,0 +1,43 @@ +/* + * 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_PRIVATE_HH +#define HB_AAT_LAYOUT_PRIVATE_HH + +#include "hb-private.hh" + +#include "hb-font-private.hh" +#include "hb-buffer-private.hh" +#include "hb-open-type-private.hh" + + +HB_INTERNAL void +hb_aat_layout_substitute (hb_font_t *font, hb_buffer_t *buffer); + +HB_INTERNAL void +hb_aat_layout_position (hb_font_t *font, hb_buffer_t *buffer); + +#endif /* HB_AAT_LAYOUT_PRIVATE_HH */ diff --git a/src/hb-aat-layout-trak-table.hh b/src/hb-aat-layout-trak-table.hh new file mode 100644 index 000000000..5767b116c --- /dev/null +++ b/src/hb-aat-layout-trak-table.hh @@ -0,0 +1,201 @@ +/* + * 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-private.hh" +#include "hb-open-type-private.hh" + +#define HB_AAT_TAG_trak HB_TAG('t','r','a','k') + + +namespace AAT { + + +struct TrackTableEntry +{ + inline bool sanitize (hb_sanitize_context_t *c, const void *base, unsigned int size) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && (values.sanitize (c, base, size))); + } + + inline float get_track_value () const + { + return track.to_float (); + } + + inline int get_value (const void *base, unsigned int index) const + { + return (base+values)[index]; + } + + protected: + Fixed track; /* Track value for this record. */ + HBUINT16 trackNameID; /* The 'name' table index for this track */ + OffsetTo > + values; /* Offset from start of tracking table to + * per-size tracking values for this track. */ + + public: + DEFINE_SIZE_STATIC (8); +}; + +struct TrackData +{ + inline bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + sizeTable.sanitize (c, base, nSizes) && + trackTable.sanitize (c, nTracks, base, nSizes)); + } + + inline float get_tracking (const void *base, float ptem) const + { + /* CoreText points are CSS pixels (96 per inch), + * NOT typographic points (72 per inch). + * + * https://developer.apple.com/library/content/documentation/GraphicsAnimation/Conceptual/HighResolutionOSX/Explained/Explained.html + */ + float csspx = ptem * 96.f / 72.f; + Fixed fixed_size; + fixed_size.set_float (csspx); + + /* XXX Clean this up. Make it work with nSizes==1 and 0. */ + + unsigned int sizes = nSizes; + + const TrackTableEntry *trackTableEntry = nullptr; + for (unsigned int i = 0; i < sizes; ++i) + // For now we only seek for track entries with zero tracking value + if (trackTable[i].get_track_value () == 0.) + trackTableEntry = &trackTable[0]; + + // We couldn't match any, exit + if (!trackTableEntry) return 0.; + + /* TODO bfind() */ + unsigned int size_index; + UnsizedArrayOf size_table = base+sizeTable; + for (size_index = 0; size_index < sizes; ++size_index) + if (size_table[size_index] >= fixed_size) + break; + + // TODO(ebraminio): We don't attempt to extrapolate to larger or + // smaller values for now but we should do, per spec + if (size_index == sizes) + return trackTableEntry->get_value (base, sizes - 1); + if (size_index == 0 || size_table[size_index] == fixed_size) + return trackTableEntry->get_value (base, size_index); + + float s0 = size_table[size_index - 1].to_float (); + float s1 = size_table[size_index].to_float (); + float t = (csspx - s0) / (s1 - s0); + return t * trackTableEntry->get_value (base, size_index) + + (1.0 - t) * trackTableEntry->get_value (base, size_index - 1); + } + + protected: + HBUINT16 nTracks; /* Number of separate tracks included in this table. */ + HBUINT16 nSizes; /* Number of point sizes included in this table. */ + LOffsetTo > /* Offset to array[nSizes] of size values. */ + sizeTable; + UnsizedArrayOf + trackTable; /* Array[nTracks] of TrackTableEntry records. */ + + public: + DEFINE_SIZE_ARRAY (8, trackTable); +}; + +struct trak +{ + static const hb_tag_t tableTag = HB_AAT_TAG_trak; + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + + return_trace (c->check_struct (this) && + horizData.sanitize (c, this, this) && + vertData.sanitize (c, this, this)); + } + + inline bool apply (hb_aat_apply_context_t *c) const + { + TRACE_APPLY (this); + + const float ptem = c->font->ptem; + if (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; + float tracking = trackData.get_tracking (this, ptem); + hb_position_t advance_to_add = c->font->em_scalef_x (tracking / 2); + foreach_grapheme (buffer, start, end) + { + /* TODO This is wrong. */ + buffer->pos[start].x_advance += advance_to_add; + buffer->pos[end].x_advance += advance_to_add; + } + } + else + { + const TrackData &trackData = this+vertData; + float tracking = trackData.get_tracking (this, ptem); + hb_position_t advance_to_add = c->font->em_scalef_y (tracking / 2); + foreach_grapheme (buffer, start, end) + { + /* TODO This is wrong. */ + buffer->pos[start].y_advance += advance_to_add; + buffer->pos[end].y_advance += advance_to_add; + } + } + + return_trace (true); + } + + protected: + FixedVersion<> version; /* Version of the tracking table--currently + * 0x00010000u for version 1.0. */ + HBUINT16 format; /* Format of the tracking table */ + OffsetTo horizData; /* TrackData for horizontal text */ + OffsetTo vertData; /* TrackData for vertical text */ + HBUINT16 reserved; /* Reserved. Set to 0. */ + + public: + DEFINE_SIZE_MIN (12); +}; + +} /* namespace AAT */ + + +#endif /* HB_AAT_LAYOUT_TRAK_TABLE_HH */ diff --git a/src/hb-aat-layout.cc b/src/hb-aat-layout.cc new file mode 100644 index 000000000..45268e3e7 --- /dev/null +++ b/src/hb-aat-layout.cc @@ -0,0 +1,143 @@ +/* + * 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-open-type-private.hh" + +#include "hb-ot-layout-private.hh" +#include "hb-ot-layout-gsubgpos-private.hh" + +#include "hb-aat-layout-private.hh" +#include "hb-aat-layout-ankr-table.hh" +#include "hb-aat-layout-kerx-table.hh" +#include "hb-aat-layout-morx-table.hh" +#include "hb-aat-layout-trak-table.hh" + +/* + * morx/kerx/trak + */ + +static inline const AAT::ankr& +_get_ankr (hb_face_t *face, hb_blob_t **blob = nullptr) +{ + if (unlikely (!hb_ot_shaper_face_data_ensure (face))) + { + if (blob) + *blob = hb_blob_get_empty (); + return OT::Null(AAT::ankr); + } + hb_ot_layout_t * layout = hb_ot_layout_from_face (face); + const AAT::ankr& ankr = *(layout->ankr.get ()); + if (blob) + *blob = layout->ankr.blob; + return ankr; +} + +static inline const AAT::kerx& +_get_kerx (hb_face_t *face, hb_blob_t **blob = nullptr) +{ + if (unlikely (!hb_ot_shaper_face_data_ensure (face))) + { + if (blob) + *blob = hb_blob_get_empty (); + return OT::Null(AAT::kerx); + } + hb_ot_layout_t * layout = hb_ot_layout_from_face (face); + /* XXX this doesn't call set_num_glyphs on sanitizer. */ + const AAT::kerx& kerx = *(layout->kerx.get ()); + if (blob) + *blob = layout->kerx.blob; + return kerx; +} + +static inline const AAT::morx& +_get_morx (hb_face_t *face, hb_blob_t **blob = nullptr) +{ + if (unlikely (!hb_ot_shaper_face_data_ensure (face))) + { + if (blob) + *blob = hb_blob_get_empty (); + return OT::Null(AAT::morx); + } + hb_ot_layout_t * layout = hb_ot_layout_from_face (face); + /* XXX this doesn't call set_num_glyphs on sanitizer. */ + const AAT::morx& morx = *(layout->morx.get ()); + if (blob) + *blob = layout->morx.blob; + return morx; +} + +static inline const AAT::trak& +_get_trak (hb_face_t *face, hb_blob_t **blob = nullptr) +{ + if (unlikely (!hb_ot_shaper_face_data_ensure (face))) + { + if (blob) + *blob = hb_blob_get_empty (); + return OT::Null(AAT::trak); + } + hb_ot_layout_t * layout = hb_ot_layout_from_face (face); + const AAT::trak& trak = *(layout->trak.get ()); + if (blob) + *blob = layout->trak.blob; + return trak; +} + +// static inline void +// _hb_aat_layout_create (hb_face_t *face) +// { +// OT::Sanitizer sanitizer; +// sanitizer.set_num_glyphs (face->get_num_glyphs ()); +// hb_blob_t *morx_blob = sanitizer.sanitize (face->reference_table (HB_AAT_TAG_MORX)); +// OT::Sanitizer::lock_instance (morx_blob); + +// if (0) +// { +// OT::Sanitizer >::lock_instance (morx_blob)->get_value (1, face->get_num_glyphs ()); +// } +// } + +void +hb_aat_layout_substitute (hb_font_t *font, hb_buffer_t *buffer) +{ + hb_blob_t *blob; + const AAT::morx& morx = _get_morx (font->face, &blob); + + AAT::hb_aat_apply_context_t c (font, buffer, blob); + morx.apply (&c); +} + +void +hb_aat_layout_position (hb_font_t *font, hb_buffer_t *buffer) +{ + hb_blob_t *blob; + const AAT::ankr& ankr = _get_ankr (font->face, &blob); + const AAT::kerx& kerx = _get_kerx (font->face, &blob); + const AAT::trak& trak = _get_trak (font->face, &blob); + + AAT::hb_aat_apply_context_t c (font, buffer, blob); + kerx.apply (&c, &ankr); + trak.apply (&c); +} diff --git a/src/hb-atomic-private.hh b/src/hb-atomic-private.hh index 100ba539e..a7e9b11af 100644 --- a/src/hb-atomic-private.hh +++ b/src/hb-atomic-private.hh @@ -70,32 +70,6 @@ typedef LONG hb_atomic_int_impl_t; #define hb_atomic_ptr_impl_cmpexch(P,O,N) (InterlockedCompareExchangePointer ((void **) (P), (void *) (N), (void *) (O)) == (void *) (O)) -#elif !defined(HB_NO_MT) && defined(__APPLE__) - -#include -#ifdef __MAC_OS_X_MIN_REQUIRED -#include -#elif defined(__IPHONE_OS_MIN_REQUIRED) -#include -#endif - - -typedef int32_t hb_atomic_int_impl_t; -#define HB_ATOMIC_INT_IMPL_INIT(V) (V) -#define hb_atomic_int_impl_add(AI, V) (OSAtomicAdd32Barrier ((V), &(AI)) - (V)) - -#define hb_atomic_ptr_impl_get(P) (OSMemoryBarrier (), (void *) *(P)) -#if (MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_4 || __IPHONE_VERSION_MIN_REQUIRED >= 20100) -#define hb_atomic_ptr_impl_cmpexch(P,O,N) OSAtomicCompareAndSwapPtrBarrier ((void *) (O), (void *) (N), (void **) (P)) -#else -#if __ppc64__ || __x86_64__ || __aarch64__ -#define hb_atomic_ptr_impl_cmpexch(P,O,N) OSAtomicCompareAndSwap64Barrier ((int64_t) (O), (int64_t) (N), (int64_t*) (P)) -#else -#define hb_atomic_ptr_impl_cmpexch(P,O,N) OSAtomicCompareAndSwap32Barrier ((int32_t) (O), (int32_t) (N), (int32_t*) (P)) -#endif -#endif - - #elif !defined(HB_NO_MT) && defined(HAVE_INTEL_ATOMIC_PRIMITIVES) typedef int hb_atomic_int_impl_t; @@ -119,18 +93,44 @@ typedef unsigned int hb_atomic_int_impl_t; #define hb_atomic_ptr_impl_cmpexch(P,O,N) ( ({__machine_rw_barrier ();}), atomic_cas_ptr ((void **) (P), (void *) (O), (void *) (N)) == (void *) (O) ? true : false) +#elif !defined(HB_NO_MT) && defined(__APPLE__) + +#include +#ifdef __MAC_OS_X_MIN_REQUIRED +#include +#elif defined(__IPHONE_OS_MIN_REQUIRED) +#include +#endif + + +typedef int32_t hb_atomic_int_impl_t; +#define HB_ATOMIC_INT_IMPL_INIT(V) (V) +#define hb_atomic_int_impl_add(AI, V) (OSAtomicAdd32Barrier ((V), &(AI)) - (V)) + +#define hb_atomic_ptr_impl_get(P) (OSMemoryBarrier (), (void *) *(P)) +#if (MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_4 || __IPHONE_VERSION_MIN_REQUIRED >= 20100) +#define hb_atomic_ptr_impl_cmpexch(P,O,N) OSAtomicCompareAndSwapPtrBarrier ((void *) (O), (void *) (N), (void **) (P)) +#else +#if __ppc64__ || __x86_64__ || __aarch64__ +#define hb_atomic_ptr_impl_cmpexch(P,O,N) OSAtomicCompareAndSwap64Barrier ((int64_t) (void *) (O), (int64_t) (void *) (N), (int64_t*) (P)) +#else +#define hb_atomic_ptr_impl_cmpexch(P,O,N) OSAtomicCompareAndSwap32Barrier ((int32_t) (void *) (O), (int32_t) (void *) (N), (int32_t*) (P)) +#endif +#endif + + #elif !defined(HB_NO_MT) && defined(_AIX) && defined(__IBMCPP__) #include -static inline int hb_fetch_and_add(volatile int* AI, unsigned int V) { +static inline int _hb_fetch_and_add(volatile int* AI, unsigned int V) { __lwsync(); int result = __fetch_and_add(AI, V); __isync(); return result; } -static inline int hb_compare_and_swaplp(volatile long* P, long O, long N) { +static inline int _hb_compare_and_swaplp(volatile long* P, long O, long N) { __sync(); int result = __compare_and_swaplp (P, &O, N); __sync(); @@ -139,10 +139,10 @@ static inline int hb_compare_and_swaplp(volatile long* P, long O, long N) { typedef int hb_atomic_int_impl_t; #define HB_ATOMIC_INT_IMPL_INIT(V) (V) -#define hb_atomic_int_impl_add(AI, V) hb_fetch_and_add (&(AI), (V)) +#define hb_atomic_int_impl_add(AI, V) _hb_fetch_and_add (&(AI), (V)) #define hb_atomic_ptr_impl_get(P) (__sync(), (void *) *(P)) -#define hb_atomic_ptr_impl_cmpexch(P,O,N) hb_compare_and_swaplp ((long*)(P), (long)(O), (long)(N)) +#define hb_atomic_ptr_impl_cmpexch(P,O,N) _hb_compare_and_swaplp ((long*)(P), (long)(O), (long)(N)) #elif !defined(HB_NO_MT) diff --git a/src/hb-blob.cc b/src/hb-blob.cc index fb48f03ca..b5291f650 100644 --- a/src/hb-blob.cc +++ b/src/hb-blob.cc @@ -26,10 +26,11 @@ /* http://www.oracle.com/technetwork/articles/servers-storage-dev/standardheaderfiles-453865.html */ #ifndef _POSIX_C_SOURCE -#define _POSIX_C_SOURCE 199309L +#define _POSIX_C_SOURCE 200809L #endif #include "hb-private.hh" +#include "hb-debug.hh" #include "hb-object-private.hh" @@ -44,12 +45,6 @@ #include - -#ifndef HB_DEBUG_BLOB -#define HB_DEBUG_BLOB (HB_DEBUG+0) -#endif - - struct hb_blob_t { hb_object_header_t header; ASSERT_POD (); @@ -72,8 +67,8 @@ _hb_blob_destroy_user_data (hb_blob_t *blob) { if (blob->destroy) { blob->destroy (blob->user_data); - blob->user_data = NULL; - blob->destroy = NULL; + blob->user_data = nullptr; + blob->destroy = nullptr; } } @@ -128,6 +123,12 @@ hb_blob_create (const char *data, return blob; } +static void +_hb_blob_destroy (void *data) +{ + hb_blob_destroy ((hb_blob_t *) data); +} + /** * hb_blob_create_sub_blob: * @parent: Parent blob. @@ -164,7 +165,32 @@ hb_blob_create_sub_blob (hb_blob_t *parent, MIN (length, parent->length - offset), HB_MEMORY_MODE_READONLY, hb_blob_reference (parent), - (hb_destroy_func_t) hb_blob_destroy); + _hb_blob_destroy); + + return blob; +} + +/** + * hb_blob_copy_writable_or_fail: + * @blob: A blob. + * + * Makes a writable copy of @blob. + * + * Return value: 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; } @@ -188,12 +214,12 @@ hb_blob_get_empty (void) true, /* immutable */ - NULL, /* data */ + nullptr, /* data */ 0, /* length */ HB_MEMORY_MODE_READONLY, /* mode */ - NULL, /* user_data */ - NULL /* destroy */ + nullptr, /* user_data */ + nullptr /* destroy */ }; return const_cast (&_hb_blob_nil); @@ -221,7 +247,7 @@ hb_blob_reference (hb_blob_t *blob) * hb_blob_destroy: (skip) * @blob: a blob. * - * Descreases the reference count on @blob, and if it reaches zero, destroys + * 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. * @@ -373,7 +399,7 @@ hb_blob_get_data_writable (hb_blob_t *blob, unsigned int *length) if (length) *length = 0; - return NULL; + return nullptr; } if (length) diff --git a/src/hb-blob.h b/src/hb-blob.h index ef3fc98c0..fd561f738 100644 --- a/src/hb-blob.h +++ b/src/hb-blob.h @@ -44,7 +44,7 @@ HB_BEGIN_DECLS * any such possibility, MODE_DUPLICATE should be used * such that HarfBuzz makes a copy immediately, * - * - Use MODE_READONLY otherse, unless you really really + * - Use MODE_READONLY otherwise, unless you really really * really know what you are doing, * * - MODE_WRITABLE is appropriate if you really made a @@ -82,6 +82,9 @@ 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); diff --git a/src/hb-buffer-deserialize-json.hh b/src/hb-buffer-deserialize-json.hh new file mode 100644 index 000000000..be374c77a --- /dev/null +++ b/src/hb-buffer-deserialize-json.hh @@ -0,0 +1,643 @@ + +#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-private.hh" + + +#line 36 "hb-buffer-deserialize-json.hh" +static const unsigned char _deserialize_json_trans_keys[] = { + 0u, 0u, 9u, 123u, 9u, 34u, 97u, 103u, 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, 34u, 34u, 9u, 58u, 9u, 57u, + 65u, 122u, 34u, 122u, 9u, 125u, 9u, 125u, 9u, 93u, 9u, 123u, 0u, 0u, 0 +}; + +static const char _deserialize_json_key_spans[] = { + 0, 115, 26, 7, 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, 50, 49, + 58, 89, 117, 117, 85, 115, 0 +}; + +static const short _deserialize_json_index_offsets[] = { + 0, 0, 116, 143, 151, 154, 156, 207, + 257, 268, 386, 504, 622, 624, 675, 725, + 736, 854, 972, 974, 976, 1027, 1077, 1195, + 1313, 1316, 1318, 1369, 1419, 1430, 1548, 1666, + 1668, 1719, 1769, 1780, 1898, 2016, 2018, 2069, + 2119, 2178, 2268, 2386, 2504, 2590, 2706 +}; + +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, 1, 8, 1, + 9, 10, 1, 11, 1, 11, 11, 11, + 11, 11, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 11, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 12, 1, + 12, 12, 12, 12, 12, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 12, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 13, 1, 1, 14, + 15, 15, 15, 15, 15, 15, 15, 15, + 15, 1, 16, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 1, 18, 18, 18, + 18, 18, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 18, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 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, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 20, 1, 21, 21, 21, 21, 21, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 21, 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, 22, + 1, 18, 18, 18, 18, 18, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 18, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 19, 1, 1, 1, + 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 20, 1, 23, + 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, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 24, 1, 24, 24, 24, 24, + 24, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 24, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 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, 30, 30, 30, 30, 30, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 30, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 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, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 32, 1, 30, + 30, 30, 30, 30, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 30, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 31, 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, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 32, 1, 33, 1, 34, + 1, 34, 34, 34, 34, 34, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 34, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 35, 1, 35, 35, 35, 35, + 35, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 35, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 36, 37, 37, 37, 37, + 37, 37, 37, 37, 37, 1, 38, 38, + 38, 38, 38, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 38, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 39, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 40, 1, 38, 38, 38, 38, + 38, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 38, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 39, + 1, 1, 1, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 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, + 40, 1, 42, 43, 1, 44, 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, + 45, 1, 45, 45, 45, 45, 45, 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, 46, 1, + 1, 47, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 1, 49, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 1, 51, + 51, 51, 51, 51, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 51, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 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, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 53, 1, 51, 51, 51, + 51, 51, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 51, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 52, 1, 1, 1, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 53, 1, 54, 1, 54, 54, 54, + 54, 54, 1, 1, 1, 1, 1, 1, + 1, 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, 55, 1, + 55, 55, 55, 55, 55, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 55, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 56, 1, 1, 57, + 58, 58, 58, 58, 58, 58, 58, 58, + 58, 1, 59, 60, 60, 60, 60, 60, + 60, 60, 60, 60, 1, 61, 61, 61, + 61, 61, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 61, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 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, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 63, 1, 61, 61, 61, 61, 61, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 61, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 62, 1, + 1, 1, 60, 60, 60, 60, 60, 60, + 60, 60, 60, 60, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 63, + 1, 64, 1, 64, 64, 64, 64, 64, + 1, 1, 1, 1, 1, 1, 1, 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, 65, 1, 65, 65, + 65, 65, 65, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 65, 1, 66, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 67, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 1, + 69, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 1, 1, 1, 1, 1, 1, + 69, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 1, 70, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 71, 71, + 1, 71, 71, 71, 71, 71, 71, 71, + 71, 71, 71, 1, 1, 1, 1, 1, + 1, 1, 71, 71, 71, 71, 71, 71, + 71, 71, 71, 71, 71, 71, 71, 71, + 71, 71, 71, 71, 71, 71, 71, 71, + 71, 71, 71, 71, 1, 1, 1, 1, + 71, 1, 71, 71, 71, 71, 71, 71, + 71, 71, 71, 71, 71, 71, 71, 71, + 71, 71, 71, 71, 71, 71, 71, 71, + 71, 71, 71, 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, + 73, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 74, 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, 73, 1, + 1, 1, 75, 75, 75, 75, 75, 75, + 75, 75, 75, 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, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 74, + 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, 1, 1, 1, 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, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 78, 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, 5, 12, 6, 7, 8, 9, 11, + 9, 11, 10, 2, 44, 10, 44, 13, + 14, 15, 16, 17, 16, 17, 10, 2, + 44, 19, 20, 21, 22, 23, 10, 2, + 44, 23, 25, 31, 26, 27, 28, 29, + 30, 29, 30, 10, 2, 44, 32, 33, + 34, 35, 36, 35, 36, 10, 2, 44, + 38, 39, 40, 42, 43, 41, 10, 41, + 10, 2, 44, 43, 44, 45, 46 +}; + +static const char _deserialize_json_trans_actions[] = { + 0, 0, 1, 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, 2, 14, 0, + 15, 15, 16, 0, 0, 0, 0 +}; + +static const int deserialize_json_start = 1; +static const int deserialize_json_first_final = 44; +static const int deserialize_json_error = 0; + +static const int deserialize_json_en_main = 1; + + +#line 97 "hb-buffer-deserialize-json.rl" + + +static hb_bool_t +_hb_buffer_deserialize_glyphs_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 466 "hb-buffer-deserialize-json.hh" + { + cs = deserialize_json_start; + } + +#line 471 "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 (buffer->in_error) + 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 14: +#line 55 "hb-buffer-deserialize-json.rl" + { + if (!hb_font_glyph_from_string (font, + tok, p - tok, + &info.codepoint)) + return false; +} + break; + case 15: +#line 62 "hb-buffer-deserialize-json.rl" + { if (!parse_uint (tok, p, &info.codepoint)) return false; } + break; + case 8: +#line 63 "hb-buffer-deserialize-json.rl" + { if (!parse_uint (tok, p, &info.cluster )) return false; } + break; + case 10: +#line 64 "hb-buffer-deserialize-json.rl" + { if (!parse_int (tok, p, &pos.x_offset )) return false; } + break; + case 12: +#line 65 "hb-buffer-deserialize-json.rl" + { if (!parse_int (tok, p, &pos.y_offset )) return false; } + break; + case 3: +#line 66 "hb-buffer-deserialize-json.rl" + { if (!parse_int (tok, p, &pos.x_advance)) return false; } + break; + case 6: +#line 67 "hb-buffer-deserialize-json.rl" + { if (!parse_int (tok, p, &pos.y_advance)) return false; } + break; + case 16: +#line 62 "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 (buffer->in_error) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 9: +#line 63 "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 (buffer->in_error) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 11: +#line 64 "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 (buffer->in_error) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 13: +#line 65 "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 (buffer->in_error) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 4: +#line 66 "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 (buffer->in_error) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 7: +#line 67 "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 (buffer->in_error) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; +#line 624 "hb-buffer-deserialize-json.hh" + } + +_again: + if ( cs == 0 ) + goto _out; + if ( ++p != pe ) + goto _resume; + _test_eof: {} + _out: {} + } + +#line 125 "hb-buffer-deserialize-json.rl" + + + *end_ptr = p; + + return p == pe && *(p-1) != ']'; +} + +#endif /* HB_BUFFER_DESERIALIZE_JSON_HH */ diff --git a/src/hb-buffer-deserialize-json.rl b/src/hb-buffer-deserialize-json.rl index 91b350f5a..0f7d48ee2 100644 --- a/src/hb-buffer-deserialize-json.rl +++ b/src/hb-buffer-deserialize-json.rl @@ -106,7 +106,7 @@ _hb_buffer_deserialize_glyphs_json (hb_buffer_t *buffer, const char *p = buf, *pe = buf + buf_len; /* Ensure we have positions. */ - (void) hb_buffer_get_glyph_positions (buffer, NULL); + (void) hb_buffer_get_glyph_positions (buffer, nullptr); while (p < pe && ISSPACE (*p)) p++; @@ -115,7 +115,7 @@ _hb_buffer_deserialize_glyphs_json (hb_buffer_t *buffer, *end_ptr = ++p; } - const char *tok = NULL; + const char *tok = nullptr; int cs; hb_glyph_info_t info = {0}; hb_glyph_position_t pos = {0}; diff --git a/src/hb-buffer-deserialize-text.hh b/src/hb-buffer-deserialize-text.hh new file mode 100644 index 000000000..a6ab0bbde --- /dev/null +++ b/src/hb-buffer-deserialize-text.hh @@ -0,0 +1,571 @@ + +#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-private.hh" + + +#line 36 "hb-buffer-deserialize-text.hh" +static const unsigned char _deserialize_text_trans_keys[] = { + 0u, 0u, 9u, 122u, 45u, 57u, 48u, 57u, 45u, 57u, 48u, 57u, 48u, 57u, 45u, 57u, + 48u, 57u, 44u, 44u, 45u, 57u, 48u, 57u, 44u, 57u, 9u, 124u, 9u, 124u, 0u, 0u, + 9u, 122u, 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, 114, 13, 10, 13, 10, 10, 13, + 10, 1, 13, 10, 14, 116, 116, 0, + 114, 116, 116, 116, 116, 116, 116, 116, + 116, 116, 116 +}; + +static const short _deserialize_text_index_offsets[] = { + 0, 0, 115, 129, 140, 154, 165, 176, + 190, 201, 203, 217, 228, 243, 360, 477, + 478, 593, 710, 827, 944, 1061, 1178, 1295, + 1412, 1529, 1646 +}; + +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, + 2, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 1, 1, 1, 1, 1, 1, + 1, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 1, 1, 1, 1, 1, + 1, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 1, 5, 1, 1, 6, + 7, 7, 7, 7, 7, 7, 7, 7, + 7, 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, 1, 1, 18, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 1, 20, + 21, 21, 21, 21, 21, 21, 21, 21, + 21, 1, 22, 1, 23, 1, 1, 24, + 25, 25, 25, 25, 25, 25, 25, 25, + 25, 1, 26, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 1, 22, 1, 1, + 1, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 1, 28, 28, 28, 28, + 28, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 28, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 29, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 30, 1, 1, 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, + 32, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 33, + 1, 34, 34, 34, 34, 34, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 34, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 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, 36, 1, 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, 2, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 1, 1, 1, 1, 1, 1, 1, 4, + 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, + 4, 1, 1, 1, 1, 1, 1, 4, + 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, + 4, 1, 28, 28, 28, 28, 28, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 28, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 29, 1, 1, 1, + 1, 37, 37, 37, 37, 37, 37, 37, + 37, 37, 37, 1, 1, 1, 30, 1, + 1, 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, 32, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 33, 1, 38, + 38, 38, 38, 38, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 38, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 39, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 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, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 41, 1, 42, 42, 42, 42, + 42, 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, + 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, 44, + 1, 42, 42, 42, 42, 42, 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, + 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, 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, 44, 1, 38, 38, + 38, 38, 38, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 38, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 39, 1, 1, 1, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 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, 1, 1, + 1, 1, 40, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 41, 1, 45, 45, 45, 45, 45, + 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, 46, 1, 1, + 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, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 48, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 49, 1, + 50, 50, 50, 50, 50, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 50, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 51, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 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, 53, 1, 50, 50, 50, + 50, 50, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 50, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 51, + 1, 1, 1, 1, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 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, + 53, 1, 45, 45, 45, 45, 45, 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, 46, 1, 1, 1, + 1, 54, 54, 54, 54, 54, 54, 54, + 54, 54, 54, 1, 1, 1, 1, 1, + 1, 47, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 48, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 49, 1, 28, + 28, 28, 28, 28, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 28, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 29, 1, 55, 55, 1, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, + 1, 1, 1, 30, 1, 1, 31, 55, + 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, + 55, 1, 1, 32, 1, 55, 1, 55, + 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, + 55, 1, 33, 1, 0 +}; + +static const char _deserialize_text_trans_targs[] = { + 1, 0, 13, 17, 26, 3, 18, 21, + 18, 21, 5, 19, 20, 19, 20, 22, + 25, 8, 9, 12, 9, 12, 10, 11, + 23, 24, 23, 24, 14, 2, 6, 7, + 15, 16, 14, 15, 16, 17, 14, 4, + 15, 16, 14, 15, 16, 14, 2, 7, + 15, 16, 14, 2, 15, 16, 25, 26 +}; + +static const char _deserialize_text_trans_actions[] = { + 0, 0, 1, 1, 1, 2, 2, 2, + 0, 0, 2, 2, 2, 0, 0, 2, + 2, 2, 2, 2, 0, 0, 3, 2, + 2, 2, 0, 0, 4, 5, 5, 5, + 4, 4, 0, 0, 0, 0, 6, 7, + 6, 6, 8, 8, 8, 9, 10, 10, + 9, 9, 11, 12, 11, 11, 0, 0 +}; + +static const char _deserialize_text_eof_actions[] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 4, 0, 0, + 0, 4, 6, 8, 8, 6, 9, 11, + 11, 9, 4 +}; + +static const int deserialize_text_start = 1; +static const int deserialize_text_first_final = 13; +static const int deserialize_text_error = 0; + +static const int deserialize_text_en_main = 1; + + +#line 91 "hb-buffer-deserialize-text.rl" + + +static hb_bool_t +_hb_buffer_deserialize_glyphs_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++; + if (p < pe && *p == (buffer->len ? '|' : '[')) + { + *end_ptr = ++p; + } + + const char *eof = pe, *tok = nullptr; + int cs; + hb_glyph_info_t info = {0}; + hb_glyph_position_t pos = {0}; + +#line 343 "hb-buffer-deserialize-text.hh" + { + cs = deserialize_text_start; + } + +#line 348 "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 2: +#line 51 "hb-buffer-deserialize-text.rl" + { + tok = p; +} + break; + case 5: +#line 55 "hb-buffer-deserialize-text.rl" + { + if (!hb_font_glyph_from_string (font, + tok, p - tok, + &info.codepoint)) + return false; +} + break; + case 10: +#line 62 "hb-buffer-deserialize-text.rl" + { if (!parse_uint (tok, p, &info.cluster )) return false; } + break; + case 3: +#line 63 "hb-buffer-deserialize-text.rl" + { if (!parse_int (tok, p, &pos.x_offset )) return false; } + break; + case 12: +#line 64 "hb-buffer-deserialize-text.rl" + { if (!parse_int (tok, p, &pos.y_offset )) return false; } + break; + case 7: +#line 65 "hb-buffer-deserialize-text.rl" + { if (!parse_int (tok, p, &pos.x_advance)) return false; } + break; + case 1: +#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 55 "hb-buffer-deserialize-text.rl" + { + 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 (buffer->in_error) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 9: +#line 62 "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 (buffer->in_error) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 11: +#line 64 "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 (buffer->in_error) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 6: +#line 65 "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 (buffer->in_error) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 8: +#line 66 "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 (buffer->in_error) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; +#line 480 "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 4: +#line 55 "hb-buffer-deserialize-text.rl" + { + 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 (buffer->in_error) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 9: +#line 62 "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 (buffer->in_error) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 11: +#line 64 "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 (buffer->in_error) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 6: +#line 65 "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 (buffer->in_error) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 8: +#line 66 "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 (buffer->in_error) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; +#line 557 "hb-buffer-deserialize-text.hh" + } + } + + _out: {} + } + +#line 119 "hb-buffer-deserialize-text.rl" + + + *end_ptr = p; + + return p == pe && *(p-1) != ']'; +} + +#endif /* HB_BUFFER_DESERIALIZE_TEXT_HH */ diff --git a/src/hb-buffer-deserialize-text.rl b/src/hb-buffer-deserialize-text.rl index 8a682f737..fd9be42df 100644 --- a/src/hb-buffer-deserialize-text.rl +++ b/src/hb-buffer-deserialize-text.rl @@ -100,7 +100,7 @@ _hb_buffer_deserialize_glyphs_text (hb_buffer_t *buffer, const char *p = buf, *pe = buf + buf_len; /* Ensure we have positions. */ - (void) hb_buffer_get_glyph_positions (buffer, NULL); + (void) hb_buffer_get_glyph_positions (buffer, nullptr); while (p < pe && ISSPACE (*p)) p++; @@ -109,7 +109,7 @@ _hb_buffer_deserialize_glyphs_text (hb_buffer_t *buffer, *end_ptr = ++p; } - const char *eof = pe, *tok = NULL; + const char *eof = pe, *tok = nullptr; int cs; hb_glyph_info_t info = {0}; hb_glyph_position_t pos = {0}; diff --git a/src/hb-buffer-private.hh b/src/hb-buffer-private.hh index ed592f448..af4767f51 100644 --- a/src/hb-buffer-private.hh +++ b/src/hb-buffer-private.hh @@ -35,8 +35,8 @@ #include "hb-unicode-private.hh" -#ifndef HB_BUFFER_MAX_EXPANSION_FACTOR -#define HB_BUFFER_MAX_EXPANSION_FACTOR 32 +#ifndef HB_BUFFER_MAX_LEN_FACTOR +#define HB_BUFFER_MAX_LEN_FACTOR 32 #endif #ifndef HB_BUFFER_MAX_LEN_MIN #define HB_BUFFER_MAX_LEN_MIN 8192 @@ -45,11 +45,22 @@ #define HB_BUFFER_MAX_LEN_DEFAULT 0x3FFFFFFF /* Shaping more than a billion chars? Let us know! */ #endif -ASSERT_STATIC (sizeof (hb_glyph_info_t) == 20); -ASSERT_STATIC (sizeof (hb_glyph_info_t) == sizeof (hb_glyph_position_t)); +#ifndef HB_BUFFER_MAX_OPS_FACTOR +#define HB_BUFFER_MAX_OPS_FACTOR 64 +#endif +#ifndef HB_BUFFER_MAX_OPS_MIN +#define HB_BUFFER_MAX_OPS_MIN 1024 +#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_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, @@ -57,6 +68,9 @@ enum hb_buffer_scratch_flags_t { 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_UNSAFE_TO_BREAK = 0x00000010u, + HB_BUFFER_SCRATCH_FLAG_HAS_CGJ = 0x00000020u, + /* Reserved for complex shapers' internal use. */ HB_BUFFER_SCRATCH_FLAG_COMPLEX0 = 0x01000000u, HB_BUFFER_SCRATCH_FLAG_COMPLEX1 = 0x02000000u, @@ -79,8 +93,9 @@ struct hb_buffer_t { 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_buffer_scratch_flags_t scratch_flags; /* Have space-flallback, etc. */ + hb_buffer_scratch_flags_t scratch_flags; /* Have space-fallback, etc. */ unsigned int max_len; /* Maximum allowed len. */ + int max_ops; /* Maximum allowed operations. */ /* Buffer contents */ hb_buffer_content_type_t content_type; @@ -99,17 +114,6 @@ struct hb_buffer_t { hb_glyph_info_t *out_info; hb_glyph_position_t *pos; - inline hb_glyph_info_t &cur (unsigned int i = 0) { return info[idx + i]; } - inline hb_glyph_info_t cur (unsigned int i = 0) const { return info[idx + i]; } - - inline hb_glyph_position_t &cur_pos (unsigned int i = 0) { return pos[idx + i]; } - inline hb_glyph_position_t cur_pos (unsigned int i = 0) const { return pos[idx + i]; } - - inline hb_glyph_info_t &prev (void) { return out_info[out_len ? out_len - 1 : 0]; } - inline hb_glyph_info_t prev (void) const { return out_info[out_len ? out_len - 1 : 0]; } - - inline bool has_separate_output (void) const { return info != out_info; } - unsigned int serial; /* Text before / after the main buffer contents. @@ -129,12 +133,16 @@ struct hb_buffer_t { #ifndef HB_NDEBUG uint8_t allocated_var_bits; #endif + + + /* Methods */ + inline void allocate_var (unsigned int start, unsigned int count) { #ifndef HB_NDEBUG unsigned int end = start + count; assert (end <= 8); - unsigned int bits = (1< (cluster, infos[i].cluster); + return cluster; + } + inline void + _unsafe_to_break_set_mask (hb_glyph_info_t *infos, + unsigned int start, unsigned int end, + unsigned int cluster) + { + for (unsigned int i = start; i < end; i++) + if (cluster != infos[i].cluster) + { + scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_UNSAFE_TO_BREAK; + infos[i].mask |= HB_GLYPH_FLAG_UNSAFE_TO_BREAK; + } + } + + inline void + unsafe_to_break_all (void) + { + unsafe_to_break_impl (0, len); + } + inline void + safe_to_break_all (void) + { + for (unsigned int i = 0; i < len; i++) + info[i].mask &= ~HB_GLYPH_FLAG_UNSAFE_TO_BREAK; + } }; +/* Loop over clusters. Duplicated in foreach_syllable(). */ +#define foreach_cluster(buffer, start, end) \ + for (unsigned int \ + _count = buffer->len, \ + start = 0, end = _count ? _next_cluster (buffer, 0) : 0; \ + start < _count; \ + start = end, end = _next_cluster (buffer, start)) + +static inline unsigned int +_next_cluster (hb_buffer_t *buffer, unsigned int start) +{ + hb_glyph_info_t *info = buffer->info; + unsigned int count = buffer->len; + + unsigned int cluster = info[start].cluster; + while (++start < count && cluster == info[start].cluster) + ; + + return start; +} + + #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)) diff --git a/src/hb-buffer-serialize.cc b/src/hb-buffer-serialize.cc index 63a0f3466..11471941d 100644 --- a/src/hb-buffer-serialize.cc +++ b/src/hb-buffer-serialize.cc @@ -30,7 +30,7 @@ static const char *serialize_formats[] = { "text", "json", - NULL + nullptr }; /** @@ -90,7 +90,7 @@ hb_buffer_serialize_format_to_string (hb_buffer_serialize_format_t format) case HB_BUFFER_SERIALIZE_FORMAT_TEXT: return serialize_formats[0]; case HB_BUFFER_SERIALIZE_FORMAT_JSON: return serialize_formats[1]; default: - case HB_BUFFER_SERIALIZE_FORMAT_INVALID: return NULL; + case HB_BUFFER_SERIALIZE_FORMAT_INVALID: return nullptr; } } @@ -104,11 +104,12 @@ _hb_buffer_serialize_glyphs_json (hb_buffer_t *buffer, hb_font_t *font, hb_buffer_serialize_flags_t flags) { - hb_glyph_info_t *info = hb_buffer_get_glyph_infos (buffer, NULL); + hb_glyph_info_t *info = hb_buffer_get_glyph_infos (buffer, nullptr); hb_glyph_position_t *pos = (flags & HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS) ? - NULL : hb_buffer_get_glyph_positions (buffer, NULL); + 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]; @@ -145,10 +146,17 @@ _hb_buffer_serialize_glyphs_json (hb_buffer_t *buffer, if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS)) { - p += snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"dx\":%d,\"dy\":%d", - pos[i].x_offset, pos[i].y_offset); - p += snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"ax\":%d,\"ay\":%d", - pos[i].x_advance, pos[i].y_advance); + p += 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 += 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 += 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) @@ -156,9 +164,9 @@ _hb_buffer_serialize_glyphs_json (hb_buffer_t *buffer, hb_glyph_extents_t extents; hb_font_get_glyph_extents(font, info[i].codepoint, &extents); p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"xb\":%d,\"yb\":%d", - extents.x_bearing, extents.y_bearing)); + extents.x_bearing, extents.y_bearing)); p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"w\":%d,\"h\":%d", - extents.width, extents.height)); + extents.width, extents.height)); } *p++ = '}'; @@ -173,6 +181,12 @@ _hb_buffer_serialize_glyphs_json (hb_buffer_t *buffer, *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; @@ -188,11 +202,12 @@ _hb_buffer_serialize_glyphs_text (hb_buffer_t *buffer, hb_font_t *font, hb_buffer_serialize_flags_t flags) { - hb_glyph_info_t *info = hb_buffer_get_glyph_infos (buffer, NULL); + hb_glyph_info_t *info = hb_buffer_get_glyph_infos (buffer, nullptr); hb_glyph_position_t *pos = (flags & HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS) ? - NULL : hb_buffer_get_glyph_positions (buffer, NULL); + 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]; @@ -217,13 +232,22 @@ _hb_buffer_serialize_glyphs_text (hb_buffer_t *buffer, if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS)) { - if (pos[i].x_offset || pos[i].y_offset) - p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "@%d,%d", pos[i].x_offset, pos[i].y_offset)); + if (x+pos[i].x_offset || y+pos[i].y_offset) + p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "@%d,%d", x+pos[i].x_offset, y+pos[i].y_offset)); - *p++ = '+'; - p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "%d", pos[i].x_advance)); - if (pos[i].y_advance) - p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",%d", pos[i].y_advance)); + if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_ADVANCES)) + { + *p++ = '+'; + p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "%d", pos[i].x_advance)); + if (pos[i].y_advance) + p += 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 += 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) @@ -243,6 +267,12 @@ _hb_buffer_serialize_glyphs_text (hb_buffer_t *buffer, *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; @@ -311,6 +341,8 @@ hb_buffer_serialize_glyphs (hb_buffer_t *buffer, if (!buf_consumed) buf_consumed = &sconsumed; *buf_consumed = 0; + if (buf_size) + *buf = '\0'; assert ((!buffer->len && buffer->content_type == HB_BUFFER_CONTENT_TYPE_INVALID) || buffer->content_type == HB_BUFFER_CONTENT_TYPE_GLYPHS); @@ -408,8 +440,8 @@ 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 */ + const char **end_ptr, /* May be nullptr */ + hb_font_t *font, /* May be nullptr */ hb_buffer_serialize_format_t format) { const char *end; diff --git a/src/hb-buffer.cc b/src/hb-buffer.cc index 406db9c84..dc0639f4f 100644 --- a/src/hb-buffer.cc +++ b/src/hb-buffer.cc @@ -31,10 +31,6 @@ #include "hb-utf-private.hh" -#ifndef HB_DEBUG_BUFFER -#define HB_DEBUG_BUFFER (HB_DEBUG+0) -#endif - /** * SECTION: hb-buffer * @title: Buffers @@ -52,7 +48,7 @@ * * Checks the equality of two #hb_segment_properties_t's. * - * Return value: (transfer full): + * Return value: * %true if all properties of @a equal those of @b, false otherwise. * * Since: 0.9.7 @@ -124,8 +120,8 @@ hb_buffer_t::enlarge (unsigned int size) } unsigned int new_allocated = allocated; - hb_glyph_position_t *new_pos = NULL; - hb_glyph_info_t *new_info = NULL; + hb_glyph_position_t *new_pos = nullptr; + hb_glyph_info_t *new_info = nullptr; bool separate_out = out_info != info; if (unlikely (_hb_unsigned_int_mul_overflows (size, sizeof (info[0])))) @@ -134,7 +130,7 @@ hb_buffer_t::enlarge (unsigned int size) while (size >= new_allocated) new_allocated += (new_allocated >> 1) + 32; - ASSERT_STATIC (sizeof (info[0]) == sizeof (pos[0])); + static_assert ((sizeof (info[0]) == sizeof (pos[0])), ""); if (unlikely (_hb_unsigned_int_mul_overflows (new_allocated, sizeof (info[0])))) goto done; @@ -183,6 +179,12 @@ hb_buffer_t::shift_forward (unsigned int count) 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... */ + memset (info + len, 0, (idx + count - len) * sizeof (info[0])); + } len += count; idx += count; @@ -261,7 +263,7 @@ hb_buffer_t::add (hb_codepoint_t codepoint, memset (glyph, 0, sizeof (*glyph)); glyph->codepoint = codepoint; - glyph->mask = 1; + glyph->mask = 0; glyph->cluster = cluster; len++; @@ -426,6 +428,8 @@ hb_buffer_t::move_to (unsigned int 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... */ if (unlikely (idx < count && !shift_forward (count + 32))) return false; assert (idx >= count); @@ -542,12 +546,15 @@ 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 = MIN (cluster, info[i].cluster); + cluster = MIN (cluster, info[i].cluster); /* Extend end */ while (end < len && info[end - 1].cluster == info[end].cluster) @@ -560,10 +567,10 @@ hb_buffer_t::merge_clusters_impl (unsigned int 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--) - out_info[i - 1].cluster = cluster; + set_cluster (out_info[i - 1], cluster); for (unsigned int i = start; i < end; i++) - info[i].cluster = cluster; + set_cluster (info[i], cluster); } void hb_buffer_t::merge_out_clusters (unsigned int start, @@ -578,7 +585,7 @@ hb_buffer_t::merge_out_clusters (unsigned int start, unsigned int cluster = out_info[start].cluster; for (unsigned int i = start + 1; i < end; i++) - cluster = MIN (cluster, out_info[i].cluster); + cluster = MIN (cluster, out_info[i].cluster); /* Extend start */ while (start && out_info[start - 1].cluster == out_info[start].cluster) @@ -591,14 +598,16 @@ hb_buffer_t::merge_out_clusters (unsigned int start, /* 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++) - info[i].cluster = cluster; + set_cluster (info[i], cluster); for (unsigned int i = start; i < end; i++) - out_info[i].cluster = cluster; + 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) { @@ -611,9 +620,10 @@ hb_buffer_t::delete_glyph () /* 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--) - out_info[i - 1].cluster = cluster; + set_cluster (out_info[i - 1], cluster, mask); } goto done; } @@ -629,6 +639,32 @@ done: skip_glyph (); } +void +hb_buffer_t::unsafe_to_break_impl (unsigned int start, unsigned int end) +{ + unsigned int cluster = (unsigned int) -1; + cluster = _unsafe_to_break_find_min_cluster (info, start, end, cluster); + _unsafe_to_break_set_mask (info, start, end, cluster); +} +void +hb_buffer_t::unsafe_to_break_from_outbuffer (unsigned int start, unsigned int end) +{ + if (!have_output) + { + unsafe_to_break_impl (start, end); + return; + } + + assert (start <= out_len); + assert (idx <= end); + + unsigned int cluster = (unsigned int) -1; + cluster = _unsafe_to_break_find_min_cluster (out_info, start, out_len, cluster); + cluster = _unsafe_to_break_find_min_cluster (info, idx, end, cluster); + _unsafe_to_break_set_mask (out_info, start, out_len, cluster); + _unsafe_to_break_set_mask (info, idx, end, cluster); +} + void hb_buffer_t::guess_segment_properties (void) { @@ -686,6 +722,7 @@ hb_buffer_create (void) return hb_buffer_get_empty (); buffer->max_len = HB_BUFFER_MAX_LEN_DEFAULT; + buffer->max_ops = HB_BUFFER_MAX_OPS_DEFAULT; buffer->reset (); @@ -713,6 +750,7 @@ hb_buffer_get_empty (void) HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT, HB_BUFFER_SCRATCH_FLAG_DEFAULT, HB_BUFFER_MAX_LEN_DEFAULT, + HB_BUFFER_MAX_OPS_DEFAULT, HB_BUFFER_CONTENT_TYPE_INVALID, HB_SEGMENT_PROPERTIES_DEFAULT, @@ -1371,6 +1409,23 @@ hb_buffer_get_glyph_positions (hb_buffer_t *buffer, return (hb_glyph_position_t *) buffer->pos; } +/** + * 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. @@ -1658,6 +1713,58 @@ hb_buffer_add_codepoints (hb_buffer_t *buffer, } +/** + * 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 (unsigned int) -1 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, + 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) + buffer->content_type = source->content_type; + if (!buffer->have_positions && source->have_positions) + buffer->clear_positions (); + + if (buffer->len + (end - start) < buffer->len) /* Overflows. */ + { + buffer->in_error = true; + return; + } + + unsigned int orig_len = buffer->len; + hb_buffer_set_length (buffer, buffer->len + (end - start)); + if (buffer->in_error) + return; + + 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])); +} + + static int compare_info_codepoint (const hb_glyph_info_t *pa, const hb_glyph_info_t *pb) @@ -1728,7 +1835,8 @@ void hb_buffer_normalize_glyphs (hb_buffer_t *buffer) { assert (buffer->have_positions); - assert (buffer->content_type == HB_BUFFER_CONTENT_TYPE_GLYPHS); + assert (buffer->content_type == HB_BUFFER_CONTENT_TYPE_GLYPHS || + (!buffer->len && buffer->content_type == HB_BUFFER_CONTENT_TYPE_INVALID)); bool backward = HB_DIRECTION_IS_BACKWARD (buffer->props.direction); @@ -1767,6 +1875,98 @@ hb_buffer_t::sort (unsigned int start, unsigned int end, int(*compar)(const hb_g } } + +/* + * Comparing buffers. + */ + +/** + * hb_buffer_diff: + * + * 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. */ @@ -1795,9 +1995,9 @@ hb_buffer_set_message_func (hb_buffer_t *buffer, buffer->message_data = user_data; buffer->message_destroy = destroy; } else { - buffer->message_func = NULL; - buffer->message_data = NULL; - buffer->message_destroy = NULL; + buffer->message_func = nullptr; + buffer->message_data = nullptr; + buffer->message_destroy = nullptr; } } diff --git a/src/hb-buffer.h b/src/hb-buffer.h index bf289c19b..8a2d3e869 100644 --- a/src/hb-buffer.h +++ b/src/hb-buffer.h @@ -63,7 +63,7 @@ HB_BEGIN_DECLS */ typedef struct hb_glyph_info_t { hb_codepoint_t codepoint; - hb_mask_t mask; + hb_mask_t mask; /* Holds hb_glyph_flags_t after hb_shape(), plus other things. */ uint32_t cluster; /*< private >*/ @@ -71,6 +71,37 @@ typedef struct hb_glyph_info_t { 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's safe to break the glyph-run at the + * beginning of this cluster, and the two sides + * 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, or limiting + * the reshaping to a small piece around the + * breaking point only. + */ +typedef enum { /*< flags >*/ + HB_GLYPH_FLAG_UNSAFE_TO_BREAK = 0x00000001, + + HB_GLYPH_FLAG_DEFINED = 0x00000001 /* 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 @@ -119,8 +150,8 @@ typedef struct hb_segment_properties_t { #define HB_SEGMENT_PROPERTIES_DEFAULT {HB_DIRECTION_INVALID, \ HB_SCRIPT_INVALID, \ HB_LANGUAGE_INVALID, \ - NULL, \ - NULL} + (void *) 0, \ + (void *) 0} HB_EXTERN hb_bool_t hb_segment_properties_equal (const hb_segment_properties_t *a, @@ -163,6 +194,7 @@ HB_EXTERN void * hb_buffer_get_user_data (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. @@ -233,13 +265,21 @@ hb_buffer_guess_segment_properties (hb_buffer_t *buffer); * 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_EOT. + * @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 (currently done - * by replacing them with the space glyph and zeroing the - * advance width.) + * 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 * * Since: 0.9.20 */ @@ -247,7 +287,8 @@ 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_PRESERVE_DEFAULT_IGNORABLES = 0x00000004u, + HB_BUFFER_FLAG_REMOVE_DEFAULT_IGNORABLES = 0x00000008u } hb_buffer_flags_t; HB_EXTERN void @@ -359,6 +400,11 @@ hb_buffer_add_codepoints (hb_buffer_t *buffer, unsigned int item_offset, int item_length); +HB_EXTERN void +hb_buffer_append (hb_buffer_t *buffer, + hb_buffer_t *source, + unsigned int start, + unsigned int end); HB_EXTERN hb_bool_t hb_buffer_set_length (hb_buffer_t *buffer, @@ -393,6 +439,9 @@ hb_buffer_normalize_glyphs (hb_buffer_t *buffer); * @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 * * Flags that control what glyph information are serialized in hb_buffer_serialize_glyphs(). * @@ -403,7 +452,9 @@ typedef enum { /*< flags >*/ 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_EXTENTS = 0x00000008u, + HB_BUFFER_SERIALIZE_FLAG_GLYPH_FLAGS = 0x00000010u, + HB_BUFFER_SERIALIZE_FLAG_NO_ADVANCES = 0x00000020u } hb_buffer_serialize_flags_t; /** @@ -452,6 +503,45 @@ hb_buffer_deserialize_glyphs (hb_buffer_t *buffer, hb_buffer_serialize_format_t format); +/* + * Compare buffers + */ + +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. */ diff --git a/src/hb-cache-private.hh b/src/hb-cache-private.hh deleted file mode 100644 index 19b70b7e3..000000000 --- a/src/hb-cache-private.hh +++ /dev/null @@ -1,74 +0,0 @@ -/* - * 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_PRIVATE_HH -#define HB_CACHE_PRIVATE_HH - -#include "hb-private.hh" - - -/* Implements a lock-free cache for int->int functions. */ - -template -struct hb_cache_t -{ - ASSERT_STATIC (key_bits >= cache_bits); - ASSERT_STATIC (key_bits + value_bits - cache_bits < 8 * sizeof (unsigned int)); - - inline void clear (void) - { - memset (values, 255, sizeof (values)); - } - - inline bool get (unsigned int key, unsigned int *value) - { - unsigned int k = key & ((1<> value_bits) != (key >> cache_bits)) - return false; - *value = v & ((1<> key_bits) || (value >> value_bits))) - return false; /* Overflows */ - unsigned int k = key & ((1<>cache_bits)< hb_cmap_cache_t; -typedef hb_cache_t<16, 24, 8> hb_advance_cache_t; - - -#endif /* HB_CACHE_PRIVATE_HH */ diff --git a/src/hb-common.cc b/src/hb-common.cc index 5ef832c00..d1fcf799a 100644 --- a/src/hb-common.cc +++ b/src/hb-common.cc @@ -32,6 +32,9 @@ #include "hb-object-private.hh" #include +#ifdef HAVE_XLOCALE_H +#include +#endif /* hb_options_t */ @@ -82,13 +85,13 @@ hb_tag_from_string (const char *str, int len) for (; i < 4; i++) tag[i] = ' '; - return HB_TAG_CHAR4 (tag); + return HB_TAG (tag[0], tag[1], tag[2], tag[3]); } /** * hb_tag_to_string: * @tag: - * @buf: (array fixed-size=4): + * @buf: (out caller-allocates) (array fixed-size=4) (element-type uint8_t): * * * @@ -186,8 +189,10 @@ lang_equal (hb_language_t v1, const unsigned char *p1 = (const unsigned char *) v1; const unsigned char *p2 = (const unsigned char *) v2; - while (*p1 && *p1 == canon_map[*p2]) - p1++, p2++; + while (*p1 && *p1 == canon_map[*p2]) { + p1++; + p2++; + } return *p1 == canon_map[*p2]; } @@ -219,9 +224,18 @@ struct hb_language_item_t { } inline hb_language_item_t & operator = (const char *s) { - lang = (hb_language_t) strdup (s); - for (unsigned char *p = (unsigned char *) lang; *p; p++) - *p = canon_map[*p]; + /* If a custom allocated is used calling strdup() pairs + badly with a call to the custom free() in finish() below. + Therefore don't call strdup(), implement its behavior. + */ + size_t len = strlen(s) + 1; + lang = (hb_language_t) 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; } @@ -235,8 +249,8 @@ struct hb_language_item_t { static hb_language_item_t *langs; #ifdef HB_USE_ATEXIT -static -void free_langs (void) +static void +free_langs (void) { while (langs) { hb_language_item_t *next = langs->next; @@ -260,9 +274,14 @@ retry: /* Not found; allocate one. */ hb_language_item_t *lang = (hb_language_item_t *) calloc (1, sizeof (hb_language_item_t)); if (unlikely (!lang)) - return NULL; + return nullptr; lang->next = first_lang; *lang = key; + if (unlikely (!lang->lang)) + { + free (lang); + return nullptr; + } if (!hb_atomic_ptr_cmpexch (&langs, first_lang, lang)) { lang->finish (); @@ -299,7 +318,7 @@ hb_language_from_string (const char *str, int len) if (!str || !len || !*str) return HB_LANGUAGE_INVALID; - hb_language_item_t *item = NULL; + hb_language_item_t *item = nullptr; if (len >= 0) { /* NUL-terminate it. */ @@ -330,7 +349,7 @@ hb_language_from_string (const char *str, int len) const char * hb_language_to_string (hb_language_t language) { - /* This is actually NULL-safe! */ + /* This is actually nullptr-safe! */ return language->s; } @@ -350,7 +369,7 @@ hb_language_get_default (void) hb_language_t language = (hb_language_t) hb_atomic_ptr_get (&default_language); if (unlikely (language == HB_LANGUAGE_INVALID)) { - language = hb_language_from_string (setlocale (LC_CTYPE, NULL), -1); + language = hb_language_from_string (setlocale (LC_CTYPE, nullptr), -1); (void) hb_atomic_ptr_cmpexch (&default_language, HB_LANGUAGE_INVALID, language); } @@ -505,6 +524,7 @@ hb_script_get_horizontal_direction (hb_script_t script) case HB_SCRIPT_PSALTER_PAHLAVI: /* Unicode-8.0 additions */ + case HB_SCRIPT_HATRAN: case HB_SCRIPT_OLD_HUNGARIAN: /* Unicode-9.0 additions */ @@ -543,9 +563,9 @@ hb_user_data_array_t::set (hb_user_data_key_t *key, void * hb_user_data_array_t::get (hb_user_data_key_t *key) { - hb_user_data_item_t item = {NULL, NULL, NULL}; + hb_user_data_item_t item = {nullptr, nullptr, nullptr}; - return items.find (key, &item, lock) ? item.data : NULL; + return items.find (key, &item, lock) ? item.data : nullptr; } @@ -605,3 +625,426 @@ hb_version_atleast (unsigned int major, { 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) +{ + char buf[32]; + unsigned int len = MIN (ARRAY_LENGTH (buf) - 1, (unsigned int) (end - *pp)); + strncpy (buf, *pp, len); + buf[len] = '\0'; + + char *p = buf; + char *pend = p; + unsigned int v; + + /* Intentionally use strtol instead of strtoul, such that + * -1 turns into "big number"... */ + errno = 0; + v = strtol (p, &pend, 0); + if (errno || p == pend) + return false; + + *pv = v; + *pp += pend - p; + return true; +} + +static bool +parse_uint32 (const char **pp, const char *end, uint32_t *pv) +{ + char buf[32]; + unsigned int len = MIN (ARRAY_LENGTH (buf) - 1, (unsigned int) (end - *pp)); + strncpy (buf, *pp, len); + buf[len] = '\0'; + + char *p = buf; + char *pend = p; + unsigned int v; + + /* Intentionally use strtol instead of strtoul, such that + * -1 turns into "big number"... */ + errno = 0; + v = strtol (p, &pend, 0); + if (errno || p == pend) + return false; + + *pv = v; + *pp += pend - p; + return true; +} + +#if defined (HAVE_NEWLOCALE) && defined (HAVE_STRTOD_L) +#define USE_XLOCALE 1 +#define HB_LOCALE_T locale_t +#define HB_CREATE_LOCALE(locName) newlocale (LC_ALL_MASK, locName, nullptr) +#define HB_FREE_LOCALE(loc) freelocale (loc) +#elif defined(_MSC_VER) +#define USE_XLOCALE 1 +#define HB_LOCALE_T _locale_t +#define HB_CREATE_LOCALE(locName) _create_locale (LC_ALL, locName) +#define HB_FREE_LOCALE(loc) _free_locale (loc) +#define strtod_l(a, b, c) _strtod_l ((a), (b), (c)) +#endif + +#ifdef USE_XLOCALE + +static HB_LOCALE_T C_locale; + +#ifdef HB_USE_ATEXIT +static void +free_C_locale (void) +{ + if (C_locale) + HB_FREE_LOCALE (C_locale); +} +#endif + +static HB_LOCALE_T +get_C_locale (void) +{ +retry: + HB_LOCALE_T C = (HB_LOCALE_T) hb_atomic_ptr_get (&C_locale); + + if (unlikely (!C)) + { + C = HB_CREATE_LOCALE ("C"); + + if (!hb_atomic_ptr_cmpexch (&C_locale, nullptr, C)) + { + HB_FREE_LOCALE (C_locale); + goto retry; + } + +#ifdef HB_USE_ATEXIT + atexit (free_C_locale); /* First person registers atexit() callback. */ +#endif + } + + return C; +} +#endif + +static bool +parse_float (const char **pp, const char *end, float *pv) +{ + char buf[32]; + unsigned int len = MIN (ARRAY_LENGTH (buf) - 1, (unsigned int) (end - *pp)); + strncpy (buf, *pp, len); + buf[len] = '\0'; + + char *p = buf; + char *pend = p; + float v; + + errno = 0; +#ifdef USE_XLOCALE + v = strtod_l (p, &pend, get_C_locale ()); +#else + v = strtod (p, &pend); +#endif + if (errno || p == pend) + return false; + + *pv = v; + *pp += pend - p; + 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 && 0 == strncmp (p, "on", 2)) + *pv = 1; + else if (*pp - p == 3 && 0 == strncmp (p, "off", 3)) + *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)++; + + 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 = 0; + feature->end = (unsigned int) -1; + + if (!parse_char (pp, end, '[')) + return true; + + has_start = parse_uint (pp, end, &feature->start); + + if (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. + * + * TODO: document the syntax here. + * + * 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 != 0 || feature->end != (unsigned int) -1) + { + s[len++] = '['; + if (feature->start) + len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->start)); + if (feature->end != feature->start + 1) { + s[len++] = ':'; + if (feature->end != (unsigned int) -1) + len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->end)); + } + s[len++] = ']'; + } + if (feature->value > 1) + { + s[len++] = '='; + len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->value)); + } + assert (len < ARRAY_LENGTH (s)); + len = 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. */ + return parse_float (pp, end, &variation->value); +} + +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: + * + * 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; +} + +/** + * hb_variation_to_string: + * + * 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++] = '='; + len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%g", variation->value)); + + assert (len < ARRAY_LENGTH (s)); + len = MIN (len, size - 1); + memcpy (buf, s, len); + buf[len] = '\0'; +} diff --git a/src/hb-common.h b/src/hb-common.h index 2cbee76a8..26200ce12 100644 --- a/src/hb-common.h +++ b/src/hb-common.h @@ -43,30 +43,16 @@ # endif /* !__cplusplus */ #endif -#if !defined (HB_DONT_DEFINE_STDINT) - #if defined (_SVR4) || defined (SVR4) || defined (__OpenBSD__) || \ defined (_sgi) || defined (__sun) || defined (sun) || \ defined (__digital__) || defined (__HP_cc) # include #elif defined (_AIX) # include -/* VS 2010 (_MSC_VER 1600) has stdint.h */ -#elif defined (_MSC_VER) && _MSC_VER < 1600 -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; #else # include #endif -#endif - HB_BEGIN_DECLS @@ -148,7 +134,7 @@ hb_language_from_string (const char *str, int len); HB_EXTERN const char * hb_language_to_string (hb_language_t language); -#define HB_LANGUAGE_INVALID ((hb_language_t) NULL) +#define HB_LANGUAGE_INVALID ((hb_language_t) 0) HB_EXTERN hb_language_t hb_language_get_default (void); @@ -321,6 +307,14 @@ typedef enum /*9.0*/ HB_SCRIPT_TANGUT = HB_TAG ('T','a','n','g'), /*9.0*/ HB_SCRIPT_NEWA = HB_TAG ('N','e','w','a'), + /* + * Since 1.6.0 + */ + /*10.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'), + /* No script set. */ HB_SCRIPT_INVALID = HB_TAG_NONE, @@ -362,6 +356,42 @@ typedef struct hb_user_data_key_t { typedef void (*hb_destroy_func_t) (void *user_data); +/* Font features and variations. */ + +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: + * + * 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_END_DECLS #endif /* HB_COMMON_H */ diff --git a/src/hb-coretext.cc b/src/hb-coretext.cc index e64d2651a..aba7cf44d 100644 --- a/src/hb-coretext.cc +++ b/src/hb-coretext.cc @@ -27,15 +27,34 @@ */ #define HB_SHAPER coretext + +#include "hb-private.hh" +#include "hb-debug.hh" #include "hb-shaper-impl-private.hh" #include "hb-coretext.h" +#include +/* https://developer.apple.com/documentation/coretext/1508745-ctfontcreatewithgraphicsfont */ +#define HB_CORETEXT_DEFAULT_FONT_SIZE 12.f -#ifndef HB_DEBUG_CORETEXT -#define HB_DEBUG_CORETEXT (HB_DEBUG+0) -#endif - +static CGFloat +coretext_font_size_from_ptem (float ptem) +{ + /* CoreText points are CSS pixels (96 per inch), + * NOT typographic points (72 per inch). + * + * https://developer.apple.com/library/content/documentation/GraphicsAnimation/Conceptual/HighResolutionOSX/Explained/Explained.html + */ + ptem *= 96.f / 72.f; + return ptem <= 0.f ? HB_CORETEXT_DEFAULT_FONT_SIZE : ptem; +} +static float +coretext_font_size_to_ptem (CGFloat size) +{ + size *= 72.f / 96.f; + return size <= 0.f ? 0 : size; +} static void release_table_data (void *user_data) @@ -50,32 +69,32 @@ 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 NULL; + return nullptr; const char *data = reinterpret_cast (CFDataGetBytePtr (cf_data)); const size_t length = CFDataGetLength (cf_data); if (!data || !length) - return NULL; + { + 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); } -hb_face_t * -hb_coretext_face_create (CGFontRef cg_font) +static void +_hb_cg_font_release (void *data) { - return hb_face_create_for_tables (reference_table, CGFontRetain (cg_font), (hb_destroy_func_t) CGFontRelease); + CGFontRelease ((CGFontRef) data); } -HB_SHAPER_DATA_ENSURE_DECLARE(coretext, face) -HB_SHAPER_DATA_ENSURE_DECLARE(coretext, font) - - -/* - * shaper face data - */ +HB_SHAPER_DATA_ENSURE_DEFINE(coretext, face) +HB_SHAPER_DATA_ENSURE_DEFINE_WITH_CONDITION(coretext, font, + fabs (CTFontGetSize((CTFontRef) data) - coretext_font_size_from_ptem (font->ptem)) <= .5 +) static CTFontDescriptorRef get_last_resort_font_desc (void) @@ -104,7 +123,7 @@ 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, NULL) == data); + hb_blob_get_data ((hb_blob_t *) info, nullptr) == data); hb_blob_destroy ((hb_blob_t *) info); } @@ -112,8 +131,8 @@ release_data (void *info, const void *data, size_t size) static CGFontRef create_cg_font (hb_face_t *face) { - CGFontRef cg_font = NULL; - if (face->destroy == (hb_destroy_func_t) CGFontRelease) + CGFontRef cg_font = nullptr; + if (face->destroy == _hb_cg_font_release) { cg_font = CGFontRetain ((CGFontRef) face->user_data); } @@ -140,75 +159,118 @@ create_cg_font (hb_face_t *face) static CTFontRef create_ct_font (CGFontRef cg_font, CGFloat font_size) { - CTFontRef ct_font = CTFontCreateWithGraphicsFont (cg_font, font_size, NULL, NULL); + 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"))) + { + 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 NULL; + 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 + if (&CTGetCoreTextVersion != nullptr && CTGetCoreTextVersion() < 0x00070000) { + CFStringRef fontName = CTFontCopyPostScriptName (ct_font); + bool isEmojiFont = CFStringCompare (fontName, CFSTR("AppleColorEmoji"), 0) == kCFCompareEqualTo; + CFRelease (fontName); + if (!isEmojiFont) + return ct_font; + } + + CFURLRef original_url = (CFURLRef)CTFontCopyAttribute(ct_font, kCTFontURLAttribute); + /* 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, NULL, 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) { - CFRelease (ct_font); - ct_font = 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 = (CFURLRef) CTFontCopyAttribute (new_ct_font, kCTFontURLAttribute); + // 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"); } - return ct_font; + if (original_url) + CFRelease (original_url); + return ct_font; } -struct hb_coretext_shaper_face_data_t { - CGFontRef cg_font; - CTFontRef ct_font; -}; - hb_coretext_shaper_face_data_t * _hb_coretext_shaper_face_data_create (hb_face_t *face) { - hb_coretext_shaper_face_data_t *data = (hb_coretext_shaper_face_data_t *) calloc (1, sizeof (hb_coretext_shaper_face_data_t)); - if (unlikely (!data)) - return NULL; + CGFontRef cg_font = create_cg_font (face); - data->cg_font = create_cg_font (face); - if (unlikely (!data->cg_font)) + if (unlikely (!cg_font)) { DEBUG_MSG (CORETEXT, face, "CGFont creation failed.."); - free (data); - return NULL; + return nullptr; } - /* We use 36pt size instead of UPEM, because CoreText implements the 'trak' table, - * which can make the font too tight at large sizes. 36pt should be a good semi-neutral - * size. - * - * Since we always create CTFont at a fixed size, our CTFont lives in face_data - * instead of font_data. Which is good, because when people change scale on - * hb_font_t, we won't need to update our CTFont. */ - data->ct_font = create_ct_font (data->cg_font, 36.); - if (unlikely (!data->ct_font)) - { - DEBUG_MSG (CORETEXT, face, "CTFont creation failed."); - CFRelease (data->cg_font); - free (data); - return NULL; - } - - return data; + return (hb_coretext_shaper_face_data_t *) cg_font; } void _hb_coretext_shaper_face_data_destroy (hb_coretext_shaper_face_data_t *data) { - CFRelease (data->ct_font); - CFRelease (data->cg_font); - free (data); + CFRelease ((CGFontRef) data); +} + +hb_face_t * +hb_coretext_face_create (CGFontRef cg_font) +{ + return hb_face_create_for_tables (reference_table, CGFontRetain (cg_font), _hb_cg_font_release); } /* @@ -217,29 +279,66 @@ _hb_coretext_shaper_face_data_destroy (hb_coretext_shaper_face_data_t *data) CGFontRef hb_coretext_face_get_cg_font (hb_face_t *face) { - if (unlikely (!hb_coretext_shaper_face_data_ensure (face))) return NULL; - hb_coretext_shaper_face_data_t *face_data = HB_SHAPER_DATA_GET (face); - return face_data->cg_font; + if (unlikely (!hb_coretext_shaper_face_data_ensure (face))) return nullptr; + return (CGFontRef) HB_SHAPER_DATA_GET (face); } -/* - * shaper font data - */ - -struct hb_coretext_shaper_font_data_t {}; - hb_coretext_shaper_font_data_t * -_hb_coretext_shaper_font_data_create (hb_font_t *font HB_UNUSED) +_hb_coretext_shaper_font_data_create (hb_font_t *font) { - return (hb_coretext_shaper_font_data_t *) HB_SHAPER_DATA_SUCCEEDED; + hb_face_t *face = font->face; + if (unlikely (!hb_coretext_shaper_face_data_ensure (face))) return nullptr; + CGFontRef cg_font = (CGFontRef) HB_SHAPER_DATA_GET (face); + + CTFontRef ct_font = create_ct_font (cg_font, coretext_font_size_from_ptem (font->ptem)); + + if (unlikely (!ct_font)) + { + DEBUG_MSG (CORETEXT, font, "CGFont creation failed.."); + return nullptr; + } + + return (hb_coretext_shaper_font_data_t *) ct_font; } void _hb_coretext_shaper_font_data_destroy (hb_coretext_shaper_font_data_t *data) { + CFRelease ((CTFontRef) data); } +/* + * 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_inert (font))) + return font; + + hb_font_set_ptem (font, coretext_font_size_to_ptem (CTFontGetSize(ct_font))); + + /* Let there be dragons here... */ + HB_SHAPER_DATA_GET (font) = (hb_coretext_shaper_font_data_t *) CFRetain (ct_font); + + return font; +} + +CTFontRef +hb_coretext_font_get_ct_font (hb_font_t *font) +{ + if (unlikely (!hb_coretext_shaper_font_data_ensure (font))) return nullptr; + return (CTFontRef) HB_SHAPER_DATA_GET (font); +} + + /* * shaper shape_plan data @@ -250,7 +349,9 @@ struct hb_coretext_shaper_shape_plan_data_t {}; hb_coretext_shaper_shape_plan_data_t * _hb_coretext_shaper_shape_plan_data_create (hb_shape_plan_t *shape_plan HB_UNUSED, const hb_feature_t *user_features HB_UNUSED, - unsigned int num_user_features HB_UNUSED) + unsigned int num_user_features HB_UNUSED, + const int *coords HB_UNUSED, + unsigned int num_coords HB_UNUSED) { return (hb_coretext_shaper_shape_plan_data_t *) HB_SHAPER_DATA_SUCCEEDED; } @@ -260,15 +361,6 @@ _hb_coretext_shaper_shape_plan_data_destroy (hb_coretext_shaper_shape_plan_data_ { } -CTFontRef -hb_coretext_font_get_ct_font (hb_font_t *font) -{ - hb_face_t *face = font->face; - if (unlikely (!hb_coretext_shaper_face_data_ensure (face))) return NULL; - hb_coretext_shaper_face_data_t *face_data = HB_SHAPER_DATA_GET (face); - return face_data->ct_font; -} - /* * shaper @@ -283,7 +375,9 @@ struct active_feature_t { feature_record_t rec; unsigned int order; - static int cmp (const active_feature_t *a, const active_feature_t *b) { + 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 : @@ -299,7 +393,9 @@ struct feature_event_t { bool start; active_feature_t feature; - static int cmp (const feature_event_t *a, const feature_event_t *b) { + 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); @@ -498,9 +594,10 @@ _hb_coretext_shape (hb_shape_plan_t *shape_plan, unsigned int num_features) { hb_face_t *face = font->face; - hb_coretext_shaper_face_data_t *face_data = HB_SHAPER_DATA_GET (face); + CGFontRef cg_font = (CGFontRef) HB_SHAPER_DATA_GET (face); + CTFontRef ct_font = (CTFontRef) HB_SHAPER_DATA_GET (font); - CGFloat ct_font_size = CTFontGetSize (face_data->ct_font); + 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; @@ -601,22 +698,23 @@ _hb_coretext_shape (hb_shape_plan_t *shape_plan, /* active_features.qsort (); */ for (unsigned int j = 0; j < active_features.len; j++) { - CFStringRef keys[2] = { + CFStringRef keys[] = { kCTFontFeatureTypeIdentifierKey, kCTFontFeatureSelectorIdentifierKey }; - CFNumberRef values[2] = { + CFNumberRef values[] = { CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &active_features[j].rec.feature), CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &active_features[j].rec.setting) }; + static_assert ((ARRAY_LENGTH_CONST (keys) == ARRAY_LENGTH_CONST (values)), ""); CFDictionaryRef dict = CFDictionaryCreate (kCFAllocatorDefault, (const void **) keys, (const void **) values, - 2, + ARRAY_LENGTH (keys), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); - CFRelease (values[0]); - CFRelease (values[1]); + for (unsigned int i = 0; i < ARRAY_LENGTH (values); i++) + CFRelease (values[i]); CFArrayAppendValue (features_array, dict); CFRelease (dict); @@ -634,12 +732,12 @@ _hb_coretext_shape (hb_shape_plan_t *shape_plan, CTFontDescriptorRef font_desc = CTFontDescriptorCreateWithAttributes (attributes); CFRelease (attributes); - range->font = CTFontCreateCopyWithAttributes (face_data->ct_font, 0.0, NULL, font_desc); + range->font = CTFontCreateCopyWithAttributes (ct_font, 0.0, nullptr, font_desc); CFRelease (font_desc); } else { - range->font = NULL; + range->font = nullptr; } range->index_first = last_index; @@ -659,9 +757,6 @@ _hb_coretext_shape (hb_shape_plan_t *shape_plan, active_features.remove (feature - active_features.array); } } - - if (!range_records.len) /* No active feature found. */ - goto fail_features; } else { @@ -695,7 +790,7 @@ _hb_coretext_shape (hb_shape_plan_t *shape_plan, pchars[chars_len++] = 0xFFFDu; else { pchars[chars_len++] = 0xD800u + ((c - 0x10000u) >> 10); - pchars[chars_len++] = 0xDC00u + ((c - 0x10000u) & ((1 << 10) - 1)); + pchars[chars_len++] = 0xDC00u + ((c - 0x10000u) & ((1u << 10) - 1)); } } @@ -712,14 +807,14 @@ _hb_coretext_shape (hb_shape_plan_t *shape_plan, #define FAIL(...) \ HB_STMT_START { \ - DEBUG_MSG (CORETEXT, NULL, __VA_ARGS__); \ + DEBUG_MSG (CORETEXT, nullptr, __VA_ARGS__); \ ret = false; \ goto fail; \ } HB_STMT_END; bool ret = true; - CFStringRef string_ref = NULL; - CTLineRef line = NULL; + CFStringRef string_ref = nullptr; + CTLineRef line = nullptr; if (0) { @@ -731,8 +826,8 @@ resize_and_retry: assert (line); CFRelease (string_ref); CFRelease (line); - string_ref = NULL; - line = NULL; + string_ref = nullptr; + line = nullptr; /* Get previous start-of-scratch-area, that we use later for readjusting * our existing scratch arrays. */ @@ -753,7 +848,7 @@ resize_and_retry: scratch_size -= old_scratch_used; } { - string_ref = CFStringCreateWithCharactersNoCopy (NULL, + string_ref = CFStringCreateWithCharactersNoCopy (nullptr, pchars, chars_len, kCFAllocatorNull); if (unlikely (!string_ref)) @@ -785,15 +880,18 @@ resize_and_retry: 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, face_data->ct_font); + kCTFontAttributeName, ct_font); - if (num_features) + if (num_features && range_records.len) { unsigned int start = 0; range_record_t *last_range = &range_records[0]; @@ -819,6 +917,30 @@ resize_and_retry: 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, + 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); @@ -828,8 +950,12 @@ resize_and_retry: 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + CFRelease (level_number); if (unlikely (!options)) + { + CFRelease (attr_string); FAIL ("CFDictionaryCreate failed"); + } CTTypesetterRef typesetter = CTTypesetterCreateWithAttributedStringAndOptions (attr_string, options); CFRelease (options); @@ -845,7 +971,7 @@ resize_and_retry: CFArrayRef glyph_runs = CTLineGetGlyphRuns (line); unsigned int num_runs = CFArrayGetCount (glyph_runs); - DEBUG_MSG (CORETEXT, NULL, "Num runs: %d", num_runs); + DEBUG_MSG (CORETEXT, nullptr, "Num runs: %d", num_runs); buffer->len = 0; uint32_t status_and = ~0, status_or = 0; @@ -871,7 +997,7 @@ resize_and_retry: status_or |= run_status; status_and &= run_status; DEBUG_MSG (CORETEXT, run, "CTRunStatus: %x", run_status); - double run_advance = CTRunGetTypographicBounds (run, range_all, NULL, NULL, NULL); + double 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", run_advance); @@ -884,7 +1010,7 @@ resize_and_retry: */ CFDictionaryRef attributes = CTRunGetAttributes (run); CTFontRef run_ct_font = static_cast(CFDictionaryGetValue (attributes, kCTFontAttributeName)); - if (!CFEqual (run_ct_font, face_data->ct_font)) + 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 @@ -906,7 +1032,7 @@ resize_and_retry: * However, even that wouldn't work if we were passed in the CGFont to * construct a hb_face to begin with. * - * See: http://github.com/behdad/harfbuzz/pull/36 + * See: http://github.com/harfbuzz/harfbuzz/pull/36 * * Also see: https://bugs.chromium.org/p/chromium/issues/detail?id=597098 */ @@ -919,16 +1045,16 @@ resize_and_retry: } if (!matched) { - CGFontRef run_cg_font = CTFontCopyGraphicsFont (run_ct_font, 0); + CGFontRef run_cg_font = CTFontCopyGraphicsFont (run_ct_font, nullptr); if (run_cg_font) { - matched = CFEqual (run_cg_font, face_data->cg_font); + matched = CFEqual (run_cg_font, cg_font); CFRelease (run_cg_font); } } if (!matched) { - CFStringRef font_ps_name = CTFontCopyName (face_data->ct_font, kCTFontPostScriptNameKey); + 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); @@ -997,7 +1123,7 @@ resize_and_retry: /* 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 NULL is returned + * 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. */ @@ -1013,13 +1139,13 @@ resize_and_retry: { /* Setup glyphs */ SCRATCH_SAVE(); - const CGGlyph* glyphs = USE_PTR ? CTRunGetGlyphsPtr (run) : NULL; + 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) : NULL; + 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); @@ -1041,7 +1167,7 @@ resize_and_retry: * 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) : NULL; + 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); @@ -1092,7 +1218,7 @@ resize_and_retry: } /* Mac OS 10.6 doesn't have kCTTypesetterOptionForcedEmbeddingLevel, - * or if it does, it doesn't resepct it. So we get runs with wrong + * or if it does, it doesn't respect it. So we get runs with wrong * directions. As such, disable the assert... It wouldn't crash, but * cursoring will be off... * @@ -1117,6 +1243,7 @@ resize_and_retry: pos->x_advance = info->mask; pos->x_offset = info->var1.i32; pos->y_offset = info->var2.i32; + info++, pos++; } else @@ -1125,6 +1252,7 @@ resize_and_retry: pos->y_advance = info->mask; pos->x_offset = info->var1.i32; pos->y_offset = info->var2.i32; + info++, pos++; } @@ -1162,6 +1290,8 @@ resize_and_retry: } } + buffer->unsafe_to_break_all (); + #undef FAIL fail: @@ -1182,6 +1312,9 @@ fail: * AAT shaper */ +HB_SHAPER_DATA_ENSURE_DEFINE(coretext_aat, face) +HB_SHAPER_DATA_ENSURE_DEFINE(coretext_aat, font) + /* * shaper face data */ @@ -1191,22 +1324,20 @@ struct hb_coretext_aat_shaper_face_data_t {}; hb_coretext_aat_shaper_face_data_t * _hb_coretext_aat_shaper_face_data_create (hb_face_t *face) { - hb_blob_t *mort_blob = face->reference_table (HB_CORETEXT_TAG_MORT); - /* Umm, we just reference the table to check whether it exists. - * Maybe add better API for this? */ - if (!hb_blob_get_length (mort_blob)) - { - hb_blob_destroy (mort_blob); - mort_blob = face->reference_table (HB_CORETEXT_TAG_MORX); - if (!hb_blob_get_length (mort_blob)) - { - hb_blob_destroy (mort_blob); - return NULL; - } - } - hb_blob_destroy (mort_blob); + static const hb_tag_t tags[] = {HB_CORETEXT_TAG_MORX, HB_CORETEXT_TAG_MORT, HB_CORETEXT_TAG_KERX}; - return hb_coretext_shaper_face_data_ensure (face) ? (hb_coretext_aat_shaper_face_data_t *) HB_SHAPER_DATA_SUCCEEDED : NULL; + for (unsigned int i = 0; i < ARRAY_LENGTH (tags); i++) + { + hb_blob_t *blob = face->reference_table (tags[i]); + if (hb_blob_get_length (blob)) + { + hb_blob_destroy (blob); + return hb_coretext_shaper_face_data_ensure (face) ? (hb_coretext_aat_shaper_face_data_t *) HB_SHAPER_DATA_SUCCEEDED : nullptr; + } + hb_blob_destroy (blob); + } + + return nullptr; } void @@ -1224,7 +1355,7 @@ struct hb_coretext_aat_shaper_font_data_t {}; hb_coretext_aat_shaper_font_data_t * _hb_coretext_aat_shaper_font_data_create (hb_font_t *font) { - return hb_coretext_shaper_font_data_ensure (font) ? (hb_coretext_aat_shaper_font_data_t *) HB_SHAPER_DATA_SUCCEEDED : NULL; + return hb_coretext_shaper_font_data_ensure (font) ? (hb_coretext_aat_shaper_font_data_t *) HB_SHAPER_DATA_SUCCEEDED : nullptr; } void @@ -1242,7 +1373,9 @@ struct hb_coretext_aat_shaper_shape_plan_data_t {}; hb_coretext_aat_shaper_shape_plan_data_t * _hb_coretext_aat_shaper_shape_plan_data_create (hb_shape_plan_t *shape_plan HB_UNUSED, const hb_feature_t *user_features HB_UNUSED, - unsigned int num_user_features HB_UNUSED) + unsigned int num_user_features HB_UNUSED, + const int *coords HB_UNUSED, + unsigned int num_coords HB_UNUSED) { return (hb_coretext_aat_shaper_shape_plan_data_t *) HB_SHAPER_DATA_SUCCEEDED; } diff --git a/src/hb-coretext.h b/src/hb-coretext.h index 82066e4e0..4b0a6f01b 100644 --- a/src/hb-coretext.h +++ b/src/hb-coretext.h @@ -42,11 +42,15 @@ HB_BEGIN_DECLS #define HB_CORETEXT_TAG_MORT HB_TAG('m','o','r','t') #define HB_CORETEXT_TAG_MORX HB_TAG('m','o','r','x') +#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); diff --git a/src/hb-debug.hh b/src/hb-debug.hh new file mode 100644 index 000000000..c244347b3 --- /dev/null +++ b/src/hb-debug.hh @@ -0,0 +1,444 @@ +/* + * 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-private.hh" + + +#ifndef HB_DEBUG +#define HB_DEBUG 0 +#endif + +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 - 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_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_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_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_void_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); + } + inline ~hb_auto_trace_t (void) + { + _hb_warn_no_return (returned); + if (!returned) { + _hb_debug_msg (what, obj, nullptr, true, plevel ? *plevel : 1, -1, " "); + } + if (plevel) --*plevel; + } + + inline ret_t ret (ret_t v, unsigned int line = 0) + { + if (unlikely (returned)) { + fprintf (stderr, "OUCH, double calls to return_trace(). This is a bug, please report.\n"); + return v; + } + + _hb_debug_msg (what, obj, nullptr, true, plevel ? *plevel : 1, -1, + "return %s (line %d)", + hb_printer_t().print (v), line); + if (plevel) --*plevel; + plevel = nullptr; + returned = true; + return 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) {} + + inline ret_t ret (ret_t v, unsigned int line HB_UNUSED = 0) { return v; } +}; + +/* For disabled tracing; optimize out everything. + * https://github.com/harfbuzz/harfbuzz/pull/605 */ +template +struct hb_no_trace_t { + inline ret_t ret (ret_t v, unsigned int line HB_UNUSED = 0) { return v; } +}; + +#define return_trace(RET) return trace.ret (RET, __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_GET_COVERAGE +#define HB_DEBUG_GET_COVERAGE (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_CLOSURE +#define HB_DEBUG_CLOSURE (HB_DEBUG+0) +#endif +#if HB_DEBUG_CLOSURE +#define TRACE_CLOSURE(this) \ + hb_auto_trace_t trace \ + (&c->debug_depth, c->get_name (), this, HB_FUNC, \ + " ") +#else +#define TRACE_CLOSURE(this) hb_no_trace_t trace HB_UNUSED +#endif + +#ifndef HB_DEBUG_COLLECT_GLYPHS +#define HB_DEBUG_COLLECT_GLYPHS (HB_DEBUG+0) +#endif +#if HB_DEBUG_COLLECT_GLYPHS +#define TRACE_COLLECT_GLYPHS(this) \ + hb_auto_trace_t trace \ + (&c->debug_depth, c->get_name (), this, HB_FUNC, \ + " ") +#else +#define TRACE_COLLECT_GLYPHS(this) hb_no_trace_t trace HB_UNUSED +#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_WOULD_APPLY +#define HB_DEBUG_WOULD_APPLY (HB_DEBUG+0) +#endif +#if HB_DEBUG_WOULD_APPLY +#define TRACE_WOULD_APPLY(this) \ + hb_auto_trace_t trace \ + (&c->debug_depth, c->get_name (), this, HB_FUNC, \ + "%d glyphs", c->len); +#else +#define TRACE_WOULD_APPLY(this) hb_no_trace_t trace +#endif + +#ifndef HB_DEBUG_DISPATCH +#define HB_DEBUG_DISPATCH ( \ + HB_DEBUG_APPLY + \ + HB_DEBUG_CLOSURE + \ + HB_DEBUG_COLLECT_GLYPHS + \ + HB_DEBUG_SANITIZE + \ + HB_DEBUG_SERIALIZE + \ + HB_DEBUG_SUBSET + \ + HB_DEBUG_WOULD_APPLY + \ + 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 + + +#endif /* HB_DEBUG_HH */ diff --git a/src/hb-deprecated.h b/src/hb-deprecated.h index 0398dfae6..eac7efb42 100644 --- a/src/hb-deprecated.h +++ b/src/hb-deprecated.h @@ -34,6 +34,7 @@ #include "hb-common.h" #include "hb-unicode.h" #include "hb-font.h" +#include "hb-set.h" HB_BEGIN_DECLS @@ -54,6 +55,9 @@ 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_EXTERN void +hb_set_invert (hb_set_t *set); + #endif HB_END_DECLS diff --git a/src/hb-directwrite.cc b/src/hb-directwrite.cc index 96d1870a0..69a8aa208 100644 --- a/src/hb-directwrite.cc +++ b/src/hb-directwrite.cc @@ -22,199 +22,202 @@ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. */ +#include "hb-private.hh" +#include "hb-debug.hh" #define HB_SHAPER directwrite #include "hb-shaper-impl-private.hh" -#ifndef HB_DIRECTWRITE_EXPERIMENTAL_JUSTIFICATION - #include -#else - #include -#endif +#include #include "hb-directwrite.h" -#include "hb-open-file-private.hh" -#include "hb-ot-name-table.hh" -#include "hb-ot-tag.h" + +HB_SHAPER_DATA_ENSURE_DEFINE(directwrite, face) +HB_SHAPER_DATA_ENSURE_DEFINE(directwrite, font) -#ifndef HB_DEBUG_DIRECTWRITE -#define HB_DEBUG_DIRECTWRITE (HB_DEBUG+0) -#endif +/* + * 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 fontFileReferenceKeySize, + OUT IDWriteFontFileStream** fontFileStream) + { + *fontFileStream = mFontFileStream; + return S_OK; + } +}; + +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; + } +}; -HB_SHAPER_DATA_ENSURE_DECLARE(directwrite, face) -HB_SHAPER_DATA_ENSURE_DECLARE(directwrite, font) /* * shaper face data */ struct hb_directwrite_shaper_face_data_t { - HANDLE fh; - wchar_t face_name[LF_FACESIZE]; + IDWriteFactory *dwriteFactory; + IDWriteFontFile *fontFile; + IDWriteFontFileStream *fontFileStream; + IDWriteFontFileLoader *fontFileLoader; + IDWriteFontFace *fontFace; + hb_blob_t *faceBlob; }; -/* 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); - ASSERT_STATIC (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 = OT::Sanitizer::sanitize (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 name_table_offset = (length + 3) & ~3; - - new_length = name_table_offset + ((name_table_length + 3) & ~3); - void *new_sfnt_data = calloc(1, new_length); - if (!new_sfnt_data) - { - hb_blob_destroy (blob); - return NULL; - } - - memcpy(new_sfnt_data, orig_sfnt_data, length); - - OT::name &name = OT::StructAtOffset (new_sfnt_data, name_table_offset); - name.format.set (0); - name.count.set (ARRAY_LENGTH (name_IDs)); - name.stringOffset.set (name.get_size()); - for (unsigned int i = 0; i < ARRAY_LENGTH (name_IDs); i++) - { - OT::NameRecord &record = name.nameRecord[i]; - record.platformID.set(3); - record.encodingID.set(1); - record.languageID.set(0x0409u); /* English */ - record.nameID.set(name_IDs[i]); - record.length.set(name_str_len * 2); - record.offset.set(0); - } - - /* Copy string data from new_name, converting wchar_t to UTF16BE. */ - unsigned char *p = &OT::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, name_table_length); - record.offset.set (name_table_offset); - record.length.set (name_table_length); - } - else if (face_index == 0) /* Fail if first face doesn't have 'name' table. */ - { - free (new_sfnt_data); - hb_blob_destroy (blob); - return NULL; - } - } - - /* 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, NULL, free); -} - hb_directwrite_shaper_face_data_t * _hb_directwrite_shaper_face_data_create(hb_face_t *face) { hb_directwrite_shaper_face_data_t *data = - (hb_directwrite_shaper_face_data_t *) calloc (1, sizeof (hb_directwrite_shaper_face_data_t)); + (hb_directwrite_shaper_face_data_t *) malloc (sizeof (hb_directwrite_shaper_face_data_t)); if (unlikely (!data)) - return NULL; + return nullptr; + // TODO: factory and fontFileLoader should be cached separately + IDWriteFactory* dwriteFactory; + DWriteCreateFactory ( + DWRITE_FACTORY_TYPE_SHARED, + __uuidof (IDWriteFactory), + (IUnknown**) &dwriteFactory + ); + + HRESULT hr; hb_blob_t *blob = hb_face_reference_blob (face); - if (unlikely (!hb_blob_get_length (blob))) - DEBUG_MSG(DIRECTWRITE, face, "Face has empty blob"); + IDWriteFontFileStream *fontFileStream = new DWriteFontFileStream ( + (uint8_t*) hb_blob_get_data (blob, nullptr), hb_blob_get_length (blob)); - blob = _hb_rename_font (blob, data->face_name); - if (unlikely (!blob)) - { - free(data); - return NULL; + IDWriteFontFileLoader *fontFileLoader = new DWriteFontFileLoader (fontFileStream); + dwriteFactory->RegisterFontFileLoader (fontFileLoader); + + IDWriteFontFile *fontFile; + uint64_t fontFileKey = 0; + hr = dwriteFactory->CreateCustomFontFileReference (&fontFileKey, sizeof (fontFileKey), + fontFileLoader, &fontFile); + +#define FAIL(...) \ + HB_STMT_START { \ + DEBUG_MSG (DIRECTWRITE, nullptr, __VA_ARGS__); \ + return false; \ + } HB_STMT_END; + + if (FAILED (hr)) { + FAIL ("Failed to load font file from data!"); + return false; } - DWORD num_fonts_installed; - data->fh = AddFontMemResourceEx ((void *)hb_blob_get_data(blob, NULL), - hb_blob_get_length (blob), - 0, &num_fonts_installed); - if (unlikely (!data->fh)) - { - DEBUG_MSG (DIRECTWRITE, face, "Face AddFontMemResourceEx() failed"); - free (data); - return NULL; + BOOL isSupported; + DWRITE_FONT_FILE_TYPE fileType; + DWRITE_FONT_FACE_TYPE faceType; + UINT32 numberOfFaces; + hr = fontFile->Analyze (&isSupported, &fileType, &faceType, &numberOfFaces); + if (FAILED (hr) || !isSupported) { + FAIL ("Font file is not supported."); + return false; } +#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_shaper_face_data_t *data) { - RemoveFontMemResourceEx(data->fh); - free(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) + free (data); } @@ -223,60 +226,17 @@ _hb_directwrite_shaper_face_data_destroy(hb_directwrite_shaper_face_data_t *data */ struct hb_directwrite_shaper_font_data_t { - HDC hdc; - LOGFONTW log_font; - HFONT hfont; }; -static bool -populate_log_font (LOGFONTW *lf, - hb_font_t *font) -{ - memset (lf, 0, sizeof (*lf)); - lf->lfHeight = -font->y_scale; - lf->lfCharSet = DEFAULT_CHARSET; - - hb_face_t *face = font->face; - hb_directwrite_shaper_face_data_t *face_data = HB_SHAPER_DATA_GET (face); - - memcpy (lf->lfFaceName, face_data->face_name, sizeof (lf->lfFaceName)); - - return true; -} - hb_directwrite_shaper_font_data_t * _hb_directwrite_shaper_font_data_create (hb_font_t *font) { - if (unlikely (!hb_directwrite_shaper_face_data_ensure (font->face))) return NULL; + if (unlikely (!hb_directwrite_shaper_face_data_ensure (font->face))) return nullptr; hb_directwrite_shaper_font_data_t *data = - (hb_directwrite_shaper_font_data_t *) calloc (1, sizeof (hb_directwrite_shaper_font_data_t)); + (hb_directwrite_shaper_font_data_t *) malloc (sizeof (hb_directwrite_shaper_font_data_t)); if (unlikely (!data)) - return NULL; - - data->hdc = GetDC (NULL); - - if (unlikely (!populate_log_font (&data->log_font, font))) - { - DEBUG_MSG (DIRECTWRITE, font, "Font populate_log_font() failed"); - _hb_directwrite_shaper_font_data_destroy (data); - return NULL; - } - - data->hfont = CreateFontIndirectW (&data->log_font); - if (unlikely (!data->hfont)) - { - DEBUG_MSG (DIRECTWRITE, font, "Font CreateFontIndirectW() failed"); - _hb_directwrite_shaper_font_data_destroy (data); - return NULL; - } - - if (!SelectObject (data->hdc, data->hfont)) - { - DEBUG_MSG (DIRECTWRITE, font, "Font SelectObject() failed"); - _hb_directwrite_shaper_font_data_destroy (data); - return NULL; - } + return nullptr; return data; } @@ -284,29 +244,9 @@ _hb_directwrite_shaper_font_data_create (hb_font_t *font) void _hb_directwrite_shaper_font_data_destroy (hb_directwrite_shaper_font_data_t *data) { - if (data->hdc) - ReleaseDC (NULL, data->hdc); - if (data->hfont) - DeleteObject (data->hfont); free (data); } -LOGFONTW * -hb_directwrite_font_get_logfontw (hb_font_t *font) -{ - if (unlikely (!hb_directwrite_shaper_font_data_ensure (font))) return NULL; - hb_directwrite_shaper_font_data_t *font_data = HB_SHAPER_DATA_GET (font); - return &font_data->log_font; -} - -HFONT -hb_directwrite_font_get_hfont (hb_font_t *font) -{ - if (unlikely (!hb_directwrite_shaper_font_data_ensure (font))) return NULL; - hb_directwrite_shaper_font_data_t *font_data = HB_SHAPER_DATA_GET (font); - return font_data->hfont; -} - /* * shaper shape_plan data @@ -316,8 +256,10 @@ struct hb_directwrite_shaper_shape_plan_data_t {}; hb_directwrite_shaper_shape_plan_data_t * _hb_directwrite_shaper_shape_plan_data_create (hb_shape_plan_t *shape_plan HB_UNUSED, - const hb_feature_t *user_features HB_UNUSED, - unsigned int num_user_features HB_UNUSED) + const hb_feature_t *user_features HB_UNUSED, + unsigned int num_user_features HB_UNUSED, + const int *coords HB_UNUSED, + unsigned int num_coords HB_UNUSED) { return (hb_directwrite_shaper_shape_plan_data_t *) HB_SHAPER_DATA_SUCCEEDED; } @@ -327,7 +269,7 @@ _hb_directwrite_shaper_shape_plan_data_destroy (hb_directwrite_shaper_shape_plan { } -// Most of here TextAnalysis is originally written by Bas Schouten for Mozilla project +// 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 @@ -369,7 +311,7 @@ public: , mTextLength(textLength) , mLocaleName(localeName) , mReadingDirection(readingDirection) - , mCurrentRun(NULL) { }; + , mCurrentRun(nullptr) { }; ~TextAnalysis() { // delete runs, except mRunHead which is part of the TextAnalysis object @@ -393,7 +335,7 @@ public: mRunHead.mTextLength = mTextLength; mRunHead.mBidiLevel = (mReadingDirection == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT); - mRunHead.nextRun = NULL; + mRunHead.nextRun = nullptr; mCurrentRun = &mRunHead; // Call each of the analyzers in sequence, recording their results. @@ -412,7 +354,7 @@ public: { if (textPosition >= mTextLength) { // No text at this position, valid query though. - *textString = NULL; + *textString = nullptr; *textLength = 0; } else { @@ -428,8 +370,8 @@ public: { if (textPosition == 0 || textPosition > mTextLength) { // Either there is no text before here (== 0), or this - // is an invalid position. The query is considered valid thouh. - *textString = NULL; + // is an invalid position. The query is considered valid though. + *textString = nullptr; *textLength = 0; } else { @@ -444,7 +386,8 @@ public: IFACEMETHODIMP GetLocaleName(uint32_t textPosition, uint32_t* textLength, - wchar_t const** localeName) { + wchar_t const** localeName) + { return S_OK; } @@ -454,7 +397,7 @@ public: OUT IDWriteNumberSubstitution** numberSubstitution) { // We do not support number substitution. - *numberSubstitution = NULL; + *numberSubstitution = nullptr; *textLength = mTextLength - textPosition; return S_OK; @@ -469,7 +412,8 @@ public: { SetCurrentRun(textPosition); SplitCurrentRun(textPosition); - while (textLength > 0) { + while (textLength > 0) + { Run *run = FetchNextRun(&textLength); run->mScript = *scriptAnalysis; } @@ -502,10 +446,12 @@ protected: 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); + if (*textLength < mCurrentRun->mTextLength) + { + SplitCurrentRun (mCurrentRun->mTextStart + *textLength); } - else { + else + { // Just advance the current run. mCurrentRun = mCurrentRun->nextRun; } @@ -522,12 +468,14 @@ protected: // this will usually just return early. If not, find the // corresponding run for the text position. - if (mCurrentRun && mCurrentRun->ContainsTextPosition(textPosition)) { + if (mCurrentRun && mCurrentRun->ContainsTextPosition (textPosition)) + { return; } for (Run *run = &mRunHead; run; run = run->nextRun) { - if (run->ContainsTextPosition(textPosition)) { + if (run->ContainsTextPosition (textPosition)) + { mCurrentRun = run; return; } @@ -538,13 +486,15 @@ protected: void SplitCurrentRun(uint32_t splitPosition) { - if (!mCurrentRun) { + if (!mCurrentRun) + { //NS_ASSERTION(false, "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) { + if (splitPosition <= mCurrentRun->mTextStart) + { // No need to split, already the start of a run // or before it. Usually the first. return; @@ -590,42 +540,22 @@ static inline uint32_t hb_uint32_swap (const uint32_t v) * shaper */ -hb_bool_t -_hb_directwrite_shape(hb_shape_plan_t *shape_plan, +static hb_bool_t +_hb_directwrite_shape_full(hb_shape_plan_t *shape_plan, hb_font_t *font, hb_buffer_t *buffer, const hb_feature_t *features, - unsigned int num_features) + unsigned int num_features, + float lineWidth) { hb_face_t *face = font->face; hb_directwrite_shaper_face_data_t *face_data = HB_SHAPER_DATA_GET (face); hb_directwrite_shaper_font_data_t *font_data = HB_SHAPER_DATA_GET (font); + IDWriteFactory *dwriteFactory = face_data->dwriteFactory; + IDWriteFontFace *fontFace = face_data->fontFace; - // factory probably should be cached -#ifndef HB_DIRECTWRITE_EXPERIMENTAL_JUSTIFICATION - IDWriteFactory* dwriteFactory; -#else - IDWriteFactory1* dwriteFactory; -#endif - DWriteCreateFactory ( - DWRITE_FACTORY_TYPE_SHARED, - __uuidof (IDWriteFactory), - (IUnknown**) &dwriteFactory - ); - - IDWriteGdiInterop *gdiInterop; - dwriteFactory->GetGdiInterop (&gdiInterop); - IDWriteFontFace* fontFace; - gdiInterop->CreateFontFaceFromHdc (font_data->hdc, &fontFace); - -#ifndef HB_DIRECTWRITE_EXPERIMENTAL_JUSTIFICATION IDWriteTextAnalyzer* analyzer; dwriteFactory->CreateTextAnalyzer(&analyzer); -#else - IDWriteTextAnalyzer* analyzer0; - dwriteFactory->CreateTextAnalyzer (&analyzer0); - IDWriteTextAnalyzer1* analyzer = (IDWriteTextAnalyzer1*) analyzer0; -#endif unsigned int scratch_size; hb_buffer_t::scratch_buffer_t *scratch = buffer->get_scratch_buffer (&scratch_size); @@ -653,7 +583,7 @@ _hb_directwrite_shape(hb_shape_plan_t *shape_plan, textString[chars_len++] = 0xFFFDu; else { textString[chars_len++] = 0xD800u + ((c - 0x10000u) >> 10); - textString[chars_len++] = 0xDC00u + ((c - 0x10000u) & ((1 << 10) - 1)); + textString[chars_len++] = 0xDC00u + ((c - 0x10000u) & ((1u << 10) - 1)); } } @@ -672,7 +602,6 @@ _hb_directwrite_shape(hb_shape_plan_t *shape_plan, } } - HRESULT hr; // TODO: Handle TEST_DISABLE_OPTIONAL_LIGATURES DWRITE_READING_DIRECTION readingDirection = buffer->props.direction ? @@ -686,13 +615,14 @@ _hb_directwrite_shape(hb_shape_plan_t *shape_plan, */ uint32_t textLength = buffer->len; - TextAnalysis analysis(textString, textLength, NULL, readingDirection); + TextAnalysis analysis(textString, textLength, nullptr, readingDirection); TextAnalysis::Run *runHead; + HRESULT hr; hr = analysis.GenerateResults(analyzer, &runHead); #define FAIL(...) \ HB_STMT_START { \ - DEBUG_MSG (DIRECTWRITE, NULL, __VA_ARGS__); \ + DEBUG_MSG (DIRECTWRITE, nullptr, __VA_ARGS__); \ return false; \ } HB_STMT_END; @@ -707,7 +637,7 @@ _hb_directwrite_shape(hb_shape_plan_t *shape_plan, bool isRightToLeft = HB_DIRECTION_IS_BACKWARD (buffer->props.direction); const wchar_t localeName[20] = {0}; - if (buffer->props.language != NULL) + if (buffer->props.language != nullptr) { mbstowcs ((wchar_t*) localeName, hb_language_to_string (buffer->props.language), 20); @@ -731,24 +661,22 @@ _hb_directwrite_shape(hb_shape_plan_t *shape_plan, (const DWRITE_TYPOGRAPHIC_FEATURES*) &singleFeatures; const uint32_t featureRangeLengths[] = { textLength }; -retry_getglyphs: - uint16_t* clusterMap = (uint16_t*) malloc (maxGlyphCount * sizeof (uint16_t)); - uint16_t* glyphIndices = (uint16_t*) malloc (maxGlyphCount * sizeof (uint16_t)); + uint16_t* clusterMap = (uint16_t*) malloc (textLength * sizeof (uint16_t)); DWRITE_SHAPING_TEXT_PROPERTIES* textProperties = (DWRITE_SHAPING_TEXT_PROPERTIES*) - malloc (maxGlyphCount * sizeof (DWRITE_SHAPING_TEXT_PROPERTIES)); + malloc (textLength * sizeof (DWRITE_SHAPING_TEXT_PROPERTIES)); +retry_getglyphs: + uint16_t* glyphIndices = (uint16_t*) malloc (maxGlyphCount * sizeof (uint16_t)); DWRITE_SHAPING_GLYPH_PROPERTIES* glyphProperties = (DWRITE_SHAPING_GLYPH_PROPERTIES*) malloc (maxGlyphCount * sizeof (DWRITE_SHAPING_GLYPH_PROPERTIES)); - hr = analyzer->GetGlyphs (textString, textLength, fontFace, FALSE, - isRightToLeft, &runHead->mScript, localeName, NULL, &dwFeatures, + hr = analyzer->GetGlyphs (textString, textLength, fontFace, false, + isRightToLeft, &runHead->mScript, localeName, nullptr, &dwFeatures, featureRangeLengths, 1, maxGlyphCount, clusterMap, textProperties, glyphIndices, glyphProperties, &glyphCount); if (unlikely (hr == HRESULT_FROM_WIN32 (ERROR_INSUFFICIENT_BUFFER))) { - free (clusterMap); free (glyphIndices); - free (textProperties); free (glyphProperties); maxGlyphCount *= 2; @@ -789,7 +717,7 @@ retry_getglyphs: hr = analyzer->GetGlyphPlacements (textString, clusterMap, textProperties, textLength, glyphIndices, glyphProperties, glyphCount, fontFace, fontEmSize, - FALSE, isRightToLeft, &runHead->mScript, localeName, + false, isRightToLeft, &runHead->mScript, localeName, &dwFeatures, featureRangeLengths, 1, glyphAdvances, glyphOffsets); @@ -799,106 +727,106 @@ retry_getglyphs: return false; } -#ifdef HB_DIRECTWRITE_EXPERIMENTAL_JUSTIFICATION + IDWriteTextAnalyzer1* analyzer1; + analyzer->QueryInterface (&analyzer1); - DWRITE_JUSTIFICATION_OPPORTUNITY* justificationOpportunities = - (DWRITE_JUSTIFICATION_OPPORTUNITY*) - malloc (maxGlyphCount * sizeof (DWRITE_JUSTIFICATION_OPPORTUNITY)); - hr = analyzer->GetJustificationOpportunities (fontFace, fontEmSize, - runHead->mScript, textLength, glyphCount, textString, clusterMap, - glyphProperties, justificationOpportunities); - - if (FAILED (hr)) + if (analyzer1 && lineWidth) { - FAIL ("Analyzer failed to get justification opportunities."); - return false; - } - // TODO: get lineWith from somewhere - float lineWidth = 60000; + DWRITE_JUSTIFICATION_OPPORTUNITY* justificationOpportunities = + (DWRITE_JUSTIFICATION_OPPORTUNITY*) + malloc (maxGlyphCount * sizeof (DWRITE_JUSTIFICATION_OPPORTUNITY)); + hr = analyzer1->GetJustificationOpportunities (fontFace, fontEmSize, + runHead->mScript, textLength, glyphCount, textString, clusterMap, + glyphProperties, justificationOpportunities); - float* justifiedGlyphAdvances = - (float*) malloc (maxGlyphCount * sizeof (float)); - DWRITE_GLYPH_OFFSET* justifiedGlyphOffsets = (DWRITE_GLYPH_OFFSET*) - malloc (glyphCount * sizeof (DWRITE_GLYPH_OFFSET)); - hr = analyzer->JustifyGlyphAdvances (lineWidth, glyphCount, justificationOpportunities, - glyphAdvances, glyphOffsets, justifiedGlyphAdvances, justifiedGlyphOffsets); + if (FAILED (hr)) + { + FAIL ("Analyzer failed to get justification opportunities."); + return false; + } - if (FAILED (hr)) - { - FAIL ("Analyzer failed to get justified glyph advances."); - return false; - } + float* justifiedGlyphAdvances = + (float*) malloc (maxGlyphCount * sizeof (float)); + DWRITE_GLYPH_OFFSET* justifiedGlyphOffsets = (DWRITE_GLYPH_OFFSET*) + malloc (glyphCount * sizeof (DWRITE_GLYPH_OFFSET)); + hr = analyzer1->JustifyGlyphAdvances (lineWidth, glyphCount, justificationOpportunities, + glyphAdvances, glyphOffsets, justifiedGlyphAdvances, justifiedGlyphOffsets); - DWRITE_SCRIPT_PROPERTIES scriptProperties; - hr = analyzer->GetScriptProperties (runHead->mScript, &scriptProperties); - if (FAILED (hr)) - { - FAIL ("Analyzer failed to get script properties."); - return false; - } - uint32_t justificationCharacter = scriptProperties.justificationCharacter; + if (FAILED (hr)) + { + FAIL("Analyzer failed to get justified glyph advances."); + return false; + } - // if a script justificationCharacter is not space, it can have GetJustifiedGlyphs - if (justificationCharacter != 32) - { -retry_getjustifiedglyphs: - uint16_t* modifiedClusterMap = (uint16_t*) malloc (maxGlyphCount * sizeof (uint16_t)); - uint16_t* modifiedGlyphIndices = (uint16_t*) malloc (maxGlyphCount * sizeof (uint16_t)); - float* modifiedGlyphAdvances = (float*) malloc (maxGlyphCount * sizeof (float)); - DWRITE_GLYPH_OFFSET* modifiedGlyphOffsets = (DWRITE_GLYPH_OFFSET*) - malloc (maxGlyphCount * sizeof (DWRITE_GLYPH_OFFSET)); - uint32_t actualGlyphsCount; - hr = analyzer->GetJustifiedGlyphs (fontFace, fontEmSize, runHead->mScript, + DWRITE_SCRIPT_PROPERTIES scriptProperties; + hr = analyzer1->GetScriptProperties (runHead->mScript, &scriptProperties); + if (FAILED (hr)) + { + FAIL("Analyzer failed to get script properties."); + return false; + } + uint32_t justificationCharacter = scriptProperties.justificationCharacter; + + // if a script justificationCharacter is not space, it can have GetJustifiedGlyphs + if (justificationCharacter != 32) + { + uint16_t* modifiedClusterMap = (uint16_t*) malloc (textLength * sizeof (uint16_t)); + retry_getjustifiedglyphs: + uint16_t* modifiedGlyphIndices = (uint16_t*) malloc (maxGlyphCount * sizeof (uint16_t)); + float* modifiedGlyphAdvances = (float*) malloc (maxGlyphCount * sizeof (float)); + DWRITE_GLYPH_OFFSET* modifiedGlyphOffsets = (DWRITE_GLYPH_OFFSET*) + malloc (maxGlyphCount * sizeof (DWRITE_GLYPH_OFFSET)); + uint32_t actualGlyphsCount; + hr = analyzer1->GetJustifiedGlyphs (fontFace, fontEmSize, runHead->mScript, textLength, glyphCount, maxGlyphCount, clusterMap, glyphIndices, glyphAdvances, justifiedGlyphAdvances, justifiedGlyphOffsets, glyphProperties, &actualGlyphsCount, modifiedClusterMap, modifiedGlyphIndices, modifiedGlyphAdvances, modifiedGlyphOffsets); - if (hr == HRESULT_FROM_WIN32 (ERROR_INSUFFICIENT_BUFFER)) - { - maxGlyphCount = actualGlyphsCount; - free (modifiedClusterMap); - free (modifiedGlyphIndices); - free (modifiedGlyphAdvances); - free (modifiedGlyphOffsets); + if (hr == HRESULT_FROM_WIN32 (ERROR_INSUFFICIENT_BUFFER)) + { + maxGlyphCount = actualGlyphsCount; + free (modifiedGlyphIndices); + free (modifiedGlyphAdvances); + free (modifiedGlyphOffsets); - maxGlyphCount = actualGlyphsCount; + maxGlyphCount = actualGlyphsCount; - goto retry_getjustifiedglyphs; + goto retry_getjustifiedglyphs; + } + if (FAILED (hr)) + { + FAIL ("Analyzer failed to get justified glyphs."); + return false; + } + + free (clusterMap); + free (glyphIndices); + free (glyphAdvances); + free (glyphOffsets); + + glyphCount = actualGlyphsCount; + clusterMap = modifiedClusterMap; + glyphIndices = modifiedGlyphIndices; + glyphAdvances = modifiedGlyphAdvances; + glyphOffsets = modifiedGlyphOffsets; + + free (justifiedGlyphAdvances); + free (justifiedGlyphOffsets); } - if (FAILED (hr)) + else { - FAIL ("Analyzer failed to get justified glyphs."); - return false; + free (glyphAdvances); + free (glyphOffsets); + + glyphAdvances = justifiedGlyphAdvances; + glyphOffsets = justifiedGlyphOffsets; } - free (clusterMap); - free (glyphIndices); - free (glyphAdvances); - free (glyphOffsets); + free (justificationOpportunities); - glyphCount = actualGlyphsCount; - clusterMap = modifiedClusterMap; - glyphIndices = modifiedGlyphIndices; - glyphAdvances = modifiedGlyphAdvances; - glyphOffsets = modifiedGlyphOffsets; - - free(justifiedGlyphAdvances); - free(justifiedGlyphOffsets); } - else - { - free(glyphAdvances); - free(glyphOffsets); - - glyphAdvances = justifiedGlyphAdvances; - glyphOffsets = justifiedGlyphOffsets; - } - - free(justificationOpportunities); - -#endif /* Ok, we've got everything we need, now compose output buffer, * very, *very*, carefully! */ @@ -968,3 +896,36 @@ retry_getjustifiedglyphs: /* Wow, done! */ return true; } + +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) +{ + return _hb_directwrite_shape_full(shape_plan, font, buffer, + features, num_features, 0); +} + +/* + * Public [experimental] API + */ + +hb_bool_t +hb_directwrite_shape_experimental_width(hb_font_t *font, + hb_buffer_t *buffer, + const hb_feature_t *features, + unsigned int num_features, + float width) +{ + static char *shapers = "directwrite"; + hb_shape_plan_t *shape_plan = hb_shape_plan_create_cached (font->face, + &buffer->props, features, num_features, &shapers); + hb_bool_t res = _hb_directwrite_shape_full (shape_plan, font, buffer, + features, num_features, width); + + buffer->unsafe_to_break_all (); + + return res; +} diff --git a/src/hb-directwrite.h b/src/hb-directwrite.h index adf33dfe9..e743af214 100644 --- a/src/hb-directwrite.h +++ b/src/hb-directwrite.h @@ -29,6 +29,10 @@ HB_BEGIN_DECLS +HB_EXTERN hb_bool_t +hb_directwrite_shape_experimental_width(hb_font_t *font, hb_buffer_t *buffer, + const hb_feature_t *features, unsigned int num_features, float width); + HB_END_DECLS -#endif /* HB_UNISCRIBE_H */ +#endif /* HB_DIRECTWRITE_H */ diff --git a/src/hb-dsalgs.hh b/src/hb-dsalgs.hh new file mode 100644 index 000000000..e41384754 --- /dev/null +++ b/src/hb-dsalgs.hh @@ -0,0 +1,161 @@ +/* + * 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_DSALGS_HH +#define HB_DSALGS_HH + +#include "hb-private.hh" + + +static inline void * +hb_bsearch_r (const void *key, const void *base, + size_t nmemb, size_t size, + int (*compar)(const void *_key, const void *_item, void *_arg), + void *arg) +{ + int min = 0, max = (int) nmemb - 1; + while (min <= max) + { + int mid = (min + max) / 2; + const void *p = (const void *) (((const char *) base) + (mid * size)); + int c = compar (key, p, arg); + if (c < 0) + max = mid - 1; + else if (c > 0) + min = mid + 1; + else + return (void *) p; + } + return NULL; +} + + + +/* From https://github.com/noporpoise/sort_r */ + +/* Isaac Turner 29 April 2014 Public Domain */ + +/* + +hb_sort_r 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 is a pointer to be passed to the comparison function + +void hb_sort_r(void *base, size_t nel, size_t width, + int (*compar)(const void *_a, const void *_b, void *_arg), + void *arg); +*/ + + +/* swap a, b iff a>b */ +/* __restrict is same as restrict but better support on old machines */ +static int sort_r_cmpswap(char *__restrict a, char *__restrict b, size_t w, + int (*compar)(const void *_a, const void *_b, + void *_arg), + void *arg) +{ + char tmp, *end = a+w; + if(compar(a, b, arg) > 0) { + for(; a < end; a++, b++) { tmp = *a; *a = *b; *b = tmp; } + return 1; + } + return 0; +} + +/* Note: quicksort is not stable, equivalent values may be swapped */ +static inline void sort_r_simple(void *base, size_t nel, size_t w, + int (*compar)(const void *_a, const void *_b, + void *_arg), + void *arg) +{ + char *b = (char *)base, *end = b + nel*w; + if(nel < 7) { + /* Insertion sort for arbitrarily small inputs */ + char *pi, *pj; + for(pi = b+w; pi < end; pi += w) { + for(pj = pi; pj > b && sort_r_cmpswap(pj-w,pj,w,compar,arg); pj -= w) {} + } + } + else + { + /* nel > 6; Quicksort */ + + /* Use median of first, middle and last items as pivot */ + char *x, *y, *xend, ch; + char *pl, *pr; + char *last = b+w*(nel-1), *tmp; + char *l[3]; + l[0] = b; + l[1] = b+w*(nel/2); + l[2] = last; + + if(compar(l[0],l[1],arg) > 0) { tmp=l[0]; l[0]=l[1]; l[1]=tmp; } + if(compar(l[1],l[2],arg) > 0) { + tmp=l[1]; l[1]=l[2]; l[2]=tmp; /* swap(l[1],l[2]) */ + if(compar(l[0],l[1],arg) > 0) { tmp=l[0]; l[0]=l[1]; l[1]=tmp; } + } + + /* swap l[id], l[2] to put pivot as last element */ + for(x = l[1], y = last, xend = x+w; x - /* * hb_face_t @@ -49,9 +43,9 @@ const hb_face_t _hb_face_nil = { true, /* immutable */ - NULL, /* reference_table_func */ - NULL, /* user_data */ - NULL, /* destroy */ + nullptr, /* reference_table_func */ + nullptr, /* user_data */ + nullptr, /* destroy */ 0, /* index */ 1000, /* upem */ @@ -63,7 +57,7 @@ const hb_face_t _hb_face_nil = { #undef HB_SHAPER_IMPLEMENT }, - NULL, /* shape_plans */ + nullptr, /* shape_plans */ }; @@ -115,7 +109,7 @@ _hb_face_for_data_closure_create (hb_blob_t *blob, unsigned int index) closure = (hb_face_for_data_closure_t *) calloc (1, sizeof (hb_face_for_data_closure_t)); if (unlikely (!closure)) - return NULL; + return nullptr; closure->blob = blob; closure->index = index; @@ -124,8 +118,10 @@ _hb_face_for_data_closure_create (hb_blob_t *blob, unsigned int index) } static void -_hb_face_for_data_closure_destroy (hb_face_for_data_closure_t *closure) +_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); free (closure); } @@ -168,16 +164,16 @@ hb_face_create (hb_blob_t *blob, if (unlikely (!blob)) blob = hb_blob_get_empty (); - hb_face_for_data_closure_t *closure = _hb_face_for_data_closure_create (OT::Sanitizer::sanitize (hb_blob_reference (blob)), index); + hb_face_for_data_closure_t *closure = _hb_face_for_data_closure_create (OT::Sanitizer().sanitize (hb_blob_reference (blob)), index); if (unlikely (!closure)) return hb_face_get_empty (); face = hb_face_create_for_tables (_hb_face_for_data_reference_table, closure, - (hb_destroy_func_t) _hb_face_for_data_closure_destroy); + _hb_face_for_data_closure_destroy); - hb_face_set_index (face, index); + face->index = index; return face; } @@ -428,7 +424,7 @@ hb_face_get_upem (hb_face_t *face) void hb_face_t::load_upem (void) const { - hb_blob_t *head_blob = OT::Sanitizer::sanitize (reference_table (HB_OT_TAG_head)); + hb_blob_t *head_blob = OT::Sanitizer().sanitize (reference_table (HB_OT_TAG_head)); const OT::head *head_table = OT::Sanitizer::lock_instance (head_blob); upem = head_table->get_upem (); hb_blob_destroy (head_blob); @@ -472,10 +468,39 @@ hb_face_get_glyph_count (hb_face_t *face) void hb_face_t::load_num_glyphs (void) const { - hb_blob_t *maxp_blob = OT::Sanitizer::sanitize (reference_table (HB_OT_TAG_maxp)); + hb_blob_t *maxp_blob = OT::Sanitizer().sanitize (reference_table (HB_OT_TAG_maxp)); const OT::maxp *maxp_table = OT::Sanitizer::lock_instance (maxp_blob); num_glyphs = maxp_table->get_num_glyphs (); hb_blob_destroy (maxp_blob); } +/** + * hb_face_get_table_tags: + * @face: a face. + * + * Retrieves table tags for a face, if possible. + * + * Return value: total number of tables, or 0 if not possible to list. + * + * Since: 1.6.0 + **/ +unsigned int +hb_face_get_table_tags (hb_face_t *face, + unsigned int start_offset, + unsigned int *table_count, /* IN/OUT */ + hb_tag_t *table_tags /* OUT */) +{ + if (face->destroy != _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 = *OT::Sanitizer::lock_instance (data->blob); + const OT::OpenTypeFontFace &ot_face = ot_file.get_face (data->index); + + return ot_face.get_table_tags (start_offset, table_count, table_tags); +} diff --git a/src/hb-face.h b/src/hb-face.h index 91237b708..0ce8d0462 100644 --- a/src/hb-face.h +++ b/src/hb-face.h @@ -71,7 +71,6 @@ hb_face_set_user_data (hb_face_t *face, hb_destroy_func_t destroy, hb_bool_t replace); - HB_EXTERN void * hb_face_get_user_data (hb_face_t *face, hb_user_data_key_t *key); @@ -111,6 +110,11 @@ hb_face_set_glyph_count (hb_face_t *face, HB_EXTERN unsigned int hb_face_get_glyph_count (hb_face_t *face); +HB_EXTERN unsigned int +hb_face_get_table_tags (hb_face_t *face, + unsigned int start_offset, + unsigned int *table_count, /* IN/OUT */ + hb_tag_t *table_tags /* OUT */); HB_END_DECLS diff --git a/src/hb-fallback-shape.cc b/src/hb-fallback-shape.cc index e2ad24001..3f09c3f53 100644 --- a/src/hb-fallback-shape.cc +++ b/src/hb-fallback-shape.cc @@ -28,6 +28,10 @@ #include "hb-shaper-impl-private.hh" +HB_SHAPER_DATA_ENSURE_DEFINE(fallback, face) +HB_SHAPER_DATA_ENSURE_DEFINE(fallback, font) + + /* * shaper face data */ @@ -73,7 +77,9 @@ struct hb_fallback_shaper_shape_plan_data_t {}; hb_fallback_shaper_shape_plan_data_t * _hb_fallback_shaper_shape_plan_data_create (hb_shape_plan_t *shape_plan HB_UNUSED, const hb_feature_t *user_features HB_UNUSED, - unsigned int num_user_features HB_UNUSED) + unsigned int num_user_features HB_UNUSED, + const int *coords HB_UNUSED, + unsigned int num_coords HB_UNUSED) { return (hb_fallback_shaper_shape_plan_data_t *) HB_SHAPER_DATA_SUCCEEDED; } @@ -123,7 +129,7 @@ _hb_fallback_shape (hb_shape_plan_t *shape_plan HB_UNUSED, pos[i].y_advance = 0; continue; } - font->get_nominal_glyph (info[i].codepoint, &info[i].codepoint); + (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, @@ -137,5 +143,7 @@ _hb_fallback_shape (hb_shape_plan_t *shape_plan HB_UNUSED, if (HB_DIRECTION_IS_BACKWARD (direction)) hb_buffer_reverse (buffer); + buffer->safe_to_break_all (); + return true; } diff --git a/src/hb-font-private.hh b/src/hb-font-private.hh index 0b7557794..992152f17 100644 --- a/src/hb-font-private.hh +++ b/src/hb-font-private.hh @@ -108,6 +108,12 @@ struct hb_font_t { unsigned int x_ppem; unsigned int y_ppem; + float ptem; + + /* Font variation coordinates. */ + unsigned int num_coords; + int *coords; + hb_font_funcs_t *klass; void *user_data; hb_destroy_func_t destroy; @@ -116,8 +122,16 @@ struct hb_font_t { /* Convert from font-space to user-space */ - inline hb_position_t em_scale_x (int16_t v) { return em_scale (v, this->x_scale); } - inline hb_position_t em_scale_y (int16_t v) { return em_scale (v, this->y_scale); } + inline int dir_scale (hb_direction_t direction) + { return HB_DIRECTION_IS_VERTICAL(direction) ? y_scale : x_scale; } + inline hb_position_t em_scale_x (int16_t v) { return em_scale (v, x_scale); } + inline hb_position_t em_scale_y (int16_t v) { return em_scale (v, y_scale); } + inline hb_position_t em_scalef_x (float v) { return em_scalef (v, this->x_scale); } + inline hb_position_t em_scalef_y (float v) { return em_scalef (v, this->y_scale); } + inline float em_fscale_x (int16_t v) { return em_fscale (v, x_scale); } + inline float em_fscale_y (int16_t v) { return em_fscale (v, y_scale); } + inline hb_position_t em_scale_dir (int16_t v, hb_direction_t direction) + { return em_scale (v, dir_scale (direction)); } /* Convert from parent-font user-space to our user-space */ inline hb_position_t parent_scale_x_distance (hb_position_t v) { @@ -292,24 +306,32 @@ struct hb_font_t { /* A bit higher-level, and with fallback */ + inline 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; + } + } + inline 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; + } + } + inline void get_extents_for_direction (hb_direction_t direction, hb_font_extents_t *extents) { - if (likely (HB_DIRECTION_IS_HORIZONTAL (direction))) { - if (!get_font_h_extents (extents)) - { - extents->ascender = y_scale * .8; - extents->descender = y_scale - extents->ascender; - extents->line_gap = 0; - } - } else { - if (!get_font_v_extents (extents)) - { - extents->ascender = x_scale / 2; - extents->descender = x_scale - extents->ascender; - extents->line_gap = 0; - } - } + if (likely (HB_DIRECTION_IS_HORIZONTAL (direction))) + get_h_extents_with_fallback (extents); + else + get_v_extents_with_fallback (extents); } inline void get_glyph_advance_for_direction (hb_codepoint_t glyph, @@ -325,14 +347,38 @@ struct hb_font_t { } } - /* Internal only */ inline 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; - /* TODO use font_extents.ascender */ - *y = y_scale; + /* TODO cache this somehow?! */ + hb_font_extents_t extents; + get_h_extents_with_fallback (&extents); + *y = extents.ascender; + } + + inline 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; + } + } + inline 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; + } } inline void get_glyph_origin_for_direction (hb_codepoint_t glyph, @@ -340,25 +386,9 @@ struct hb_font_t { hb_position_t *x, hb_position_t *y) { if (likely (HB_DIRECTION_IS_HORIZONTAL (direction))) - { - 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; - } - } + get_glyph_h_origin_with_fallback (glyph, x, y); else - { - 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; - } - } + get_glyph_v_origin_with_fallback (glyph, x, y); } inline void add_glyph_h_origin (hb_codepoint_t glyph, @@ -366,7 +396,7 @@ struct hb_font_t { { hb_position_t origin_x, origin_y; - get_glyph_h_origin (glyph, &origin_x, &origin_y); + get_glyph_h_origin_with_fallback (glyph, &origin_x, &origin_y); *x += origin_x; *y += origin_y; @@ -376,7 +406,7 @@ struct hb_font_t { { hb_position_t origin_x, origin_y; - get_glyph_v_origin (glyph, &origin_x, &origin_y); + get_glyph_v_origin_with_fallback (glyph, &origin_x, &origin_y); *x += origin_x; *y += origin_y; @@ -398,7 +428,7 @@ struct hb_font_t { { hb_position_t origin_x, origin_y; - get_glyph_h_origin (glyph, &origin_x, &origin_y); + get_glyph_h_origin_with_fallback (glyph, &origin_x, &origin_y); *x -= origin_x; *y -= origin_y; @@ -408,7 +438,7 @@ struct hb_font_t { { hb_position_t origin_x, origin_y; - get_glyph_v_origin (glyph, &origin_x, &origin_y); + get_glyph_v_origin_with_fallback (glyph, &origin_x, &origin_y); *x -= origin_x; *y -= origin_y; @@ -504,7 +534,6 @@ struct hb_font_t { return false; } - private: inline hb_position_t em_scale (int16_t v, int scale) { int upem = face->get_upem (); @@ -512,6 +541,14 @@ struct hb_font_t { scaled += scaled >= 0 ? upem/2 : -upem/2; /* Round. */ return (hb_position_t) (scaled / upem); } + inline hb_position_t em_scalef (float v, int scale) + { + return (hb_position_t) round (v * scale / face->get_upem ()); + } + inline float em_fscale (int16_t v, int scale) + { + return (float) v * scale / face->get_upem (); + } }; #define HB_SHAPER_DATA_CREATE_FUNC_EXTRA_ARGS diff --git a/src/hb-font.cc b/src/hb-font.cc index 60554b9c7..f3534b686 100644 --- a/src/hb-font.cc +++ b/src/hb-font.cc @@ -28,16 +28,7 @@ #include "hb-private.hh" -#include "hb-ot-layout-private.hh" - #include "hb-font-private.hh" -#include "hb-open-file-private.hh" -#include "hb-ot-head-table.hh" -#include "hb-ot-maxp-table.hh" - -#include "hb-cache-private.hh" - -#include /* @@ -45,7 +36,7 @@ */ static hb_bool_t -hb_font_get_font_h_extents_nil (hb_font_t *font, +hb_font_get_font_h_extents_nil (hb_font_t *font HB_UNUSED, void *font_data HB_UNUSED, hb_font_extents_t *metrics, void *user_data HB_UNUSED) @@ -69,7 +60,7 @@ hb_font_get_font_h_extents_parent (hb_font_t *font, } static hb_bool_t -hb_font_get_font_v_extents_nil (hb_font_t *font, +hb_font_get_font_v_extents_nil (hb_font_t *font HB_UNUSED, void *font_data HB_UNUSED, hb_font_extents_t *metrics, void *user_data HB_UNUSED) @@ -356,12 +347,12 @@ static const hb_font_funcs_t _hb_font_funcs_nil = { true, /* immutable */ { -#define HB_FONT_FUNC_IMPLEMENT(name) NULL, +#define HB_FONT_FUNC_IMPLEMENT(name) nullptr, HB_FONT_FUNCS_IMPLEMENT_CALLBACKS #undef HB_FONT_FUNC_IMPLEMENT }, { -#define HB_FONT_FUNC_IMPLEMENT(name) NULL, +#define HB_FONT_FUNC_IMPLEMENT(name) nullptr, HB_FONT_FUNCS_IMPLEMENT_CALLBACKS #undef HB_FONT_FUNC_IMPLEMENT }, @@ -379,12 +370,12 @@ static const hb_font_funcs_t _hb_font_funcs_parent = { true, /* immutable */ { -#define HB_FONT_FUNC_IMPLEMENT(name) NULL, +#define HB_FONT_FUNC_IMPLEMENT(name) nullptr, HB_FONT_FUNCS_IMPLEMENT_CALLBACKS #undef HB_FONT_FUNC_IMPLEMENT }, { -#define HB_FONT_FUNC_IMPLEMENT(name) NULL, +#define HB_FONT_FUNC_IMPLEMENT(name) nullptr, HB_FONT_FUNCS_IMPLEMENT_CALLBACKS #undef HB_FONT_FUNC_IMPLEMENT }, @@ -572,8 +563,8 @@ hb_font_funcs_set_##name##_func (hb_font_funcs_t *ffuncs, \ ffuncs->destroy.name = destroy; \ } else { \ ffuncs->get.f.name = hb_font_get_##name##_parent; \ - ffuncs->user_data.name = NULL; \ - ffuncs->destroy.name = NULL; \ + ffuncs->user_data.name = nullptr; \ + ffuncs->destroy.name = nullptr; \ } \ } @@ -1166,6 +1157,20 @@ hb_font_create_sub_font (hb_font_t *parent) font->y_scale = parent->y_scale; font->x_ppem = parent->x_ppem; font->y_ppem = parent->y_ppem; + font->ptem = parent->ptem; + + font->num_coords = parent->num_coords; + if (!font->num_coords) + font->coords = nullptr; + else + { + unsigned int size = parent->num_coords * sizeof (parent->coords[0]); + font->coords = (int *) malloc (size); + if (unlikely (!font->coords)) + font->num_coords = 0; + else + memcpy (font->coords, parent->coords, size); + } return font; } @@ -1187,7 +1192,7 @@ hb_font_get_empty (void) true, /* immutable */ - NULL, /* parent */ + nullptr, /* parent */ const_cast (&_hb_face_nil), 1000, /* x_scale */ @@ -1195,10 +1200,14 @@ hb_font_get_empty (void) 0, /* x_ppem */ 0, /* y_ppem */ + 0, /* ptem */ + + 0, /* num_coords */ + nullptr, /* coords */ const_cast (&_hb_font_funcs_nil), /* klass */ - NULL, /* user_data */ - NULL, /* destroy */ + nullptr, /* user_data */ + nullptr, /* destroy */ { #define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_INVALID, @@ -1250,6 +1259,8 @@ hb_font_destroy (hb_font_t *font) hb_face_destroy (font->face); hb_font_funcs_destroy (font->klass); + free (font->coords); + free (font); } @@ -1373,6 +1384,32 @@ hb_font_get_parent (hb_font_t *font) return font->parent; } +/** + * hb_font_set_face: + * @font: a font. + * @face: new face. + * + * Sets font-face of @font. + * + * Since: 1.4.3 + **/ +void +hb_font_set_face (hb_font_t *font, + hb_face_t *face) +{ + if (font->immutable) + return; + + if (unlikely (!face)) + face = hb_face_get_empty (); + + hb_face_t *old = font->face; + + font->face = hb_face_reference (face); + + hb_face_destroy (old); +} + /** * hb_font_get_face: * @font: a font. @@ -1538,6 +1575,148 @@ hb_font_get_ppem (hb_font_t *font, if (y_ppem) *y_ppem = font->y_ppem; } +/** + * hb_font_set_ptem: + * @font: a font. + * @ptem: + * + * Sets "point size" of the font. + * + * Since: 1.6.0 + **/ +void +hb_font_set_ptem (hb_font_t *font, float ptem) +{ + if (font->immutable) + return; + + font->ptem = ptem; +} + +/** + * hb_font_get_ptem: + * @font: a font. + * + * Gets the "point size" of the font. A value of 0 means unset. + * + * Return value: Point size. + * + * Since: 0.9.2 + **/ +float +hb_font_get_ptem (hb_font_t *font) +{ + return font->ptem; +} + +/* + * Variations + */ + +static void +_hb_font_adopt_var_coords_normalized (hb_font_t *font, + int *coords, /* 2.14 normalized */ + unsigned int coords_length) +{ + free (font->coords); + + font->coords = coords; + font->num_coords = coords_length; +} + +/** + * hb_font_set_variations: + * + * Since: 1.4.2 + */ +void +hb_font_set_variations (hb_font_t *font, + const hb_variation_t *variations, + unsigned int variations_length) +{ + if (font->immutable) + return; + + if (!variations_length) + { + hb_font_set_var_coords_normalized (font, nullptr, 0); + return; + } + + unsigned int coords_length = hb_ot_var_get_axis_count (font->face); + + int *normalized = coords_length ? (int *) calloc (coords_length, sizeof (int)) : nullptr; + if (unlikely (coords_length && !normalized)) + return; + + hb_ot_var_normalize_variations (font->face, + variations, variations_length, + normalized, coords_length); + _hb_font_adopt_var_coords_normalized (font, normalized, coords_length); +} + +/** + * hb_font_set_var_coords_design: + * + * Since: 1.4.2 + */ +void +hb_font_set_var_coords_design (hb_font_t *font, + const float *coords, + unsigned int coords_length) +{ + if (font->immutable) + return; + + int *normalized = coords_length ? (int *) calloc (coords_length, sizeof (int)) : nullptr; + if (unlikely (coords_length && !normalized)) + return; + + hb_ot_var_normalize_coords (font->face, coords_length, coords, normalized); + _hb_font_adopt_var_coords_normalized (font, normalized, coords_length); +} + +/** + * hb_font_set_var_coords_normalized: + * + * 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 (font->immutable) + return; + + int *copy = coords_length ? (int *) calloc (coords_length, sizeof (coords[0])) : nullptr; + if (unlikely (coords_length && !copy)) + return; + + if (coords_length) + memcpy (copy, coords, coords_length * sizeof (coords[0])); + + _hb_font_adopt_var_coords_normalized (font, copy, coords_length); +} + +/** + * hb_font_get_var_coords_normalized: + * + * Return value is valid as long as variation coordinates of the font + * are not modified. + * + * 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; +} + #ifndef HB_DISABLE_DEPRECATED @@ -1570,7 +1749,7 @@ trampoline_create (FuncType func, trampoline_t *trampoline = (trampoline_t *) calloc (1, sizeof (trampoline_t)); if (unlikely (!trampoline)) - return NULL; + return nullptr; trampoline->closure.user_data = user_data; trampoline->closure.destroy = destroy; diff --git a/src/hb-font.h b/src/hb-font.h index 2b6ab5088..c95b61d2d 100644 --- a/src/hb-font.h +++ b/src/hb-font.h @@ -456,7 +456,7 @@ hb_font_get_glyph_from_name (hb_font_t *font, /* high-level funcs, with fallback */ /* Calls either hb_font_get_nominal_glyph() if variation_selector is 0, - * otherwise callse hb_font_get_variation_glyph(). */ + * 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, @@ -563,6 +563,10 @@ hb_font_set_parent (hb_font_t *font, 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); @@ -603,6 +607,34 @@ 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_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 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_END_DECLS diff --git a/src/hb-ft.cc b/src/hb-ft.cc index 2cad8c264..fc4b11220 100644 --- a/src/hb-ft.cc +++ b/src/hb-ft.cc @@ -28,21 +28,17 @@ */ #include "hb-private.hh" +#include "hb-debug.hh" #include "hb-ft.h" #include "hb-font-private.hh" #include FT_ADVANCES_H +#include FT_MULTIPLE_MASTERS_H #include FT_TRUETYPE_TABLES_H - -#ifndef HB_DEBUG_FT -#define HB_DEBUG_FT (HB_DEBUG+0) -#endif - - /* TODO: * * In general, this file does a fine job of what it's supposed to do. @@ -62,7 +58,7 @@ * * - In the future, we should add constructors to create fonts in font space? * - * - FT_Load_Glyph() is exteremely costly. Do something about it? + * - FT_Load_Glyph() is extremely costly. Do something about it? */ @@ -70,18 +66,20 @@ struct hb_ft_font_t { FT_Face ft_face; int load_flags; + bool symbol; /* Whether selected cmap is symbol cmap. */ bool unref; /* Whether to destroy ft_face when done. */ }; static hb_ft_font_t * -_hb_ft_font_create (FT_Face ft_face, bool unref) +_hb_ft_font_create (FT_Face ft_face, bool symbol, bool unref) { hb_ft_font_t *ft_font = (hb_ft_font_t *) calloc (1, sizeof (hb_ft_font_t)); if (unlikely (!ft_font)) - return NULL; + return nullptr; 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; @@ -90,10 +88,18 @@ _hb_ft_font_create (FT_Face ft_face, bool unref) } static void -_hb_ft_font_destroy (hb_ft_font_t *ft_font) +_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; + if (ft_font->unref) - FT_Done_Face (ft_font->ft_face); + _hb_ft_face_destroy (ft_font->ft_face); free (ft_font); } @@ -113,7 +119,7 @@ hb_ft_font_set_load_flags (hb_font_t *font, int load_flags) if (font->immutable) return; - if (font->destroy != (hb_destroy_func_t) _hb_ft_font_destroy) + if (font->destroy != _hb_ft_font_destroy) return; hb_ft_font_t *ft_font = (hb_ft_font_t *) font->user_data; @@ -133,7 +139,7 @@ hb_ft_font_set_load_flags (hb_font_t *font, int load_flags) int hb_ft_font_get_load_flags (hb_font_t *font) { - if (font->destroy != (hb_destroy_func_t) _hb_ft_font_destroy) + if (font->destroy != _hb_ft_font_destroy) return 0; const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font->user_data; @@ -144,8 +150,8 @@ hb_ft_font_get_load_flags (hb_font_t *font) FT_Face hb_ft_font_get_face (hb_font_t *font) { - if (font->destroy != (hb_destroy_func_t) _hb_ft_font_destroy) - return NULL; + if (font->destroy != _hb_ft_font_destroy) + return nullptr; const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font->user_data; @@ -165,7 +171,21 @@ hb_ft_get_nominal_glyph (hb_font_t *font HB_UNUSED, unsigned int g = FT_Get_Char_Index (ft_font->ft_face, unicode); if (unlikely (!g)) - return false; + { + if (unlikely (ft_font->symbol) && 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: + * http://www.microsoft.com/typography/otspec/recom.htm + * under "Non-Standard (Symbol) Fonts". */ + g = FT_Get_Char_Index (ft_font->ft_face, 0xF000u + unicode); + if (!g) + return false; + } + else + return false; + } *glyph = g; return true; @@ -397,7 +417,7 @@ hb_ft_get_font_h_extents (hb_font_t *font HB_UNUSED, return true; } -static hb_font_funcs_t *static_ft_funcs = NULL; +static hb_font_funcs_t *static_ft_funcs = nullptr; #ifdef HB_USE_ATEXIT static @@ -417,24 +437,24 @@ retry: { funcs = hb_font_funcs_create (); - hb_font_funcs_set_font_h_extents_func (funcs, hb_ft_get_font_h_extents, NULL, NULL); - //hb_font_funcs_set_font_v_extents_func (funcs, hb_ft_get_font_v_extents, NULL, NULL); - hb_font_funcs_set_nominal_glyph_func (funcs, hb_ft_get_nominal_glyph, NULL, NULL); - hb_font_funcs_set_variation_glyph_func (funcs, hb_ft_get_variation_glyph, NULL, NULL); - hb_font_funcs_set_glyph_h_advance_func (funcs, hb_ft_get_glyph_h_advance, NULL, NULL); - hb_font_funcs_set_glyph_v_advance_func (funcs, hb_ft_get_glyph_v_advance, NULL, NULL); - //hb_font_funcs_set_glyph_h_origin_func (funcs, hb_ft_get_glyph_h_origin, NULL, NULL); - hb_font_funcs_set_glyph_v_origin_func (funcs, hb_ft_get_glyph_v_origin, NULL, NULL); - hb_font_funcs_set_glyph_h_kerning_func (funcs, hb_ft_get_glyph_h_kerning, NULL, NULL); - //hb_font_funcs_set_glyph_v_kerning_func (funcs, hb_ft_get_glyph_v_kerning, NULL, NULL); - hb_font_funcs_set_glyph_extents_func (funcs, hb_ft_get_glyph_extents, NULL, NULL); - hb_font_funcs_set_glyph_contour_point_func (funcs, hb_ft_get_glyph_contour_point, NULL, NULL); - hb_font_funcs_set_glyph_name_func (funcs, hb_ft_get_glyph_name, NULL, NULL); - hb_font_funcs_set_glyph_from_name_func (funcs, hb_ft_get_glyph_from_name, NULL, NULL); + hb_font_funcs_set_font_h_extents_func (funcs, hb_ft_get_font_h_extents, nullptr, nullptr); + //hb_font_funcs_set_font_v_extents_func (funcs, hb_ft_get_font_v_extents, nullptr, nullptr); + hb_font_funcs_set_nominal_glyph_func (funcs, hb_ft_get_nominal_glyph, nullptr, nullptr); + hb_font_funcs_set_variation_glyph_func (funcs, hb_ft_get_variation_glyph, nullptr, nullptr); + hb_font_funcs_set_glyph_h_advance_func (funcs, hb_ft_get_glyph_h_advance, nullptr, nullptr); + hb_font_funcs_set_glyph_v_advance_func (funcs, hb_ft_get_glyph_v_advance, nullptr, nullptr); + //hb_font_funcs_set_glyph_h_origin_func (funcs, hb_ft_get_glyph_h_origin, nullptr, nullptr); + hb_font_funcs_set_glyph_v_origin_func (funcs, hb_ft_get_glyph_v_origin, nullptr, nullptr); + hb_font_funcs_set_glyph_h_kerning_func (funcs, hb_ft_get_glyph_h_kerning, nullptr, nullptr); + //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); hb_font_funcs_make_immutable (funcs); - if (!hb_atomic_ptr_cmpexch (&static_ft_funcs, NULL, funcs)) { + if (!hb_atomic_ptr_cmpexch (&static_ft_funcs, nullptr, funcs)) { hb_font_funcs_destroy (funcs); goto retry; } @@ -444,10 +464,12 @@ retry: #endif }; + bool symbol = ft_face->charmap && ft_face->charmap->encoding == FT_ENCODING_MS_SYMBOL; + hb_font_set_funcs (font, funcs, - _hb_ft_font_create (ft_face, unref), - (hb_destroy_func_t) _hb_ft_font_destroy); + _hb_ft_font_create (ft_face, symbol, unref), + _hb_ft_font_destroy); } @@ -461,17 +483,17 @@ reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data) /* Note: FreeType like HarfBuzz uses the NONE tag for fetching the entire blob */ - error = FT_Load_Sfnt_Table (ft_face, tag, 0, NULL, &length); + error = FT_Load_Sfnt_Table (ft_face, tag, 0, nullptr, &length); if (error) - return NULL; + return nullptr; buffer = (FT_Byte *) malloc (length); - if (buffer == NULL) - return NULL; + if (!buffer) + return nullptr; error = FT_Load_Sfnt_Table (ft_face, tag, 0, buffer, &length); if (error) - return NULL; + return nullptr; return hb_blob_create ((const char *) buffer, length, HB_MEMORY_MODE_WRITABLE, @@ -494,7 +516,7 @@ hb_ft_face_create (FT_Face ft_face, { hb_face_t *face; - if (ft_face->stream->read == NULL) { + if (!ft_face->stream->read) { hb_blob_t *blob; blob = hb_blob_create ((const char *) ft_face->stream->base, @@ -526,7 +548,7 @@ 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_destroy_func_t) FT_Done_Face); + return hb_ft_face_create (ft_face, _hb_ft_face_destroy); } static void @@ -552,7 +574,7 @@ hb_ft_face_create_cached (FT_Face ft_face) if (ft_face->generic.finalizer) ft_face->generic.finalizer (ft_face); - ft_face->generic.data = hb_ft_face_create (ft_face, NULL); + ft_face->generic.data = hb_ft_face_create (ft_face, nullptr); ft_face->generic.finalizer = (FT_Generic_Finalizer) hb_ft_face_finalize; } @@ -581,16 +603,61 @@ hb_ft_font_create (FT_Face ft_face, 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; +} + +void +hb_ft_font_changed (hb_font_t *font) +{ + if (font->destroy != _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 + (1<<15)) >> 16), - (int) (((uint64_t) ft_face->size->metrics.y_scale * (uint64_t) ft_face->units_per_EM + (1<<15)) >> 16)); + (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 - return font; +#ifdef HAVE_FT_GET_VAR_BLEND_COORDINATES + FT_MM_Var *mm_var = nullptr; + if (!FT_Get_MM_Var (ft_face, &mm_var)) + { + FT_Fixed *ft_coords = (FT_Fixed *) calloc (mm_var->num_axis, sizeof (FT_Fixed)); + int *coords = (int *) 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); + } + } + free (coords); + free (ft_coords); +#ifdef HAVE_FT_DONE_MM_VAR + FT_Done_MM_Var (ft_face->glyph->library, mm_var); +#else + free (mm_var); +#endif + } +#endif } /** @@ -606,7 +673,7 @@ 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_destroy_func_t) FT_Done_Face); + return hb_ft_font_create (ft_face, _hb_ft_face_destroy); } @@ -632,9 +699,9 @@ retry: { /* Not found; allocate one. */ if (FT_Init_FreeType (&library)) - return NULL; + return nullptr; - if (!hb_atomic_ptr_cmpexch (&ft_library, NULL, library)) { + if (!hb_atomic_ptr_cmpexch (&ft_library, nullptr, library)) { FT_Done_FreeType (library); goto retry; } @@ -662,7 +729,7 @@ hb_ft_font_set_funcs (hb_font_t *font) if (unlikely (!blob_length)) DEBUG_MSG (FT, font, "Font face has empty blob"); - FT_Face ft_face = NULL; + FT_Face ft_face = nullptr; FT_Error err = FT_New_Memory_Face (get_ft_library (), (const FT_Byte *) blob_data, blob_length, @@ -675,7 +742,8 @@ hb_ft_font_set_funcs (hb_font_t *font) return; } - FT_Select_Charmap (ft_face, FT_ENCODING_UNICODE); + if (FT_Select_Charmap (ft_face, FT_ENCODING_UNICODE)) + FT_Select_Charmap (ft_face, FT_ENCODING_MS_SYMBOL); FT_Set_Char_Size (ft_face, abs (font->x_scale), abs (font->y_scale), @@ -688,9 +756,25 @@ hb_ft_font_set_funcs (hb_font_t *font) { FT_Matrix matrix = { font->x_scale < 0 ? -1 : +1, 0, 0, font->y_scale < 0 ? -1 : +1}; - FT_Set_Transform (ft_face, &matrix, NULL); + FT_Set_Transform (ft_face, &matrix, nullptr); } +#ifdef HAVE_FT_SET_VAR_BLEND_COORDINATES + unsigned int num_coords; + const int *coords = hb_font_get_var_coords_normalized (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] << 2; + FT_Set_Var_Blend_Coordinates (ft_face, num_coords, ft_coords); + free (ft_coords); + } + } +#endif + ft_face->generic.data = blob; ft_face->generic.finalizer = (FT_Generic_Finalizer) _release_blob; diff --git a/src/hb-ft.h b/src/hb-ft.h index dc8ef8558..94013eeb9 100644 --- a/src/hb-ft.h +++ b/src/hb-ft.h @@ -116,7 +116,13 @@ 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); -/* Makes an hb_font_t use FreeType internally to implement font functions. */ +/* Call when size or variations settings on underlying FT_Face change. */ +HB_EXTERN void +hb_ft_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); diff --git a/src/hb-glib.cc b/src/hb-glib.cc index e20352475..50c30e9c7 100644 --- a/src/hb-glib.cc +++ b/src/hb-glib.cc @@ -364,22 +364,52 @@ hb_glib_unicode_decompose_compatibility (hb_unicode_funcs_t *ufuncs HB_UNUSED, return utf8_decomposed_len; } +static hb_unicode_funcs_t *static_glib_funcs = nullptr; + +#ifdef HB_USE_ATEXIT +static +void free_static_glib_funcs (void) +{ + hb_unicode_funcs_destroy (static_glib_funcs); +} +#endif + hb_unicode_funcs_t * hb_glib_get_unicode_funcs (void) { - static const hb_unicode_funcs_t _hb_glib_unicode_funcs = { - HB_OBJECT_HEADER_STATIC, +retry: + hb_unicode_funcs_t *funcs = (hb_unicode_funcs_t *) hb_atomic_ptr_get (&static_glib_funcs); - NULL, /* parent */ - true, /* immutable */ - { -#define HB_UNICODE_FUNC_IMPLEMENT(name) hb_glib_unicode_##name, + if (unlikely (!funcs)) + { + funcs = hb_unicode_funcs_create (nullptr); + +#define HB_UNICODE_FUNC_IMPLEMENT(name) \ + hb_unicode_funcs_set_##name##_func (funcs, hb_glib_unicode_##name, nullptr, nullptr); HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS #undef HB_UNICODE_FUNC_IMPLEMENT + + hb_unicode_funcs_make_immutable (funcs); + + if (!hb_atomic_ptr_cmpexch (&static_glib_funcs, nullptr, funcs)) { + hb_unicode_funcs_destroy (funcs); + goto retry; } + +#ifdef HB_USE_ATEXIT + atexit (free_static_glib_funcs); /* First person registers atexit() callback. */ +#endif }; - return const_cast (&_hb_glib_unicode_funcs); + return hb_unicode_funcs_reference (funcs); +} + +#if GLIB_CHECK_VERSION(2,31,10) + +static void +_hb_g_bytes_unref (void *data) +{ + g_bytes_unref ((GBytes *) data); } /** @@ -396,5 +426,6 @@ hb_glib_blob_create (GBytes *gbytes) size, HB_MEMORY_MODE_READONLY, g_bytes_ref (gbytes), - (hb_destroy_func_t) g_bytes_unref); + _hb_g_bytes_unref); } +#endif diff --git a/src/hb-glib.h b/src/hb-glib.h index 12c3e3b3a..5f04183ba 100644 --- a/src/hb-glib.h +++ b/src/hb-glib.h @@ -46,9 +46,10 @@ 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 diff --git a/src/hb-gobject-enums.h.tmpl b/src/hb-gobject-enums.h.tmpl index e28510c22..606727cd3 100644 --- a/src/hb-gobject-enums.h.tmpl +++ b/src/hb-gobject-enums.h.tmpl @@ -42,7 +42,8 @@ HB_BEGIN_DECLS /*** END file-header ***/ /*** BEGIN value-header ***/ -HB_EXTERN GType @enum_name@_get_type (void) G_GNUC_CONST; +HB_EXTERN GType +@enum_name@_get_type (void) G_GNUC_CONST; #define @ENUMPREFIX@_TYPE_@ENUMSHORT@ (@enum_name@_get_type ()) /*** END value-header ***/ diff --git a/src/hb-gobject-structs.cc b/src/hb-gobject-structs.cc index 6bd63368b..a96c35804 100644 --- a/src/hb-gobject-structs.cc +++ b/src/hb-gobject-structs.cc @@ -58,7 +58,7 @@ hb_gobject_##name##_get_type (void) \ static hb_##name##_t *_hb_##name##_reference (const hb_##name##_t *l) \ { \ hb_##name##_t *c = (hb_##name##_t *) calloc (1, sizeof (hb_##name##_t)); \ - if (unlikely (!c)) return NULL; \ + if (unlikely (!c)) return nullptr; \ *c = *l; \ return c; \ } \ @@ -78,3 +78,6 @@ 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) diff --git a/src/hb-gobject-structs.h b/src/hb-gobject-structs.h index 0ea3b12cf..302dc9584 100644 --- a/src/hb-gobject-structs.h +++ b/src/hb-gobject-structs.h @@ -41,64 +41,97 @@ HB_BEGIN_DECLS /* Object types */ /** + * hb_gobject_blob_get_type: + * * Since: 0.9.2 **/ -HB_EXTERN GType hb_gobject_blob_get_type (void); +HB_EXTERN GType +hb_gobject_blob_get_type (void); #define HB_GOBJECT_TYPE_BLOB (hb_gobject_blob_get_type ()) /** + * hb_gobject_buffer_get_type: + * * Since: 0.9.2 **/ -HB_EXTERN GType hb_gobject_buffer_get_type (void); +HB_EXTERN GType +hb_gobject_buffer_get_type (void); #define HB_GOBJECT_TYPE_BUFFER (hb_gobject_buffer_get_type ()) /** + * hb_gobject_face_get_type: + * * Since: 0.9.2 **/ -HB_EXTERN GType hb_gobject_face_get_type (void); +HB_EXTERN GType +hb_gobject_face_get_type (void); #define HB_GOBJECT_TYPE_FACE (hb_gobject_face_get_type ()) /** + * hb_gobject_font_get_type: + * * Since: 0.9.2 **/ -HB_EXTERN GType hb_gobject_font_get_type (void); +HB_EXTERN GType +hb_gobject_font_get_type (void); #define HB_GOBJECT_TYPE_FONT (hb_gobject_font_get_type ()) /** + * hb_gobject_font_funcs_get_type: + * * Since: 0.9.2 **/ -HB_EXTERN GType hb_gobject_font_funcs_get_type (void); +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); +HB_EXTERN GType +hb_gobject_set_get_type (void); #define HB_GOBJECT_TYPE_SET (hb_gobject_set_get_type ()) -HB_EXTERN GType hb_gobject_shape_plan_get_type (void); +HB_EXTERN GType +hb_gobject_shape_plan_get_type (void); #define HB_GOBJECT_TYPE_SHAPE_PLAN (hb_gobject_shape_plan_get_type ()) /** + * hb_gobject_unicode_funcs_get_type: + * * Since: 0.9.2 **/ -HB_EXTERN GType hb_gobject_unicode_funcs_get_type (void); +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); +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); +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); +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); +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); +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 diff --git a/src/hb-graphite2.cc b/src/hb-graphite2.cc index c32318d27..46fe13999 100644 --- a/src/hb-graphite2.cc +++ b/src/hb-graphite2.cc @@ -27,7 +27,6 @@ */ #define HB_SHAPER graphite2 -#define hb_graphite2_shaper_font_data_t gr_font #include "hb-shaper-impl-private.hh" #include "hb-graphite2.h" @@ -35,8 +34,8 @@ #include -HB_SHAPER_DATA_ENSURE_DECLARE(graphite2, face) -HB_SHAPER_DATA_ENSURE_DECLARE(graphite2, font) +HB_SHAPER_DATA_ENSURE_DEFINE(graphite2, face) +HB_SHAPER_DATA_ENSURE_DEFINE(graphite2, font) /* @@ -60,7 +59,7 @@ static const void *hb_graphite2_get_table (const void *data, unsigned int tag, s hb_graphite2_shaper_face_data_t *face_data = (hb_graphite2_shaper_face_data_t *) data; hb_graphite2_tablelist_t *tlist = face_data->tlist; - hb_blob_t *blob = NULL; + hb_blob_t *blob = nullptr; for (hb_graphite2_tablelist_t *p = tlist; p; p = p->next) if (p->tag == tag) { @@ -75,13 +74,13 @@ static const void *hb_graphite2_get_table (const void *data, unsigned int tag, s hb_graphite2_tablelist_t *p = (hb_graphite2_tablelist_t *) calloc (1, sizeof (hb_graphite2_tablelist_t)); if (unlikely (!p)) { hb_blob_destroy (blob); - return NULL; + return nullptr; } p->blob = blob; p->tag = tag; /* TODO Not thread-safe, but fairly harmless. - * We can do the double-chcked pointer cmpexch thing here. */ + * We can do the double-checked pointer cmpexch thing here. */ p->next = face_data->tlist; face_data->tlist = p; } @@ -101,20 +100,20 @@ _hb_graphite2_shaper_face_data_create (hb_face_t *face) if (!hb_blob_get_length (silf_blob)) { hb_blob_destroy (silf_blob); - return NULL; + return nullptr; } hb_blob_destroy (silf_blob); hb_graphite2_shaper_face_data_t *data = (hb_graphite2_shaper_face_data_t *) calloc (1, sizeof (hb_graphite2_shaper_face_data_t)); if (unlikely (!data)) - return NULL; + return nullptr; data->face = face; data->grface = gr_make_face (data, &hb_graphite2_get_table, gr_face_preloadAll); if (unlikely (!data->grface)) { free (data); - return NULL; + return nullptr; } return data; @@ -144,7 +143,7 @@ _hb_graphite2_shaper_face_data_destroy (hb_graphite2_shaper_face_data_t *data) gr_face * hb_graphite2_face_get_gr_face (hb_face_t *face) { - if (unlikely (!hb_graphite2_shaper_face_data_ensure (face))) return NULL; + if (unlikely (!hb_graphite2_shaper_face_data_ensure (face))) return nullptr; return HB_SHAPER_DATA_GET (face)->grface; } @@ -153,26 +152,17 @@ hb_graphite2_face_get_gr_face (hb_face_t *face) * shaper font data */ -static float hb_graphite2_get_advance (const void *hb_font, unsigned short gid) -{ - return ((hb_font_t *) hb_font)->get_glyph_h_advance (gid); -} +struct hb_graphite2_shaper_font_data_t {}; hb_graphite2_shaper_font_data_t * -_hb_graphite2_shaper_font_data_create (hb_font_t *font) +_hb_graphite2_shaper_font_data_create (hb_font_t *font HB_UNUSED) { - if (unlikely (!hb_graphite2_shaper_face_data_ensure (font->face))) return NULL; - - hb_face_t *face = font->face; - hb_graphite2_shaper_face_data_t *face_data = HB_SHAPER_DATA_GET (face); - - return gr_make_font_with_advance_fn (font->x_scale, font, &hb_graphite2_get_advance, face_data->grface); + return (hb_graphite2_shaper_font_data_t *) HB_SHAPER_DATA_SUCCEEDED; } void -_hb_graphite2_shaper_font_data_destroy (hb_graphite2_shaper_font_data_t *data) +_hb_graphite2_shaper_font_data_destroy (hb_graphite2_shaper_font_data_t *data HB_UNUSED) { - gr_font_destroy (data); } /* @@ -181,8 +171,7 @@ _hb_graphite2_shaper_font_data_destroy (hb_graphite2_shaper_font_data_t *data) gr_font * hb_graphite2_font_get_gr_font (hb_font_t *font) { - if (unlikely (!hb_graphite2_shaper_font_data_ensure (font))) return NULL; - return HB_SHAPER_DATA_GET (font); + return nullptr; } @@ -195,7 +184,9 @@ struct hb_graphite2_shaper_shape_plan_data_t {}; hb_graphite2_shaper_shape_plan_data_t * _hb_graphite2_shaper_shape_plan_data_create (hb_shape_plan_t *shape_plan HB_UNUSED, const hb_feature_t *user_features HB_UNUSED, - unsigned int num_user_features HB_UNUSED) + unsigned int num_user_features HB_UNUSED, + const int *coords HB_UNUSED, + unsigned int num_coords HB_UNUSED) { return (hb_graphite2_shaper_shape_plan_data_t *) HB_SHAPER_DATA_SUCCEEDED; } @@ -228,10 +219,9 @@ _hb_graphite2_shape (hb_shape_plan_t *shape_plan, { hb_face_t *face = font->face; gr_face *grface = HB_SHAPER_DATA_GET (face)->grface; - gr_font *grfont = HB_SHAPER_DATA_GET (font); const char *lang = hb_language_to_string (hb_buffer_get_language (buffer)); - const char *lang_end = lang ? strchr (lang, '-') : NULL; + 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); @@ -242,7 +232,7 @@ _hb_graphite2_shape (hb_shape_plan_t *shape_plan, gr_fref_set_feature_value (fref, features[i].value, feats); } - gr_segment *seg = NULL; + gr_segment *seg = nullptr; const gr_slot *is; unsigned int ci = 0, ic = 0; float curradvx = 0., curradvy = 0.; @@ -260,7 +250,7 @@ _hb_graphite2_shape (hb_shape_plan_t *shape_plan, hb_tag_t script_tag[2]; hb_ot_tags_from_script (hb_buffer_get_script (buffer), &script_tag[0], &script_tag[1]); - seg = gr_make_seg (grfont, grface, + seg = gr_make_seg (nullptr, grface, script_tag[1] == HB_TAG_NONE ? script_tag[0] : script_tag[1], feats, gr_utf32, chars, buffer->len, @@ -311,12 +301,14 @@ _hb_graphite2_shape (hb_shape_plan_t *shape_plan, hb_codepoint_t *pg = gids; clusters[0].cluster = buffer->info[0].cluster; - float curradv = HB_DIRECTION_IS_BACKWARD(buffer->props.direction) ? gr_slot_origin_X(gr_seg_first_slot(seg)) : 0.; + float curradv = 0.; if (HB_DIRECTION_IS_BACKWARD(buffer->props.direction)) { curradv = gr_slot_origin_X(gr_seg_first_slot(seg)); clusters[0].advance = gr_seg_advance_X(seg) - 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); @@ -340,13 +332,13 @@ _hb_graphite2_shape (hb_shape_plan_t *shape_plan, c->base_glyph = ic; c->num_glyphs = 0; if (HB_DIRECTION_IS_BACKWARD(buffer->props.direction)) + c->advance = curradv - gr_slot_origin_X(is); + else { - ci++; - clusters[ci].advance = curradv - gr_slot_origin_X(is); - } else { - clusters[ci].advance = gr_slot_origin_X(is) - curradv; - ci++; + c->advance = 0; + clusters[ci].advance += gr_slot_origin_X(is) - curradv; } + ci++; curradv = gr_slot_origin_X(is); } clusters[ci].num_glyphs++; @@ -355,8 +347,10 @@ _hb_graphite2_shape (hb_shape_plan_t *shape_plan, clusters[ci].num_chars = after + 1 - clusters[ci].base_char; } - if (!HB_DIRECTION_IS_BACKWARD(buffer->props.direction)) - clusters[ci].advance = gr_seg_advance_X(seg) - curradv; + if (HB_DIRECTION_IS_BACKWARD(buffer->props.direction)) + clusters[ci].advance += curradv; + else + clusters[ci].advance += gr_seg_advance_X(seg) - curradv; ci++; for (unsigned int i = 0; i < ci; ++i) @@ -371,48 +365,48 @@ _hb_graphite2_shape (hb_shape_plan_t *shape_plan, } buffer->len = glyph_count; - float yscale = font->y_scale / font->x_scale; + 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; /* Positioning. */ + unsigned int currclus = (unsigned int) -1; + 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)) { - int currclus = -1; - const hb_glyph_info_t *info = buffer->info; - hb_glyph_position_t *pPos = hb_buffer_get_glyph_positions (buffer, NULL); 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) - curradvx; + 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; + pPos->x_advance = info->var1.i32 * xscale; curradvx += pPos->x_advance; currclus = info->cluster; } else pPos->x_advance = 0.; - pPos->y_advance = gr_slot_advance_Y (is, grface, grfont) * yscale; + pPos->y_advance = gr_slot_advance_Y (is, grface, nullptr) * yscale; curradvy += pPos->y_advance; } } else { - int currclus = -1; - const hb_glyph_info_t *info = buffer->info; - hb_glyph_position_t *pPos = hb_buffer_get_glyph_positions (buffer, NULL); - curradvx = gr_seg_advance_X(seg); + 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; - if (currclus != -1) curradvx -= info[-1].var1.i32; + pPos->x_advance = info->var1.i32 * xscale; + curradvx -= pPos->x_advance; currclus = info->cluster; } else - pPos->x_advance = 0.; + pPos->x_advance = 0.; - pPos->y_advance = gr_slot_advance_Y (is, grface, grfont) * yscale; + pPos->y_advance = gr_slot_advance_Y (is, grface, nullptr) * yscale; curradvy -= pPos->y_advance; - pPos->x_offset = gr_slot_origin_X (is) - curradvx + pPos->x_advance; + pPos->x_offset = (gr_slot_origin_X (is) - info->var1.i32) * xscale - curradvx + pPos->x_advance; pPos->y_offset = gr_slot_origin_Y (is) * yscale - curradvy; } hb_buffer_reverse_clusters (buffer); @@ -421,5 +415,7 @@ _hb_graphite2_shape (hb_shape_plan_t *shape_plan, if (feats) gr_featureval_destroy (feats); gr_seg_destroy (seg); + buffer->unsafe_to_break_all (); + return true; } diff --git a/src/hb-graphite2.h b/src/hb-graphite2.h index 122c3e476..82b1e64cd 100644 --- a/src/hb-graphite2.h +++ b/src/hb-graphite2.h @@ -39,9 +39,13 @@ HB_BEGIN_DECLS HB_EXTERN gr_face * hb_graphite2_face_get_gr_face (hb_face_t *face); +#ifndef HB_DISABLE_DEPRECATED + HB_EXTERN gr_font * hb_graphite2_font_get_gr_font (hb_font_t *font); +#endif + HB_END_DECLS diff --git a/src/hb-icu.cc b/src/hb-icu.cc index ee54721fd..552eaeca5 100644 --- a/src/hb-icu.cc +++ b/src/hb-icu.cc @@ -34,7 +34,7 @@ #include "hb-unicode-private.hh" #include -#include +#include #include #include #include @@ -200,7 +200,7 @@ hb_icu_unicode_compose (hb_unicode_funcs_t *ufuncs HB_UNUSED, if (err) return false; icu_err = U_ZERO_ERROR; - len = unorm_normalize (utf16, len, UNORM_NFC, 0, normalized, ARRAY_LENGTH (normalized), &icu_err); + len = unorm2_normalize (unorm2_getNFCInstance (&icu_err), utf16, len, normalized, ARRAY_LENGTH (normalized), &icu_err); if (U_FAILURE (icu_err)) return false; if (u_countChar32 (normalized, len) == 1) { @@ -261,7 +261,7 @@ hb_icu_unicode_decompose (hb_unicode_funcs_t *ufuncs HB_UNUSED, if (err) return false; icu_err = U_ZERO_ERROR; - len = unorm_normalize (utf16, len, UNORM_NFD, 0, normalized, ARRAY_LENGTH (normalized), &icu_err); + len = unorm2_normalize (unorm2_getNFDInstance (&icu_err), utf16, len, normalized, ARRAY_LENGTH (normalized), &icu_err); if (U_FAILURE (icu_err)) return false; @@ -281,7 +281,7 @@ hb_icu_unicode_decompose (hb_unicode_funcs_t *ufuncs HB_UNUSED, * the second part :-(. */ UChar recomposed[20]; icu_err = U_ZERO_ERROR; - unorm_normalize (normalized, len, UNORM_NFC, 0, recomposed, ARRAY_LENGTH (recomposed), &icu_err); + unorm2_normalize (unorm2_getNFCInstance (&icu_err), normalized, len, recomposed, ARRAY_LENGTH (recomposed), &icu_err); if (U_FAILURE (icu_err)) return false; hb_codepoint_t c; @@ -297,7 +297,7 @@ hb_icu_unicode_decompose (hb_unicode_funcs_t *ufuncs HB_UNUSED, U16_PREV_UNSAFE (normalized, len, *b); /* Changes len in-place. */ UChar recomposed[18 * 2]; icu_err = U_ZERO_ERROR; - len = unorm_normalize (normalized, len, UNORM_NFC, 0, recomposed, ARRAY_LENGTH (recomposed), &icu_err); + len = unorm2_normalize (unorm2_getNFCInstance (&icu_err), normalized, len, recomposed, ARRAY_LENGTH (recomposed), &icu_err); if (U_FAILURE (icu_err)) return false; /* We expect that recomposed has exactly one character now. */ @@ -331,41 +331,64 @@ hb_icu_unicode_decompose_compatibility (hb_unicode_funcs_t *ufuncs HB_UNUSED, /* Normalise the codepoint using NFKD mode. */ icu_err = U_ZERO_ERROR; - len = unorm_normalize (utf16, len, UNORM_NFKD, 0, normalized, ARRAY_LENGTH (normalized), &icu_err); - if (icu_err) + len = unorm2_normalize (unorm2_getNFKDInstance (&icu_err), utf16, len, normalized, ARRAY_LENGTH (normalized), &icu_err); + if (U_FAILURE (icu_err)) return 0; /* Convert the decomposed form from UTF-16 to UTF-32. */ icu_err = U_ZERO_ERROR; u_strToUTF32 ((UChar32*) decomposed, HB_UNICODE_MAX_DECOMPOSITION_LEN, &utf32_len, normalized, len, &icu_err); - if (icu_err) + if (U_FAILURE (icu_err)) return 0; return utf32_len; } +static hb_unicode_funcs_t *static_icu_funcs = nullptr; + +#ifdef HB_USE_ATEXIT +static +void free_static_icu_funcs (void) +{ + hb_unicode_funcs_destroy (static_icu_funcs); +} +#endif + hb_unicode_funcs_t * hb_icu_get_unicode_funcs (void) { - static const hb_unicode_funcs_t _hb_icu_unicode_funcs = { - HB_OBJECT_HEADER_STATIC, +retry: + hb_unicode_funcs_t *funcs = (hb_unicode_funcs_t *) hb_atomic_ptr_get (&static_icu_funcs); - NULL, /* parent */ - true, /* immutable */ - { -#define HB_UNICODE_FUNC_IMPLEMENT(name) hb_icu_unicode_##name, + if (unlikely (!funcs)) + { +#if U_ICU_VERSION_MAJOR_NUM >= 49 + if (!hb_atomic_ptr_get (&normalizer)) { + UErrorCode icu_err = U_ZERO_ERROR; + /* We ignore failure in getNFCInstace(). */ + (void) hb_atomic_ptr_cmpexch (&normalizer, nullptr, unorm2_getNFCInstance (&icu_err)); + } +#endif + + funcs = hb_unicode_funcs_create (nullptr); + +#define HB_UNICODE_FUNC_IMPLEMENT(name) \ + hb_unicode_funcs_set_##name##_func (funcs, hb_icu_unicode_##name, nullptr, nullptr); HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS #undef HB_UNICODE_FUNC_IMPLEMENT + + hb_unicode_funcs_make_immutable (funcs); + + if (!hb_atomic_ptr_cmpexch (&static_icu_funcs, nullptr, funcs)) { + hb_unicode_funcs_destroy (funcs); + goto retry; } + +#ifdef HB_USE_ATEXIT + atexit (free_static_icu_funcs); /* First person registers atexit() callback. */ +#endif }; -#if U_ICU_VERSION_MAJOR_NUM >= 49 - if (!hb_atomic_ptr_get (&normalizer)) { - UErrorCode icu_err = U_ZERO_ERROR; - /* We ignore failure in getNFCInstace(). */ - (void) hb_atomic_ptr_cmpexch (&normalizer, NULL, unorm2_getNFCInstance (&icu_err)); - } -#endif - return const_cast (&_hb_icu_unicode_funcs); + return hb_unicode_funcs_reference (funcs); } diff --git a/src/hb-mutex-private.hh b/src/hb-mutex-private.hh index ed2703571..49ed10e08 100644 --- a/src/hb-mutex-private.hh +++ b/src/hb-mutex-private.hh @@ -68,7 +68,7 @@ typedef CRITICAL_SECTION hb_mutex_impl_t; #include typedef pthread_mutex_t hb_mutex_impl_t; #define HB_MUTEX_IMPL_INIT PTHREAD_MUTEX_INITIALIZER -#define hb_mutex_impl_init(M) pthread_mutex_init (M, NULL) +#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) diff --git a/src/hb-object-private.hh b/src/hb-object-private.hh index 6b73ff92d..baa1f8f05 100644 --- a/src/hb-object-private.hh +++ b/src/hb-object-private.hh @@ -33,18 +33,12 @@ #define HB_OBJECT_PRIVATE_HH #include "hb-private.hh" +#include "hb-debug.hh" #include "hb-atomic-private.hh" #include "hb-mutex-private.hh" -/* Debug */ - -#ifndef HB_DEBUG_OBJECT -#define HB_DEBUG_OBJECT (HB_DEBUG+0) -#endif - - /* reference_count */ #define HB_REFERENCE_COUNT_INERT_VALUE -1 @@ -193,7 +187,7 @@ static inline void *hb_object_get_user_data (Type *obj, hb_user_data_key_t *key) { if (unlikely (!obj || hb_object_is_inert (obj))) - return NULL; + return nullptr; assert (hb_object_is_valid (obj)); return obj->header.user_data.get (key); } diff --git a/src/hb-open-file-private.hh b/src/hb-open-file-private.hh index 5357ddcf5..e2644eaf9 100644 --- a/src/hb-open-file-private.hh +++ b/src/hb-open-file-private.hh @@ -30,6 +30,7 @@ #define HB_OPEN_FILE_PRIVATE_HH #include "hb-open-type-private.hh" +#include "hb-ot-head-table.hh" namespace OT { @@ -53,6 +54,16 @@ struct TTCHeader; typedef struct TableRecord { + int cmp (Tag t) const + { return -t.cmp (tag); } + + 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); + } + inline bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -61,9 +72,9 @@ typedef struct TableRecord Tag tag; /* 4-byte identifier. */ CheckSum checkSum; /* CheckSum for this table. */ - ULONG offset; /* Offset from beginning of TrueType font + Offset32 offset; /* Offset from beginning of TrueType font * file. */ - ULONG length; /* Length of this table. */ + HBUINT32 length; /* Length of this table. */ public: DEFINE_SIZE_STATIC (16); } OpenTypeTable; @@ -73,27 +84,39 @@ typedef struct OffsetTable friend struct OpenTypeFontFile; inline unsigned int get_table_count (void) const - { return numTables; } + { return tables.len; } inline const TableRecord& get_table (unsigned int i) const { - if (unlikely (i >= numTables)) return Null(TableRecord); return tables[i]; } + inline 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) + { + if (start_offset >= tables.len) + *table_count = 0; + else + *table_count = MIN (*table_count, tables.len - start_offset); + + const TableRecord *sub_tables = tables.array + start_offset; + unsigned int count = *table_count; + for (unsigned int i = 0; i < count; i++) + table_tags[i] = sub_tables[i].tag; + } + return tables.len; + } inline bool find_table_index (hb_tag_t tag, unsigned int *table_index) const { Tag t; t.set (tag); - unsigned int count = numTables; - for (unsigned int i = 0; i < count; i++) - { - if (t == tables[i].tag) - { - if (table_index) *table_index = i; - return true; - } - } - if (table_index) *table_index = Index::NOT_FOUND_INDEX; - return false; + /* Linear-search for small tables to work around fonts with unsorted + * table list. */ + int i = tables.len < 64 ? tables.lsearch (t) : tables.bsearch (t); + if (table_index) + *table_index = i == -1 ? Index::NOT_FOUND_INDEX : (unsigned int) i; + return i != -1; } inline const TableRecord& get_table_by_tag (hb_tag_t tag) const { @@ -103,19 +126,88 @@ typedef struct OffsetTable } public: + + inline bool serialize (hb_serialize_context_t *c, + hb_tag_t sfnt_tag, + Supplier &tags, + Supplier &blobs, + unsigned int table_count) + { + TRACE_SERIALIZE (this); + /* Alloc 12 for the OTHeader. */ + if (unlikely (!c->extend_min (*this))) return_trace (false); + /* Write sfntVersion (bytes 0..3). */ + sfnt_version.set (sfnt_tag); + /* Take space for numTables, searchRange, entrySelector, RangeShift + * and the TableRecords themselves. */ + if (unlikely (!tables.serialize (c, table_count))) return_trace (false); + + const char *dir_end = (const char *) c->head; + HBUINT32 *checksum_adjustment = nullptr; + + /* Write OffsetTables, alloc for and write actual table blobs. */ + for (unsigned int i = 0; i < table_count; i++) + { + TableRecord &rec = tables.array[i]; + hb_blob_t *blob = blobs[i]; + rec.tag.set (tags[i]); + rec.length.set (hb_blob_get_length (blob)); + rec.offset.serialize (c, this); + + /* Allocate room for the table and copy it. */ + char *start = (char *) c->allocate_size (rec.length); + if (unlikely (!start)) {return false;} + + memcpy (start, hb_blob_get_data (blob, nullptr), rec.length); + + /* 4-byte allignment. */ + if (rec.length % 4) + c->allocate_size (4 - rec.length % 4); + const char *end = (const char *) c->head; + + if (tags[i] == HB_OT_TAG_head && end - start >= head::static_size) + { + head *h = (head *) start; + checksum_adjustment = &h->checkSumAdjustment; + checksum_adjustment->set (0); + } + + rec.checkSum.set_for_data (start, end - start); + } + tags += table_count; + blobs += table_count; + + 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 < table_count; i++) + { + TableRecord &rec = tables.array[i]; + checksum.set (checksum + rec.checkSum); + } + + checksum_adjustment->set (0xB1B0AFBAu - checksum); + } + + return_trace (true); + } + inline bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); - return_trace (c->check_struct (this) && c->check_array (tables, TableRecord::static_size, numTables)); + return_trace (c->check_struct (this) && tables.sanitize (c)); } protected: Tag sfnt_version; /* '\0\001\0\00' if TrueType / 'OTTO' if CFF */ - USHORT numTables; /* Number of tables. */ - USHORT searchRangeZ; /* (Maximum power of 2 <= numTables) x 16 */ - USHORT entrySelectorZ; /* Log2(maximum power of 2 <= numTables). */ - USHORT rangeShiftZ; /* NumTables x 16-searchRange. */ - TableRecord tables[VAR]; /* TableRecord entries. numTables items */ + BinSearchArrayOf + tables; public: DEFINE_SIZE_ARRAY (12, tables); } OpenTypeFontFace; @@ -142,7 +234,7 @@ struct TTCHeaderVersion1 Tag ttcTag; /* TrueType Collection ID string: 'ttcf' */ FixedVersion<>version; /* Version of the TTC Header (1.0), * 0x00010000u */ - ArrayOf, ULONG> + ArrayOf, HBUINT32> table; /* Array of offsets to the OffsetTable for each font * from the beginning of the file */ public: @@ -237,6 +329,18 @@ struct OpenTypeFontFile } } + inline bool serialize_single (hb_serialize_context_t *c, + hb_tag_t sfnt_tag, + Supplier &tags, + Supplier &blobs, + unsigned int table_count) + { + TRACE_SERIALIZE (this); + assert (sfnt_tag != TTCTag); + if (unlikely (!c->extend_min (*this))) return_trace (false); + return_trace (u.fontFace.serialize (c, sfnt_tag, tags, blobs, table_count)); + } + inline bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); diff --git a/src/hb-open-type-private.hh b/src/hb-open-type-private.hh index df683ca4b..5d33199e1 100644 --- a/src/hb-open-type-private.hh +++ b/src/hb-open-type-private.hh @@ -30,6 +30,8 @@ #define HB_OPEN_TYPE_PRIVATE_HH #include "hb-private.hh" +#include "hb-debug.hh" +#include "hb-face-private.hh" namespace OT { @@ -84,7 +86,7 @@ static inline Type& StructAfter(TObject &X) #define _DEFINE_INSTANCE_ASSERTION1(_line, _assertion) \ inline void _instance_assertion_on_line_##_line (void) const \ { \ - ASSERT_STATIC (_assertion); \ + static_assert ((_assertion), ""); \ ASSERT_INSTANCE_POD (*this); /* Make sure it's POD. */ \ } # define _DEFINE_INSTANCE_ASSERTION0(_line, _assertion) _DEFINE_INSTANCE_ASSERTION1 (_line, _assertion) @@ -105,7 +107,7 @@ static inline Type& StructAfter(TObject &X) inline unsigned int get_size (void) const { return (size); } #define DEFINE_SIZE_UNION(size, _member) \ - DEFINE_INSTANCE_ASSERTION (this->u._member.static_size == (size)); \ + DEFINE_INSTANCE_ASSERTION (0*sizeof(this->u._member.static_size) + sizeof(this->u._member) == (size)); \ static const unsigned int min_size = (size) #define DEFINE_SIZE_MIN(size) \ @@ -129,14 +131,26 @@ static inline Type& StructAfter(TObject &X) */ /* Global nul-content Null pool. Enlarge as necessary. */ -/* TODO This really should be a extern HB_INTERNAL and defined somewhere... */ -static const void *_NullPool[(256+8) / sizeof (void *)]; + +#define HB_NULL_POOL_SIZE 264 +static_assert (HB_NULL_POOL_SIZE % sizeof (void *) == 0, "Align HB_NULL_POOL_SIZE."); + +#ifdef HB_NO_VISIBILITY +static +#else +extern HB_INTERNAL +#endif +const void * const _hb_NullPool[HB_NULL_POOL_SIZE / sizeof (void *)] +#ifdef HB_NO_VISIBILITY += {} +#endif +; /* Generic nul-content Null objects. */ template static inline const Type& Null (void) { - ASSERT_STATIC (sizeof (Type) <= sizeof (_NullPool)); - return *CastP (_NullPool); + static_assert (sizeof (Type) <= HB_NULL_POOL_SIZE, "Increase HB_NULL_POOL_SIZE."); + return *CastP (_hb_NullPool); } /* Specializaiton for arbitrary-content arbitrary-sized Null objects. */ @@ -146,7 +160,7 @@ template <> \ /*static*/ inline const Type& Null (void) { \ return *CastP (_Null##Type); \ } /* The following line really exists such that we end in a place needing semicolon */ \ -ASSERT_STATIC (Type::min_size + 1 <= sizeof (_Null##Type)) +static_assert (Type::min_size + 1 <= sizeof (_Null##Type), "Null pool too small. Enlarge.") /* Accessor macro. */ #define Null(Type) Null() @@ -171,29 +185,26 @@ struct hb_dispatch_context_t * Sanitize */ -#ifndef HB_DEBUG_SANITIZE -#define HB_DEBUG_SANITIZE (HB_DEBUG+0) -#endif - - -#define TRACE_SANITIZE(this) \ - hb_auto_trace_t trace \ - (&c->debug_depth, c->get_name (), this, HB_FUNC, \ - ""); - /* 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 8 +#endif +#ifndef HB_SANITIZE_MAX_OPS_MIN +#define HB_SANITIZE_MAX_OPS_MIN 16384 +#endif struct hb_sanitize_context_t : hb_dispatch_context_t { inline hb_sanitize_context_t (void) : debug_depth (0), - start (NULL), end (NULL), - writable (false), edit_count (0), - blob (NULL) {} + start (nullptr), end (nullptr), + writable (false), edit_count (0), max_ops (0), + blob (nullptr), + num_glyphs (0) {} inline const char *get_name (void) { return "SANITIZE"; } template @@ -213,9 +224,11 @@ struct hb_sanitize_context_t : inline void start_processing (void) { - this->start = hb_blob_get_data (this->blob, NULL); + this->start = hb_blob_get_data (this->blob, nullptr); this->end = this->start + hb_blob_get_length (this->blob); assert (this->start <= this->end); /* Must not overflow. */ + this->max_ops = MAX ((unsigned int) (this->end - this->start) * HB_SANITIZE_MAX_OPS_FACTOR, + (unsigned) HB_SANITIZE_MAX_OPS_MIN); this->edit_count = 0; this->debug_depth = 0; @@ -232,14 +245,17 @@ struct hb_sanitize_context_t : this->start, this->end, this->edit_count); hb_blob_destroy (this->blob); - this->blob = NULL; - this->start = this->end = NULL; + this->blob = nullptr; + this->start = this->end = nullptr; } inline bool check_range (const void *base, unsigned int len) const { const char *p = (const char *) base; - bool ok = this->start <= p && p <= this->end && (unsigned int) (this->end - p) >= len; + bool ok = this->max_ops-- > 0 && + this->start <= p && + p <= this->end && + (unsigned int) (this->end - p) >= len; DEBUG_MSG_LEVEL (SANITIZE, p, this->debug_depth+1, 0, "check_range [%p..%p] (%d bytes) in [%p..%p] -> %s", @@ -303,7 +319,9 @@ struct hb_sanitize_context_t : const char *start, *end; bool writable; unsigned int edit_count; + mutable int max_ops; hb_blob_t *blob; + unsigned int num_glyphs; }; @@ -312,8 +330,9 @@ struct hb_sanitize_context_t : template struct Sanitizer { - static hb_blob_t *sanitize (hb_blob_t *blob) { - hb_sanitize_context_t c[1]; + inline Sanitizer (void) {} + + inline hb_blob_t *sanitize (hb_blob_t *blob) { bool sane; /* TODO is_sane() stuff */ @@ -348,7 +367,7 @@ struct Sanitizer } else { unsigned int edit_count = c->edit_count; if (edit_count && !c->writable) { - c->start = hb_blob_get_data_writable (blob, NULL); + c->start = hb_blob_get_data_writable (blob, nullptr); c->end = c->start + hb_blob_get_length (blob); if (c->start) { @@ -373,9 +392,14 @@ struct Sanitizer static const Type* lock_instance (hb_blob_t *blob) { hb_blob_make_immutable (blob); - const char *base = hb_blob_get_data (blob, NULL); + const char *base = hb_blob_get_data (blob, nullptr); return unlikely (!base) ? &Null(Type) : CastP (base); } + + inline void set_num_glyphs (unsigned int num_glyphs) { c->num_glyphs = num_glyphs; } + + private: + hb_sanitize_context_t c[1]; }; @@ -384,16 +408,6 @@ struct Sanitizer * Serialize */ -#ifndef HB_DEBUG_SERIALIZE -#define HB_DEBUG_SERIALIZE (HB_DEBUG+0) -#endif - - -#define TRACE_SERIALIZE(this) \ - hb_auto_trace_t trace \ - (&c->debug_depth, "SERIALIZE", c, HB_FUNC, \ - ""); - struct hb_serialize_context_t { @@ -444,7 +458,7 @@ struct hb_serialize_context_t { if (unlikely (this->ran_out_of_room || this->end - this->head < ptrdiff_t (size))) { this->ran_out_of_room = true; - return NULL; + return nullptr; } memset (this->head, 0, size); char *ret = this->head; @@ -470,7 +484,7 @@ struct hb_serialize_context_t { unsigned int size = obj.get_size (); Type *ret = this->allocate_size (size); - if (unlikely (!ret)) return NULL; + if (unlikely (!ret)) return nullptr; memcpy (ret, obj, size); return ret; } @@ -480,7 +494,7 @@ struct hb_serialize_context_t { unsigned int size = obj.min_size; assert (this->start <= (char *) &obj && (char *) &obj <= this->head && (char *) &obj + size >= this->head); - if (unlikely (!this->allocate_size (((char *) &obj) + size - this->head))) return NULL; + if (unlikely (!this->allocate_size (((char *) &obj) + size - this->head))) return nullptr; return reinterpret_cast (&obj); } @@ -489,7 +503,7 @@ struct hb_serialize_context_t { unsigned int size = obj.get_size (); assert (this->start < (char *) &obj && (char *) &obj <= this->head && (char *) &obj + size >= this->head); - if (unlikely (!this->allocate_size (((char *) &obj) + size - this->head))) return NULL; + if (unlikely (!this->allocate_size (((char *) &obj) + size - this->head))) return nullptr; return reinterpret_cast (&obj); } @@ -507,23 +521,25 @@ struct hb_serialize_context_t template struct Supplier { - inline Supplier (const Type *array, unsigned int len_) + inline Supplier (const Type *array, unsigned int len_, unsigned int stride_=sizeof(Type)) { head = array; len = len_; + stride = stride_; } inline const Type operator [] (unsigned int i) const { if (unlikely (i >= len)) return Type (); - return head[i]; + return * (const Type *) (const void *) ((const char *) head + stride * i); } - inline void advance (unsigned int count) + inline Supplier & operator += (unsigned int count) { if (unlikely (count > len)) count = len; len -= count; - head += count; + head = (const Type *) (const void *) ((const char *) head + stride * count); + return *this; } private: @@ -531,12 +547,11 @@ struct Supplier inline Supplier& operator= (const Supplier &); /* Disallow copy */ unsigned int len; + unsigned int stride; const Type *head; }; - - /* * * The OpenType Font File: Data Types @@ -631,10 +646,11 @@ struct IntType inline bool operator == (const IntType &o) const { return (Type) v == (Type) o.v; } inline bool operator != (const IntType &o) const { return !(*this == o); } static inline int cmp (const IntType *a, const IntType *b) { return b->cmp (*a); } - inline int cmp (Type a) const + template + inline int cmp (Type2 a) const { Type b = v; - if (sizeof (Type) < sizeof (int)) + if (sizeof (Type) < sizeof (int) && sizeof (Type2) < sizeof (int)) return (int) a - (int) b; else return a < b ? -1 : a == b ? 0 : +1; @@ -650,21 +666,22 @@ struct IntType DEFINE_SIZE_STATIC (Size); }; -typedef IntType BYTE; /* 8-bit unsigned integer. */ -typedef IntType USHORT; /* 16-bit unsigned integer. */ -typedef IntType SHORT; /* 16-bit signed integer. */ -typedef IntType ULONG; /* 32-bit unsigned integer. */ -typedef IntType LONG; /* 32-bit signed integer. */ +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. */ typedef IntType UINT24; /* 24-bit unsigned integer. */ -/* 16-bit signed integer (SHORT) that describes a quantity in FUnits. */ -typedef SHORT FWORD; +/* 16-bit signed integer (HBINT16) that describes a quantity in FUnits. */ +typedef HBINT16 FWORD; -/* 16-bit unsigned integer (USHORT) that describes a quantity in FUnits. */ -typedef USHORT UFWORD; +/* 16-bit unsigned integer (HBUINT16) that describes a quantity in FUnits. */ +typedef HBUINT16 UFWORD; /* 16-bit signed fixed number with the low 14 bits of fraction (2.14). */ -struct F2DOT14 : SHORT +struct F2DOT14 : HBINT16 { //inline float to_float (void) const { return ???; } //inline void set_float (float f) { v.set (f * ???); } @@ -673,10 +690,10 @@ struct F2DOT14 : SHORT }; /* 32-bit signed fixed-point number (16.16). */ -struct Fixed: LONG +struct Fixed: HBINT32 { - //inline float to_float (void) const { return ???; } - //inline void set_float (float f) { v.set (f * ???); } + inline float to_float (void) const { return ((int32_t) v) / 65536.0; } + inline void set_float (float f) { v.set (round (f * 65536.0)); } public: DEFINE_SIZE_STATIC (4); }; @@ -691,15 +708,15 @@ struct LONGDATETIME return_trace (likely (c->check_struct (this))); } protected: - LONG major; - ULONG minor; + 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 : ULONG +struct Tag : HBUINT32 { /* What the char* converters return is NOT nul-terminated. Print using "%.4s" */ inline operator const char* (void) const { return reinterpret_cast (&this->v); } @@ -710,35 +727,44 @@ struct Tag : ULONG DEFINE_NULL_DATA (Tag, " "); /* Glyph index number, same as uint16 (length = 16 bits) */ -struct GlyphID : USHORT { - static inline int cmp (const GlyphID *a, const GlyphID *b) { return b->USHORT::cmp (*a); } - inline int cmp (hb_codepoint_t a) const { return (int) a - (int) *this; } -}; +typedef HBUINT16 GlyphID; /* Script/language-system/feature index */ -struct Index : USHORT { +struct Index : HBUINT16 { static const unsigned int NOT_FOUND_INDEX = 0xFFFFu; }; DEFINE_NULL_DATA (Index, "\xff\xff"); /* Offset, Null offset = 0 */ -template +template struct Offset : Type { inline bool is_null (void) const { return 0 == *this; } + + inline void *serialize (hb_serialize_context_t *c, const void *base) + { + void *t = c->start_embed (); + this->set ((char *) t - (char *) base); /* TODO(serialize) Overflow? */ + return t; + } + public: DEFINE_SIZE_STATIC (sizeof(Type)); }; +typedef Offset Offset16; +typedef Offset Offset32; + /* CheckSum */ -struct CheckSum : ULONG +struct CheckSum : HBUINT32 { /* This is reference implementation from the spec. */ - static inline uint32_t CalcTableChecksum (const ULONG *Table, uint32_t Length) + static inline uint32_t CalcTableChecksum (const HBUINT32 *Table, uint32_t Length) { uint32_t Sum = 0L; - const ULONG *EndPtr = Table+((Length+3) & ~3) / ULONG::static_size; + assert (0 == (Length & 3)); + const HBUINT32 *EndPtr = Table + Length / HBUINT32::static_size; while (Table < EndPtr) Sum += *Table++; @@ -747,7 +773,7 @@ struct CheckSum : ULONG /* Note: data should be 4byte aligned and have 4byte padding at the end. */ inline void set_for_data (const void *data, unsigned int length) - { set (CalcTableChecksum ((const ULONG *) data, length)); } + { set (CalcTableChecksum ((const HBUINT32 *) data, length)); } public: DEFINE_SIZE_STATIC (4); @@ -758,7 +784,7 @@ struct CheckSum : ULONG * Version Numbers */ -template +template struct FixedVersion { inline uint32_t to_int (void) const { return (major << (sizeof(FixedType) * 8)) + minor; } @@ -782,7 +808,7 @@ struct FixedVersion * Use: (base+offset) */ -template +template struct OffsetTo : Offset { inline const Type& operator () (const void *base) const @@ -794,9 +820,7 @@ struct OffsetTo : Offset inline Type& serialize (hb_serialize_context_t *c, const void *base) { - Type *t = c->start_embed (); - this->set ((char *) t - (char *) base); /* TODO(serialize) Overflow? */ - return *t; + return * (Type *) Offset::serialize (c, base); } inline bool sanitize (hb_sanitize_context_t *c, const void *base) const @@ -805,6 +829,7 @@ struct OffsetTo : Offset if (unlikely (!c->check_struct (this))) return_trace (false); unsigned int offset = *this; if (unlikely (!offset)) return_trace (true); + if (unlikely (!c->check_range (base, offset))) return_trace (false); const Type &obj = StructAtOffset (base, offset); return_trace (likely (obj.sanitize (c)) || neuter (c)); } @@ -815,6 +840,7 @@ struct OffsetTo : Offset if (unlikely (!c->check_struct (this))) return_trace (false); unsigned int offset = *this; if (unlikely (!offset)) return_trace (true); + if (unlikely (!c->check_range (base, offset))) return_trace (false); const Type &obj = StructAtOffset (base, offset); return_trace (likely (obj.sanitize (c, user_data)) || neuter (c)); } @@ -825,6 +851,7 @@ struct OffsetTo : Offset } DEFINE_SIZE_STATIC (sizeof(OffsetType)); }; +template struct LOffsetTo : OffsetTo {}; template static inline const Type& operator + (const Base &base, const OffsetTo &offset) { return offset (base); } template @@ -836,7 +863,7 @@ static inline Type& operator + (Base &base, OffsetTo &offset) */ /* An array with a number of elements. */ -template +template struct ArrayOf { const Type *sub_array (unsigned int start_offset, unsigned int *pcount /* IN/OUT */) const @@ -881,7 +908,7 @@ struct ArrayOf if (unlikely (!serialize (c, items_len))) return_trace (false); for (unsigned int i = 0; i < items_len; i++) array[i] = items[i]; - items.advance (items_len); + items += items_len; return_trace (true); } @@ -933,11 +960,16 @@ struct ArrayOf return -1; } + inline void qsort (void) + { + ::qsort (array, len, sizeof (Type), Type::cmp); + } + private: inline bool sanitize_shallow (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); - return_trace (c->check_struct (this) && c->check_array (array, Type::static_size, len)); + return_trace (len.sanitize (c) && c->check_array (array, Type::static_size, len)); } public: @@ -946,10 +978,11 @@ struct ArrayOf public: DEFINE_SIZE_ARRAY (sizeof (LenType), array); }; +template struct LArrayOf : ArrayOf {}; /* Array of Offset's */ -template -struct OffsetArrayOf : ArrayOf > {}; +template +struct OffsetArrayOf : ArrayOf > {}; /* Array of offsets relative to the beginning of the array itself. */ template @@ -976,7 +1009,7 @@ struct OffsetListOf : OffsetArrayOf /* An array starting at second element. */ -template +template struct HeadlessArrayOf { inline const Type& operator [] (unsigned int i) const @@ -998,16 +1031,10 @@ struct HeadlessArrayOf if (unlikely (!c->extend (*this))) return_trace (false); for (unsigned int i = 0; i < items_len - 1; i++) array[i] = items[i]; - items.advance (items_len - 1); + items += items_len - 1; return_trace (true); } - inline bool sanitize_shallow (hb_sanitize_context_t *c) const - { - return c->check_struct (this) - && c->check_array (this, Type::static_size, len); - } - inline bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -1025,6 +1052,15 @@ struct HeadlessArrayOf return_trace (true); } + private: + inline bool sanitize_shallow (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (len.sanitize (c) && + (!len || c->check_array (array, Type::static_size, len - 1))); + } + + public: LenType len; Type array[VAR]; public: @@ -1032,19 +1068,22 @@ struct HeadlessArrayOf }; -/* An array with sorted elements. Supports binary searching. */ -template +/* + * An array with sorted elements. Supports binary searching. + */ +template struct SortedArrayOf : ArrayOf { template inline int bsearch (const SearchType &x) const { /* Hand-coded bsearch here since this is in the hot inner loop. */ + const Type *arr = this->array; int min = 0, max = (int) this->len - 1; while (min <= max) { int mid = (min + max) / 2; - int c = this->array[mid].cmp (x); + int c = arr[mid].cmp (x); if (c < 0) max = mid - 1; else if (c > 0) @@ -1056,6 +1095,142 @@ struct SortedArrayOf : ArrayOf } }; +/* + * Binary-search arrays + */ + +struct BinSearchHeader +{ + inline operator uint32_t (void) const { return len; } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + inline void set (unsigned int v) + { + len.set (v); + assert (len == v); + entrySelectorZ.set (MAX (1u, _hb_bit_storage (v)) - 1); + searchRangeZ.set (16 * (1u << entrySelectorZ)); + rangeShiftZ.set (v * 16 > searchRangeZ + ? 16 * v - searchRangeZ + : 0); + } + + protected: + HBUINT16 len; + HBUINT16 searchRangeZ; + HBUINT16 entrySelectorZ; + HBUINT16 rangeShiftZ; + + public: + DEFINE_SIZE_STATIC (8); +}; + +template +struct BinSearchArrayOf : SortedArrayOf {}; + + +/* Lazy struct and blob loaders. */ + +/* Logic is shared between hb_lazy_loader_t and hb_lazy_table_loader_t */ +template +struct hb_lazy_loader_t +{ + inline void init (hb_face_t *face_) + { + face = face_; + instance = nullptr; + } + + inline void fini (void) + { + if (instance && instance != &OT::Null(T)) + { + instance->fini(); + free (instance); + } + } + + inline const T* get (void) const + { + retry: + T *p = (T *) hb_atomic_ptr_get (&instance); + if (unlikely (!p)) + { + p = (T *) calloc (1, sizeof (T)); + if (unlikely (!p)) + p = const_cast (&OT::Null(T)); + else + p->init (face); + if (unlikely (!hb_atomic_ptr_cmpexch (const_cast(&instance), nullptr, p))) + { + if (p != &OT::Null(T)) + p->fini (); + goto retry; + } + } + return p; + } + + inline const T* operator-> (void) const + { + return get (); + } + + private: + hb_face_t *face; + T *instance; +}; + +/* Logic is shared between hb_lazy_loader_t and hb_lazy_table_loader_t */ +template +struct hb_lazy_table_loader_t +{ + inline void init (hb_face_t *face_) + { + face = face_; + blob = nullptr; + instance = nullptr; + } + + inline void fini (void) + { + hb_blob_destroy (blob); + } + + inline const T* get (void) const + { + retry: + T *p = (T *) hb_atomic_ptr_get (&instance); + if (unlikely (!p)) + { + hb_blob_t *blob_ = OT::Sanitizer().sanitize (face->reference_table (T::tableTag)); + p = const_cast(OT::Sanitizer::lock_instance (blob_)); + if (!hb_atomic_ptr_cmpexch (const_cast(&instance), nullptr, p)) + { + hb_blob_destroy (blob_); + goto retry; + } + blob = blob_; + } + return p; + } + + inline const T* operator-> (void) const + { + return get(); + } + + hb_face_t *face; + mutable hb_blob_t *blob; + private: + mutable T *instance; +}; + } /* namespace OT */ diff --git a/src/hb-ot-base.h b/src/hb-ot-base.h new file mode 100644 index 000000000..0437c1689 --- /dev/null +++ b/src/hb-ot-base.h @@ -0,0 +1,56 @@ +/* + * Copyright © 2017 Elie Roux + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE 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_H_IN +#error "Include instead." +#endif + +#ifndef HB_OT_BASE_H +#define HB_OT_BASE_H + +#include "hb.h" + +HB_BEGIN_DECLS + +#define HB_OT_TAG_BASE HB_TAG('B','A','S','E') + +// https://www.microsoft.com/typography/otspec/baselinetags.htm + +#define HB_OT_TAG_BASE_HANG HB_TAG('h','a','n','g') +#define HB_OT_TAG_BASE_ICFB HB_TAG('i','c','f','b') +#define HB_OT_TAG_BASE_ICFT HB_TAG('i','c','f','t') +#define HB_OT_TAG_BASE_IDEO HB_TAG('i','d','e','o') +#define HB_OT_TAG_BASE_IDTB HB_TAG('i','d','t','b') +#define HB_OT_TAG_BASE_MATH HB_TAG('m','a','t','h') +#define HB_OT_TAG_BASE_ROMN HB_TAG('r','o','m','n') + +/* Methods */ + +// HB_EXTERN hb_bool_t +// hb_ot_base_has_data (hb_face_t *face); + +HB_END_DECLS + +#endif /* HB_OT_BASE_H */ diff --git a/src/hb-ot-cbdt-table.hh b/src/hb-ot-cbdt-table.hh new file mode 100644 index 000000000..e4519529b --- /dev/null +++ b/src/hb-ot-cbdt-table.hh @@ -0,0 +1,471 @@ +/* + * 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 + */ + +#ifndef HB_OT_CBDT_TABLE_HH +#define HB_OT_CBDT_TABLE_HH + +#include "hb-open-type-private.hh" + +namespace OT { + +struct SmallGlyphMetrics +{ + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + inline void get_extents (hb_glyph_extents_t *extents) const + { + extents->x_bearing = bearingX; + extents->y_bearing = bearingY; + extents->width = width; + extents->height = -height; + } + + HBUINT8 height; + HBUINT8 width; + HBINT8 bearingX; + HBINT8 bearingY; + HBUINT8 advance; + + DEFINE_SIZE_STATIC(5); +}; + +struct BigGlyphMetrics : SmallGlyphMetrics +{ + HBINT8 vertBearingX; + HBINT8 vertBearingY; + HBUINT8 vertAdvance; + + DEFINE_SIZE_STATIC(8); +}; + +struct SBitLineMetrics +{ + inline 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; + + DEFINE_SIZE_STATIC(12); +}; + + +/* + * Index Subtables. + */ + +struct IndexSubtableHeader +{ + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + HBUINT16 indexFormat; + HBUINT16 imageFormat; + HBUINT32 imageDataOffset; + + DEFINE_SIZE_STATIC(8); +}; + +template +struct IndexSubtableFormat1Or3 +{ + inline bool sanitize (hb_sanitize_context_t *c, unsigned int glyph_count) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + c->check_array (offsetArrayZ, offsetArrayZ[0].static_size, 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; + } + + IndexSubtableHeader header; + Offset offsetArrayZ[VAR]; + + DEFINE_SIZE_ARRAY(8, offsetArrayZ); +}; + +struct IndexSubtableFormat1 : IndexSubtableFormat1Or3 {}; +struct IndexSubtableFormat3 : IndexSubtableFormat1Or3 {}; + +struct IndexSubtable +{ + inline 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); + } + } + + inline bool get_extents (hb_glyph_extents_t *extents) 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; + } + } + + protected: + union { + IndexSubtableHeader header; + IndexSubtableFormat1 format1; + IndexSubtableFormat3 format3; + /* TODO: Format 2, 4, 5. */ + } u; + public: + DEFINE_SIZE_UNION (8, header); +}; + +struct IndexSubtableRecord +{ + inline 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, this, lastGlyphIndex - firstGlyphIndex + 1)); + } + + inline bool get_extents (hb_glyph_extents_t *extents) const + { + return (this+offsetToSubtable).get_extents (extents); + } + + bool get_image_data (unsigned int gid, + unsigned int *offset, + unsigned int *length, + unsigned int *format) const + { + if (gid < firstGlyphIndex || gid > lastGlyphIndex) + { + return false; + } + return (this+offsetToSubtable).get_image_data (gid - firstGlyphIndex, + offset, length, format); + } + + HBUINT16 firstGlyphIndex; + HBUINT16 lastGlyphIndex; + LOffsetTo offsetToSubtable; + + DEFINE_SIZE_STATIC(8); +}; + +struct IndexSubtableArray +{ + inline bool sanitize (hb_sanitize_context_t *c, unsigned int count) const + { + TRACE_SANITIZE (this); + if (unlikely (!c->check_array (&indexSubtablesZ, indexSubtablesZ[0].static_size, count))) + return_trace (false); + for (unsigned int i = 0; i < count; i++) + if (unlikely (!indexSubtablesZ[i].sanitize (c, this))) + return_trace (false); + 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: + IndexSubtableRecord indexSubtablesZ[VAR]; + + public: + DEFINE_SIZE_ARRAY(0, indexSubtablesZ); +}; + +struct BitmapSizeTable +{ + friend struct CBLC; + + inline 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) && + c->check_range (&(base+indexSubtableArrayOffset), indexTablesSize) && + horizontal.sanitize (c) && + vertical.sanitize (c)); + } + + const IndexSubtableRecord *find_table (hb_codepoint_t glyph, const void *base) const + { + return (base+indexSubtableArrayOffset).find_table (glyph, numberOfIndexSubtables); + } + + protected: + LOffsetTo indexSubtableArrayOffset; + HBUINT32 indexTablesSize; + HBUINT32 numberOfIndexSubtables; + HBUINT32 colorRef; + SBitLineMetrics horizontal; + SBitLineMetrics vertical; + HBUINT16 startGlyphIndex; + HBUINT16 endGlyphIndex; + HBUINT8 ppemX; + HBUINT8 ppemY; + HBUINT8 bitDepth; + HBINT8 flags; + + public: + DEFINE_SIZE_STATIC(48); +}; + + +/* + * Glyph Bitmap Data Formats. + */ + +struct GlyphBitmapDataFormat17 +{ + SmallGlyphMetrics glyphMetrics; + HBUINT32 dataLen; + HBUINT8 dataZ[VAR]; + + DEFINE_SIZE_ARRAY(9, dataZ); +}; + + +/* + * CBLC -- Color Bitmap Location Table + */ + +#define HB_OT_TAG_CBLC HB_TAG('C','B','L','C') + +struct CBLC +{ + friend struct CBDT; + + static const hb_tag_t tableTag = HB_OT_TAG_CBLC; + + inline 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)); + } + + protected: + const IndexSubtableRecord *find_table (hb_codepoint_t glyph, + unsigned int *x_ppem, unsigned int *y_ppem) const + { + /* TODO: Make it possible to select strike. */ + + unsigned int count = sizeTables.len; + for (uint32_t i = 0; i < count; ++i) + { + unsigned int startGlyphIndex = sizeTables.array[i].startGlyphIndex; + unsigned int endGlyphIndex = sizeTables.array[i].endGlyphIndex; + if (startGlyphIndex <= glyph && glyph <= endGlyphIndex) + { + *x_ppem = sizeTables[i].ppemX; + *y_ppem = sizeTables[i].ppemY; + return sizeTables[i].find_table (glyph, this); + } + } + + return nullptr; + } + + protected: + FixedVersion<> version; + LArrayOf sizeTables; + + public: + DEFINE_SIZE_ARRAY(8, sizeTables); +}; + +/* + * CBDT -- Color Bitmap Data Table + */ +#define HB_OT_TAG_CBDT HB_TAG('C','B','D','T') + +struct CBDT +{ + static const hb_tag_t tableTag = HB_OT_TAG_CBDT; + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + likely (version.major == 2 || version.major == 3)); + } + + struct accelerator_t + { + inline void init (hb_face_t *face) + { + upem = hb_face_get_upem (face); + + cblc_blob = Sanitizer().sanitize (face->reference_table (HB_OT_TAG_CBLC)); + cbdt_blob = Sanitizer().sanitize (face->reference_table (HB_OT_TAG_CBDT)); + cbdt_len = hb_blob_get_length (cbdt_blob); + + if (hb_blob_get_length (cblc_blob) == 0) { + cblc = nullptr; + cbdt = nullptr; + return; /* Not a bitmap font. */ + } + cblc = Sanitizer::lock_instance (cblc_blob); + cbdt = Sanitizer::lock_instance (cbdt_blob); + + } + + inline void fini (void) + { + hb_blob_destroy (this->cblc_blob); + hb_blob_destroy (this->cbdt_blob); + } + + inline bool get_extents (hb_codepoint_t glyph, hb_glyph_extents_t *extents) const + { + unsigned int x_ppem = upem, y_ppem = upem; /* TODO Use font ppem if available. */ + + if (!cblc) + return false; // Not a color bitmap font. + + const IndexSubtableRecord *subtable_record = this->cblc->find_table(glyph, &x_ppem, &y_ppem); + if (!subtable_record || !x_ppem || !y_ppem) + return false; + + if (subtable_record->get_extents (extents)) + return true; + + unsigned int image_offset = 0, image_length = 0, image_format = 0; + if (!subtable_record->get_image_data (glyph, &image_offset, &image_length, &image_format)) + return false; + + { + 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; + + const GlyphBitmapDataFormat17& glyphFormat17 = + StructAtOffset (this->cbdt, image_offset); + glyphFormat17.glyphMetrics.get_extents (extents); + } + break; + default: + // TODO: Support other image formats. + return false; + } + } + + /* Convert to the font units. */ + extents->x_bearing *= upem / (float) x_ppem; + extents->y_bearing *= upem / (float) y_ppem; + extents->width *= upem / (float) x_ppem; + extents->height *= upem / (float) y_ppem; + + return true; + } + + private: + hb_blob_t *cblc_blob; + hb_blob_t *cbdt_blob; + const CBLC *cblc; + const CBDT *cbdt; + + unsigned int cbdt_len; + unsigned int upem; + }; + + + protected: + FixedVersion<>version; + HBUINT8 dataZ[VAR]; + + public: + DEFINE_SIZE_ARRAY(4, dataZ); +}; + +} /* namespace OT */ + +#endif /* HB_OT_CBDT_TABLE_HH */ diff --git a/src/hb-ot-cmap-table.hh b/src/hb-ot-cmap-table.hh index d7a94a1ef..020798986 100644 --- a/src/hb-ot-cmap-table.hh +++ b/src/hb-ot-cmap-table.hh @@ -28,7 +28,7 @@ #define HB_OT_CMAP_TABLE_HH #include "hb-open-type-private.hh" - +#include "hb-subset-plan.hh" namespace OT { @@ -58,10 +58,10 @@ struct CmapSubtableFormat0 } protected: - USHORT format; /* Format number is set to 0. */ - USHORT lengthZ; /* Byte length of this subtable. */ - USHORT languageZ; /* Ignore. */ - BYTE glyphIdArray[256];/* An array that maps character + HBUINT16 format; /* Format number is set to 0. */ + HBUINT16 lengthZ; /* Byte length of this subtable. */ + HBUINT16 languageZ; /* Ignore. */ + HBUINT8 glyphIdArray[256];/* An array that maps character * code to glyph index values. */ public: DEFINE_SIZE_STATIC (6 + 256); @@ -88,8 +88,8 @@ struct CmapSubtableFormat4 /* Custom two-array bsearch. */ int min = 0, max = (int) thiz->segCount - 1; - const USHORT *startCount = thiz->startCount; - const USHORT *endCount = thiz->endCount; + const HBUINT16 *startCount = thiz->startCount; + const HBUINT16 *endCount = thiz->endCount; unsigned int i; while (min <= max) { @@ -127,11 +127,11 @@ struct CmapSubtableFormat4 return true; } - const USHORT *endCount; - const USHORT *startCount; - const USHORT *idDelta; - const USHORT *idRangeOffset; - const USHORT *glyphIdArray; + const HBUINT16 *endCount; + const HBUINT16 *startCount; + const HBUINT16 *idDelta; + const HBUINT16 *idRangeOffset; + const HBUINT16 *glyphIdArray; unsigned int segCount; unsigned int glyphIdArrayLength; }; @@ -165,24 +165,24 @@ struct CmapSubtableFormat4 } protected: - USHORT format; /* Format number is set to 4. */ - USHORT length; /* This is the length in bytes of the + HBUINT16 format; /* Format number is set to 4. */ + HBUINT16 length; /* This is the length in bytes of the * subtable. */ - USHORT languageZ; /* Ignore. */ - USHORT segCountX2; /* 2 x segCount. */ - USHORT searchRangeZ; /* 2 * (2**floor(log2(segCount))) */ - USHORT entrySelectorZ; /* log2(searchRange/2) */ - USHORT rangeShiftZ; /* 2 x segCount - searchRange */ + HBUINT16 languageZ; /* Ignore. */ + HBUINT16 segCountX2; /* 2 x segCount. */ + HBUINT16 searchRangeZ; /* 2 * (2**floor(log2(segCount))) */ + HBUINT16 entrySelectorZ; /* log2(searchRange/2) */ + HBUINT16 rangeShiftZ; /* 2 x segCount - searchRange */ - USHORT values[VAR]; + HBUINT16 values[VAR]; #if 0 - USHORT endCount[segCount]; /* End characterCode for each segment, + HBUINT16 endCount[segCount]; /* End characterCode for each segment, * last=0xFFFFu. */ - USHORT reservedPad; /* Set to 0. */ - USHORT startCount[segCount]; /* Start character code for each segment. */ - SHORT idDelta[segCount]; /* Delta for all character codes in segment. */ - USHORT idRangeOffset[segCount];/* Offsets into glyphIdArray or 0 */ - USHORT glyphIdArray[VAR]; /* Glyph index array (arbitrary length) */ + 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 */ + HBUINT16 glyphIdArray[VAR]; /* Glyph index array (arbitrary length) */ #endif public: @@ -193,6 +193,7 @@ struct CmapSubtableLongGroup { friend struct CmapSubtableFormat12; friend struct CmapSubtableFormat13; + friend struct cmap; int cmp (hb_codepoint_t codepoint) const { @@ -208,9 +209,9 @@ struct CmapSubtableLongGroup } private: - ULONG startCharCode; /* First character code in this group. */ - ULONG endCharCode; /* Last character code in this group. */ - ULONG glyphID; /* Glyph index; interpretation depends on + 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); @@ -247,12 +248,14 @@ struct CmapSubtableTrimmed DEFINE_SIZE_ARRAY (5 * sizeof (UINT), glyphIdArray); }; -struct CmapSubtableFormat6 : CmapSubtableTrimmed {}; -struct CmapSubtableFormat10 : CmapSubtableTrimmed {}; +struct CmapSubtableFormat6 : CmapSubtableTrimmed {}; +struct CmapSubtableFormat10 : CmapSubtableTrimmed {}; template struct CmapSubtableLongSegmented { + friend struct cmap; + inline bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const { int i = groups.bsearch (codepoint); @@ -268,12 +271,22 @@ struct CmapSubtableLongSegmented return_trace (c->check_struct (this) && groups.sanitize (c)); } + inline bool serialize (hb_serialize_context_t *c, + hb_prealloced_array_t &group_data) + { + TRACE_SERIALIZE (this); + if (unlikely (!c->extend_min (*this))) return_trace (false); + Supplier supplier (group_data.array, group_data.len); + if (unlikely (!groups.serialize (c, supplier, group_data.len))) return_trace (false); + return true; + } + protected: - USHORT format; /* Subtable format; set to 12. */ - USHORT reservedZ; /* Reserved; set to 0. */ - ULONG lengthZ; /* Byte length of this subtable. */ - ULONG languageZ; /* Ignore. */ - SortedArrayOf + HBUINT16 format; /* Subtable format; set to 12. */ + HBUINT16 reservedZ; /* Reserved; set to 0. */ + HBUINT32 lengthZ; /* Byte length of this subtable. */ + HBUINT32 languageZ; /* Ignore. */ + SortedArrayOf groups; /* Groupings. */ public: DEFINE_SIZE_ARRAY (16, groups); @@ -316,13 +329,13 @@ struct UnicodeValueRange } UINT24 startUnicodeValue; /* First value in this range. */ - BYTE additionalCount; /* Number of additional values in this + HBUINT8 additionalCount; /* Number of additional values in this * range. */ public: DEFINE_SIZE_STATIC (4); }; -typedef SortedArrayOf DefaultUVS; +typedef SortedArrayOf DefaultUVS; struct UVSMapping { @@ -343,7 +356,7 @@ struct UVSMapping DEFINE_SIZE_STATIC (5); }; -typedef SortedArrayOf NonDefaultUVS; +typedef SortedArrayOf NonDefaultUVS; struct VariationSelectorRecord { @@ -380,9 +393,9 @@ struct VariationSelectorRecord } UINT24 varSelector; /* Variation selector. */ - OffsetTo + LOffsetTo defaultUVS; /* Offset to Default UVS Table. May be 0. */ - OffsetTo + LOffsetTo nonDefaultUVS; /* Offset to Non-Default UVS Table. May be 0. */ public: DEFINE_SIZE_STATIC (11); @@ -405,9 +418,9 @@ struct CmapSubtableFormat14 } protected: - USHORT format; /* Format number is set to 14. */ - ULONG lengthZ; /* Byte length of this subtable. */ - SortedArrayOf + HBUINT16 format; /* Format number is set to 14. */ + HBUINT32 lengthZ; /* Byte length of this subtable. */ + SortedArrayOf record; /* Variation selector records; sorted * in increasing order of `varSelector'. */ public: @@ -422,12 +435,12 @@ struct CmapSubtable 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 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; } @@ -451,7 +464,7 @@ struct CmapSubtable public: union { - USHORT format; /* Format identifier */ + HBUINT16 format; /* Format identifier */ CmapSubtableFormat0 format0; CmapSubtableFormat4 format4; CmapSubtableFormat6 format6; @@ -484,9 +497,9 @@ struct EncodingRecord subtable.sanitize (c, base)); } - USHORT platformID; /* Platform ID. */ - USHORT encodingID; /* Platform-specific encoding ID. */ - OffsetTo + HBUINT16 platformID; /* Platform ID. */ + HBUINT16 encodingID; /* Platform-specific encoding ID. */ + LOffsetTo subtable; /* Byte offset from beginning of table to the subtable for this encoding. */ public: DEFINE_SIZE_STATIC (8); @@ -496,6 +509,254 @@ struct cmap { static const hb_tag_t tableTag = HB_OT_TAG_cmap; + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + likely (version == 0) && + encodingRecord.sanitize (c, this)); + } + + inline bool populate_groups (hb_subset_plan_t *plan, + hb_prealloced_array_t *groups) const + { + CmapSubtableLongGroup *group = nullptr; + for (unsigned int i = 0; i < plan->codepoints.len; i++) { + + hb_codepoint_t cp = plan->codepoints[i]; + if (!group || cp - 1 != group->endCharCode) + { + group = groups->push (); + group->startCharCode.set (cp); + group->endCharCode.set (cp); + hb_codepoint_t new_gid; + if (unlikely (!hb_subset_plan_new_gid_for_codepoint (plan, cp, &new_gid))) + { + DEBUG_MSG(SUBSET, nullptr, "Unable to find new gid for %04x", cp); + return false; + } + group->glyphID.set (new_gid); + } else + { + group->endCharCode.set (cp); + } + } + + DEBUG_MSG(SUBSET, nullptr, "cmap"); + for (unsigned int i = 0; i < groups->len; i++) { + CmapSubtableLongGroup& group = (*groups)[i]; + DEBUG_MSG(SUBSET, nullptr, " %d: U+%04X-U+%04X, gid %d-%d", i, (uint32_t) group.startCharCode, (uint32_t) group.endCharCode, (uint32_t) group.glyphID, (uint32_t) group.glyphID + ((uint32_t) group.endCharCode - (uint32_t) group.startCharCode)); + } + + return true; + } + + inline bool _subset (hb_prealloced_array_t &groups, + size_t dest_sz, + void *dest) const + { + hb_serialize_context_t c (dest, dest_sz); + + OT::cmap *cmap = c.start_serialize (); + if (unlikely (!c.extend_min (*cmap))) + { + return false; + } + + cmap->version.set (0); + + if (unlikely (!cmap->encodingRecord.serialize (&c, /* numTables */ 1))) return false; + + EncodingRecord &rec = cmap->encodingRecord[0]; + rec.platformID.set (3); // Windows + rec.encodingID.set (10); // Unicode UCS-4 + + /* capture offset to subtable */ + CmapSubtable &subtable = rec.subtable.serialize (&c, cmap); + + subtable.u.format.set (12); + + CmapSubtableFormat12 &format12 = subtable.u.format12; + if (unlikely (!c.extend_min (format12))) return false; + + format12.format.set (12); + format12.reservedZ.set (0); + format12.lengthZ.set (16 + 12 * groups.len); + + if (unlikely (!format12.serialize (&c, groups))) return false; + + c.end_serialize (); + + return true; + } + + inline bool subset (hb_subset_plan_t *plan) const + { + hb_auto_array_t groups; + + if (unlikely (!populate_groups (plan, &groups))) return false; + + // We now know how big our blob needs to be + // TODO use APIs from the structs to get size? + size_t dest_sz = 4 // header + + 8 // 1 EncodingRecord + + 16 // Format 12 header + + 12 * groups.len; // SequentialMapGroup records + void *dest = malloc (dest_sz); + if (unlikely (!dest)) { + DEBUG_MSG(SUBSET, nullptr, "Unable to alloc %lu for cmap subset output", (unsigned long) dest_sz); + return false; + } + + if (unlikely (!_subset (groups, dest_sz, dest))) + { + free (dest); + return false; + } + + // all done, write the blob into dest + hb_blob_t *cmap_prime = hb_blob_create ((const char *)dest, + dest_sz, + HB_MEMORY_MODE_READONLY, + dest, + free); + bool result = hb_subset_plan_add_table (plan, HB_OT_TAG_cmap, cmap_prime); + hb_blob_destroy (cmap_prime); + return result; + } + + struct accelerator_t + { + inline void init (hb_face_t *face) + { + this->blob = OT::Sanitizer().sanitize (face->reference_table (HB_OT_TAG_cmap)); + const OT::cmap *cmap = OT::Sanitizer::lock_instance (this->blob); + const OT::CmapSubtable *subtable = nullptr; + const OT::CmapSubtableFormat14 *subtable_uvs = nullptr; + + bool symbol = false; + /* 32-bit subtables. */ + if (!subtable) subtable = cmap->find_subtable (3, 10); + if (!subtable) subtable = cmap->find_subtable (0, 6); + if (!subtable) subtable = cmap->find_subtable (0, 4); + /* 16-bit subtables. */ + if (!subtable) subtable = cmap->find_subtable (3, 1); + if (!subtable) subtable = cmap->find_subtable (0, 3); + if (!subtable) subtable = cmap->find_subtable (0, 2); + if (!subtable) subtable = cmap->find_subtable (0, 1); + if (!subtable) subtable = cmap->find_subtable (0, 0); + if (!subtable) + { + subtable = cmap->find_subtable (3, 0); + if (subtable) symbol = true; + } + /* Meh. */ + if (!subtable) subtable = &OT::Null(OT::CmapSubtable); + + /* UVS subtable. */ + if (!subtable_uvs) + { + const OT::CmapSubtable *st = cmap->find_subtable (0, 5); + if (st && st->u.format == 14) + subtable_uvs = &st->u.format14; + } + /* Meh. */ + if (!subtable_uvs) subtable_uvs = &OT::Null(OT::CmapSubtableFormat14); + + this->uvs_table = subtable_uvs; + + this->get_glyph_data = subtable; + if (unlikely (symbol)) + this->get_glyph_func = get_glyph_from_symbol; + else + switch (subtable->u.format) { + /* Accelerate format 4 and format 12. */ + default: this->get_glyph_func = get_glyph_from; break; + case 12: this->get_glyph_func = get_glyph_from; break; + case 4: + { + this->format4_accel.init (&subtable->u.format4); + this->get_glyph_data = &this->format4_accel; + this->get_glyph_func = this->format4_accel.get_glyph_func; + } + break; + } + } + + inline void fini (void) + { + hb_blob_destroy (this->blob); + } + + inline bool get_nominal_glyph (hb_codepoint_t unicode, + hb_codepoint_t *glyph) const + { + return this->get_glyph_func (this->get_glyph_data, unicode, glyph); + } + + inline bool get_variation_glyph (hb_codepoint_t unicode, + hb_codepoint_t variation_selector, + hb_codepoint_t *glyph) const + { + switch (this->uvs_table->get_glyph_variant (unicode, + variation_selector, + glyph)) + { + case OT::GLYPH_VARIANT_NOT_FOUND: return false; + case OT::GLYPH_VARIANT_FOUND: return true; + case OT::GLYPH_VARIANT_USE_DEFAULT: break; + } + + return get_nominal_glyph (unicode, glyph); + } + + protected: + typedef bool (*hb_cmap_get_glyph_func_t) (const void *obj, + hb_codepoint_t codepoint, + hb_codepoint_t *glyph); + + template + static inline 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 + static inline 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 (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: + * http://www.microsoft.com/typography/otspec/recom.htm + * under "Non-Standard (Symbol) Fonts". */ + return typed_obj->get_glyph (0xF000u + codepoint, glyph); + } + + return false; + } + + private: + hb_cmap_get_glyph_func_t get_glyph_func; + const void *get_glyph_data; + OT::CmapSubtableFormat4::accelerator_t format4_accel; + + const OT::CmapSubtableFormat14 *uvs_table; + hb_blob_t *blob; + }; + + protected: + inline const CmapSubtable *find_subtable (unsigned int platform_id, unsigned int encoding_id) const { @@ -508,20 +769,13 @@ struct cmap * unsorted subtable list. */ int result = encodingRecord./*bsearch*/lsearch (key); if (result == -1 || !encodingRecord[result].subtable) - return NULL; + return nullptr; return &(this+encodingRecord[result].subtable); } - inline bool sanitize (hb_sanitize_context_t *c) const - { - TRACE_SANITIZE (this); - return_trace (c->check_struct (this) && - likely (version == 0) && - encodingRecord.sanitize (c, this)); - } - - USHORT version; /* Table version number (0). */ + protected: + HBUINT16 version; /* Table version number (0). */ SortedArrayOf encodingRecord; /* Encoding tables. */ public: diff --git a/src/hb-ot-font.cc b/src/hb-ot-font.cc index 39fc849a9..9864064b1 100644 --- a/src/hb-ot-font.cc +++ b/src/hb-ot-font.cc @@ -31,323 +31,22 @@ #include "hb-font-private.hh" #include "hb-ot-cmap-table.hh" +#include "hb-ot-cbdt-table.hh" #include "hb-ot-glyf-table.hh" -#include "hb-ot-head-table.hh" -#include "hb-ot-hhea-table.hh" #include "hb-ot-hmtx-table.hh" -#include "hb-ot-os2-table.hh" -//#include "hb-ot-post-table.hh" +#include "hb-ot-kern-table.hh" +#include "hb-ot-post-table.hh" -struct hb_ot_face_metrics_accelerator_t -{ - unsigned int num_metrics; - unsigned int num_advances; - unsigned int default_advance; - unsigned short ascender; - unsigned short descender; - unsigned short line_gap; - - const OT::_mtx *table; - hb_blob_t *blob; - - inline void init (hb_face_t *face, - hb_tag_t _hea_tag, - hb_tag_t _mtx_tag, - hb_tag_t os2_tag) - { - this->default_advance = face->get_upem (); - - bool got_font_extents = false; - if (os2_tag) - { - hb_blob_t *os2_blob = OT::Sanitizer::sanitize (face->reference_table (os2_tag)); - const OT::os2 *os2 = OT::Sanitizer::lock_instance (os2_blob); -#define USE_TYPO_METRICS (1u<<7) - if (0 != (os2->fsSelection & USE_TYPO_METRICS)) - { - this->ascender = os2->sTypoAscender; - this->descender = os2->sTypoDescender; - this->line_gap = os2->sTypoLineGap; - got_font_extents = (this->ascender | this->descender) != 0; - } - hb_blob_destroy (os2_blob); - } - - hb_blob_t *_hea_blob = OT::Sanitizer::sanitize (face->reference_table (_hea_tag)); - const OT::_hea *_hea = OT::Sanitizer::lock_instance (_hea_blob); - this->num_advances = _hea->numberOfLongMetrics; - if (!got_font_extents) - { - this->ascender = _hea->ascender; - this->descender = _hea->descender; - this->line_gap = _hea->lineGap; - } - hb_blob_destroy (_hea_blob); - - this->blob = OT::Sanitizer::sanitize (face->reference_table (_mtx_tag)); - - /* Cap num_metrics() and num_advances() based on table length. */ - unsigned int len = hb_blob_get_length (this->blob); - if (unlikely (this->num_advances * 4 > len)) - this->num_advances = len / 4; - this->num_metrics = this->num_advances + (len - 4 * this->num_advances) / 2; - - /* We MUST set num_metrics to zero if num_advances is zero. - * Our get_advance() depends on that. */ - if (unlikely (!this->num_advances)) - { - this->num_metrics = this->num_advances = 0; - hb_blob_destroy (this->blob); - this->blob = hb_blob_get_empty (); - } - this->table = OT::Sanitizer::lock_instance (this->blob); - } - - inline void fini (void) - { - hb_blob_destroy (this->blob); - } - - inline unsigned int get_advance (hb_codepoint_t glyph) const - { - if (unlikely (glyph >= this->num_metrics)) - { - /* If this->num_metrics is zero, it means we don't have the metrics table - * for this direction: return default advance. Otherwise, it means that the - * glyph index is out of bound: return zero. */ - if (this->num_metrics) - return 0; - else - return this->default_advance; - } - - if (glyph >= this->num_advances) - glyph = this->num_advances - 1; - - return this->table->longMetric[glyph].advance; - } -}; - -struct hb_ot_face_glyf_accelerator_t -{ - bool short_offset; - unsigned int num_glyphs; - const OT::loca *loca; - const OT::glyf *glyf; - hb_blob_t *loca_blob; - hb_blob_t *glyf_blob; - unsigned int glyf_len; - - inline void init (hb_face_t *face) - { - hb_blob_t *head_blob = OT::Sanitizer::sanitize (face->reference_table (HB_OT_TAG_head)); - const OT::head *head = OT::Sanitizer::lock_instance (head_blob); - if ((unsigned int) head->indexToLocFormat > 1 || head->glyphDataFormat != 0) - { - /* Unknown format. Leave num_glyphs=0, that takes care of disabling us. */ - hb_blob_destroy (head_blob); - return; - } - this->short_offset = 0 == head->indexToLocFormat; - hb_blob_destroy (head_blob); - - this->loca_blob = OT::Sanitizer::sanitize (face->reference_table (HB_OT_TAG_loca)); - this->loca = OT::Sanitizer::lock_instance (this->loca_blob); - this->glyf_blob = OT::Sanitizer::sanitize (face->reference_table (HB_OT_TAG_glyf)); - this->glyf = OT::Sanitizer::lock_instance (this->glyf_blob); - - this->num_glyphs = MAX (1u, hb_blob_get_length (this->loca_blob) / (this->short_offset ? 2 : 4)) - 1; - this->glyf_len = hb_blob_get_length (this->glyf_blob); - } - - inline void fini (void) - { - hb_blob_destroy (this->loca_blob); - hb_blob_destroy (this->glyf_blob); - } - - inline bool get_extents (hb_codepoint_t glyph, - hb_glyph_extents_t *extents) const - { - if (unlikely (glyph >= this->num_glyphs)) - return false; - - unsigned int start_offset, end_offset; - if (this->short_offset) - { - start_offset = 2 * this->loca->u.shortsZ[glyph]; - end_offset = 2 * this->loca->u.shortsZ[glyph + 1]; - } - else - { - start_offset = this->loca->u.longsZ[glyph]; - end_offset = this->loca->u.longsZ[glyph + 1]; - } - - if (start_offset > end_offset || end_offset > this->glyf_len) - return false; - - if (end_offset - start_offset < OT::glyfGlyphHeader::static_size) - return true; /* Empty glyph; zero extents. */ - - const OT::glyfGlyphHeader &glyph_header = OT::StructAtOffset (this->glyf, start_offset); - - extents->x_bearing = MIN (glyph_header.xMin, glyph_header.xMax); - extents->y_bearing = MAX (glyph_header.yMin, glyph_header.yMax); - extents->width = MAX (glyph_header.xMin, glyph_header.xMax) - extents->x_bearing; - extents->height = MIN (glyph_header.yMin, glyph_header.yMax) - extents->y_bearing; - - return true; - } -}; - -typedef bool (*hb_cmap_get_glyph_func_t) (const void *obj, - hb_codepoint_t codepoint, - hb_codepoint_t *glyph); - -template -static inline 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); -} - -struct hb_ot_face_cmap_accelerator_t -{ - hb_cmap_get_glyph_func_t get_glyph_func; - const void *get_glyph_data; - OT::CmapSubtableFormat4::accelerator_t format4_accel; - - const OT::CmapSubtableFormat14 *uvs_table; - hb_blob_t *blob; - - inline void init (hb_face_t *face) - { - this->blob = OT::Sanitizer::sanitize (face->reference_table (HB_OT_TAG_cmap)); - const OT::cmap *cmap = OT::Sanitizer::lock_instance (this->blob); - const OT::CmapSubtable *subtable = NULL; - const OT::CmapSubtableFormat14 *subtable_uvs = NULL; - - /* 32-bit subtables. */ - if (!subtable) subtable = cmap->find_subtable (3, 10); - if (!subtable) subtable = cmap->find_subtable (0, 6); - if (!subtable) subtable = cmap->find_subtable (0, 4); - /* 16-bit subtables. */ - if (!subtable) subtable = cmap->find_subtable (3, 1); - if (!subtable) subtable = cmap->find_subtable (0, 3); - if (!subtable) subtable = cmap->find_subtable (0, 2); - if (!subtable) subtable = cmap->find_subtable (0, 1); - if (!subtable) subtable = cmap->find_subtable (0, 0); - if (!subtable) subtable = cmap->find_subtable (3, 0); - /* Meh. */ - if (!subtable) subtable = &OT::Null(OT::CmapSubtable); - - /* UVS subtable. */ - if (!subtable_uvs) - { - const OT::CmapSubtable *st = cmap->find_subtable (0, 5); - if (st && st->u.format == 14) - subtable_uvs = &st->u.format14; - } - /* Meh. */ - if (!subtable_uvs) subtable_uvs = &OT::Null(OT::CmapSubtableFormat14); - - this->uvs_table = subtable_uvs; - - this->get_glyph_data = subtable; - switch (subtable->u.format) { - /* Accelerate format 4 and format 12. */ - default: this->get_glyph_func = get_glyph_from; break; - case 12: this->get_glyph_func = get_glyph_from; break; - case 4: - { - this->format4_accel.init (&subtable->u.format4); - this->get_glyph_data = &this->format4_accel; - this->get_glyph_func = this->format4_accel.get_glyph_func; - } - break; - } - } - - inline void fini (void) - { - hb_blob_destroy (this->blob); - } - - inline bool get_nominal_glyph (hb_codepoint_t unicode, - hb_codepoint_t *glyph) const - { - return this->get_glyph_func (this->get_glyph_data, unicode, glyph); - } - - inline bool get_variation_glyph (hb_codepoint_t unicode, - hb_codepoint_t variation_selector, - hb_codepoint_t *glyph) const - { - switch (this->uvs_table->get_glyph_variant (unicode, - variation_selector, - glyph)) - { - case OT::GLYPH_VARIANT_NOT_FOUND: return false; - case OT::GLYPH_VARIANT_FOUND: return true; - case OT::GLYPH_VARIANT_USE_DEFAULT: break; - } - - return get_nominal_glyph (unicode, glyph); - } -}; - -template -struct hb_lazy_loader_t -{ - inline void init (hb_face_t *face_) - { - face = face_; - instance = NULL; - } - - inline void fini (void) - { - if (instance && instance != &OT::Null(T)) - { - instance->fini(); - free (instance); - } - } - - inline const T* operator-> (void) const - { - retry: - T *p = (T *) hb_atomic_ptr_get (&instance); - if (unlikely (!p)) - { - p = (T *) calloc (1, sizeof (T)); - if (unlikely (!p)) - return &OT::Null(T); - p->init (face); - if (unlikely (!hb_atomic_ptr_cmpexch (const_cast(&instance), NULL, p))) - { - p->fini (); - goto retry; - } - } - return p; - } - - private: - hb_face_t *face; - T *instance; -}; - struct hb_ot_font_t { - hb_ot_face_cmap_accelerator_t cmap; - hb_ot_face_metrics_accelerator_t h_metrics; - hb_ot_face_metrics_accelerator_t v_metrics; - hb_lazy_loader_t glyf; + OT::cmap::accelerator_t cmap; + OT::hmtx::accelerator_t h_metrics; + OT::vmtx::accelerator_t v_metrics; + OT::hb_lazy_loader_t glyf; + OT::hb_lazy_loader_t cbdt; + OT::hb_lazy_loader_t post; + OT::hb_lazy_loader_t kern; }; @@ -357,23 +56,31 @@ _hb_ot_font_create (hb_face_t *face) hb_ot_font_t *ot_font = (hb_ot_font_t *) calloc (1, sizeof (hb_ot_font_t)); if (unlikely (!ot_font)) - return NULL; + return nullptr; ot_font->cmap.init (face); - ot_font->h_metrics.init (face, HB_OT_TAG_hhea, HB_OT_TAG_hmtx, HB_OT_TAG_os2); - ot_font->v_metrics.init (face, HB_OT_TAG_vhea, HB_OT_TAG_vmtx, HB_TAG_NONE); /* TODO Can we do this lazily? */ + ot_font->h_metrics.init (face); + ot_font->v_metrics.init (face, ot_font->h_metrics.ascender - ot_font->h_metrics.descender); /* TODO Can we do this lazily? */ ot_font->glyf.init (face); + ot_font->cbdt.init (face); + ot_font->post.init (face); + ot_font->kern.init (face); return ot_font; } static void -_hb_ot_font_destroy (hb_ot_font_t *ot_font) +_hb_ot_font_destroy (void *data) { + hb_ot_font_t *ot_font = (hb_ot_font_t *) data; + ot_font->cmap.fini (); ot_font->h_metrics.fini (); ot_font->v_metrics.fini (); ot_font->glyf.fini (); + ot_font->cbdt.fini (); + ot_font->post.fini (); + ot_font->kern.fini (); free (ot_font); } @@ -404,23 +111,34 @@ hb_ot_get_variation_glyph (hb_font_t *font HB_UNUSED, } static hb_position_t -hb_ot_get_glyph_h_advance (hb_font_t *font HB_UNUSED, +hb_ot_get_glyph_h_advance (hb_font_t *font, void *font_data, hb_codepoint_t glyph, void *user_data HB_UNUSED) { const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data; - return font->em_scale_x (ot_font->h_metrics.get_advance (glyph)); + return font->em_scale_x (ot_font->h_metrics.get_advance (glyph, font)); } static hb_position_t -hb_ot_get_glyph_v_advance (hb_font_t *font HB_UNUSED, +hb_ot_get_glyph_v_advance (hb_font_t *font, void *font_data, hb_codepoint_t glyph, void *user_data HB_UNUSED) { const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data; - return font->em_scale_y (-(int) ot_font->v_metrics.get_advance (glyph)); + return font->em_scale_y (-(int) ot_font->v_metrics.get_advance (glyph, font)); +} + +static hb_position_t +hb_ot_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_ot_font_t *ot_font = (const hb_ot_font_t *) font_data; + return font->em_scale_x (ot_font->kern->get_h_kerning (left_glyph, right_glyph)); } static hb_bool_t @@ -432,6 +150,9 @@ hb_ot_get_glyph_extents (hb_font_t *font HB_UNUSED, { const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data; bool ret = ot_font->glyf->get_extents (glyph, extents); + if (!ret) + ret = ot_font->cbdt->get_extents (glyph, extents); + // TODO Hook up side-bearings variations. 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); @@ -439,6 +160,28 @@ hb_ot_get_glyph_extents (hb_font_t *font HB_UNUSED, return ret; } +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; + return ot_font->post->get_glyph_name (glyph, name, size); +} + +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; + return ot_font->post->get_glyph_from_name (name, len, glyph); +} + static hb_bool_t hb_ot_get_font_h_extents (hb_font_t *font HB_UNUSED, void *font_data, @@ -449,7 +192,8 @@ hb_ot_get_font_h_extents (hb_font_t *font HB_UNUSED, metrics->ascender = font->em_scale_y (ot_font->h_metrics.ascender); metrics->descender = font->em_scale_y (ot_font->h_metrics.descender); metrics->line_gap = font->em_scale_y (ot_font->h_metrics.line_gap); - return true; + // TODO Hook up variations. + return ot_font->h_metrics.has_font_extents; } static hb_bool_t @@ -462,10 +206,11 @@ hb_ot_get_font_v_extents (hb_font_t *font HB_UNUSED, metrics->ascender = font->em_scale_x (ot_font->v_metrics.ascender); metrics->descender = font->em_scale_x (ot_font->v_metrics.descender); metrics->line_gap = font->em_scale_x (ot_font->v_metrics.line_gap); - return true; + // TODO Hook up variations. + return ot_font->v_metrics.has_font_extents; } -static hb_font_funcs_t *static_ot_funcs = NULL; +static hb_font_funcs_t *static_ot_funcs = nullptr; #ifdef HB_USE_ATEXIT static @@ -485,24 +230,24 @@ retry: { funcs = hb_font_funcs_create (); - hb_font_funcs_set_font_h_extents_func (funcs, hb_ot_get_font_h_extents, NULL, NULL); - hb_font_funcs_set_font_v_extents_func (funcs, hb_ot_get_font_v_extents, NULL, NULL); - hb_font_funcs_set_nominal_glyph_func (funcs, hb_ot_get_nominal_glyph, NULL, NULL); - hb_font_funcs_set_variation_glyph_func (funcs, hb_ot_get_variation_glyph, NULL, NULL); - hb_font_funcs_set_glyph_h_advance_func (funcs, hb_ot_get_glyph_h_advance, NULL, NULL); - hb_font_funcs_set_glyph_v_advance_func (funcs, hb_ot_get_glyph_v_advance, NULL, NULL); - //hb_font_funcs_set_glyph_h_origin_func (funcs, hb_ot_get_glyph_h_origin, NULL, NULL); - //hb_font_funcs_set_glyph_v_origin_func (funcs, hb_ot_get_glyph_v_origin, NULL, NULL); - //hb_font_funcs_set_glyph_h_kerning_func (funcs, hb_ot_get_glyph_h_kerning, NULL, NULL); TODO - //hb_font_funcs_set_glyph_v_kerning_func (funcs, hb_ot_get_glyph_v_kerning, NULL, NULL); - hb_font_funcs_set_glyph_extents_func (funcs, hb_ot_get_glyph_extents, NULL, NULL); - //hb_font_funcs_set_glyph_contour_point_func (funcs, hb_ot_get_glyph_contour_point, NULL, NULL); TODO - //hb_font_funcs_set_glyph_name_func (funcs, hb_ot_get_glyph_name, NULL, NULL); TODO - //hb_font_funcs_set_glyph_from_name_func (funcs, hb_ot_get_glyph_from_name, NULL, NULL); TODO + hb_font_funcs_set_font_h_extents_func (funcs, hb_ot_get_font_h_extents, nullptr, nullptr); + hb_font_funcs_set_font_v_extents_func (funcs, hb_ot_get_font_v_extents, nullptr, nullptr); + hb_font_funcs_set_nominal_glyph_func (funcs, hb_ot_get_nominal_glyph, nullptr, nullptr); + hb_font_funcs_set_variation_glyph_func (funcs, hb_ot_get_variation_glyph, nullptr, nullptr); + hb_font_funcs_set_glyph_h_advance_func (funcs, hb_ot_get_glyph_h_advance, nullptr, nullptr); + hb_font_funcs_set_glyph_v_advance_func (funcs, hb_ot_get_glyph_v_advance, nullptr, nullptr); + //hb_font_funcs_set_glyph_h_origin_func (funcs, hb_ot_get_glyph_h_origin, nullptr, nullptr); + //hb_font_funcs_set_glyph_v_origin_func (funcs, hb_ot_get_glyph_v_origin, nullptr, nullptr); + hb_font_funcs_set_glyph_h_kerning_func (funcs, hb_ot_get_glyph_h_kerning, nullptr, nullptr); + //hb_font_funcs_set_glyph_v_kerning_func (funcs, hb_ot_get_glyph_v_kerning, nullptr, nullptr); + 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); + 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); hb_font_funcs_make_immutable (funcs); - if (!hb_atomic_ptr_cmpexch (&static_ot_funcs, NULL, funcs)) { + if (!hb_atomic_ptr_cmpexch (&static_ot_funcs, nullptr, funcs)) { hb_font_funcs_destroy (funcs); goto retry; } @@ -531,5 +276,5 @@ hb_ot_font_set_funcs (hb_font_t *font) hb_font_set_funcs (font, _hb_ot_get_font_funcs (), ot_font, - (hb_destroy_func_t) _hb_ot_font_destroy); + _hb_ot_font_destroy); } diff --git a/src/hb-ot-glyf-table.hh b/src/hb-ot-glyf-table.hh index dc7aa8469..441d4b9ec 100644 --- a/src/hb-ot-glyf-table.hh +++ b/src/hb-ot-glyf-table.hh @@ -28,7 +28,10 @@ #define HB_OT_GLYF_TABLE_HH #include "hb-open-type-private.hh" - +#include "hb-ot-head-table.hh" +#include "hb-subset-glyf.hh" +#include "hb-subset-plan.hh" +#include "hb-subset-private.hh" namespace OT { @@ -42,6 +45,8 @@ namespace OT { struct loca { + friend struct glyf; + static const hb_tag_t tableTag = HB_OT_TAG_loca; inline bool sanitize (hb_sanitize_context_t *c) const @@ -50,12 +55,9 @@ struct loca return_trace (true); } - public: - union { - USHORT shortsZ[VAR]; /* Location offset divided by 2. */ - ULONG longsZ[VAR]; /* Location offset. */ - } u; - DEFINE_SIZE_ARRAY (0, u.longsZ); + protected: + HBUINT8 dataX[VAR]; /* Location data. */ + DEFINE_SIZE_ARRAY (0, dataX); }; @@ -78,26 +80,394 @@ struct glyf return_trace (true); } - public: - BYTE dataX[VAR]; /* Glyphs data. */ + inline bool subset (hb_subset_plan_t *plan) const + { + hb_blob_t *glyf_prime = nullptr; + hb_blob_t *loca_prime = nullptr; + + bool success = true; + bool use_short_loca = false; + if (hb_subset_glyf_and_loca (plan, &use_short_loca, &glyf_prime, &loca_prime)) { + success = success && hb_subset_plan_add_table (plan, HB_OT_TAG_glyf, glyf_prime); + success = success && hb_subset_plan_add_table (plan, HB_OT_TAG_loca, loca_prime); + success = success && _add_head_and_set_loca_version (plan->source, use_short_loca, plan->dest); + } else { + success = false; + } + hb_blob_destroy (loca_prime); + hb_blob_destroy (glyf_prime); + + return success; + } + + static bool + _add_head_and_set_loca_version (hb_face_t *source, bool use_short_loca, hb_face_t *dest) + { + hb_blob_t *head_blob = OT::Sanitizer().sanitize (hb_face_reference_table (source, HB_OT_TAG_head)); + 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; + + OT::head *head_prime = (OT::head *) hb_blob_get_data_writable (head_prime_blob, nullptr); + head_prime->indexToLocFormat.set (use_short_loca ? 0 : 1); + bool success = hb_subset_face_add_table (dest, HB_OT_TAG_head, head_prime_blob); + + hb_blob_destroy (head_prime_blob); + return success; + } + + struct GlyphHeader + { + 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. */ + + DEFINE_SIZE_STATIC (10); + }; + + struct CompositeGlyphHeader + { + static const uint16_t ARG_1_AND_2_ARE_WORDS = 0x0001; + static const uint16_t ARGS_ARE_XY_VALUES = 0x0002; + static const uint16_t ROUND_XY_TO_GRID = 0x0004; + static const uint16_t WE_HAVE_A_SCALE = 0x0008; + static const uint16_t MORE_COMPONENTS = 0x0020; + static const uint16_t WE_HAVE_AN_X_AND_Y_SCALE = 0x0040; + static const uint16_t WE_HAVE_A_TWO_BY_TWO = 0x0080; + static const uint16_t WE_HAVE_INSTRUCTIONS = 0x0100; + static const uint16_t USE_MY_METRICS = 0x0200; + static const uint16_t OVERLAP_COMPOUND = 0x0400; + static const uint16_t SCALED_COMPONENT_OFFSET = 0x0800; + static const uint16_t UNSCALED_COMPONENT_OFFSET = 0x1000; + + HBUINT16 flags; + HBUINT16 glyphIndex; + + inline unsigned int get_size (void) const + { + unsigned int size = min_size; + if (flags & ARG_1_AND_2_ARE_WORDS) { + // arg1 and 2 are int16 + size += 4; + } else { + // arg1 and 2 are int8 + size += 2; + } + if (flags & WE_HAVE_A_SCALE) { + // One x 16 bit (scale) + size += 2; + } else if (flags & WE_HAVE_AN_X_AND_Y_SCALE) { + // Two x 16 bit (xscale, yscale) + size += 4; + } else if (flags & WE_HAVE_A_TWO_BY_TWO) { + // Four x 16 bit (xscale, scale01, scale10, yscale) + size += 8; + } + return size; + } + + struct Iterator + { + const char *glyph_start; + const char *glyph_end; + const CompositeGlyphHeader *current; + + inline bool move_to_next () + { + if (current->flags & CompositeGlyphHeader::MORE_COMPONENTS) + { + const CompositeGlyphHeader *possible = + &StructAfter (*current); + if (!in_range (possible)) + return false; + current = possible; + return true; + } + return false; + } + + inline bool in_range (const CompositeGlyphHeader *composite) const + { + return (const char *) composite >= glyph_start + && ((const char *) composite + CompositeGlyphHeader::min_size) <= glyph_end + && ((const char *) composite + composite->get_size()) <= glyph_end; + } + }; + + static inline bool get_iterator (const char * glyph_data, + unsigned int length, + CompositeGlyphHeader::Iterator *iterator /* OUT */) + { + if (length < GlyphHeader::static_size) + return false; /* Empty glyph; zero extents. */ + + const GlyphHeader &glyph_header = StructAtOffset (glyph_data, 0); + if (glyph_header.numberOfContours < 0) + { + const CompositeGlyphHeader *possible = + &StructAfter (glyph_header); + + iterator->glyph_start = glyph_data; + iterator->glyph_end = (const char *) glyph_data + length; + if (!iterator->in_range (possible)) + return false; + iterator->current = possible; + return true; + } + + return false; + } + + DEFINE_SIZE_MIN (4); + }; + + struct accelerator_t + { + inline void init (hb_face_t *face) + { + hb_blob_t *head_blob = Sanitizer().sanitize (face->reference_table (HB_OT_TAG_head)); + const head *head_table = Sanitizer::lock_instance (head_blob); + if ((unsigned int) head_table->indexToLocFormat > 1 || head_table->glyphDataFormat != 0) + { + /* Unknown format. Leave num_glyphs=0, that takes care of disabling us. */ + hb_blob_destroy (head_blob); + return; + } + short_offset = 0 == head_table->indexToLocFormat; + hb_blob_destroy (head_blob); + + loca_blob = Sanitizer().sanitize (face->reference_table (HB_OT_TAG_loca)); + loca_table = Sanitizer::lock_instance (loca_blob); + glyf_blob = Sanitizer().sanitize (face->reference_table (HB_OT_TAG_glyf)); + glyf_table = Sanitizer::lock_instance (glyf_blob); + + num_glyphs = MAX (1u, hb_blob_get_length (loca_blob) / (short_offset ? 2 : 4)) - 1; + glyf_len = hb_blob_get_length (glyf_blob); + } + + inline void fini (void) + { + hb_blob_destroy (loca_blob); + hb_blob_destroy (glyf_blob); + } + + /* + * Returns true if the referenced glyph is a valid glyph and a composite glyph. + * If true is returned a pointer to the composite glyph will be written into + * composite. + */ + inline bool get_composite (hb_codepoint_t glyph, + CompositeGlyphHeader::Iterator *composite /* OUT */) const + { + unsigned int start_offset, end_offset; + if (!get_offsets (glyph, &start_offset, &end_offset)) + return false; /* glyph not found */ + + return CompositeGlyphHeader::get_iterator ((const char*) this->glyf_table + start_offset, + end_offset - start_offset, + composite); + } + + /* based on FontTools _g_l_y_f.py::trim */ + inline bool remove_padding(unsigned int start_offset, + unsigned int *end_offset) const + { + static const int FLAG_X_SHORT = 0x02; + static const int FLAG_Y_SHORT = 0x04; + static const int FLAG_REPEAT = 0x08; + static const int FLAG_X_SAME = 0x10; + static const int FLAG_Y_SAME = 0x20; + + if (*end_offset - start_offset < GlyphHeader::static_size) + return true; + + const char *glyph = ((const char *) glyf_table) + start_offset; + const char * const glyph_end = glyph + (*end_offset - start_offset); + const GlyphHeader &glyph_header = StructAtOffset (glyph, 0); + int16_t num_contours = (int16_t) glyph_header.numberOfContours; + + if (num_contours < 0) + /* Trimming for composites not implemented. + * If removing hints it falls out of that. */ + return true; + else if (num_contours > 0) + { + unsigned int glyph_len = *end_offset - start_offset; + /* simple glyph w/contours, possibly trimmable */ + glyph += GlyphHeader::static_size + 2 * num_contours; + + if (unlikely (glyph + 2 >= glyph_end)) return false; + uint16_t nCoordinates = (uint16_t) StructAtOffset(glyph - 2, 0) + 1; + uint16_t nInstructions = (uint16_t) StructAtOffset(glyph, 0); + + glyph += 2 + nInstructions; + if (unlikely (glyph + 2 >= glyph_end)) return false; + + unsigned int coordBytes = 0; + unsigned int coordsWithFlags = 0; + while (glyph < glyph_end) + { + uint8_t flag = (uint8_t) *glyph; + glyph++; + + unsigned int repeat = 1; + if (flag & FLAG_REPEAT) + { + if (glyph >= glyph_end) + { + DEBUG_MSG(SUBSET, nullptr, "Bad flag"); + return false; + } + repeat = ((uint8_t) *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; + + coordBytes += (xBytes + yBytes) * repeat; + coordsWithFlags += repeat; + if (coordsWithFlags >= nCoordinates) + break; + } + + if (coordsWithFlags != nCoordinates) + { + DEBUG_MSG(SUBSET, nullptr, "Expect %d coords to have flags, got flags for %d", nCoordinates, coordsWithFlags); + return false; + } + glyph += coordBytes; + + if (glyph < glyph_end) + *end_offset -= glyph_end - glyph; + } + return true; + } + + inline bool get_offsets (hb_codepoint_t glyph, + unsigned int *start_offset /* OUT */, + unsigned int *end_offset /* OUT */) const + { + if (unlikely (glyph >= num_glyphs)) + return false; + + if (short_offset) + { + const HBUINT16 *offsets = (const HBUINT16 *) loca_table->dataX; + *start_offset = 2 * offsets[glyph]; + *end_offset = 2 * offsets[glyph + 1]; + } + else + { + const HBUINT32 *offsets = (const HBUINT32 *) loca_table->dataX; + + *start_offset = offsets[glyph]; + *end_offset = offsets[glyph + 1]; + } + + if (*start_offset > *end_offset || *end_offset > glyf_len) + return false; + + return true; + } + + inline bool get_instruction_offsets(unsigned int start_offset, + unsigned int end_offset, + unsigned int *instruction_start /* OUT */, + unsigned int *instruction_end /* OUT */) const + { + if (end_offset - start_offset < GlyphHeader::static_size) + { + *instruction_start = 0; + *instruction_end = 0; + return true; /* Empty glyph; no instructions. */ + } + const GlyphHeader &glyph_header = StructAtOffset (glyf_table, start_offset); + int16_t num_contours = (int16_t) glyph_header.numberOfContours; + if (num_contours < 0) + { + CompositeGlyphHeader::Iterator *composite_it; + if (unlikely (!CompositeGlyphHeader::get_iterator ( + (const char*) this->glyf_table + start_offset, + end_offset - start_offset, composite_it))) return false; + const CompositeGlyphHeader *last; + do { + last = composite_it->current; + } while (composite_it->move_to_next()); + + if ( (uint16_t) last->flags & CompositeGlyphHeader::WE_HAVE_INSTRUCTIONS) + *instruction_start = start_offset + ((char *) last - (char *) glyf_table->dataX) + last->get_size(); + else + *instruction_start = end_offset; + *instruction_end = end_offset; + if (unlikely (*instruction_start > *instruction_end)) + { + DEBUG_MSG(SUBSET, nullptr, "Invalid instruction offset, %d is outside [%d, %d]", *instruction_start, start_offset, end_offset); + return false; + } + } + else + { + unsigned int instruction_length_offset = start_offset + GlyphHeader::static_size + 2 * num_contours; + const HBUINT16 &instruction_length = StructAtOffset (glyf_table, instruction_length_offset); + *instruction_start = instruction_length_offset + 2; + *instruction_end = *instruction_start + (uint16_t) instruction_length; + } + return true; + } + + inline bool get_extents (hb_codepoint_t glyph, + hb_glyph_extents_t *extents) const + { + unsigned int start_offset, end_offset; + if (!get_offsets (glyph, &start_offset, &end_offset)) + return false; + + if (end_offset - start_offset < GlyphHeader::static_size) + return true; /* Empty glyph; zero extents. */ + + const GlyphHeader &glyph_header = StructAtOffset (glyf_table, start_offset); + + extents->x_bearing = MIN (glyph_header.xMin, glyph_header.xMax); + extents->y_bearing = MAX (glyph_header.yMin, glyph_header.yMax); + extents->width = MAX (glyph_header.xMin, glyph_header.xMax) - extents->x_bearing; + extents->height = MIN (glyph_header.yMin, glyph_header.yMax) - extents->y_bearing; + + return true; + } + + private: + bool short_offset; + unsigned int num_glyphs; + const loca *loca_table; + const glyf *glyf_table; + hb_blob_t *loca_blob; + hb_blob_t *glyf_blob; + unsigned int glyf_len; + }; + + protected: + HBUINT8 dataX[VAR]; /* Glyphs data. */ DEFINE_SIZE_ARRAY (0, dataX); }; -struct glyfGlyphHeader -{ - SHORT 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. */ - - DEFINE_SIZE_STATIC (10); -}; - } /* namespace OT */ diff --git a/src/hb-ot-hdmx-table.hh b/src/hb-ot-hdmx-table.hh new file mode 100644 index 000000000..f08fe39d8 --- /dev/null +++ b/src/hb-ot-hdmx-table.hh @@ -0,0 +1,198 @@ +/* + * 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-private.hh" + +namespace OT { + + +/* + * hdmx - Horizontal Device Metric + */ + +#define HB_OT_TAG_hdmx HB_TAG('h','d','m','x') + +struct DeviceRecord +{ + struct SubsetView + { + const DeviceRecord *source_device_record; + hb_subset_plan_t *subset_plan; + + inline void init(const DeviceRecord *source_device_record, + hb_subset_plan_t *subset_plan) + { + this->source_device_record = source_device_record; + this->subset_plan = subset_plan; + } + + inline unsigned int len () const + { + return this->subset_plan->gids_to_retain_sorted.len; + } + + inline const HBUINT8& operator [] (unsigned int i) const + { + if (unlikely (i >= len())) return Null(HBUINT8); + hb_codepoint_t gid = this->subset_plan->gids_to_retain_sorted [i]; + return this->source_device_record->widths[gid]; + } + }; + + static inline unsigned int get_size (unsigned int count) + { + unsigned int raw_size = min_size + count * HBUINT8::static_size; + if (raw_size % 4) + /* Align to 32 bits */ + return raw_size + (4 - (raw_size % 4)); + return raw_size; + } + + inline bool serialize (hb_serialize_context_t *c, const SubsetView &subset_view) + { + TRACE_SERIALIZE (this); + + if (unlikely (!c->allocate_size (get_size (subset_view.len())))) + return_trace (false); + + this->pixel_size.set (subset_view.source_device_record->pixel_size); + this->max_width.set (subset_view.source_device_record->max_width); + + for (unsigned int i = 0; i < subset_view.len(); i++) + widths[i].set (subset_view[i]); + + return_trace (true); + } + + inline bool sanitize (hb_sanitize_context_t *c, unsigned int size_device_record) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + c->check_range (this, size_device_record))); + } + + HBUINT8 pixel_size; /* Pixel size for following widths (as ppem). */ + HBUINT8 max_width; /* Maximum width. */ + HBUINT8 widths[VAR]; /* Array of widths (numGlyphs is from the 'maxp' table). */ + public: + DEFINE_SIZE_ARRAY (2, widths); +}; + + +struct hdmx +{ + static const hb_tag_t tableTag = HB_OT_TAG_hdmx; + + inline unsigned int get_size (void) const + { + return min_size + num_records * size_device_record; + } + + inline const DeviceRecord& operator [] (unsigned int i) const + { + if (unlikely (i >= num_records)) return Null(DeviceRecord); + return StructAtOffset (this, min_size + i * size_device_record); + } + + inline bool serialize (hb_serialize_context_t *c, const hdmx *source_hdmx, hb_subset_plan_t *plan) + { + TRACE_SERIALIZE (this); + + if (unlikely (!c->extend_min ((*this)))) return_trace (false); + + this->version.set (source_hdmx->version); + this->num_records.set (source_hdmx->num_records); + this->size_device_record.set (DeviceRecord::get_size (plan->gids_to_retain_sorted.len)); + + for (unsigned int i = 0; i < source_hdmx->num_records; i++) + { + DeviceRecord::SubsetView subset_view; + subset_view.init (&(*source_hdmx)[i], plan); + + c->start_embed ()->serialize (c, subset_view); + } + + return_trace (true); + } + + static inline size_t get_subsetted_size (hb_subset_plan_t *plan) + { + return min_size + DeviceRecord::get_size (plan->gids_to_retain_sorted.len); + } + + inline bool subset (hb_subset_plan_t *plan) const + { + size_t dest_size = get_subsetted_size (plan); + hdmx *dest = (hdmx *) malloc (dest_size); + if (unlikely (!dest)) + { + DEBUG_MSG(SUBSET, nullptr, "Unable to alloc %lu for hdmx subset output.", (unsigned long) dest_size); + return false; + } + + hb_serialize_context_t c (dest, dest_size); + hdmx *hdmx_prime = c.start_serialize (); + if (!hdmx_prime || !hdmx_prime->serialize (&c, this, plan)) { + free (dest); + return false; + } + c.end_serialize (); + + hb_blob_t *hdmx_prime_blob = hb_blob_create ((const char *) dest, + dest_size, + HB_MEMORY_MODE_READONLY, + dest, + free); + bool result = hb_subset_plan_add_table (plan, HB_OT_TAG_hdmx, hdmx_prime_blob); + hb_blob_destroy (hdmx_prime_blob); + + return result; + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && version == 0 && + !_hb_unsigned_int_mul_overflows (num_records, size_device_record) && + c->check_range (this, get_size())); + } + + protected: + HBUINT16 version; /* Table version number (0) */ + HBUINT16 num_records; /* Number of device records. */ + HBUINT32 size_device_record; /* Size of a device record, 32-bit aligned. */ + HBUINT8 data[VAR]; /* Array of device records. */ + public: + DEFINE_SIZE_ARRAY (8, data); +}; + +} /* namespace OT */ + + +#endif /* HB_OT_HDMX_TABLE_HH */ diff --git a/src/hb-ot-head-table.hh b/src/hb-ot-head-table.hh index 9c3e51eb0..1d4584045 100644 --- a/src/hb-ot-head-table.hh +++ b/src/hb-ot-head-table.hh @@ -43,6 +43,8 @@ namespace OT { struct head { + friend struct OffsetTable; + static const hb_tag_t tableTag = HB_OT_TAG_head; inline unsigned int get_upem (void) const @@ -64,11 +66,11 @@ struct head FixedVersion<>version; /* Version of the head table--currently * 0x00010000u for version 1.0. */ FixedVersion<>fontRevision; /* Set by font manufacturer. */ - ULONG checkSumAdjustment; /* To compute: set it to 0, sum the - * entire font as ULONG, then store + HBUINT32 checkSumAdjustment; /* To compute: set it to 0, sum the + * entire font as HBUINT32, then store * 0xB1B0AFBAu - sum. */ - ULONG magicNumber; /* Set to 0x5F0F3CF5u. */ - USHORT flags; /* Bit 0: Baseline for font at y=0; + 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 @@ -76,7 +78,6 @@ struct head * 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. @@ -96,7 +97,6 @@ struct head * 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. @@ -114,18 +114,18 @@ struct head * encoded in the cmap subtables represent proper * support for those code points. * Bit 15: Reserved, set to 0. */ - USHORT unitsPerEm; /* Valid range is from 16 to 16384. This value + 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 */ - SHORT xMin; /* For all glyph bounding boxes. */ - SHORT yMin; /* For all glyph bounding boxes. */ - SHORT xMax; /* For all glyph bounding boxes. */ - SHORT yMax; /* For all glyph bounding boxes. */ - USHORT macStyle; /* Bit 0: Bold (if set to 1); + 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) @@ -133,16 +133,16 @@ struct head * Bit 5: Condensed (if set to 1) * Bit 6: Extended (if set to 1) * Bits 7-15: Reserved (set to 0). */ - USHORT lowestRecPPEM; /* Smallest readable size in pixels. */ - SHORT fontDirectionHint; /* Deprecated (Set to 2). + 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: - SHORT indexToLocFormat; /* 0 for short offsets, 1 for long. */ - SHORT glyphDataFormat; /* 0 for current format. */ + HBINT16 indexToLocFormat; /* 0 for short offsets, 1 for long. */ + HBINT16 glyphDataFormat; /* 0 for current format. */ DEFINE_SIZE_STATIC (54); }; diff --git a/src/hb-ot-hhea-table.hh b/src/hb-ot-hhea-table.hh index c8e9536cf..97952b4eb 100644 --- a/src/hb-ot-hhea-table.hh +++ b/src/hb-ot-hhea-table.hh @@ -41,14 +41,9 @@ namespace OT { #define HB_OT_TAG_hhea HB_TAG('h','h','e','a') #define HB_OT_TAG_vhea HB_TAG('v','h','e','a') - +template struct _hea { - static const hb_tag_t tableTag = HB_TAG('_','h','e','a'); - - static const hb_tag_t hheaTag = HB_OT_TAG_hhea; - static const hb_tag_t vheaTag = HB_OT_TAG_vhea; - inline bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -69,30 +64,30 @@ struct _hea * (xMax - xMin)) for horizontal. */ FWORD maxExtent; /* horizontal: Max(lsb + (xMax - xMin)), * vertical: minLeadingBearing+(yMax-yMin). */ - SHORT caretSlopeRise; /* Used to calculate the slope of the + HBINT16 caretSlopeRise; /* Used to calculate the slope of the * cursor (rise/run); 1 for vertical caret, * 0 for horizontal.*/ - SHORT caretSlopeRun; /* 0 for vertical caret, 1 for horizontal. */ - SHORT caretOffset; /* The amount by which a slanted + 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. */ - SHORT reserved1; /* Set to 0. */ - SHORT reserved2; /* Set to 0. */ - SHORT reserved3; /* Set to 0. */ - SHORT reserved4; /* Set to 0. */ - SHORT metricDataFormat; /* 0 for current format. */ - USHORT numberOfLongMetrics; /* Number of LongMetric entries in metric + 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 { +struct hhea : _hea { static const hb_tag_t tableTag = HB_OT_TAG_hhea; }; -struct vhea : _hea { +struct vhea : _hea { static const hb_tag_t tableTag = HB_OT_TAG_vhea; }; diff --git a/src/hb-ot-hmtx-table.hh b/src/hb-ot-hmtx-table.hh index a9606b3d2..3cd48a62e 100644 --- a/src/hb-ot-hmtx-table.hh +++ b/src/hb-ot-hmtx-table.hh @@ -21,13 +21,16 @@ * 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 + * Google Author(s): Behdad Esfahbod, Roderick Sheeter */ #ifndef HB_OT_HMTX_TABLE_HH #define HB_OT_HMTX_TABLE_HH #include "hb-open-type-private.hh" +#include "hb-ot-hhea-table.hh" +#include "hb-ot-os2-table.hh" +#include "hb-ot-var-hvar-table.hh" namespace OT { @@ -50,13 +53,9 @@ struct LongMetric DEFINE_SIZE_STATIC (4); }; -struct _mtx +template +struct hmtxvmtx { - static const hb_tag_t tableTag = HB_TAG('_','m','t','x'); - - static const hb_tag_t hmtxTag = HB_OT_TAG_hmtx; - static const hb_tag_t vmtxTag = HB_OT_TAG_vmtx; - inline bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -65,7 +64,228 @@ struct _mtx return_trace (true); } - public: + + inline bool subset_update_header (hb_subset_plan_t *plan, + unsigned int num_hmetrics) const + { + hb_blob_t *src_blob = OT::Sanitizer ().sanitize (plan->source->reference_table (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.set (num_hmetrics); + + bool result = hb_subset_plan_add_table (plan, H::tableTag, dest_blob); + hb_blob_destroy (dest_blob); + + return result; + } + + inline bool subset (hb_subset_plan_t *plan) const + { + typename T::accelerator_t _mtx; + _mtx.init (plan->source); + + /* All the trailing glyphs with the same advance can use one LongMetric + * and just keep LSB */ + hb_prealloced_array_t &gids = plan->gids_to_retain_sorted; + unsigned int num_advances = gids.len; + unsigned int last_advance = _mtx.get_advance (gids[num_advances - 1]); + while (num_advances > 1 + && last_advance == _mtx.get_advance (gids[num_advances - 2])) + { + num_advances--; + } + + /* alloc the new table */ + size_t dest_sz = num_advances * 4 + + (gids.len - num_advances) * 2; + void *dest = (void *) malloc (dest_sz); + if (unlikely (!dest)) + { + return false; + } + DEBUG_MSG(SUBSET, nullptr, "%c%c%c%c in src has %d advances, %d lsbs", HB_UNTAG(T::tableTag), _mtx.num_advances, _mtx.num_metrics - _mtx.num_advances); + DEBUG_MSG(SUBSET, nullptr, "%c%c%c%c in dest has %d advances, %d lsbs, %u bytes", HB_UNTAG(T::tableTag), num_advances, gids.len - num_advances, (unsigned int) dest_sz); + + const char *source_table = hb_blob_get_data (_mtx.blob, nullptr); + // Copy everything over + LongMetric * old_metrics = (LongMetric *) source_table; + FWORD *lsbs = (FWORD *) (old_metrics + _mtx.num_advances); + char * dest_pos = (char *) dest; + for (unsigned int i = 0; i < gids.len; i++) + { + /* the last metric or the one for gids[i] */ + LongMetric *src_metric = old_metrics + MIN ((hb_codepoint_t) _mtx.num_advances - 1, gids[i]); + if (gids[i] < _mtx.num_advances) + { + /* src is a LongMetric */ + if (i < num_advances) + { + /* dest is a LongMetric, copy it */ + *((LongMetric *) dest_pos) = *src_metric; + } + else + { + /* dest just lsb */ + *((FWORD *) dest_pos) = src_metric->lsb; + } + } + else + { + FWORD src_lsb = *(lsbs + gids[i] - _mtx.num_advances); + if (i < num_advances) + { + /* dest needs a full LongMetric */ + LongMetric *metric = (LongMetric *)dest_pos; + metric->advance = src_metric->advance; + metric->lsb = src_lsb; + } + else + { + /* dest just needs an lsb */ + *((FWORD *) dest_pos) = src_lsb; + } + } + dest_pos += (i < num_advances ? 4 : 2); + } + _mtx.fini (); + + // Amend header num hmetrics + if (unlikely (!subset_update_header (plan, num_advances))) + { + free (dest); + return false; + } + + hb_blob_t *result = hb_blob_create ((const char *)dest, + dest_sz, + HB_MEMORY_MODE_READONLY, + dest, + free); + bool success = hb_subset_plan_add_table (plan, T::tableTag, result); + hb_blob_destroy (result); + return success; + } + + struct accelerator_t + { + friend struct hmtxvmtx; + + inline void init (hb_face_t *face, + unsigned int default_advance_ = 0) + { + default_advance = default_advance_ ? default_advance_ : hb_face_get_upem (face); + + bool got_font_extents = false; + if (T::os2Tag) + { + hb_blob_t *os2_blob = Sanitizer ().sanitize (face->reference_table (T::os2Tag)); + const os2 *os2_table = Sanitizer::lock_instance (os2_blob); +#define USE_TYPO_METRICS (1u<<7) + if (0 != (os2_table->fsSelection & USE_TYPO_METRICS)) + { + ascender = os2_table->sTypoAscender; + descender = os2_table->sTypoDescender; + line_gap = os2_table->sTypoLineGap; + got_font_extents = (ascender | descender) != 0; + } + hb_blob_destroy (os2_blob); + } + + hb_blob_t *_hea_blob = Sanitizer ().sanitize (face->reference_table (H::tableTag)); + const H *_hea_table = Sanitizer::lock_instance (_hea_blob); + num_advances = _hea_table->numberOfLongMetrics; + if (!got_font_extents) + { + ascender = _hea_table->ascender; + descender = _hea_table->descender; + line_gap = _hea_table->lineGap; + got_font_extents = (ascender | descender) != 0; + } + hb_blob_destroy (_hea_blob); + + has_font_extents = got_font_extents; + + blob = Sanitizer ().sanitize (face->reference_table (T::tableTag)); + + /* Cap num_metrics() and num_advances() based on table length. */ + unsigned int len = hb_blob_get_length (blob); + if (unlikely (num_advances * 4 > len)) + num_advances = len / 4; + num_metrics = num_advances + (len - 4 * num_advances) / 2; + + /* We MUST set num_metrics to zero if num_advances is zero. + * Our get_advance() depends on that. */ + if (unlikely (!num_advances)) + { + num_metrics = num_advances = 0; + hb_blob_destroy (blob); + blob = hb_blob_get_empty (); + } + table = Sanitizer::lock_instance (blob); + + var_blob = Sanitizer ().sanitize (face->reference_table (T::variationsTag)); + var_table = Sanitizer::lock_instance (var_blob); + } + + inline void fini (void) + { + hb_blob_destroy (blob); + hb_blob_destroy (var_blob); + } + + inline unsigned int get_advance (hb_codepoint_t glyph) const + { + if (unlikely (glyph >= num_metrics)) + { + /* If num_metrics is zero, it means we don't have the metrics table + * for this direction: return default advance. Otherwise, it means that the + * glyph index is out of bound: return zero. */ + if (num_metrics) + return 0; + else + return default_advance; + } + + return table->longMetric[MIN (glyph, (uint32_t) num_advances - 1)].advance; + } + + inline unsigned int get_advance (hb_codepoint_t glyph, + hb_font_t *font) const + { + unsigned int advance = get_advance (glyph); + if (likely(glyph < num_metrics)) + { + advance += (font->num_coords ? var_table->get_advance_var (glyph, font->coords, font->num_coords) : 0); // TODO Optimize?! + } + return advance; + } + + public: + bool has_font_extents; + unsigned short ascender; + unsigned short descender; + unsigned short line_gap; + + protected: + unsigned int num_metrics; + unsigned int num_advances; + unsigned int default_advance; + + private: + const hmtxvmtx *table; + hb_blob_t *blob; + const HVARVVAR *var_table; + hb_blob_t *var_blob; + }; + + protected: LongMetric longMetric[VAR]; /* Paired advance width and leading * bearing values for each glyph. The * value numOfHMetrics comes from @@ -74,7 +294,7 @@ struct _mtx * be in the array, but that entry is * required. The last entry applies to * all subsequent glyphs. */ - FWORD leadingBearingX[VAR]; /* Here the advance is assumed +/*FWORD leadingBearingX[VAR];*/ /* 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 @@ -88,14 +308,18 @@ struct _mtx * font to vary the side bearing * values for each glyph. */ public: - DEFINE_SIZE_ARRAY2 (0, longMetric, leadingBearingX); + DEFINE_SIZE_ARRAY (0, longMetric); }; -struct hmtx : _mtx { +struct hmtx : hmtxvmtx { static const hb_tag_t tableTag = HB_OT_TAG_hmtx; + static const hb_tag_t variationsTag = HB_OT_TAG_HVAR; + static const hb_tag_t os2Tag = HB_OT_TAG_os2; }; -struct vmtx : _mtx { +struct vmtx : hmtxvmtx { static const hb_tag_t tableTag = HB_OT_TAG_vmtx; + static const hb_tag_t variationsTag = HB_OT_TAG_VVAR; + static const hb_tag_t os2Tag = HB_TAG_NONE; }; } /* namespace OT */ diff --git a/src/hb-ot-kern-table.hh b/src/hb-ot-kern-table.hh new file mode 100644 index 000000000..368f547a6 --- /dev/null +++ b/src/hb-ot-kern-table.hh @@ -0,0 +1,394 @@ +/* + * 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-open-type-private.hh" + +namespace OT { + + +/* + * kern -- Kerning + */ + +#define HB_OT_TAG_kern HB_TAG('k','e','r','n') + +struct hb_glyph_pair_t +{ + hb_codepoint_t left; + hb_codepoint_t right; +}; + +struct KernPair +{ + inline int get_kerning (void) const + { return value; } + + inline int cmp (const hb_glyph_pair_t &o) const + { + int ret = left.cmp (o.left); + if (ret) return ret; + return right.cmp (o.right); + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + protected: + GlyphID left; + GlyphID right; + FWORD value; + public: + DEFINE_SIZE_STATIC (6); +}; + +struct KernSubTableFormat0 +{ + inline int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const + { + hb_glyph_pair_t pair = {left, right}; + int i = pairs.bsearch (pair); + if (i == -1) + return 0; + return pairs[i].get_kerning (); + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (pairs.sanitize (c)); + } + + protected: + BinSearchArrayOf pairs; /* Array of kerning pairs. */ + public: + DEFINE_SIZE_ARRAY (8, pairs); +}; + +struct KernClassTable +{ + inline unsigned int get_class (hb_codepoint_t g) const { return classes[g - firstGlyph]; } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (firstGlyph.sanitize (c) && classes.sanitize (c)); + } + + protected: + HBUINT16 firstGlyph; /* First glyph in class range. */ + ArrayOf classes; /* Glyph classes. */ + public: + DEFINE_SIZE_ARRAY (4, classes); +}; + +struct KernSubTableFormat2 +{ + inline int get_kerning (hb_codepoint_t left, hb_codepoint_t right, const char *end) const + { + unsigned int l = (this+leftClassTable).get_class (left); + unsigned int r = (this+rightClassTable).get_class (right); + unsigned int offset = l * rowWidth + r * sizeof (FWORD); + const FWORD *arr = &(this+array); + if (unlikely ((const void *) arr < (const void *) this || (const void *) arr >= (const void *) end)) + return 0; + const FWORD *v = &StructAtOffset (arr, offset); + if (unlikely ((const void *) v < (const void *) arr || (const void *) (v + 1) > (const void *) end)) + return 0; + return *v; + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (rowWidth.sanitize (c) && + leftClassTable.sanitize (c, this) && + rightClassTable.sanitize (c, this) && + array.sanitize (c, this)); + } + + protected: + HBUINT16 rowWidth; /* The width, in bytes, of a row in the table. */ + OffsetTo + leftClassTable; /* Offset from beginning of this subtable to + * left-hand class table. */ + OffsetTo + rightClassTable;/* Offset from beginning of this subtable to + * right-hand class table. */ + OffsetTo + array; /* Offset from beginning of this subtable to + * the start of the kerning array. */ + public: + DEFINE_SIZE_MIN (8); +}; + +struct KernSubTable +{ + inline int get_kerning (hb_codepoint_t left, hb_codepoint_t right, const char *end, unsigned int format) const + { + switch (format) { + case 0: return u.format0.get_kerning (left, right); + case 2: return u.format2.get_kerning (left, right, end); + default:return 0; + } + } + + inline bool sanitize (hb_sanitize_context_t *c, unsigned int format) const + { + TRACE_SANITIZE (this); + switch (format) { + case 0: return_trace (u.format0.sanitize (c)); + case 2: return_trace (u.format2.sanitize (c)); + default:return_trace (true); + } + } + + protected: + union { + KernSubTableFormat0 format0; + KernSubTableFormat2 format2; + } u; + public: + DEFINE_SIZE_MIN (0); +}; + + +template +struct KernSubTableWrapper +{ + /* https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern */ + inline const T* thiz (void) const { return static_cast (this); } + + inline bool is_horizontal (void) const + { return (thiz()->coverage & T::COVERAGE_CHECK_FLAGS) == T::COVERAGE_CHECK_HORIZONTAL; } + + inline bool is_override (void) const + { return bool (thiz()->coverage & T::COVERAGE_OVERRIDE_FLAG); } + + inline int get_kerning (hb_codepoint_t left, hb_codepoint_t right, const char *end) const + { return thiz()->subtable.get_kerning (left, right, end, thiz()->format); } + + inline int get_h_kerning (hb_codepoint_t left, hb_codepoint_t right, const char *end) const + { return is_horizontal () ? get_kerning (left, right, end) : 0; } + + inline unsigned int get_size (void) const { return thiz()->length; } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (thiz()) && + thiz()->length >= thiz()->min_size && + c->check_array (thiz(), 1, thiz()->length) && + thiz()->subtable.sanitize (c, thiz()->format)); + } +}; + +template +struct KernTable +{ + /* https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern */ + inline const T* thiz (void) const { return static_cast (this); } + + inline int get_h_kerning (hb_codepoint_t left, hb_codepoint_t right, unsigned int table_length) const + { + int v = 0; + const typename T::SubTableWrapper *st = CastP (thiz()->data); + unsigned int count = thiz()->nTables; + for (unsigned int i = 0; i < count; i++) + { + if (st->is_override ()) + v = 0; + v += st->get_h_kerning (left, right, table_length + (const char *) this); + st = &StructAfter (*st); + } + return v; + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (unlikely (!c->check_struct (thiz()) || + thiz()->version != T::VERSION)) + return_trace (false); + + const typename T::SubTableWrapper *st = CastP (thiz()->data); + unsigned int count = thiz()->nTables; + for (unsigned int i = 0; i < count; i++) + { + if (unlikely (!st->sanitize (c))) + return_trace (false); + st = &StructAfter (*st); + } + + return_trace (true); + } +}; + +struct KernOT : KernTable +{ + friend struct KernTable; + + static const uint16_t VERSION = 0x0000u; + + struct SubTableWrapper : KernSubTableWrapper + { + friend struct KernSubTableWrapper; + + enum coverage_flags_t { + COVERAGE_DIRECTION_FLAG = 0x01u, + COVERAGE_MINIMUM_FLAG = 0x02u, + COVERAGE_CROSSSTREAM_FLAG = 0x04u, + COVERAGE_OVERRIDE_FLAG = 0x08u, + + COVERAGE_VARIATION_FLAG = 0x00u, /* Not supported. */ + + COVERAGE_CHECK_FLAGS = 0x07u, + COVERAGE_CHECK_HORIZONTAL = 0x01u + }; + + protected: + HBUINT16 versionZ; /* Unused. */ + HBUINT16 length; /* Length of the subtable (including this header). */ + HBUINT8 format; /* Subtable format. */ + HBUINT8 coverage; /* Coverage bits. */ + KernSubTable subtable; /* Subtable data. */ + public: + DEFINE_SIZE_MIN (6); + }; + + protected: + HBUINT16 version; /* Version--0x0000u */ + HBUINT16 nTables; /* Number of subtables in the kerning table. */ + HBUINT8 data[VAR]; + public: + DEFINE_SIZE_ARRAY (4, data); +}; + +struct KernAAT : KernTable +{ + friend struct KernTable; + + static const uint32_t VERSION = 0x00010000u; + + struct SubTableWrapper : KernSubTableWrapper + { + friend struct KernSubTableWrapper; + + enum coverage_flags_t { + COVERAGE_DIRECTION_FLAG = 0x80u, + COVERAGE_CROSSSTREAM_FLAG = 0x40u, + COVERAGE_VARIATION_FLAG = 0x20u, + + COVERAGE_OVERRIDE_FLAG = 0x00u, /* Not supported. */ + + COVERAGE_CHECK_FLAGS = 0xE0u, + COVERAGE_CHECK_HORIZONTAL = 0x00u + }; + + protected: + 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. */ + KernSubTable subtable; /* Subtable data. */ + public: + DEFINE_SIZE_MIN (8); + }; + + protected: + HBUINT32 version; /* Version--0x00010000u */ + HBUINT32 nTables; /* Number of subtables in the kerning table. */ + HBUINT8 data[VAR]; + public: + DEFINE_SIZE_ARRAY (8, data); +}; + +struct kern +{ + static const hb_tag_t tableTag = HB_OT_TAG_kern; + + inline int get_h_kerning (hb_codepoint_t left, hb_codepoint_t right, unsigned int table_length) const + { + switch (u.major) { + case 0: return u.ot.get_h_kerning (left, right, table_length); + case 1: return u.aat.get_h_kerning (left, right, table_length); + default:return 0; + } + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (!u.major.sanitize (c)) return_trace (false); + switch (u.major) { + case 0: return_trace (u.ot.sanitize (c)); + case 1: return_trace (u.aat.sanitize (c)); + default:return_trace (true); + } + } + + struct accelerator_t + { + inline void init (hb_face_t *face) + { + blob = Sanitizer().sanitize (face->reference_table (HB_OT_TAG_kern)); + table = Sanitizer::lock_instance (blob); + table_length = hb_blob_get_length (blob); + } + inline void fini (void) + { + hb_blob_destroy (blob); + } + + inline int get_h_kerning (hb_codepoint_t left, hb_codepoint_t right) const + { return table->get_h_kerning (left, right, table_length); } + + private: + hb_blob_t *blob; + const kern *table; + unsigned int table_length; + }; + + protected: + union { + HBUINT16 major; + KernOT ot; + KernAAT aat; + } u; + public: + DEFINE_SIZE_UNION (2, major); +}; + +} /* namespace OT */ + + +#endif /* HB_OT_KERN_TABLE_HH */ diff --git a/src/hb-ot-layout-base-table.hh b/src/hb-ot-layout-base-table.hh new file mode 100644 index 000000000..3c3d3ade3 --- /dev/null +++ b/src/hb-ot-layout-base-table.hh @@ -0,0 +1,799 @@ +/* + * Copyright © 2016 Elie Roux + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE 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_LAYOUT_BASE_TABLE_HH +#define HB_OT_LAYOUT_BASE_TABLE_HH + +#include "hb-open-type-private.hh" +#include "hb-ot-layout-common-private.hh" +#include "hb-ot-base.h" + +namespace OT { + +#define NO_COORD Null(HBINT16)//HBINT16((short int) -32767) + +#define NOT_INDEXED ((unsigned int) -1) + +/* + * BASE -- The BASE Table + */ + +struct BaseCoordFormat1 { + + inline HBINT16 get_coord (void) const + { return coordinate; } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + protected: + HBUINT16 baseCoordFormat; + HBINT16 coordinate; + + public: + DEFINE_SIZE_STATIC (4); +}; + +struct BaseCoordFormat2 { + + inline HBINT16 get_coord (void) const + { return coordinate; } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + protected: + HBUINT16 baseCoordFormat; + HBINT16 coordinate; + HBUINT16 referenceGlyph; + HBUINT16 baseCoordPoint; + + public: + DEFINE_SIZE_STATIC (8); +}; + +struct BaseCoordFormat3 { + + inline HBINT16 get_coord (void) const + { return coordinate; } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && deviceTable.sanitize (c, this)); + } + + protected: + HBUINT16 baseCoordFormat; + HBINT16 coordinate; + OffsetTo deviceTable; + + public: + DEFINE_SIZE_STATIC (6); +}; + +struct BaseCoord { + + inline HBINT16 get_coord (void) const + { return u.format1.get_coord(); } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (!u.baseCoordFormat.sanitize (c)) return_trace (false); + switch (u.baseCoordFormat) { + 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 baseCoordFormat; + BaseCoordFormat1 format1; + BaseCoordFormat2 format2; + BaseCoordFormat3 format3; + } u; + + public: + DEFINE_SIZE_MIN (4); +}; + +struct FeatMinMaxRecord { + + inline HBINT16 get_min_value (void) const + { + if (minCoord == Null(OffsetTo)) return NO_COORD; + return (this+minCoord).get_coord(); + } + + inline HBINT16 get_max_value (void) const + { + if (minCoord == Null(OffsetTo)) return NO_COORD; + return (this+maxCoord).get_coord(); + } + + inline Tag get_tag () const + { return featureTableTag; } + + inline bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + minCoord.sanitize (c, base) && + maxCoord.sanitize (c, base)); + } + + protected: + Tag featureTableTag; + OffsetTo minCoord; + OffsetTo maxCoord; + + public: + DEFINE_SIZE_STATIC (8); + +}; + +struct MinMax { + + inline unsigned int get_feature_tag_index (Tag featureTableTag) const + { + Tag tag; + int cmp; + // taking advantage of alphabetical order + for (unsigned int i = 0; i < featMinMaxCount; i++) { + tag = featMinMaxRecords[i].get_tag(); + cmp = tag.cmp(featureTableTag); + if (cmp == 0) return i; + if (cmp > 0) return NOT_INDEXED; + } + return NOT_INDEXED; + } + + inline HBINT16 get_min_value (unsigned int featureTableTagIndex) const + { + if (featureTableTagIndex == NOT_INDEXED) { + if (minCoord == Null(OffsetTo)) return NO_COORD; + return (this+minCoord).get_coord(); + } + if (unlikely(featureTableTagIndex >= featMinMaxCount)) return NO_COORD; + return featMinMaxRecords[featureTableTagIndex].get_min_value(); + } + + inline HBINT16 get_max_value (unsigned int featureTableTagIndex) const + { + if (featureTableTagIndex == NOT_INDEXED) { + if (minCoord == Null(OffsetTo)) return NO_COORD; + return (this+maxCoord).get_coord(); + } + if (unlikely(featureTableTagIndex >= featMinMaxCount)) return NO_COORD; + return featMinMaxRecords[featureTableTagIndex].get_max_value(); + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + minCoord.sanitize (c, this) && + maxCoord.sanitize (c, this) && + featMinMaxRecords.sanitize (c, this)); + // TODO: test alphabetical order? + } + + protected: + OffsetTo minCoord; + OffsetTo maxCoord; + HBUINT16 featMinMaxCount; + ArrayOf featMinMaxRecords; + + public: + DEFINE_SIZE_ARRAY (8, featMinMaxRecords); + +}; + +struct BaseLangSysRecord +{ + inline Tag get_tag(void) const + { return baseLangSysTag; } + + inline unsigned int get_feature_tag_index (Tag featureTableTag) const + { return (this+minMax).get_feature_tag_index(featureTableTag); } + + inline HBINT16 get_min_value (unsigned int featureTableTagIndex) const + { return (this+minMax).get_min_value(featureTableTagIndex); } + + inline HBINT16 get_max_value (unsigned int featureTableTagIndex) const + { return (this+minMax).get_max_value(featureTableTagIndex); } + + inline bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + minMax != Null(OffsetTo) && + minMax.sanitize (c, base)); + } + + protected: + Tag baseLangSysTag; + OffsetTo minMax; + public: + DEFINE_SIZE_STATIC (6); + +}; + +struct BaseValues +{ + inline unsigned int get_default_base_tag_index (void) const + { return defaultIndex; } + + inline HBINT16 get_base_coord (unsigned int baselineTagIndex) const + { + if (unlikely(baselineTagIndex >= baseCoordCount)) return NO_COORD; + return (this+baseCoords[baselineTagIndex]).get_coord(); + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + defaultIndex <= baseCoordCount && + baseCoords.sanitize (c, this)); + } + + protected: + Index defaultIndex; + HBUINT16 baseCoordCount; + OffsetArrayOf baseCoords; + public: + DEFINE_SIZE_ARRAY (6, baseCoords); + +}; + +struct BaseScript { + + inline unsigned int get_lang_tag_index (Tag baseLangSysTag) const + { + Tag tag; + int cmp; + for (unsigned int i = 0; i < baseLangSysCount; i++) { + tag = baseLangSysRecords[i].get_tag(); + // taking advantage of alphabetical order + cmp = tag.cmp(baseLangSysTag); + if (cmp == 0) return i; + if (cmp > 0) return NOT_INDEXED; + } + return NOT_INDEXED; + } + + inline unsigned int get_feature_tag_index (unsigned int baseLangSysIndex, Tag featureTableTag) const + { + if (baseLangSysIndex == NOT_INDEXED) { + if (unlikely(defaultMinMax)) return NOT_INDEXED; + return (this+defaultMinMax).get_feature_tag_index(featureTableTag); + } + if (unlikely(baseLangSysIndex >= baseLangSysCount)) return NOT_INDEXED; + return baseLangSysRecords[baseLangSysIndex].get_feature_tag_index(featureTableTag); + } + + inline HBINT16 get_min_value (unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const + { + if (baseLangSysIndex == NOT_INDEXED) { + if (unlikely(defaultMinMax == Null(OffsetTo))) return NO_COORD; + return (this+defaultMinMax).get_min_value(featureTableTagIndex); + } + if (unlikely(baseLangSysIndex >= baseLangSysCount)) return NO_COORD; + return baseLangSysRecords[baseLangSysIndex].get_max_value(featureTableTagIndex); + } + + inline HBINT16 get_max_value (unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const + { + if (baseLangSysIndex == NOT_INDEXED) { + if (unlikely(defaultMinMax == Null(OffsetTo))) return NO_COORD; + return (this+defaultMinMax).get_min_value(featureTableTagIndex); + } + if (unlikely(baseLangSysIndex >= baseLangSysCount)) return NO_COORD; + return baseLangSysRecords[baseLangSysIndex].get_max_value(featureTableTagIndex); + } + + inline unsigned int get_default_base_tag_index (void) const + { return (this+baseValues).get_default_base_tag_index(); } + + inline HBINT16 get_base_coord (unsigned int baselineTagIndex) const + { return (this+baseValues).get_base_coord(baselineTagIndex); } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + baseValues.sanitize (c, this) && + defaultMinMax.sanitize (c, this) && + baseLangSysRecords.sanitize (c, this)); + } + + protected: + OffsetTo baseValues; + OffsetTo defaultMinMax; + HBUINT16 baseLangSysCount; + ArrayOf baseLangSysRecords; + + public: + DEFINE_SIZE_ARRAY (8, baseLangSysRecords); +}; + + +struct BaseScriptRecord { + + inline bool get_tag (void) const + { return baseScriptTag; } + + inline unsigned int get_default_base_tag_index(void) const + { return (this+baseScript).get_default_base_tag_index(); } + + inline HBINT16 get_base_coord(unsigned int baselineTagIndex) const + { return (this+baseScript).get_base_coord(baselineTagIndex); } + + inline unsigned int get_lang_tag_index (Tag baseLangSysTag) const + { return (this+baseScript).get_lang_tag_index(baseLangSysTag); } + + inline unsigned int get_feature_tag_index (unsigned int baseLangSysIndex, Tag featureTableTag) const + { return (this+baseScript).get_feature_tag_index(baseLangSysIndex, featureTableTag); } + + inline HBINT16 get_max_value (unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const + { return (this+baseScript).get_max_value(baseLangSysIndex, featureTableTagIndex); } + + inline HBINT16 get_min_value (unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const + { return (this+baseScript).get_min_value(baseLangSysIndex, featureTableTagIndex); } + + inline bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + baseScript != Null(OffsetTo) && + baseScript.sanitize (c, base)); + } + + protected: + Tag baseScriptTag; + OffsetTo baseScript; + + public: + DEFINE_SIZE_STATIC (6); +}; + +struct BaseScriptList { + + inline unsigned int get_base_script_index (Tag baseScriptTag) const + { + for (unsigned int i = 0; i < baseScriptCount; i++) + if (baseScriptRecords[i].get_tag() == baseScriptTag) + return i; + return NOT_INDEXED; + } + + inline unsigned int get_default_base_tag_index (unsigned int baseScriptIndex) const + { + if (unlikely(baseScriptIndex >= baseScriptCount)) return NOT_INDEXED; + return baseScriptRecords[baseScriptIndex].get_default_base_tag_index(); + } + + inline HBINT16 get_base_coord(unsigned int baseScriptIndex, unsigned int baselineTagIndex) const + { + if (unlikely(baseScriptIndex >= baseScriptCount)) return NO_COORD; + return baseScriptRecords[baseScriptIndex].get_base_coord(baselineTagIndex); + } + + inline unsigned int get_lang_tag_index (unsigned int baseScriptIndex, Tag baseLangSysTag) const + { + if (unlikely(baseScriptIndex >= baseScriptCount)) return NOT_INDEXED; + return baseScriptRecords[baseScriptIndex].get_lang_tag_index(baseLangSysTag); + } + + inline unsigned int get_feature_tag_index (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, Tag featureTableTag) const + { + if (unlikely(baseScriptIndex >= baseScriptCount)) return NOT_INDEXED; + return baseScriptRecords[baseScriptIndex].get_feature_tag_index(baseLangSysIndex, featureTableTag); + } + + inline HBINT16 get_max_value (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const + { + if (unlikely(baseScriptIndex >= baseScriptCount)) return NO_COORD; + return baseScriptRecords[baseScriptIndex].get_max_value(baseLangSysIndex, featureTableTagIndex); + } + + inline HBINT16 get_min_value (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const + { + if (unlikely(baseScriptIndex >= baseScriptCount)) return NO_COORD; + return baseScriptRecords[baseScriptIndex].get_min_value(baseLangSysIndex, featureTableTagIndex); + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + baseScriptRecords.sanitize (c, this)); + } + + protected: + HBUINT16 baseScriptCount; + ArrayOf baseScriptRecords; + + public: + DEFINE_SIZE_ARRAY (4, baseScriptRecords); + +}; + +struct BaseTagList +{ + + inline unsigned int get_tag_index(Tag baselineTag) const + { + for (unsigned int i = 0; i < baseTagCount; i++) + if (baselineTags[i] == baselineTag) + return i; + return NOT_INDEXED; + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + protected: + HBUINT16 baseTagCount; + SortedArrayOf baselineTags; + + public: + DEFINE_SIZE_ARRAY (4, baselineTags); +}; + +struct Axis +{ + + inline unsigned int get_base_tag_index(Tag baselineTag) const + { + if (unlikely(baseTagList == Null(OffsetTo))) return NOT_INDEXED; + return (this+baseTagList).get_tag_index(baselineTag); + } + + inline unsigned int get_default_base_tag_index_for_script_index (unsigned int baseScriptIndex) const + { + if (unlikely(baseScriptList == Null(OffsetTo))) return NOT_INDEXED; + return (this+baseScriptList).get_default_base_tag_index(baseScriptIndex); + } + + inline HBINT16 get_base_coord(unsigned int baseScriptIndex, unsigned int baselineTagIndex) const + { + if (unlikely(baseScriptList == Null(OffsetTo))) return NO_COORD; + return (this+baseScriptList).get_base_coord(baseScriptIndex, baselineTagIndex); + } + + inline unsigned int get_lang_tag_index (unsigned int baseScriptIndex, Tag baseLangSysTag) const + { + if (unlikely(baseScriptList == Null(OffsetTo))) return NOT_INDEXED; + return (this+baseScriptList).get_lang_tag_index(baseScriptIndex, baseLangSysTag); + } + + inline unsigned int get_feature_tag_index (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, Tag featureTableTag) const + { + if (unlikely(baseScriptList == Null(OffsetTo))) return NOT_INDEXED; + return (this+baseScriptList).get_feature_tag_index(baseScriptIndex, baseLangSysIndex, featureTableTag); + } + + inline HBINT16 get_max_value (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const + { + if (unlikely(baseScriptList == Null(OffsetTo))) return NO_COORD; + return (this+baseScriptList).get_max_value(baseScriptIndex, baseLangSysIndex, featureTableTagIndex); + } + + inline HBINT16 get_min_value (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const + { + if (unlikely(baseScriptList == Null(OffsetTo))) return NO_COORD; + return (this+baseScriptList).get_min_value(baseScriptIndex, baseLangSysIndex, featureTableTagIndex); + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + baseTagList.sanitize (c, this) && + baseScriptList.sanitize (c, this)); + } + + protected: + OffsetTo baseTagList; + OffsetTo baseScriptList; + + public: + DEFINE_SIZE_STATIC (4); +}; + +struct BASEFormat1_1 +{ + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + horizAxis.sanitize (c, this) && + vertAxis.sanitize (c, this) && + itemVarStore.sanitize (c, this)); + } + + protected: + FixedVersion<> version; + OffsetTo horizAxis; + OffsetTo vertAxis; + LOffsetTo itemVarStore; + + public: + DEFINE_SIZE_STATIC (12); +}; + +struct BASEFormat1_0 +{ + + inline bool has_vert_axis(void) + { return vertAxis != Null(OffsetTo); } + + inline bool has_horiz_axis(void) + { return horizAxis != Null(OffsetTo); } + + // horizontal axis base coords: + + inline unsigned int get_horiz_base_tag_index(Tag baselineTag) const + { + if (unlikely(horizAxis == Null(OffsetTo))) return NOT_INDEXED; + return (this+horizAxis).get_base_tag_index(baselineTag); + } + + inline unsigned int get_horiz_default_base_tag_index_for_script_index (unsigned int baseScriptIndex) const + { + if (unlikely(horizAxis == Null(OffsetTo))) return NOT_INDEXED; + return (this+horizAxis).get_default_base_tag_index_for_script_index(baseScriptIndex); + } + + inline HBINT16 get_horiz_base_coord(unsigned int baseScriptIndex, unsigned int baselineTagIndex) const + { + if (unlikely(horizAxis == Null(OffsetTo))) return NO_COORD; + return (this+horizAxis).get_base_coord(baseScriptIndex, baselineTagIndex); + } + + // vertical axis base coords: + + inline unsigned int get_vert_base_tag_index(Tag baselineTag) const + { + if (unlikely(vertAxis == Null(OffsetTo))) return NOT_INDEXED; + return (this+vertAxis).get_base_tag_index(baselineTag); + } + + inline unsigned int get_vert_default_base_tag_index_for_script_index (unsigned int baseScriptIndex) const + { + if (unlikely(vertAxis == Null(OffsetTo))) return NOT_INDEXED; + return (this+vertAxis).get_default_base_tag_index_for_script_index(baseScriptIndex); + } + + inline HBINT16 get_vert_base_coord(unsigned int baseScriptIndex, unsigned int baselineTagIndex) const + { + if (vertAxis == Null(OffsetTo)) return NO_COORD; + return (this+vertAxis).get_base_coord(baseScriptIndex, baselineTagIndex); + } + + // horizontal axis min/max coords: + + inline unsigned int get_horiz_lang_tag_index (unsigned int baseScriptIndex, Tag baseLangSysTag) const + { + if (unlikely(horizAxis == Null(OffsetTo))) return NOT_INDEXED; + return (this+horizAxis).get_lang_tag_index (baseScriptIndex, baseLangSysTag); + } + + inline unsigned int get_horiz_feature_tag_index (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, Tag featureTableTag) const + { + if (unlikely(horizAxis == Null(OffsetTo))) return NOT_INDEXED; + return (this+horizAxis).get_feature_tag_index (baseScriptIndex, baseLangSysIndex, featureTableTag); + } + + inline HBINT16 get_horiz_max_value (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const + { + if (unlikely(horizAxis == Null(OffsetTo))) return NO_COORD; + return (this+horizAxis).get_max_value (baseScriptIndex, baseLangSysIndex, featureTableTagIndex); + } + + inline HBINT16 get_horiz_min_value (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const + { + if (unlikely(horizAxis == Null(OffsetTo))) return NO_COORD; + return (this+horizAxis).get_min_value (baseScriptIndex, baseLangSysIndex, featureTableTagIndex); + } + + // vertical axis min/max coords: + + inline unsigned int get_vert_lang_tag_index (unsigned int baseScriptIndex, Tag baseLangSysTag) const + { + if (unlikely(vertAxis == Null(OffsetTo))) return NOT_INDEXED; + return (this+vertAxis).get_lang_tag_index (baseScriptIndex, baseLangSysTag); + } + + inline unsigned int get_vert_feature_tag_index (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, Tag featureTableTag) const + { + if (unlikely(vertAxis == Null(OffsetTo))) return NOT_INDEXED; + return (this+vertAxis).get_feature_tag_index (baseScriptIndex, baseLangSysIndex, featureTableTag); + } + + inline HBINT16 get_vert_max_value (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const + { + if (unlikely(vertAxis == Null(OffsetTo))) return NO_COORD; + return (this+vertAxis).get_max_value (baseScriptIndex, baseLangSysIndex, featureTableTagIndex); + } + + inline HBINT16 get_vert_min_value (unsigned int baseScriptIndex, unsigned int baseLangSysIndex, unsigned int featureTableTagIndex) const + { + if (unlikely(vertAxis == Null(OffsetTo))) return NO_COORD; + return (this+vertAxis).get_min_value (baseScriptIndex, baseLangSysIndex, featureTableTagIndex); + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + horizAxis.sanitize (c, this) && + vertAxis.sanitize (c, this)); + } + + protected: + FixedVersion<> version; + OffsetTo horizAxis; + OffsetTo vertAxis; + + public: + DEFINE_SIZE_STATIC (8); +}; + +struct BASE +{ + static const hb_tag_t tableTag = HB_OT_TAG_BASE; + + inline bool has_vert_axis(void) + { return u.format1_0.has_vert_axis(); } + + inline bool has_horiz_axis(void) + { return u.format1_0.has_horiz_axis(); } + + inline unsigned int get_horiz_base_tag_index(Tag baselineTag) const + { return u.format1_0.get_horiz_base_tag_index(baselineTag); } + + inline unsigned int get_horiz_default_base_tag_index_for_script_index (unsigned int baseScriptIndex) const + { return u.format1_0.get_horiz_default_base_tag_index_for_script_index(baseScriptIndex); } + + inline HBINT16 get_horiz_base_coord(unsigned int baseScriptIndex, unsigned int baselineTagIndex) const + { return u.format1_0.get_horiz_base_coord(baseScriptIndex, baselineTagIndex); } + + inline unsigned int get_vert_base_tag_index(Tag baselineTag) const + { return u.format1_0.get_vert_base_tag_index(baselineTag); } + + inline unsigned int get_vert_default_base_tag_index_for_script_index (unsigned int baseScriptIndex) const + { return u.format1_0.get_vert_default_base_tag_index_for_script_index(baseScriptIndex); } + + inline HBINT16 get_vert_base_coord(unsigned int baseScriptIndex, unsigned int baselineTagIndex) const + { return u.format1_0.get_vert_base_coord(baseScriptIndex, baselineTagIndex); } + + inline unsigned int get_horiz_lang_tag_index (unsigned int baseScriptIndex, Tag baseLangSysTag) const + { return u.format1_0.get_horiz_lang_tag_index(baseScriptIndex, baseLangSysTag); } + + inline unsigned int get_horiz_feature_tag_index (unsigned int baseScriptIndex, + unsigned int baseLangSysIndex, + Tag featureTableTag) const + { + return u.format1_0.get_horiz_feature_tag_index (baseScriptIndex, + baseLangSysIndex, + featureTableTag); + } + + inline HBINT16 get_horiz_max_value (unsigned int baseScriptIndex, + unsigned int baseLangSysIndex, + unsigned int featureTableTagIndex) const + { + return u.format1_0.get_horiz_max_value (baseScriptIndex, + baseLangSysIndex, + featureTableTagIndex); + } + + inline HBINT16 get_horiz_min_value (unsigned int baseScriptIndex, + unsigned int baseLangSysIndex, + unsigned int featureTableTagIndex) const + { + return u.format1_0.get_horiz_min_value (baseScriptIndex, + baseLangSysIndex, + featureTableTagIndex); + } + + inline unsigned int get_vert_lang_tag_index (unsigned int baseScriptIndex, + Tag baseLangSysTag) const + { + return u.format1_0.get_vert_lang_tag_index (baseScriptIndex, + baseLangSysTag); + } + + inline unsigned int get_vert_feature_tag_index (unsigned int baseScriptIndex, + unsigned int baseLangSysIndex, + Tag featureTableTag) const + { + return u.format1_0.get_vert_feature_tag_index (baseScriptIndex, + baseLangSysIndex, + featureTableTag); + } + + inline HBINT16 get_vert_max_value (unsigned int baseScriptIndex, + unsigned int baseLangSysIndex, + unsigned int featureTableTagIndex) const + { + return u.format1_0.get_vert_max_value (baseScriptIndex, + baseLangSysIndex, + featureTableTagIndex); + } + + inline HBINT16 get_vert_min_value (unsigned int baseScriptIndex, + unsigned int baseLangSysIndex, + unsigned int featureTableTagIndex) const + { + return u.format1_0.get_vert_min_value (baseScriptIndex, + baseLangSysIndex, + featureTableTagIndex); + } + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (!u.version.sanitize (c)) return_trace (false); + if (!likely(u.version.major == 1)) return_trace (false); + switch (u.version.minor) { + case 0: return_trace (u.format1_0.sanitize (c)); + case 1: return_trace (u.format1_1.sanitize (c)); + default:return_trace (false); + } + } + + protected: + union { + FixedVersion<> version; /* Version of the BASE table: 1.0 or 1.1 */ + BASEFormat1_0 format1_0; + BASEFormat1_1 format1_1; + } u; + public: + DEFINE_SIZE_UNION (4, version); +}; + + +} /* namespace OT */ + + +#endif /* HB_OT_LAYOUT_BASE_TABLE_HH */ diff --git a/src/hb-ot-layout-common-private.hh b/src/hb-ot-layout-common-private.hh index 34fa1b773..c5e7f5217 100644 --- a/src/hb-ot-layout-common-private.hh +++ b/src/hb-ot-layout-common-private.hh @@ -29,6 +29,8 @@ #ifndef HB_OT_LAYOUT_COMMON_PRIVATE_HH #define HB_OT_LAYOUT_COMMON_PRIVATE_HH +#include "hb-private.hh" +#include "hb-debug.hh" #include "hb-ot-layout-private.hh" #include "hb-open-type-private.hh" #include "hb-set-private.hh" @@ -45,12 +47,6 @@ namespace OT { -#define TRACE_DISPATCH(this, format) \ - hb_auto_trace_t trace \ - (&c->debug_depth, c->get_name (), this, HB_FUNC, \ - "format %d", (int) format); - - #define NOT_COVERED ((unsigned int) -1) @@ -159,13 +155,13 @@ struct RangeRecord } template - inline void add_coverage (set_t *glyphs) const { - glyphs->add_range (start, end); + inline bool add_coverage (set_t *glyphs) const { + return glyphs->add_range (start, end); } GlyphID start; /* First GlyphID in the range */ GlyphID end; /* Last GlyphID in the range */ - USHORT value; /* Value */ + HBUINT16 value; /* Value */ public: DEFINE_SIZE_STATIC (6); }; @@ -179,7 +175,7 @@ struct IndexArray : ArrayOf unsigned int *_indexes /* OUT */) const { if (_count) { - const USHORT *arr = this->sub_array (start_offset, _count); + const HBUINT16 *arr = this->sub_array (start_offset, _count); unsigned int count = *_count; for (unsigned int i = 0; i < count; i++) _indexes[i] = arr[i]; @@ -214,15 +210,15 @@ struct LangSys } inline bool sanitize (hb_sanitize_context_t *c, - const Record::sanitize_closure_t * = NULL) const + const Record::sanitize_closure_t * = nullptr) const { TRACE_SANITIZE (this); return_trace (c->check_struct (this) && featureIndex.sanitize (c)); } - Offset<> lookupOrderZ; /* = Null (reserved for an offset to a + Offset16 lookupOrderZ; /* = Null (reserved for an offset to a * reordering table) */ - USHORT reqFeatureIndex;/* Index of a feature required for this + HBUINT16 reqFeatureIndex;/* Index of a feature required for this * language system--if no required features * = 0xFFFFu */ IndexArray featureIndex; /* Array of indices into the FeatureList */ @@ -254,7 +250,7 @@ struct Script inline const LangSys& get_default_lang_sys (void) const { return this+defaultLangSys; } inline bool sanitize (hb_sanitize_context_t *c, - const Record