Merge branch 'master' into usermanual-integration

This commit is contained in:
Behdad Esfahbod 2020-04-23 15:32:43 -07:00 committed by GitHub
commit a11db0b9d4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1318 changed files with 51455 additions and 20294 deletions

View File

@ -4,10 +4,10 @@ jobs:
macos-10.12.6-aat-fonts:
macos:
xcode: "9.2.0"
xcode: "9.0.1"
steps:
- checkout
- run: HOMEBREW_NO_AUTO_UPDATE=1 brew install wget autoconf automake libtool pkg-config ragel freetype glib cairo
- run: HOMEBREW_NO_AUTO_UPDATE=1 brew install wget autoconf automake libtool pkg-config ragel freetype glib cairo python3
- run: ./autogen.sh --with-freetype --with-glib --with-gobject --with-cairo
- run: make -j4
- run: make check || .ci/fail.sh
@ -22,30 +22,42 @@ jobs:
- run: make -j4
- run: make check || .ci/fail.sh
macos-10.14.3-aat-fonts:
macos-10.14.4-aat-fonts:
macos:
xcode: "10.2.0"
xcode: "11.1.0"
steps:
- checkout
- run: HOMEBREW_NO_AUTO_UPDATE=1 brew install wget autoconf automake libtool pkg-config ragel freetype glib cairo icu4c graphite2
- run: export PKG_CONFIG_PATH="/usr/local/opt/icu4c/lib/pkgconfig" && ./autogen.sh --with-freetype --with-glib --with-gobject --with-cairo --with-icu --with-coretext --with-graphite2
- run: export PKG_CONFIG_PATH="/usr/local/opt/icu4c/lib/pkgconfig:/usr/local/opt/libffi/lib/pkgconfig" && ./autogen.sh --with-freetype --with-glib --with-gobject --with-cairo --with-icu --with-coretext --with-graphite2
- run: make -j4
- run: make check || .ci/fail.sh
distcheck:
docker:
- image: ubuntu:17.10
macos-10.15.3-aat-fonts:
macos:
xcode: "11.4.0"
steps:
- checkout
- run: apt update && apt install -y ninja-build binutils libtool autoconf automake make cmake gcc g++ pkg-config ragel gtk-doc-tools libfontconfig1-dev libfreetype6-dev libglib2.0-dev libcairo2-dev libicu-dev libgraphite2-dev python python-pip
- run: HOMEBREW_NO_AUTO_UPDATE=1 brew install wget autoconf automake libtool pkg-config ragel freetype glib cairo icu4c graphite2 meson
- run: export PKG_CONFIG_PATH="/usr/local/opt/icu4c/lib/pkgconfig:/usr/local/opt/libffi/lib/pkgconfig" && ./autogen.sh --with-freetype --with-glib --with-gobject --with-cairo --with-icu --with-coretext --with-graphite2
- run: make -j4
- run: make check || .ci/fail.sh
- run: make clean && meson build -Damalgam=true && ninja -Cbuild test
distcheck:
docker:
- image: ubuntu:19.10
steps:
- checkout
- run: apt update && apt install -y git ninja-build binutils libtool autoconf automake make gcc g++ pkg-config ragel gtk-doc-tools libfontconfig1-dev libfreetype6-dev libglib2.0-dev libcairo2-dev libicu-dev libgraphite2-dev python python-pip meson cmake
- run: pip install fonttools
- run: ./autogen.sh
- run: make -j32
- 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
- run: rm harfbuzz-* && make distdir
- run: cd harfbuzz-* && meson build && ninja -Cbuild test
- run: cd harfbuzz-* && cmake -Bcmakebuild -H. && cmake --build cmakebuild
alpine-O3-NOMMAP:
alpine-O3-Os-NOMMAP:
docker:
- image: alpine
steps:
@ -55,6 +67,10 @@ jobs:
- run: CFLAGS="-O3" CXXFLAGS="-O3 -DHB_NO_MMAP" ./autogen.sh
- run: make -j32
- run: make check || .ci/fail.sh
- run: make clean
- run: CFLAGS="-Os -DHB_OPTIMIZE_SIZE" CXXFLAGS="-Os -DHB_NO_MMAP -DHB_OPTIMIZE_SIZE" ./autogen.sh
- run: make -j32
- run: make check || .ci/fail.sh
archlinux-py3-all:
docker:
@ -69,34 +85,30 @@ jobs:
- run: make -j32 CPPFLAGS="-Werror"
- run: make check CPPFLAGS="-Werror" || .ci/fail.sh
## Doesn't play well with CircleCI apparently
#void-notest:
# docker:
# - image: voidlinux/voidlinux
# steps:
# - checkout
# - run: xbps-install -Suy freetype gettext gcc glib graphite pkg-config ragel libtool autoconf automake make
# - run: ./autogen.sh && make -j32 && make check
clang-O3-O0:
clang-O3-O0-and-nobuildsystem:
docker:
- image: ubuntu:18.10
- image: ubuntu:19.10
steps:
- checkout
- run: apt update || true
- run: apt install -y clang wget autoconf automake libtool pkg-config ragel libfreetype6-dev libfontconfig1-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 -j32 && cd ..
- run: CFLAGS="-O3" CXXFLAGS="-O3" CC=clang CXX=clang++ ./autogen.sh --with-freetype --with-fontconfig --with-glib --with-cairo --with-icu --with-graphite2
- run: make -j32
- run: LD_LIBRARY_PATH="$PWD/freetype-2.9/objs/.libs" make check || .ci/fail.sh
- run: make check || .ci/fail.sh
- run: make clean
- run: CFLAGS="-O0" CXXFLAGS="-O0" CC=clang CXX=clang++ ./autogen.sh --with-freetype --with-fontconfig --with-glib --with-cairo --with-icu --with-graphite2
- run: make -j32
- run: LD_LIBRARY_PATH="$PWD/freetype-2.9/objs/.libs" make check || .ci/fail.sh
- run: make check || .ci/fail.sh
- run: make clean
- run: make -Csrc CPPFLAGS="-DHB_TINY -DHB_NO_OT_FONT" libharfbuzz-subset.la && make clean
- run: clang -c src/harfbuzz.cc -DHB_NO_MT
# -Werror -Werror=deprecated-this-capture can be added but it will confuse contributors so let's skip for now
- run: clang -c src/hb-*.cc -DHB_NO_MT -std=c++2a -fno-exceptions
gcc-valgrind:
docker:
- image: ubuntu:18.10
- image: ubuntu:19.10
steps:
- checkout
- run: apt update || true
@ -106,35 +118,35 @@ jobs:
- run: make -j32
# run-shape-fuzzer-tests.py automatically runs valgrind if see available
# but test/api runs it by request, we probably should normalize the approaches
- run: RUN_VALGRIND=1 make check && make -Ctest/api check-valgrind || .ci/fail.sh
- run: HB_TEST_SHAPE_FUZZER_TIMEOUT=3 HB_TEST_SUBSET_FUZZER_TIMEOUT=30 RUN_VALGRIND=1 make check && make -Ctest/api check-valgrind || .ci/fail.sh
# informational for now
- run: make -Ctest/api check-symbols || true
clang-everything:
docker:
- image: ubuntu:18.10
- image: ubuntu:19.10
steps:
- checkout
- run: apt update || true; apt install -y wget gnupg
- run: wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add -
- run: echo "deb http://apt.llvm.org/cosmic/ llvm-toolchain-cosmic main" > /etc/apt/sources.list.d/llvmdev.list
- run: echo "deb-src http://apt.llvm.org/cosmic/ llvm-toolchain-cosmic main" > /etc/apt/sources.list.d/llvmdevsrc.list
- run: echo "deb http://apt.llvm.org/eoan/ llvm-toolchain-eoan main" > /etc/apt/sources.list.d/llvmdev.list
- run: echo "deb-src http://apt.llvm.org/eoan/ llvm-toolchain-eoan main" > /etc/apt/sources.list.d/llvmdevsrc.list
- run: apt update || true
- run: apt install -y clang lld binutils libtool autoconf automake make pkg-config gtk-doc-tools ragel libfreetype6-dev libfontconfig1-dev libglib2.0-dev libcairo2-dev libicu-dev libgraphite2-dev python python-pip
- run: pip install fonttools
- run: CFLAGS="-Weverything -Wno-reserved-id-macro -Wno-conversion -Wno-padded -Wno-sign-conversion -Wno-cast-qual -Wno-documentation -Wno-documentation-unknown-command" CXXFLAGS="-Weverything -Wno-old-style-cast -Wno-documentation -Wno-documentation-unknown-command -Wno-c++98-compat -Wno-cast-qual -Wno-c++98-compat-pedantic -Wno-sign-conversion -Wno-padded -Wno-shorten-64-to-32 -Wno-reserved-id-macro -Wno-float-conversion -Wno-format-pedantic -Wno-shadow -Wno-conversion -Wno-zero-as-null-pointer-constant -Wno-missing-field-initializers -Wno-used-but-marked-unused -Wno-unused-macros -Wno-comma -Wno-float-equal -Wno-disabled-macro-expansion -Wno-weak-vtables -Wno-unused-parameter -Wno-covered-switch-default -Wno-unreachable-code -Wno-unused-template" CC=clang CXX=clang++ ./autogen.sh --with-freetype --with-glib --with-cairo --with-icu --with-graphite2 --with-fontconfig
- run: CFLAGS="-Weverything -Wno-reserved-id-macro -Wno-conversion -Wno-padded -Wno-sign-conversion -Wno-cast-qual -Wno-documentation -Wno-documentation-unknown-command -DHB_WITH_WIN1256" CXXFLAGS="-Weverything -Wno-old-style-cast -Wno-documentation -Wno-documentation-unknown-command -Wno-c++98-compat -Wno-cast-qual -Wno-c++98-compat-pedantic -Wno-sign-conversion -Wno-padded -Wno-shorten-64-to-32 -Wno-reserved-id-macro -Wno-float-conversion -Wno-format-pedantic -Wno-shadow -Wno-conversion -Wno-zero-as-null-pointer-constant -Wno-missing-field-initializers -Wno-used-but-marked-unused -Wno-unused-macros -Wno-comma -Wno-float-equal -Wno-disabled-macro-expansion -Wno-weak-vtables -Wno-unused-parameter -Wno-covered-switch-default -Wno-unreachable-code -Wno-unused-template -DHB_WITH_WIN1256" CC=clang CXX=clang++ ./autogen.sh --with-freetype --with-glib --with-cairo --with-icu --with-graphite2 --with-fontconfig
- run: make -j32 CPPFLAGS="-Werror"
- run: make check CPPFLAGS="-Werror" || .ci/fail.sh
clang-asan:
docker:
- image: ubuntu:18.10
- image: ubuntu:19.10
steps:
- checkout
- run: apt update || true; apt install -y wget gnupg
- run: wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add -
- run: echo "deb http://apt.llvm.org/cosmic/ llvm-toolchain-cosmic main" > /etc/apt/sources.list.d/llvmdev.list
- run: echo "deb-src http://apt.llvm.org/cosmic/ llvm-toolchain-cosmic main" > /etc/apt/sources.list.d/llvmdevsrc.list
- run: echo "deb http://apt.llvm.org/eoan/ llvm-toolchain-eoan main" > /etc/apt/sources.list.d/llvmdev.list
- run: echo "deb-src http://apt.llvm.org/eoan/ llvm-toolchain-eoan main" > /etc/apt/sources.list.d/llvmdevsrc.list
- run: apt update || true
- run: apt install -y clang lld binutils libtool autoconf automake make pkg-config gtk-doc-tools ragel libfreetype6-dev libglib2.0-dev libcairo2-dev libicu-dev libgraphite2-dev python python-pip
- run: pip install fonttools
@ -144,13 +156,13 @@ jobs:
clang-msan:
docker:
- image: ubuntu:18.10
- image: ubuntu:19.10
steps:
- checkout
- run: apt update || true; apt install -y wget gnupg
- run: wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add -
- run: echo "deb http://apt.llvm.org/cosmic/ llvm-toolchain-cosmic main" > /etc/apt/sources.list.d/llvmdev.list
- run: echo "deb-src http://apt.llvm.org/cosmic/ llvm-toolchain-cosmic main" > /etc/apt/sources.list.d/llvmdevsrc.list
- run: echo "deb http://apt.llvm.org/eoan/ llvm-toolchain-eoan main" > /etc/apt/sources.list.d/llvmdev.list
- run: echo "deb-src http://apt.llvm.org/eoan/ llvm-toolchain-eoan main" > /etc/apt/sources.list.d/llvmdevsrc.list
- run: apt update || true
- run: apt install -y clang lld binutils libtool autoconf automake gtk-doc-tools gettext make pkg-config ragel libcairo2-dev libicu-dev libmount-dev libgraphite2-dev python python-pip
- run: pip install fonttools
@ -158,72 +170,72 @@ jobs:
- run: wget https://ftp.gnome.org/pub/gnome/sources/glib/2.58/glib-2.58.1.tar.xz && tar xf glib-2.58.1.tar.xz && cd glib-2.58.1 && ./autogen.sh --with-pcre CPPFLAGS="-fsanitize=memory" LDFLAGS="-fsanitize=memory" CFLAGS="-fsanitize=memory" CXXFLAGS="-fsanitize=memory" LD=ld.lld CC=clang CXX=clang++ && make -j32 && make install && cd ..
- 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 CPPFLAGS="-fsanitize=memory" LDFLAGS="-fsanitize=memory -O1 -g -fno-omit-frame-pointer" CFLAGS="-fsanitize=memory -O1 -g -fno-omit-frame-pointer" CXXFLAGS="-fsanitize=memory -O1 -g -fno-omit-frame-pointer" LD=ld.lld CC=clang CXX=clang++ && make -j32 && make install && cd ..
- run: CPPFLAGS="-fsanitize=memory -fsanitize-memory-track-origins" LDFLAGS="-fsanitize=memory -fsanitize-memory-track-origins -O1 -g -fno-omit-frame-pointer" CFLAGS="-fsanitize=memory -fsanitize-memory-track-origins -O1 -g -fno-omit-frame-pointer" CXXFLAGS="-fsanitize=memory -fsanitize-memory-track-origins -O1 -g -fno-omit-frame-pointer" LD=ld.lld CC=clang CXX=clang++ ./autogen.sh --with-freetype --with-glib --without-icu
- run: make -j32 && MSAN_OPTIONS=exitcode=42 make check || .ci/fail.sh | asan_symbolize | c++filt
- run: make -j32 && MSAN_OPTIONS=exitcode=42 HB_TEST_SUBSET_FUZZER_TIMEOUT=18 make check || .ci/fail.sh | asan_symbolize | c++filt
clang-tsan:
docker:
- image: ubuntu:18.10
- image: ubuntu:19.10
steps:
- checkout
- run: apt update || true; apt install -y wget gnupg
- run: wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add -
- run: echo "deb http://apt.llvm.org/cosmic/ llvm-toolchain-cosmic main" > /etc/apt/sources.list.d/llvmdev.list
- run: echo "deb-src http://apt.llvm.org/cosmic/ llvm-toolchain-cosmic main" > /etc/apt/sources.list.d/llvmdevsrc.list
- run: echo "deb http://apt.llvm.org/eoan/ llvm-toolchain-eoan main" > /etc/apt/sources.list.d/llvmdev.list
- run: echo "deb-src http://apt.llvm.org/eoan/ llvm-toolchain-eoan main" > /etc/apt/sources.list.d/llvmdevsrc.list
- run: apt update || true
- run: apt install -y clang lld binutils libtool autoconf automake make pkg-config ragel libfreetype6-dev libglib2.0-dev libcairo2-dev libicu-dev libgraphite2-dev python python-pip
- run: pip install fonttools
- run: CPPFLAGS="-fsanitize=thread" LDFLAGS="-fsanitize=thread -O1 -g -fno-omit-frame-pointer" CFLAGS="-fsanitize=thread -O1 -g -fno-omit-frame-pointer" CXXFLAGS="-fsanitize=thread -O1 -g -fno-omit-frame-pointer" LD=ld.lld CC=clang CXX=clang++ ./autogen.sh --with-freetype --with-glib --with-cairo --with-icu --with-graphite2
- run: CPPFLAGS="-fsanitize=thread" LDFLAGS="-fsanitize=thread -O3" CFLAGS="-fsanitize=thread -O3" CXXFLAGS="-fsanitize=thread -O3" LD=ld.lld CC=clang CXX=clang++ ./autogen.sh --with-freetype --with-glib --with-cairo --with-icu --with-graphite2
- run: make -j32
- run: make check || .ci/fail.sh | asan_symbolize | c++filt
- run: HB_TEST_SUBSET_FUZZER_TIMEOUT=40 make check || .ci/fail.sh | asan_symbolize | c++filt
clang-ubsan:
docker:
- image: ubuntu:18.10
- image: ubuntu:19.10
steps:
- checkout
- run: apt update || true; apt install -y wget gnupg
- run: wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add -
- run: echo "deb http://apt.llvm.org/cosmic/ llvm-toolchain-cosmic main" > /etc/apt/sources.list.d/llvmdev.list
- run: echo "deb-src http://apt.llvm.org/cosmic/ llvm-toolchain-cosmic main" > /etc/apt/sources.list.d/llvmdevsrc.list
- run: echo "deb http://apt.llvm.org/eoan/ llvm-toolchain-eoan main" > /etc/apt/sources.list.d/llvmdev.list
- run: echo "deb-src http://apt.llvm.org/eoan/ llvm-toolchain-eoan main" > /etc/apt/sources.list.d/llvmdevsrc.list
- run: apt update || true
- run: apt install -y clang lld binutils libtool autoconf automake make pkg-config ragel libfreetype6-dev libglib2.0-dev libcairo2-dev libicu-dev libgraphite2-dev python python-pip
- run: pip install fonttools
- run: CPPFLAGS="-fsanitize=undefined" LDFLAGS="-fsanitize=undefined -O1 -g -fno-omit-frame-pointer" CFLAGS="-fsanitize=undefined -O1 -g -fno-omit-frame-pointer" CXXFLAGS="-fsanitize=undefined -O1 -g -fno-omit-frame-pointer" LD=ld.lld CC=clang CXX=clang++ ./autogen.sh --with-freetype --with-glib --with-cairo --with-icu --with-graphite2
- run: CPPFLAGS="-fsanitize=undefined -fno-sanitize-recover=undefined" LDFLAGS="-fsanitize=undefined -fno-sanitize-recover=undefined -O1 -g -fno-omit-frame-pointer" CFLAGS="-fsanitize=undefined -O1 -g -fno-omit-frame-pointer" CXXFLAGS="-fsanitize=undefined -O1 -g -fno-omit-frame-pointer" LD=ld.lld CC=clang CXX=clang++ ./autogen.sh --with-freetype --with-glib --with-cairo --with-icu --with-graphite2
- run: make -j32
- run: make check || .ci/fail.sh | asan_symbolize | c++filt
- run: UBSAN_OPTIONS=print_stacktrace=1 make check || .ci/fail.sh | asan_symbolize | c++filt
fedora-O0-debug-outoftreebuild:
fedora-O0-debug-outoftreebuild-mingw:
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: CFLAGS="-O0" CXXFLAGS="-O0" CPPFLAGS="-DHB_DEBUG" 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)
- run: dnf install -y pkg-config ragel gcc gcc-c++ automake autoconf libtool make which diffutils glib2-devel freetype-devel cairo-devel libicu-devel gobject-introspection-devel graphite2-devel redhat-rpm-config python python-pip mingw32-gcc-c++ mingw64-gcc-c++ mingw32-glib2 mingw32-cairo mingw32-freetype mingw64-glib2 mingw64-cairo mingw64-freetype glibc-devel.i686 || true
- run: NOCONFIGURE=1 ./autogen.sh
- run: mkdir build && cd build && CFLAGS="-O0" CXXFLAGS="-O0" CPPFLAGS="-DHB_DEBUG" ../configure --with-freetype --with-glib --with-gobject --with-cairo --with-icu --with-graphite2 && make -j32 && (HB_TEST_SUBSET_FUZZER_TIMEOUT=15 make check || ../.ci/fail.sh)
- run: pip install pefile
- run: mkdir winbuild32 && cd winbuild32 && ../mingw32.sh && make -j32 && make dist-win && cp harfbuzz-*-win32.zip harfbuzz-win32.zip
- run: mkdir winbuild64 && cd winbuild64 && ../mingw64.sh && make -j32 && make dist-win && cp harfbuzz-*-win64.zip harfbuzz-win64.zip
- store_artifacts:
path: winbuild32/harfbuzz-win32.zip
destination: harfbuzz-win32.zip
- store_artifacts:
path: winbuild64/harfbuzz-win64.zip
destination: harfbuzz-win64.zip
cmake-gcc:
meson-gcc:
docker:
- image: ubuntu:17.10
- image: ubuntu:19.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: apt update && apt install -y ninja-build binutils meson 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 libnsl || 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_FREETYPE=ON -Bbuild -H.
- run: make -Cbuild -j32
- run: CTEST_OUTPUT_ON_FAILURE=1 make -Cbuild test
- run: make -Cbuild install
- run: meson build && ninja -Cbuild test && rm -rf build
# test amalgam build
- run: meson build -Damalgam=true && ninja -Cbuild && rm -rf build
# test meson's own unity feature also
- run: meson build --unity on && ninja -Cbuild && rm -rf build
# test experimental APIs
- run: meson build -Dexperimental-api=true && ninja -Cbuild test && rm -rf build
crosscompile-notest-djgpp:
docker:
@ -235,65 +247,16 @@ jobs:
- run: CFLAGS="-Wno-attributes" CXXFLAGS="-Wno-attributes" ./autogen.sh --prefix=/usr/local/djgpp --host=i586-pc-msdosdjgpp
- run: make -j32
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 -j32
crosscompile-notest-psvita:
docker:
- image: dockcross/base
steps:
- checkout
- run: git clone https://github.com/vitasdk/vdpm && cd vdpm && ./bootstrap-vitasdk.sh
- run: echo "#""!""/bin/true" > /usr/bin/ragel && chmod +x /usr/bin/ragel
- run: echo '#!/bin/true' > /usr/bin/ragel && chmod +x /usr/bin/ragel
- run: ./autogen.sh --prefix=/usr/local/vitasdk/arm-vita-eabi --host=arm-vita-eabi
- run: make -j32
crosscompile-cmake-notest-android-arm:
docker:
- image: dockcross/android-arm
steps:
- checkout
- run: cmake -Bbuild -H. -GNinja
- run: ninja -Cbuild
crosscompile-cmake-notest-browser-asmjs:
docker:
- image: dockcross/browser-asmjs
steps:
- checkout
- run: cmake -Bbuild -H. -GNinja
- run: ninja -Cbuild
crosscompile-cmake-notest-linux-arm64:
docker:
- image: dockcross/linux-arm64
steps:
- checkout
- run: cmake -Bbuild -H. -GNinja
- run: ninja -Cbuild
crosscompile-cmake-notest-linux-mips:
docker:
- image: dockcross/linux-mips
steps:
- checkout
- run: cmake -Bbuild -H. -GNinja
- run: ninja -Cbuild
#crosscompile-cmake-notest-windows-x64:
# docker:
# - image: dockcross/windows-x64
# steps:
# - checkout
# - run: cmake -Bbuild -H. -GNinja
# - run: ninja -Cbuild
workflows:
version: 2
build:
@ -301,38 +264,28 @@ workflows:
# macOS
- macos-10.12.6-aat-fonts
- macos-10.13.6-aat-fonts
- macos-10.14.3-aat-fonts
- macos-10.14.4-aat-fonts
- macos-10.15.3-aat-fonts
# both autotools and cmake
- distcheck
# autotools based builds
- alpine-O3-NOMMAP
- alpine-O3-Os-NOMMAP
- archlinux-py3-all
#- void-notest
- gcc-valgrind
- clang-O3-O0
- clang-O3-O0-and-nobuildsystem
- clang-everything
- clang-asan
- clang-msan
- clang-tsan
- clang-ubsan
- fedora-O0-debug-outoftreebuild
- fedora-O0-debug-outoftreebuild-mingw
# cmake based builds
- cmake-gcc
#- cmake-oracledeveloperstudio
- meson-gcc
# 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

View File

@ -6,10 +6,10 @@ trim_trailing_whitespace = true
end_of_line = lf
insert_final_newline = true
[*.{c,cc,h,hh}]
[*.{c,cc,h,hh,rl}]
tab_width = 8
indent_size = 2
indent_style = space
indent_style = tab # should be space
[*.{py,sh}]
indent_style = tab
@ -17,5 +17,7 @@ indent_style = tab
[{Makefile.am,Makefile.sources,configure.ac}]
tab_width = 8
[{CMakeLists.txt,*.cmake}]
[{meson.build,meson_options.txt}]
tab_width = 8
indent_style = space
indent_size = 2

View File

@ -1,11 +1,14 @@
Behdad Esfahbod
David Corbett
David Turner
Ebrahim Byagowi
Garret Rieger
Jonathan Kew
Khaled Hosny
Lars Knoll
Martin Hosken
Owen Taylor
Roderick Sheeter
Roozbeh Pournader
Simon Hausmann
Werner Lemberg

View File

@ -7,11 +7,8 @@ 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 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 Windows, consider using [vcpkg](https://github.com/Microsoft/vcpkg)
or `meson build && ninja -Cbuild`.
on macOS, using MacPorts:

View File

@ -1,7 +1,7 @@
cmake_minimum_required(VERSION 2.8.0)
project(harfbuzz)
enable_testing()
message("HarfBuzz has a Meson port also and tries to migrate all the other build systems to it, please consider using it.")
## Limit framework build to Xcode generator
if (BUILD_FRAMEWORK)
@ -35,7 +35,6 @@ 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)
@ -44,6 +43,7 @@ if (APPLE)
endif ()
if (WIN32)
option(HB_HAVE_UNISCRIBE "Enable Uniscribe shaper backend on Windows" OFF)
option(HB_HAVE_GDI "Enable GDI integration helpers on Windows" OFF)
option(HB_HAVE_DIRECTWRITE "Enable DirectWrite shaper backend on Windows" OFF)
endif ()
option(HB_BUILD_UTILS "Build harfbuzz utils, needs cairo, freetype, and glib properly be installed" OFF)
@ -53,7 +53,6 @@ if (HB_BUILD_UTILS)
endif ()
option(HB_BUILD_SUBSET "Build harfbuzz-subset" ON)
option(HB_BUILD_TESTS "Build harfbuzz tests" ON)
option(HB_HAVE_GOBJECT "Enable GObject Bindings" OFF)
if (HB_HAVE_GOBJECT)
@ -66,32 +65,11 @@ if (HB_HAVE_INTROSPECTION)
set (HB_HAVE_GLIB ON)
endif ()
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_FALLBACK)
# We need PYTHON_EXECUTABLE to be set for running the tests...
include (FindPythonInterp)
@ -110,7 +88,7 @@ endmacro ()
if (UNIX)
list(APPEND CMAKE_REQUIRED_LIBRARIES m)
endif ()
check_funcs(atexit mprotect sysconf getpagesize mmap isatty newlocale strtod_l round)
check_funcs(atexit mprotect sysconf getpagesize mmap isatty newlocale strtod_l roundf)
check_include_file(unistd.h HAVE_UNISTD_H)
if (${HAVE_UNISTD_H})
add_definitions(-DHAVE_UNISTD_H)
@ -144,12 +122,12 @@ 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})
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
# https://stackoverflow.com/a/27630120
function (add_prefix_to_list var prefix)
set (listVar "")
foreach (f ${${var}})
@ -160,14 +138,9 @@ 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_SUBSET_sources ${SRCSOURCES})
add_prefix_to_list(HB_SUBSET_sources "${PROJECT_SOURCE_DIR}/src/")
@ -191,9 +164,6 @@ 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})
@ -202,61 +172,12 @@ 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)
# foreach (ragel_output IN ITEMS ${HB_BASE_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}
)
set (subset_project_sources
${HB_SUBSET_sources}
)
set (project_sources ${PROJECT_SOURCE_DIR}/src/harfbuzz.cc) # use amalgam source
set (subset_project_sources ${HB_SUBSET_sources})
set (project_extra_sources)
set (project_headers
#${HB_VERSION_H}
${HB_BASE_headers}
)
set (subset_project_headers
${HB_SUBSET_headers}
)
set (project_headers ${HB_BASE_headers})
set (subset_project_headers ${HB_SUBSET_headers})
## Find and include needed header folders and libraries
if (HB_HAVE_FREETYPE)
@ -269,7 +190,6 @@ if (HB_HAVE_FREETYPE)
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
@ -287,7 +207,6 @@ if (HB_HAVE_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})
@ -295,14 +214,6 @@ if (HB_HAVE_GRAPHITE2)
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)
@ -316,7 +227,6 @@ if (HB_HAVE_GLIB)
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})
@ -336,7 +246,6 @@ if (HB_HAVE_ICU)
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})
@ -348,7 +257,6 @@ 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)
if (HB_IOS)
@ -379,21 +287,21 @@ if (APPLE AND HB_HAVE_CORETEXT)
endif ()
endif ()
if (WIN32 AND HB_HAVE_GDI)
add_definitions(-DHAVE_GDI)
list(APPEND project_headers ${PROJECT_SOURCE_DIR}/src/hb-gdi.h)
list(APPEND THIRD_PARTY_LIBS gdi32)
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 ()
@ -501,7 +409,6 @@ if (HB_HAVE_GOBJECT)
)
endif ()
## Atomic ops availability detection
file(WRITE "${PROJECT_BINARY_DIR}/try_compile_intel_atomic_primitives.c"
" void memory_barrier (void) { __sync_synchronize (); }
@ -538,6 +445,19 @@ endif ()
add_library(harfbuzz ${project_sources} ${project_extra_sources} ${project_headers})
target_link_libraries(harfbuzz ${THIRD_PARTY_LIBS})
## Define harfbuzz-icu library
if (HB_HAVE_ICU)
add_library(harfbuzz-icu ${PROJECT_SOURCE_DIR}/src/hb-icu.cc ${PROJECT_SOURCE_DIR}/src/hb-icu.h)
add_dependencies(harfbuzz-icu harfbuzz)
target_link_libraries(harfbuzz-icu harfbuzz ${THIRD_PARTY_LIBS})
if (BUILD_SHARED_LIBS)
set_target_properties(harfbuzz harfbuzz-icu PROPERTIES VISIBILITY_INLINES_HIDDEN TRUE)
endif ()
endif ()
## Define harfbuzz-subset library
if (HB_BUILD_SUBSET)
add_library(harfbuzz-subset ${subset_project_sources} ${subset_project_headers})
@ -610,7 +530,6 @@ if (WIN32)
endif ()
if (HB_HAVE_INTROSPECTION)
find_package(PkgConfig)
pkg_check_modules(PC_GI QUIET gobject-introspection-1.0)
@ -689,12 +608,14 @@ if (HB_HAVE_INTROSPECTION)
POST_BUILD
COMMAND ${G_IR_SCANNER_CMD}
--warn-all --no-libtool --verbose
-n hb
--namespace=HarfBuzz
--nsversion=0.0
--symbol-prefix=hb
--symbol-prefix=hb_gobject
--identifier-prefix=hb_
--include GObject-2.0
--pkg-export=harfbuzz
--pkg-export=harfbuzz-gobject
--c-include=hb-gobject.h
--cflags-begin
-I${PROJECT_SOURCE_DIR}/src
-I${PROJECT_BINARY_DIR}/src
@ -797,6 +718,14 @@ if (NOT SKIP_INSTALL_LIBRARIES AND NOT SKIP_INSTALL_ALL)
NAMESPACE harfbuzz::
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/harfbuzz
)
if (HB_HAVE_ICU)
install(TARGETS harfbuzz-icu
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
FRAMEWORK DESTINATION Library/Frameworks
)
endif ()
if (HB_BUILD_UTILS)
if (WIN32 AND BUILD_SHARED_LIBS)
install(TARGETS harfbuzz-subset
@ -841,54 +770,3 @@ if (NOT SKIP_INSTALL_LIBRARIES AND NOT SKIP_INSTALL_ALL)
endif ()
endif ()
endif ()
if (HB_BUILD_TESTS)
## src/ executables
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")
## Tests
if (UNIX OR MINGW)
if (BUILD_SHARED_LIBS)
# generate harfbuzz.def after build completion
add_custom_command(TARGET harfbuzz POST_BUILD
COMMAND "${PYTHON_EXECUTABLE}" ${PROJECT_SOURCE_DIR}/src/gen-def.py ${PROJECT_BINARY_DIR}/harfbuzz.def ${project_headers}
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)
endif ()

151
CONFIG.md Normal file
View File

@ -0,0 +1,151 @@
# Configuring HarfBuzz
Most of the time you will not need any custom configuration. The configuration
options provided by `configure` or `meson` should be enough. In particular,
if you just want HarfBuzz library plus hb-shape / hb-view utilities, make sure
FreeType and Cairo are available and found during configuration.
If you are building for distribution, you should more carefully consider whether
you need Glib, ICU, Graphite2, as well as CoreText / Uniscribe / DWrite. Make
sure the relevant ones are enabled.
If you are building for custom environment (embedded, downloadable app, etc)
where you mostly just want to call `hb_shape()` and the binary size of the
resulting library is very important to you, the rest of this file guides you
through your options to disable features you may not need, in exchange for
binary size savings.
## Compiler Options
Make sure you build with your compiler's "optimize for size" option. On `gcc`
this is `-Os`, and can be enabled by passing `CXXFLAGS=-Os` either to `configure`
(sticky) or to `make` (non-sticky). On clang there is an even more extreme flag,
`-Oz`.
HarfBuzz heavily uses inline functions and the optimize-size flag can make the
library smaller by 20% or more. Moreover, sometimes, based on the target CPU,
the optimize-size builds perform *faster* as well, thanks to lower code
footprint and caching effects. So, definitely try that even if size is not
extremely tight but you have a huge application. For example, Chrome does
that. Note that this configuration also automatically enables certain internal
optimizations. Search for `HB_OPTIMIZE_SIZE` for details, if you are using
other compilers, or continue reading.
Another compiler option to consider is "link-time optimization", also known as
'lto'. To enable that, with `gcc` or `clang`, add `-flto` to both `CXXFLAGS`
and `LDFLAGS`, either on `configure` invocation (sticky) or on `make` (non-sticky).
This, also, can have a huge impact on the final size, 20% or more.
Finally, if you are making a static library build or otherwise linking the
library into your app, make sure your linker removes unused functions. This
can be tricky and differ from environment to environment, but you definitely
want to make sure this happens. Otherwise, every unused public function will
be adding unneeded bytes to your binary. The following pointers might come
handy:
* https://lwn.net/Articles/741494/ (all of the four-part series)
* https://elinux.org/images/2/2d/ELC2010-gc-sections_Denys_Vlasenko.pdf
Combining the above three build options should already shrink your library a lot.
The rest of this file shows you ways to shrink the library even further at the
expense of removing functionality (that may not be needed). The remaining
options are all enabled by defining pre-processor macros, which can be done
via `CXXFLAGS` or `CPPFLAGS` similarly.
## Unicode-functions
Access to Unicode data can be configured at compile time as well as run-time.
By default, HarfBuzz ships with its own compact subset of properties from
Unicode Character Database that it needs. This is a highly-optimized
implementation that depending on compile settings (optimize-size or not)
takes around ~40kb or ~60kb. Using this implementation (default) is highly
recommended, as HarfBuzz always ships with data from latest version of Unicode.
This implementation can be disabled by defining `HB_NO_UCD`.
For example, if you are enabling ICU as a built-in option, or GLib, those
can provide Unicode data as well, so defining `HB_NO_UCD` might save you
space without reducing functionality (to the extent that the Unicode version
of those implementations is recent.)
If, however, you provide your own Unicode data to HarfBuzz at run-time by
calling `hb_buffer_set_unicode_funcs` on every buffer you create, and you do
not rely on `hb_unicode_funcs_get_default()` results, you can disable the
internal implementation by defining both `HB_NO_UCD` and `HB_NO_UNICODE_FUNCS`.
The latter is needed to guard against accidentally building a library without
any default Unicode implementations.
## Font-functions
Access to certain font functionalities can also be configured at run-time. By
default, HarfBuzz uses an efficient internal implementation of OpenType
functionality for this. This internal implementation is called `hb-ot-font`.
All newly-created `hb_font_t` objects by default use `hb-ot-font`. Using this
is highly recommended, and is what fonts use by default when they are created.
Most embedded uses will probably use HarfBuzz with FreeType using `hb-ft.h`.
In that case, or if you otherwise provide those functions by calling
`hb_font_set_funcs()` on every font you create, you can disable `hb-ot-font`
without loss of functionality by defining `HB_NO_OT_FONT`.
## Shapers
Most HarfBuzz clients use it for the main shaper, called "ot". However, it
is legitimate to want to compile HarfBuzz with only another backend, eg.
CoreText, for example for an iOS app. For that, you want `HB_NO_OT_SHAPE`.
If you are going down that route, check if you want `HB_NO_OT`.
This is very rarely what you need. Make sure you understand exactly what you
are doing.
Defining `HB_NO_FALLBACK_SHAPE` however is pretty harmless. That removes the
(unused) "fallback" shaper.
## Thread-safety
By default HarfBuzz builds as a thread-safe library. The exception is that
the `HB_TINY` predefined configuring (more below) disables thread-safety.
If you do /not/ need thread-safety in the library (eg. you always call into
HarfBuzz from the same thread), you can disable thread-safety by defining
`HB_NO_MT`. As noted already, this is enabled by `HB_TINY`.
## Pre-defined configurations
The [`hb-config.hh`](src/hb-config.hh) internal header supports three
pre-defined configurations as well grouping of various configuration options.
The pre-defined configurations are:
* `HB_MINI`: Disables shaping of AAT as well as legacy fonts. Ie. it produces
a capable OpenType shaper only.
* `HB_LEAN`: Disables various non-shaping functionality in the library, as well
as esoteric or rarely-used shaping features. See the definition for details.
* `HB_TINY`: Enables both `HB_MINI` and `HB_LEAN` configurations, as well as
disabling thread-safety and debugging, and use even more size-optimized data
tables.
## Tailoring configuration
Most of the time, one of the pre-defined configuration is exactly what one needs.
Sometimes, however, the pre-defined configuration cuts out features that might
be desired in the library. Unfortunately there is no quick way to undo those
configurations from the command-line. But one can add a header file called
`config-override.h` to undefine certain `HB_NO_*` symbols as desired. Then
define `HAVE_CONFIG_OVERRIDE_H` to make `hb-config.hh` include your configuration
overrides at the end.
## Notes
Note that the config option `HB_NO_CFF`, which is enabled by `HB_LEAN` and
`HB_TINY` does /not/ mean that the resulting library won't work with CFF fonts.
The library can shape valid CFF fonts just fine, with or without this option.
This option disables (among other things) the code to calculate glyph exntents
for CFF fonts.

View File

@ -2,7 +2,9 @@ HarfBuzz is licensed under the so-called "Old MIT" license. Details follow.
For parts of HarfBuzz that are licensed under different licenses see individual
files names COPYING in subdirectories where applicable.
Copyright © 2010,2011,2012 Google, Inc.
Copyright © 2010,2011,2012,2013,2014,2015,2016,2017,2018,2019,2020 Google, Inc.
Copyright © 2018,2019,2020 Ebrahim Byagowi
Copyright © 2019,2020 Facebook, Inc.
Copyright © 2012 Mozilla Foundation
Copyright © 2011 Codethink Limited
Copyright © 2008,2010 Nokia Corporation and/or its subsidiary(-ies)

View File

@ -9,12 +9,30 @@ SUBDIRS = src util test docs
EXTRA_DIST = \
autogen.sh \
harfbuzz.doap \
README.md \
README.mingw.md \
README.python.md \
README.wine.md \
BUILD.md \
CONFIG.md \
RELEASING.md \
TESTING.md \
CMakeLists.txt \
replace-enum-strings.cmake \
meson.build \
meson_options.txt \
subprojects/expat.wrap \
subprojects/fontconfig.wrap \
subprojects/freetype2.wrap \
subprojects/glib.wrap \
subprojects/libffi.wrap \
subprojects/proxy-libintl.wrap \
subprojects/zlib.wrap \
meson-cc-tests/intel-atomic-primitives-test.c \
meson-cc-tests/solaris-atomic-operations.c \
mingw-configure.sh \
mingw-ldd.py \
mingw32.sh \
mingw64.sh \
$(NULL)
MAINTAINERCLEANFILES = \
@ -60,8 +78,6 @@ DISTCHECK_CONFIGURE_FLAGS = \
--enable-introspection \
$(NULL)
# TODO: Copy infrastructure from cairo
# TAR_OPTIONS is not set as env var for 'make dist'. How to fix that?
TAR_OPTIONS = --owner=0 --group=0
@ -70,8 +86,7 @@ dist-hook: dist-clear-sticky-bits
dist-clear-sticky-bits:
chmod -R a-s $(distdir)
tar_file = $(PACKAGE_TARNAME)-$(VERSION).tar.bz2
tar_file = $(PACKAGE_TARNAME)-$(VERSION).tar.xz
sha256_file = $(tar_file).sha256
gpg_file = $(sha256_file).asc
$(sha256_file): $(tar_file)
@ -82,5 +97,18 @@ $(gpg_file): $(sha256_file)
release-files: $(tar_file) $(sha256_file) $(gpg_file)
dist-win:
@case $(host_triplet) in *-w64-mingw32) ;; *) echo "Error: Requires mingw build. See README.mingw.md.">&2; exit 1 ;; esac
@DIR=$(PACKAGE_TARNAME)-$(VERSION)-win`case $(host_triplet) in i686-*) echo 32 ;; x86_64-*) echo 64 ;; esac`; \
$(RM) -r $$DIR; $(MKDIR_P) $$DIR || exit 1; \
cp util/.libs/hb-{shape,view,subset}.exe $$DIR && \
$(top_srcdir)/mingw-ldd.py $$DIR/hb-view.exe | grep -v 'not found' | cut -d '>' -f 2 | xargs cp -t $$DIR && \
cp src/.libs/libharfbuzz{,-subset}-0.dll $$DIR && \
chmod a+x $$DIR/*.{exe,dll} && \
$(STRIP) $$DIR/*.{exe,dll} && \
zip -r $$DIR.zip $$DIR && \
$(RM) -r $$DIR && \
echo "$$DIR.zip is ready."
-include $(top_srcdir)/git.mk

112
NEWS
View File

@ -1,3 +1,115 @@
Overview of changes leading to 2.6.5
Friday, April 17, 2020
====================================
- Add experimental meson build system. Autotools is still the primary
and supported build system.
- AAT is now always preferred for horizontal scripts when both AAT and OT
layout tables exist at the same time.
- Subsetter improvements.
- New API:
+hb_ft_font_lock_face()
+hb_ft_font_unlock_face()
Overview of changes leading to 2.6.4
Monday, October 29, 2019
====================================
- Small bug fix.
- Build fixes.
Overview of changes leading to 2.6.3
Monday, October 28, 2019
====================================
- Misc small fixes, mostly to build-related issues.
- New API:
+hb_font_get_nominal_glyphs()
Overview of changes leading to 2.6.2
Monday, September 30, 2019
====================================
- Misc small fixes, mostly to build-related issues.
Overview of changes leading to 2.6.1
Thursday, August 22, 2019
====================================
- Fix regression with hb_font_create_sub_font scaling introduced in 2.6.0.
- Change interpretation of font PTEM size / CoreText font size handling.
See https://github.com/harfbuzz/harfbuzz/pull/1484
- hb-ot-font: Prefer symbol cmap subtable if present.
- Apply 'dist'/'abvm'/'blwm' features to all scripts.
- Drop experimental DirectWrite API.
Overview of changes leading to 2.6.0
Tuesday, August 13, 2019
====================================
- New OpenType metrics, baseline, and metadata table access APIs.
- New API to set font variations to a named-instance.
- New hb-gdi.h header and API for creating hb_face_t from HFONT.
- Amalgam: Provide a single-file harfbuzz.cc file for easier alternate building.
- More size-reduction configurable options, enabled by HB_TINY.
- New API:
+hb_font_set_var_named_instance()
+hb_gdi_face_create()
+hb_ot_layout_baseline_tag_t
+hb_ot_layout_get_baseline()
+hb_ot_meta_tag_t
+hb_ot_meta_get_entry_tags()
+hb_ot_meta_reference_entry()
+hb_ot_metrics_tag_t
+hb_ot_metrics_get_position()
+hb_ot_metrics_get_variation()
+hb_ot_metrics_get_x_variation()
+hb_ot_metrics_get_y_variation()
Overview of changes leading to 2.5.3
Wednesday, June 26, 2019
====================================
- Fix UCD script data for Unicode 10+ scripts. This was broken since 2.5.0.
- More optimizations for HB_TINY.
Overview of changes leading to 2.5.2
Thursday, June 20, 2019
====================================
- More hb-config.hh facilities to shrink library size, namely when built as
HB_TINY.
- New documentation of custom configurations in CONFIG.md.
- Fix build on gcc 4.8. That's supported again.
- Universal Shaping Engine improvements thanks to David Corbett.
- API Changes: Undeprecate some horizontal-kerning API and re-enable in hb-ft,
such that Type1 fonts will continue kerning.
Overview of changes leading to 2.5.1
Friday, May 31, 2019
====================================
- Fix build with various versions of Visual Studio.
- Improved documentation, thanks to Nathan Willis.
- Bugfix in subsetting glyf table.
- Improved scripts for cross-compiling for Windows using mingw.
- Rename HB_MATH_GLYPH_PART_FLAG_EXTENDER to HB_OT_MATH_GLYPH_PART_FLAG_EXTENDER.
A deprecated macro is added for backwards-compatibility.
Overview of changes leading to 2.5.0
Friday, May 24, 2019
====================================
- This release does not include much functional changes, but includes major internal
code-base changes. We now require C++11. Support for gcc 4.8 and earlier has been
dropped.
- New hb-config.hh facility for compiling smaller library for embedded and web usecases.
- New Unicode Character Databse implementation that is half the size of previously-used
UCDN.
- Subsetter improvements.
- Improved documentation, thanks to Nathan Willis.
- Misc shaping fixes.
Overview of changes leading to 2.4.0
Monday, March 25, 2019
====================================

View File

@ -1,10 +1,12 @@
[![Travis Build Status](https://travis-ci.org/harfbuzz/harfbuzz.svg?branch=master)](https://travis-ci.org/harfbuzz/harfbuzz)
[![AppVeyor Build Status](https://ci.appveyor.com/api/projects/status/0t0flrxpstj9lb9w?svg=true&branch=master)](https://ci.appveyor.com/project/harfbuzz/harfbuzz)
[![CircleCI Build Status](https://circleci.com/gh/harfbuzz/harfbuzz/tree/master.svg?style=svg)](https://circleci.com/gh/harfbuzz/harfbuzz/tree/master)
[![OSS-Fuzz Status](https://oss-fuzz-build-logs.storage.googleapis.com/badges/harfbuzz.svg)](https://oss-fuzz-build-logs.storage.googleapis.com/index.html)
[![Coverity Code Health](https://img.shields.io/coverity/scan/5450.svg)](https://scan.coverity.com/projects/behdad-harfbuzz)
[![Codacy Code Health](https://api.codacy.com/project/badge/Grade/f17f1708783c447488bc8dd317150eaa)](https://app.codacy.com/app/behdad/harfbuzz)
[![Codecov Code Coverage](https://codecov.io/gh/harfbuzz/harfbuzz/branch/master/graph/badge.svg)](https://codecov.io/gh/harfbuzz/harfbuzz)
[![Coverals Code Coverage](https://img.shields.io/coveralls/harfbuzz/harfbuzz.svg)](https://coveralls.io/r/harfbuzz/harfbuzz)
[![Packaging status](https://repology.org/badge/tiny-repos/harfbuzz.svg)](https://repology.org/project/harfbuzz/versions)
[ABI Tracker](http://abi-laboratory.pro/tracker/timeline/harfbuzz/)
This is HarfBuzz, a text shaping library.
@ -13,6 +15,20 @@ For bug reports, mailing list, and other information please visit:
http://harfbuzz.org/
For license information, see the file COPYING.
For license information, see [COPYING](COPYING).
For build information, see [BUILD.md](BUILD.md).
For custom configurations, see [CONFIG.md](CONFIG.md).
For test execution, see [TESTING.md](TESTING.md).
Documentation: https://harfbuzz.github.io
<details>
<summary>Packaging status of HarfBuzz</summary>
[![Packaging status](https://repology.org/badge/vertical-allrepos/harfbuzz.svg?header=harfbuzz)](https://repology.org/project/harfbuzz/versions)
</details>

48
README.mingw.md Normal file
View File

@ -0,0 +1,48 @@
For the development of HarfBuzz, the Microsoft shaping technology, Uniscribe,
as a widely used and tested shaper is used as more-or-less OpenType reference
implementation and that specially is important where OpenType specification
is or wasn't that clear. For having access to Uniscribe on Linux/macOS these
steps are recommended:
1. Install Wine from your favorite package manager. On Fedora that's `dnf install wine`.
2. And `mingw-w64` compiler.
With `brew` on macOS, you can have it like `brew install mingw-w64`.
On Fedora, with `dnf install mingw32-gcc-c++`, or `dnf install mingw64-gcc-c++` for the
64-bit Windows.
3. Install cross-compiled dependency packages. Alternatively see [^1] below.
On Fedora that would be `dnf install mingw32-glib2 mingw32-cairo mingw32-freetype`
for 32-bit, or `dnf install mingw64-glib2 mingw64-cairo mingw64-freetype` for 64-bit.
5. `NOCONFIGURE=1 ./autogen.sh && mkdir winbuild && cd winbuild`
6. Run `../mingw32.sh` for 32-bit build, or `../mingw64.sh` for 64-bit. This configures
HarfBuzz for cross-compiling. It enables Uniscribe backend as well.
7. `make`
Now you can use hb-shape using `wine util/hb-shape.exe` but if you like to shape with
the Microsoft Uniscribe,
8. Bring a 32bit version of `usp10.dll` for yourself from `C:\Windows\SysWOW64\usp10.dll` of your
Windows installation (assuming you have a 64-bit installation, otherwise
`C:\Windows\System32\usp10.dll`) that it is not a DirectWrite proxy
([for more info](https://en.wikipedia.org/wiki/Uniscribe)).
Rule of thumb, your `usp10.dll` should have a size more than 500kb, otherwise
it is designed to work with DirectWrite which Wine can't work with its original one.
You want a Uniscribe from Windows 7 or older.
Put the DLL in the folder you are going to run the next command,
9. `WINEDLLOVERRIDES="usp10=n" wine util/hb-shape.exe fontname.ttf -u 0061,0062,0063 --shaper=uniscribe`
(`0061,0062,0063` means `abc`, use test/shaping/hb-unicode-decode to generate ones you need)
[^1] Download and put [this](https://drive.google.com/open?id=0B3_fQkxDZZXXbWltRGd5bjVrUDQ)
in your `~/.local/i686-w64-mingw32`. Then replace all the instances of
`/home/behdad/.local/i586-mingw32msvc` and `/home/behdad/.local/i686-w64-mingw32`
with `<$HOME>/.local/i686-w64-mingw32` on that folder.
(`<$HOME>` replace it with `/home/XXX` or `/Users/XXX` on macOS)
You shouldn't replace the instances of those inside binary files.

View File

@ -6,21 +6,21 @@ you can install that this way:
sudo apt-get install libgirepository1.0-dev
```
And then run autogen.sh (if building from git), and then:
And then run `autogen.sh` (if building from git), and then:
```bash
./configure --with-gobject --enable-introspection
```
Make sure that gobject-introspection is enabled then in the final report.
Make sure that gobject-introspection is reported enabled then in the `configure` script output.
Compile and install.
Make sure you have the installation lib dir in LD_LIBRARY_PATH, as needed
Make sure you have the installation lib dir in `LD_LIBRARY_PATH`, as needed
for the linker to find the library.
Then make sure you also have GI_TYPELIB_PATH pointing to the resulting
$prefix/lib/girepository-* directory.
Then make sure you also have `GI_TYPELIB_PATH` pointing to the resulting
`$prefix/lib/girepository-*` directory.
Make sure you have pygobject installed. Then check that the following
import works in your Python interpreter:
@ -30,7 +30,7 @@ from gi.repository import HarfBuzz
```
If it does, you are ready to call HarfBuzz from Python! Congratulations.
See src/sample.py.
See [`src/sample.py`](src/sample.py).
The Python API will change. Let us know on the mailing list if you are
using it, and send lots of feedback.

View File

@ -1,40 +0,0 @@
For the development of HarfBuzz, the Microsoft shaping technology, Uniscribe,
as a widely used and tested shaper is used as more-or-less OpenType reference
implementation and that specially is important where OpenType specification
is or wasn't that clear. For having access to Uniscribe on Linux/macOS these
steps are recommended:
1. Install Wine from your favorite package manager.
2. And `mingw-w64` compiler.
With `brew` on macOS, you can have it like `brew install mingw-w64`
3. Download and put [this](https://drive.google.com/open?id=0B3_fQkxDZZXXbWltRGd5bjVrUDQ)
on your `~/.local/i686-w64-mingw32`.
4. Replace all the instances of `/home/behdad/.local/i586-mingw32msvc`
and `/home/behdad/.local/i686-w64-mingw32` with `<$HOME>/.local/i686-w64-mingw32`
on that folder. (`<$HOME>` replace it with `/home/XXX` or `/Users/XXX` on macOS)
Probably you shouldn't replace the ones are inside binaries.
5. `NOCONFIGURE=1 ./autogen.sh && mkdir winbuild && cd winbuild`
6. `../mingw32.sh --with-uniscribe && cd ..`
7. `make -Cwinbuild`
Now you can use hb-shape using `wine winbuild/util/hb-shape.exe` but if you like to
to use the original Uniscribe,
8. Bring a 32bit version of `usp10.dll` for yourself from `C:\Windows\SysWOW64\usp10.dll` of your
Windows installation (assuming you have a 64-bit installation, otherwise `C:\Windows\System32\usp10.dll`)
that it is not a DirectWrite proxy ([for more info](https://en.wikipedia.org/wiki/Uniscribe)).
Rule of thumb, your `usp10.dll` should have a size more than 500kb, otherwise
it is designed to work with DirectWrite which Wine can't work with its original one.
Put the dll on the folder you are going to run the next command,
9. `WINEDLLOVERRIDES="usp10=n" wine winbuild/util/hb-shape.exe fontname.ttf -u 0061,0062,0063 --shaper=uniscribe`
(`0061,0062,0063` means `abc`, use test/shaping/hb-unicode-decode to generate ones you need)

View File

@ -46,27 +46,21 @@ HarfBuzz release walk-through checklist:
10. Build win32 bundle.
a. Put contents of [this](https://drive.google.com/open?id=0B3_fQkxDZZXXbWltRGd5bjVrUDQ) on your `~/.local/i686-w64-mingw32`,
a. Build Win32 binaries. See [README.mingw.md](README.mingw.md).
b. Run `../mingw32.sh --with-uniscribe` script 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.
b. Run "make dist-win" to build Win32 bundle.
11. 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 1592693 Jul 18 11:25 harfbuzz-1.4.7.tar.xz
-rw-r--r-- 1 behdad eng 89 Jul 18 11:34 harfbuzz-1.4.7.tar.xz.sha256
-rw-r--r-- 1 behdad eng 339 Jul 18 11:34 harfbuzz-1.4.7.tar.xz.sha256.asc
-rw-r--r-- 1 behdad eng 2895619 Jul 18 11:34 harfbuzz-1.4.7-win32.zip
```
12. While doing that, quickly double-check the size of the .tar.bz2 and .zip
12. While doing that, quickly double-check the size of the .tar.xz 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.
@ -76,39 +70,3 @@ HarfBuzz release walk-through checklist:
14. 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
```

85
TESTING.md Normal file
View File

@ -0,0 +1,85 @@
## Build & Run
Depending on what area you are working in change or add `HB_DEBUG_<whatever>`.
Values defined in `hb-debug.hh`.
```shell
# quick sanity check
time (make -j4 CPPFLAGS='-DHB_DEBUG_SUBSET=100' \
&& (make -j4 -C test/api check || cat test/api/test-suite.log))
# slower sanity check
time (make -j4 CPPFLAGS='-DHB_DEBUG_SUBSET=100' \
&& make -j4 -C src check \
&& make -j4 -C test/api check \
&& make -j4 -C test/subset check)
# confirm you didn't break anything else
time (make -j4 CPPFLAGS='-DHB_DEBUG_SUBSET=100' \
&& make -j4 check)
# often catches files you didn't add, e.g. test fonts to EXTRA_DIST
make distcheck
```
### Run tests with asan
**NOTE**: this sometimes yields harder to read results than the full fuzzer
```shell
# For nice symbols tell asan how to symoblize. Note that it doesn't like versioned copies like llvm-symbolizer-3.8
# export ASAN_SYMBOLIZER_PATH=path to version-less llvm-symbolizer
# ex
export ASAN_SYMBOLIZER_PATH=/usr/lib/llvm-3.8/bin/llvm-symbolizer
./configure CC=clang CXX=clang++ CPPFLAGS=-fsanitize=address LDFLAGS=-fsanitize=address
# make/run tests as usual
```
### Debug with GDB
```
cd ./util
../libtool --mode=execute gdb --args ./hb-subset ...
```
### Enable Debug Logging
```shell
# make clean if you previously build w/o debug logging
make CPPFLAGS=-DHB_DEBUG_SUBSET=100
```
## Build and Test via CMake
Note: You'll need to first install ninja-build via apt-get.
```shell
meson build && ninja -Cbuild && ninja -Cbuild test
```
## Test with the Fuzzer
```shell
# push your changs to a branch on googlefonts/harfbuzz
# In a local copy of oss-fuzz, edit projects/harfbuzz/Dockerfile
# Change the git clone to pull your branch
# Do this periodically
sudo python infra/helper.py build_image harfbuzz
# Do these to update/run
sudo python infra/helper.py build_fuzzers --sanitizer address harfbuzz
sudo python infra/helper.py run_fuzzer harfbuzz hb-subset-fuzzer
```
## Profiling
```
make clean
./configure CXXFLAGS="-fno-omit-frame-pointer -g"
make
perf record -o <perf output file> -g <command to run>
perf report -i<perf output file>
```

2
THANKS
View File

@ -1,6 +1,6 @@
Bradley Grainger
Khaled Hosny
Kenichi Ishibashi
Ivan Kuckir <https://photopea.com/>
Ryan Lortie
Jeff Muizelaar
suzuki toshiya

View File

@ -4,56 +4,50 @@ 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
vcvarsallpath: C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat
arch: amd64
- compiler: msvc
generator: Visual Studio 14 ARM
platform: ARM
configuration: Debug
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
compiler: msvc
generator: Visual Studio 15
vcvarsallpath: C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvarsall.bat
arch: x86
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
compiler: msvc
generator: Visual Studio 16
vcvarsallpath: C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvarsall.bat
arch: amd64
- compiler: msys2
MINGW_PREFIX: /mingw64
MINGW_CHOST: x86_64-w64-mingw32
MSYS2_ARCH: x86_64
MSYSTEM: MINGW64
- compiler: msys2
MINGW_PREFIX: /mingw32
MINGW_CHOST: i686-w64-mingw32
MSYS2_ARCH: i686
MSYSTEM: MINGW32
install:
# - 'if "%compiler%"=="msys2" C:\msys64\usr\bin\bash -lc "pacman --noconfirm --force -Sy && pacman --noconfirm --force -S pacman-mirrors && pacman --force -Syu --noconfirm"'
- 'if "%compiler%"=="msys2" C:\msys64\usr\bin\bash -lc "pacman --noconfirm --force -S --needed mingw-w64-$MSYS2_ARCH-{gcc,freetype,cairo,icu,gettext,gobject-introspection,gcc,gcc-libs,glib2,graphite2,pkg-config,python2}"'
- C:\msys64\usr\bin\bash -lc "pacman --noconfirm -S mingw-w64-x86_64-ragel"
- set PATH=%PATH%;C:\msys64\mingw64\bin # msys2 is added just for having "ragel" on PATH
- 'if "%compiler%"=="cygwin" %CYGWIN_PREFIX%\setup-%CYGWIN_ARCH%.exe -g -q -P cygwin-devel,libfreetype-devel,libcairo-devel,libicu-devel,gcc,gcc-g++,gobject-introspection,libglib2.0-devel,libgraphite2-devel,pkg-config,python2'
- 'if "%compiler%"=="msvc" if not "%platform%"=="ARM" vcpkg install glib:%triplet% freetype:%triplet% cairo:%triplet%'
- 'if "%compiler%"=="msys2" C:\msys64\usr\bin\bash -lc "pacman --noconfirm --force -S --needed mingw-w64-$MSYS2_ARCH-{gcc,freetype,cairo,icu,gettext,gobject-introspection,gcc,gcc-libs,glib2,graphite2,pkg-config,python3,python3-pip,ragel,meson,ninja}"'
- 'if "%compiler%"=="msys2" C:\msys64\usr\bin\bash -lc "pip install fonttools"'
build_script:
- 'if "%compiler%"=="msvc" md build'
- 'if "%compiler%"=="msvc" cd build'
- 'if "%compiler%"=="msvc" set PATH=%PATH%;C:\Program Files (x86)\MSBuild\14.0\Bin'
- '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%'
- set "PYTHON_ROOT=C:\python37-x64"
- set "PATH=%PYTHON_ROOT%;%PYTHON_ROOT%\Scripts;%PATH%"
- pip install --upgrade meson
- pip install fonttools
- 'if "%compiler%"=="msvc" "%vcvarsallpath%" %arch% && meson setup build --buildtype=release -Dglib=enabled -Dfreetype=enabled -Dgdi=enabled -Ddirectwrite=enabled && meson test --print-errorlogs --suite=harfbuzz -Cbuild'
- 'if "%compiler%"=="msys2" C:\msys64\usr\bin\bash -lc "curl https://raw.githubusercontent.com/mirror/mingw-w64/023eb04c396d4e8d8fcf604cfababc53dae13398/mingw-w64-headers/include/dwrite_1.h > %MINGW_PREFIX%/%MINGW_CHOST%/include/dwrite_1.h"'
- '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 --with-directwrite --build=%MINGW_CHOST% --host=%MINGW_CHOST% --prefix=%MINGW_PREFIX%; make -j3 check || .ci/fail.sh"'
- 'if "%compiler%"=="msys2" C:\msys64\usr\bin\bash -lc "cd $APPVEYOR_BUILD_FOLDER; meson build --wrap-mode=nofallback -Dfreetype=enabled -Dglib=enabled -Dcairo=enabled -Dgobject=enabled -Dgdi=enabled -Ddirectwrite=enabled -Dgraphite=enabled && meson test -Cbuild --print-errorlogs"'
cache:
- c:\tools\vcpkg\installed\
- '%CYGWIN_PREFIX%\var\cache\setup'
notifications:
- provider: Email

View File

@ -1,21 +0,0 @@
pool:
vmImage: 'VS2017-Win2016'
variables:
buildPlatform: 'x86'
buildConfiguration: 'Debug'
triplet: 'x86-windows'
steps:
- script: |
git clone https://github.com/Microsoft/vcpkg
cd vcpkg
.\bootstrap-vcpkg.bat
.\vcpkg integrate install
.\vcpkg install glib:x86-windows freetype:x86-windows cairo:x86-windows
cd ..
cmake -Bbuild -H. -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=vcpkg/scripts/buildsystems/vcpkg.cmake ../
msbuild harfbuzz.sln /p:Configuration=Debug /p:Platform=Win32
cd build
ctest --output-on-failure -C Debug
displayName: Build and test

View File

@ -1,6 +1,6 @@
AC_PREREQ([2.64])
AC_INIT([HarfBuzz],
[2.4.0],
[2.6.5],
[https://github.com/harfbuzz/harfbuzz/issues/new],
[harfbuzz],
[http://harfbuzz.org/])
@ -9,7 +9,7 @@ AC_CONFIG_MACRO_DIR([m4])
AC_CONFIG_SRCDIR([src/harfbuzz.pc.in])
AC_CONFIG_HEADERS([config.h])
AM_INIT_AUTOMAKE([1.13.0 gnits tar-ustar dist-bzip2 no-dist-gzip -Wall no-define color-tests -Wno-portability])
AM_INIT_AUTOMAKE([1.13.0 gnits tar-ustar dist-xz no-dist-gzip -Wall no-define color-tests -Wno-portability])
AM_SILENT_RULES([yes])
AX_CODE_COVERAGE
@ -23,7 +23,7 @@ AC_PROG_CC
AC_PROG_CC_C99
AM_PROG_CC_C_O
AC_PROG_CXX
AX_CXX_COMPILE_STDCXX(11,, optional)
AX_CXX_COMPILE_STDCXX(11)
AC_SYS_LARGEFILE
PKG_PROG_PKG_CONFIG([0.20])
AM_MISSING_PROG([RAGEL], [ragel])
@ -77,13 +77,7 @@ GTK_DOC_CHECK([1.15],[--flavour no-tmpl])
])
# Functions and headers
AC_CHECK_FUNCS(atexit mprotect sysconf getpagesize mmap isatty newlocale strtod_l posix_memalign)
save_libs="$LIBS"
LIBS="$LIBS -lm"
AC_CHECK_FUNCS([round], ,[AC_CHECK_DECLS([round], , ,[#include <math.h>])])
LIBS="$save_libs"
AC_CHECK_FUNCS(atexit mprotect sysconf getpagesize mmap isatty newlocale strtod_l roundf)
AC_CHECK_HEADERS(unistd.h sys/mman.h xlocale.h stdbool.h)
# Compiler flags
@ -134,9 +128,7 @@ AC_MSG_RESULT([$hb_os_win32])
AM_CONDITIONAL(OS_WIN32, test "$hb_os_win32" = "yes")
have_pthread=false
if test "$hb_os_win32" = no; then
AX_PTHREAD([have_pthread=true])
fi
AX_PTHREAD([have_pthread=true])
if $have_pthread; then
AC_DEFINE(HAVE_PTHREAD, 1, [Have POSIX threads])
fi
@ -144,14 +136,6 @@ AM_CONDITIONAL(HAVE_PTHREAD, $have_pthread)
dnl ==========================================================================
have_fallback=true
if $have_fallback; then
AC_DEFINE(HAVE_FALLBACK, 1, [Have simple TrueType Layout backend])
fi
AM_CONDITIONAL(HAVE_FALLBACK, $have_fallback)
dnl ===========================================================================
AC_ARG_WITH(glib,
[AS_HELP_STRING([--with-glib=@<:@yes/no/auto@:>@],
[Use glib @<:@default=auto@:>@])],,
@ -300,21 +284,6 @@ AM_CONDITIONAL(HAVE_ICU_BUILTIN, $have_icu && test "x$with_icu" = "xbuiltin")
dnl ===========================================================================
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])
fi
AM_CONDITIONAL(HAVE_UCDN, $have_ucdn)
dnl ==========================================================================
AC_ARG_WITH(graphite2,
[AS_HELP_STRING([--with-graphite2=@<:@yes/no/auto@:>@],
[Use the graphite2 library @<:@default=no@:>@])],,
@ -392,6 +361,28 @@ AM_CONDITIONAL(HAVE_UNISCRIBE, $have_uniscribe)
dnl ===========================================================================
AC_ARG_WITH(gdi,
[AS_HELP_STRING([--with-gdi=@<:@yes/no/auto@:>@],
[Provide GDI integration helpers @<:@default=no@:>@])],,
[with_gdi=no])
have_gdi=false
if test "x$with_gdi" = "xyes" -o "x$with_gdi" = "xauto"; then
AC_CHECK_HEADERS(windows.h, have_gdi=true)
fi
if test "x$with_gdi" = "xyes" -a "x$have_gdi" != "xtrue"; then
AC_MSG_ERROR([gdi support requested but not found])
fi
if $have_gdi; then
GDI_CFLAGS=
GDI_LIBS="-lgdi32"
AC_SUBST(GDI_CFLAGS)
AC_SUBST(GDI_LIBS)
AC_DEFINE(HAVE_GDI, 1, [Have GDI library])
fi
AM_CONDITIONAL(HAVE_GDI, $have_gdi)
dnl ===========================================================================
AC_ARG_WITH(directwrite,
[AS_HELP_STRING([--with-directwrite=@<:@yes/no/auto@:>@],
[Use the DirectWrite library (experimental) @<:@default=no@:>@])],,
@ -407,7 +398,7 @@ if test "x$with_directwrite" = "xyes" -a "x$have_directwrite" != "xtrue"; then
fi
if $have_directwrite; then
DIRECTWRITE_CXXFLAGS=
DIRECTWRITE_LIBS="-ldwrite"
DIRECTWRITE_LIBS=
AC_SUBST(DIRECTWRITE_CXXFLAGS)
AC_SUBST(DIRECTWRITE_LIBS)
AC_DEFINE(HAVE_DIRECTWRITE, 1, [Have DirectWrite library])
@ -497,7 +488,6 @@ AC_CONFIG_FILES([
Makefile
src/Makefile
src/harfbuzz-config.cmake
src/hb-ucdn/Makefile
util/Makefile
test/Makefile
test/api/Makefile
@ -525,7 +515,7 @@ AC_MSG_NOTICE([
Build configuration:
Unicode callbacks (you want at least one):
Builtin (UCDN): ${have_ucdn}
Builtin true
Glib: ${have_glib}
ICU: ${have_icu}
@ -542,6 +532,7 @@ Additional shapers (the more the merrier):
Platform shapers (not normally needed):
CoreText: ${have_coretext}
DirectWrite: ${have_directwrite}
GDI: ${have_gdi}
Uniscribe: ${have_uniscribe}
Other features:

View File

@ -33,7 +33,7 @@ SCAN_OPTIONS=--rebuild-types --deprecated-guards="HB_DISABLE_DEPRECATED" \
# Header files or dirs to ignore when scanning. Use base file/dir names
# e.g. IGNORE_HFILES=gtkdebug.h gtkintl.h private_code
IGNORE_HFILES=`cd $(top_srcdir)/src; find . -path './hb-*/*.h' | sed 's@^.*/@@'`
IGNORE_HFILES=`cd $(top_srcdir)/src; find . -path './*/*.h' | sed 's@^.*/@@'`
if HAVE_GOBJECT
else
IGNORE_HFILES+=hb-gobject.h hb-gobject-enums.h hb-gobject-structs.h
@ -75,6 +75,7 @@ content_files= \
usermanual-what-is-harfbuzz.xml \
usermanual-install-harfbuzz.xml \
usermanual-getting-started.xml \
usermanual-glyph-information.xml \
usermanual-shaping-concepts.xml \
usermanual-object-model.xml \
usermanual-buffers-language-script-and-direction.xml \

View File

@ -20,11 +20,7 @@
<para>
The canonical source-code tree is available at
<ulink
url="https://github.com/harfbuzz/harfbuzz">github.com/harfbuzz/harfbuzz</ulink>
and is also available at
<ulink
url="http://cgit.freedesktop.org/harfbuzz/">cgit.freedesktop.org/harfbuzz</ulink>.
<ulink url="https://github.com/harfbuzz/harfbuzz">github.com/harfbuzz/harfbuzz</ulink>.
See <xref linkend="download" endterm="download.title"/> for
release tarballs.
</para>
@ -138,6 +134,10 @@
<index id="api-index-full"><title>API Index</title><xi:include href="xml/api-index-full.xml"><xi:fallback /></xi:include></index>
<index id="deprecated-api-index" role="deprecated"><title>Index of deprecated API</title><xi:include href="xml/api-index-deprecated.xml"><xi:fallback /></xi:include></index>
<index id="api-index-2-6-0" role="2.6.0"><title>Index of new symbols in 2.6.0</title><xi:include href="xml/api-index-2.6.0.xml"><xi:fallback /></xi:include></index>
<index id="api-index-2-5-0" role="2.5.0"><title>Index of new symbols in 2.5.0</title><xi:include href="xml/api-index-2.5.0.xml"><xi:fallback /></xi:include></index>
<index id="api-index-2-4-0" role="2.4.0"><title>Index of new symbols in 2.4.0</title><xi:include href="xml/api-index-2.4.0.xml"><xi:fallback /></xi:include></index>
<index id="api-index-2-3-0" role="2.3.0"><title>Index of new symbols in 2.3.0</title><xi:include href="xml/api-index-2.3.0.xml"><xi:fallback /></xi:include></index>
<index id="api-index-2-2-0" role="2.2.0"><title>Index of new symbols in 2.2.0</title><xi:include href="xml/api-index-2.2.0.xml"><xi:fallback /></xi:include></index>
<index id="api-index-2-1-0" role="2.1.0"><title>Index of new symbols in 2.1.0</title><xi:include href="xml/api-index-2.1.0.xml"><xi:fallback /></xi:include></index>
<index id="api-index-2-0-0" role="2.0.0"><title>Index of new symbols in 2.0.0</title><xi:include href="xml/api-index-2.0.0.xml"><xi:fallback /></xi:include></index>

View File

@ -1,6 +1,7 @@
<SUBSECTION Private>
HB_H_IN
HB_OT_H_IN
HB_AAT_H_IN
</SECTION>
<SECTION>
@ -179,6 +180,7 @@ HB_BUFFER_SERIALIZE_FLAGS_DEFAULT
HB_SCRIPT_CANADIAN_ABORIGINAL
hb_font_funcs_set_glyph_func
hb_font_get_glyph_func_t
HB_MATH_GLYPH_PART_FLAG_EXTENDER
hb_ot_layout_table_choose_script
hb_ot_layout_table_find_script
hb_ot_tag_from_language
@ -195,12 +197,7 @@ HB_UNICODE_MAX_DECOMPOSITION_LEN
hb_unicode_decompose_compatibility_func_t
hb_unicode_decompose_compatibility
hb_unicode_funcs_set_decompose_compatibility_func
hb_font_funcs_set_glyph_h_kerning_func
hb_font_funcs_set_glyph_v_kerning_func
hb_font_get_glyph_h_kerning
hb_font_get_glyph_h_kerning_func_t
hb_font_get_glyph_kerning_for_direction
hb_font_get_glyph_kerning_func_t
hb_font_get_glyph_v_kerning
hb_font_get_glyph_v_kerning_func_t
</SECTION>
@ -271,6 +268,7 @@ hb_font_funcs_set_glyph_extents_func
hb_font_funcs_set_glyph_from_name_func
hb_font_funcs_set_glyph_h_advance_func
hb_font_funcs_set_glyph_h_advances_func
hb_font_funcs_set_glyph_h_kerning_func
hb_font_funcs_set_glyph_h_origin_func
hb_font_funcs_set_glyph_name_func
hb_font_funcs_set_glyph_v_advance_func
@ -300,8 +298,12 @@ hb_font_get_glyph_h_advance
hb_font_get_glyph_h_advance_func_t
hb_font_get_glyph_h_advances
hb_font_get_glyph_h_advances_func_t
hb_font_get_glyph_h_kerning
hb_font_get_glyph_h_kerning_func_t
hb_font_get_glyph_h_origin
hb_font_get_glyph_h_origin_func_t
hb_font_get_glyph_kerning_for_direction
hb_font_get_glyph_kerning_func_t
hb_font_get_glyph_name
hb_font_get_glyph_name_func_t
hb_font_get_glyph_origin_for_direction
@ -323,6 +325,7 @@ 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_design
hb_font_get_var_coords_normalized
hb_font_glyph_from_string
hb_font_glyph_to_string
@ -340,6 +343,7 @@ hb_font_set_user_data
hb_font_set_variations
hb_font_set_var_coords_design
hb_font_set_var_coords_normalized
hb_font_set_var_named_instance
hb_font_subtract_glyph_origin_for_direction
hb_font_t
hb_reference_table_func_t
@ -362,11 +366,18 @@ hb_ft_font_create
hb_ft_font_create_referenced
hb_ft_font_changed
hb_ft_font_get_face
hb_ft_font_lock_face
hb_ft_font_unlock_face
hb_ft_font_set_load_flags
hb_ft_font_get_load_flags
hb_ft_font_set_funcs
</SECTION>
<SECTION>
<FILE>hb-gdi</FILE>
hb_gdi_face_create
</SECTION>
<SECTION>
<FILE>hb-glib</FILE>
hb_glib_get_unicode_funcs
@ -540,6 +551,9 @@ HB_OT_TAG_GDEF
HB_OT_TAG_GPOS
HB_OT_TAG_GSUB
HB_OT_TAG_JSTF
hb_ot_layout_baseline_tag_t
hb_ot_layout_closure_lookups
hb_ot_layout_closure_features
hb_ot_layout_collect_lookups
hb_ot_layout_collect_features
hb_ot_layout_feature_get_characters
@ -547,6 +561,7 @@ hb_ot_layout_feature_get_lookups
hb_ot_layout_feature_get_name_ids
hb_ot_layout_feature_with_variations_get_lookups
hb_ot_layout_get_attach_points
hb_ot_layout_get_baseline
hb_ot_layout_get_glyph_class
hb_ot_layout_get_glyphs_in_class
hb_ot_layout_get_ligature_carets
@ -600,6 +615,22 @@ hb_ot_math_get_min_connector_overlap
hb_ot_math_get_glyph_assembly
</SECTION>
<SECTION>
<FILE>hb-ot-meta</FILE>
hb_ot_meta_tag_t
hb_ot_meta_get_entry_tags
hb_ot_meta_reference_entry
</SECTION>
<SECTION>
<FILE>hb-ot-metrics</FILE>
hb_ot_metrics_tag_t
hb_ot_metrics_get_position
hb_ot_metrics_get_variation
hb_ot_metrics_get_x_variation
hb_ot_metrics_get_y_variation
</SECTION>
<SECTION>
<FILE>hb-ot-shape</FILE>
hb_ot_shape_glyphs_closure

View File

@ -193,7 +193,7 @@
hb_buffer_set_language(buf, hb_language_from_string("en", -1));
</programlisting>
<para>
However, since these properties are often the repeated for
However, since these properties are often repeated for
multiple text runs, you can also save them in a
<literal>hb_segment_properties_t</literal> for reuse:
</para>
@ -215,7 +215,7 @@
bottom-to-top (<literal>HB_DIRECTION_BTT</literal>). For the
script property, HarfBuzz uses identifiers based on the
<ulink
url="https://unicode.org/iso15924/">ISO-15924
url="https://unicode.org/iso15924/">ISO 15924
standard</ulink>. For languages, HarfBuzz uses tags based on the
<ulink url="https://tools.ietf.org/html/bcp47">IETF BCP 47</ulink> standard.
</para>
@ -300,10 +300,13 @@
decomposing code points.
</para>
<para>
At build time, HarfBuzz looks first for the GLib library. If
it is found, HarfBuzz will use GLib's Unicode functions by
default. If there is no GLib, HarfBuzz will fall back to its own
internal, lightweight set of Unicode functions instead.
HarfBuzz includes its own internal, lightweight set of Unicode
functions. At build time, it is also possible to compile support
for some other options, such as the Unicode functions provided
by GLib or the International Components for Unicode (ICU)
library. Generally, this option is only of interest for client
programs that have specific integration requirements or that do
a significant amount of customization.
</para>
<para>
If your program has access to other Unicode functions, however,

View File

@ -156,18 +156,20 @@
order.
</para>
<para>
For left-to-right scripts (LTR) and top-to-bottom scripts (TTB),
For buffers in the left-to-right (LTR)
or top-to-bottom (TTB) text flow direction,
HarfBuzz will preserve the monotonic property: client programs
are guaranteed that monotonically increasing initial clulster
are guaranteed that monotonically increasing initial cluster
values will be returned as monotonically increasing final
cluster values.
</para>
<para>
For right-to-left scripts (RTL) and bottom-to-top scripts (BTT),
For buffers in the right-to-left (RTL)
or bottom-to-top (BTT) text flow direction,
the directionality of the buffer itself is reversed for final
output as a matter of design. Therefore, HarfBuzz inverts the
monotonic property: client programs are guaranteed that
monotonically increasing initial clulster values will be
monotonically increasing initial cluster values will be
returned as monotonically <emphasis>decreasing</emphasis> final
cluster values.
</para>

View File

@ -37,7 +37,7 @@
An <type>hb_face_t</type> represents a <emphasis>face</emphasis>
in HarfBuzz. This data type is a wrapper around an
<type>hb_blob_t</type> blob that holds the contents of a binary
fotn file. Since HarfBuzz supports TrueType Collections and
font file. Since HarfBuzz supports TrueType Collections and
OpenType Collections (each of which can include multiple
typefaces), a HarfBuzz face also requires an index number
specifying which typeface in the file you want to use. Most of
@ -86,7 +86,7 @@
objects. <function>hb_font_set_ppem(font, x_ppem,
y_ppem)</function> sets the pixels-per-EM value of the font. You
can also set the point size of the font with
<function>hb_font_set_ppem(font, ptem)</function>. HarfBuzz uses the
<function>hb_font_set_ptem(font, ptem)</function>. HarfBuzz uses the
industry standard 72 points per inch.
</para>
<para>

View File

@ -246,7 +246,7 @@
<listitem>
<para>
Use <ulink url="https://developer.gnome.org/glib/">GLib</ulink>. <emphasis>(Default = auto)</emphasis>
</para>
</para>
<para>
This option enables or disables usage of the GLib
library. The default setting is to check for the
@ -297,7 +297,7 @@
<listitem>
<para>
Use <ulink url="https://www.freedesktop.org/wiki/Software/fontconfig/">Fontconfig</ulink>. <emphasis>(Default = auto)</emphasis>
</para>
</para>
<para>
This option enables or disables usage of the Fontconfig
library, which provides font-matching functions and
@ -317,7 +317,7 @@
<listitem>
<para>
Use the <ulink url="http://site.icu-project.org/home">ICU</ulink> library. <emphasis>(Default = auto)</emphasis>
</para>
</para>
<para>
This option enables or disables usage of the
<emphasis>International Components for
@ -330,30 +330,12 @@
</listitem>
</varlistentry>
<varlistentry>
<term><command>--with-ucdn</command></term>
<listitem>
<para>
Use HarfBuzz's <ulink url="https://github.com/harfbuzz/harfbuzz/tree/master/src/hb-ucdn">built-in UCDN library</ulink>. <emphasis>(Default = auto)</emphasis>
</para>
<para>
The HarfBuzz source tree includes a <emphasis>Unicode
Database and Normalization</emphasis> (UCDN) library
that provides access to basic character properties in
the Unicode Character Database (UCD) as well as low-level
normalization functions. HarfBuzz can be built without
this UCDN support if the usage of a different UCDN
library is desired.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>--with-graphite2</command></term>
<listitem>
<para>
Use the <ulink url="http://graphite.sil.org/">Graphite2</ulink> library. <emphasis>(Default = no)</emphasis>
</para>
</para>
<para>
This option enables or disables usage of the Graphite2
library, which provides support for the Graphite shaping
@ -367,7 +349,7 @@
<listitem>
<para>
Use the <ulink url="https://www.freetype.org/">FreeType</ulink> library. <emphasis>(Default = auto)</emphasis>
</para>
</para>
<para>
This option enables or disables usage of the FreeType
font-rendering library. The default setting is to check for the
@ -384,7 +366,7 @@
Use the <ulink
url="https://docs.microsoft.com/en-us/windows/desktop/intl/uniscribe">Uniscribe</ulink>
library (experimental). <emphasis>(Default = no)</emphasis>
</para>
</para>
<para>
This option enables or disables usage of the Uniscribe
font-rendering library. Uniscribe is available on
@ -400,7 +382,7 @@
<listitem>
<para>
Use the <ulink url="https://docs.microsoft.com/en-us/windows/desktop/directwrite/direct-write-portal">DirectWrite</ulink> library (experimental). <emphasis>(Default = no)</emphasis>
</para>
</para>
<para>
This option enables or disables usage of the DirectWrite
font-rendering library. DirectWrite is available on
@ -416,7 +398,7 @@
<listitem>
<para>
Use the <ulink url="https://developer.apple.com/documentation/coretext">CoreText</ulink> library. <emphasis>(Default = no)</emphasis>
</para>
</para>
<para>
This option enables or disables usage of the CoreText
library. CoreText is available on macOS and iOS systems.

View File

@ -244,7 +244,7 @@
<function>malloc()</function>, you would create the blob using
</para>
<programlisting language="C">
hb_blob_create (data, length, HB_MEMORY_MODE_WRITABLE, NULL, free)
hb_blob_create (data, length, HB_MEMORY_MODE_WRITABLE, data, free)
</programlisting>
<para>
That way, HarfBuzz will call <function>free()</function> on the

View File

@ -66,15 +66,15 @@
<para>
The algorithms
used for complex scripts can be quite involved; HarfBuzz tries
to be 100% compatible with the OpenType Layout specification
to be compatible with the OpenType Layout specification
and, wherever there is any ambiguity, HarfBuzz attempts to replicate the
output of Microsoft's Uniscribe engine. See the <ulink
url="https://docs.microsoft.com/en-us/typography/script-development/standard">Microsoft
Typeography pages</ulink> for more detail.
Typography pages</ulink> for more detail.
</para>
<para>
In general, though, all that you need to know if that
<function>hb-shape()</function> returns the results of shaping
In general, though, all that you need to know is that
<function>hb_shape()</function> returns the results of shaping
in the same buffer that you provided. The buffer's content type
will now be set to
<literal>HB_BUFFER_CONTENT_TYPE_GLYPHS</literal>, indicating
@ -139,7 +139,8 @@
Other features are more generic and can apply to several (or
any) script, and shaping engines are expected to implement
them. By default, HarfBuzz activates several of these features
on every text run. They include <literal>ccmp</literal>,
on every text run. They include <literal>abvm</literal>,
<literal>blwm</literal>, <literal>ccmp</literal>,
<literal>locl</literal>, <literal>mark</literal>,
<literal>mkmk</literal>, and <literal>rlig</literal>.
</para>
@ -147,8 +148,9 @@
In addition, if the text direction is horizontal, HarfBuzz
also applies the <literal>calt</literal>,
<literal>clig</literal>, <literal>curs</literal>,
<literal>kern</literal>, <literal>liga</literal>,
<literal>rclt</literal>, and <literal>frac</literal> features.
<literal>dist</literal>, <literal>kern</literal>,
<literal>liga</literal>, <literal>rclt</literal>,
and <literal>frac</literal> features.
</para>
<para>
If the text direction is vertical, HarfBuzz applies
@ -194,7 +196,10 @@
Just like we enabled the <literal>dlig</literal> feature by
setting its <parameter>value</parameter> to
<literal>1</literal>, you would disable a feature by setting its
<parameter>value</parameter> to <literal>0</literal>.
<parameter>value</parameter> to <literal>0</literal>. Some
features can take other <parameter>value</parameter> settings;
be sure you read the full specification of each feature tag to
understand what it does and how to control it.
</para>
</section>
@ -212,7 +217,7 @@
mix of tables, or one of the tables might simply be broken for
the script you need to shape. So, sometimes, you might not
want to rely on HarfBuzz's process for deciding what to do, and
just tell hb-shape what you want it to try.
just tell <function>hb_shape()</function> what you want it to try.
</para>
<para>
<function>hb_shape_full()</function> is an alternate shaping
@ -243,7 +248,7 @@
<para>
Internally, HarfBuzz uses a structure called a shape plan to
track its decisions about how to shape the contents of a
buffer. The hb-shape function builds up the shape plan by
buffer. The <function>hb_shape()</function> function builds up the shape plan by
examining segment properties and by inspecting the contents of
the font.
</para>

View File

@ -18,8 +18,8 @@
<title>Command-line tools</title>
<para>
HarfBuzz include three command-line tools:
<program>hb-shape</program>, <program>hb-view</program>, and
<program>hb-subset</program>. They can be used to examine
<command>hb-shape</command>, <command>hb-view</command>, and
<command>hb-subset</command>. They can be used to examine
HarfBuzz's functionality, debug font binaries, or explore the
various shaping models and features from a terminal.
</para>
@ -27,12 +27,12 @@
<section id="utilities-command-line-hbshape">
<title>hb-shape</title>
<para>
<emphasis><program>hb-shape</program></emphasis> allows you to run HarfBuzz's
<emphasis><command>hb-shape</command></emphasis> allows you to run HarfBuzz's
<function>hb_shape()</function> function on an input string and
to examine the outcome, in human-readable form, as terminal
output. <program>hb-shape</program> does
output. <command>hb-shape</command> does
<emphasis>not</emphasis> render the results of the shaping call
into rendered text (you can use <program>hb-view</program>, below, for
into rendered text (you can use <command>hb-view</command>, below, for
that). Instead, it prints out the final glyph indices and
positions, taking all shaping operations into account, as if the
input string were a HarfBuzz input buffer.
@ -80,10 +80,10 @@
<section id="utilities-command-line-hbview">
<title>hb-view</title>
<para>
<emphasis><program>hb-view</program></emphasis> allows you to
<emphasis><command>hb-view</command></emphasis> allows you to
see the shaped output of an input string in rendered
form. Like <program>hb-shape</program>,
<program>hb-view</program> takes a font file and a text string
form. Like <command>hb-shape</command>,
<command>hb-view</command> takes a font file and a text string
as its arguments:
</para>
<programlisting>
@ -92,7 +92,7 @@
<parameter>yourinputtext</parameter>
</programlisting>
<para>
By default, <program>hb-view</program> renders the shaped
By default, <command>hb-view</command> renders the shaped
text in ASCII block-character images as terminal output. By
appending the
<command>--output-file=<optional>filename</optional></command>
@ -100,7 +100,7 @@
(among other formats).
</para>
<para>
As with <program>hb-shape</program>, a lengthy set of options
As with <command>hb-shape</command>, a lengthy set of options
is available, with which you can enable or disable
specific font features, set variation-font axis values,
alter the language, script, direction, and clustering settings
@ -114,10 +114,10 @@
with
</para>
<para>
In general, <program>hb-view</program> is a quick way to
In general, <command>hb-view</command> is a quick way to
verify that the output of HarfBuzz's shaping operation looks
correct for a given text-and-font combination, but you may
want to use <program>hb-shape</program> to figure out exactly
want to use <command>hb-shape</command> to figure out exactly
why something does not appear as expected.
</para>
</section>
@ -125,13 +125,13 @@
<section id="utilities-command-line-hbsubset">
<title>hb-subset</title>
<para>
<emphasis><program>hb-subset</program></emphasis> allows you
<emphasis><command>hb-subset</command></emphasis> allows you
to generate a subset of a given font, with a limited set of
supported characters, features, and variation settings.
</para>
<para>
By default, you provide an input font and an input text string
as the arguments to <program>hb-subset</program>, and it will
as the arguments to <command>hb-subset</command>, and it will
generate a font that covers the input text exactly like the
input font does, but includes no other characters or features.
</para>

View File

@ -151,9 +151,9 @@
</para>
<para>
For example, in Tamil, when the letter &quot;TTA&quot; (ட)
letter is followed by &quot;U&quot; (உ), the pair
letter is followed by the vowel sign &quot;U&quot; (ு), the pair
must be replaced by the single glyph &quot;டு&quot;. The
sequence of Unicode characters &quot;&quot; needs to be
sequence of Unicode characters &quot;,ு&quot; needs to be
substituted with a single &quot;டு&quot; glyph from the
font.
</para>

View File

@ -422,7 +422,7 @@ namespace cxx11
}
// http://stackoverflow.com/questions/13728184/template-aliases-and-sfinae
// https://stackoverflow.com/questions/13728184/template-aliases-and-sfinae
// Clang 3.1 fails with headers of libstd++ 4.8.3 when using std::function
// because of this.
namespace test_template_alias_sfinae

View File

@ -0,0 +1,6 @@
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(void) { return 0;}

View File

@ -0,0 +1,8 @@
#include <atomic.h>
/* This requires Solaris Studio 12.2 or newer: */
#include <mbarrier.h>
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(void) { return 0; }

348
meson.build Normal file
View File

@ -0,0 +1,348 @@
project('harfbuzz', 'c', 'cpp',
meson_version: '>= 0.47.0',
default_options : ['cpp_std=c++11'],
version: '2.6.5')
warning('Meson is not our main build system yet, don\'t use it for packaging HarfBuzz for *nix distros for now')
hb_version_arr = meson.project_version().split('.')
hb_version_major = hb_version_arr[0].to_int()
hb_version_minor = hb_version_arr[1].to_int()
hb_version_micro = hb_version_arr[2].to_int()
# libtool versioning
hb_version_int = hb_version_major*10000 + hb_version_minor*100 + hb_version_micro
if hb_version_minor % 2 == 1
hb_libtool_revision = 0 # for unstable releases
else
hb_libtool_revision = hb_version_micro # for stable releases
endif
hb_libtool_age = hb_version_int - hb_libtool_revision
hb_libtool_current = hb_libtool_age
hb_libtool_version_info = '@0@:@1@:@2@'.format(hb_libtool_current, hb_libtool_revision, hb_libtool_age)
pkgmod = import('pkgconfig')
cpp = meson.get_compiler('cpp')
if cpp.get_id() == 'msvc'
# Ignore several spurious warnings for things HarfBuzz does very commonly.
# If a warning is completely useless and spammy, use '/wdXXXX' to suppress it
# If a warning is harmless but hard to fix, use '/woXXXX' so it's shown once
# NOTE: Only add warnings here if you are sure they're spurious
msvc_args = [
'/wd4018', # implicit signed/unsigned conversion
'/wd4146', # unary minus on unsigned (beware INT_MIN)
'/wd4244', # lossy type conversion (e.g. double -> int)
'/wd4305', # truncating type conversion (e.g. double -> float)
cpp.get_supported_arguments(['/utf-8']), # set the input encoding to utf-8
]
add_project_arguments(msvc_args, language : ['c', 'cpp'])
# Disable SAFESEH with MSVC for libs that use external deps that are built with MinGW
# noseh_link_args = ['/SAFESEH:NO']
endif
add_project_arguments(cpp.get_supported_arguments([
'-fno-rtti',
'-fno-exceptions',
'-fno-threadsafe-statics',
'-fvisibility-inlines-hidden', # maybe shouldn't be applied for mingw
]), language : 'cpp')
if host_machine.cpu_family() == 'arm' and cpp.alignment('struct { char c; }') != 1
if cpp.has_argument('-mstructure-size-boundary=8')
add_project_arguments('-mstructure-size-boundary=8', language : 'cpp')
endif
endif
python3 = import('python').find_installation('python3')
check_headers = [
['unistd.h'],
['sys/mman.h'],
['xlocale.h'],
['stdbool.h'],
]
check_funcs = [
['atexit'],
['mprotect'],
['sysconf'],
['getpagesize'],
['mmap'],
['isatty'],
['newlocale'],
['strtod_l'],
['roundf'],
]
freetype_dep = dependency('freetype2', required: false)
if not freetype_dep.found() and cpp.get_id() == 'msvc'
if cpp.has_header('ft2build.h')
freetype_dep = cpp.find_library('freetype', required: false)
endif
endif
if not freetype_dep.found() and get_option('freetype').enabled()
freetype_dep = dependency('freetype2', fallback: ['freetype2', 'freetype_dep'])
endif
glib_dep = dependency('glib-2.0', required: get_option('glib'),
fallback: ['glib', 'libglib_dep'])
gobject_dep = dependency('gobject-2.0', required: get_option('gobject'),
fallback: ['glib', 'libgobject_dep'])
cairo_dep = dependency('cairo', required: false)
fontconfig_dep = dependency('fontconfig', required: get_option('fontconfig'),
fallback: ['fontconfig', 'fontconfig_dep'])
graphite2_dep = dependency('graphite2', required: get_option('graphite'))
icu_dep = dependency('icu-uc', required: false)
m_dep = cpp.find_library('m', required: false)
if not icu_dep.found() and get_option('icu').enabled()
icu_dep = dependency('icu-uc', required: cpp.get_id() != 'msvc')
endif
if not icu_dep.found() and cpp.get_id() == 'msvc'
if cpp.has_header('unicode/uchar.h') and \
cpp.has_header('unicode/unorm2.h') and \
cpp.has_header('unicode/ustring.h') and \
cpp.has_header('unicode/utf16.h') and \
cpp.has_header('unicode/uversion.h') and \
cpp.has_header('unicode/uscript.h')
if get_option('buildtype') == 'debug'
icu_dep = cpp.find_library('icuucd', required: get_option('icu'))
else
icu_dep = cpp.find_library('icuuc', required: get_option('icu'))
endif
else
if get_option('icu').enabled()
error('ICU headers and libraries must be present to build ICU support')
endif
endif
endif
if not cairo_dep.found() and cpp.get_id() == 'msvc'
if cpp.has_header('cairo.h')
cairo_dep = cpp.find_library('cairo')
endif
endif
if not cairo_dep.found() and get_option('cairo').enabled()
cairo_dep = dependency('cairo', fallback: ['cairo', 'libcairo_dep'])
endif
# Ensure that cairo-ft is fetched from the same library as cairo itself
if cairo_dep.found()
if cairo_dep.type_name() == 'pkgconfig'
cairo_ft_dep = dependency('cairo-ft', required: get_option('cairo'))
else
if cpp.has_header('cairo-ft.h') and \
cpp.has_function('cairo_ft_font_face_create_for_ft_face', dependencies: cairo_dep)
cairo_ft_dep = cairo_dep
endif
endif
else
# Not-found dependency
cairo_ft_dep = dependency('', required: false)
endif
deps = []
conf = configuration_data()
incconfig = include_directories('.')
add_project_arguments('-DHAVE_CONFIG_H', language: ['c', 'cpp'])
warn_cflags = [
'-Wno-non-virtual-dtor',
]
cpp_args = cpp.get_supported_arguments(warn_cflags)
if m_dep.found()
deps += [m_dep]
endif
if glib_dep.found()
conf.set('HAVE_GLIB', 1)
deps += [glib_dep]
endif
if gobject_dep.found()
conf.set('HAVE_GOBJECT', 1)
deps += [gobject_dep]
endif
if cairo_dep.found()
conf.set('HAVE_CAIRO', 1)
deps += [cairo_dep]
endif
if cairo_ft_dep.found()
conf.set('HAVE_CAIRO_FT', 1)
deps += [cairo_ft_dep]
endif
if graphite2_dep.found()
conf.set('HAVE_GRAPHITE2', 1)
deps += [graphite2_dep]
endif
if icu_dep.found()
conf.set('HAVE_ICU', 1)
endif
if get_option('icu-builtin')
conf.set('HAVE_ICU_BUILTIN', 1)
endif
if get_option('experimental-api')
conf.set('HB_EXPERIMENTAL_API', 1)
endif
if freetype_dep.found()
conf.set('HAVE_FREETYPE', 1)
deps += [freetype_dep]
check_freetype_funcs = [
['FT_Get_Var_Blend_Coordinates', {'deps': freetype_dep}],
['FT_Set_Var_Blend_Coordinates', {'deps': freetype_dep}],
['FT_Done_MM_Var', {'deps': freetype_dep}],
]
if freetype_dep.type_name() == 'internal'
foreach func: check_freetype_funcs
name = func[0]
conf.set('HAVE_@0@'.format(name.to_upper()), 1)
endforeach
else
check_funcs += check_freetype_funcs
endif
endif
if fontconfig_dep.found()
conf.set('HAVE_FONTCONFIG', 1)
deps += [fontconfig_dep]
endif
# GDI (uniscribe) (windows)
if host_machine.system() == 'windows' and not get_option('gdi').disabled()
# TODO: make nicer once we have https://github.com/mesonbuild/meson/issues/3940
if cpp.has_header('usp10.h') and cpp.has_header('windows.h')
foreach usplib : ['usp10', 'gdi32', 'rpcrt4']
deps += [cpp.find_library(usplib, required: true)]
endforeach
conf.set('HAVE_UNISCRIBE', 1)
conf.set('HAVE_GDI', 1)
elif get_option('gdi').enabled()
error('gdi was enabled explicitly, but some required headers are missing.')
endif
endif
# DirectWrite (windows)
if host_machine.system() == 'windows' and not get_option('directwrite').disabled()
if cpp.has_header('dwrite_1.h')
deps += [cpp.find_library('dwrite', required: true)]
conf.set('HAVE_DIRECTWRITE', 1)
elif get_option('directwrite').enabled()
error('DirectWrite was enabled explicitly, but required header is missing.')
endif
endif
# CoreText (macOS) - FIXME: untested
if host_machine.system() == 'darwin' and not get_option('coretext').disabled()
app_services_dep = dependency('appleframeworks', modules : ['ApplicationServices'], required: false)
if cpp.has_type('CTFontRef', prefix: '#include <ApplicationServices/ApplicationServices.h>', dependencies: app_services_dep)
deps += [app_services_dep]
conf.set('HAVE_CORETEXT', 1)
# On iOS CoreText and CoreGraphics are stand-alone frameworks
# Check for a different symbol to avoid getting cached result
else
coretext_dep = dependency('appleframeworks', modules : ['CoreText'], required: false)
coregraphics_dep = dependency('appleframeworks', modules : ['CoreGraphics'], required: false)
corefoundation_dep = dependency('appleframeworks', modules : ['CoreFoundation'], required: false)
if cpp.has_type('CTRunRef', prefix: '#include <CoreText/CoreText.h>', dependencies: [coretext_dep, coregraphics_dep, corefoundation_dep])
deps += [coretext_dep, coregraphics_dep, corefoundation_dep]
conf.set('HAVE_CORETEXT', 1)
elif get_option('coretext').enabled()
error('CoreText was enabled explicitly, but required headers or frameworks are missing.')
endif
endif
endif
# threads
if host_machine.system() != 'windows'
thread_dep = dependency('threads', required: false)
if thread_dep.found()
conf.set('HAVE_PTHREAD', 1)
deps += [thread_dep]
else
check_headers += ['sched.h']
check_funcs += ['sched_yield', {'link_with': 'rt'}]
endif
endif
conf.set('HAVE_OT', 1)
conf.set('HAVE_FALLBACK', 1)
conf.set_quoted('PACKAGE_NAME', 'HarfBuzz')
conf.set_quoted('PACKAGE_VERSION', meson.project_version())
foreach check : check_headers
name = check[0]
if cpp.has_header(name)
conf.set('HAVE_@0@'.format(name.to_upper().underscorify()), 1)
endif
endforeach
foreach check : check_funcs
name = check[0]
opts = check.get(1, {})
link_withs = opts.get('link_with', [])
check_deps = opts.get('deps', [])
extra_deps = []
found = true
# First try without linking
found = cpp.has_function(name, dependencies: check_deps)
if not found and link_withs.length() > 0
found = true
foreach link_with : link_withs
dep = cpp.find_library(link_with, required: false)
if dep.found()
extra_deps += dep
else
found = false
endif
endforeach
if found
found = cpp.has_function(name, dependencies: check_deps + extra_deps)
endif
endif
if found
deps += extra_deps
conf.set('HAVE_@0@'.format(name.to_upper()), 1)
endif
endforeach
if cpp.links(files('meson-cc-tests/intel-atomic-primitives-test.c'), name: 'Intel atomics')
conf.set('HAVE_INTEL_ATOMIC_PRIMITIVES', 1)
endif
if cpp.links(files('meson-cc-tests/solaris-atomic-operations.c'), name: 'Solaris atomic ops')
conf.set('HAVE_SOLARIS_ATOMIC_OPS', 1)
endif
subdir('src')
subdir('util')
if not get_option('tests').disabled()
subdir('test')
endif
configure_file(output: 'config.h', configuration: conf)

34
meson_options.txt Normal file
View File

@ -0,0 +1,34 @@
# HarfBuzz feature options
option('glib', type: 'feature', value: 'auto',
description: 'Enable GLib unicode functions')
option('gobject', type: 'feature', value: 'disabled',
description: 'Enable GObject bindings')
option('cairo', type: 'feature', value: 'auto',
description: 'Use Cairo graphics library')
option('fontconfig', type: 'feature', value: 'auto',
description: 'Use fontconfig')
option('icu', type: 'feature', value: 'auto',
description: 'Enable ICU library unicode functions')
option('graphite', type: 'feature', value: 'disabled',
description: 'Enable Graphite2 complementary shaper')
option('freetype', type: 'feature', value: 'auto',
description: 'Enable freetype interop helpers')
option('gdi', type: 'feature', value: 'disabled',
description: 'Enable GDI helpers and Uniscribe shaper backend (Windows only)')
option('directwrite', type: 'feature', value: 'disabled',
description: 'Enable DirectWrite shaper backend on Windows (experimental)')
option('coretext', type: 'feature', value: 'disabled',
description: 'Enable CoreText shaper backend on macOS')
# Common feature options
option('tests', type : 'feature', value : 'enabled', yield : true,
description: 'Enable or disable unit tests')
option('introspection', type : 'feature', value : 'disabled', yield : true,
description : 'Generate gobject-introspection bindings (.gir/.typelib files)')
option('icu-builtin', type: 'boolean', value: false,
description: 'Don\'t separate ICU support as harfbuzz-icu module')
option('experimental-api', type: 'boolean', value: false,
description: 'Enable experimental APIs')
option('amalgam', type : 'boolean', value : false,
description : 'Enable amalgam builds')

28
mingw-configure.sh Executable file
View File

@ -0,0 +1,28 @@
#!/bin/sh
case $1 in
i686 | x86_64) ;;
*) echo "Usage: $0 i686|x86_64" >&2; exit 1 ;;
esac
target=$1-w64-mingw32
shift
exec "$(dirname "$0")"/configure \
--build=`../config.guess` \
--host=$target \
--prefix=$HOME/.local/$target \
CC= \
CXX= \
CPP= \
LD= \
CFLAGS="-static-libgcc" \
CXXFLAGS="-static-libgcc -static-libstdc++" \
CPPFLAGS="-I$HOME/.local/$target/include" \
LDFLAGS=-L$HOME/.local/$target/lib \
PKG_CONFIG_LIBDIR=$HOME/.local/$target/lib/pkgconfig:/usr/$target/sys-root/mingw/lib/pkgconfig/ \
PKG_CONFIG_PATH=$HOME/.local/$target/share/pkgconfig:/usr/$target/sys-root/mingw/share/pkgconfig/ \
PATH=$HOME/.local/$target/bin:/usr/$target/sys-root/mingw/bin:/usr/$target/bin:$PATH \
--without-icu \
--with-uniscribe \
"$@"

57
mingw-ldd.py Executable file
View File

@ -0,0 +1,57 @@
#!/usr/bin/env python3
# Copied from https://github.com/xantares/mingw-ldd/blob/master/mingw-ldd.py
# Modified to point to right prefix location on Fedora.
# WTFPL - Do What the Fuck You Want to Public License
import pefile
import os
import sys
def get_dependency(filename):
deps = []
pe = pefile.PE(filename)
for imp in pe.DIRECTORY_ENTRY_IMPORT:
deps.append(imp.dll.decode())
return deps
def dep_tree(root, prefix=None):
if not prefix:
arch = get_arch(root)
#print('Arch =', arch)
prefix = '/usr/'+arch+'-w64-mingw32/sys-root/mingw/bin'
#print('Using default prefix', prefix)
dep_dlls = dict()
def dep_tree_impl(root, prefix):
for dll in get_dependency(root):
if dll in dep_dlls:
continue
full_path = os.path.join(prefix, dll)
if os.path.exists(full_path):
dep_dlls[dll] = full_path
dep_tree_impl(full_path, prefix=prefix)
else:
dep_dlls[dll] = 'not found'
dep_tree_impl(root, prefix)
return (dep_dlls)
def get_arch(filename):
type2arch= {pefile.OPTIONAL_HEADER_MAGIC_PE: 'i686',
pefile.OPTIONAL_HEADER_MAGIC_PE_PLUS: 'x86_64'}
pe = pefile.PE(filename)
try:
return type2arch[pe.PE_TYPE]
except KeyError:
sys.stderr.write('Error: unknown architecture')
sys.exit(1)
if __name__ == '__main__':
filename = sys.argv[1]
for dll, full_path in dep_tree(filename).items():
print(' ' * 7, dll, '=>', full_path)

View File

@ -1,22 +1,2 @@
#!/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 "$@"
#!/bin/sh
exec "$(dirname "$0")"/mingw-configure.sh i686 "$@"

View File

@ -1,22 +1,2 @@
#!/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 "$@"
#!/bin/sh
exec "$(dirname "$0")"/mingw-configure.sh x86_64 "$@"

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

25
perf/run.sh Executable file
View File

@ -0,0 +1,25 @@
#!/bin/bash
CXX=clang++
FONT=fonts/NotoNastaliqUrdu-Regular.ttf
TEXT=texts/fa-monologue.txt
$CXX ../util/hb-shape.cc ../util/options.cc ../src/harfbuzz.cc \
-lm -fno-rtti -fno-exceptions -fno-omit-frame-pointer -DHB_NO_MT \
-I../src $FLAGS $SOURCES \
-DPACKAGE_NAME='""' -DPACKAGE_VERSION='""' \
-DHAVE_GLIB $(pkg-config --cflags --libs glib-2.0) \
-o hb-shape -g -O2 # -O3 \
#-march=native -mtune=native \
#-Rpass=loop-vectorize -Rpass-missed=loop-vectorize \
#-Rpass-analysis=loop-vectorize -fsave-optimization-record
# -march=native: enable all vector instructions current CPU can offer
# -Rpass*: https://llvm.org/docs/Vectorizers.html#diagnostics
#sudo rm capture.syscap > /dev/null
#sysprof-cli -c "./a.out $@"
#sysprof capture.syscap
perf stat ./hb-shape -o /dev/null $FONT --text-file $TEXT --num-iterations=100 --font-funcs=ot
#perf record -g ./hb-shape -O '' -o /dev/null $FONT --text-file $TEXT --num-iterations=100 --font-funcs=ot
#perf report -g

File diff suppressed because it is too large Load Diff

12392
perf/texts/en-words.txt Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,923 @@
شازده کوچولو
اثر آنتوان دو سن‌تگزوپه‌ری
برگردان احمد شاملو
اهدانام‌چه
به لئون ورث Leon Werth
از بچه‌ها عذر می‌خواهم که این کتاب را به یکی از بزرگ‌ترها هدیه کرده‌ام. برای این کار یک دلیل حسابی دارم: این «بزرگ‌تر» به‌ترین دوست من تو همه دنیا است. یک دلیل دیگرم هم آن که این «بزرگ‌تر» همه چیز را می‌تواند بفهمد حتا کتاب‌هایی را که برای بچه‌ها نوشته باشند. عذر سومم این است که این «بزرگ‌تر» تو فرانسه زندگی می‌کند و آن‌جا گشنگی و تشنگی می‌کشد و سخت محتاج دلجویی است. اگر همه‌ی این عذرها کافی نباشد اجازه می‌خواهم این کتاب را تقدیم آن بچه‌ای کنم که این آدم‌بزرگ یک روزی بوده. آخر هر آدم بزرگی هم روزی روزگاری بچه‌ای بوده (گیرم کم‌تر کسی از آن‌ها این را به یاد می‌آورد). پس من هم اهدانام‌چه‌ام را به این شکل تصحیح می‌کنم:
به لئون ورث
موقعی که پسربچه بود
آنتوان دو سن‌تگزوپه‌ری
من هم برگردان فارسی این شعر بزرگ را به دو بچه‌ی دوست‌داشتنی دیگر تقدیم می‌کنم: دکتر جهانگیر کازرونی و دکتر محمدجواد گلبن
احمد شاملو
۱
یک بار شش سالم که بود تو کتابی به اسم قصه‌های واقعی -که درباره‌ی جنگل بِکر نوشته شده بود- تصویر محشری دیدم از یک مار بوآ که داشت حیوانی را می‌بلعید. آن تصویر یک چنین چیزی بود:
یک مار بوآ که دارد حیوانی را می‌بلعد
تو کتاب آمده بود که: «مارهای بوآ شکارشان را همین جور درسته قورت می‌دهند. بی این که بجوندش. بعد دیگر نمی‌توانند از جا بجنبند و تمام شش ماهی را که هضمش طول می‌کشد می‌گیرند می‌خوابند».
این را که خواندم، راجع به چیزهایی که تو جنگل اتفاق می‌افتد کلی فکر کردم و دست آخر توانستم با یک مداد رنگی اولین نقاشیم را از کار درآرم. یعنی نقاشی شماره‌ی یکم را که این جوری بود:
نقاشی شماره‌ی یکم — مار شبیه به کلاه
شاهکارم را نشان بزرگ‌تر ها دادم و پرسیدم از دیدنش ترس‌تان بر می‌دارد؟
جوابم دادند: -چرا کلاه باید آدم را بترساند؟
نقاشی من کلاه نبود، یک مار بوآ بود که داشت یک فیل را هضم می‌کرد. آن وقت برای فهم بزرگ‌ترها برداشتم توی شکم بوآ را کشیدم. آخر همیشه باید به آن‌ها توضیحات داد. نقاشی دومم این جوری بود:
نقاشی دوم — مار و فیل درونش
بزرگ‌ترها بم گفتند کشیدن مار بوآی باز یا بسته را بگذارم کنار و عوضش حواسم را بیش‌تر جمع جغرافی و تاریخ و حساب و دستور زبان کنم. و این جوری شد که تو شش سالگی دور کار ظریف نقاشی را قلم گرفتم. از این که نقاشی شماره‌ی یک و نقاشی شماره‌ی دو ام یخ‌شان نگرفت دلسرد شده بودم. بزرگ‌ترها اگر به خودشان باشد هیچ وقت نمی‌توانند از چیزی سر درآرند. برای بچه‌ها هم خسته کننده است که همین جور مدام هر چیزی را به آن‌ها توضیح بدهند.
ناچار شدم برای خودم کار دیگری پیدا کنم و این بود که رفتم خلبانی یاد گرفتم. بگویی نگویی تا حالا به همه جای دنیا پرواز کرده ام و راستی راستی جغرافی خیلی بم خدمت کرده. می‌توانم به یک نظر چین و آریزونا را از هم تمیز بدهم. اگر آدم تو دل شب سرگردان شده باشد جغرافی خیلی به دادش می‌رسد.
از این راه است که من تو زندگیم با گروه گروه آدم‌های حسابی برخورد داشته‌ام. پیش خیلی از بزرگ‌ترها زندگی کرده‌ام و آن‌ها را از خیلی نزدیک دیده‌ام گیرم این موضوع باعث نشده در باره‌ی آن‌ها عقیده‌ی بهتری پیدا کنم.
هر وقت یکی‌شان را گیر آورده‌ام که یک خرده روشن بین به نظرم آمده با نقاشی شماره‌ی یکم که هنوز هم دارمش محکش زده‌ام ببینم راستی راستی چیزی بارش هست یا نه. اما او هم طبق معمول در جوابم در آمده که: «این یک کلاه است». آن وقت دیگر من هم نه از مارهای بوآ باش اختلاط کرده‌ام نه از جنگل‌های بکر دست نخورده نه از ستاره‌ها. خودم را تا حد او آورده‌ام پایین و باش از بریج و گلف و سیاست و انواع کرات حرف زده‌ام. او هم از این که با یک چنین شخص معقولی آشنایی به هم رسانده سخت خوش‌وقت شده.
۲
این جوری بود که روزگارم تو تنهایی می‌گذشت بی این که راستی راستی یکی را داشته باشم که باش دو کلمه حرف بزنم، تااین که زد و شش سال پیش در کویر صحرا حادثه‌یی برایم اتفاق افتاد؛ یک چیز موتور هواپیمایم شکسته بود و چون نه تعمیرکاری همراهم بود نه مسافری یکه و تنها دست به کار شدم تا از پس چنان تعمیر مشکلی برآیم. مساله‌ی مرگ و زندگی بود. آبی که داشتم زورکی هشت روز را کفاف می‌داد.
شب اول را هزار میل دورتر از هر آبادی مسکونی رو ماسه‌ها به روز آوردم پرت افتاده‌تر از هر کشتی شکسته‌یی که وسط اقیانوس به تخته پاره‌یی چسبیده باشد. پس لابد می‌توانید حدس بزنید چه جور هاج و واج ماندم وقتی کله‌ی آفتاب به شنیدن صدای ظریف عجیبی که گفت: «بی زحمت یک برّه برام بکش!» از خواب پریدم.
-ها؟
-یک برّه برام بکش...
چنان از جا جستم که انگار صاعقه بم زده. خوب که چشم‌هام را مالیدم و نگاه کردم آدم کوچولوی بسیار عجیبی را دیدم که با وقار تمام تو نخ من بود. این به‌ترین شکلی است که بعد ها توانستم از او در آرم، گیرم البته آن‌چه من کشیده‌ام کجا و خود او کجا! تقصیر من چیست؟ بزرگ‌تر ها تو شش سالگی از نقاشی دل‌سردم کردند و جز بوآی باز و بسته یاد نگرفتم چیزی بکشم.
با چشم‌هایی که از تعجب گرد شده بود به این حضور ناگهانی خیره شدم. یادتان نرود که من از نزدیک‌ترین آبادی مسکونی هزار میل فاصله داشتم و این آدمی‌زاد کوچولوی من هم اصلا به نظر نمی‌آمد که راه گم کرده باشد یا از خستگی دم مرگ باشد یا از گشنگی دم مرگ باشد یا از تشنگی دم مرگ باشد یا از وحشت دم مرگ باشد. هیچ چیزش به بچه‌یی نمی‌بُرد که هزار میل دور از هر آبادی مسکونی تو دل صحرا گم شده باشد.
این بهترین شکلی است که بعدها از او در آوردم.
وقتی بالاخره صدام در آمد، گفتم:
-آخه... تو این جا چه می‌کنی؟
و آن وقت او خیلی آرام، مثل یک چیز خیلی جدی، دوباره در آمد که:
-بی زحمت واسه‌ی من یک برّه بکش.
آدم وقتی تحت تاثیر شدید رازی قرار گرفت جرات نافرمانی نمی‌کند. گرچه تو آن نقطه‌ی هزار میل دورتر از هر آبادی مسکونی و با قرار داشتن در معرض خطر مرگ این نکته در نظرم بی معنی جلوه کرد باز کاغذ و خودنویسی از جیبم در آوردم اما تازه یادم آمد که آن‌چه من یاد گرفته‌ام بیش‌تر جغرافیا و تاریخ و حساب و دستور زبان است، و با کج خلقی مختصری به آن موجود کوچولو گفتم نقاشی بلد نیستم.
بم جواب داد: -عیب ندارد، یک بَرّه برام بکش.
از آن‌جایی که هیچ وقت تو عمرم بَرّه نکشیده بودم یکی از آن دو تا نقاشی‌ای را که بلد بودم برایش کشیدم. آن بوآی بسته را. ولی چه یکه‌ای خوردم وقتی آن موجود کوچولو در آمد که: -نه! نه! فیلِ تو شکم یک بوآ نمی‌خواهم. بوآ خیلی خطرناک است فیل جا تنگ کن. خانه‌ی من خیلی کوچولوست، من یک بره لازم دارم. برام یک بره بکش.بره‌ی مریض
-خب، کشیدم.
با دقت نگاهش کرد و گفت:
-نه! این که همین حالاش هم حسابی مریض است. یکی دیگر بکش.قوچ
-کشیدم.
لبخند با نمکی زد و در نهایت گذشت گفت:
-خودت که می‌بینی... این بره نیست، قوچ است. شاخ دارد نه...بره‌ی پیر
باز نقاشی را عوض کردم.
آن را هم مثل قبلی ها رد کرد:
-این یکی خیلی پیر است... من یک بره می‌خواهم که مدت ها عمر کند...
باری چون عجله داشتم که موتورم را پیاده کنم رو بی حوصلگی جعبه‌ای کشیدم که دیواره‌اش سه تا سوراخ داشت، و از دهنم پرید که:جعبه
-این یک جعبه است. بره‌ای که می‌خواهی این تو است.
و چه قدر تعجب کردم از این که دیدم داور کوچولوی من قیافه‌اش از هم باز شد و گفت:
-آها... این درست همان چیزی است که می‌خواستم! فکر می‌کنی این بره خیلی علف بخواهد؟
-چطور مگر؟
-آخر جای من خیلی تنگ است...
-هر چه باشد حتماً بسش است. بره‌یی که بت داده‌ام خیلی کوچولوست.
-آن قدرهاهم کوچولو نیست... اِه! گرفته خوابیده...
و این جوری بود که من با شهریار کوچولو آشنا شدم.
۳
خیلی طول کشید تا توانستم بفهمم از کجا آمده. شهریار کوچولو که مدام مرا سوال پیچ می‌کرد خودش انگار هیچ وقت سوال‌های مرا نمی‌شنید. فقط چیزهایی که جسته گریخته از دهنش می‌پرید کم کم همه چیز را به من آشکار کرد. مثلا اول بار که هواپیمای مرا دید (راستی من هواپیما نقاشی نمی‌کنم، سختم است.) ازم پرسید:
-این چیز چیه؟
-این «چیز» نیست: این پرواز می‌کند. هواپیماست. هواپیمای من است.
و از این که به‌اش می‌فهماندم من کسی‌ام که پرواز می‌کنم به خود می‌بالیدم.
حیرت زده گفت: -چی؟ تو از آسمان افتاده‌ای؟
با فروتنی گفتم: -آره.
گفت: -اوه، این دیگر خیلی عجیب است!
و چنان قهقهه‌ی ملوسی سر داد که مرا حسابی از جا در برد. راستش من دلم می‌خواهد دیگران گرفتاری‌هایم را جدی بگیرند.
خنده‌هایش را که کرد گفت: -خب، پس تو هم از آسمان می‌آیی! اهل کدام سیاره‌ای؟...
بفهمی نفهمی نور مبهمی به معمای حضورش تابید. یکهو پرسیدم:
-پس تو از یک سیاره‌ی دیگر آمده‌ای؟
آرام سرش را تکان داد بی این که چشم از هواپیما بردارد.
اما جوابم را نداد، تو نخ هواپیما رفته بود و آرام آرام سر تکان می‌داد.
گفت: -هر چه باشد با این نباید از جای خیلی دوری آمده باشی...
مدت درازی تو خیال فرو رفت، بعد بره‌اش را از جیب در آورد و محو تماشای آن گنج گرانبها شد.تصویری از شهریار کوچولو بر روی زمین
فکر می‌کنید از این نیمچه اعتراف «سیاره‌ی دیگر»ِ او چه هیجانی به من دست داد؟ زیر پاش نشستم که حرف بیشتری از زبانش بکشم:
-تو از کجا می‌آیی آقا کوچولوی من؟ خانه‌ات کجاست؟ بره‌ی مرا می‌خواهی کجا ببری؟
مدتی در سکوت به فکر فرورفت و بعد در جوابم گفت:
-حسن جعبه‌ای که بم داده‌ای این است که شب‌ها می‌تواند خانه‌اش بشود.
-معلوم است... اما اگر بچه‌ی خوبی باشی یک ریسمان هم بِت می‌دهم که روزها ببندیش. یک ریسمان با یک میخ طویله...
انگار از پیش‌نهادم جا خورد، چون که گفت:
-ببندمش؟ چه فکر ها!
-آخر اگر نبندیش راه می‌افتد می‌رود گم می‌شود.
دوست کوچولوی من دوباره غش غش خنده را سر داد:
-مگر کجا می‌تواند برود؟
-خدا می‌داند. راستِ شکمش را می‌گیرد و می‌رود...
-بگذار برود...اوه، خانه‌ی من آن‌قدر کوچک است!
و شاید با یک خرده اندوه در آمد که:
-یک‌راست هم که بگیرد برود جای دوری نمی‌رود...
۴
به این ترتیب از یک موضوع خیلی مهم دیگر هم سر در آوردم: این که سیاره‌ی او کمی از یک خانه‌ی معمولی بزرگ‌تر بود.این نکته آن‌قدرها به حیرتم نینداخت. می‌دانستم گذشته از سیاره‌های بزرگی مثل زمین و کیوان و تیر و ناهید که هرکدام برای خودشان اسمی دارند، صدها سیاره‌ی دیگر هم هست که بعضی‌شان از بس کوچکند با دوربین نجومی هم به هزار زحمت دیده می‌شوند و هرگاه اخترشناسی یکی‌شان را کشف کند به جای اسم شماره‌ای به‌اش می‌دهد. مثلا اسمش را می‌گذارد «اخترک ۳۲۵۱».
دلایل قاطعی دارم که ثابت می‌کند شهریار کوچولو از اخترک ب۶۱۲ آمده‌بود.
شهریار کوچولو بر اخترکِ ب۶۱۲
این اخترک را فقط یک بار به سال ۱۹۰۹ یک اخترشناس ترک توانسته بود ببیند اخترشناسِ ترک در حالِ دیدنِ اخترکِ ب۶۱۲که تو یک کنگره‌ی بین‌المللی نجوم هم با کشفش هیاهوی زیادی به راه انداخت اما واسه خاطر لباسی که تنش بود هیچ کس حرفش را باور نکرد. اخترشناسِ ترک در کنگره‌ی بین‌المللیِ نجوم، با لباس قدیمیآدم بزرگ‌ها این جوری‌اند!
بختِ اخترک ب۶۱۲ زد و، ترک مستبدی ملتش را به ضرب دگنک وادار به پوشیدن لباس اروپایی‌ها کرد. اخترشناس به سال ۱۹۲۰ دوباره، و این بار با سر و وضع آراسته برای کشفش ارائه‌ی دلیل کرد و این بار همه جانب او را گرفتند.اخترشناسِ ترک در کنگره‌ی بین‌المللیِ نجوم، با لباس جدید
به خاطر آدم بزرگ‌هاست که من این جزئیات را در باب اخترکِ ب۶۱۲ برای‌تان نقل می‌کنم یا شماره‌اش را می‌گویم چون که آن‌ها عاشق عدد و رقم‌اند. وقتی با آن‌ها از یک دوست تازه‌تان حرف بزنید هیچ وقت ازتان درباره‌ی چیزهای اساسی‌اش سوال نمی‌کنند که هیج وقت نمی‌پرسند «آهنگ صداش چه‌طور است؟ چه بازی‌هایی را بیشتر دوست دارد؟ پروانه جمع می‌کند یا نه؟» -می‌پرسند: «چند سالش است؟ چند تا برادر دارد؟ وزنش چه‌قدر است؟ پدرش چه‌قدر حقوق می‌گیرد؟» و تازه بعد از این سوال‌ها است که خیال می‌کنند طرف را شناخته‌اند.
اگر به آدم بزرگ‌ها بگویید یک خانه‌ی قشنگ دیدم از آجر قرمز که جلو پنجره‌هاش غرقِ شمعدانی و بامش پر از کبوتر بود محال است بتوانند مجسمش کنند. باید حتماً به‌شان گفت یک خانه‌ی صد میلیون تومنی دیدم تا صداشان بلند بشود که: -وای چه قشنگ!
یا مثلا اگر به‌شان بگویید «دلیل وجودِ شهریارِ کوچولو این که تودل‌برو بود و می‌خندید و دلش یک بره می‌خواست و بره خواستن، خودش بهترین دلیل وجود داشتن هر کسی است» شانه بالا می‌اندازند و باتان مثل بچ‌ه‌ها رفتار می‌کنند! اما اگر به‌شان بگویید «سیاره‌ای که ازش آمده‌بود اخترک ب۶۱۲ است» بی‌معطلی قبول می‌کنند و دیگر هزار جور چیز ازتان نمی‌پرسند. این جوری‌اند دیگر. نباید ازشان دل‌خور شد. بچه‌ها باید نسبت به آدم بزرگ‌ها گذشت داشته باشند.
اما البته ماها که مفهوم حقیقی زندگی را درک می‌کنیم می‌خندیم به ریش هرچه عدد و رقم است! چیزی که من دلم می‌خواست این بود که این ماجرا را مثل قصه‌ی پریا نقل کنم. دلم می‌خواست بگویم: «یکی بود یکی نبود. روزی روزگاری یه شهریار کوچولو بود که تو اخترکی زندگی می‌کرد همه‌اش یه خورده از خودش بزرگ‌تر و واسه خودش پیِ دوستِ هم‌زبونی می‌گشت...»، آن هایی که مفهوم حقیقی زندگی را درک کرده‌اند واقعیت قضیه را با این لحن بیشتر حس می‌کنند. آخر من دوست ندارم کسی کتابم را سرسری بخواند. خدا می‌داند با نقل این خاطرات چه بار غمی روی دلم می‌نشیند. شش سالی می‌شود که دوستم با بَرّه‌اش رفته. این که این جا می‌کوشم او را وصف کنم برای آن است که از خاطرم نرود. فراموش کردن یک دوست خیلی غم‌انگیز است. همه کس که دوستی ندارد. من هم می‌توانم مثل آدم بزرگ‌ها بشوم که فقط اعداد و ارقام چشم‌شان را می‌گیرد. و باز به همین دلیل است که رفته‌ام یک جعبه رنگ و چند تا مداد خریده‌ام. تو سن و سال من واسه کسی که جز کشیدنِ یک بوآی باز یا یک بوآی بسته هیچ کار دیگری نکرده -و تازه آن هم در شش سالگی- دوباره به نقاشی رو کردن از آن حرف‌هاست! البته تا آن‌جا که بتوانم سعی می‌کنم چیزهایی که می‌کشم تا حد ممکن شبیه باشد. گیرم به موفقیت خودم اطمینان چندانی ندارم. یکیش شبیه از آب در می‌آید یکیش نه. سرِ قدّ و قواره‌اش هم حرف است. یک جا زیادی بلند درش آورده‌ام یک جا زیادی کوتاه. از رنگ لباسش هم مطمئن نیستم. خب، رو حدس و گمان پیش رفته‌ام؛ کاچی به زِ هیچی. و دست آخر گفته باشم که تو بعضِ جزئیات مهم‌ترش هم دچار اشتباه شده‌ام. اما در این مورد دیگر باید ببخشید: دوستم زیر بار هیچ جور شرح و توصیفی نمی‌رفت. شاید مرا هم مثل خودش می‌پنداشت. اما از بختِ بد، دیدن بره‌ها از پشتِ جعبه از من بر نمی‌آید. نکند من هم یک خرده به آدم بزرگ‌ها رفته‌ام؟ «باید پیر شده باشم».
۵
هر روزی که می‌گذشت از اخترک و از فکرِ عزیمت و از سفر و این حرف‌ها چیزهای تازه‌ای دست‌گیرم می‌شد که همه‌اش معلولِ بازتاب‌هایِ اتفاقی بود. و از همین راه بود که روز سوم از ماجرایِ تلخِ بائوباب ها سردرآوردم.
این بار هم بَرّه باعثش شد، چون شهریار کوچولو که انگار سخت دودل مانده‌بود ناگهان ازم پرسید:
-بَرّه‌ها بته‌ها را هم می‌خورند دیگر، مگر نه؟
-آره. همین جور است.
-آخ! چه خوشحال شدم!
نتوانستم بفهمم این موضوع که بَرّه‌ها بوته‌ها را هم می‌خورند اهمیتش کجاست اما شهریار کوچولو درآمد که:
-پس لابد بائوباب ها را هم می‌خورند دیگر؟
یک گله فیل که در اخترکِ ب۶۱۲ روی هم چیده شده‌اندمن برایش توضیح دادم که بائوباب بُتّه نیست. درخت است و از ساختمان یک معبد هم گنده‌تر، و اگر یک گَلّه فیل هم با خودش ببرد حتا یک درخت بائوباب را هم نمی‌توانند بخورند.
از فکر یک گَلّه فیل به خنده افتاد و گفت: -باید چیدشان روی هم.
اما با فرزانگی تمام متذکر شد که: -بائوباب هم از بُتِّگی شروع می‌کند به بزرگ شدن.
-درست است. اما نگفتی چرا دلت می‌خواهد بره‌هایت نهال‌های بائوباب را بخورند؟
گفت: -دِ! معلوم است!
و این را چنان گفت که انگار موضوع از آفتاب هم روشن‌تر است؛ منتها من برای این که به تنهایی از این راز سر در آرم ناچار شدم حسابی کَلّه را به کار بیندازم.
راستش این که تو اخترکِ شهریار کوچولو هم مثل سیارات دیگر هم گیاهِ خوب به هم می‌رسید هم گیاهِ بد. یعنی هم تخمِ خوب گیاه‌های خوب به هم می‌رسید، هم تخمِ بدِ گیاه‌هایِ بد. اما تخم گیاه‌ها نامریی‌اند. آن‌ها تو حرمِ تاریک خاک به خواب می‌روند تا یکی‌شان هوس بیدار شدن به سرش بزند. آن وقت کش و قوسی می‌آید و اول با کم رویی شاخکِ باریکِ خوشگل و بی‌آزاری به طرف خورشید می‌دواند. اگر این شاخک شاخکِ تربچه‌ای گلِ سرخی چیزی باشد می‌شود گذاشت برای خودش رشد کند اما اگر گیاهِ بدی باشد آدم باید به مجردی که دستش را خواند ریشه‌کنش کند.
باری، تو سیاره‌ی شهریار کوچولو گیاه تخمه‌های وحشتناکی به هم می‌رسید. یعنی تخم درختِ بائوباب که خاکِ سیاره حسابی ازشان لطمه خورده بود. بائوباب هم اگر دیر به‌اش برسند دیگر هیچ جور نمی‌شود حریفش شد: تمام سیاره را می‌گیرد و با ریشه‌هایش سوراخ سوراخش می‌کند و اگر سیاره خیلی کوچولو باشد و بائوباب‌ها خیلی زیاد باشند پاک از هم متلاشیش می‌کنند.
شهریار کوچولو در حال نظافت ِ اخترکششهریار کوچولو بعدها یک روز به من گفت: «این، یک امر انضباطی است. صبح به صبح بعد از نظافتِ خود باید با دفت تمام به نظافتِ اخترک پرداخت. آدم باید خودش را مجبور کند که به مجردِ تشخیص دادن بائوباب‌ها از بته‌های گلِ سرخ که تا کوچولواَند عین هم‌اَند با دقت ریشه‌کن‌شان بکند. کار کسل‌کننده‌ای هست اما هیچ مشکل نیست.»
یک روز هم بم توصیه کرد سعی کنم هر جور شده یک نقاشی حسابی از کار درآرم که بتواند قضیه را به بچه‌های سیاره‌ی من هم حالی کند. گفت اگر یک روز بروند سفر ممکن است به دردشان بخورد. پاره‌ای وقت‌ها پشت گوش انداختن کار ایرادی ندارد اما اگر پای بائوباب در میان باشد گاوِ آدم می‌زاید. اخترکی را سراغ دارم که یک تنبل‌باشی ساکنش بود و برای کندن سه تا نهال بائوباب امروز و فردا کرد...».
آن وقت من با استفاده از چیزهایی که گفت شکل آن اخترک را کشیدم.
اخترکِ تنبل‌باشی با سه درختِ بائوباب
هیچ دوست ندارم اندرزگویی کنم. اما خطر بائوباب‌ها آن‌قدر کم شناخته شده و سر راهِ کسی که تو چنان اخترکی سرگیدان بشود آن قدر خطر به کمین نشسته که این مرتبه را از رویه‌ی همیشگی خودم دست بر می‌دارم و می‌گویم: «بچه‌ها! هوای بائوباب‌ها را داشته باشید!»
اگر من سرِ این نقاشی این همه به خودم فشار آورده‌ام فقط برای آن بوده که دوستانم را متوجه خطری کنم که از مدت‌ها پیش بیخ گوش‌شان بوده و مثلِ خودِ من ازش غافل بوده‌اند. درسی که با این نقاشی داده‌ام به زحمتش می‌ارزد. حالا ممکن است شما از خودتان بپرسید: «پس چرا هیچ کدام از بقیه‌ی نقاشی‌های این کتاب هیبتِ تصویرِ بائوباب‌ها را ندارد؟» -خب، جوابش خیلی ساده است: من زور خودم را زده‌ام اما نتوانسته‌ام از کار درشان بیاورم. اما عکس بائوباب‌ها را که می‌کشیدم احساس می‌کردم قضیه خیلی فوریت دارد و به این دلیل شور بَرَم داشته بود.
۶
آخ، شهریار کوچولو! این جوری بود که من کَم کَمَک از زندگیِ محدود و دل‌گیر تو سر درآوردم. تا مدت‌ها تنها سرگرمیِ تو تماشای زیباییِ غروب آفتاب بوده. به این نکته‌ی تازه صبح روز چهارم بود که پی بردم؛ یعنی وقتی که به من گفتی:
-غروب آفتاب را خیلی دوست دارم. برویم فرورفتن آفتاب را تماشا کنیم...شهریار کوچولو در اخترکش مشغولِ تماشای غروبِ آفتاب
-هوم، حالاها باید صبر کنی...
-واسه چی صبر کنم؟
-صبر کنی که آفتاب غروب کند.
اول سخت حیرت کردی بعد از خودت خنده‌ات گرفت و برگشتی به من گفتی:
-همه‌اش خیال می‌کنم تو اخترکِ خودمم!
-راستش موقعی که تو آمریکا ظهر باشد همه می‌دانند تو فرانسه تازه آفتاب دارد غروب می‌کند. کافی است آدم بتواند در یک دقیقه خودش را برساند به فرانسه تا بتواند غروب آفتاب را تماشا کند. متاسفانه فرانسه کجا این‌جا کجا! اما رو اخترک تو که به آن کوچکی است همین‌قدر که چند قدمی صندلیت را جلو بکشی می‌توانی هرقدر دلت خواست غروب تماشا کنی.
-یک روز چهل و سه بار غروب آفتاب را تماشا کردم!
و کمی بعد گفت:
-خودت که می‌دانی... وقتی آدم خیلی دلش گرفته باشد از تماشای غروب لذت می‌برد.
-پس خدا می‌داند آن روز چهل و سه غروبه چه‌قدر دلت گرفته بوده.
اما مسافر کوچولو جوابم را نداد.
۷
روز پنجم باز سرِ گوسفند از یک راز دیگر زندگی شهریار کوچولو سر در آوردم. مثل چیزی که مدت‌ه‌ا تو دلش به‌اش فکر کرده باشد یک‌هو بی مقدمه از من پرسید:
-گوسفندی که بُتّه ها را بخورد گل ها را هم می‌خورد؟
-گوسفند هرچه گیرش بیاید می‌خورد.
-حتا گل‌هایی را هم که خار دارند؟
-آره، حتا گل‌هایی را هم که خار دارند.
-پس خارها فایده‌شان چیست؟
من چه می‌دانستم؟ یکی از آن: سخت گرفتار باز کردن یک مهره‌ی سفتِ موتور بودم. از این که یواش یواش بو می‌بردم خرابیِ کار به آن سادگی‌ها هم که خیال می‌کردم نیست برج زهرمار شده‌بودم و ذخیره‌ی آبم هم که داشت ته می‌کشید بیش‌تر به وحشتم می‌انداخت.
-پس خارها فایده‌شان چسیت؟
شهریار کوچولو وقتی سوالی را می‌کشید وسط دیگر به این مفتی‌ها دست بر نمی‌داشت. مهره پاک کلافه‌ام کرده بود. همین جور سرسری پراندم که:
-خارها به درد هیچ کوفتی نمی‌خورند. آن‌ها فقط نشانه‌ی بدجنسی گل‌ها هستند.
-دِ!
و پس از لحظه‌یی سکوت با یک جور کینه درآمد که:
-حرفت را باور نمی‌کنم! گل‌ها ضعیفند. بی شیله‌پیله‌اند. سعی می‌کنند یک جوری تهِ دل خودشان را قرص کنند. این است که خیال می‌کنند با آن خارها چیزِ ترسناکِ وحشت‌آوری می‌شوند...
لام تا کام به‌اش جواب ندادم. در آن لحظه داشتم تو دلم می‌گفتم: «اگر این مهره‌ی لعنتی همین جور بخواهد لج کند با یک ضربه‌ی چکش حسابش را می‌رسم.» اما شهریار کوچولو دوباره افکارم را به هم ریخت:
-تو فکر می‌کنی گل‌ها...
من باز همان جور بی‌توجه گفتم:
-ای داد بیداد! ای داد بیداد! نه، من هیچ کوفتی فکر نمی‌کنم! آخر من گرفتار هزار مساله‌ی مهم‌تر از آنم!
هاج و واج نگاهم کرد و گفت:
-مساله‌ی مهم!
مرا می‌دید که چکش به دست با دست و بالِ سیاه روی چیزی که خیلی هم به نظرش زشت می‌آمد خم شده‌ام.
-مثل آدم بزرگ‌ها حرف می‌زنی!
از شنیدنِ این حرف خجل شدم اما او همین جور بی‌رحمانه می‌گفت:
-تو همه چیز را به هم می‌ریزی... همه چیز را قاتی می‌کنی!
حسابی از کوره در رفته‌بود.
موهای طلایی طلائیش تو باد می‌جنبید.
-اخترکی را سراغ دارم که یک آقا سرخ روئه توش زندگی می‌کند. او هیچ وقت یک گل را بو نکرده، هیچ وقت یک ستاره‌را تماشا نکرده هیچ وقت کسی را دوست نداشته هیچ وقت جز جمع زدن عددها کاری نکرده. او هم مثل تو صبح تا شب کارش همین است که بگوید: «من یک آدم مهمم! یک آدم مهمم!» این را بگوید و از غرور به خودش باد کند. اما خیال کرده: او آدم نیست، یک قارچ است!
-یک چی؟
-یک قارچ!گلِ سرخ
حالا دیگر رنگش از فرط خشم مثل گچ سفید شده‌بود:
-کرورها سال است که گل‌ها خار می‌سازند و با وجود این کرورها سال است که برّه‌ها گل‌ها را می‌خورند. آن وقت هیچ مهم نیست آدم بداند پس چرا گل‌ها واسه ساختنِ خارهایی که هیچ وقتِ خدا به هیچ دردی نمی‌خورند این قدر به خودشان زحمت می‌دهند؟ جنگ میان برّه‌ها و گل‌ها هیچ مهم نیست؟ این موضوع از آن جمع زدن‌های آقا سرخ‌روئه‌یِ شکم‌گنده مهم‌تر و جدی‌تر نیست؟ اگر من گلی را بشناسم که تو همه‌ی دنیا تک است و جز رو اخترک خودم هیچ جای دیگر پیدا نمیشه و ممکن است یک روز صبح یک برّه کوچولو، مفت و مسلم، بی این که بفهمد چه‌کار دارد می‌کند به یک ضرب پاک از میان ببردش چی؟ یعنی این هم هیچ اهمیتی ندارد؟ اگر کسی گلی را دوست داشته باشد که تو کرورها و کرورها ستاره فقط یک دانه ازش هست واسه احساس وشبختی همین قدر بس است که نگاهی به آن همه ستاره بیندازد و با خودش بگوید: «گل من یک جایی میان آن ستاره‌هاست»، اما اگر برّه گل را بخورد برایش مثل این است که یکهو تمام آن ستاره‌ها پِتّی کنند و خاموش بشوند. یعنی این هم هیچ اهمیتی ندارد؟
دیگر نتوانست چیزی بگوید و ناگهان هِق هِق کنان زد زیر گریه.
حالا دیگر شب شده‌بود. اسباب و ابزارم را کنار انداخته‌بودم. دیگر چکش و مهره و تشنگی و مرگ به نظرم مضحک می‌آمد. رو ستاره‌ای، رو سیاره‌ای، رو سیاره‌ی من، زمین، شهریارِ کوچولویی بود که احتیاج به دلداری داشت! به آغوشش گرفتم مثل گهواره تابش دادم به‌اش گفتم: «گلی که تو دوست داری تو خطر نیست. خودم واسه گوسفندت یک پوزه‌بند می‌کشم... خودم واسه گفت یک تجیر می‌کشم... خودم...» بیش از این نمی‌دانستم چه بگویم. خودم را سخت چُلمَن و بی دست و پا حس می‌کردم. نمی‌دانستم چه‌طور باید خودم را به‌اش برسانم یا به‌اش بپیوندم...p چه دیار اسرارآمیزی است دیار اشک!
۸
راه شناختن آن گل را خیلی زود پیدا کردم:
تو اخترکِ شهریار کوچولو همیشه یک مشت گل‌های خیلی ساده در می‌آمده. گل‌هایی با یک ردیف گلبرگ که جای چندانی نمی‌گرفته، دست و پاگیرِ کسی نمی‌شده. صبحی سر و کله‌شان میان علف‌ها پیدا می‌شده شب از میان می‌رفته‌اند. اما این یکی یک روز از دانه‌ای جوانه زده بود که خدا می‌دانست از کجا آمده رود و شهریار کوچولو با جان و دل از این شاخکِ نازکی که به هیچ کدام از شاخک‌های دیگر نمی‌رفت مواظبت کرده‌بود. بعید بنود که این هم نوعِ تازه‌ای از بائوباب باشد اما بته خیلی زود از رشد بازماند و دست‌به‌کارِ آوردن گل شد. شهریار کوچولو که موقعِ نیش زدن آن غنچه‌ی بزرگ حاضر و ناظر بود به دلش افتاد که باید چیز معجزه‌آسایی از آن بیرون بیاید. اما گل تو پناهِ خوابگاهِ سبزش سر فرصت دست اندکار خودآرایی بود تا هرچه زیباتر جلوه‌کند. رنگ‌هایش را با وسواس تمام انتخاب می‌کرد سر صبر لباس می‌پوشید و گلبرگ‌ها را یکی یکی به خودش می‌بست. دلش نمی‌خواست مثل شقایق‌ها با جامه‌ی مچاله و پر چروک بیرون بیاید.
شهریار کوچولو و گلِ سرخنمی‌خواست جز در اوج درخشندگی زیبائیش رو نشان بدهد!...
هوه، بله عشوه‌گری تمام عیار بود! آرایشِ پر راز و رمزش روزها و روزها طول کشید تا آن که سرانجام یک روز صبح درست با بر آمدن آفتاب نقاب از چهره برداشت و با این که با آن همه دقت و ظرافت روی آرایش و پیرایش خودش کار کرده بود خمیازه‌کشان گفت:
-اوه، تازه همین حالا از خواب پا شده‌ام... عذر می‌خواهم که موهام این جور آشفته‌است...
شهریار کوچولو نتوانست جلو خودش را بگیرد و از ستایش او خودداری کند:
-وای چه‌قدر زیبائید!
گل به نرمی گفت:
-چرا که نه؟ من و آفتاب تو یک لحظه به دنیا آمدیم...
شهریار کوچولو شستش خبردار شد که طرف آن‌قدرها هم اهل شکسته‌نفسی نیست اما راستی که چه‌قدر هیجان انگیز بود!
-به نظرم وقت خوردن ناشتایی است. بی زحمت برایم فکری بکنید.
و شهریار کوچولوی مشوش و در هم یک آبپاش آب خنک آورده به گل داده‌بود.شهریار کوچولو در حالِ تماشای گلِ سرخ
با این حساب، هنوزهیچی نشده با آن خودپسندیش که بفهمی‌نفهمی از ضعفش آب می‌خورد دل او را شکسته بود. مثلا یک روز که داشت راجع به چهارتا خارش حرف می‌زد یک‌هو در آمده بود که:ببر و گلِ سرخ
-نکند ببرها با آن چنگال‌های تیزشان بیایند سراغم!
شهریار کوچولو ازش ایراد گرفته‌بود که:
-تو اخترک من ببر به هم نمی‌رسد. تازه ببرها که علف‌خوار نیستند.
گل به گلایه جواب داده بود:
-من که علف نیستم.
و شهریار کوچولو گفته بود:
-عذر می‌خواهم...
-من از ببرها هیچ ترسی ندارم اما از جریان هوا وحشت می‌کنم. تو دستگاه‌تان تجیر به هم نمی‌رسد؟شهریار کوچولو در حالِ پوشاندنِ گلِ سرخ
شهریار کوچولو تو دلش گفت: «وحشت از جریان هوا... این که واسه یک گیاه تعریفی ندارد... چه مرموز است این گل!»
-شب مرا بگذارید زیر یک سرپوش. این جا هواش خیلی سرد است. چه جای بدی افتادم! جایی که پیش از این بودم...شهریار کوچولو در حالِ گذاشتنِ سرپوش روی گلِ سرخ
اما حرفش را خورده بود. آخر، آمدنا هنوز به شکل دانه بود. امکان نداشت توانسته‌باشد دنیاهای دیگری را بشناسد. شرم‌سار از این که گذاشته بود سر به هم بافتن دروغی به این آشکاری مچش گیربیفتد دو سه بار سرفه کرده بود تا اهمالِ شهریار کوچولو را به‌اش یادآور شود:
-تجیر کو پس؟
-داشتم می‌رفتم اما شما داشتید صحبت می‌کردید!
و با وجود این زورکی بنا کرده‌بود به سرفه کردن تا او احساس پشیمانی کند.
به این ترتیب شهریار کوچولو با همه‌ی حسن نیّتی که از عشقش آب می‌خورد همان اول کار به او بد گمان شده‌بود. حرف‌های بی سر و تهش را جدی گرفته‌بود و سخت احساس شوربختی می‌کرد.
یک روز دردِدل کنان به من گفت: -حقش بود به حرف‌هاش گوش نمی‌دادم. هیچ وقت نباید به حرف گل‌ها گوش داد. گل را فقط باید بوئید و تماشا کرد. گلِ من تمامِ اخترکم را معطر می‌کرد گیرم من بلد نبودم چه‌جوری از آن لذت ببرم. قضیه‌ی چنگال‌های ببر که آن جور دَمَغم کرده‌بود می‌بایست دلم را نرم کرده باشد...»
یک روز دیگر هم به من گفت: «آن روزها نتوانستم چیزی بفهمم. من بایست روی کرد و کارِ او در باره‌اش قضاوت می‌کردم نه روی گفتارش... عطرآگینم می‌کرد. دلم را روشن می‌کرد. نمی‌بایست ازش بگریزم. می‌بایست به مهر و محبتی که پشتِ آن کلک‌های معصومانه‌اش پنهان بود پی می‌بردم. گل‌ها پُرَند از این جور تضادها. اما خب دیگر، من خام‌تر از آن بودم که راهِ دوست داشتنش را بدانم!».
۹
گمان کنم شهریار کوچولو برای فرارش از مهاجرت پرنده‌های وحشی استفاده کرد.
گمان کنم شهریار کوچولو برای فرارش از مهاجرت پرنده‌های وحشی استفاده کرد.
صبح روز حرکت، اخترکش را آن جور که باید مرتب کرد، آتش‌فشان‌های فعالش را با دقت پاک و دوده‌گیری کرد: شهریار کوچولو در حالِ پاک کردنِ آتش‌فشان.دو تا آتش‌فشان فعال داشت که برای گرم کردن ناشتایی خیلی خوب بود. یک آتش‌فشان خاموش هم داشت. منتها به قول خودش «آدم کف دستش را که بو نکرده!» این بود که آتش‌فشان خاموش را هم پاک کرد. آتش‌فشان که پاک باشد مرتب و یک هوا می‌سوزد و یک‌هو گُر نمی‌زند. آتش‌فشان هم عین‌هو بخاری یک‌هو اَلُو می‌زند. البته ما رو سیاره‌مان زمین کوچک‌تر از آن هستیم که آتش‌فشان‌هامان را پاک و دوده‌گیری کنیم و برای همین است که گاهی آن جور اسباب زحمت‌مان می‌شوند.
شهریار کوچولو با دل‌ِگرفته آخرین نهال‌های بائوباب را هم ریشه‌کن کرد. فکر می‌کرد دیگر هیچ وقت نباید برگردد. اما آن روز صبح گرچه از این کارهای معمولیِ هر روزه کُلّی لذت برد موقعی که آخرین آب را پای گل داد و خواست بگذاردش زیرِ سرپوش چیزی نمانده‌بود که اشکش سرازیر شود.
به گل گفت: -خدا نگهدار!
اما او جوابش را نداد.
دوباره گفت: -خدا نگهدار!
گل سرفه‌کرد، گیرم این سرفه اثر چائیدن نبود. بالاخره به زبان آمد و گفت:
-من سبک مغز بودم. ازت عذر می‌خواهم. سعی کن خوشبخت باشی.
از این که به سرکوفت و سرزنش‌های همیشگی برنخورد حیرت کرد و سرپوش به دست هاج‌وواج ماند. از این محبتِ آرام سر در نمی‌آورد.
گل به‌اش گفت: -خب دیگر، دوستت دارم. اگر تو روحت هم از این موضوع خبردار نشد تقصیر من است. باشد، زیاد مهم نیست. اما تو هم مثل من بی‌عقل بودی... سعی کن خوشبخت بشوی... این سرپوش را هم بگذار کنار، دیگر به دردم نمی‌خورد.
-آخر، باد...
-آن قدرهاهم سَرمائو نیستم... هوای خنک شب برای سلامتیم خوب است. خدانکرده گُلم آخر.
-آخر حیوانات...
-اگر خواسته‌باشم با شب‌پره‌ها آشنا بشوم جز این که دو سه تا کرمِ حشره را تحمل کنم چاره‌ای ندارم. شب‌پره باید خیلی قشنگ باشد. جز آن کی به دیدنم می‌آید؟ تو که می‌روی به آن دور دورها. از بابتِ درنده‌ها هم هیچ کَکَم نمی‌گزد: «من هم برای خودم چنگ و پنجه‌ای دارم».
و با سادگی تمام چهارتا خارش را نشان داد. بعد گفت:
-دست‌دست نکن دیگر! این کارت خلق آدم را تنگ می‌کند. حالا که تصمیم گرفته‌ای بروی برو!
و این را گفت، چون که نمی‌خواست شهریار کوچولو گریه‌اش را ببیند. گلی بود تا این حد خودپسند...
۱۰
خودش را در منطقه‌ی اخترک‌های ۳۲۵، ۳۲۶، ۳۲۷، ۳۲۸، ۳۲۹ و ۳۳۰ دید. این بود که هم برای سرگرمی و هم برای چیزیادگرفتن بنا کرد یکی‌یکی‌شان را سیاحت کردن.
اخترکِ اول مسکن پادشاهی بود که با شنلی از مخمل ارغوانی قاقم بر اورنگی بسیار ساده و در عین حال پرشکوه نشسته بود و همین که چشمش به شهریار کوچولو افتاد داد زد:
-خب، این هم رعیت!
شهریار کوچولو از خودش پرسید: -او که تا حالا هیچ وقت مرا ندیده چه جوری می‌تواند بشناسدم؟
دیگر اینش را نخوانده‌بود که دنیابرای پادشاهان به نحو عجیبی ساده شده و تمام مردم فقط یک مشت رعیت به حساب می‌آیند.
پادشاه در سیاره‌اش
پادشاه که می‌دید بالاخره شاهِ کسی شده و از این بابت کبکش خروس می‌خواند گفت: -بیا جلو بهتر ببینیمت. شهریار کوچولو با چشم پیِ جایی گشت که بنشیند اما شنلِ قاقمِ حضرتِ پادشاهی تمام اخترک را دربرگرفته‌بود. ناچار همان طور سر پا ماند و چون سخت خسته بود به دهن‌دره افتاد.
شاه به‌اش گفت: -خمیازه کشیدن در حضرتِ سلطان از نزاکت به دور است. این کار را برایت قدغن می‌کنم. شهریار کوچولو که سخت خجل شده‌بود در آمد که:
-نمی‌توانم جلوِ خودم را بگیرم. راه درازی طی‌کرده‌ام و هیچ هم نخوابیده‌ام...
پادشاه گفت: -خب خب، پس بِت امر می‌کنم خمیازه بکشی. سال‌هاست خمیازه‌کشیدن کسی را ندیده‌ام برایم تازگی دارد. یاالله باز هم خمیازه بکش. این یک امر است.
شهریار کوچولو گفت: -آخر این جوری من دست و پایم را گم می‌کنم... دیگر نمی‌توانم.
شاه گفت: -هوم! هوم! خب، پس من به‌ات امر می‌کنم که گاهی خمیازه بکشی گاهی نه.
تند و نامفهوم حرف می‌زد و انگار خلقش حسابی تنگ بود.
پادشاه فقط دربند این بود که مطیع فرمانش باشند. در مورد نافرمانی‌ها هم هیچ نرمشی از خودش نشان نمی‌داد. یک پادشاهِ تمام عیار بود گیرم چون زیادی خوب بود اوامری که صادر می‌کرد اوامری بود منطقی. مثلا خیلی راحت در آمد که: «اگر من به یکی از سردارانم امر کنم تبدیل به یکی از این مرغ‌های دریایی بشود و یارو اطاعت نکند تقسیر او نیست که، تقصیر خودم است».
شهریار کوچولو در نهایت ادب پرسید: -اجازه می‌فرمایید بنشینم؟
پادشاه که در نهایتِ شکوه و جلال چینی از شنل قاقمش را جمع می‌کرد گفت: -به‌ات امر می‌کنیم بنشینی.
منتها شهریار کوچولو مانده‌بود حیران: آخر آن اخترک کوچک‌تر از آن بود که تصورش را بشود کرد. واقعا این پادشاه به چی سلطنت می‌کرد؟ گفت: -قربان عفو می‌فرمایید که ازتان سوال می‌کنم...
پادشاه با عجله گفت: -به‌ات امر می‌کنیم از ما سوال کنی.
-شما قربان به چی سلطنت می‌فرمایید؟
پادشاه خیلی ساده گفت: -به همه چی.
-به همه‌چی؟
پادشاه با حرکتی قاطع به اخترک خودش و اخترک‌های دیگر و باقی ستاره‌ها اشاره کرد.
شهریار کوچولو پرسید: -یعنی به همه‌ی این ها؟
شاه جواب داد: -به همه‌ی این ها.
آخر او فقط یک پادشاه معمولی نبود که، یک پادشاهِ جهانی بود.
-آن وقت ستاره‌ها هم سربه‌فرمان‌تانند؟
پادشاه گفت: -البته که هستند. همه‌شان بی‌درنگ هر فرمانی را اطاعت می‌کنند. ما نافرمانی را مطلقا تحمل نمی‌کنیم.
یک چنین قدرتی شهریار کوچولو را به شدت متعجب کرد. اگر خودش چنین قدرتی می‌داشت بی این که حتا صندلیش را یک ذره تکان بدهد روزی چهل و چهار بار که هیچ روزی هفتاد بار و حتا صدبار و دویست‌بار غروب آفتاب را تماشا می‌کرد! و چون بفهمی نفهمی از یادآوریِ اخترکش که به امان خدا ول‌کرده‌بود غصه‌اش شد جراتی به خودش داد که از پادشاه درخواست محبتی بکند:
-دلم می‌خواست یک غروب آفتاب تماشا کنم... در حقم التفات بفرمایید امر کنید خورشید غروب کند.
-اگر ما به یک سردار امر کنیم مثل شب‌پره از این گل به آن گل بپرد یا قصه‌ی سوزناکی بنویسد یا به شکل مرغ دریایی در آید و او امریه را اجرا نکند کدام یکی‌مان مقصریم، ما یا او؟
شهریار کوچولو نه گذاشت، نه برداشت، گفت: -شما.
پادشاه گفت: -حرف ندارد. باید از هر کسی چیزی را توقع داشت که ازش ساخته باشد. قدرت باید پیش از هر چیز به عقل متکی باشد. اگر تو به ملتت فرمان بدهی که بروند خودشان را بیندازند تو دریا انقلاب می‌کنند. حق داریم توقع اطاعت داشته باشیم چون اوامرمان عاقلانه است.
شهریار کوچولو که هیچ وقت چیزی را که پرسیده بود فراموش نمی‌کرد گفت: -غروب آفتاب من چی؟
-تو هم به غروب آفتابت می‌رسی. امریه‌اش را صادر می‌کنیم. منتها با شَمِّ حکمرانی‌مان منتظریم زمینه‌اش فراهم بشود.
شهریار کوچولو پرسید: -کِی فراهم می‌شود؟
پادشاه بعد از آن که تقویم کَت و کلفتی را نگاه کرد جواب داد:
-هوم! هوم! حدودِ... حدودِ... غروب. حدودِ ساعت هفت و چهل دقیقه... و آن وقت تو با چشم‌های خودت می‌بینی که چه‌طور فرمان ما اجرا می‌شود!
شهریار کوچولو خمیازه کشید. از این که تماشای آفتاب غروب از کیسه‌اش رفته‌بود تاسف می‌خورد. از آن گذشته دلش هم کمی گرفته‌بود. این بود که به پادشاه گفت:
-من دیگر این‌جا کاری ندارم. می‌خواهم بروم.
شاه که دلش برای داشتن یک رعیت غنج می‌زد گفت:
-نرو! نرو! وزیرت می‌کنیم.
-وزیرِ چی؟
-وزیرِ دادگستری!
-آخر این جا کسی نیست که محاکمه بشود.
پادشاه گفت: -معلوم نیست. ما که هنوز گشتی دور قلمرومان نزده‌ایم. خیلی پیر شده‌ایم، برای کالسکه جا نداریم. پیاده‌روی هم خسته‌مان می‌کند.
شهریار کوچولو که خم شده‌بود تا نگاهی هم به آن طرف اخترک بیندازد گفت: -بَه! من نگاه کرده‌ام، آن طرف هم دیارالبشری نیست.
پادشاه به‌اش جواب داد: -خب، پس خودت را محاکمه کن. این کار مشکل‌تر هم هست. محاکمه کردن خود از محاکمه‌کردن دیگران خیلی مشکل تر است. اگر توانستی در مورد خودت قضاوت درستی بکنی معلوم می‌شود یک فرزانه‌ی تمام عیاری.
شهریار کوچولو گفت: -من هر جا باشم می‌توانم خودم را محاکمه کنم، چه احتیاجی است این جا بمانم؟ پادشاه گفت: -هوم! هوم! فکر می‌کنیم یک جایی تو اخترک ما یک موش پیر هست. صدایش را شب ها می‌شنویم. می‌توانی او را به محاکمه بکشی و گاه‌گاهی هم به اعدام محکومش کنی. در این صورت زندگی او به عدالت تو بستگی پیدا می‌کند. گیرم تو هر دفعه عفوش می‌کنی تا همیشه زیر چاق داشته باشیش. آخر یکی بیش‌تر نیست که.
شهریار کوچولو جواب داد: -من از حکم اعدام خوشم نمی‌آید. فکر می‌کنم دیگر باید بروم.
پادشاه گفت: -نه!
اما شهریار کوچولو که آماده‌ی حرکت شده بود و ضمنا هم هیچ دلش نمی‌خواست اسباب ناراحتی سلطان پیر بشود گفت:
-اگر اعلی‌حضرت مایلند اوامرشان دقیقا اجرا بشود می‌توانند فرمان خردمندانه‌ای در مورد بنده صادر بفرمایند. مثلا می‌توانند به بنده امر کنند ظرف یک دقیقه راه بیفتم. تصور می‌کنم زمینه‌اش هم آماده باشد...
چون پادشاه جوابی نداد شهریار کوچولو اول دو دل ماند اما بعد آهی کشید و به راه افتاد.
آن‌وقت پادشاه با شتاب فریاد زد: -سفیر خودمان فرمودیمت!
حالت بسیار شکوهمندی داشت.
شهریار کوچولو همان طور که می‌رفت تو دلش می‌گفت: -این آدم بزرگ‌ها راستی راستی چه‌قدر عجیبند!
۱۱
اخترک دوم مسکن آدم خود پسندی بود.
خود پسند چشمش که به شهریار کوچولو افتاد از همان دور داد زد: -به‌به! این هم یک ستایشگر که دارد می‌آید مرا ببیند!
خودپسند در سیاره‌اش
آخر برای خودپسندها دیگران فقط یک مشت ستایش‌گرند.
شهریار کوچولو گفت: -سلام! چه کلاه عجیب غریبی سرتان گذاشته‌اید!
خود پسند جواب داد: -مال اظهار تشکر است. منظورم موقعی است که هلهله‌ی ستایشگرهایم بلند می‌شود. گیرم متاسفانه تنابنده‌ای گذارش به این طرف‌ها نمی‌افتد.
شهریار کوچولو که چیزی حالیش نشده بود گفت:
-چی؟
خودپسند گفت: -دست‌هایت را بزن به هم دیگر.
شهریار کوچولو دست زد و خودپسند کلاهش را برداشت و متواضعانه از او تشکر کرد.
شهریار کوچولو با خودش گفت: «دیدنِ این تفریحش خیلی بیش‌تر از دیدنِ پادشاه‌است». و دوباره بنا کرد دست‌زدن و خودپسند با برداشتن کلاه بنا کرد تشکر کردن.
پس از پنج دقیقه‌ای شهریار کوچولو که از این بازی یک‌نواخت خسته شده بود پرسید: -چه کار باید کرد که کلاه از سرت بیفتد؟
اما خودپسند حرفش را نشنید. آخر آن‌ها جز ستایش خودشان چیزی را نمی‌شنوند.
از شهریار کوچولو پرسید: -تو راستی راستی به من با چشم ستایش و تحسین نگاه می‌کنی؟
-ستایش و تحسین یعنی چه؟
-یعنی قبول این که من خوش‌قیافه‌ترین و خوش‌پوش‌ترین و ثروت‌مندترین و باهوش‌ترین مرد این اخترکم.
-آخر روی این اخترک که فقط خودتی و کلاهت.
-با وجود این ستایشم کن. این لطف را در حق من بکن.
شهریار کوچولو نیم‌چه شانه‌ای بالا انداخت و گفت: -خب، ستایشت کردم. اما آخر واقعا چیِ این برایت جالب است؟
شهریار کوچولو به راه افتاد و همان طور که می‌رفت تو دلش می‌گفت: -این آدم بزرگ‌ها راستی راستی چه‌قدر عجیبند!
۱۲
تو اخترک بعدی می‌خواره‌ای می‌نشست. دیدار کوتاه بود اما شهریار کوچولو را به غم بزرگی فرو برد.
می‌خواره در سیاره‌اش
به می‌خواره که صُم‌بُکم پشت یک مشت بطری خالی و یک مشت بطری پر نشسته بود گفت: -چه کار داری می‌کنی؟
می‌خواره با لحن غم‌زده‌ای جواب داد: -مِی می‌زنم.
شهریار کوچولو پرسید: -مِی می‌زنی که چی؟
می‌خواره جواب داد: -که فراموش کنم.
شهریار کوچولو که حالا دیگر دلش برای او می‌سوخت پرسید: -چی را فراموش کنی؟
می‌خواره همان طور که سرش را می‌انداخت پایین گفت: -سر شکستگیم را.
شهریار کوچولو که دلش می‌خواست دردی از او دوا کند پرسید: -سرشکستگی از چی؟
می‌خواره جواب داد: -سرشکستگیِ می‌خواره بودنم را.
این را گفت و قال را کند و به کلی خاموش شد. و شهریار کوچولو مات و مبهوت راهش را گرفت و رفت و همان جور که می‌رفت تو دلش می‌گفت: -این آدم بزرگ‌ها راستی‌راستی چه‌قدر عجیبند!
۱۳
اخترک چهارم اخترک مرد تجارت‌پیشه بود. این بابا چنان مشغول و گرفتار بود که با ورود شهریار کوچولو حتا سرش را هم بلند نکرد.
مردِ تجارت‌پیشه در سیاره‌اش
شهریار کوچولو گفت: -سلام. آتش‌سیگارتان خاموش شده.
-سه و دو می‌کند پنج. پنج و هفت دوازده و سه پانزده. سلام. پانزده و هفت بیست و دو. بیست و دو و شش بیست و هشت. وقت ندارم روشنش کنم. بیست و شش و پنج سی و یک. اوف! پس جمعش می‌کند پانصدویک میلیون و ششصد و بیست و دو هزار هفتصد و سی و یک.
-پانصد میلیون چی؟
-ها؟ هنوز این جایی تو؟ پانصد و یک میلیون چیز. چه می‌دانم، آن قدر کار سرم ریخته که!... من یک مرد جدی هستم و با حرف‌های هشت‌من‌نه‌شاهی سر و کار ندارم!... دو و پنج هفت...
شهریار کوچولو که وقتی چیزی می‌پرسید دیگر تا جوابش را نمی‌گرفت دست بردار نبود دوباره پرسید:
-پانصد و یک میلیون چی؟
تاجر پیشه سرش را بلند کرد:
-تو این پنجاه و چهار سالی که ساکن این اخترکم همه‌اش سه بار گرفتار مودماغ شده‌ام. اولیش بیست و دو سال پیش یک سوسک بود که خدا می‌داند از کدام جهنم پیدایش شد. صدای وحشت‌ناکی از خودش در می‌آورد که باعث شد تو یک جمع چهار جا اشتباه کنم. دفعه‌ی دوم یازده سال پیش بود که استخوان درد بی‌چاره‌ام کرد. من ورزش نمی‌کنم. وقت یللی‌تللی هم ندارم. آدمی هستم جدی... این هم بار سومش!... کجا بودم؟ پانصد و یک میلیون و...
-این همه میلیون چی؟
تاجرپیشه فهمید که نباید امید خلاصی داشته باشد. گفت: -میلیون‌ها از این چیزهای کوچولویی که پاره‌ای وقت‌ها تو هوا دیده می‌شود.
-مگس؟
-نه بابا. این چیزهای کوچولوی براق.
-زنبور عسل؟
-نه بابا! همین چیزهای کوچولوی طلایی که وِلِنگارها را به عالم هپروت می‌برد. گیرم من شخصا آدمی هستم جدی که وقتم را صرف خیال‌بافی نمی‌کنم.
-آها، ستاره؟
-خودش است: ستاره.
-خب پانصد میلیون ستاره به چه دردت می‌خورد؟
-پانصد و یک میلیون و ششصد و بیست و دو هزار و هفتصد و سی و یکی. من جدیّم و دقیق.
-خب، به چه دردت می‌خورند؟
-به چه دردم می‌خورند؟
-ها.
-هیچی تصاحب‌شان می‌کنم.
-ستاره‌ها را؟
-آره خب.
-آخر من به یک پادشاهی برخوردم که...
-پادشاه‌ها تصاحب نمی‌کنند بل‌که به‌اش «سلطنت» می‌کنند. این دو تا با هم خیلی فرق دارد.
-خب، حالا تو آن‌ها را تصاحب می‌کنی که چی بشود؟
-که دارا بشوم.
-خب دارا شدن به چه کارت می‌خورد؟
-به این کار که، اگر کسی ستاره‌ای پیدا کرد من ازش بخرم.
شهریار کوچولو با خودش گفت: «این بابا هم منطقش یک خرده به منطق آن دائم‌الخمره می‌بَرَد.» با وجود این باز ازش پرسید:
-چه جوری می‌شود یک ستاره را صاحب شد؟
تاجرپیشه بی درنگ با اَخم و تَخم پرسید: -این ستاره‌ها مال کی‌اند؟
-چه می‌دانم؟ مال هیچ کس.
-پس مال منند، چون من اول به این فکر افتادم.
-همین کافی است؟
-البته که کافی است. اگر تو یک جواهر پیدا کنی که مال هیچ کس نباشد می‌شود مال تو. اگر جزیره‌ای کشف کنی که مال هیچ کس نباشد می‌شود مال تو. اگر فکری به کله‌ات بزند که تا آن موقع به سر کسی نزده به اسم خودت ثبتش می‌کنی و می‌شود مال تو. من هم ستاره‌ها را برای این صاحب شده‌ام که پیش از من هیچ کس به فکر نیفتاده بود آن‌ها را مالک بشود.
شهریار کوچولو گفت: -این ها همه‌اش درست. منتها چه کارشان می‌کنی؟
تاجر پیشه گفت: -اداره‌شان می‌کنم، همین جور می‌شمارم‌شان و می‌شمارم‌شان. البته کار مشکلی است ولی خب دیگر، من آدمی هستم بسیار جدی.
شهریار کوچولو که هنوز این حرف تو کَتَش نرفته‌بود گفت:
-اگر من یک شال گردن ابریشمی داشته باشم می‌توانم بپیچم دور گردنم با خودم ببرمش. اگر یک گل داشته باشم می‌توانم بچینم با خودم ببرمش. اما تو که نمی‌توانی ستاره‌ها را بچینی!
-نه. اما می‌توانم بگذارم‌شان تو بانک.
-اینی که گفتی یعنی چه؟
-یعنی این که تعداد ستاره‌هایم را رو یک تکه کاغذ می‌نویسم می‌گذارم تو کشو درش را قفل می‌کنم.
-همه‌اش همین؟
-آره همین کافی است.
شهریار کوچولو فکر کرد «جالب است. یک خرده هم شاعرانه است. اما کاری نیست که آن قدرها جدیش بشود گرفت». آخر تعبیر او از چیزهای جدی با تعبیر آدم‌های بزرگ فرق می‌کرد.
باز گفت: -من یک گل دارم که هر روز آبش می‌دهم. سه تا هم آتش‌فشان دارم که هفته‌ای یک بار پاک و دوده‌گیری‌شان می‌کنم. آخر آتش‌فشان خاموشه را هم پاک می‌کنم. آدم کفِ دستش را که بو نکرده! رو این حساب، هم برای آتش‌فشان‌ها و هم برای گل این که من صاحب‌شان باشم فایده دارد. تو چه فایده‌ای به حال ستاره‌ها داری؟
تاجرپیشه دهن باز کرد که جوابی بدهد اما چیزی پیدا نکرد. و شهریار کوچولو راهش را گرفت و رفت و همان جور که می‌رفت تو دلش می‌گفت: -این آدم بزرگ‌ها راستی راستی چه‌قدر عجیبند!
۱۴
اخترکِ پنجم چیز غریبی بود. از همه‌ی اخترک‌های دیگر کوچک‌تر بود، یعنی فقط به اندازه‌ی یک فانوس پایه‌دار و یک فانوس‌بان جا داشت.
فانوس‌بان در حالِ روشن کردنِ فانوس در سیاره‌اش
شهریار کوچولو از این راز سر در نیاورد که یک جا میان آسمان خدا تو اخترکی که نه خانه‌ای روش هست نه آدمی، حکمت وجودی یک فانوس و یک فانوس‌بان چه می‌تواند باشد. با وجود این تو دلش گفت:
-خیلی احتمال دارد که این بابا عقلش پاره‌سنگ ببرد. اما به هر حال از پادشاه و خودپسند و تاجرپیشه و مسته کم عقل‌تر نیست. دست کم کاری که می‌کند یک معنایی دارد. فانوسش را که روشن می‌کند عین‌هو مثل این است که یک ستاره‌ی دیگر یا یک گل به دنیا می‌آورد و خاموشش که می‌کند پنداری گل یا ستاره‌ای را می‌خواباند. سرگرمی زیبایی است و چیزی که زیبا باشد بی گفت‌وگو مفید هم هست.
وقتی رو اخترک پایین آمد با ادب فراوان به فانوس‌بان سلام کرد:
-سلام. واسه چی فانوس را خاموش کردی؟
-دستور است. صبح به خیر!
-دستور چیه؟
-این است که فانوسم را خاموش کنم. شب خوش!
و دوباره فانوس را روشن کرد.
-پس چرا روشنش کردی باز؟
فانوس‌بان جواب داد: -خب دستور است دیگر.
شهریار کوچولو گفت: -اصلا سر در نمیارم.
فانوس‌بان گفت: -چیز سر در آوردنی‌یی توش نیست که. دستور دستور است. روز بخیر!
و باز فانوس را خاموش کرد.
بعد با دستمال شطرنجی قرمزی عرق پیشانیش را خشکاند و گفت:
-کار جان‌فرسایی دارم. پیش‌تر ها معقول بود: صبح خاموشش می‌کردم و شب که می‌شد روشنش می‌کردم. باقی روز را فرصت داشتم که استراحت کنم و باقی شب را هم می‌توانستم بگیرم بخوابم...
-بعدش دستور عوض شد؟
فانوس‌بان گفت: -دستور عوض نشد و بدبختی من هم از همین جاست: سیاره سال به سال گردشش تندتر و تندتر شده اما دستور همان جور به قوت خودش باقی مانده است.
-خب؟
-حالا که سیاره دقیقه‌ای یک بار دور خودش می‌گردد دیگر من یک ثانیه هم فرصت استراحت ندارم: دقیقه‌ای یک بار فانوس را روشن می‌کنم یک بار خاموش.
-چه عجیب است! تو اخترک تو شبانه روز همه‌اش یک دقیقه طول می‌کشد!
فانوس‌بان گفت: -هیچ هم عجیب نیست. الان یک ماه تمام است که ما داریم با هم اختلاط می‌کنیم.
-یک ماه؟
-آره. سی دقیقه. سی روز! شب خوش!
و دوباره فانوس را روشن کرد.
شهریار کوچولو به فانوس‌بان نگاه کرد و حس کرد این مرد را که تا این حد به دستور وفادار است دوست می‌دارد. یادِ آفتاب‌غروب‌هایی افتاد که آن وقت‌ها خودش با جابه‌جا کردن صندلیش دنبال می‌کرد. برای این که دستی زیر بال دوستش کرده باشد گفت:
-می‌دانی؟ یک راهی بلدم که می‌توانی هر وقت دلت بخواهد استراحت کنی.
فانوس‌بان گفت: -آرزوش را دارم.
آخر آدم می‌تواند هم به دستور وفادار بماند هم تنبلی کند.
شهریار کوچولو دنبال حرفش را گرفت و گفت:
-تو، اخترکت آن‌قدر کوچولوست که با سه تا شلنگ برداشتن می‌توانی یک بار دور بزنیش. اگر آن اندازه که لازم است یواش راه بروی می‌توانی کاری کنی که مدام تو آفتاب بمانی. پس هر وقت خواستی استراحت کنی شروع می‌کنی به راه‌رفتن... به این ترتیب روز هرقدر که بخواهی برایت کِش می‌آید.
فانوس‌بان گفت: -این کار گرهی از بدبختی من وا نمی‌کند. تنها چیزی که تو زندگی آرزویش را دارم یک چرت خواب است.
شهریار کوچولو گفت: -این یکی را دیگر باید بگذاری در کوزه.
فانوس‌بان گفت: -آره. باید بگذارمش در کوزه... صبح بخیر!
و فانوس را خاموش کرد.
شهریار کوچولو میان راه با خودش گفت: گرچه آن‌های دیگر، یعنی خودپسنده و تاجره اگر این را می‌دیدند دستش می‌انداختند و تحقیرش می‌کردند، هر چه نباشد کار این یکی به نظر من کم‌تر از کار آن‌ها بی‌معنی و مضحک است. شاید به خاطر این که دست کم این یکی به چیزی جز خودش مشغول است.
از حسرت آهی کشید و همان طور با خودش گفت:
-این تنها کسی بود که من می‌توانستم باش دوست بشوم. گیرم اخترکش راستی راستی خیلی کوچولو است و دو نفر روش جا نمی‌گیرند.
چیزی که جرات اعترافش را نداشت حسرت او بود به این اخترک کوچولویی که، بخصوص، به هزار و چهارصد و چهل بار غروب آفتاب در هر بیست و چهار ساعت برکت پیدا کرده بود.
۱۵
اخترک ششم اخترکی بود ده بار فراخ‌تر، و آقاپیره‌ای توش بود که کتاب‌های کَت‌وکلفت می‌نوشت.
جغرافی‌دان در سیاره‌اش
همین که چشمش به شهریار کوچولو افتاد با خودش گفت:
-خب، این هم یک کاشف!
شهریار کوچولو لب میز نشست و نفس نفس زد. نه این که راه زیادی طی کرده بود؟
آقا پیره به‌اش گفت: -از کجا می‌آیی؟
شهریار کوچولو گفت: -این کتاب به این کلفتی چی است؟ شما این‌جا چه‌کار می‌کنید؟
آقا پیره گفت: -من جغرافی‌دانم.
-جغرافی‌دان چه باشد؟
-جغرافی‌دان به دانشمندی می‌گویند که جای دریاها و رودخانه‌ها و شهرها و کوه‌ها و بیابان‌ها را می‌داند.
شهریار کوچولو گفت: -محشر است. یک کار درست و حسابی است.
و به اخترک جغرافی‌دان، این سو و آن‌سو نگاهی انداخت. تا آن وقت اخترکی به این عظمت ندیده‌بود.
-اخترک‌تان خیلی قشنگ است. اقیانوس هم دارد؟
جغرافی‌دان گفت: -از کجا بدانم؟
شهریار کوچولو گفت: -عجب! (بد جوری جا خورده بود) کوه چه‌طور؟
جغرافی‌دان گفت: -از کجا بدانم؟
-شهر، رودخانه، بیابان؟
جغرافی‌دان گفت: از این‌ها هم خبری ندارم.
-آخر شما جغرافی‌دانید؟
جغرافی‌دان گفت: -درست است ولی کاشف که نیستم. من حتا یک نفر کاشف هم ندارم. کار جغرافی‌دان نیست که دوره‌بیفتد برود شهرها و رودخانه‌ها و کوه‌ها و دریاها و اقیانوس‌ها و بیابان‌ها را بشمرد. مقام جغرافی‌دان برتر از آن است که دوره بیفتد و ول‌بگردد. اصلا از اتاق کارش پا بیرون نمی‌گذارد بلکه کاشف‌ها را آن تو می‌پذیرد ازشان سوالات می‌کند و از خاطرات‌شان یادداشت بر می‌دارد و اگر خاطرات یکی از آن‌ها به نظرش جالب آمد دستور می‌دهد روی خُلقیات آن کاشف تحقیقاتی صورت بگیرد.
-برای چه؟
-برای این که اگر کاشفی گنده‌گو باشد کار کتاب‌های جغرافیا را به فاجعه می‌کشاند. هکذا کاشفی که اهل پیاله باشد.
-آن دیگر چرا؟
b-چون آدم‌های دائم‌الخمر همه چیز را دوتا می‌بینند. آن وقت جغرافی‌دان برمی‌دارد جایی که یک کوه
بیشتر نیست می‌نویسد دو کوه.
شهریار کوچولو گفت: -پس من یک بابایی را می‌شناسم که کاشف هجوی از آب در می‌آید.
-بعید نیست. بنابراین، بعد از آن که کاملا ثابت شد پالان کاشف کج نیست تحقیقاتی هم روی کشفی که کرده انجام می‌گیرد.
-یعنی می‌روند می‌بینند؟
-نه، این کار گرفتاریش زیاد است. از خود کاشف می‌خواهند دلیل بیاورد. مثلا اگر پای کشف یک کوه بزرگ در میان بود ازش می‌خواهند سنگ‌های گنده‌ای از آن کوه رو کند.
جغرافی‌دان ناگهان به هیجان در آمد و گفت: -راستی تو داری از راه دوری می‌آیی! تو کاشفی! باید چند و چون اخترکت را برای من بگویی.
و با این حرف دفتر و دستکش را باز کرد و مدادش را تراشید. معمولا خاطرات کاشف‌ها را اول بامداد یادداشت می‌کنند و دست نگه می‌دارند تا دلیل اقامه کند، آن وقت با جوهر می‌نویسند.
گفت: -خب؟
شهریار کوچولو گفت: -اخترک من چیز چندان جالبی ندارد. آخر خیلی کوچک است. سه تا آتش‌فشان دارم که دوتاش فعال است یکیش خاموش. اما، خب دیگر، آدم کف دستش را که بو نکرده.
جغرافی‌دان هم گفت: -آدم چه می‌داند چه پیش می‌آید.
-یک گل هم دارم.
-نه، نه، ما دیگر گل ها را یادداشت نمی‌کنیم.
-چرا؟ گل که زیباتر است.
-برای این که گل‌ها فانی‌اند.
-فانی یعنی چی؟
جغرافی‌دان گفت: -کتاب‌های جغرافیا از کتاب‌های دیگر گران‌بهاترست و هیچ وقت هم از اعتبار نمی‌افتد. بسیار به ندرت ممکن است یک کوه جا عوض کند. بسیار به ندرت ممکن است آب یک اقیانوس خالی شود. ما فقط چیزهای پایدار را می‌نویسیم.
شهریار کوچولو تو حرف او دوید و گفت: -اما آتش‌فشان‌های خاموش می‌توانند از نو بیدار بشوند. فانی را نگفتید یعنی چه؟
جغرافی‌دان گفت: -آتش‌فشان چه روشن باشد چه خاموش برای ما فرقی نمی‌کند. آن‌چه به حساب می‌آید خود کوه است که تغییر پیدا نمی‌کند.
شهریار کوچولو که تو تمام عمرش وقتی چیزی از کسی می‌پرسید دیگر دست بردار نبود دوباره سوال کرد: -فانی یعنی چه؟
-یعنی چیزی که در آینده تهدید به نابودی شود.
-گل من هم در آینده نابود می‌شود؟
-البته که می‌شود.
شهریار کوچولو در دل گفت: «گل من فانی است و جلو دنیا برای دفاع از خودش جز چهارتا خار هیچی ندارد، و آن وقت مرا بگو که او را توی اخترکم تک و تنها رها کرده‌ام!»
این اولین باری بود که دچار پریشانی و اندوه می‌شد اما توانست به خودش مسلط بشود. پرسید: -شما به من دیدن کجا را توصیه می‌کنید؟
جغرافی‌دان به‌اش جواب داد: -سیاره‌ی زمین. شهرت خوبی دارد...
و شهریار کوچولو هم چنان که به گلش فکر می‌کرد به راه افتاد.
۱۶
لاجرم، زمین، سیاره‌ی هفتم شد.
زمین، فلان و بهمان سیاره نیست. رو پهنه‌ی زمین یک‌صد و یازده پادشاه (البته بامحاسبه‌ی پادشاهان سیاه‌پوست)، هفت هزار جغرافی‌دان، نه‌صد هزار تاجرپیشه، پانزده کرور می‌خواره و شش‌صد و بیست و دو کرور خودپسند و به عبارت دیگر حدود دو میلیارد آدم بزرگ زندگی می‌کند. برای آن‌که از حجم زمین مقیاسی به دست‌تان بدهم بگذارید به‌تان بگویم که پیش از اختراع برق مجبور بودند در مجموع شش قاره‌ی زمین وسایل زندگیِ لشکری جانانه شامل یکصد و شصت و دو هزار و پانصد و یازده نفر فانوس‌بان را تامین کنند.
روشن شدن فانوس‌ها از دور خیلی باشکوه بود. حرکات این لشکر مثل حرکات یک باله‌ی تو اپرا مرتب و منظم بود. اول از همه نوبت فانوس‌بان‌های زلاندنو و استرالیا بود. این‌ها که فانوس‌هاشان را روشن می‌کردند، می‌رفتند می‌گرفتند می‌خوابیدند آن وقت نوبت فانوس‌بان‌های چین و سیبری می‌رسید که به رقص درآیند. بعد، این‌ها با تردستی تمام به پشت صحنه می‌خزیدند و جا را برای فانوس‌بان‌های ترکیه و هفت پَرکَنِه‌ی هند خالی می کردند. بعد نوبت به فانوس‌بان‌های آمریکای‌جنوبی می‌شد. و آخر سر هم نوبت فانوس‌بان‌های افریقا و اروپا می‌رسد و بعد نوبت فانوس‌بان‌های آمریکای شمالی بود. و هیچ وقتِ خدا هم هیچ‌کدام این‌ها در ترتیب ورودشان به صحنه دچار اشتباه نمی‌شدند. چه شکوهی داشت! میان این جمع عظیم فقط نگه‌بانِ تنها فانوسِ قطب شمال و همکارش نگه‌بانِ تنها فانوسِ قطب جنوب بودند که عمری به بطالت و بی‌هودگی می‌گذراندند: آخر آن‌ها سالی به سالی همه‌اش دو بار کار می‌کردند.
۱۷
آدمی که اهل اظهار لحیه باشد بفهمی نفهمی می‌افتد به چاخان کردن. من هم تو تعریف قضیه‌ی فانوس‌بان‌ها برای شما آن‌قدرهاروراست نبودم. می‌ترسم به آن‌هایی که زمین ما را نمی‌سناسند تصور نادرستی داده باشم. انسان‌ها رو پهنه‌ی زمین جای خیلی کمی را اشغال می‌کنند. اگر همه‌ی دو میلیارد نفری که رو کره‌ی زمین زندگی می‌کنند بلند بشوند و مثل موقعی که به تظاهرات می‌روند یک خورده جمع و جور بایستند راحت و بی‌درپسر تو میدانی به مساحت بیست میل در بیست میل جا می‌گیرند. همه‌ی جامعه‌ی بشری را می‌شود یک‌جا روی کوچک‌ترین جزیره‌ی اقیانوس آرام کُپه کرد.
البته گفت‌وگو ندارد که آدم بزرگ‌ها حرف‌تان را باور نمی‌کنند. آخر تصور آن‌ها این است که کلی جا اشغال کرده‌اند، نه این‌که مثل بائوباب‌ها خودشان را خیلی مهم می‌بینند؟ بنابراین به‌شان پیش‌نهاد می‌کنید که بنشینند حساب کنند. آن‌ها هم که عاشق اعداد و ارقامند، پس این پیش‌نهاد حسابی کیفورشان می‌کند. اما شما را به خدا بی‌خودی وقت خودتان را سر این جریمه‌ی مدرسه به هدر ندهید. این کار دو قاز هم نمی‌ارزد. به من که اطمینان دارید. شهریار کوچولو پاش که به زمین رسید از این که دیارالبشری دیده نمی‌شد سخت هاج و واج ماند.
شهریار کوچولو وسطِ کویر
تازه داشت از این فکر که شاید سیاره را عوضی گرفته ترسش بر می‌داشت که چنبره‌ی مهتابی رنگی رو ماسه‌ها جابه‌جا شد.شهریار کوچولو و مار
شهریار کوچولو همین‌جوری سلام کرد.
مار گفت: -سلام.
شهریار کوچولو پرسید: -رو چه سیاره‌ای پایین آمده‌ام؟
مار جواب داد: -رو زمین تو قاره‌ی آفریقا.
-عجب! پس رو زمین انسان به هم نمی‌رسد؟
مار گفت: -این‌جا کویر است. تو کویر کسی زندگی نمی‌کند. زمین بسیار وسیع است.
شهریار کوچولو رو سنگی نشست و به آسمان نگاه کرد. گفت: -به خودم می‌گویم ستاره‌ها واسه این روشنند که هرکسی بتواند یک روز مال خودش را پیدا کند!... اخترک مرا نگاه! درست بالا سرمان است... اما چه‌قدر دور است!
مار گفت: -قشنگ است. این‌جا آمده‌ای چه کار؟
شهریار کوچولو گفت: -با یک گل بگومگویم شده.
مار گفت: -عجب!
و هر دوشان خاموش ماندند.
دست آخر شهریار کوچولو درآمد که: -آدم‌ها کجاند؟ آدم تو کویر یک خرده احساس تنهایی می‌کند.
مار گفت: -پیش آدم‌ها هم احساس تنهایی می‌کنی.
شهریار کوچولو مدت درازی تو نخ او رفت و آخر سر به‌اش گفت: -تو چه جانور بامزه‌ای هستی! مثل یک انگشت، باریکی.
مار گفت: -عوضش از انگشت هر پادشاهی مقتدرترم.
شهریار کوچولو لب‌خندی زد و گفت: -نه چندان... پا هم که نداری. حتا راه هم نمی‌تونی بری...
-من می‌تونم تو را به چنان جای دوری ببرم که با هیچ کشتی‌یی هم نتونی بری.
مار این را گفت و دور قوزک پای شهریار کوچولو پیچید. عین یک خلخال طلا. و باز درآمد که: -هر کسی را لمس کنم به خاکی که ازش درآمده بر می‌گردانم اما تو پاکی و از یک سیّاره‌ی دیگر آمده‌ای...
شهریار کوچولو جوابی بش نداد.
-تو رو این زمین خارایی آن‌قدر ضعیفی که به حالت رحمم می‌آید. روزی‌روزگاری اگر دلت خیلی هوای اخترکت را کرد بیا من کمکت کنم... من می‌توانم...
شهریار کوچولو گفت: -آره تا تهش را خواندم. اما راستی تو چرا همه‌ی حرف‌هایت را به صورت معما درمی‌آری؟
مار گفت: -حلّال همه‌ی معماهام من.
و هر دوشان خاموش شدند.
۱۸
شهریار کوچولو کویر را از پاشنه درکرد و جز یک گل به هیچی برنخورد: یک گل سه گل‌برگه. یک گلِ ناچیز.
یک گُل وسطِ کویر
شهریار کوچولو گفت: -سلام.
گل گفت: -سلام.
شهریار کوچولو با ادب پرسید: -آدم‌ها کجاند؟
گل روزی روزگاری عبور کاروانی را دیده‌بود. این بود که گفت: -آدم‌ها؟ گمان کنم ازشان شش هفت تایی باشد. سال‌ها پیش دیدم‌شان. منتها خدا می‌داند کجا می‌شود پیداشان کرد. باد این‌ور و آن‌ور می‌بَرَدشان؛ نه این که ریشه ندارند؟ بی‌ریشگی هم حسابی اسباب دردسرشان شده.
شهریار کوچولو گفت: -خداحافظ.
گل گفت: -خداحافظ.
۱۹
از کوه بلندی بالا رفت.
شهریار کوچولو بر قله‌ی کوهِ بلند
تنها کوه‌هایی که به عمرش دیده بود سه تا آتش‌فشان‌های اخترک خودش بود که تا سر زانویش می‌رسید و از آن یکی که خاموش بود جای چارپایه استفاده می‌کرد. این بود که با خودش گفت: «از سر یک کوه به این بلندی می‌توانم به یک نظر همه‌ی سیاره و همه‌ی آدم‌ها را ببینم...» اما جز نوکِ تیزِ صخره‌های نوک‌تیز چیزی ندید.
همین جوری گفت: -سلام.
طنین به‌اش جواب داد: -سلام... سلام... سلام...
شهریار کوچولو گفت: -کی هستید شما؟
طنین به‌اش جواب داد: -کی هستید شما... کی هستید شما... کی هستید شما...
گفت: -با من دوست بشوید. من تک و تنهام.
طنین به‌اش جواب داد: -من تک و تنهام... من تک و تنهام... من تک و تنهام...
آن‌وقت با خودش فکر کرد: «چه سیاره‌ی عجیبی! خشک‌ِخشک و تیزِتیز و شورِشور. این آدم‌هاش که یک ذره قوه‌ی تخیل ندارند و هر چه را بشنوند عینا تکرار می‌کنند... تو اخترک خودم گلی داشتم که همیشه اول او حرف می‌زد...»
۲۰
اما سرانجام، بعد از مدت‌ها راه رفتن از میان ریگ‌ها و صخره‌ها و برف‌ها به جاده‌ای برخورد. و هر جاده‌ای یک‌راست می‌رود سراغ آدم‌ها.
گفت: -سلام.
و مخاطبش گلستان پرگلی بود.
شهریار کوچولو در گلستانِ پرگل
گل‌ها گفتند: -سلام.
شهریار کوچولو رفت تو بحرشان. همه‌شان عین گل خودش بودند. حیرت‌زده ازشان پرسید: -شماها کی هستید؟
گفتند: -ما گل سرخیم.
آهی کشید و سخت احساس شوربختی کرد. گلش به او گفته بود که از نوع او تو تمام عالم فقط همان یکی هست و حالا پنج‌هزارتا گل، همه مثل هم، فقط تو یک گلستان! فکر کرد: «اگر گل من این را می‌دید بدجور از رو می‌رفت. پشت سر هم بنا می‌کرد سرفه‌کردن و، برای این‌که از هُوشدن نجات پیدا کند خودش را به مردن می‌زد و من هم مجبور می‌شدم وانمود کنم به پرستاریش، وگرنه برای سرشکسته کردنِ من هم شده بود راستی راستی می‌مرد...» و باز تو دلش گفت: «مرا باش که فقط بایک دانه گل خودم را دولت‌مندِ عالم خیال می‌کردم در صورتی‌که آن‌چه دارم فقط یک گل معمولی است. با آن گل و آن سه تا آتش‌فشان که تا سرِ زانومَند و شاید هم یکی‌شان تا ابد خاموش بماند شهریارِ چندان پُرشوکتی به حساب نمی‌آیم.»
شهریار کوچولو در حالِ احساسِ شوربختی
رو سبزه‌ها دراز شد و حالا گریه نکن کی گریه‌کن.
۲۱
آن وقت بود که سر و کله‌ی روباه پیدا شد.
شهریار کوچولو و روباه
روباه گفت: -سلام.
شهریار کوچولو برگشت اما کسی را ندید. با وجود این با ادب تمام گفت: -سلام.
صداگفت: -من این‌جام، زیر درخت سیب...
شهریار کوچولو گفت: -کی هستی تو؟ عجب خوشگلی!
روباه گفت: -یک روباهم من.
شهریار کوچولو گفت: -بیا با من بازی کن. نمی‌دانی چه قدر دلم گرفته...
روباه گفت: -نمی‌توانم بات بازی کنم. هنوز اهلیم نکرده‌اند آخر.
شهریار کوچولو آهی کشید و گفت: -معذرت می‌خواهم.
اما فکری کرد و پرسید: -اهلی کردن یعنی چه؟
روباه گفت: -تو اهل این‌جا نیستی. پی چی می‌گردی؟
شهریار کوچولو گفت: -پی آدم‌ها می‌گردم. نگفتی اهلی کردن یعنی چه؟
روباه گفت: -آدم‌ها تفنگ دارند و شکار می‌کنند. اینش اسباب دلخوری است! اما مرغ و ماکیان هم پرورش می‌دهند و خیرشان فقط همین است. تو پی مرغ می‌کردی؟
شهریار کوچولو گفت: -نَه، پیِ دوست می‌گردم. اهلی کردن یعنی چی؟
روباه گفت: -یک چیزی است که پاک فراموش شده. معنیش ایجاد علاقه کردن است.
-ایجاد علاقه کردن؟
روباه گفت: -معلوم است. تو الان واسه من یک پسر بچه‌ای مثل صد هزار پسر بچه‌ی دیگر. نه من هیچ احتیاجی به تو دارم نه تو هیچ احتیاجی به من. من هم واسه تو یک روباهم مثل صد هزار روباه دیگر. اما اگر منو اهلی کردی هر دوتامان به هم احتیاج پیدا می‌کنیم. تو واسه من میان همه‌ی عالم موجود یگانه‌ای می‌شوی من واسه تو.
شهریار کوچولو گفت: -کم‌کم دارد دستگیرم می‌شود. یک گلی هست که گمانم مرا اهلی کرده باشد.
روباه گفت: -بعید نیست. رو این کره‌ی زمین هزار جور چیز می‌شود دید.
شهریار کوچولو گفت: -اوه نه! آن رو کره‌ی زمین نیست.
روباه که انگار حسابی حیرت کرده بود گفت: -رو یک سیاره‌ی دیگر است؟
-آره.
شکارچی-تو آن سیاره شکارچی هم هست؟
-نه.
-محشر است! مرغ و ماکیان چه‌طور؟
-نه.
روباه آه‌کشان گفت: -همیشه‌ی خدا یک پای بساط لنگ است!
اما پی حرفش را گرفت و گفت: -زندگی یک‌نواختی دارم. من مرغ‌ها را شکار می‌کنم آدم‌ها مرا. همه‌ی مرغ‌ها عین همند همه‌ی آدم‌ها هم عین همند. این وضع یک خرده خلقم را تنگ می‌کند. اما اگر تو منو اهلی کنی انگار که زندگیم را چراغان کرده باشی. آن وقت صدای پایی را می‌شناسم که باهر صدای پای دیگر فرق می‌کند: صدای پای دیگران مرا وادار می‌کند تو هفت تا سوراخ قایم بشوم اما صدای پای تو مثل نغمه‌ای مرا از سوراخم می‌کشد بیرون. تازه، نگاه کن آن‌جا آن گندم‌زار را می‌بینی؟ برای من که نان بخور نیستم گندم چیز بی‌فایده‌ای است. پس گندم‌زار هم مرا به یاد چیزی نمی‌اندازد. اسباب تاسف است. اما تو موهات رنگ طلا است. پس وقتی اهلیم کردی محشر می‌شود! گندم که طلایی رنگ است مرا به یاد تو می‌اندازد و صدای باد را هم که تو گندم‌زار می‌پیچد دوست خواهم داشت...
خاموش شد و مدت درازی شهریار کوچولو را نگاه کرد. آن وقت گفت: -اگر دلت می‌خواهد منو اهلی کن!
شهریار کوچولو جواب داد: -دلم که خیلی می‌خواهد، اما وقتِ چندانی ندارم. باید بروم دوستانی پیدا کنم و از کلی چیزها سر در آرم.
روباه گفت: -آدم فقط از چیزهایی که اهلی کند می‌تواند سر در آرد. انسان‌ها دیگر برای سر در آوردن از چیزها وقت ندارند. همه چیز را همین جور حاضر آماده از دکان‌ها می‌خرند. اما چون دکانی نیست که دوست معامله کند آدم‌ها مانده‌اند بی‌دوست... تو اگر دوست می‌خواهی خب منو اهلی کن!
شهریار کوچولو پرسید: -راهش چیست؟
روباه جواب داد: -باید خیلی خیلی حوصله کنی. اولش یک خرده دورتر از من می‌گیری این جوری میان علف‌ها می‌نشینی. من زیر چشمی نگاهت می‌کنم و تو لام‌تاکام هیچی نمی‌گویی، چون تقصیر همه‌ی سؤِتفاهم‌ها زیر سر زبان است. عوضش می‌توانی هر روز یک خرده نزدیک‌تر بنشینی.
روباه در حالِ انتظارفردای آن روز دوباره شهریار کوچولو آمد.
روباه گفت: -کاش سر همان ساعت دیروز آمده بودی. اگر مثلا سر ساعت چهار بعد از ظهر بیایی من از ساعت سه تو دلم قند آب می‌شود و هر چه ساعت جلوتر برود بیش‌تر احساس شادی و خوشبختی می‌کنم. ساعت چهار که شد دلم بنا می‌کند شور زدن و نگران شدن. آن وقت است که قدرِ خوشبختی را می‌فهمم! اما اگر تو وقت و بی وقت بیایی من از کجا بدانم چه ساعتی باید دلم را برای دیدارت آماده کنم؟... هر چیزی برای خودش قاعده‌ای دارد.
شهریار کوچولو گفت: -قاعده یعنی چه؟
روباه گفت: -این هم از آن چیزهایی است که پاک از خاطرها رفته. این همان چیزی است که باعث می‌شود فلان روز با باقی روزها و فلان ساعت با باقی ساعت‌ها فرق کند. مثلا شکارچی‌های ما میان خودشان رسمی دارند و آن این است که پنج‌شنبه‌ها را با دخترهای ده می‌روند رقص. پس پنج‌شنبه‌ها بَرّه‌کشانِ من است: برای خودم گردش‌کنان می‌روم تا دم مُوِستان. حالا اگر شکارچی‌ها وقت و بی وقت می‌رقصیدند همه‌ی روزها شبیه هم می‌شد و منِ بیچاره دیگر فرصت و فراغتی نداشتم.
به این ترتیب شهریار کوچولو روباه را اهلی کرد.
لحظه‌ی جدایی که نزدیک شد روباه گفت: -آخ! نمی‌توانم جلو اشکم را بگیرم.
شهریار کوچولو گفت: -تقصیر خودت است. من که بدت را نمی‌خواستم، خودت خواستی اهلیت کنم.
روباه گفت: -همین طور است.
شهریار کوچولو گفت: -آخر اشکت دارد سرازیر می‌شود!
روباه گفت: -همین طور است.
-پس این ماجرا فایده‌ای به حال تو نداشته.
روباه گفت: -چرا، واسه خاطرِ رنگ گندم.
بعد گفت: -برو یک بار دیگر گل‌ها را ببین تا بفهمی که گلِ خودت تو عالم تک است. برگشتنا با هم وداع می‌کنیم و من به عنوان هدیه رازی را به‌ات می‌گویم.
شهریار کوچولو بار دیگر به تماشای گل‌ها رفت و به آن‌ها گفت: -شما سرِ سوزنی به گل من نمی‌مانید و هنوز هیچی نیستید. نه کسی شما را اهلی کرده نه شما کسی را. درست همان جوری هستید که روباه من بود: روباهی بود مثل صدهزار روباه دیگر. او را دوست خودم کردم و حالا تو همه‌ی عالم تک است.
گل‌ها حسابی از رو رفتند.
شهریار کوچولو دوباره درآمد که: -خوشگلید اما خالی هستید. برای‌تان نمی‌شود مُرد. گفت‌وگو ندارد که گلِ مرا هم فلان ره‌گذر می‌بیند مثل شما. اما او به تنهایی از همه‌ی شما سر است چون فقط اوست که آبش داده‌ام، چون فقط اوست که زیر حبابش گذاشته‌ام، چون فقط اوست که با تجیر برایش حفاظ درست کرده‌ام، چون فقط اوست که حشراتش را کشته‌ام (جز دو سه‌تایی که می‌بایست شب‌پره بشوند)، چون فقط اوست که پای گِلِه‌گزاری‌ها یا خودنمایی‌ها و حتا گاهی پای بُغ کردن و هیچی نگفتن‌هاش نشسته‌ام، چون او گلِ من است.
و برگشت پیش روباه.
گفت: -خدانگه‌دار!
روباه گفت: -خدانگه‌دار!... و اما رازی که گفتم خیلی ساده است:
جز با دل هیچی را چنان که باید نمی‌شود دید. نهاد و گوهر را چشمِ سَر نمی‌بیند.
شهریار کوچولو برای آن که یادش بماند تکرار کرد: -نهاد و گوهر را چشمِ سَر نمی‌بیند.
-ارزش گل تو به قدرِ عمری است که به پاش صرف کرده‌ای.
شهریار کوچولو برای آن که یادش بماند تکرار کرد: -به قدر عمری است که به پاش صرف کرده‌ام.
روباه گفت: -انسان‌ها این حقیقت را فراموش کرده‌اند اما تو نباید فراموشش کنی. تو تا زنده‌ای نسبت به چیزی که اهلی کرده‌ای مسئولی. تو مسئول گُلِتی...
شهریار کوچولو برای آن که یادش بماند تکرار کرد: -من مسئول گُلمَم.
۲۲
شهریار کوچولو گفت: -سلام.
سوزن‌بان گفت: -سلام.
شهریار کوچولو گفت: -تو چه کار می‌کنی این‌جا؟
سوزن‌بان گفت: -مسافرها را به دسته‌های هزارتایی تقسیم می‌کنم و قطارهایی را که می‌بَرَدشان گاهی به سمت راست می‌فرستم گاهی به سمت چپ. و همان دم سریع‌السیری با چراغ‌های روشن و غرّشی رعدوار اتاقک سوزن‌بانی را به لرزه انداخت.
-عجب عجله‌ای دارند! پیِ چی می‌روند؟
سوزن‌بان گفت: -از خودِ آتش‌کارِ لکوموتیف هم بپرسی نمی‌داند!
سریع‌السیر دیگری با چراغ‌های روشن غرّید و در جهت مخالف گذشت .
شهریار کوچولو پرسید: -برگشتند که؟
سوزن‌بان گفت: -این‌ها اولی‌ها نیستند. آن‌ها رفتند این‌ها برمی‌گردند.
-جایی را که بودند خوش نداشتند؟
سوزن‌بان گفت: -آدمی‌زاد هیچ وقت جایی را که هست خوش ندارد.
و رعدِ سریع‌السیرِ نورانیِ ثالثی غرّید.
شهریار کوچولو پرسید: -این‌ها دارند مسافرهای اولی را دنبال می‌کنند؟
سوزن‌بان گفت: -این‌ها هیچ چیزی را دنبال نمی‌کنند. آن تو یا خواب‌شان می‌بَرَد یا دهن‌دره می‌کنند. فقط بچه‌هاند که دماغ‌شان را فشار می‌دهند به شیشه‌ها.
شهریار کوچولو گفت: -فقط بچه‌هاند که می‌دانند پیِ چی می‌گردند. بچه‌هاند که کُلّی وقت صرف یک عروسک پارچه‌ای می‌کنند و عروسک برای‌شان آن قدر اهمیت به هم می‌رساند که اگر یکی آن را ازشان کِش برود می‌زنند زیر گریه...
سوزن‌بان گفت: -بخت، یارِ بچه‌هاست.
۲۳
شهریار کوچولو گفت: -سلام!
پیله‌ور گفت: -سلام.
این بابا فروشنده‌ی حَب‌های ضد تشنگی بود. خریدار هفته‌ای یک حب می‌انداخت بالا و دیگر تشنگی بی تشنگی.
شهریار کوچولو پرسید: -این‌ها را می‌فروشی که چی؟
پیله‌ور گفت: -باعث صرفه‌جویی کُلّی وقت است. کارشناس‌های خبره نشسته‌اند دقیقا حساب کرده‌اند که با خوردن این حب‌ها هفته‌ای پنجاه و سه دقیقه وقت صرفه‌جویی می‌شود.
-خب، آن وقت آن پنجاه و سه دقیقه را چه کار می‌کنند؟
ـ هر چی دل‌شان خواست...
چشمه
شهریار کوچولو تو دلش گفت: «من اگر پنجاه و سه دقیقه وقتِ زیادی داشته باشم خوش‌خوشک به طرفِ یک چشمه می‌روم...»
۲۴
هشتمین روزِ خرابی هواپیمام تو کویر بود که، در حال نوشیدنِ آخرین چک‌ّه‌ی ذخیره‌ی آبم به قضیه‌ی پیله‌وره گوش داده بودم. به شهریار کوچولو گفتم:
-خاطرات تو راستی راستی زیباند اما من هنوز از پسِ تعمیر هواپیما برنیامده‌ام، یک چکه آب هم ندارم. و راستی که من هم اگر می‌توانستم خوش‌خوشک به طرف چشمه‌ای بروم سعادتی احساس می‌کردم که نگو!
درآمد که: -دوستم روباه...
گفتم: -آقا کوچولو، دورِ روباه را قلم بگیر!
-واسه چی؟
-واسه این که تشنگی کارمان را می سازد. واسه این!
از استدلال من چیزی حالیش نشد و در جوابم گفت:
-حتا اگر آدم دَمِ مرگ باشد هم داشتن یک دوست عالی است. من که از داشتن یک دوستِ روباه خیلی خوشحالم...
به خودم گفتم نمی‌تواند میزان خطر را تخمین بزند: آخر او هیچ وقت نه تشنه‌اش می‌شود نه گشنه‌اش. یه ذره آفتاب بسش است...
اما او به من نگاه کرد و در جواب فکرم گفت: -من هم تشنه‌م است... بگردیم یک چاه پیدا کنیم...
از سرِ خستگی حرکتی کردم: -این جوری تو کویرِ برهوت رو هوا پیِ چاه گشتن احمقانه است.
و با وجود این به راه افتادیم.
پس از ساعت‌ها که در سکوت راه رفتیم شب شد و ستاره‌ها یکی یکی درآمدند. من که از زور تشنگی تب کرده بودم انگار آن‌ها را خواب می‌دیدم. حرف‌های شهریار کوچولو تو ذهنم می‌رقصید.
ازش پرسیدم: -پس تو هم تشنه‌ات هست، ها؟
اما او به سوآلِ من جواب نداد فقط در نهایت سادگی گفت: -آب ممکن است برای دلِ من هم خوب باشد...
از حرفش چیزی دستگیرم نشد اما ساکت ماندم. می‌دانستم از او نباید حرف کشید.
خسته شده بود. گرفت نشست. من هم کنارش نشستم. پس از مدتی سکوت گفت:
-قشنگیِ ستاره‌ها واسه خاطرِ گلی است که ما نمی‌بینیمش...
گفتم: -همین طور است
و بدون حرف در مهتاب غرق تماشای چین و شکن‌های شن شدم.
باز گفت: -کویر زیباست.
و حق با او بود. من همیشه عاشق کویر بوده‌ام. آدم بالای توده‌ای شن لغزان می‌نشیند، هیچی نمی‌بیند و هیچی نمی‌شنود اما با وجود این چیزی توی سکوت برق‌برق می‌زند.
شهریار کوچولو گفت: -چیزی که کویر را زیبا می‌کند این است که یک جایی یک چاه قایم کرده...
از این‌که ناگهان به راز آن درخشش اسرارآمیزِ شن پی بردم حیرت‌زده شدم. بچگی‌هام تو خانه‌ی کهنه‌سازی می‌نشستیم که معروف بود تو آن گنجی چال کرده‌اند. البته نگفته پیداست که هیچ وقت کسی آن را پیدا نکرد و شاید حتا اصلا کسی دنبالش نگشت اما فکرش همه‌ی اهل خانه را تردماغ می‌کرد: «خانه‌ی ما تهِ دلش رازی پنهان کرده بود...»
گفتم: -آره. چه خانه باشد چه ستاره، چه کویر، چیزی که اسباب زیبایی‌اش می‌شود نامریی است!
گفت: -خوشحالم که با روباه من توافق داری.
چون خوابش برده بود بغلش کردم و راه افتادم. دست و دلم می‌لرزید.انگار چیز شکستنیِ بسیار گران‌بهایی را روی دست می‌بردم. حتا به نظرم می‌آمد که تو تمام عالم چیزی شکستنی‌تر از آن هم به نظر نمی‌رسد. تو روشنی مهتاب به آن پیشانی رنگ‌پریده و آن چشم‌های بسته و آن طُرّه‌های مو که باد می‌جنباند نگاه کردم و تو دلم گفتم: «آن چه می‌بینم صورت ظاهری بیش‌تر نیست. مهم‌ترش را با چشم نمی‌شود دید...»
باز، چون دهان نیمه‌بازش طرح کم‌رنگِ نیمه‌لبخندی را داشت به خود گفتم: «چیزی که تو شهریار کوچولوی خوابیده مرا به این شدت متاثر می‌کند وفاداری اوست به یک گل: او تصویرِ گل سرخی است که مثل شعله‌ی چراغی حتا در خوابِ ناز هم که هست تو وجودش می‌درخشد...» و آن وقت او را باز هم شکننده‌تر دیدم. حس کردم باید خیلی مواظبش باشم: به شعله‌ی چراغی می‌مانست که یک وزش باد هم می‌توانست خاموشش کند.
و همان طور در حال راه رفتن بود که دمدمه‌ی سحر چاه را پیداکردم.
۲۵
شهریار کوچولو درآمد که: -آدم‌ها!... می‌چپند تو قطارهای تندرو اما نمی‌دانند دنبال چی می‌گردند. این است که بنامی‌کنند دور خودشان چرخک‌زدن.
و بعد گفت: -این هم کار نشد...
چاهی که به‌اش رسیده‌بودیم اصلا به چاه‌های کویری نمی‌مانست. چاه کویری یک چاله‌ی ساده است وسط شن‌ها. این یکی به چاه‌های واحه‌ای می‌مانست اما آن دوروبر واحه‌ای نبود و من فکر کردم دارم خواب می‌بینم.
گفتم: -عجیب است! قرقره و سطل و تناب، همه‌چیز روبه‌راه است.
خندید تناب را گرفت و قرقره را به کار انداخت
شهریار کوچولو در حالِ کشیدنِ آب از چاه
و قرقره مثل بادنمای کهنه‌ای که تا مدت‌ها پس از خوابیدنِ باد می‌نالد به ناله‌درآمد.
گفت: -می‌شنوی؟ ما داریم این چاه را از خواب بیدار می‌کنیم و او دارد برای‌مان آواز می‌خواند...
دلم نمی‌خواست او تلاش و تقلا کند. بش گفتم: -بدهش به من. برای تو زیادی سنگین است.
سطل را آرام تا طوقه‌ی چاه آوردم بالا و آن‌جا کاملا در تعادل نگهش داشتم. از حاصل کار شاد بودم. خسته و شاد. آواز قرقره را همان‌طور تو گوشم داشتم و تو آب که هنوز می‌لرزید لرزش خورشید را می‌دیدم.
گفت: -بده من، که تشنه‌ی این آبم.
ومن تازه توانستم بفهمم پی چه چیز می‌گشته!
سطل را تا لب‌هایش بالا بردم. با چشم‌های بسته نوشید. آبی بود به شیرینیِ عیدی. این آب به کُلّی چیزی بود سوایِ هرگونه خوردنی. زاییده‌ی راه رفتنِ زیر ستاره‌ها و سرود قرقره و تقلای بازوهای من بود. مثل یک چشم روشنی برای دل خوب بود. پسر بچه که بودم هم، چراغ درخت عید و موسیقیِ نماز نیمه‌شب عید کریسمس و لطف لب‌خنده‌ها عیدیی را که بم می‌دادند درست به همین شکل آن همه جلا و جلوه می‌بخشید.
گفت: -مردم سیاره‌ی تو ور می‌دارند پنج هزار تا گل را تو یک گلستان می‌کارند، و آن یک دانه‌ای را که پِیَش می‌گردند آن وسط پیدا نمی‌کنند...
گفتم: -پیدایش نمی‌کنند.
-با وجود این، چیزی که پیَش می‌گردند ممکن است فقط تو یک گل یا تو یک جرعه آب پیدا بشود...
جواب دادم: -گفت‌وگو ندارد.
باز گفت: -گیرم چشمِ سَر کور است، باید با چشم دل پی‌اش گشت.
من هم سیراب شده بودم. راحت نفس می‌کشیدم. وقتی آفتاب درمی‌آید شن به رنگ عسل است. من هم از این رنگ عسلی لذت می‌بردم. چرا می‌بایست در زحمت باشم...
شهریار کوچولو که باز گرفته بود کنار من نشسته بود با لطف بم گفت: -هِی! قولت قول باشد ها!
-کدام قول؟
-یادت است؟ یک پوزه‌بند برای بَرّه‌ام... آخر من مسئول گلمَم!
طرح‌های اولیه‌ام را از جیب درآوردم. نگاه‌شان کرد و خندان‌خندان گفت: -بائوباب‌هات یک خرده شبیه کلم شده.
ای وای! مرا بگو که آن‌قدر به بائوباب‌هام می‌نازیدم.
-روباهت... گوش‌هاش بیش‌تر به شاخ می‌ماند... زیادی درازند!
و باز زد زیر خنده.
-آقا کوچولو داری بی‌انصافی می‌کنی. من جز بوآهای بسته و بوآهای باز چیزی بلد نبودم بکشم که.
گفت: -خب، مهم نیست. عوضش بچه‌ها سرشان تو حساب است.
با مداد یک پوزه‌بند کشیدم دادم دستش و با دلِ فشرده گفتم:
-تو خیالاتی به سر داری که من ازشان بی‌خبرم...
اما جواب مرا نداد. بم گفت: -می‌دانی؟ فردا سالِ به زمین آمدنِ من است.
بعد پس از لحظه‌ای سکوت دوباره گفت: -همین نزدیکی‌ها پایین آمدم.
و سرخ شد.
و من از نو بی این که بدانم چرا غم عجیبی احساس کردم. با وجود این سوآلی به ذهنم رسید: -پس هشت روز پیش، آن روز صبح که تو تک و تنها هزار میل دورتر از هر آبادی وسطِ کویر به من برخوردی اتفاقی نبود: داشتی برمی‌گشتی به همان جایی که پایین‌آمدی...
دوباره سرخ شد
و من با دودلی به دنبال حرفم گفتم:
-شاید به مناسبت همین سال‌گرد؟...
باز سرخ شد. او هیچ وقت به سوآل‌هایی که ازش می‌شد جواب نمی‌داد اما وقتی کسی سرخ می‌شود معنیش این است که «بله»، مگر نه؟
به‌اش گفتم: -آخر، من ترسم برداشته...
اما او حرفم را برید:
-دیگر تو باید بروی به کارت برسی. باید بروی سراغ موتورت. من همین‌جا منتظرت می‌مانم. فردا عصر برگرد...
منتها من خاطر جمع نبودم. به یاد روباه افتادم: اگر آدم گذاشت اهلیش کنند بفهمی‌نفهمی خودش را به این خطر انداخته که کارش به گریه‌کردن بکشد.
۲۶
کنار چاه دیوارِ سنگی مخروبه‌ای بود. فردا عصر که از سرِ کار برگشتم از دور دیدم که آن بالا نشسته پاها را آویزان کرده،
شهریار کوچولو نشسته بر دیوارِ سنگی و مار در پایینِ آن
و شنیدم که می‌گوید:
-پس یادت نمی‌آید؟ درست این نقطه نبود ها!
لابد صدای دیگری به‌اش جوابی داد، چون شهریار کوچولو در رَدِّ حرفش گفت:
-چرا چرا! روزش که درست همین امروز است گیرم محلش این جا نیست...
راهم را به طرف دیوار ادامه دادم. هنوز نه کسی به چشم خورده بود نه صدای کسی را شنیده بودم اما شهریار کوچولو باز در جواب درآمد که:
-... آره، معلوم است. خودت می‌توانی ببینی رَدِّ پاهایم روی شن از کجا شروع می‌شود.
همان جا منتظرم باش، تاریک که شد می‌آیم.
بیست متری دیوار بودم و هنوز چیزی نمی‌دیدم. پس از مختصر مکثی دوباره گفت:
-زهرت خوب هست؟ مطمئنی درد و زجرم را کِش نمی‌دهد؟
با دل فشرده از راه ماندم اما هنوز از موضوع سر در نیاورده بودم.
گفت: -خب، حالا دیگر برو. دِ برو. می‌خواهم بیایم پایین!
آن وقت من نگاهم را به پایین به پای دیوار انداختم و از جا جستم! یکی از آن مارهای زردی که تو سی ثانیه کَلَکِ آدم را می‌کنند، به طرف شهریار کوچولو قد راست کرده بود. من همان طور که به دنبال تپانچه دست به جیبم می‌بردم پا گذاشتم به دو، اما ماره از سر و صدای من مثل فواره‌ای که بنشیند آرام روی شن جاری شد و بی آن که چندان عجله‌ای از خودش نشان دهد باصدای خفیف فلزی لای سنگ‌ها خزید.
من درست به موقع به دیوار رسیدم و طفلکی شهریار کوچولو را که رنگش مثل برف پریده بود تو هوا بغل کردم.
-این دیگر چه حکایتی است! حالا دیگر با مارها حرف می‌زنی؟
شال زردش را که مدام به گردن داشت باز کردم به شقیقه‌هایش آب زدم و جرعه‌ای به‌اش نوشاندم. اما حالا دیگر اصلا جرات نمی کردم ازش چیزی بپرسم. با وقار به من نگاه کرد و دستش را دور گردنم انداخت. حس کردم قلبش مثل قلب پرنده‌ای می‌زند که تیر خورده‌است و دارد می‌میرد.
گفت: -از این که کم و کسرِ لوازم ماشینت را پیدا کردی خوش‌حالم. حالا می‌توانی برگردی خانه‌ات...
-تو از کجا فهمیدی؟
درست همان دم لب‌واکرده‌بودم بش خبر بدهم که علی‌رغم همه‌ی نومیدی‌ها تو کارم موفق شده‌ام!
به سوآل‌های من هیچ جوابی نداد اما گفت: -آخر من هم امروز بر می‌گردم خانه‌ام...
و بعد غم‌زده درآمد که: -گیرم راه من خیلی دورتر است... خیلی سخت‌تر است...
حس می‌کردم اتفاق فوق‌العاده‌ای دارد می‌افتد. گرفتمش تو بغلم. عین یک بچه‌ی کوچولو. با وجود این به نظرم می‌آمد که او دارد به گردابی فرو می‌رود و برای نگه داشتنش از من کاری ساخته نیست... نگاه متینش به دوردست‌های دور راه کشیده بود.
گفت: بَرِّه‌ات را دارم. جعبه‌هه را هم واسه بره‌هه دارم. پوزه‌بنده را هم دارم.
و با دلِ گرفته لبخندی زد.
مدت درازی صبر کردم. حس کردم کم‌کمَک تنش دوباره دارد گرم می‌شود.
-عزیز کوچولوی من، وحشت کردی...
-امشب وحشت خیلی بیش‌تری چشم به‌راهم است.
دوباره از احساسِ واقعه‌ای جبران ناپذیر یخ زدم. این فکر که دیگر هیچ وقت غش‌غش خنده‌ی او را نخواهم شنید برایم سخت تحمل‌ناپذیر بود. خنده‌ی او برای من به چشمه‌ای در دلِ کویر می‌مانست.
-کوچولوئَکِ من، دلم می‌خواهد باز هم غش‌غشِ خنده‌ات را بشنوم.
اما به‌ام گفت: -امشب درست می‌شود یک سال و اخترَکَم درست بالای همان نقطه‌ای می‌رسد که پارسال به زمین آمدم.
-کوچولوئک، این قضیه‌ی مار و میعاد و ستاره یک خواب آشفته بیش‌تر نیست. مگر نه؟
به سوال من جوابی نداد اما گفت: -چیزی که مهم است با چشمِ سَر دیده نمی‌شود.
-مسلم است.
-در مورد گل هم همین‌طور است: اگر گلی را دوست داشته باشی که تو یک ستاره‌ی دیگر است، شب تماشای آسمان چه لطفی پیدا می‌کند: همه‌ی ستاره‌ها غرق گل می‌شوند!
-مسلم است...
-در مورد آب هم همین‌طور است. آبی که تو به من دادی به خاطر قرقره و ریسمان درست به یک موسیقی می‌مانست... یادت که هست... چه خوب بود.
-مسلم است...
-شب‌به‌شب ستاره‌ها را نگاه می‌کنی. اخترک من کوچولوتر از آن است که بتوانم جایش را نشانت بدهم. اما چه بهتر! آن هم برای تو می‌شود یکی از ستاره‌ها؛ و آن وقت تو دوست داری همه‌ی ستاره‌ها را تماشا کنی... همه‌شان می‌شوند دوست‌های تو... راستی می‌خواهم هدیه‌ای بت بدهم...
و غش غش خندید.
-آخ، کوچولوئک، کوچولوئک! من عاشقِ شنیدنِ این خنده‌ام!
-هدیه‌ی من هم درست همین است... درست مثل مورد آب.
-چی می‌خواهی بگویی؟
-همه‌ی مردم ستاره دارند اما همه‌ی ستاره‌ها یک‌جور نیست: واسه آن‌هایی که به سفر می‌روند حکم راهنما را دارند واسه بعضی دیگر فقط یک مشت روشناییِ سوسوزن‌اند. برای بعضی که اهل دانشند هر ستاره یک معما است واسه آن بابای تاجر طلا بود. اما این ستاره‌ها همه‌شان زبان به کام کشیده و خاموشند. فقط تو یکی ستاره‌هایی خواهی داشت که تنابنده‌ای مِثلش را ندارد.
-چی می‌خواهی بگویی؟
-نه این که من تو یکی از ستاره‌هام؟ نه این که من تو یکی از آن‌ها می‌خندم؟... خب، پس هر شب که به آسمان نگاه می‌کنی برایت مثل این خواهد بود که همه‌ی ستاره‌ها می‌خندند. پس تو ستاره‌هایی خواهی داشت که بلدند بخندند!
و باز خندید.
-و خاطرت که تسلا پیدا کرد (خب بالاخره آدمی‌زاد یک جوری تسلا پیدا می‌کند دیگر) از آشنایی با من خوش‌حال می‌شوی. دوست همیشگی من باقی می‌مانی و دلت می‌خواهد با من بخندی و پاره‌ای وقت‌هام واسه تفریح پنجره‌ی اتاقت را وا می‌کنی... دوستانت از این‌که می‌بینند تو به آسمان نگاه می‌کنی و می‌خندی حسابی تعجب می‌کنند آن وقت تو به‌شان می‌گویی: «آره، ستاره‌ها همیشه مرا خنده می‌اندازند!» و آن‌وقت آن‌ها یقین‌شان می‌شود که تو پاک عقلت را از دست داده‌ای. جان! می‌بینی چه کَلَکی به‌ات زده‌ام...
و باز زد زیر خنده.
-به آن می‌ماند که عوضِ ستاره یک مشت زنگوله بت داده باشم که بلدند بخندند...
دوباره خندید و بعد حالتی جدی به خودش گرفت:
-نه، من تنهات نمی‌گذارم.
شهریار کوچولو تنها
-ظاهر آدمی را پیدا می‌کنم که دارد درد می‌کشد... یک خرده هم مثل آدمی می‌شوم که دارد جان می‌کند. رو هم رفته این جوری‌ها است. نیا که این را نبینی. چه زحمتی است بی‌خود؟
-تنهات نمی‌گذارم.
اندوه‌زده بود.
-این را بیش‌تر از بابت ماره می‌گویم که، نکند یک‌هو تو را هم بگزد. مارها خیلی خبیثند. حتا واسه خنده هم ممکن است آدم را نیش بزنند.
-تنهات نمی‌گذارم.
منتها یک چیز باعث خاطر جمعیش شد:
-گر چه، بار دوم که بخواهند بگزند دیگر زهر ندارند.
شب متوجه راه افتادنش نشدم. بی سر و صدا گریخت.
وقتی خودم را به‌اش رساندم با قیافه‌ی مصمم و قدم‌های محکم پیش می‌رفت. همین قدر گفت: -اِ! این‌جایی؟
و دستم را گرفت.
اما باز بی‌قرار شد وگفت: -اشتباه کردی آمدی. رنج می‌بری. گرچه حقیقت این نیست، اما ظاهرِ یک مرده را پیدا می‌کنم.
من ساکت ماندم.
-خودت درک می‌کنی. راه خیلی دور است. نمی‌توانم این جسم را با خودم ببرم. خیلی سنگین است.
من ساکت ماندم.
-گیرم عینِ پوستِ کهنه‌ای می‌شود که دورش انداخته باشند؛ پوست کهنه که غصه ندارد، ها؟
من ساکت ماندم.
کمی دل‌سرد شد اما باز هم سعی کرد:
-خیلی با مزه می‌شود، نه؟ من هم به ستاره‌ها نگاه می‌کنم. هم‌شان به صورت چاه‌هایی در می‌آیند با قرقره‌های زنگ زده. همه‌ی ستاره‌ها بم آب می‌دهند بخورم...
من ساکت ماندم.
-خیلی با مزه می‌شود. نه؟ تو صاحب هزار کرور زنگوله می‌شوی من صاحب هزار کرور فواره...
او هم ساکت شد، چرا که داشت گریه می‌کرد...
-خب، همین جاست. بگذار چند قدم خودم تنهایی بروم.
و گرفت نشست، چرا که می‌ترسید.
شهریار کوچولو نشسته
می‌دانی؟... گلم را می‌گویم... آخر من مسئولشم. تازه چه قدر هم لطیف است و چه قدر هم ساده و بی‌شیله‌پیله. برای آن که جلو همه‌ی عالم از خودش دفاع کند همه‌اش چی دارد مگر؟ چهارتا خار پِرپِرَک!
من هم گرفتم نشستم. دیگر نمی‌توانستم سر پا بند بشوم.
گفت: -همین... همه‌اش همین و بس...
باز هم کمی دودلی نشان داد اما بالاخره پا شد و قدمی به جلو رفت. من قادر به حرکت نبودم.
کنار قوزکِ پایش جرقه‌ی زردی جست و... فقط همین! یک دم بی‌حرکت ماند. فریادی نزد. مثل درختی که بیفتد آرام‌آرام به زمین افتاد که به وجود شن از آن هم صدایی بلند نشد.
شهریار کوچولو در حالی که آرام‌آرام به زمین می‌افتد
۲۷
شش سال گذشته است و من هنوز بابت این قضیه جایی لب‌ترنکرده‌ام. دوستانم از این که مرا دوباره زنده می‌دیدند سخت شاد شدند. من غم‌زده بودم اما به آن‌ها می‌گفتم اثر خستگی است.
حالا کمی تسلای خاطر پیدا کرده‌ام. یعنی نه کاملا... اما این را خوب می‌دانم که او به اخترکش برگشته. چون آفتاب که زد پیکرش را پیدا نکردم. پیکری هم نبود که چندان وزنی داشته باشد... و شب‌ها دوست دارم به ستاره‌ها گوش بدهم. عین هزار زنگوله‌اند.
اما موضوع خیلی مهمی که هست، من پاک یادم رفت به پوزه‌بندی که برای شهریار کوچولو کشیدم تسمه‌ی چرمی اضافه کنم و او ممکن نیست بتواند آن را به پوزه‌ی بَرّه ببندد. این است که از خودم می‌پرسم: «یعنی تو اخترکش چه اتفاقی افتاده؟ نکند بره‌هه گل را چریده باشد؟...»
گاه به خودم می‌گویم: «حتما نه، شهریار کوچولو هر شب گلش را زیر حباب شیشه‌ای می‌گذارد و هوای بره‌اش را هم دارد...» آن وقت است که خیالم راحت می‌شود و ستاره‌ها همه به شیرینی می‌خندند.
گاه به خودم می‌گویم: «همین کافی است که آدم یک بار حواسش نباشد... آمدیم و یک شب حباب یادش رفت یا بَرّه شب نصف‌شبی بی‌سروصدا از جعبه زد بیرون...» آن وقت است که زنگوله‌ها همه تبدیل به اشک می‌شوند!...
یک راز خیلی خیلی بزرگ این جا هست: برای شما هم که او را دوست دارید، مثل من هیچ چیزِ عالم مهم‌تر از دانستن این نیست که تو فلان نقطه‌ای که نمی‌دانیم، فلان بره‌ای که نمی‌شماسیم گل سرخی را چریده یا نچریده...
خب. آسمان را نگاه کنید و بپرسید: «بَرّه گل را چریده یا نچریده؟» و آن وقت با چشم‌های خودتان تفاوتش را ببینید...
و محال است آدم بزرگ‌ها روح‌شان خبردار بشود که این موضوع چه قدر مهم است!
بدونِ شهریار کوچولو
در نظر من این زیباترین و حزن‌انگیزترین منظره‌ی عالم است. این همان منظره‌ی دو صفحه پیش است گیرم آن را دوباره کشیده‌ام که به‌تر نشان‌تان بدهم: «ظهور شهریار کوچولو بر زمین در این جا بود؛ و بعد در همین جا هم بود که ناپدید شد».
آن قدر به دقت این منظره را نگاه کنید که مطمئن بشوید اگر روزی تو آفریقا گذرتان به کویر صحرا افتاد حتما آن را خواهید شناخت. و اگر پاداد و گذارتان به آن جا افتاد به التماس ازتان می‌خواهم که عجله به خرج ندهید و درست زیر ستاره چند لحظه‌ای توقف کنید. آن وقت اگر بچه‌ای به طرف‌تان آمد، اگر خندید، اگر موهایش طلایی بود، اگر وقتی ازش سوالی کردید جوابی نداد، لابد حدس می‌زنید که کیست. در آن صورت لطف کنید و نگذارید من این جور افسرده خاطر بمانم:
بی درنگ بردارید به من بنویسید که او برگشته.

View File

@ -12,9 +12,17 @@ DISTCHECK_CONFIGURE_FLAGS = --enable-introspection
TESTS =
check_PROGRAMS =
EXTRA_DIST += harfbuzz.cc
EXTRA_DIST += meson.build
EXTRA_DIST += fix_get_types.py
# Convenience targets:
lib: $(BUILT_SOURCES) libharfbuzz.la
libs: $(BUILT_SOURCES) $(lib_LTLIBRARIES)
tiny:
$(MAKE) $(AM_MAKEFLAGS) CPPFLAGS="-Os -DHB_TINY $(CPPFLAGS)" libs
tinyz:
$(MAKE) $(AM_MAKEFLAGS) CPPFLAGS="-Oz -DHB_TINY $(CPPFLAGS)" libs
lib_LTLIBRARIES = libharfbuzz.la
@ -28,10 +36,6 @@ HBSOURCES = $(HB_BASE_sources)
HBSOURCES += $(HB_BASE_RAGEL_GENERATED_sources)
HBHEADERS = $(HB_BASE_headers)
if HAVE_FALLBACK
HBSOURCES += $(HB_FALLBACK_sources)
endif
if HAVE_PTHREAD
HBCFLAGS += $(PTHREAD_CFLAGS)
HBNONPCLIBS += $(PTHREAD_LIBS)
@ -48,12 +52,7 @@ endif
if HAVE_FREETYPE
HBCFLAGS += $(FREETYPE_CFLAGS)
HBLIBS += $(FREETYPE_LIBS)
# XXX
# The following creates a recursive dependency on FreeType if FreeType is
# built with HarfBuzz support enabled. Newer pkg-config handles that just
# fine but pkg-config 0.26 as shipped in Ubuntu 14.04 crashes. Remove
# in a year or two, or otherwise work around it...
#HBDEPS += $(FREETYPE_DEPS)
HBDEPS += $(FREETYPE_DEPS)
HBSOURCES += $(HB_FT_sources)
HBHEADERS += $(HB_FT_headers)
endif
@ -80,6 +79,13 @@ HBSOURCES += $(HB_DIRECTWRITE_sources)
HBHEADERS += $(HB_DIRECTWRITE_headers)
endif
if HAVE_GDI
HBCFLAGS += $(GDI_CXXFLAGS)
HBNONPCLIBS += $(GDI_LIBS)
HBSOURCES += $(HB_GDI_sources)
HBHEADERS += $(HB_GDI_headers)
endif
if HAVE_CORETEXT
HBCFLAGS += $(CORETEXT_CFLAGS)
HBNONPCLIBS += $(CORETEXT_LIBS)
@ -87,17 +93,6 @@ HBSOURCES += $(HB_CORETEXT_sources)
HBHEADERS += $(HB_CORETEXT_headers)
endif
if HAVE_UCDN
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
BUILT_SOURCES += \
hb-version.h
@ -155,6 +150,7 @@ cmake_DATA = harfbuzz-config.cmake
EXTRA_DIST += hb-version.h.in harfbuzz.pc.in harfbuzz-config.cmake.in
lib_LTLIBRARIES += libharfbuzz-subset.la
libharfbuzz_subset_la_LINK = $(chosen_linker) $(libharfbuzz_subset_la_LDFLAGS)
libharfbuzz_subset_la_SOURCES = $(HB_SUBSET_sources)
libharfbuzz_subset_la_CPPFLAGS = $(HBCFLAGS) $(CODE_COVERAGE_CFLAGS)
libharfbuzz_subset_la_LDFLAGS = $(base_link_flags) $(export_symbols_subset) $(CODE_COVERAGE_LDFLAGS)
@ -258,36 +254,44 @@ GENERATORS = \
gen-indic-table.py \
gen-os2-unicode-ranges.py \
gen-tag-table.py \
gen-ucd-table.py \
gen-use-table.py \
gen-vowel-constraints.py \
$(NULL)
EXTRA_DIST += $(GENERATORS)
unicode-tables: arabic-table indic-table tag-table use-table emoji-table
unicode-tables: \
arabic-table \
emoji-table \
indic-table \
tag-table \
ucd-table \
use-table \
emoji-table \
$(NULL)
arabic-table: gen-arabic-table.py ArabicShaping.txt UnicodeData.txt Blocks.txt
$(AM_V_GEN) $(builddir)/$^ > $(srcdir)/hb-ot-shape-complex-arabic-table.hh \
|| ($(RM) $(srcdir)/hb-ot-shape-complex-arabic-table.hh; false)
indic-table: gen-indic-table.py IndicSyllabicCategory.txt IndicPositionalCategory.txt Blocks.txt
$(AM_V_GEN) $(builddir)/$^ > $(srcdir)/hb-ot-shape-complex-indic-table.cc \
|| ($(RM) $(srcdir)/hb-ot-shape-complex-indic-table.cc; false)
tag-table: gen-tag-table.py languagetags language-subtag-registry
$(AM_V_GEN) $(builddir)/$^ > $(srcdir)/hb-ot-tag-table.hh \
|| ($(RM) $(srcdir)/hb-ot-tag-table.hh; false)
use-table: gen-use-table.py IndicSyllabicCategory.txt IndicPositionalCategory.txt UnicodeData.txt Blocks.txt
$(AM_V_GEN) $(builddir)/$^ > $(srcdir)/hb-ot-shape-complex-use-table.cc \
|| ($(RM) $(srcdir)/hb-ot-shape-complex-use-table.cc; false)
vowel-constraints: gen-vowel-constraints.py HBIndicVowelConstraints.txt Scripts.txt
$(AM_V_GEN) $(builddir)/$^ > $(srcdir)/hb-ot-shape-complex-vowel-constraints.cc \
|| ($(RM) $(srcdir)/hb-ot-shape-complex-vowel-constraints.cc; false)
emoji-table: gen-emoji-table.py emoji-data.txt
$(AM_V_GEN) $(builddir)/$^ > $(srcdir)/hb-unicode-emoji-table.hh \
|| ($(RM) $(srcdir)/hb-unicode-emoji-table.hh; false)
indic-table: gen-indic-table.py IndicSyllabicCategory.txt IndicPositionalCategory.txt Blocks.txt
$(AM_V_GEN) $(builddir)/$^ > $(srcdir)/hb-ot-shape-complex-indic-table.cc \
|| ($(RM) $(srcdir)/hb-ot-shape-complex-indic-table.cc; false)
tag-table: gen-tag-table.py languagetags language-subtag-registry
$(AM_V_GEN) $(builddir)/$^ > $(srcdir)/hb-ot-tag-table.hh \
|| ($(RM) $(srcdir)/hb-ot-tag-table.hh; false)
ucd-table: gen-ucd-table.py ucd.nounihan.grouped.zip hb-common.h
$(AM_V_GEN) $(builddir)/$^ > $(srcdir)/hb-ucd-table.hh \
|| ($(RM) $(srcdir)/hb-ucd-table.hh; false)
use-table: gen-use-table.py IndicSyllabicCategory.txt IndicPositionalCategory.txt UnicodeData.txt Blocks.txt
$(AM_V_GEN) $(builddir)/$^ > $(srcdir)/hb-ot-shape-complex-use-table.cc \
|| ($(RM) $(srcdir)/hb-ot-shape-complex-use-table.cc; false)
vowel-constraints: gen-vowel-constraints.py ms-use/IndicShapingInvalidCluster.txt Scripts.txt
$(AM_V_GEN) $(builddir)/$^ > $(srcdir)/hb-ot-shape-complex-vowel-constraints.cc \
|| ($(RM) $(srcdir)/hb-ot-shape-complex-vowel-constraints.cc; false)
built-sources: $(BUILT_SOURCES)
@ -306,13 +310,31 @@ $(srcdir)/%.hh: $(srcdir)/%.rl
$(AM_V_GEN)(cd $(srcdir) && $(RAGEL) -e -F1 -o "$*.hh" "$*.rl") \
|| ($(RM) "$@"; false)
harfbuzz.cc: Makefile.sources
$(AM_V_GEN) \
for f in \
$(HB_BASE_sources) \
$(HB_GLIB_sources) \
$(HB_FT_sources) \
$(HB_GRAPHITE2_sources) \
$(HB_UNISCRIBE_sources) \
$(HB_GDI_sources) \
$(HB_DIRECTWRITE_sources) \
$(HB_CORETEXT_sources) \
; do echo '#include "'$$f'"'; done | \
grep '[.]cc"' > $(srcdir)/harfbuzz.cc \
|| ($(RM) $(srcdir)/harfbuzz.cc; false)
BUILT_SOURCES += harfbuzz.cc
noinst_PROGRAMS = \
main \
test \
test-buffer-serialize \
test-name-table \
test-size-params \
test-would-substitute \
test-ot-meta \
test-ot-name \
test-ot-glyphname \
test-gpos-size-params \
test-gsub-would-substitute \
$(NULL)
bin_PROGRAMS =
@ -328,42 +350,25 @@ test_buffer_serialize_SOURCES = test-buffer-serialize.cc
test_buffer_serialize_CPPFLAGS = $(HBCFLAGS)
test_buffer_serialize_LDADD = libharfbuzz.la $(HBLIBS)
test_name_table_SOURCES = test-name-table.cc
test_name_table_CPPFLAGS = $(HBCFLAGS)
test_name_table_LDADD = libharfbuzz.la $(HBLIBS)
test_ot_meta_SOURCES = test-ot-meta.cc
test_ot_meta_CPPFLAGS = $(HBCFLAGS)
test_ot_meta_LDADD = libharfbuzz.la $(HBLIBS)
test_size_params_SOURCES = test-size-params.cc
test_size_params_CPPFLAGS = $(HBCFLAGS)
test_size_params_LDADD = libharfbuzz.la $(HBLIBS)
test_ot_name_SOURCES = test-ot-name.cc
test_ot_name_CPPFLAGS = $(HBCFLAGS)
test_ot_name_LDADD = libharfbuzz.la $(HBLIBS)
test_would_substitute_SOURCES = test-would-substitute.cc
test_would_substitute_CPPFLAGS = $(HBCFLAGS) $(FREETYPE_CFLAGS)
test_would_substitute_LDADD = libharfbuzz.la $(HBLIBS) $(FREETYPE_LIBS)
test_ot_glyphname_SOURCES = test-ot-glyphname.cc
test_ot_glyphname_CPPFLAGS = $(HBCFLAGS)
test_ot_glyphname_LDADD = libharfbuzz.la $(HBLIBS)
if HAVE_FREETYPE
if HAVE_CAIRO_FT
noinst_PROGRAMS += test-ot-color
test_ot_color_SOURCES = test-ot-color.cc
test_ot_color_CPPFLAGS = $(HBCFLAGS) $(FREETYPE_CFLAGS) $(CAIRO_FT_CFLAGS)
test_ot_color_LDADD = libharfbuzz.la $(HBLIBS) $(FREETYPE_LIBS) $(CAIRO_LIBS) $(CAIRO_FT_LIBS)
endif # HAVE_CAIRO_FT
endif # HAVE_FREETYPE
test_gpos_size_params_SOURCES = test-gpos-size-params.cc
test_gpos_size_params_CPPFLAGS = $(HBCFLAGS)
test_gpos_size_params_LDADD = libharfbuzz.la $(HBLIBS)
dist_check_SCRIPTS = \
check-c-linkage-decls.sh \
check-externs.sh \
check-header-guards.sh \
check-includes.sh \
check-static-inits.sh \
check-symbols.sh \
$(NULL)
TESTS += $(dist_check_SCRIPTS)
if !WITH_LIBSTDCXX
dist_check_SCRIPTS += \
check-libstdc++.sh \
$(NULL)
endif
test_gsub_would_substitute_SOURCES = test-gsub-would-substitute.cc
test_gsub_would_substitute_CPPFLAGS = $(HBCFLAGS) $(FREETYPE_CFLAGS)
test_gsub_would_substitute_LDADD = libharfbuzz.la $(HBLIBS) $(FREETYPE_LIBS)
check_PROGRAMS += \
dump-indic-data \
@ -384,7 +389,7 @@ 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)
COMPILED_TESTS = test-algs test-iter test-ot-tag test-unicode-ranges
COMPILED_TESTS = test-algs test-array test-iter test-meta test-number test-ot-tag test-unicode-ranges test-bimap
COMPILED_TESTS_CPPFLAGS = $(HBCFLAGS) -DMAIN -UNDEBUG
COMPILED_TESTS_LDADD = libharfbuzz.la $(HBLIBS)
check_PROGRAMS += $(COMPILED_TESTS)
@ -394,10 +399,22 @@ test_algs_SOURCES = test-algs.cc hb-static.cc
test_algs_CPPFLAGS = $(COMPILED_TESTS_CPPFLAGS)
test_algs_LDADD = $(COMPILED_TESTS_LDADD)
test_array_SOURCES = test-array.cc
test_array_CPPFLAGS = $(HBCFLAGS)
test_array_LDADD = libharfbuzz.la $(HBLIBS)
test_iter_SOURCES = test-iter.cc hb-static.cc
test_iter_CPPFLAGS = $(COMPILED_TESTS_CPPFLAGS)
test_iter_LDADD = $(COMPILED_TESTS_LDADD)
test_meta_SOURCES = test-meta.cc hb-static.cc
test_meta_CPPFLAGS = $(COMPILED_TESTS_CPPFLAGS)
test_meta_LDADD = $(COMPILED_TESTS_LDADD)
test_number_SOURCES = test-number.cc hb-number.cc
test_number_CPPFLAGS = $(COMPILED_TESTS_CPPFLAGS)
test_number_LDADD = $(COMPILED_TESTS_LDADD)
test_ot_tag_SOURCES = hb-ot-tag.cc
test_ot_tag_CPPFLAGS = $(COMPILED_TESTS_CPPFLAGS)
test_ot_tag_LDADD = $(COMPILED_TESTS_LDADD)
@ -406,8 +423,29 @@ test_unicode_ranges_SOURCES = test-unicode-ranges.cc
test_unicode_ranges_CPPFLAGS = $(COMPILED_TESTS_CPPFLAGS)
test_unicode_ranges_LDADD = $(COMPILED_TESTS_LDADD)
test_bimap_SOURCES = test-bimap.cc hb-static.cc
test_bimap_CPPFLAGS = $(COMPILED_TESTS_CPPFLAGS)
test_bimap_LDADD = $(COMPILED_TESTS_LDADD)
dist_check_SCRIPTS = \
check-c-linkage-decls.sh \
check-externs.sh \
check-header-guards.sh \
check-includes.sh \
check-static-inits.sh \
check-symbols.sh \
$(NULL)
TESTS += $(dist_check_SCRIPTS)
if !WITH_LIBSTDCXX
dist_check_SCRIPTS += \
check-libstdc++.sh \
$(NULL)
endif
TESTS_ENVIRONMENT = \
srcdir="$(srcdir)" \
builddir="$(builddir)" \
MAKE="$(MAKE) $(AM_MAKEFLAGS)" \
HBSOURCES="$(HBSOURCES)" \
HBHEADERS="$(HBHEADERS)" \
@ -417,7 +455,16 @@ if HAVE_INTROSPECTION
-include $(INTROSPECTION_MAKEFILE)
INTROSPECTION_GIRS = HarfBuzz-0.0.gir # What does the 0 mean anyway?!
INTROSPECTION_SCANNER_ARGS = -I$(srcdir) -n hb --identifier-prefix=hb_ --warn-all
INTROSPECTION_SCANNER_ARGS = \
-I$(srcdir) \
--warn-all --verbose \
--namespace=HarfBuzz \
--nsversion=0.0 \
--symbol-prefix=hb \
--symbol-prefix=hb_gobject \
--identifier-prefix=hb_ \
--pkg-export=harfbuzz-gobject \
--c-include=hb-gobject.h
INTROSPECTION_COMPILER_ARGS = --includedir=$(srcdir)
INTROSPECTION_SCANNER_ENV = CC="$(CC)"
@ -434,6 +481,7 @@ HarfBuzz_0_0_gir_CFLAGS = \
-DHB_AAT_H_IN \
-DHB_GOBJECT_H \
-DHB_GOBJECT_H_IN \
-DHAVE_GOBJECT \
-DHB_EXTERN= \
$(NULL)
HarfBuzz_0_0_gir_LIBS = \

View File

@ -10,6 +10,7 @@ HB_BASE_sources = \
hb-aat-layout-kerx-table.hh \
hb-aat-layout-lcar-table.hh \
hb-aat-layout-morx-table.hh \
hb-aat-layout-opbd-table.hh \
hb-aat-layout-trak-table.hh \
hb-aat-layout.cc \
hb-aat-layout.hh \
@ -31,10 +32,14 @@ HB_BASE_sources = \
hb-cff1-interp-cs.hh \
hb-cff2-interp-cs.hh \
hb-common.cc \
hb-config.hh \
hb-debug.hh \
hb-dispatch.hh \
hb-draw.cc \
hb-draw.hh \
hb-face.cc \
hb-face.hh \
hb-fallback-shape.cc \
hb-font.cc \
hb-font.hh \
hb-iter.hh \
@ -42,15 +47,19 @@ HB_BASE_sources = \
hb-machinery.hh \
hb-map.cc \
hb-map.hh \
hb-bimap.hh \
hb-meta.hh \
hb-mutex.hh \
hb-null.hh \
hb-number.cc \
hb-number.hh \
hb-object.hh \
hb-open-file.hh \
hb-open-type.hh \
hb-ot-cff-common.hh \
hb-ot-cff1-table.cc \
hb-ot-cff1-table.hh \
hb-ot-cff1-std-str.hh \
hb-ot-cff2-table.cc \
hb-ot-cff2-table.hh \
hb-ot-cmap-table.hh \
@ -62,6 +71,7 @@ HB_BASE_sources = \
hb-ot-color.cc \
hb-ot-face.cc \
hb-ot-face.hh \
hb-ot-face-table-list.hh \
hb-ot-font.cc \
hb-ot-gasp-table.hh \
hb-ot-glyf-table.hh \
@ -84,7 +94,11 @@ HB_BASE_sources = \
hb-ot-math-table.hh \
hb-ot-math.cc \
hb-ot-maxp-table.hh \
hb-ot-name-language.cc \
hb-ot-meta-table.hh \
hb-ot-meta.cc \
hb-ot-metrics.cc \
hb-ot-metrics.hh \
hb-ot-name-language-static.hh \
hb-ot-name-language.hh \
hb-ot-name-table.hh \
hb-ot-name.cc \
@ -125,6 +139,7 @@ HB_BASE_sources = \
hb-ot-tag.cc \
hb-ot-var-avar-table.hh \
hb-ot-var-fvar-table.hh \
hb-ot-var-gvar-table.hh \
hb-ot-var-hvar-table.hh \
hb-ot-var-mvar-table.hh \
hb-ot-var.cc \
@ -144,18 +159,20 @@ HB_BASE_sources = \
hb-shaper.hh \
hb-static.cc \
hb-string-array.hh \
hb-ucd-table.hh \
hb-ucd.cc \
hb-unicode-emoji-table.hh \
hb-unicode.cc \
hb-unicode.hh \
hb-utf.hh \
hb-vector.hh \
hb-warning.cc \
hb.hh \
$(NULL)
HB_BASE_RAGEL_GENERATED_sources = \
hb-buffer-deserialize-json.hh \
hb-buffer-deserialize-text.hh \
hb-number-parser.hh \
hb-ot-shape-complex-indic-machine.hh \
hb-ot-shape-complex-khmer-machine.hh \
hb-ot-shape-complex-myanmar-machine.hh \
@ -164,6 +181,7 @@ HB_BASE_RAGEL_GENERATED_sources = \
HB_BASE_RAGEL_sources = \
hb-buffer-deserialize-json.rl \
hb-buffer-deserialize-text.rl \
hb-number-parser.rl \
hb-ot-shape-complex-indic-machine.rl \
hb-ot-shape-complex-khmer-machine.rl \
hb-ot-shape-complex-myanmar-machine.rl \
@ -177,6 +195,7 @@ HB_BASE_headers = \
hb-buffer.h \
hb-common.h \
hb-deprecated.h \
hb-draw.h \
hb-face.h \
hb-font.h \
hb-map.h \
@ -185,6 +204,8 @@ HB_BASE_headers = \
hb-ot-font.h \
hb-ot-layout.h \
hb-ot-math.h \
hb-ot-meta.h \
hb-ot-metrics.h \
hb-ot-name.h \
hb-ot-shape.h \
hb-ot-var.h \
@ -197,10 +218,6 @@ HB_BASE_headers = \
hb.h \
$(NULL)
HB_FALLBACK_sources = \
hb-fallback-shape.cc \
$(NULL)
# Optional Sources and Headers with external deps
HB_FT_sources = hb-ft.cc
@ -220,18 +237,20 @@ HB_CORETEXT_headers = hb-coretext.h
HB_DIRECTWRITE_sources = hb-directwrite.cc
HB_DIRECTWRITE_headers = hb-directwrite.h
HB_GDI_sources = hb-gdi.cc
HB_GDI_headers = hb-gdi.h
HB_UNISCRIBE_sources = hb-uniscribe.cc
HB_UNISCRIBE_headers = hb-uniscribe.h
# Additional supplemental sources
HB_UCDN_sources = hb-ucdn.cc
# Sources for libharfbuzz-gobject and libharfbuzz-icu
HB_ICU_sources = hb-icu.cc
HB_ICU_headers = hb-icu.h
# Sources for libharfbuzz-subset
HB_SUBSET_sources = \
hb-number.cc \
hb-number.hh \
hb-ot-cff1-table.cc \
hb-ot-cff2-table.cc \
hb-static.cc \
@ -241,9 +260,6 @@ HB_SUBSET_sources = \
hb-subset-cff1.hh \
hb-subset-cff2.cc \
hb-subset-cff2.hh \
hb-subset-glyf.cc \
hb-subset-glyf.hh \
hb-subset-glyf.hh \
hb-subset-input.cc \
hb-subset-input.hh \
hb-subset-plan.cc \

View File

@ -22,8 +22,7 @@ fi
tested=false
# harfbuzz-icu links to libstdc++ because icu does.
# harfbuzz-subset uses libstdc++.
for soname in harfbuzz harfbuzz-gobject; do
for soname in harfbuzz harfbuzz-subset harfbuzz-gobject; do
for suffix in so dylib; do
so=$libs/lib$soname.$suffix
if ! test -f "$so"; then continue; fi

View File

@ -4,7 +4,7 @@ LC_ALL=C
export LC_ALL
test -z "$srcdir" && srcdir=.
test -z "$libs" && libs=.libs
test -z "$builddir" && builddir=.
stat=0
if which objdump 2>/dev/null >/dev/null; then
@ -14,18 +14,21 @@ else
exit 77
fi
OBJS=$libs/*.o
OBJS=$(find $builddir/ -name '*.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
fi
tested=false
echo "Checking that no object file has static initializers"
for obj in $OBJS; do
if objdump -t "$obj" | grep '[.][cd]tors' | grep -v '\<00*\>'; then
echo "Ouch, $obj has static initializers/finalizers"
stat=1
fi
tested=true
done
echo "Checking that no object file has lazy static C++ constructors/destructors or other such stuff"
@ -35,6 +38,12 @@ for obj in $OBJS; do
echo "Ouch, $obj has lazy static C++ constructors/destructors or other such stuff"
stat=1
fi
tested=true
done
if ! $tested; then
echo "check-static-inits.sh: no objects found; skipping test"
exit 77
fi
exit $stat

View File

@ -4,10 +4,11 @@ LC_ALL=C
export LC_ALL
test -z "$srcdir" && srcdir=.
test -z "$builddir" && builddir=.
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_.*'
IGNORED_SYMBOLS='_fini\|_init\|_fdata\|_ftext\|_fbss\|__bss_start\|__bss_start__\|__bss_end__\|_edata\|_end\|_bss_end__\|__end__\|__gcov_.*\|llvm_.*'
if which nm 2>/dev/null >/dev/null; then
:
@ -26,7 +27,7 @@ for soname in harfbuzz harfbuzz-subset harfbuzz-icu harfbuzz-gobject; do
symprefix=
if test $suffix = dylib; then symprefix=_; fi
EXPORTED_SYMBOLS=`nm "$so" | grep ' [BCDGINRST] .' | grep -v " $symprefix\\($IGNORED_SYMBOLS\\>\\)" | cut -d' ' -f3 | c++filt`
EXPORTED_SYMBOLS=`nm "$so" | grep ' [BCDGIRST] .' | grep -v " $symprefix\\($IGNORED_SYMBOLS\\>\\)" | cut -d' ' -f3 | c++filt`
prefix=$symprefix`basename "$so" | sed 's/libharfbuzz/hb/; s/-/_/g; s/[.].*//'`
@ -36,7 +37,7 @@ for soname in harfbuzz harfbuzz-subset harfbuzz-icu harfbuzz-gobject; do
stat=1
fi
def=$soname.def
def=$builddir/$soname.def
if ! test -f "$def"; then
echo "'$def' not found; skipping"
else
@ -47,9 +48,9 @@ for soname in harfbuzz harfbuzz-subset harfbuzz-icu harfbuzz-gobject; do
# cheat: copy the last line from the def file!
tail -n1 "$def"
} | c++filt | diff "$def" - >&2 || stat=1
fi
tested=true
tested=true
fi
done
done
if ! $tested; then

View File

@ -1,99 +0,0 @@
#!/bin/bash
# Suggested setup to use the script:
# (on the root of the project)
# $ NOCONFIGURE=1 ./autogen.sh && mkdir build && cd build
# $ ../configure --with-freetype --with-glib --with-gobject --with-cairo
# $ 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 want to open the result rendering using a GUI app,
# $ src/dev-run.sh open [FONT-FILE] [TEXT]
#
# And if you are using iTerm2, you can use 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 = "open" ] && openimg=1 && shift
OPEN=xdg-open
[ "$(uname)" == "Darwin" ] && OPEN=open
[ $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=tmp.png
[ -f 'build/build.ninja' ] && CMAKENINJA=TRUE
# or "fswatch -0 . -e build/ -e .git"
find src/ | entr printf '\0' | while read -d ""; do
clear
yes = | head -n`tput cols` | tr -d '\n'
if [[ $CMAKENINJA ]]; then
ninja -Cbuild hb-shape hb-view && {
build/hb-shape $@
if [ $openimg ]; then
build/hb-view $@ -O png -o $tmp
$OPEN $tmp
elif [ $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 [ $openimg ]; then
build/util/hb-view $@ -O png -o $tmp
$OPEN $tmp
elif [ $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

15
src/fix_get_types.py Normal file
View File

@ -0,0 +1,15 @@
import re
import argparse
if __name__=='__main__':
parser = argparse.ArgumentParser()
parser.add_argument('input')
parser.add_argument('output')
args = parser.parse_args()
with open(args.input, 'r') as inp:
with open(args.output, 'w') as out:
for l in inp.readlines():
l = re.sub('_t_get_type', '_get_type', l)
l = re.sub('_T \(', ' (', l)
out.write(l)

View File

@ -1,11 +1,15 @@
#!/usr/bin/env python
from __future__ import print_function, division, absolute_import
#!/usr/bin/env python3
import io, os.path, sys
if len (sys.argv) != 4:
print ("usage: ./gen-arabic-table.py ArabicShaping.txt UnicodeData.txt Blocks.txt", file=sys.stderr)
print ("""usage: ./gen-arabic-table.py ArabicShaping.txt UnicodeData.txt Blocks.txt
Input files, as of Unicode 12:
* https://unicode.org/Public/UCD/latest/ucd/ArabicShaping.txt
* https://unicode.org/Public/UCD/latest/ucd/UnicodeData.txt
* https://unicode.org/Public/UCD/latest/ucd/Blocks.txt
""", file=sys.stderr)
sys.exit (1)
files = [io.open (x, encoding='utf-8') for x in sys.argv[1:]]

View File

@ -1,6 +1,4 @@
#!/usr/bin/env python
from __future__ import print_function, division, absolute_import
#!/usr/bin/env python3
import io, os, re, sys
@ -15,10 +13,35 @@ for h in header_paths:
if h.endswith (".h"):
with io.open (h, encoding='utf-8') as f: headers_content.append (f.read ())
symbols = "\n".join (sorted (re.findall (r"^hb_\w+(?= \()", "\n".join (headers_content), re.M)))
symbols = sorted (re.findall (r"^hb_\w+(?= \()", "\n".join (headers_content), re.M))
if '--experimental-api' not in sys.argv:
# Move these to harfbuzz-sections.txt when got stable
experimental_symbols = \
"""hb_font_draw_glyph
hb_draw_funcs_t
hb_draw_close_path_func_t
hb_draw_cubic_to_func_t
hb_draw_line_to_func_t
hb_draw_move_to_func_t
hb_draw_quadratic_to_func_t
hb_draw_funcs_create
hb_draw_funcs_destroy
hb_draw_funcs_is_immutable
hb_draw_funcs_make_immutable
hb_draw_funcs_reference
hb_draw_funcs_set_close_path_func
hb_draw_funcs_set_cubic_to_func
hb_draw_funcs_set_line_to_func
hb_draw_funcs_set_move_to_func
hb_draw_funcs_set_quadratic_to_func
hb_font_get_var_coords_design
hb_ot_layout_closure_lookups
hb_ot_layout_closure_features""".splitlines ()
symbols = [x for x in symbols if x not in experimental_symbols]
symbols = "\n".join (symbols)
result = symbols if os.environ.get('PLAIN_LIST', '') else """EXPORTS
%s
LIBRARY lib%s-0.dll""" % (symbols, output_file.replace ('.def', ''))
LIBRARY lib%s-0.dll""" % (symbols, output_file.replace ('src/', '').replace ('.def', ''))
with open (output_file, "w") as f: f.write (result)

View File

@ -1,12 +1,15 @@
#!/usr/bin/python
#!/usr/bin/env python3
from __future__ import print_function, division, absolute_import
import sys
import os.path
from collections import OrderedDict
import packTab
if len (sys.argv) != 2:
print("usage: ./gen-emoji-table.py emoji-data.txt", file=sys.stderr)
print("""usage: ./gen-emoji-table.py emoji-data.txt
Input file, as of Unicode 12:
* https://www.unicode.org/Public/emoji/12.0/emoji-data.txt""", file=sys.stderr)
sys.exit (1)
f = open(sys.argv[1])
@ -52,14 +55,19 @@ print ()
print ('#include "hb-unicode.hh"')
print ()
for typ,s in ranges.items():
for typ, s in ranges.items():
if typ != "Extended_Pictographic": continue
arr = dict()
for start,end in s:
for i in range(start,end):
arr[i] = 1
sol = packTab.pack_table(arr, 0, compression=3)
code = packTab.Code('_hb_emoji')
sol.genCode(code, 'is_'+typ)
code.print_c(linkage='static inline')
print()
print("static const struct hb_unicode_range_t _hb_unicode_emoji_%s_table[] =" % typ)
print("{")
for pair in sorted(s):
print(" {0x%04X, 0x%04X}," % pair)
print("};")
print ()
print ("#endif /* HB_UNICODE_EMOJI_TABLE_HH */")

View File

@ -1,11 +1,14 @@
#!/usr/bin/env python
from __future__ import print_function, division, absolute_import
#!/usr/bin/env python3
import io, sys
if len (sys.argv) != 4:
print ("usage: ./gen-indic-table.py IndicSyllabicCategory.txt IndicPositionalCategory.txt Blocks.txt", file=sys.stderr)
print ("""usage: ./gen-indic-table.py IndicSyllabicCategory.txt IndicPositionalCategory.txt Blocks.txt
Input files, as of Unicode 12:
* https://unicode.org/Public/UCD/latest/ucd/IndicSyllabicCategory.txt
* https://unicode.org/Public/UCD/latest/ucd/IndicPositionalCategory.txt
* https://unicode.org/Public/UCD/latest/ucd/Blocks.txt""", file=sys.stderr)
sys.exit (1)
ALLOWED_SINGLES = [0x00A0, 0x25CC]
@ -98,6 +101,10 @@ for h in headers:
print (" * %s" % (l.strip()))
print (" */")
print ()
print ('#include "hb.hh"')
print ()
print ('#ifndef HB_NO_OT_SHAPE')
print ()
print ('#include "hb-ot-shape-complex-indic.hh"')
print ()
@ -129,8 +136,8 @@ what = ["INDIC_SYLLABIC_CATEGORY", "INDIC_MATRA_CATEGORY"]
what_short = ["ISC", "IMC"]
print ('#pragma GCC diagnostic push')
print ('#pragma GCC diagnostic ignored "-Wunused-macros"')
cat_defs = []
for i in range (2):
print ()
vv = sorted (values[i].keys ())
for v in vv:
v_no_and = v.replace ('_And_', '_')
@ -142,10 +149,17 @@ for i in range (2):
raise Exception ("Duplicate short value alias", v, all_shorts[i][s])
all_shorts[i][s] = v
short[i][v] = s
print ("#define %s_%s %s_%s %s/* %3d chars; %s */" %
(what_short[i], s, what[i], v.upper (),
' '* ((48-1 - len (what[i]) - 1 - len (v)) // 8),
values[i][v], v))
cat_defs.append ((what_short[i] + '_' + s, what[i] + '_' + v.upper (), str (values[i][v]), v))
maxlen_s = max ([len (c[0]) for c in cat_defs])
maxlen_l = max ([len (c[1]) for c in cat_defs])
maxlen_n = max ([len (c[2]) for c in cat_defs])
for s in what_short:
print ()
for c in [c for c in cat_defs if s in c[0]]:
print ("#define %s %s /* %s chars; %s */" %
(c[0].ljust (maxlen_s), c[1].ljust (maxlen_l), c[2].rjust (maxlen_n), c[3]))
print ()
print ('#pragma GCC diagnostic pop')
print ()
print ("#define _(S,M) INDIC_COMBINE_CATEGORIES (ISC_##S, IMC_##M)")
@ -245,12 +259,14 @@ print ("}")
print ()
print ("#undef _")
for i in range (2):
print
print ()
vv = sorted (values[i].keys ())
for v in vv:
print ("#undef %s_%s" %
(what_short[i], short[i][v]))
print ()
print ('#endif')
print ()
print ("/* == End of generated table == */")
# Maintain at least 30% occupancy in the table */

11
src/gen-os2-unicode-ranges.py Normal file → Executable file
View File

@ -1,20 +1,13 @@
# -*- coding: utf-8 -*-
#!/usr/bin/env python3
# Generates the code for a sorted unicode range array as used in hb-ot-os2-unicode-ranges.hh
# Input is a tab seperated list of unicode ranges from the otspec
# (https://docs.microsoft.com/en-us/typography/opentype/spec/os2#ulunicoderange1).
from __future__ import print_function, division, absolute_import
# (https://docs.microsoft.com/en-us/typography/opentype/spec/os2#ur).
import io
import re
import sys
try:
reload(sys)
sys.setdefaultencoding('utf-8')
except NameError:
pass # Python 3
print ("""static OS2Range _hb_os2_unicode_ranges[] =
{""")

View File

@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python3
"""Generator of the mapping from OpenType tags to BCP 47 tags and vice
versa.
@ -18,18 +18,11 @@ first BCP 47 tag happens to be the chosen disambiguated tag. In that
case, the fallback behavior will choose the right tag anyway.
"""
from __future__ import absolute_import, division, print_function, unicode_literals
import collections
try:
from HTMLParser import HTMLParser
def write (s):
print (s.encode ('utf-8'), end='')
except ImportError:
from html.parser import HTMLParser
def write (s):
sys.stdout.flush ()
sys.stdout.buffer.write (s.encode ('utf-8'))
from html.parser import HTMLParser
def write (s):
sys.stdout.flush ()
sys.stdout.buffer.write (s.encode ('utf-8'))
import io
import itertools
import re
@ -37,16 +30,16 @@ import sys
import unicodedata
if len (sys.argv) != 3:
print ('usage: ./gen-tag-table.py languagetags language-subtag-registry', file=sys.stderr)
print ('''usage: ./gen-tag-table.py languagetags language-subtag-registry
Input files, as of Unicode 12:
* https://docs.microsoft.com/en-us/typography/opentype/spec/languagetags
* https://www.iana.org/assignments/language-subtag-registry/language-subtag-registry''', file=sys.stderr)
sys.exit (1)
try:
from html import unescape
def html_unescape (parser, entity):
return unescape (entity)
except ImportError:
def html_unescape (parser, entity):
return parser.unescape (entity)
from html import unescape
def html_unescape (parser, entity):
return unescape (entity)
def expect (condition, message=None):
if not condition:
@ -54,7 +47,7 @@ def expect (condition, message=None):
raise AssertionError
raise AssertionError (message)
# from http://www-01.sil.org/iso639-3/iso-639-3.tab
# from https://www-01.sil.org/iso639-3/iso-639-3.tab
ISO_639_3_TO_1 = {
'aar': 'aa',
'abk': 'ab',
@ -754,7 +747,7 @@ ot.add_language ('und-Syre', 'SYRE')
ot.add_language ('und-Syrj', 'SYRJ')
ot.add_language ('und-Syrn', 'SYRN')
bcp_47.names['xst'] = u"Silt'e"
bcp_47.names['xst'] = "Silt'e"
bcp_47.scopes['xst'] = ' (retired code)'
bcp_47.macrolanguages['xst'] = {'stv', 'wle'}
@ -861,7 +854,7 @@ def hb_tag (tag):
Returns:
A snippet of C++ representing ``tag``.
"""
return u"HB_TAG('%s','%s','%s','%s')" % tuple (('%-4s' % tag)[:4])
return "HB_TAG('%s','%s','%s','%s')" % tuple (('%-4s' % tag)[:4])
def get_variant_set (name):
"""Return a set of variant language names from a name.
@ -873,7 +866,7 @@ def get_variant_set (name):
Returns:
A set of normalized language names.
"""
return set (unicodedata.normalize ('NFD', n.replace ('\u2019', u"'"))
return set (unicodedata.normalize ('NFD', n.replace ('\u2019', "'"))
.encode ('ASCII', 'ignore')
.strip ()
for n in re.split ('[\n(),]', name) if n)
@ -895,20 +888,18 @@ def language_name_intersection (a, b):
def get_matching_language_name (intersection, candidates):
return next (iter (c for c in candidates if not intersection.isdisjoint (get_variant_set (c))))
maximum_tags = 0
def same_tag (bcp_47_tag, ot_tags):
return len (bcp_47_tag) == 3 and len (ot_tags) == 1 and bcp_47_tag == ot_tags[0].lower ()
for language, tags in sorted (ot.from_bcp_47.items ()):
if language == '' or '-' in language:
continue
print (' {\"%s\",\t{' % language, end='')
maximum_tags = max (maximum_tags, len (tags))
tag_count = len (tags)
commented_out = same_tag (language, tags)
for i, tag in enumerate (tags, start=1):
if i > 1:
print ('\t\t ', end='')
print (hb_tag (tag), end='')
if i == tag_count:
print ('}}', end='')
print (',\t/* ', end='')
print ('%s{\"%s\",\t%s},' % ('/*' if commented_out else ' ', language, hb_tag (tag)), end='')
if commented_out:
print ('*/', end='')
print ('\t/* ', end='')
bcp_47_name = bcp_47.names.get (language, '')
bcp_47_name_candidates = bcp_47_name.split ('\n')
intersection = language_name_intersection (bcp_47_name, ot.names[tag])
@ -923,8 +914,6 @@ for language, tags in sorted (ot.from_bcp_47.items ()):
print ('};')
print ()
print ('static_assert (HB_OT_MAX_TAGS_PER_LANGUAGE == %iu, "");' % maximum_tags)
print ()
print ('/**')
print (' * hb_ot_tags_from_complex_language:')
@ -1051,7 +1040,8 @@ print (' * @tag: A language tag.')
print (' *')
print (' * Converts @tag to a BCP 47 language tag if it is ambiguous (it corresponds to')
print (' * many language tags) and the best tag is not the alphabetically first, or if')
print (' * the best tag consists of multiple subtags.')
print (' * the best tag consists of multiple subtags, or if the best tag does not appear')
print (' * in #ot_languages.')
print (' *')
print (' * Return value: The #hb_language_t corresponding to the BCP 47 language tag,')
print (' * or #HB_LANGUAGE_INVALID if @tag is not ambiguous.')
@ -1102,7 +1092,8 @@ def verify_disambiguation_dict ():
'%s is not a valid disambiguation for %s' % (disambiguation[ot_tag], ot_tag))
elif ot_tag not in disambiguation:
disambiguation[ot_tag] = macrolanguages[0]
if disambiguation[ot_tag] == sorted (primary_tags)[0] and '-' not in disambiguation[ot_tag]:
different_primary_tags = sorted (t for t in primary_tags if not same_tag (t, ot.from_bcp_47.get (t)))
if different_primary_tags and disambiguation[ot_tag] == different_primary_tags[0] and '-' not in disambiguation[ot_tag]:
del disambiguation[ot_tag]
for ot_tag in disambiguation.keys ():
expect (ot_tag in ot.to_bcp_47, 'unknown OT tag: %s' % ot_tag)

165
src/gen-ucd-table.py Executable file
View File

@ -0,0 +1,165 @@
#!/usr/bin/env python3
import io, os.path, sys, re
import logging
logging.basicConfig(format='%(levelname)s: %(message)s', level=logging.INFO)
if len (sys.argv) not in (2, 3):
print("""usage: ./gen-ucd-table ucd.nounihan.grouped.xml [/path/to/hb-common.h]
Input file, as of Unicode 12:
* https://unicode.org/Public/UCD/latest/ucdxml/ucd.nounihan.grouped.zip""", file=sys.stderr)
sys.exit(1)
# https://github.com/harfbuzz/packtab
import packTab
import packTab.ucdxml
logging.info('Loading UCDXML...')
ucdxml = packTab.ucdxml.load_ucdxml(sys.argv[1])
ucd = packTab.ucdxml.ucdxml_get_repertoire(ucdxml)
hb_common_h = 'hb-common.h' if len (sys.argv) < 3 else sys.argv[2]
logging.info('Preparing data tables...')
gc = [u['gc'] for u in ucd]
ccc = [int(u['ccc']) for u in ucd]
bmg = [int(v, 16) - int(u) if v else 0 for u,v in enumerate(u['bmg'] for u in ucd)]
#gc_ccc_non0 = set((cat,klass) for cat,klass in zip(gc,ccc) if klass)
#gc_bmg_non0 = set((cat,mirr) for cat,mirr in zip(gc, bmg) if mirr)
sc = [u['sc'] for u in ucd]
dm = {i:tuple(int(v, 16) for v in u['dm'].split()) for i,u in enumerate(ucd)
if u['dm'] != '#' and u['dt'] == 'can' and not (0xAC00 <= i < 0xAC00+11172)}
ce = {i for i,u in enumerate(ucd) if u['Comp_Ex'] == 'Y'}
assert not any(v for v in dm.values() if len(v) not in (1,2))
dm1 = sorted(set(v for v in dm.values() if len(v) == 1))
assert all((v[0] >> 16) in (0,2) for v in dm1)
dm1_p0_array = ['0x%04Xu' % (v[0] & 0xFFFF) for v in dm1 if (v[0] >> 16) == 0]
dm1_p2_array = ['0x%04Xu' % (v[0] & 0xFFFF) for v in dm1 if (v[0] >> 16) == 2]
dm1_order = {v:i+1 for i,v in enumerate(dm1)}
dm2 = sorted((v+(i if i not in ce and not ccc[i] else 0,), v)
for i,v in dm.items() if len(v) == 2)
filt = lambda v: ((v[0] & 0xFFFFF800) == 0x0000 and
(v[1] & 0xFFFFFF80) == 0x0300 and
(v[2] & 0xFFF0C000) == 0x0000)
dm2_u32_array = [v for v in dm2 if filt(v[0])]
dm2_u64_array = [v for v in dm2 if not filt(v[0])]
assert dm2_u32_array + dm2_u64_array == dm2
dm2_u32_array = ["HB_CODEPOINT_ENCODE3_11_7_14 (0x%04Xu, 0x%04Xu, 0x%04Xu)" % v[0] for v in dm2_u32_array]
dm2_u64_array = ["HB_CODEPOINT_ENCODE3 (0x%04Xu, 0x%04Xu, 0x%04Xu)" % v[0] for v in dm2_u64_array]
l = 1 + len(dm1_p0_array) + len(dm1_p2_array)
dm2_order = {v[1]:i+l for i,v in enumerate(dm2)}
dm_order = {None: 0}
dm_order.update(dm1_order)
dm_order.update(dm2_order)
gc_order = dict()
for i,v in enumerate(('Cc', 'Cf', 'Cn', 'Co', 'Cs', 'Ll', 'Lm', 'Lo', 'Lt', 'Lu',
'Mc', 'Me', 'Mn', 'Nd', 'Nl', 'No', 'Pc', 'Pd', 'Pe', 'Pf',
'Pi', 'Po', 'Ps', 'Sc', 'Sk', 'Sm', 'So', 'Zl', 'Zp', 'Zs',)):
gc_order[i] = v
gc_order[v] = i
sc_order = dict()
sc_array = []
sc_re = re.compile(r"\b(HB_SCRIPT_[_A-Z]*).*HB_TAG [(]'(.)','(.)','(.)','(.)'[)]")
for line in open(hb_common_h):
m = sc_re.search (line)
if not m: continue
name = m.group(1)
tag = ''.join(m.group(i) for i in range(2, 6))
i = len(sc_array)
sc_order[tag] = i
sc_order[i] = tag
sc_array.append(name)
DEFAULT = 1
COMPACT = 3
SLOPPY = 5
logging.info('Generating output...')
print("/* == Start of generated table == */")
print("/*")
print(" * The following table is generated by running:")
print(" *")
print(" * ./gen-ucd-table.py ucd.nounihan.grouped.xml")
print(" *")
print(" * on file with this description:", ucdxml.description)
print(" */")
print()
print("#ifndef HB_UCD_TABLE_HH")
print("#define HB_UCD_TABLE_HH")
print()
print('#include "hb.hh"')
print()
code = packTab.Code('_hb_ucd')
sc_array, _ = code.addArray('hb_script_t', 'sc_map', sc_array)
dm1_p0_array, _ = code.addArray('uint16_t', 'dm1_p0_map', dm1_p0_array)
dm1_p2_array, _ = code.addArray('uint16_t', 'dm1_p2_map', dm1_p2_array)
dm2_u32_array, _ = code.addArray('uint32_t', 'dm2_u32_map', dm2_u32_array)
dm2_u64_array, _ = code.addArray('uint64_t', 'dm2_u64_map', dm2_u64_array)
code.print_c(linkage='static inline')
datasets = [
('gc', gc, 'Cn', gc_order),
('ccc', ccc, 0, None),
('bmg', bmg, 0, None),
('sc', sc, 'Zzzz', sc_order),
('dm', dm, None, dm_order),
]
for compression in (DEFAULT, COMPACT, SLOPPY):
logging.info(' Compression=%d:' % compression)
print()
if compression == DEFAULT:
print('#ifndef HB_OPTIMIZE_SIZE')
elif compression == COMPACT:
print('#elif !defined(HB_NO_UCD_UNASSIGNED)')
else:
print('#else')
print()
if compression == SLOPPY:
for i in range(len(gc)):
if (i % 128) and gc[i] == 'Cn':
gc[i] = gc[i - 1]
for i in range(len(gc) - 2, -1, -1):
if ((i + 1) % 128) and gc[i] == 'Cn':
gc[i] = gc[i + 1]
for i in range(len(sc)):
if (i % 128) and sc[i] == 'Zzzz':
sc[i] = sc[i - 1]
for i in range(len(sc) - 2, -1, -1):
if ((i + 1) % 128) and sc[i] == 'Zzzz':
sc[i] = sc[i + 1]
code = packTab.Code('_hb_ucd')
for name,data,default,mapping in datasets:
sol = packTab.pack_table(data, default, mapping=mapping, compression=compression)
logging.info(' Dataset=%-8s FullCost=%d' % (name, sol.fullCost))
sol.genCode(code, name)
code.print_c(linkage='static inline')
print()
print('#endif')
print()
print()
print("#endif /* HB_UCD_TABLE_HH */")
print()
print("/* == End of generated table == */")
logging.info('Done.')

View File

@ -1,13 +1,17 @@
#!/usr/bin/env python
#!/usr/bin/env python3
# flake8: noqa
from __future__ import print_function, division, absolute_import
import io
import sys
if len (sys.argv) != 5:
print ("usage: ./gen-use-table.py IndicSyllabicCategory.txt IndicPositionalCategory.txt UnicodeData.txt Blocks.txt", file=sys.stderr)
print ("""usage: ./gen-use-table.py IndicSyllabicCategory.txt IndicPositionalCategory.txt UnicodeData.txt Blocks.txt
Input file, as of Unicode 12:
* https://unicode.org/Public/UCD/latest/ucd/IndicSyllabicCategory.txt
* https://unicode.org/Public/UCD/latest/ucd/IndicPositionalCategory.txt
* https://unicode.org/Public/UCD/latest/ucd/UnicodeData.txt
* https://unicode.org/Public/UCD/latest/ucd/Blocks.txt""", file=sys.stderr)
sys.exit (1)
BLACKLISTED_BLOCKS = ["Thai", "Lao"]
@ -47,8 +51,22 @@ 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][0x1B61] = defaults[0]
data[0][0x1B63] = defaults[0]
data[0][0x1B64] = defaults[0]
data[0][0x1B65] = defaults[0]
data[0][0x1B66] = defaults[0]
data[0][0x1B67] = defaults[0]
data[0][0x1B69] = defaults[0]
data[0][0x1B6A] = defaults[0]
data[0][0x2060] = defaults[0]
# TODO https://github.com/roozbehp/unicode-data/issues/9
# TODO https://github.com/harfbuzz/harfbuzz/pull/1685
data[0][0x1B5B] = 'Consonant_Placeholder'
data[0][0x1B5C] = 'Consonant_Placeholder'
data[0][0x1B5F] = 'Consonant_Placeholder'
data[0][0x1B62] = 'Consonant_Placeholder'
data[0][0x1B68] = 'Consonant_Placeholder'
# TODO https://github.com/harfbuzz/harfbuzz/issues/1035
data[0][0x11C44] = 'Consonant_Placeholder'
data[0][0x11C45] = 'Consonant_Placeholder'
# TODO https://github.com/harfbuzz/harfbuzz/pull/1399
@ -133,18 +151,13 @@ property_names = [
'Overstruck',
]
try:
basestring
except NameError:
basestring = str
class PropertyValue(object):
def __init__(self, name_):
self.name = name_
def __str__(self):
return self.name
def __eq__(self, other):
return self.name == (other if isinstance(other, basestring) else other.name)
return self.name == (other if isinstance(other, str) else other.name)
def __ne__(self, other):
return not (self == other)
def __hash__(self):
@ -171,7 +184,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 [0x104B, 0x104E, 0x2022, 0x111C8, 0x11A3F, 0x11A45, 0x11C44, 0x11C45]) or
(UGC == Po and not U in [0x104B, 0x104E, 0x1B5B, 0x1B5C, 0x1B5F, 0x2022, 0x111C8, 0x11A3F, 0x11A45, 0x11C44, 0x11C45]) or
False # SPEC-DRAFT-OUTDATED! U == 0x002D
)
def is_BASE_NUM(U, UISC, UGC):
@ -183,15 +196,15 @@ def is_BASE_OTHER(U, UISC, UGC):
def is_CGJ(U, UISC, UGC):
return U == 0x034F
def is_CONS_FINAL(U, UISC, UGC):
# Consonant_Initial_Postfixed is new in Unicode 11; not in the spec.
return ((UISC == Consonant_Final and UGC != Lo) or
UISC == Consonant_Initial_Postfixed or
UISC == Consonant_Succeeding_Repha)
def is_CONS_FINAL_MOD(U, UISC, UGC):
#SPEC-DRAFT return UISC in [Consonant_Final_Modifier, Syllable_Modifier]
return UISC == Syllable_Modifier
def is_CONS_MED(U, UISC, UGC):
return UISC == Consonant_Medial and UGC != Lo
# Consonant_Initial_Postfixed is new in Unicode 11; not in the spec.
return (UISC == Consonant_Medial and UGC != Lo or
UISC == Consonant_Initial_Postfixed)
def is_CONS_MOD(U, UISC, UGC):
return UISC in [Nukta, Gemination_Mark, Consonant_Killer]
def is_CONS_SUB(U, UISC, UGC):
@ -200,7 +213,9 @@ def is_CONS_SUB(U, UISC, UGC):
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] and not is_HALANT_OR_VOWEL_MODIFIER(U, UISC, UGC)
return (UISC in [Virama, Invisible_Stacker]
and not is_HALANT_OR_VOWEL_MODIFIER(U, UISC, UGC)
and not is_SAKOT(U, UISC, UGC))
def is_HALANT_OR_VOWEL_MODIFIER(U, UISC, UGC):
# https://github.com/harfbuzz/harfbuzz/issues/1102
# https://github.com/harfbuzz/harfbuzz/issues/1379
@ -216,6 +231,7 @@ def is_Word_Joiner(U, UISC, UGC):
def is_OTHER(U, UISC, UGC):
#SPEC-OUTDATED return UGC == Zs # or any other SCRIPT_COMMON characters
return (UISC == Other
and not is_SYM(U, UISC, UGC)
and not is_SYM_MOD(U, UISC, UGC)
and not is_CGJ(U, UISC, UGC)
and not is_Word_Joiner(U, UISC, UGC)
@ -225,20 +241,22 @@ def is_Reserved(U, UISC, UGC):
return UGC == 'Cn'
def is_REPHA(U, UISC, UGC):
return UISC in [Consonant_Preceding_Repha, Consonant_Prefixed]
def is_SAKOT(U, UISC, UGC):
return U == 0x1A60
def is_SYM(U, UISC, UGC):
if U == 0x25CC: return False #SPEC-DRAFT
#SPEC-DRAFT return UGC in [So, Sc] or UISC == Symbol_Letter
return UGC in [So, Sc]
return UGC in [So, Sc] and U not in [0x1B62, 0x1B68]
def is_SYM_MOD(U, UISC, UGC):
return U in [0x1B6B, 0x1B6C, 0x1B6D, 0x1B6E, 0x1B6F, 0x1B70, 0x1B71, 0x1B72, 0x1B73]
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
# https://github.com/harfbuzz/harfbuzz/issues/376
return (UISC == Pure_Killer or
(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
# https://github.com/harfbuzz/harfbuzz/issues/376
return (UISC in [Tone_Mark, Cantillation_Mark, Register_Shifter, Visarga] or
(UGC != Lo and (UISC == Bindu or U in [0xAA29])))
@ -264,6 +282,7 @@ use_mapping = {
'Rsv': is_Reserved,
'R': is_REPHA,
'S': is_SYM,
'Sk': is_SAKOT,
'SM': is_SYM_MOD,
'VS': is_VARIATION_SELECTOR,
'V': is_VOWEL,
@ -305,7 +324,11 @@ use_positions = {
'H': None,
'HVM': None,
'B': None,
'FM': None,
'FM': {
'Abv': [Top],
'Blw': [Bottom],
'Pst': [Not_Applicable],
},
'SUB': None,
}
@ -344,15 +367,9 @@ def map_to_use(data):
# the nasalization marks, maybe only for U+1CE9..U+1CF1.
if U == 0x1CED: UISC = Tone_Mark
# TODO: https://github.com/harfbuzz/harfbuzz/issues/525
if U == 0x1A7F: UISC = Consonant_Final
# TODO: https://github.com/harfbuzz/harfbuzz/issues/1105
if U == 0x11134: UISC = Gemination_Mark
# TODO: https://github.com/harfbuzz/harfbuzz/pull/1399
if U == 0x111C9: UISC = Consonant_Final
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]
@ -365,6 +382,9 @@ def map_to_use(data):
# TODO: In USE's override list but not in Unicode 12.0
if U == 0x103C: UIPC = Left
# TODO: https://github.com/harfbuzz/harfbuzz/pull/2012
if U == 0x1C29: UIPC = Left
# TODO: These are not in USE's override list that we have, nor are they in Unicode 12.0
if 0xA926 <= U <= 0xA92A: UIPC = Top
# TODO: https://github.com/harfbuzz/harfbuzz/pull/1037
@ -401,6 +421,10 @@ for h in headers:
print (" * %s" % (l.strip()))
print (" */")
print ()
print ('#include "hb.hh"')
print ()
print ('#ifndef HB_NO_OT_SHAPE')
print ()
print ('#include "hb-ot-shape-complex-use.hh"')
print ()
@ -515,6 +539,8 @@ for k,v in sorted(use_positions.items()):
tag = k + suf
print ("#undef %s" % tag)
print ()
print ()
print ('#endif')
print ("/* == End of generated table == */")
# Maintain at least 50% occupancy in the table */

View File

@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python3
"""Generator of the function to prohibit certain vowel sequences.
@ -6,26 +6,23 @@ It creates ``_hb_preprocess_text_vowel_constraints``, which inserts dotted
circles into sequences prohibited by the USE script development spec.
This function should be used as the ``preprocess_text`` of an
``hb_ot_complex_shaper_t``.
"""
from __future__ import absolute_import, division, print_function, unicode_literals
import collections
try:
from HTMLParser import HTMLParser
def write (s):
print (s.encode ('utf-8'), end='')
except ImportError:
from html.parser import HTMLParser
def write (s):
sys.stdout.flush ()
sys.stdout.buffer.write (s.encode ('utf-8'))
from html.parser import HTMLParser
def write (s):
sys.stdout.flush ()
sys.stdout.buffer.write (s.encode ('utf-8'))
import itertools
import io
import sys
if len (sys.argv) != 3:
print ('usage: ./gen-vowel-constraints.py HBIndicVowelConstraints.txt Scripts.txt', file=sys.stderr)
print ("""usage: ./gen-vowel-constraints.py ms-use/IndicShapingInvalidCluster.txt Scripts.txt
Input file, as of Unicode 12:
* https://unicode.org/Public/UCD/latest/ucd/Scripts.txt""", file=sys.stderr)
sys.exit (1)
with io.open (sys.argv[2], encoding='utf-8') as f:
@ -84,7 +81,8 @@ class ConstraintSet (object):
else:
self._c[first] = ConstraintSet (rest)
def _indent (self, depth):
@staticmethod
def _indent (depth):
return (' ' * depth).replace (' ', '\t')
def __str__ (self, index=0, depth=4):
@ -92,17 +90,20 @@ class ConstraintSet (object):
indent = self._indent (depth)
if isinstance (self._c, list):
if len (self._c) == 0:
assert index == 2, 'Cannot use `matched` for this constraint; the general case has not been implemented'
s.append ('{}matched = true;\n'.format (indent))
elif len (self._c) == 1:
assert index == 1, 'Cannot use `matched` for this constraint; the general case has not been implemented'
s.append ('{}matched = 0x{:04X}u == buffer->cur ({}).codepoint;\n'.format (indent, next (iter (self._c)), index or ''))
else:
s.append ('{}if (0x{:04X}u == buffer->cur ({}).codepoint &&\n'.format (indent, self._c[0], index))
s.append ('{}buffer->idx + {} < count &&\n'.format (self._indent (depth + 2), len (self._c)))
s.append ('{}if (0x{:04X}u == buffer->cur ({}).codepoint &&\n'.format (indent, self._c[0], index or ''))
if index:
s.append ('{}buffer->idx + {} < count &&\n'.format (self._indent (depth + 2), index + 1))
for i, cp in enumerate (self._c[1:], start=1):
s.append ('{}0x{:04X}u == buffer->cur ({}).codepoint{}\n'.format (
self._indent (depth + 2), cp, index + i, ')' if i == len (self._c) - 1 else ' &&'))
s.append ('{}{{\n'.format (indent))
for i in range (len (self._c)):
for i in range (index + 1):
s.append ('{}buffer->next_glyph ();\n'.format (self._indent (depth + 1)))
s.append ('{}_output_dotted_circle (buffer);\n'.format (self._indent (depth + 1)))
s.append ('{}}}\n'.format (indent))
@ -128,7 +129,12 @@ class ConstraintSet (object):
constraints = {}
with io.open (sys.argv[1], encoding='utf-8') as f:
constraints_header = [f.readline ().strip () for i in range (2)]
constraints_header = []
while True:
line = f.readline ().strip ()
if line == '#':
break
constraints_header.append(line)
for line in f:
j = line.find ('#')
if j >= 0:
@ -147,7 +153,7 @@ print ('/* == Start of generated functions == */')
print ('/*')
print (' * The following functions are generated by running:')
print (' *')
print (' * %s use Scripts.txt' % sys.argv[0])
print (' * %s ms-use/IndicShapingInvalidCluster.txt Scripts.txt' % sys.argv[0])
print (' *')
print (' * on files with these headers:')
print (' *')
@ -157,6 +163,11 @@ print (' *')
for line in scripts_header:
print (' * %s' % line.strip ())
print (' */')
print ()
print ('#include "hb.hh"')
print ()
print ('#ifndef HB_NO_OT_SHAPE')
print ()
print ('#include "hb-ot-shape-complex-vowel-constraints.hh"')
print ()
@ -180,6 +191,9 @@ print ('_hb_preprocess_text_vowel_constraints (const hb_ot_shape_plan_t *plan HB
print ('\t\t\t\t hb_buffer_t *buffer,')
print ('\t\t\t\t hb_font_t *font HB_UNUSED)')
print ('{')
print ('#ifdef HB_NO_OT_SHAPE_COMPLEX_VOWEL_CONSTRAINTS')
print (' return;')
print ('#endif')
print (' if (buffer->flags & HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE)')
print (' return;')
print ()
@ -220,4 +234,6 @@ print (' }')
print ('}')
print ()
print ()
print ('#endif')
print ('/* == End of generated functions == */')

54
src/harfbuzz.cc Normal file
View File

@ -0,0 +1,54 @@
#include "hb-aat-layout.cc"
#include "hb-aat-map.cc"
#include "hb-blob.cc"
#include "hb-buffer-serialize.cc"
#include "hb-buffer.cc"
#include "hb-common.cc"
#include "hb-draw.cc"
#include "hb-face.cc"
#include "hb-fallback-shape.cc"
#include "hb-font.cc"
#include "hb-map.cc"
#include "hb-number.cc"
#include "hb-ot-cff1-table.cc"
#include "hb-ot-cff2-table.cc"
#include "hb-ot-color.cc"
#include "hb-ot-face.cc"
#include "hb-ot-font.cc"
#include "hb-ot-layout.cc"
#include "hb-ot-map.cc"
#include "hb-ot-math.cc"
#include "hb-ot-meta.cc"
#include "hb-ot-metrics.cc"
#include "hb-ot-name.cc"
#include "hb-ot-shape-complex-arabic.cc"
#include "hb-ot-shape-complex-default.cc"
#include "hb-ot-shape-complex-hangul.cc"
#include "hb-ot-shape-complex-hebrew.cc"
#include "hb-ot-shape-complex-indic-table.cc"
#include "hb-ot-shape-complex-indic.cc"
#include "hb-ot-shape-complex-khmer.cc"
#include "hb-ot-shape-complex-myanmar.cc"
#include "hb-ot-shape-complex-thai.cc"
#include "hb-ot-shape-complex-use-table.cc"
#include "hb-ot-shape-complex-use.cc"
#include "hb-ot-shape-complex-vowel-constraints.cc"
#include "hb-ot-shape-fallback.cc"
#include "hb-ot-shape-normalize.cc"
#include "hb-ot-shape.cc"
#include "hb-ot-tag.cc"
#include "hb-ot-var.cc"
#include "hb-set.cc"
#include "hb-shape-plan.cc"
#include "hb-shape.cc"
#include "hb-shaper.cc"
#include "hb-static.cc"
#include "hb-ucd.cc"
#include "hb-unicode.cc"
#include "hb-glib.cc"
#include "hb-ft.cc"
#include "hb-graphite2.cc"
#include "hb-uniscribe.cc"
#include "hb-gdi.cc"
#include "hb-directwrite.cc"
#include "hb-coretext.cc"

View File

@ -65,7 +65,7 @@ struct FontDescriptor
protected:
Tag tag; /* The 4-byte table tag name. */
union {
Fixed value; /* The value for the descriptor tag. */
HBFixed value; /* The value for the descriptor tag. */
HBUINT32 nalfType; /* If the tag is `nalf`, see non_alphabetic_value_t */
} u;
public:
@ -80,10 +80,10 @@ struct fdsc
Weight = HB_TAG ('w','g','h','t'),
/* Percent weight relative to regular weight.
* (defaul value: 1.0) */
Width = HB_TAG ('w','d','t','h'),
Width = HB_TAG ('w','d','t','h'),
/* Percent width relative to regular width.
* (default value: 1.0) */
Slant = HB_TAG ('s','l','n','t'),
Slant = HB_TAG ('s','l','n','t'),
/* Angle of slant in degrees, where positive
* is clockwise from straight up.
* (default value: 0.0) */
@ -108,7 +108,7 @@ struct fdsc
}
protected:
Fixed version; /* Version number of the font descriptors
HBFixed version; /* Version number of the font descriptors
* table (0x00010000 for the current version). */
LArrayOf<FontDescriptor>
descriptors; /* List of tagged-coordinate pairs style descriptors

View File

@ -66,7 +66,7 @@ struct ankr
{
const NNOffsetTo<GlyphAnchors> *offset = (this+lookupTable).get_value (glyph_id, num_glyphs);
if (!offset)
return Null(Anchor);
return Null (Anchor);
const GlyphAnchors &anchors = &(this+anchorData) + *offset;
return anchors[i];
}
@ -81,9 +81,9 @@ struct ankr
}
protected:
HBUINT16 version; /* Version number (set to zero) */
HBUINT16 version; /* Version number (set to zero) */
HBUINT16 flags; /* Flags (currently unused; set to zero) */
LOffsetTo<Lookup<NNOffsetTo<GlyphAnchors> > >
LOffsetTo<Lookup<NNOffsetTo<GlyphAnchors>>>
lookupTable; /* Offset to the table's lookup table */
LNNOffsetTo<HBUINT8>
anchorData; /* Offset to the glyph data table */

View File

@ -82,7 +82,7 @@ struct BaselineTableFormat2Part
}
protected:
GlyphID stdGlyph; /* The specific glyph index number in this
HBGlyphID stdGlyph; /* The specific glyph index number in this
* font that is used to set the baseline values.
* This is the standard glyph.
* This glyph must contain a set of control points
@ -105,7 +105,7 @@ struct BaselineTableFormat3Part
}
protected:
GlyphID stdGlyph; /* ditto */
HBGlyphID stdGlyph; /* ditto */
HBUINT16 ctlPoints[32]; /* ditto */
Lookup<HBUINT16>
lookupTable; /* Lookup table that maps glyphs to their

View File

@ -93,8 +93,8 @@ struct LookupSegmentSingle
return_trace (c->check_struct (this) && value.sanitize (c, base));
}
GlyphID last; /* Last GlyphID in this segment */
GlyphID first; /* First GlyphID in this segment */
HBGlyphID last; /* Last GlyphID in this segment */
HBGlyphID first; /* First GlyphID in this segment */
T value; /* The lookup value (only one) */
public:
DEFINE_SIZE_STATIC (4 + T::static_size);
@ -125,7 +125,7 @@ struct LookupFormat2
protected:
HBUINT16 format; /* Format identifier--format = 2 */
VarSizedBinSearchArrayOf<LookupSegmentSingle<T> >
VarSizedBinSearchArrayOf<LookupSegmentSingle<T>>
segments; /* The actual segments. These must already be sorted,
* according to the first word in each one (the last
* glyph in each segment). */
@ -153,18 +153,18 @@ struct LookupSegmentArray
first <= last &&
valuesZ.sanitize (c, base, last - first + 1));
}
template <typename T2>
bool sanitize (hb_sanitize_context_t *c, const void *base, T2 user_data) const
template <typename ...Ts>
bool sanitize (hb_sanitize_context_t *c, const void *base, Ts&&... ds) const
{
TRACE_SANITIZE (this);
return_trace (c->check_struct (this) &&
first <= last &&
valuesZ.sanitize (c, base, last - first + 1, user_data));
valuesZ.sanitize (c, base, last - first + 1, hb_forward<Ts> (ds)...));
}
GlyphID last; /* Last GlyphID in this segment */
GlyphID first; /* First GlyphID in this segment */
NNOffsetTo<UnsizedArrayOf<T> >
HBGlyphID last; /* Last GlyphID in this segment */
HBGlyphID first; /* First GlyphID in this segment */
NNOffsetTo<UnsizedArrayOf<T>>
valuesZ; /* A 16-bit offset from the start of
* the table to the data. */
public:
@ -196,7 +196,7 @@ struct LookupFormat4
protected:
HBUINT16 format; /* Format identifier--format = 4 */
VarSizedBinSearchArrayOf<LookupSegmentArray<T> >
VarSizedBinSearchArrayOf<LookupSegmentArray<T>>
segments; /* The actual segments. These must already be sorted,
* according to the first word in each one (the last
* glyph in each segment). */
@ -222,7 +222,7 @@ struct LookupSingle
return_trace (c->check_struct (this) && value.sanitize (c, base));
}
GlyphID glyph; /* Last GlyphID */
HBGlyphID glyph; /* Last GlyphID */
T value; /* The lookup value (only one) */
public:
DEFINE_SIZE_STATIC (2 + T::static_size);
@ -253,7 +253,7 @@ struct LookupFormat6
protected:
HBUINT16 format; /* Format identifier--format = 6 */
VarSizedBinSearchArrayOf<LookupSingle<T> >
VarSizedBinSearchArrayOf<LookupSingle<T>>
entries; /* The actual entries, sorted by glyph index. */
public:
DEFINE_SIZE_ARRAY (8, entries);
@ -284,7 +284,7 @@ struct LookupFormat8
protected:
HBUINT16 format; /* Format identifier--format = 8 */
GlyphID firstGlyph; /* First glyph index included in the trimmed array. */
HBGlyphID 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<T>
@ -303,7 +303,7 @@ struct LookupFormat10
const typename T::type get_value_or_null (hb_codepoint_t glyph_id) const
{
if (!(firstGlyph <= glyph_id && glyph_id - firstGlyph < glyphCount))
return Null(T);
return Null (T);
const HBUINT8 *p = &valueArrayZ[(glyph_id - firstGlyph) * valueSize];
@ -326,7 +326,7 @@ struct LookupFormat10
protected:
HBUINT16 format; /* Format identifier--format = 8 */
HBUINT16 valueSize; /* Byte size of each value. */
GlyphID firstGlyph; /* First glyph index included in the trimmed array. */
HBGlyphID 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<HBUINT8>
@ -358,7 +358,7 @@ struct Lookup
case 10: return u.format10.get_value_or_null (glyph_id);
default:
const T *v = get_value (glyph_id, num_glyphs);
return v ? *v : Null(T);
return v ? *v : Null (T);
}
}
@ -419,7 +419,7 @@ struct Lookup
/* Ugly hand-coded null objects for template Lookup<> :(. */
extern HB_INTERNAL const unsigned char _hb_Null_AAT_Lookup[2];
template <typename T>
struct Null<AAT::Lookup<T> > {
struct Null<AAT::Lookup<T>> {
static AAT::Lookup<T> const & get_null ()
{ return *reinterpret_cast<const AAT::Lookup<T> *> (_hb_Null_AAT_Lookup); }
};
@ -510,7 +510,7 @@ struct StateTable
const Entry<Extra> &get_entry (int state, unsigned int klass) const
{
if (unlikely (klass >= nClasses))
klass = StateTable<Types, Entry<Extra> >::CLASS_OUT_OF_BOUNDS;
klass = StateTable<Types, Entry<Extra>>::CLASS_OUT_OF_BOUNDS;
const HBUSHORT *states = (this+stateArrayTable).arrayZ;
const Entry<Extra> *entries = (this+entryTable).arrayZ;
@ -576,7 +576,7 @@ struct StateTable
if (unlikely (stop > states))
return_trace (false);
for (const HBUSHORT *p = states; stop < p; p--)
num_entries = MAX<unsigned int> (num_entries, *(p - 1) + 1);
num_entries = hb_max (num_entries, *(p - 1) + 1);
state_neg = min_state;
}
}
@ -597,7 +597,7 @@ struct StateTable
if (unlikely (stop < states))
return_trace (false);
for (const HBUSHORT *p = &states[state_pos * num_classes]; p < stop; p++)
num_entries = MAX<unsigned int> (num_entries, *p + 1);
num_entries = hb_max (num_entries, *p + 1);
state_pos = max_state + 1;
}
}
@ -611,8 +611,8 @@ struct StateTable
for (const Entry<Extra> *p = &entries[entry]; p < stop; p++)
{
int newState = new_state (p->newState);
min_state = MIN (min_state, newState);
max_state = MAX (max_state, newState);
min_state = hb_min (min_state, newState);
max_state = hb_max (max_state, newState);
}
entry = num_entries;
}
@ -631,7 +631,7 @@ struct StateTable
classTable; /* Offset to the class table. */
NNOffsetTo<UnsizedArrayOf<HBUSHORT>, HBUINT>
stateArrayTable;/* Offset to the state array. */
NNOffsetTo<UnsizedArrayOf<Entry<Extra> >, HBUINT>
NNOffsetTo<UnsizedArrayOf<Entry<Extra>>, HBUINT>
entryTable; /* Offset to the entry array. */
public:
@ -658,7 +658,7 @@ struct ClassTable
return_trace (c->check_struct (this) && classArray.sanitize (c));
}
protected:
GlyphID firstGlyph; /* First glyph index included in the trimmed array. */
HBGlyphID firstGlyph; /* First glyph index included in the trimmed array. */
ArrayOf<HBUCHAR> classArray; /* The class codes (indexed by glyph index minus
* firstGlyph). */
public:
@ -678,7 +678,7 @@ struct ObsoleteTypes
const void *base,
const T *array)
{
return (offset - ((const char *) array - (const char *) base)) / sizeof (T);
return (offset - ((const char *) array - (const char *) base)) / T::static_size;
}
template <typename T>
static unsigned int byteOffsetToIndex (unsigned int offset,
@ -825,7 +825,7 @@ struct hb_aat_apply_context_t :
HB_INTERNAL hb_aat_apply_context_t (const hb_ot_shape_plan_t *plan_,
hb_font_t *font_,
hb_buffer_t *buffer_,
hb_blob_t *blob = const_cast<hb_blob_t *> (&Null(hb_blob_t)));
hb_blob_t *blob = const_cast<hb_blob_t *> (&Null (hb_blob_t)));
HB_INTERNAL ~hb_aat_apply_context_t ();

View File

@ -47,17 +47,16 @@ struct SettingName
hb_aat_layout_feature_selector_t get_selector () const
{ return (hb_aat_layout_feature_selector_t) (unsigned) setting; }
void get_info (hb_aat_layout_feature_selector_info_t *s,
hb_aat_layout_feature_selector_t default_selector) const
hb_aat_layout_feature_selector_info_t get_info (hb_aat_layout_feature_selector_t default_selector) const
{
s->name_id = nameIndex;
s->enable = (hb_aat_layout_feature_selector_t) (unsigned int) setting;
s->disable = default_selector == HB_AAT_LAYOUT_FEATURE_SELECTOR_INVALID ?
(hb_aat_layout_feature_selector_t) (s->enable + 1) :
default_selector;
s->reserved = 0;
return {
nameIndex,
(hb_aat_layout_feature_selector_t) (unsigned int) setting,
default_selector == HB_AAT_LAYOUT_FEATURE_SELECTOR_INVALID
? (hb_aat_layout_feature_selector_t) (setting + 1)
: default_selector,
0
};
}
bool sanitize (hb_sanitize_context_t *c) const
@ -117,9 +116,10 @@ struct FeatureName
if (selectors_count)
{
hb_array_t<const SettingName> arr = settings_table.sub_array (start_offset, selectors_count);
for (unsigned int i = 0; i < arr.length; i++)
settings_table[start_offset + i].get_info (&selectors[i], default_selector);
+ settings_table.sub_array (start_offset, selectors_count)
| hb_map ([=] (const SettingName& setting) { return setting.get_info (default_selector); })
| hb_sink (hb_array (selectors, *selectors_count))
;
}
return settings_table.length;
}
@ -129,6 +129,11 @@ struct FeatureName
hb_ot_name_id_t get_feature_name_id () const { return nameIndex; }
bool is_exclusive () const { return featureFlags & Exclusive; }
/* A FeatureName with no settings is meaningless */
bool has_data () const { return nSettings; }
bool sanitize (hb_sanitize_context_t *c, const void *base) const
{
TRACE_SANITIZE (this);
@ -139,7 +144,7 @@ struct FeatureName
protected:
HBUINT16 feature; /* Feature type. */
HBUINT16 nSettings; /* The number of records in the setting name array. */
LOffsetTo<UnsizedArrayOf<SettingName>, false>
LNNOffsetTo<UnsizedArrayOf<SettingName>>
settingTableZ; /* Offset in bytes from the beginning of this table to
* this feature's setting name array. The actual type of
* record this offset refers to will depend on the
@ -162,21 +167,21 @@ struct feat
unsigned int *count,
hb_aat_layout_feature_type_t *features) const
{
unsigned int feature_count = featureNameCount;
if (count && *count)
if (count)
{
unsigned int len = MIN (feature_count - start_offset, *count);
for (unsigned int i = 0; i < len; i++)
features[i] = namesZ[i + start_offset].get_feature_type ();
*count = len;
+ namesZ.as_array (featureNameCount).sub_array (start_offset, count)
| hb_map (&FeatureName::get_feature_type)
| hb_sink (hb_array (features, *count))
;
}
return featureNameCount;
}
bool exposes_feature (hb_aat_layout_feature_type_t feature_type) const
{ return get_feature (feature_type).has_data (); }
const FeatureName& get_feature (hb_aat_layout_feature_type_t feature_type) const
{
return namesZ.bsearch (featureNameCount, feature_type);
}
{ return namesZ.bsearch (featureNameCount, feature_type); }
hb_ot_name_id_t get_feature_name_id (hb_aat_layout_feature_type_t feature) const
{ return get_feature (feature).get_feature_name_id (); }
@ -209,7 +214,7 @@ struct feat
SortedUnsizedArrayOf<FeatureName>
namesZ; /* The feature name array. */
public:
DEFINE_SIZE_STATIC (24);
DEFINE_SIZE_ARRAY (12, namesZ);
};
} /* namespace AAT */

View File

@ -51,10 +51,10 @@ struct ActionSubrecordHeader
return_trace (likely (c->check_struct (this)));
}
HBUINT16 actionClass; /* The JustClass value associated with this
HBUINT16 actionClass; /* The JustClass value associated with this
* ActionSubrecord. */
HBUINT16 actionType; /* The type of postcompensation action. */
HBUINT16 actionLength; /* Length of this ActionSubrecord record, which
HBUINT16 actionType; /* The type of postcompensation action. */
HBUINT16 actionLength; /* Length of this ActionSubrecord record, which
* must be a multiple of 4. */
public:
DEFINE_SIZE_STATIC (6);
@ -70,11 +70,11 @@ struct DecompositionAction
ActionSubrecordHeader
header;
Fixed lowerLimit; /* If the distance factor is less than this value,
HBFixed lowerLimit; /* If the distance factor is less than this value,
* then the ligature is decomposed. */
Fixed upperLimit; /* If the distance factor is greater than this value,
HBFixed upperLimit; /* If the distance factor is greater than this value,
* then the ligature is decomposed. */
HBUINT16 order; /* Numerical order in which this ligature will
HBUINT16 order; /* Numerical order in which this ligature will
* be decomposed; you may want infrequent ligatures
* to decompose before more frequent ones. The ligatures
* on the line of text will decompose in increasing
@ -100,7 +100,7 @@ struct UnconditionalAddGlyphAction
protected:
ActionSubrecordHeader
header;
GlyphID addGlyph; /* Glyph that should be added if the distance factor
HBGlyphID addGlyph; /* Glyph that should be added if the distance factor
* is growing. */
public:
@ -118,14 +118,14 @@ struct ConditionalAddGlyphAction
protected:
ActionSubrecordHeader
header;
Fixed substThreshold; /* Distance growth factor (in ems) at which
HBFixed substThreshold; /* Distance growth factor (in ems) at which
* this glyph is replaced and the growth factor
* recalculated. */
GlyphID addGlyph; /* Glyph to be added as kashida. If this value is
HBGlyphID addGlyph; /* Glyph to be added as kashida. If this value is
* 0xFFFF, no extra glyph will be added. Note that
* generally when a glyph is added, justification
* will need to be redone. */
GlyphID substGlyph; /* Glyph to be substituted for this glyph if the
HBGlyphID substGlyph; /* Glyph to be substituted for this glyph if the
* growth factor equals or exceeds the value of
* substThreshold. */
public:
@ -143,16 +143,16 @@ struct DuctileGlyphAction
protected:
ActionSubrecordHeader
header;
HBUINT32 variationAxis; /* The 4-byte tag identifying the ductile axis.
HBUINT32 variationAxis; /* The 4-byte tag identifying the ductile axis.
* This would normally be 0x64756374 ('duct'),
* but you may use any axis the font contains. */
Fixed minimumLimit; /* The lowest value for the ductility axis tha
HBFixed minimumLimit; /* The lowest value for the ductility axis tha
* still yields an acceptable appearance. Normally
* this will be 1.0. */
Fixed noStretchValue; /* This is the default value that corresponds to
HBFixed noStretchValue; /* This is the default value that corresponds to
* no change in appearance. Normally, this will
* be 1.0. */
Fixed maximumLimit; /* The highest value for the ductility axis that
HBFixed maximumLimit; /* The highest value for the ductility axis that
* still yields an acceptable appearance. */
public:
DEFINE_SIZE_STATIC (22);
@ -169,8 +169,8 @@ struct RepeatedAddGlyphAction
protected:
ActionSubrecordHeader
header;
HBUINT16 flags; /* Currently unused; set to 0. */
GlyphID glyph; /* Glyph that should be added if the distance factor
HBUINT16 flags; /* Currently unused; set to 0. */
HBGlyphID glyph; /* Glyph that should be added if the distance factor
* is growing. */
public:
DEFINE_SIZE_STATIC (10);
@ -271,14 +271,14 @@ struct JustWidthDeltaEntry
};
protected:
Fixed beforeGrowLimit;/* The ratio by which the advance width of the
HBFixed beforeGrowLimit;/* The ratio by which the advance width of the
* glyph is permitted to grow on the left or top side. */
Fixed beforeShrinkLimit;
HBFixed beforeShrinkLimit;
/* The ratio by which the advance width of the
* glyph is permitted to shrink on the left or top side. */
Fixed afterGrowLimit; /* The ratio by which the advance width of the glyph
HBFixed afterGrowLimit; /* The ratio by which the advance width of the glyph
* is permitted to shrink on the left or top side. */
Fixed afterShrinkLimit;
HBFixed afterShrinkLimit;
/* The ratio by which the advance width of the glyph
* is at most permitted to shrink on the right or
* bottom side. */
@ -309,7 +309,7 @@ struct WidthDeltaPair
public:
DEFINE_SIZE_STATIC (24);
};
typedef OT::LArrayOf<WidthDeltaPair> WidthDeltaCluster;
struct JustificationCategory
@ -361,7 +361,7 @@ struct JustificationHeader
OffsetTo<JustificationCategory>
justClassTable; /* Offset to the justification category state table. */
OffsetTo<WidthDeltaCluster>
wdcTable; /* Offset from start of justification table to start
wdcTable; /* Offset from start of justification table to start
* of the subtable containing the width delta factors
* for the glyphs in your font.
*
@ -371,8 +371,8 @@ struct JustificationHeader
* of postcompensation subtable (set to zero if none).
*
* The postcompensation subtable, if present in the font. */
Lookup<OffsetTo<WidthDeltaCluster> >
lookupTable; /* Lookup table associating glyphs with width delta
Lookup<OffsetTo<WidthDeltaCluster>>
lookupTable; /* Lookup table associating glyphs with width delta
* clusters. See the description of Width Delta Clusters
* table for details on how to interpret the lookup values. */
@ -397,7 +397,7 @@ struct just
protected:
FixedVersion<>version; /* Version of the justification table
* (0x00010000u for version 1.0). */
HBUINT16 format; /* Format of the justification table (set to 0). */
HBUINT16 format; /* Format of the justification table (set to 0). */
OffsetTo<JustificationHeader>
horizData; /* Byte offset from the start of the justification table
* to the header for tables that contain justification

View File

@ -82,8 +82,8 @@ struct KernPair
}
protected:
GlyphID left;
GlyphID right;
HBGlyphID left;
HBGlyphID right;
FWORD value;
public:
DEFINE_SIZE_STATIC (6);
@ -251,7 +251,7 @@ struct KerxSubTableFormat1
if (Format1EntryT::performAction (entry) && depth)
{
unsigned int tuple_count = MAX (1u, table->header.tuple_count ());
unsigned int tuple_count = hb_max (1u, table->header.tuple_count ());
unsigned int kern_idx = Format1EntryT::kernActionIndex (entry);
kern_idx = Types::byteOffsetToIndex (kern_idx, &table->machine, kernAction.arrayZ);
@ -392,7 +392,7 @@ struct KerxSubTableFormat2
const UnsizedArrayOf<FWORD> &arrayZ = this+array;
unsigned int kern_idx = l + r;
kern_idx = Types::offsetToIndex (kern_idx, this, &arrayZ);
kern_idx = Types::offsetToIndex (kern_idx, this, arrayZ.arrayZ);
const FWORD *v = &arrayZ[kern_idx];
if (unlikely (!v->sanitize (&c->sanitizer))) return 0;
@ -712,18 +712,18 @@ struct KerxSubTableFormat6
{
struct Long
{
LNNOffsetTo<Lookup<HBUINT32> > rowIndexTable;
LNNOffsetTo<Lookup<HBUINT32> > columnIndexTable;
LNNOffsetTo<UnsizedArrayOf<FWORD32> > array;
LNNOffsetTo<Lookup<HBUINT32>> rowIndexTable;
LNNOffsetTo<Lookup<HBUINT32>> columnIndexTable;
LNNOffsetTo<UnsizedArrayOf<FWORD32>> array;
} l;
struct Short
{
LNNOffsetTo<Lookup<HBUINT16> > rowIndexTable;
LNNOffsetTo<Lookup<HBUINT16> > columnIndexTable;
LNNOffsetTo<UnsizedArrayOf<FWORD> > array;
LNNOffsetTo<Lookup<HBUINT16>> rowIndexTable;
LNNOffsetTo<Lookup<HBUINT16>> columnIndexTable;
LNNOffsetTo<UnsizedArrayOf<FWORD>> array;
} s;
} u;
LNNOffsetTo<UnsizedArrayOf<FWORD> > vector;
LNNOffsetTo<UnsizedArrayOf<FWORD>> vector;
public:
DEFINE_SIZE_STATIC (KernSubTableHeader::static_size + 24);
};
@ -733,8 +733,8 @@ struct KerxSubTableHeader
{
typedef ExtendedTypes Types;
unsigned int tuple_count () const { return tupleCount; }
bool is_horizontal () const { return !(coverage & Vertical); }
unsigned tuple_count () const { return tupleCount; }
bool is_horizontal () const { return !(coverage & Vertical); }
enum Coverage
{
@ -771,17 +771,17 @@ struct KerxSubTable
unsigned int get_size () const { return u.header.length; }
unsigned int get_type () const { return u.header.coverage & u.header.SubtableType; }
template <typename context_t>
typename context_t::return_t dispatch (context_t *c) const
template <typename context_t, typename ...Ts>
typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
{
unsigned int subtable_type = get_type ();
TRACE_DISPATCH (this, subtable_type);
switch (subtable_type) {
case 0: return_trace (c->dispatch (u.format0));
case 1: return_trace (c->dispatch (u.format1));
case 2: return_trace (c->dispatch (u.format2));
case 4: return_trace (c->dispatch (u.format4));
case 6: return_trace (c->dispatch (u.format6));
case 0: return_trace (c->dispatch (u.format0, hb_forward<Ts> (ds)...));
case 1: return_trace (c->dispatch (u.format1, hb_forward<Ts> (ds)...));
case 2: return_trace (c->dispatch (u.format2, hb_forward<Ts> (ds)...));
case 4: return_trace (c->dispatch (u.format4, hb_forward<Ts> (ds)...));
case 6: return_trace (c->dispatch (u.format6, hb_forward<Ts> (ds)...));
default: return_trace (c->default_return_value ());
}
}
@ -830,7 +830,7 @@ struct KerxTable
for (unsigned int i = 0; i < count; i++)
{
if (st->get_type () == 1)
return true;
return true;
st = &StructAfter<SubTable> (*st);
}
return false;
@ -845,7 +845,7 @@ struct KerxTable
for (unsigned int i = 0; i < count; i++)
{
if (st->u.header.coverage & st->u.header.CrossStream)
return true;
return true;
st = &StructAfter<SubTable> (*st);
}
return false;
@ -862,7 +862,7 @@ struct KerxTable
{
if ((st->u.header.coverage & (st->u.header.Variation | st->u.header.CrossStream)) ||
!st->u.header.is_horizontal ())
continue;
continue;
v += st->get_kerning (left, right);
st = &StructAfter<SubTable> (*st);
}
@ -883,7 +883,7 @@ struct KerxTable
bool reverse;
if (!T::Types::extended && (st->u.header.coverage & st->u.header.Variation))
goto skip;
goto skip;
if (HB_DIRECTION_IS_HORIZONTAL (c->buffer->props.direction) != st->u.header.is_horizontal ())
goto skip;
@ -897,8 +897,8 @@ struct KerxTable
if (!seenCrossStream &&
(st->u.header.coverage & st->u.header.CrossStream))
{
/* Attach all glyphs into a chain. */
seenCrossStream = true;
/* Attach all glyphs into a chain. */
seenCrossStream = true;
hb_glyph_position_t *pos = c->buffer->pos;
unsigned int count = c->buffer->len;
for (unsigned int i = 0; i < count; i++)

View File

@ -38,6 +38,80 @@ namespace AAT {
typedef ArrayOf<HBINT16> LigCaretClassEntry;
struct lcarFormat0
{
unsigned int get_lig_carets (hb_font_t *font,
hb_direction_t direction,
hb_codepoint_t glyph,
unsigned int start_offset,
unsigned int *caret_count /* IN/OUT */,
hb_position_t *caret_array /* OUT */,
const void *base) const
{
const OffsetTo<LigCaretClassEntry>* entry_offset = lookupTable.get_value (glyph,
font->face->get_num_glyphs ());
const LigCaretClassEntry& array = entry_offset ? base+*entry_offset : Null (LigCaretClassEntry);
if (caret_count)
{
hb_array_t<const HBINT16> arr = array.sub_array (start_offset, caret_count);
for (unsigned int i = 0; i < arr.length; ++i)
caret_array[i] = font->em_scale_dir (arr[i], direction);
}
return array.len;
}
bool sanitize (hb_sanitize_context_t *c, const void *base) const
{
TRACE_SANITIZE (this);
return_trace (likely (c->check_struct (this) && lookupTable.sanitize (c, base)));
}
protected:
Lookup<OffsetTo<LigCaretClassEntry>>
lookupTable; /* data Lookup table associating glyphs */
public:
DEFINE_SIZE_MIN (2);
};
struct lcarFormat1
{
unsigned int get_lig_carets (hb_font_t *font,
hb_direction_t direction,
hb_codepoint_t glyph,
unsigned int start_offset,
unsigned int *caret_count /* IN/OUT */,
hb_position_t *caret_array /* OUT */,
const void *base) const
{
const OffsetTo<LigCaretClassEntry>* entry_offset = lookupTable.get_value (glyph,
font->face->get_num_glyphs ());
const LigCaretClassEntry& array = entry_offset ? base+*entry_offset : Null (LigCaretClassEntry);
if (caret_count)
{
hb_array_t<const HBINT16> arr = array.sub_array (start_offset, caret_count);
for (unsigned int i = 0; i < arr.length; ++i)
{
hb_position_t x = 0, y = 0;
font->get_glyph_contour_point_for_origin (glyph, arr[i], direction, &x, &y);
caret_array[i] = HB_DIRECTION_IS_HORIZONTAL (direction) ? x : y;
}
}
return array.len;
}
bool sanitize (hb_sanitize_context_t *c, const void *base) const
{
TRACE_SANITIZE (this);
return_trace (likely (c->check_struct (this) && lookupTable.sanitize (c, base)));
}
protected:
Lookup<OffsetTo<LigCaretClassEntry>>
lookupTable; /* data Lookup table associating glyphs */
public:
DEFINE_SIZE_MIN (2);
};
struct lcar
{
static constexpr hb_tag_t tableTag = HB_AAT_TAG_lcar;
@ -49,41 +123,36 @@ struct lcar
unsigned int *caret_count /* IN/OUT */,
hb_position_t *caret_array /* OUT */) const
{
const OffsetTo<LigCaretClassEntry>* entry_offset = lookup.get_value (glyph,
font->face->get_num_glyphs ());
const LigCaretClassEntry& array = entry_offset ? this+*entry_offset : Null (LigCaretClassEntry);
if (caret_count)
switch (format)
{
hb_array_t<const HBINT16> arr = array.sub_array (start_offset, caret_count);
unsigned int count = arr.length;
for (unsigned int i = 0; i < count; ++i)
switch (format)
{
case 0: caret_array[i] = font->em_scale_dir (arr[i], direction); break;
case 1:
hb_position_t x, y;
font->get_glyph_contour_point_for_origin (glyph, arr[i], direction, &x, &y);
caret_array[i] = HB_DIRECTION_IS_HORIZONTAL (direction) ? x : y;
break;
}
case 0: return u.format0.get_lig_carets (font, direction, glyph, start_offset,
caret_count, caret_array, this);
case 1: return u.format1.get_lig_carets (font, direction, glyph, start_offset,
caret_count, caret_array, this);
default:if (caret_count) *caret_count = 0; return 0;
}
return array.len;
}
bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
return_trace (likely (c->check_struct (this) &&
version.major == 1 &&
lookup.sanitize (c, this)));
if (unlikely (!c->check_struct (this) || version.major != 1))
return_trace (false);
switch (format) {
case 0: return_trace (u.format0.sanitize (c, this));
case 1: return_trace (u.format1.sanitize (c, this));
default:return_trace (true);
}
}
protected:
FixedVersion<>version; /* Version number of the ligature caret table */
HBUINT16 format; /* Format of the ligature caret table. */
Lookup<OffsetTo<LigCaretClassEntry> >
lookup; /* data Lookup table associating glyphs */
union {
lcarFormat0 format0;
lcarFormat0 format1;
} u;
public:
DEFINE_SIZE_MIN (8);
};

View File

@ -88,7 +88,7 @@ struct RearrangementSubtable
start = buffer->idx;
if (flags & MarkLast)
end = MIN (buffer->idx + 1, buffer->len);
end = hb_min (buffer->idx + 1, buffer->len);
if ((flags & Verb) && start < end)
{
@ -117,14 +117,14 @@ struct RearrangementSubtable
};
unsigned int m = map[flags & Verb];
unsigned int l = MIN<unsigned int> (2, m >> 4);
unsigned int r = MIN<unsigned int> (2, m & 0x0F);
unsigned int l = hb_min (2u, m >> 4);
unsigned int r = hb_min (2u, m & 0x0F);
bool reverse_l = 3 == (m >> 4);
bool reverse_r = 3 == (m & 0x0F);
if (end - start >= l + r)
{
buffer->merge_clusters (start, MIN (buffer->idx + 1, buffer->len));
buffer->merge_clusters (start, hb_min (buffer->idx + 1, buffer->len));
buffer->merge_clusters (start, end);
hb_glyph_info_t *info = buffer->info;
@ -226,7 +226,7 @@ struct ContextualSubtable
hb_buffer_t *buffer = driver->buffer;
if (buffer->idx == buffer->len && !mark_set)
return false;
return false;
return entry.data.markIndex != 0xFFFF || entry.data.currentIndex != 0xFFFF;
}
@ -238,48 +238,48 @@ struct ContextualSubtable
/* 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;
return;
const GlyphID *replacement;
const HBGlyphID *replacement;
replacement = nullptr;
if (Types::extended)
{
if (entry.data.markIndex != 0xFFFF)
{
const Lookup<GlyphID> &lookup = subs[entry.data.markIndex];
const Lookup<HBGlyphID> &lookup = subs[entry.data.markIndex];
replacement = lookup.get_value (buffer->info[mark].codepoint, driver->num_glyphs);
}
}
else
{
unsigned int offset = entry.data.markIndex + buffer->info[mark].codepoint;
const UnsizedArrayOf<GlyphID> &subs_old = (const UnsizedArrayOf<GlyphID> &) subs;
const UnsizedArrayOf<HBGlyphID> &subs_old = (const UnsizedArrayOf<HBGlyphID> &) subs;
replacement = &subs_old[Types::wordOffsetToIndex (offset, table, subs_old.arrayZ)];
if (!replacement->sanitize (&c->sanitizer) || !*replacement)
replacement = nullptr;
}
if (replacement)
{
buffer->unsafe_to_break (mark, MIN (buffer->idx + 1, buffer->len));
buffer->unsafe_to_break (mark, hb_min (buffer->idx + 1, buffer->len));
buffer->info[mark].codepoint = *replacement;
ret = true;
}
replacement = nullptr;
unsigned int idx = MIN (buffer->idx, buffer->len - 1);
unsigned int idx = hb_min (buffer->idx, buffer->len - 1);
if (Types::extended)
{
if (entry.data.currentIndex != 0xFFFF)
{
const Lookup<GlyphID> &lookup = subs[entry.data.currentIndex];
const Lookup<HBGlyphID> &lookup = subs[entry.data.currentIndex];
replacement = lookup.get_value (buffer->info[idx].codepoint, driver->num_glyphs);
}
}
else
{
unsigned int offset = entry.data.currentIndex + buffer->info[idx].codepoint;
const UnsizedArrayOf<GlyphID> &subs_old = (const UnsizedArrayOf<GlyphID> &) subs;
const UnsizedArrayOf<HBGlyphID> &subs_old = (const UnsizedArrayOf<HBGlyphID> &) subs;
replacement = &subs_old[Types::wordOffsetToIndex (offset, table, subs_old.arrayZ)];
if (!replacement->sanitize (&c->sanitizer) || !*replacement)
replacement = nullptr;
@ -304,7 +304,7 @@ struct ContextualSubtable
bool mark_set;
unsigned int mark;
const ContextualSubtable *table;
const UnsizedOffsetListOf<Lookup<GlyphID>, HBUINT, false> &subs;
const UnsizedOffsetListOf<Lookup<HBGlyphID>, HBUINT, false> &subs;
};
bool apply (hb_aat_apply_context_t *c) const
@ -337,9 +337,9 @@ struct ContextualSubtable
const EntryData &data = entries[i].data;
if (data.markIndex != 0xFFFF)
num_lookups = MAX<unsigned int> (num_lookups, 1 + data.markIndex);
num_lookups = hb_max (num_lookups, 1 + data.markIndex);
if (data.currentIndex != 0xFFFF)
num_lookups = MAX<unsigned int> (num_lookups, 1 + data.currentIndex);
num_lookups = hb_max (num_lookups, 1 + data.currentIndex);
}
return_trace (substitutionTables.sanitize (c, this, num_lookups));
@ -348,7 +348,7 @@ struct ContextualSubtable
protected:
StateTable<Types, EntryData>
machine;
NNOffsetTo<UnsizedOffsetListOf<Lookup<GlyphID>, HBUINT, false>, HBUINT>
NNOffsetTo<UnsizedOffsetListOf<Lookup<HBGlyphID>, HBUINT, false>, HBUINT>
substitutionTables;
public:
DEFINE_SIZE_STATIC (20);
@ -488,7 +488,7 @@ struct LigatureSubtable
unsigned int ligature_idx = 0;
unsigned int action;
do
do
{
if (unlikely (!cursor))
{
@ -520,7 +520,7 @@ struct LigatureSubtable
if (action & (LigActionStore | LigActionLast))
{
ligature_idx = Types::offsetToIndex (ligature_idx, table, ligature.arrayZ);
const GlyphID &ligatureData = ligature[ligature_idx];
const HBGlyphID &ligatureData = ligature[ligature_idx];
if (unlikely (!ligatureData.sanitize (&c->sanitizer))) break;
hb_codepoint_t lig = ligatureData;
@ -554,7 +554,7 @@ struct LigatureSubtable
const LigatureSubtable *table;
const UnsizedArrayOf<HBUINT32> &ligAction;
const UnsizedArrayOf<HBUINT16> &component;
const UnsizedArrayOf<GlyphID> &ligature;
const UnsizedArrayOf<HBGlyphID> &ligature;
unsigned int match_length;
unsigned int match_positions[HB_MAX_CONTEXT_LENGTH];
};
@ -586,7 +586,7 @@ struct LigatureSubtable
ligAction; /* Offset to the ligature action table. */
NNOffsetTo<UnsizedArrayOf<HBUINT16>, HBUINT>
component; /* Offset to the component table. */
NNOffsetTo<UnsizedArrayOf<GlyphID>, HBUINT>
NNOffsetTo<UnsizedArrayOf<HBGlyphID>, HBUINT>
ligature; /* Offset to the actual ligature lists. */
public:
DEFINE_SIZE_STATIC (28);
@ -606,7 +606,7 @@ struct NoncontextualSubtable
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);
const HBGlyphID *replacement = substitute.get_value (info[i].codepoint, num_glyphs);
if (replacement)
{
info[i].codepoint = *replacement;
@ -624,7 +624,7 @@ struct NoncontextualSubtable
}
protected:
Lookup<GlyphID> substitute;
Lookup<HBGlyphID> substitute;
public:
DEFINE_SIZE_MIN (2);
};
@ -726,7 +726,7 @@ struct InsertionSubtable
{
unsigned int count = (flags & MarkedInsertCount);
unsigned int start = entry.data.markedInsertIndex;
const GlyphID *glyphs = &insertionAction[start];
const HBGlyphID *glyphs = &insertionAction[start];
if (unlikely (!c->sanitizer.check_array (glyphs, count))) count = 0;
bool before = flags & MarkedInsertBefore;
@ -744,7 +744,7 @@ struct InsertionSubtable
buffer->move_to (end + count);
buffer->unsafe_to_break_from_outbuffer (mark, MIN (buffer->idx + 1, buffer->len));
buffer->unsafe_to_break_from_outbuffer (mark, hb_min (buffer->idx + 1, buffer->len));
}
if (flags & SetMark)
@ -754,7 +754,7 @@ struct InsertionSubtable
{
unsigned int count = (flags & CurrentInsertCount) >> 5;
unsigned int start = entry.data.currentInsertIndex;
const GlyphID *glyphs = &insertionAction[start];
const HBGlyphID *glyphs = &insertionAction[start];
if (unlikely (!c->sanitizer.check_array (glyphs, count))) count = 0;
bool before = flags & CurrentInsertBefore;
@ -793,7 +793,7 @@ struct InsertionSubtable
private:
hb_aat_apply_context_t *c;
unsigned int mark;
const UnsizedArrayOf<GlyphID> &insertionAction;
const UnsizedArrayOf<HBGlyphID> &insertionAction;
};
bool apply (hb_aat_apply_context_t *c) const
@ -819,7 +819,7 @@ struct InsertionSubtable
protected:
StateTable<Types, EntryData>
machine;
NNOffsetTo<UnsizedArrayOf<GlyphID>, HBUINT>
NNOffsetTo<UnsizedArrayOf<HBGlyphID>, HBUINT>
insertionAction; /* Byte offset from stateHeader to the start of
* the insertion glyph table. */
public:
@ -883,17 +883,17 @@ struct ChainSubtable
Insertion = 5
};
template <typename context_t>
typename context_t::return_t dispatch (context_t *c) const
template <typename context_t, typename ...Ts>
typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
{
unsigned int subtable_type = get_type ();
TRACE_DISPATCH (this, subtable_type);
switch (subtable_type) {
case Rearrangement: return_trace (c->dispatch (u.rearrangement));
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));
case Rearrangement: return_trace (c->dispatch (u.rearrangement, hb_forward<Ts> (ds)...));
case Contextual: return_trace (c->dispatch (u.contextual, hb_forward<Ts> (ds)...));
case Ligature: return_trace (c->dispatch (u.ligature, hb_forward<Ts> (ds)...));
case Noncontextual: return_trace (c->dispatch (u.noncontextual, hb_forward<Ts> (ds)...));
case Insertion: return_trace (c->dispatch (u.insertion, hb_forward<Ts> (ds)...));
default: return_trace (c->default_return_value ());
}
}
@ -948,8 +948,10 @@ struct Chain
hb_aat_layout_feature_type_t type = (hb_aat_layout_feature_type_t) (unsigned int) feature.featureType;
hb_aat_layout_feature_selector_t setting = (hb_aat_layout_feature_selector_t) (unsigned int) feature.featureSetting;
retry:
const hb_aat_map_builder_t::feature_info_t *info = map->features.bsearch (type);
if (info && info->setting == setting)
// Check whether this type/setting pair was requested in the map, and if so, apply its flags.
// (The search here only looks at the type and setting fields of feature_info_t.)
hb_aat_map_builder_t::feature_info_t info = { type, setting, false, 0 };
if (map->features.bsearch (info))
{
flags &= feature.disableFlags;
flags |= feature.enableFlags;
@ -969,19 +971,19 @@ struct Chain
void apply (hb_aat_apply_context_t *c,
hb_mask_t flags) const
{
const ChainSubtable<Types> *subtable = &StructAfter<ChainSubtable<Types> > (featureZ.as_array (featureCount));
const ChainSubtable<Types> *subtable = &StructAfter<ChainSubtable<Types>> (featureZ.as_array (featureCount));
unsigned int count = subtableCount;
for (unsigned int i = 0; i < count; i++)
{
bool reverse;
if (!(subtable->subFeatureFlags & flags))
goto skip;
goto skip;
if (!(subtable->get_coverage() & ChainSubtable<Types>::AllDirections) &&
HB_DIRECTION_IS_VERTICAL (c->buffer->props.direction) !=
bool (subtable->get_coverage() & ChainSubtable<Types>::Vertical))
goto skip;
goto skip;
/* Buffer contents is always in logical direction. Determine if
* we need to reverse before applying this subtable. We reverse
@ -1016,22 +1018,22 @@ struct Chain
HB_DIRECTION_IS_BACKWARD (c->buffer->props.direction);
if (!c->buffer->message (c->font, "start chain subtable %d", c->lookup_index))
goto skip;
goto skip;
if (reverse)
c->buffer->reverse ();
c->buffer->reverse ();
subtable->apply (c);
if (reverse)
c->buffer->reverse ();
c->buffer->reverse ();
(void) c->buffer->message (c->font, "end chain subtable %d", c->lookup_index);
if (unlikely (!c->buffer->successful)) return;
skip:
subtable = &StructAfter<ChainSubtable<Types> > (*subtable);
subtable = &StructAfter<ChainSubtable<Types>> (*subtable);
c->set_lookup_index (c->lookup_index + 1);
}
}
@ -1049,13 +1051,13 @@ struct Chain
if (!c->check_array (featureZ.arrayZ, featureCount))
return_trace (false);
const ChainSubtable<Types> *subtable = &StructAfter<ChainSubtable<Types> > (featureZ.as_array (featureCount));
const ChainSubtable<Types> *subtable = &StructAfter<ChainSubtable<Types>> (featureZ.as_array (featureCount));
unsigned int count = subtableCount;
for (unsigned int i = 0; i < count; i++)
{
if (!subtable->sanitize (c))
return_trace (false);
subtable = &StructAfter<ChainSubtable<Types> > (*subtable);
subtable = &StructAfter<ChainSubtable<Types>> (*subtable);
}
return_trace (true);
@ -1080,10 +1082,10 @@ struct Chain
* The 'mort'/'morx' Table
*/
template <typename Types>
template <typename Types, hb_tag_t TAG>
struct mortmorx
{
static constexpr hb_tag_t tableTag = HB_AAT_TAG_morx;
static constexpr hb_tag_t tableTag = TAG;
bool has_data () const { return version != 0; }
@ -1095,7 +1097,7 @@ struct mortmorx
for (unsigned int i = 0; i < count; i++)
{
map->chain_flags.push (chain->compile_flags (mapper));
chain = &StructAfter<Chain<Types> > (*chain);
chain = &StructAfter<Chain<Types>> (*chain);
}
}
@ -1109,7 +1111,7 @@ struct mortmorx
{
chain->apply (c, c->plan->aat_map.chain_flags[i]);
if (unlikely (!c->buffer->successful)) return;
chain = &StructAfter<Chain<Types> > (*chain);
chain = &StructAfter<Chain<Types>> (*chain);
}
}
@ -1125,7 +1127,7 @@ struct mortmorx
{
if (!chain->sanitize (c, version))
return_trace (false);
chain = &StructAfter<Chain<Types> > (*chain);
chain = &StructAfter<Chain<Types>> (*chain);
}
return_trace (true);
@ -1143,14 +1145,8 @@ struct mortmorx
DEFINE_SIZE_MIN (8);
};
struct morx : mortmorx<ExtendedTypes>
{
static constexpr hb_tag_t tableTag = HB_AAT_TAG_morx;
};
struct mort : mortmorx<ObsoleteTypes>
{
static constexpr hb_tag_t tableTag = HB_AAT_TAG_mort;
};
struct morx : mortmorx<ExtendedTypes, HB_AAT_TAG_morx> {};
struct mort : mortmorx<ObsoleteTypes, HB_AAT_TAG_mort> {};
} /* namespace AAT */

View File

@ -0,0 +1,173 @@
/*
* Copyright © 2019 Ebrahim Byagowi
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*/
#ifndef HB_AAT_LAYOUT_OPBD_TABLE_HH
#define HB_AAT_LAYOUT_OPBD_TABLE_HH
#include "hb-aat-layout-common.hh"
#include "hb-open-type.hh"
/*
* opbd -- Optical Bounds
* https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6opbd.html
*/
#define HB_AAT_TAG_opbd HB_TAG('o','p','b','d')
namespace AAT {
struct OpticalBounds
{
bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
return_trace (likely (c->check_struct (this)));
}
FWORD leftSide;
FWORD topSide;
FWORD rightSide;
FWORD bottomSide;
public:
DEFINE_SIZE_STATIC (8);
};
struct opbdFormat0
{
bool get_bounds (hb_font_t *font, hb_codepoint_t glyph_id,
hb_glyph_extents_t *extents, const void *base) const
{
const OffsetTo<OpticalBounds> *bounds_offset = lookupTable.get_value (glyph_id, font->face->get_num_glyphs ());
if (!bounds_offset) return false;
const OpticalBounds &bounds = base+*bounds_offset;
if (extents)
*extents = {
font->em_scale_x (bounds.leftSide),
font->em_scale_y (bounds.topSide),
font->em_scale_x (bounds.rightSide),
font->em_scale_y (bounds.bottomSide)
};
return true;
}
bool sanitize (hb_sanitize_context_t *c, const void *base) const
{
TRACE_SANITIZE (this);
return_trace (likely (c->check_struct (this) && lookupTable.sanitize (c, base)));
}
protected:
Lookup<OffsetTo<OpticalBounds>>
lookupTable; /* Lookup table associating glyphs with the four
* int16 values for the left-side, top-side,
* right-side, and bottom-side optical bounds. */
public:
DEFINE_SIZE_MIN (2);
};
struct opbdFormat1
{
bool get_bounds (hb_font_t *font, hb_codepoint_t glyph_id,
hb_glyph_extents_t *extents, const void *base) const
{
const OffsetTo<OpticalBounds> *bounds_offset = lookupTable.get_value (glyph_id, font->face->get_num_glyphs ());
if (!bounds_offset) return false;
const OpticalBounds &bounds = base+*bounds_offset;
hb_position_t left = 0, top = 0, right = 0, bottom = 0, ignore;
if (font->get_glyph_contour_point (glyph_id, bounds.leftSide, &left, &ignore) ||
font->get_glyph_contour_point (glyph_id, bounds.topSide, &ignore, &top) ||
font->get_glyph_contour_point (glyph_id, bounds.rightSide, &right, &ignore) ||
font->get_glyph_contour_point (glyph_id, bounds.bottomSide, &ignore, &bottom))
{
if (extents)
*extents = {left, top, right, bottom};
return true;
}
return false;
}
bool sanitize (hb_sanitize_context_t *c, const void *base) const
{
TRACE_SANITIZE (this);
return_trace (likely (c->check_struct (this) && lookupTable.sanitize (c, base)));
}
protected:
Lookup<OffsetTo<OpticalBounds>>
lookupTable; /* Lookup table associating glyphs with the four
* int16 values for the left-side, top-side,
* right-side, and bottom-side optical bounds. */
public:
DEFINE_SIZE_MIN (2);
};
struct opbd
{
static constexpr hb_tag_t tableTag = HB_AAT_TAG_opbd;
bool get_bounds (hb_font_t *font, hb_codepoint_t glyph_id,
hb_glyph_extents_t *extents) const
{
switch (format)
{
case 0: return u.format0.get_bounds (font, glyph_id, extents, this);
case 1: return u.format1.get_bounds (font, glyph_id, extents, this);
default:return false;
}
}
bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
if (unlikely (!c->check_struct (this) || version.major != 1))
return_trace (false);
switch (format)
{
case 0: return_trace (u.format0.sanitize (c, this));
case 1: return_trace (u.format1.sanitize (c, this));
default:return_trace (true);
}
}
protected:
FixedVersion<>version; /* Version number of the optical bounds
* table (0x00010000 for the current version). */
HBUINT16 format; /* Format of the optical bounds table.
* Format 0 indicates distance and Format 1 indicates
* control point. */
union {
opbdFormat0 format0;
opbdFormat1 format1;
} u;
public:
DEFINE_SIZE_MIN (8);
};
} /* namespace AAT */
#endif /* HB_AAT_LAYOUT_OPBD_TABLE_HH */

View File

@ -62,11 +62,11 @@ struct TrackTableEntry
}
protected:
Fixed track; /* Track value for this record. */
HBFixed track; /* Track value for this record. */
NameID trackNameID; /* The 'name' table index for this track.
* (a short word or phrase like "loose"
* or "very tight") */
NNOffsetTo<UnsizedArrayOf<FWORD> >
NNOffsetTo<UnsizedArrayOf<FWORD>>
valuesZ; /* Offset from start of tracking table to
* per-size tracking values for this track. */
@ -82,7 +82,7 @@ struct TrackData
const void *base) const
{
unsigned int sizes = nSizes;
hb_array_t<const Fixed> size_table ((base+sizeTable).arrayZ, sizes);
hb_array_t<const HBFixed> size_table ((base+sizeTable).arrayZ, sizes);
float s0 = size_table[idx].to_float ();
float s1 = size_table[idx + 1].to_float ();
@ -93,13 +93,6 @@ struct TrackData
int 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;
/*
* Choose track.
*/
@ -127,14 +120,14 @@ struct TrackData
if (!sizes) return 0.;
if (sizes == 1) return trackTableEntry->get_value (base, 0, sizes);
hb_array_t<const Fixed> size_table ((base+sizeTable).arrayZ, sizes);
hb_array_t<const HBFixed> size_table ((base+sizeTable).arrayZ, sizes);
unsigned int size_index;
for (size_index = 0; size_index < sizes - 1; size_index++)
if (size_table[size_index].to_float () >= csspx)
break;
if (size_table[size_index].to_float () >= ptem)
break;
return round (interpolate_at (size_index ? size_index - 1 : 0, csspx,
*trackTableEntry, base));
return roundf (interpolate_at (size_index ? size_index - 1 : 0, ptem,
*trackTableEntry, base));
}
bool sanitize (hb_sanitize_context_t *c, const void *base) const
@ -148,7 +141,7 @@ struct TrackData
protected:
HBUINT16 nTracks; /* Number of separate tracks included in this table. */
HBUINT16 nSizes; /* Number of point sizes included in this table. */
LOffsetTo<UnsizedArrayOf<Fixed>, false>
LNNOffsetTo<UnsizedArrayOf<HBFixed>>
sizeTable; /* Offset from start of the tracking table to
* Array[nSizes] of size values.. */
UnsizedArrayOf<TrackTableEntry>
@ -183,7 +176,7 @@ struct trak
hb_position_t advance_to_add = c->font->em_scalef_x (tracking);
foreach_grapheme (buffer, start, end)
{
if (!(buffer->info[start].mask & trak_mask)) continue;
if (!(buffer->info[start].mask & trak_mask)) continue;
buffer->pos[start].x_advance += advance_to_add;
buffer->pos[start].x_offset += offset_to_add;
}
@ -196,7 +189,7 @@ struct trak
hb_position_t advance_to_add = c->font->em_scalef_y (tracking);
foreach_grapheme (buffer, start, end)
{
if (!(buffer->info[start].mask & trak_mask)) continue;
if (!(buffer->info[start].mask & trak_mask)) continue;
buffer->pos[start].y_advance += advance_to_add;
buffer->pos[start].y_offset += offset_to_add;
}
@ -217,8 +210,8 @@ struct trak
protected:
FixedVersion<>version; /* Version of the tracking table
* (0x00010000u for version 1.0). */
HBUINT16 format; /* Format of the tracking table (set to 0). */
* (0x00010000u for version 1.0). */
HBUINT16 format; /* Format of the tracking table (set to 0). */
OffsetTo<TrackData>
horizData; /* Offset from start of tracking table to TrackData
* for horizontal text (or 0 if none). */

View File

@ -25,9 +25,8 @@
* Google Author(s): Behdad Esfahbod
*/
#include "hb-open-type.hh"
#include "hb.hh"
#include "hb-ot-face.hh"
#include "hb-aat-layout.hh"
#include "hb-aat-fdsc-table.hh" // Just so we compile it; unused otherwise.
#include "hb-aat-layout-ankr-table.hh"
@ -40,6 +39,42 @@
#include "hb-aat-ltag-table.hh"
/*
* hb_aat_apply_context_t
*/
/* Note: This context is used for kerning, even without AAT, hence the condition. */
#if !defined(HB_NO_AAT) || !defined(HB_NO_OT_KERN)
AAT::hb_aat_apply_context_t::hb_aat_apply_context_t (const hb_ot_shape_plan_t *plan_,
hb_font_t *font_,
hb_buffer_t *buffer_,
hb_blob_t *blob) :
plan (plan_),
font (font_),
face (font->face),
buffer (buffer_),
sanitizer (),
ankr_table (&Null (AAT::ankr)),
lookup_index (0),
debug_depth (0)
{
sanitizer.init (blob);
sanitizer.set_num_glyphs (face->get_num_glyphs ());
sanitizer.start_processing ();
sanitizer.set_max_ops (HB_SANITIZE_MAX_OPS_MAX);
}
AAT::hb_aat_apply_context_t::~hb_aat_apply_context_t ()
{ sanitizer.end_processing (); }
void
AAT::hb_aat_apply_context_t::set_ankr_table (const AAT::ankr *ankr_table_)
{ ankr_table = ankr_table_; }
#endif
/**
* SECTION:hb-aat-layout
* @title: hb-aat-layout
@ -50,6 +85,8 @@
**/
#if !defined(HB_NO_AAT) || defined(HAVE_CORETEXT)
/* Table data courtesy of Apple. Converted from mnemonics to integers
* when moving to this file. */
static const hb_aat_feature_mapping_t feature_mappings[] =
@ -135,44 +172,12 @@ static const hb_aat_feature_mapping_t feature_mappings[] =
const hb_aat_feature_mapping_t *
hb_aat_layout_find_feature_mapping (hb_tag_t tag)
{
return (const hb_aat_feature_mapping_t *) bsearch (&tag,
feature_mappings,
ARRAY_LENGTH (feature_mappings),
sizeof (feature_mappings[0]),
hb_aat_feature_mapping_t::cmp);
return hb_sorted_array (feature_mappings).bsearch (tag);
}
#endif
/*
* hb_aat_apply_context_t
*/
AAT::hb_aat_apply_context_t::hb_aat_apply_context_t (const hb_ot_shape_plan_t *plan_,
hb_font_t *font_,
hb_buffer_t *buffer_,
hb_blob_t *blob) :
plan (plan_),
font (font_),
face (font->face),
buffer (buffer_),
sanitizer (),
ankr_table (&Null(AAT::ankr)),
lookup_index (0),
debug_depth (0)
{
sanitizer.init (blob);
sanitizer.set_num_glyphs (face->get_num_glyphs ());
sanitizer.start_processing ();
sanitizer.set_max_ops (HB_SANITIZE_MAX_OPS_MAX);
}
AAT::hb_aat_apply_context_t::~hb_aat_apply_context_t ()
{ sanitizer.end_processing (); }
void
AAT::hb_aat_apply_context_t::set_ankr_table (const AAT::ankr *ankr_table_)
{ ankr_table = ankr_table_; }
#ifndef HB_NO_AAT
/*
* mort/morx/kerx/trak
@ -311,14 +316,6 @@ hb_aat_layout_track (const hb_ot_shape_plan_t *plan,
trak.apply (&c);
}
hb_language_t
_hb_aat_language_get (hb_face_t *face,
unsigned int i)
{
return face->table.ltag->get_language (i);
}
/**
* hb_aat_layout_get_feature_types:
* @face: a face object
@ -382,3 +379,6 @@ hb_aat_layout_feature_type_get_selector_infos (hb_face_t
{
return face->table.feat->get_selector_infos (feature_type, start_offset, selector_count, selectors, default_index);
}
#endif

View File

@ -85,7 +85,7 @@ typedef enum
HB_AAT_LAYOUT_FEATURE_TYPE_LANGUAGE_TAG_TYPE = 39,
HB_AAT_LAYOUT_FEATURE_TYPE_CJK_ROMAN_SPACING_TYPE = 103,
_HB_AAT_LAYOUT_FEATURE_TYPE_MAX_VALUE= 0x7FFFFFFFu, /*< skip >*/
_HB_AAT_LAYOUT_FEATURE_TYPE_MAX_VALUE = HB_TAG_MAX_SIGNED /*< skip >*/
} hb_aat_layout_feature_type_t;
/**
@ -424,7 +424,7 @@ typedef enum
HB_AAT_LAYOUT_FEATURE_SELECTOR_DEFAULT_CJK_ROMAN = 2,
HB_AAT_LAYOUT_FEATURE_SELECTOR_FULL_WIDTH_CJK_ROMAN = 3,
_HB_AAT_LAYOUT_FEATURE_SELECTOR_MAX_VALUE= 0x7FFFFFFFu, /*< skip >*/
_HB_AAT_LAYOUT_FEATURE_SELECTOR_MAX_VALUE = HB_TAG_MAX_SIGNED /*< skip >*/
} hb_aat_layout_feature_selector_t;
HB_EXTERN unsigned int

View File

@ -30,7 +30,7 @@
#include "hb.hh"
#include "hb-ot-shape.hh"
#include "hb-aat-ltag-table.hh"
struct hb_aat_feature_mapping_t
{
@ -39,14 +39,8 @@ struct hb_aat_feature_mapping_t
hb_aat_layout_feature_selector_t selectorToEnable;
hb_aat_layout_feature_selector_t selectorToDisable;
HB_INTERNAL static int cmp (const void *key_, const void *entry_)
{
hb_tag_t key = * (unsigned int *) key_;
const hb_aat_feature_mapping_t * entry = (const hb_aat_feature_mapping_t *) entry_;
return key < entry->otFeatureTag ? -1 :
key > entry->otFeatureTag ? 1 :
0;
}
int cmp (hb_tag_t key) const
{ return key < otFeatureTag ? -1 : key > otFeatureTag ? 1 : 0; }
};
HB_INTERNAL const hb_aat_feature_mapping_t *
@ -77,9 +71,5 @@ hb_aat_layout_track (const hb_ot_shape_plan_t *plan,
hb_font_t *font,
hb_buffer_t *buffer);
HB_INTERNAL hb_language_t
_hb_aat_language_get (hb_face_t *face,
unsigned int i);
#endif /* HB_AAT_LAYOUT_HH */

View File

@ -50,7 +50,7 @@ struct FTStringRange
}
protected:
NNOffsetTo<UnsizedArrayOf<HBUINT8> >
NNOffsetTo<UnsizedArrayOf<HBUINT8>>
tag; /* Offset from the start of the table to
* the beginning of the string */
HBUINT16 length; /* String length (in bytes) */

View File

@ -26,28 +26,55 @@
* Google Author(s): Behdad Esfahbod
*/
#include "hb.hh"
#ifndef HB_NO_AAT_SHAPE
#include "hb-aat-map.hh"
#include "hb-aat-layout.hh"
#include "hb-aat-layout-feat-table.hh"
void hb_aat_map_builder_t::add_feature (hb_tag_t tag,
unsigned int value)
void hb_aat_map_builder_t::add_feature (hb_tag_t tag, unsigned value)
{
if (!face->table.feat->has_data ()) return;
if (tag == HB_TAG ('a','a','l','t'))
{
if (!face->table.feat->exposes_feature (HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_ALTERNATIVES))
return;
feature_info_t *info = features.push();
info->type = HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_ALTERNATIVES;
info->setting = (hb_aat_layout_feature_selector_t) value;
info->seq = features.length;
info->is_exclusive = true;
return;
}
const hb_aat_feature_mapping_t *mapping = hb_aat_layout_find_feature_mapping (tag);
if (!mapping) return;
const AAT::FeatureName* feature = &face->table.feat->get_feature (mapping->aatFeatureType);
if (!feature->has_data ())
{
/* Special case: Chain::compile_flags will fall back to the deprecated version of
* small-caps if necessary, so we need to check for that possibility.
* https://github.com/harfbuzz/harfbuzz/issues/2307 */
if (mapping->aatFeatureType == HB_AAT_LAYOUT_FEATURE_TYPE_LOWER_CASE &&
mapping->selectorToEnable == HB_AAT_LAYOUT_FEATURE_SELECTOR_LOWER_CASE_SMALL_CAPS)
{
feature = &face->table.feat->get_feature (HB_AAT_LAYOUT_FEATURE_TYPE_LETTER_CASE);
if (!feature->has_data ()) return;
}
else return;
}
feature_info_t *info = features.push();
info->type = mapping->aatFeatureType;
info->setting = value ? mapping->selectorToEnable : mapping->selectorToDisable;
info->seq = features.length;
info->is_exclusive = feature->is_exclusive ();
}
void
@ -59,10 +86,17 @@ hb_aat_map_builder_t::compile (hb_aat_map_t &m)
features.qsort ();
unsigned int j = 0;
for (unsigned int i = 1; i < features.length; i++)
if (features[i].type != features[j].type)
if (features[i].type != features[j].type ||
/* Nonexclusive feature selectors come in even/odd pairs to turn a setting on/off
* respectively, so we mask out the low-order bit when checking for "duplicates"
* (selectors referring to the same feature setting) here. */
(!features[i].is_exclusive && ((features[i].setting & ~1) != (features[j].setting & ~1))))
features[++j] = features[i];
features.shrink (j + 1);
}
hb_aat_layout_compile_map (this, &m);
}
#endif

View File

@ -64,19 +64,24 @@ struct hb_aat_map_builder_t
{
hb_aat_layout_feature_type_t type;
hb_aat_layout_feature_selector_t setting;
bool is_exclusive;
unsigned seq; /* For stable sorting only. */
HB_INTERNAL static int cmp (const void *pa, const void *pb)
{
const feature_info_t *a = (const feature_info_t *) pa;
const feature_info_t *b = (const feature_info_t *) pb;
return (a->type != b->type) ? (a->type < b->type ? -1 : 1) :
(a->seq < b->seq ? -1 : a->seq > b->seq ? 1 : 0);
if (a->type != b->type) return (a->type < b->type ? -1 : 1);
if (!a->is_exclusive &&
(a->setting & ~1) != (b->setting & ~1)) return (a->setting < b->setting ? -1 : 1);
return (a->seq < b->seq ? -1 : a->seq > b->seq ? 1 : 0);
}
int cmp (hb_aat_layout_feature_type_t ty) const
/* compares type & setting only, not is_exclusive flag or seq number */
int cmp (const feature_info_t& f) const
{
return (type != ty) ? (type < ty ? -1 : 1) : 0;
return (f.type != type) ? (f.type < type ? -1 : 1) :
(f.setting != setting) ? (f.setting < setting ? -1 : 1) : 0;
}
};

File diff suppressed because it is too large Load Diff

View File

@ -42,19 +42,21 @@ struct hb_array_t : hb_iter_with_fallback_t<hb_array_t<Type>, Type&>
/*
* Constructors.
*/
hb_array_t () : arrayZ (nullptr), length (0) {}
hb_array_t (const hb_array_t<Type> &o) : arrayZ (o.arrayZ), length (o.length) {}
template <typename U = Type, hb_enable_if (hb_is_const (U))>
hb_array_t (const hb_array_t<hb_remove_const (Type)> &o) : arrayZ (o.arrayZ), length (o.length) {}
hb_array_t () : arrayZ (nullptr), length (0), backwards_length (0) {}
hb_array_t (Type *array_, unsigned int length_) : arrayZ (array_), length (length_), backwards_length (0) {}
template <unsigned int length_>
hb_array_t (Type (&array_)[length_]) : arrayZ (array_), length (length_), backwards_length (0) {}
hb_array_t (Type *array_, unsigned int length_) : arrayZ (array_), length (length_) {}
template <unsigned int length_> hb_array_t (Type (&array_)[length_]) : arrayZ (array_), length (length_) {}
template <typename U,
hb_enable_if (hb_is_cr_convertible(U, Type))>
hb_array_t (const hb_array_t<U> &o) :
hb_iter_with_fallback_t<hb_array_t, Type&> (),
arrayZ (o.arrayZ), length (o.length), backwards_length (o.backwards_length) {}
template <typename U,
hb_enable_if (hb_is_cr_convertible(U, Type))>
hb_array_t& operator = (const hb_array_t<U> &o)
{ arrayZ = o.arrayZ; length = o.length; backwards_length = o.backwards_length; return *this; }
template <typename U = Type, hb_enable_if (hb_is_const (U))>
hb_array_t& operator = (const hb_array_t<hb_remove_const (Type)> &o)
{ arrayZ = o.arrayZ; length = o.length; return *this; }
hb_array_t& operator = (const hb_array_t &o)
{ arrayZ = o.arrayZ; length = o.length; return *this; }
/*
* Iterator implementation.
*/
@ -70,15 +72,25 @@ struct hb_array_t : hb_iter_with_fallback_t<hb_array_t<Type>, Type&>
if (unlikely (n > length))
n = length;
length -= n;
backwards_length += n;
arrayZ += n;
}
void __rewind__ (unsigned n)
{
if (unlikely (n > length))
n = length;
length -= n;
if (unlikely (n > backwards_length))
n = backwards_length;
length += n;
backwards_length -= n;
arrayZ -= n;
}
unsigned __len__ () const { return length; }
/* Ouch. The operator== compares the contents of the array. For range-based for loops,
* it's best if we can just compare arrayZ, though comparing contents is still fast,
* but also would require that Type has operator==. As such, we optimize this operator
* for range-based for loop and just compare arrayZ. No need to compare length, as we
* assume we're only compared to .end(). */
bool operator != (const hb_array_t& o) const
{ return arrayZ != o.arrayZ; }
/* Extra operators.
*/
@ -87,14 +99,21 @@ struct hb_array_t : hb_iter_with_fallback_t<hb_array_t<Type>, Type&>
template <typename T> operator T * () const { return arrayZ; }
HB_INTERNAL bool operator == (const hb_array_t &o) const;
HB_INTERNAL uint32_t hash () const;
uint32_t hash () const {
uint32_t current = 0;
for (unsigned int i = 0; i < this->length; i++) {
current = current * 31 + hb_hash (this->arrayZ[i]);
}
return current;
}
/*
* Compare, Sort, and Search.
*/
/* Note: our compare is NOT lexicographic; it also does NOT call Type::cmp. */
int cmp (const hb_array_t<Type> &a) const
int cmp (const hb_array_t &a) const
{
if (length != a.length)
return (int) a.length - (int) length;
@ -102,8 +121,8 @@ struct hb_array_t : hb_iter_with_fallback_t<hb_array_t<Type>, Type&>
}
HB_INTERNAL static int cmp (const void *pa, const void *pb)
{
hb_array_t<Type> *a = (hb_array_t<Type> *) pa;
hb_array_t<Type> *b = (hb_array_t<Type> *) pb;
hb_array_t *a = (hb_array_t *) pa;
hb_array_t *b = (hb_array_t *) pb;
return b->cmp (*a);
}
@ -129,30 +148,48 @@ struct hb_array_t : hb_iter_with_fallback_t<hb_array_t<Type>, Type&>
hb_sorted_array_t<Type> qsort (int (*cmp_)(const void*, const void*))
{
if (likely (length))
::qsort (arrayZ, length, this->item_size, cmp_);
hb_qsort (arrayZ, length, this->get_item_size (), cmp_);
return hb_sorted_array_t<Type> (*this);
}
hb_sorted_array_t<Type> qsort ()
{
if (likely (length))
::qsort (arrayZ, length, this->item_size, Type::cmp);
hb_qsort (arrayZ, length, this->get_item_size (), Type::cmp);
return hb_sorted_array_t<Type> (*this);
}
void qsort (unsigned int start, unsigned int end)
{
end = MIN (end, length);
end = hb_min (end, length);
assert (start <= end);
if (likely (start < end))
::qsort (arrayZ + start, end - start, this->item_size, Type::cmp);
hb_qsort (arrayZ + start, end - start, this->get_item_size (), Type::cmp);
}
/*
* Other methods.
*/
unsigned int get_size () const { return length * this->item_size; }
unsigned int get_size () const { return length * this->get_item_size (); }
hb_array_t<Type> sub_array (unsigned int start_offset = 0, unsigned int *seg_count = nullptr /* IN/OUT */) const
/*
* Reverse the order of items in this array in the range [start, end).
*/
void reverse (unsigned start = 0, unsigned end = -1)
{
start = hb_min (start, length);
end = hb_min (end, length);
if (end < start + 2)
return;
for (unsigned lhs = start, rhs = end - 1; lhs < rhs; lhs++, rhs--) {
Type temp = arrayZ[rhs];
arrayZ[rhs] = arrayZ[lhs];
arrayZ[lhs] = temp;
}
}
hb_array_t sub_array (unsigned int start_offset = 0, unsigned int *seg_count = nullptr /* IN/OUT */) const
{
if (!start_offset && !seg_count)
return *this;
@ -163,16 +200,45 @@ struct hb_array_t : hb_iter_with_fallback_t<hb_array_t<Type>, Type&>
else
count -= start_offset;
if (seg_count)
count = *seg_count = MIN (count, *seg_count);
return hb_array_t<Type> (arrayZ + start_offset, count);
count = *seg_count = hb_min (count, *seg_count);
return hb_array_t (arrayZ + start_offset, count);
}
hb_array_t<Type> sub_array (unsigned int start_offset, unsigned int seg_count) const
hb_array_t sub_array (unsigned int start_offset, unsigned int seg_count) const
{ return sub_array (start_offset, &seg_count); }
hb_array_t truncate (unsigned length) const { return sub_array (0, length); }
template <typename T,
unsigned P = sizeof (Type),
hb_enable_if (P == 1)>
const T *as () const
{ return length < hb_null_size (T) ? &Null (T) : reinterpret_cast<const T *> (arrayZ); }
template <typename T,
unsigned P = sizeof (Type),
hb_enable_if (P == 1)>
bool check_range (const T *p, unsigned int size = T::static_size) const
{
return arrayZ <= ((const char *) p)
&& ((const char *) p) <= arrayZ + length
&& (unsigned int) (arrayZ + length - (const char *) p) >= size;
}
/* Only call if you allocated the underlying array using malloc() or similar. */
void free ()
{ ::free ((void *) arrayZ); arrayZ = nullptr; length = 0; }
template <typename hb_serialize_context_t>
hb_array_t copy (hb_serialize_context_t *c) const
{
TRACE_SERIALIZE (this);
auto* out = c->start_embed (arrayZ);
if (unlikely (!c->extend_size (out, get_size ()))) return_trace (hb_array_t ());
for (unsigned i = 0; i < length; i++)
out[i] = arrayZ[i]; /* TODO: add version that calls c->copy() */
return_trace (hb_array_t (out, length));
}
template <typename hb_sanitize_context_t>
bool sanitize (hb_sanitize_context_t *c) const
{ return c->check_array (arrayZ, length); }
@ -184,6 +250,7 @@ struct hb_array_t : hb_iter_with_fallback_t<hb_array_t<Type>, Type&>
public:
Type *arrayZ;
unsigned int length;
unsigned int backwards_length;
};
template <typename T> inline hb_array_t<T>
hb_array (T *array, unsigned int length)
@ -204,23 +271,37 @@ struct hb_sorted_array_t :
hb_iter_t<hb_sorted_array_t<Type>, Type&>,
hb_array_t<Type>
{
typedef hb_iter_t<hb_sorted_array_t<Type>, Type&> iter_base_t;
typedef hb_iter_t<hb_sorted_array_t, Type&> iter_base_t;
HB_ITER_USING (iter_base_t);
static constexpr bool is_random_access_iterator = true;
static constexpr bool is_sorted_iterator = true;
hb_sorted_array_t () : hb_array_t<Type> () {}
hb_sorted_array_t (const hb_array_t<Type> &o) : hb_array_t<Type> (o) {}
template <typename U = Type, hb_enable_if (hb_is_const (U))>
hb_sorted_array_t (const hb_sorted_array_t<hb_remove_const (Type)> &o) : hb_array_t<Type> (o) {}
hb_sorted_array_t (Type *array_, unsigned int length_) : hb_array_t<Type> (array_, length_) {}
template <unsigned int length_> hb_sorted_array_t (Type (&array_)[length_]) : hb_array_t<Type> (array_) {}
template <unsigned int length_>
hb_sorted_array_t (Type (&array_)[length_]) : hb_array_t<Type> (array_) {}
hb_sorted_array_t<Type> sub_array (unsigned int start_offset, unsigned int *seg_count /* IN/OUT */) const
{ return hb_sorted_array_t<Type> (((const hb_array_t<Type> *) (this))->sub_array (start_offset, seg_count)); }
hb_sorted_array_t<Type> sub_array (unsigned int start_offset, unsigned int seg_count) const
template <typename U,
hb_enable_if (hb_is_cr_convertible(U, Type))>
hb_sorted_array_t (const hb_array_t<U> &o) :
hb_iter_t<hb_sorted_array_t, Type&> (),
hb_array_t<Type> (o) {}
template <typename U,
hb_enable_if (hb_is_cr_convertible(U, Type))>
hb_sorted_array_t& operator = (const hb_array_t<U> &o)
{ hb_array_t<Type> (*this) = o; return *this; }
/* Iterator implementation. */
bool operator != (const hb_sorted_array_t& o) const
{ return this->arrayZ != o.arrayZ || this->length != o.length; }
hb_sorted_array_t sub_array (unsigned int start_offset, unsigned int *seg_count /* IN/OUT */) const
{ return hb_sorted_array_t (((const hb_array_t<Type> *) (this))->sub_array (start_offset, seg_count)); }
hb_sorted_array_t sub_array (unsigned int start_offset, unsigned int seg_count) const
{ return sub_array (start_offset, &seg_count); }
hb_sorted_array_t truncate (unsigned length) const { return sub_array (0, length); }
template <typename T>
Type *bsearch (const T &x, Type *not_found = nullptr)
{
@ -235,26 +316,18 @@ struct hb_sorted_array_t :
}
template <typename T>
bool bfind (const T &x, unsigned int *i = nullptr,
hb_bfind_not_found_t not_found = HB_BFIND_NOT_FOUND_DONT_STORE,
unsigned int to_store = (unsigned int) -1) const
hb_bfind_not_found_t not_found = HB_BFIND_NOT_FOUND_DONT_STORE,
unsigned int to_store = (unsigned int) -1) const
{
int min = 0, max = (int) this->length - 1;
const Type *array = this->arrayZ;
while (min <= max)
unsigned pos;
if (bsearch_impl (x, &pos))
{
int mid = ((unsigned int) min + (unsigned int) max) / 2;
int c = array[mid].cmp (x);
if (c < 0)
max = mid - 1;
else if (c > 0)
min = mid + 1;
else
{
if (i)
*i = mid;
return true;
}
if (i)
*i = pos;
return true;
}
if (i)
{
switch (not_found)
@ -267,14 +340,22 @@ struct hb_sorted_array_t :
break;
case HB_BFIND_NOT_FOUND_STORE_CLOSEST:
if (max < 0 || (max < (int) this->length && array[max].cmp (x) > 0))
max++;
*i = max;
*i = pos;
break;
}
}
return false;
}
template <typename T>
bool bsearch_impl (const T &x, unsigned *pos) const
{
return hb_bsearch_impl (pos,
x,
this->arrayZ,
this->length,
sizeof (Type),
_hb_cmp_method<T, Type>);
}
};
template <typename T> inline hb_sorted_array_t<T>
hb_sorted_array (T *array, unsigned int length)
@ -286,27 +367,35 @@ hb_sorted_array (T (&array_)[length_])
template <typename T>
bool hb_array_t<T>::operator == (const hb_array_t<T> &o) const
{
return length == o.length &&
+ hb_zip (*this, o)
| hb_map ([] (hb_pair_t<T&, T&> &&_) -> bool { return _.first == _.second; })
| hb_all
;
if (o.length != this->length) return false;
for (unsigned int i = 0; i < this->length; i++) {
if (this->arrayZ[i] != o.arrayZ[i]) return false;
}
return true;
}
template <typename T>
uint32_t hb_array_t<T>::hash () const
{
return
+ hb_iter (*this)
| hb_map (hb_hash)
| hb_reduce ([] (uint32_t a, uint32_t b) -> uint32_t { return a * 31 + b; }, 0)
;
/* TODO Specialize opeator== for hb_bytes_t and hb_ubytes_t. */
template <>
inline uint32_t hb_array_t<const char>::hash () const {
uint32_t current = 0;
for (unsigned int i = 0; i < this->length; i++)
current = current * 31 + (uint32_t) (this->arrayZ[i] * 2654435761u);
return current;
}
template <>
inline uint32_t hb_array_t<const unsigned char>::hash () const {
uint32_t current = 0;
for (unsigned int i = 0; i < this->length; i++)
current = current * 31 + (uint32_t) (this->arrayZ[i] * 2654435761u);
return current;
}
typedef hb_array_t<const char> hb_bytes_t;
typedef hb_array_t<const unsigned char> hb_ubytes_t;
/* TODO Specialize opeator==/hash() for hb_bytes_t and hb_ubytes_t. */
//template <>
//uint32_t hb_array_t<const char>::hash () const { return 0; }
#endif /* HB_ARRAY_HH */

View File

@ -107,7 +107,7 @@ _hb_atomic_ptr_impl_cmplexch (const void **P, const void *O_, const void *N)
static inline void _hb_memory_barrier ()
{
#if !defined(MemoryBarrier)
#if !defined(MemoryBarrier) && !defined(__MINGW32_VERSION)
/* MinGW has a convoluted history of supporting MemoryBarrier. */
LONG dummy = 0;
InterlockedExchange (&dummy, 1);
@ -212,25 +212,19 @@ static inline bool _hb_compare_and_swaplp (long *P, long O, long N)
static_assert ((sizeof (long) == sizeof (void *)), "");
#elif !defined(HB_NO_MT)
#define HB_ATOMIC_INT_NIL 1 /* Warn that fallback implementation is in use. */
#define _hb_memory_barrier()
#elif defined(HB_NO_MT)
#define hb_atomic_int_impl_add(AI, V) ((*(AI) += (V)) - (V))
#define _hb_memory_barrier() do {} while (0)
#define hb_atomic_ptr_impl_cmpexch(P,O,N) (* (void **) (P) == (void *) (O) ? (* (void **) (P) = (void *) (N), true) : false)
#else /* HB_NO_MT */
#define hb_atomic_int_impl_add(AI, V) ((*(AI) += (V)) - (V))
#define _hb_memory_barrier()
#define hb_atomic_ptr_impl_cmpexch(P,O,N) (* (void **) (P) == (void *) (O) ? (* (void **) (P) = (void *) (N), true) : false)
#else
#error "Could not find any system to define atomic_int macros."
#error "Check hb-atomic.hh for possible resolutions."
#endif
@ -283,7 +277,7 @@ struct hb_atomic_int_t
template <typename P>
struct hb_atomic_ptr_t
{
typedef hb_remove_pointer (P) T;
typedef hb_remove_pointer<P> T;
void init (T* v_ = nullptr) { set_relaxed (v_); }
void set_relaxed (T* v_) { hb_atomic_ptr_impl_set_relaxed (&v, v_); }

166
src/hb-bimap.hh Normal file
View File

@ -0,0 +1,166 @@
/*
* Copyright © 2019 Adobe Inc.
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Adobe Author(s): Michiharu Ariza
*/
#ifndef HB_BIMAP_HH
#define HB_BIMAP_HH
#include "hb.hh"
#include "hb-map.hh"
/* Bi-directional map */
struct hb_bimap_t
{
hb_bimap_t () { init (); }
~hb_bimap_t () { fini (); }
void init ()
{
forw_map.init ();
back_map.init ();
}
void fini ()
{
forw_map.fini ();
back_map.fini ();
}
void reset ()
{
forw_map.reset ();
back_map.reset ();
}
bool in_error () const { return forw_map.in_error () || back_map.in_error (); }
void set (hb_codepoint_t lhs, hb_codepoint_t rhs)
{
if (unlikely (lhs == HB_MAP_VALUE_INVALID)) return;
if (unlikely (rhs == HB_MAP_VALUE_INVALID)) { del (lhs); return; }
forw_map.set (lhs, rhs);
back_map.set (rhs, lhs);
}
hb_codepoint_t get (hb_codepoint_t lhs) const { return forw_map.get (lhs); }
hb_codepoint_t backward (hb_codepoint_t rhs) const { return back_map.get (rhs); }
hb_codepoint_t operator [] (hb_codepoint_t lhs) const { return get (lhs); }
bool has (hb_codepoint_t lhs, hb_codepoint_t *vp = nullptr) const { return forw_map.has (lhs, vp); }
void del (hb_codepoint_t lhs)
{
back_map.del (get (lhs));
forw_map.del (lhs);
}
void clear ()
{
forw_map.clear ();
back_map.clear ();
}
bool is_empty () const { return get_population () == 0; }
unsigned int get_population () const { return forw_map.get_population (); }
protected:
hb_map_t forw_map;
hb_map_t back_map;
};
/* Inremental bimap: only lhs is given, rhs is incrementally assigned */
struct hb_inc_bimap_t : hb_bimap_t
{
hb_inc_bimap_t () { init (); }
void init ()
{
hb_bimap_t::init ();
next_value = 0;
}
/* Add a mapping from lhs to rhs with a unique value if lhs is unknown.
* Return the rhs value as the result.
*/
hb_codepoint_t add (hb_codepoint_t lhs)
{
hb_codepoint_t rhs = forw_map[lhs];
if (rhs == HB_MAP_VALUE_INVALID)
{
rhs = next_value++;
set (lhs, rhs);
}
return rhs;
}
hb_codepoint_t skip ()
{ return next_value++; }
hb_codepoint_t get_next_value () const
{ return next_value; }
void add_set (const hb_set_t *set)
{
hb_codepoint_t i = HB_SET_VALUE_INVALID;
while (hb_set_next (set, &i)) add (i);
}
/* Create an identity map. */
bool identity (unsigned int size)
{
clear ();
for (hb_codepoint_t i = 0; i < size; i++) set (i, i);
return !in_error ();
}
protected:
static int cmp_id (const void* a, const void* b)
{ return (int)*(const hb_codepoint_t *)a - (int)*(const hb_codepoint_t *)b; }
public:
/* Optional: after finished adding all mappings in a random order,
* reassign rhs to lhs so that they are in the same order. */
void sort ()
{
hb_codepoint_t count = get_population ();
hb_vector_t <hb_codepoint_t> work;
work.resize (count);
for (hb_codepoint_t rhs = 0; rhs < count; rhs++)
work[rhs] = back_map[rhs];
work.qsort (cmp_id);
clear ();
for (hb_codepoint_t rhs = 0; rhs < count; rhs++)
set (work[rhs], rhs);
}
protected:
unsigned int next_value;
};
#endif /* HB_BIMAP_HH */

View File

@ -25,18 +25,6 @@
* Red Hat Author(s): Behdad Esfahbod
*/
/* https://github.com/harfbuzz/harfbuzz/issues/1308
* http://www.gnu.org/software/libc/manual/html_node/Feature-Test-Macros.html
* https://www.oracle.com/technetwork/articles/servers-storage-dev/standardheaderfiles-453865.html
*/
#if !defined(_POSIX_C_SOURCE) && !defined(_MSC_VER)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-macros"
#define _POSIX_C_SOURCE 200809L
#pragma GCC diagnostic pop
#endif
#include "hb.hh"
#include "hb-blob.hh"
@ -48,7 +36,6 @@
#endif /* HAVE_SYS_MMAN_H */
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
@ -155,7 +142,7 @@ hb_blob_create_sub_blob (hb_blob_t *parent,
hb_blob_make_immutable (parent);
blob = hb_blob_create (parent->data + offset,
MIN (length, parent->length - offset),
hb_min (length, parent->length - offset),
HB_MEMORY_MODE_READONLY,
hb_blob_reference (parent),
_hb_blob_destroy);
@ -202,7 +189,7 @@ hb_blob_copy_writable_or_fail (hb_blob_t *blob)
hb_blob_t *
hb_blob_get_empty ()
{
return const_cast<hb_blob_t *> (&Null(hb_blob_t));
return const_cast<hb_blob_t *> (&Null (hb_blob_t));
}
/**
@ -487,6 +474,7 @@ hb_blob_t::try_make_writable ()
* Mmap
*/
#ifndef HB_NO_OPEN
#ifdef HAVE_MMAP
# include <sys/types.h>
# include <sys/stat.h>
@ -579,7 +567,7 @@ fail_without_close:
HANDLE fd;
unsigned int size = strlen (file_name) + 1;
wchar_t * wchar_file_name = (wchar_t *) malloc (sizeof (wchar_t) * size);
if (unlikely (wchar_file_name == nullptr)) goto fail_without_close;
if (unlikely (!wchar_file_name)) goto fail_without_close;
mbstowcs (wchar_file_name, file_name, size);
#if defined(WINAPI_FAMILY) && (WINAPI_FAMILY==WINAPI_FAMILY_PC_APP || WINAPI_FAMILY==WINAPI_FAMILY_PHONE_APP)
{
@ -591,7 +579,7 @@ fail_without_close:
ceparams.lpSecurityAttributes = nullptr;
ceparams.hTemplateFile = nullptr;
fd = CreateFile2 (wchar_file_name, GENERIC_READ, FILE_SHARE_READ,
OPEN_EXISTING, &ceparams);
OPEN_EXISTING, &ceparams);
}
#else
fd = CreateFileW (wchar_file_name, GENERIC_READ, FILE_SHARE_READ, nullptr,
@ -613,14 +601,14 @@ fail_without_close:
file->length = (unsigned long) GetFileSize (fd, nullptr);
file->mapping = CreateFileMapping (fd, nullptr, PAGE_READONLY, 0, 0, nullptr);
#endif
if (unlikely (file->mapping == nullptr)) goto fail;
if (unlikely (!file->mapping)) goto fail;
#if defined(WINAPI_FAMILY) && (WINAPI_FAMILY==WINAPI_FAMILY_PC_APP || WINAPI_FAMILY==WINAPI_FAMILY_PHONE_APP)
file->contents = (char *) MapViewOfFileFromApp (file->mapping, FILE_MAP_READ, 0, 0);
#else
file->contents = (char *) MapViewOfFile (file->mapping, FILE_MAP_READ, 0, 0, 0);
#endif
if (unlikely (file->contents == nullptr)) goto fail;
if (unlikely (!file->contents)) goto fail;
CloseHandle (fd);
return hb_blob_create (file->contents, file->length,
@ -638,10 +626,10 @@ fail_without_close:
It's used as a fallback for systems without mmap or to read from pipes */
unsigned long len = 0, allocated = BUFSIZ * 16;
char *data = (char *) malloc (allocated);
if (unlikely (data == nullptr)) return hb_blob_get_empty ();
if (unlikely (!data)) return hb_blob_get_empty ();
FILE *fp = fopen (file_name, "rb");
if (unlikely (fp == nullptr)) goto fread_fail_without_close;
if (unlikely (!fp)) goto fread_fail_without_close;
while (!feof (fp))
{
@ -652,7 +640,7 @@ fail_without_close:
can cover files like that but lets limit our fallback reader */
if (unlikely (allocated > (2 << 28))) goto fread_fail;
char *new_data = (char *) realloc (data, allocated);
if (unlikely (new_data == nullptr)) goto fread_fail;
if (unlikely (!new_data)) goto fread_fail;
data = new_data;
}
@ -668,7 +656,7 @@ fail_without_close:
}
return hb_blob_create (data, len, HB_MEMORY_MODE_WRITABLE, data,
(hb_destroy_func_t) free);
(hb_destroy_func_t) free);
fread_fail:
fclose (fp);
@ -676,3 +664,4 @@ fread_fail_without_close:
free (data);
return hb_blob_get_empty ();
}
#endif /* !HB_NO_OPEN */

View File

@ -71,6 +71,9 @@ hb_blob_create (const char *data,
void *user_data,
hb_destroy_func_t destroy);
HB_EXTERN hb_blob_t *
hb_blob_create_from_file (const char *file_name);
/* Always creates with MEMORY_MODE_READONLY.
* Even if the parent blob is writable, we don't
* want the user of the sub-blob to be able to
@ -123,9 +126,6 @@ hb_blob_get_data (hb_blob_t *blob, unsigned int *length);
HB_EXTERN char *
hb_blob_get_data_writable (hb_blob_t *blob, unsigned int *length);
HB_EXTERN hb_blob_t *
hb_blob_create_from_file (const char *file_name);
HB_END_DECLS
#endif /* HB_BLOB_H */

View File

@ -54,13 +54,9 @@ struct hb_blob_t
HB_INTERNAL bool try_make_writable_inplace ();
HB_INTERNAL bool try_make_writable_inplace_unix ();
hb_bytes_t as_bytes () const { return hb_bytes_t (data, length); }
template <typename Type>
const Type* as () const
{
return length < hb_null_size (Type) ? &Null(Type) : reinterpret_cast<const Type *> (data);
}
hb_bytes_t as_bytes () const
{ return hb_bytes_t (data, length); }
const Type* as () const { return as_bytes ().as<Type> (); }
public:
hb_object_header_t header;
@ -81,7 +77,7 @@ struct hb_blob_t
template <typename P>
struct hb_blob_ptr_t
{
typedef hb_remove_pointer (P) T;
typedef hb_remove_pointer<P> T;
hb_blob_ptr_t (hb_blob_t *b_ = nullptr) : b (b_) {}
hb_blob_t * operator = (hb_blob_t *b_) { return b = b_; }

View File

@ -24,6 +24,10 @@
* Google Author(s): Behdad Esfahbod
*/
#include "hb.hh"
#ifndef HB_NO_BUFFER_SERIALIZE
#include "hb-buffer.hh"
@ -85,7 +89,7 @@ hb_buffer_serialize_format_from_string (const char *str, int len)
const char *
hb_buffer_serialize_format_to_string (hb_buffer_serialize_format_t format)
{
switch (format)
switch ((unsigned) format)
{
case HB_BUFFER_SERIALIZE_FORMAT_TEXT: return serialize_formats[0];
case HB_BUFFER_SERIALIZE_FORMAT_JSON: return serialize_formats[1];
@ -131,41 +135,41 @@ _hb_buffer_serialize_glyphs_json (hb_buffer_t *buffer,
hb_font_glyph_to_string (font, info[i].codepoint, g, sizeof (g));
*p++ = '"';
for (char *q = g; *q; q++) {
if (*q == '"')
if (*q == '"')
*p++ = '\\';
*p++ = *q;
}
*p++ = '"';
}
else
p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "%u", info[i].codepoint));
p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "%u", info[i].codepoint));
if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS)) {
p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"cl\":%u", info[i].cluster));
p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"cl\":%u", info[i].cluster));
}
if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS))
{
p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"dx\":%d,\"dy\":%d",
p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"dx\":%d,\"dy\":%d",
x+pos[i].x_offset, y+pos[i].y_offset));
if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_ADVANCES))
p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"ax\":%d,\"ay\":%d",
p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"ax\":%d,\"ay\":%d",
pos[i].x_advance, pos[i].y_advance));
}
if (flags & HB_BUFFER_SERIALIZE_FLAG_GLYPH_FLAGS)
{
if (info[i].mask & HB_GLYPH_FLAG_DEFINED)
p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"fl\":%u", info[i].mask & HB_GLYPH_FLAG_DEFINED));
p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"fl\":%u", info[i].mask & HB_GLYPH_FLAG_DEFINED));
}
if (flags & HB_BUFFER_SERIALIZE_FLAG_GLYPH_EXTENTS)
{
hb_glyph_extents_t extents;
hb_font_get_glyph_extents(font, info[i].codepoint, &extents);
p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"xb\":%d,\"yb\":%d",
p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"xb\":%d,\"yb\":%d",
extents.x_bearing, extents.y_bearing));
p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"w\":%d,\"h\":%d",
p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"w\":%d,\"h\":%d",
extents.width, extents.height));
}
@ -224,37 +228,37 @@ _hb_buffer_serialize_glyphs_text (hb_buffer_t *buffer,
p += strlen (p);
}
else
p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "%u", info[i].codepoint));
p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "%u", info[i].codepoint));
if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS)) {
p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "=%u", info[i].cluster));
p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "=%u", info[i].cluster));
}
if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS))
{
if (x+pos[i].x_offset || y+pos[i].y_offset)
p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "@%d,%d", x+pos[i].x_offset, y+pos[i].y_offset));
p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "@%d,%d", x+pos[i].x_offset, y+pos[i].y_offset));
if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_ADVANCES))
{
*p++ = '+';
p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "%d", pos[i].x_advance));
p += hb_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));
p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",%d", pos[i].y_advance));
}
}
if (flags & HB_BUFFER_SERIALIZE_FLAG_GLYPH_FLAGS)
{
if (info[i].mask & HB_GLYPH_FLAG_DEFINED)
p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "#%X", info[i].mask &HB_GLYPH_FLAG_DEFINED));
p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "#%X", info[i].mask &HB_GLYPH_FLAG_DEFINED));
}
if (flags & HB_BUFFER_SERIALIZE_FLAG_GLYPH_EXTENTS)
{
hb_glyph_extents_t extents;
hb_font_get_glyph_extents(font, info[i].codepoint, &extents);
p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "<%d,%d,%d,%d>", extents.x_bearing, extents.y_bearing, extents.width, extents.height));
p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "<%d,%d,%d,%d>", extents.x_bearing, extents.y_bearing, extents.width, extents.height));
}
unsigned int l = p - b;
@ -375,43 +379,24 @@ hb_buffer_serialize_glyphs (hb_buffer_t *buffer,
}
}
static hb_bool_t
parse_uint (const char *pp, const char *end, uint32_t *pv)
static bool
parse_int (const char *pp, const char *end, int32_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;
uint32_t v;
errno = 0;
v = strtol (p, &pend, 10);
if (errno || p == pend || pend - p != end - pp)
int v;
const char *p = pp;
if (unlikely (!hb_parse_int (&p, end, &v, true/* whole buffer */)))
return false;
*pv = v;
return true;
}
static hb_bool_t
parse_int (const char *pp, const char *end, int32_t *pv)
static bool
parse_uint (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;
int32_t v;
errno = 0;
v = strtol (p, &pend, 10);
if (errno || p == pend || pend - p != end - pp)
unsigned int v;
const char *p = pp;
if (unlikely (!hb_parse_uint (&p, end, &v, true/* whole buffer */)))
return false;
*pv = v;
@ -484,3 +469,6 @@ hb_buffer_deserialize_glyphs (hb_buffer_t *buffer,
}
}
#endif

View File

@ -324,7 +324,7 @@ hb_buffer_t::clear_positions ()
out_len = 0;
out_info = info;
memset (pos, 0, sizeof (pos[0]) * len);
hb_memset (pos, 0, sizeof (pos[0]) * len);
}
void
@ -438,13 +438,6 @@ hb_buffer_t::set_masks (hb_mask_t value,
if (!mask)
return;
if (cluster_start == 0 && cluster_end == (unsigned int)-1) {
unsigned int count = len;
for (unsigned int i = 0; i < count; i++)
info[i].mask = (info[i].mask & not_mask) | value;
return;
}
unsigned int count = len;
for (unsigned int i = 0; i < count; i++)
if (cluster_start <= info[i].cluster && info[i].cluster < cluster_end)
@ -455,27 +448,13 @@ void
hb_buffer_t::reverse_range (unsigned int start,
unsigned int end)
{
unsigned int i, j;
if (end - start < 2)
return;
for (i = start, j = end - 1; i < j; i++, j--) {
hb_glyph_info_t t;
t = info[i];
info[i] = info[j];
info[j] = t;
}
hb_array_t<hb_glyph_info_t> (info, len).reverse (start, end);
if (have_positions) {
for (i = start, j = end - 1; i < j; i++, j--) {
hb_glyph_position_t t;
t = pos[i];
pos[i] = pos[j];
pos[j] = t;
}
hb_array_t<hb_glyph_position_t> (pos, len).reverse (start, end);
}
}
@ -524,7 +503,7 @@ hb_buffer_t::merge_clusters_impl (unsigned int start,
unsigned int cluster = info[start].cluster;
for (unsigned int i = start + 1; i < end; i++)
cluster = MIN<unsigned int> (cluster, info[i].cluster);
cluster = hb_min (cluster, info[i].cluster);
/* Extend end */
while (end < len && info[end - 1].cluster == info[end].cluster)
@ -555,7 +534,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<unsigned int> (cluster, out_info[i].cluster);
cluster = hb_min (cluster, out_info[i].cluster);
/* Extend start */
while (start && out_info[start - 1].cluster == out_info[start].cluster)
@ -612,7 +591,7 @@ done:
void
hb_buffer_t::unsafe_to_break_impl (unsigned int start, unsigned int end)
{
unsigned int cluster = (unsigned int) -1;
unsigned int cluster = UINT_MAX;
cluster = _unsafe_to_break_find_min_cluster (info, start, end, cluster);
_unsafe_to_break_set_mask (info, start, end, cluster);
}
@ -628,7 +607,7 @@ hb_buffer_t::unsafe_to_break_from_outbuffer (unsigned int start, unsigned int en
assert (start <= out_len);
assert (idx <= end);
unsigned int cluster = (unsigned int) -1;
unsigned int cluster = UINT_MAX;
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);
@ -648,8 +627,8 @@ hb_buffer_t::guess_segment_properties ()
if (likely (script != HB_SCRIPT_COMMON &&
script != HB_SCRIPT_INHERITED &&
script != HB_SCRIPT_UNKNOWN)) {
props.script = script;
break;
props.script = script;
break;
}
}
}
@ -736,7 +715,7 @@ hb_buffer_create ()
hb_buffer_t *
hb_buffer_get_empty ()
{
return const_cast<hb_buffer_t *> (&Null(hb_buffer_t));
return const_cast<hb_buffer_t *> (&Null (hb_buffer_t));
}
/**
@ -776,8 +755,10 @@ hb_buffer_destroy (hb_buffer_t *buffer)
free (buffer->info);
free (buffer->pos);
#ifndef HB_NO_BUFFER_MESSAGE
if (buffer->message_destroy)
buffer->message_destroy (buffer->message_data);
#endif
free (buffer);
}
@ -956,7 +937,7 @@ hb_buffer_get_direction (hb_buffer_t *buffer)
*
* You can pass one of the predefined #hb_script_t values, or use
* hb_script_from_string() or hb_script_from_iso15924_tag() to get the
* corresponding script from an ISO 15924 script tag.
* corresponding script from an ISO 15924 script tag.
*
* Since: 0.9.2
**/
@ -999,7 +980,7 @@ hb_buffer_get_script (hb_buffer_t *buffer)
* are orthogonal to the scripts, and though they are related, they are
* different concepts and should not be confused with each other.
*
* Use hb_language_from_string() to convert from BCP 47 language tags to
* Use hb_language_from_string() to convert from BCP 47 language tags to
* #hb_language_t.
*
* Since: 0.9.2
@ -1388,7 +1369,7 @@ hb_buffer_get_length (hb_buffer_t *buffer)
**/
hb_glyph_info_t *
hb_buffer_get_glyph_infos (hb_buffer_t *buffer,
unsigned int *length)
unsigned int *length)
{
if (length)
*length = buffer->len;
@ -1412,7 +1393,7 @@ hb_buffer_get_glyph_infos (hb_buffer_t *buffer,
**/
hb_glyph_position_t *
hb_buffer_get_glyph_positions (hb_buffer_t *buffer,
unsigned int *length)
unsigned int *length)
{
if (!buffer->have_positions)
buffer->clear_positions ();
@ -1736,7 +1717,7 @@ hb_buffer_add_codepoints (hb_buffer_t *buffer,
* @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.
* @end: end index into source buffer to copy. Use @HB_FEATURE_GLOBAL_END to copy to end of buffer.
*
* Append (part of) contents of another buffer to this buffer.
*
@ -1858,18 +1839,8 @@ hb_buffer_normalize_glyphs (hb_buffer_t *buffer)
bool backward = HB_DIRECTION_IS_BACKWARD (buffer->props.direction);
unsigned int count = buffer->len;
if (unlikely (!count)) return;
hb_glyph_info_t *info = buffer->info;
unsigned int start = 0;
unsigned int end;
for (end = start + 1; end < count; end++)
if (info[start].cluster != info[end].cluster) {
normalize_glyphs_cluster (buffer, start, end, backward);
start = end;
}
normalize_glyphs_cluster (buffer, start, end, backward);
foreach_cluster (buffer, start, end)
normalize_glyphs_cluster (buffer, start, end, backward);
}
void
@ -1936,9 +1907,9 @@ hb_buffer_diff (hb_buffer_t *buffer,
for (i = 0; i < count; i++)
{
if (contains && info[i].codepoint == dottedcircle_glyph)
result |= HB_BUFFER_DIFF_FLAG_DOTTED_CIRCLE_PRESENT;
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_NOTDEF_PRESENT;
}
result |= HB_BUFFER_DIFF_FLAG_LENGTH_MISMATCH;
return hb_buffer_diff_flags_t (result);
@ -1973,12 +1944,12 @@ hb_buffer_diff (hb_buffer_t *buffer,
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)
(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;
result |= HB_BUFFER_DIFF_FLAG_POSITION_MISMATCH;
break;
}
buf_pos++;
ref_pos++;
@ -1993,6 +1964,7 @@ hb_buffer_diff (hb_buffer_t *buffer,
* Debugging.
*/
#ifndef HB_NO_BUFFER_MESSAGE
/**
* hb_buffer_set_message_func:
* @buffer: an #hb_buffer_t.
@ -2022,11 +1994,11 @@ hb_buffer_set_message_func (hb_buffer_t *buffer,
buffer->message_destroy = nullptr;
}
}
bool
hb_buffer_t::message_impl (hb_font_t *font, const char *fmt, va_list ap)
{
char buf[100];
vsnprintf (buf, sizeof (buf), fmt, ap);
vsnprintf (buf, sizeof (buf), fmt, ap);
return (bool) this->message_func (this, font, buf, this->message_data);
}
#endif

View File

@ -365,7 +365,7 @@ hb_buffer_clear_contents (hb_buffer_t *buffer);
HB_EXTERN hb_bool_t
hb_buffer_pre_allocate (hb_buffer_t *buffer,
unsigned int size);
unsigned int size);
HB_EXTERN hb_bool_t
@ -441,11 +441,11 @@ hb_buffer_get_length (hb_buffer_t *buffer);
HB_EXTERN hb_glyph_info_t *
hb_buffer_get_glyph_infos (hb_buffer_t *buffer,
unsigned int *length);
unsigned int *length);
HB_EXTERN hb_glyph_position_t *
hb_buffer_get_glyph_positions (hb_buffer_t *buffer,
unsigned int *length);
unsigned int *length);
HB_EXTERN void

View File

@ -124,9 +124,11 @@ struct hb_buffer_t
unsigned int context_len[2];
/* Debugging API */
#ifndef HB_NO_BUFFER_MESSAGE
hb_buffer_message_func_t message_func;
void *message_data;
hb_destroy_func_t message_destroy;
#endif
/* Internal debugging. */
/* The bits here reflect current allocations of the bytes in glyph_info_t's var1 and var2. */
@ -226,10 +228,10 @@ struct hb_buffer_t
/* Makes a copy of the glyph at idx to output and replace glyph_index */
hb_glyph_info_t & output_glyph (hb_codepoint_t glyph_index)
{
if (unlikely (!make_room_for (0, 1))) return Crap(hb_glyph_info_t);
if (unlikely (!make_room_for (0, 1))) return Crap (hb_glyph_info_t);
if (unlikely (idx == len && !out_len))
return Crap(hb_glyph_info_t);
return Crap (hb_glyph_info_t);
out_info[out_len] = idx < len ? info[idx] : out_info[out_len - 1];
out_info[out_len].codepoint = glyph_index;
@ -347,9 +349,19 @@ struct hb_buffer_t
HB_INTERNAL void sort (unsigned int start, unsigned int end, int(*compar)(const hb_glyph_info_t *, const hb_glyph_info_t *));
bool messaging () { return unlikely (message_func); }
bool messaging ()
{
#ifdef HB_NO_BUFFER_MESSAGE
return false;
#else
return unlikely (message_func);
#endif
}
bool message (hb_font_t *font, const char *fmt, ...) HB_PRINTF_FUNC(3, 4)
{
#ifdef HB_NO_BUFFER_MESSAGE
return true;
#else
if (!messaging ())
return true;
va_list ap;
@ -357,6 +369,7 @@ struct hb_buffer_t
bool ret = message_impl (font, fmt, ap);
va_end (ap);
return ret;
#endif
}
HB_INTERNAL bool message_impl (hb_font_t *font, const char *fmt, va_list ap) HB_PRINTF_FUNC(3, 0);
@ -373,13 +386,13 @@ struct hb_buffer_t
inf.cluster = cluster;
}
int
unsigned int
_unsafe_to_break_find_min_cluster (const hb_glyph_info_t *infos,
unsigned int start, unsigned int end,
unsigned int cluster) const
{
for (unsigned int i = start; i < end; i++)
cluster = MIN<unsigned int> (cluster, infos[i].cluster);
cluster = hb_min (cluster, infos[i].cluster);
return cluster;
}
void
@ -395,8 +408,7 @@ struct hb_buffer_t
}
}
void unsafe_to_break_all ()
{ unsafe_to_break_impl (0, len); }
void unsafe_to_break_all () { unsafe_to_break_impl (0, len); }
void safe_to_break_all ()
{
for (unsigned int i = 0; i < len; i++)

View File

@ -220,32 +220,22 @@ struct number_t
void init () { set_real (0.0); }
void fini () {}
void set_int (int v) { value = (double) v; }
int to_int () const { return (int) value; }
void set_int (int v) { value = v; }
int to_int () const { return value; }
void set_fixed (int32_t v) { value = v / 65536.0; }
int32_t to_fixed () const { return (int32_t) (value * 65536.0); }
int32_t to_fixed () const { return value * 65536.0; }
void set_real (double v) { value = v; }
void set_real (double v) { value = v; }
double to_real () const { return value; }
int ceil () const { return (int) ::ceil (value); }
int floor () const { return (int) ::floor (value); }
bool in_int_range () const
{ return ((double) (int16_t) to_int () == value); }
bool operator > (const number_t &n) const
{ return value > n.to_real (); }
bool operator < (const number_t &n) const
{ return n > *this; }
bool operator >= (const number_t &n) const
{ return !(*this < n); }
bool operator <= (const number_t &n) const
{ return !(*this > n); }
bool operator > (const number_t &n) const { return value > n.to_real (); }
bool operator < (const number_t &n) const { return n > *this; }
bool operator >= (const number_t &n) const { return !(*this < n); }
bool operator <= (const number_t &n) const { return !(*this > n); }
const number_t &operator += (const number_t &n)
{
@ -255,37 +245,34 @@ struct number_t
}
protected:
double value;
double value;
};
/* byte string */
struct UnsizedByteStr : UnsizedArrayOf <HBUINT8>
{
// encode 2-byte int (Dict/CharString) or 4-byte int (Dict)
template <typename INTTYPE, int minVal, int maxVal>
static bool serialize_int (hb_serialize_context_t *c, op_code_t intOp, int value)
template <typename T, typename V>
static bool serialize_int (hb_serialize_context_t *c, op_code_t intOp, V value)
{
TRACE_SERIALIZE (this);
if (unlikely ((value < minVal || value > maxVal)))
return_trace (false);
HBUINT8 *p = c->allocate_size<HBUINT8> (1);
if (unlikely (p == nullptr)) return_trace (false);
if (unlikely (!p)) return_trace (false);
*p = intOp;
INTTYPE *ip = c->allocate_size<INTTYPE> (INTTYPE::static_size);
if (unlikely (ip == nullptr)) return_trace (false);
*ip = (unsigned int) value;
return_trace (true);
T *ip = c->allocate_size<T> (T::static_size);
if (unlikely (!ip)) return_trace (false);
return_trace (c->check_assign (*ip, value));
}
static bool serialize_int4 (hb_serialize_context_t *c, int value)
{ return serialize_int<HBUINT32, 0, 0x7FFFFFFF> (c, OpCode_longintdict, value); }
template <typename V>
static bool serialize_int4 (hb_serialize_context_t *c, V value)
{ return serialize_int<HBINT32> (c, OpCode_longintdict, value); }
static bool serialize_int2 (hb_serialize_context_t *c, int value)
{ return serialize_int<HBUINT16, 0, 0x7FFF> (c, OpCode_shortint, value); }
template <typename V>
static bool serialize_int2 (hb_serialize_context_t *c, V value)
{ return serialize_int<HBINT16> (c, OpCode_shortint, value); }
/* Defining null_size allows a Null object may be created. Should be safe because:
* A descendent struct Dict uses a Null pointer to indicate a missing table,
@ -308,7 +295,7 @@ struct byte_str_t : hb_ubytes_t
: hb_ubytes_t (s, l) {}
byte_str_t (const hb_ubytes_t &ub) /* conversion from hb_ubytes_t */
: hb_ubytes_t (ub) {}
/* sub-string */
byte_str_t sub_str (unsigned int offset, unsigned int len_) const
{ return byte_str_t (hb_ubytes_t::sub_array (offset, len_)); }
@ -320,8 +307,7 @@ struct byte_str_t : hb_ubytes_t
/* A byte string associated with the current offset and an error condition */
struct byte_str_ref_t
{
byte_str_ref_t ()
{ init (); }
byte_str_ref_t () { init (); }
void init ()
{
@ -343,13 +329,12 @@ struct byte_str_ref_t
}
const unsigned char& operator [] (int i) {
if (unlikely ((unsigned int)(offset + i) >= str.length))
if (unlikely ((unsigned int) (offset + i) >= str.length))
{
set_error ();
return Null(unsigned char);
return Null (unsigned char);
}
else
return str[offset + i];
return str[offset + i];
}
/* Conversion to byte_str_t */
@ -359,9 +344,7 @@ struct byte_str_ref_t
{ return str.sub_str (offset_, len_); }
bool avail (unsigned int count=1) const
{
return (!in_error () && str.check_limit (offset, count));
}
{ return (!in_error () && str.check_limit (offset, count)); }
void inc (unsigned int count=1)
{
if (likely (!in_error () && (offset <= str.length) && (offset + count <= str.length)))
@ -389,7 +372,7 @@ typedef hb_vector_t<byte_str_t> byte_str_array_t;
/* stack */
template <typename ELEM, int LIMIT>
struct stack_t
struct cff_stack_t
{
void init ()
{
@ -400,11 +383,7 @@ struct stack_t
for (unsigned int i = 0; i < elements.length; i++)
elements[i].init ();
}
void fini ()
{
elements.fini_deep ();
}
void fini () { elements.fini_deep (); }
ELEM& operator [] (unsigned int i)
{
@ -419,7 +398,6 @@ struct stack_t
else
set_error ();
}
ELEM &push ()
{
if (likely (count < elements.length))
@ -427,7 +405,7 @@ struct stack_t
else
{
set_error ();
return Crap(ELEM);
return Crap (ELEM);
}
}
@ -438,10 +416,9 @@ struct stack_t
else
{
set_error ();
return Crap(ELEM);
return Crap (ELEM);
}
}
void pop (unsigned int n)
{
if (likely (count >= n))
@ -452,13 +429,12 @@ struct stack_t
const ELEM& peek ()
{
if (likely (count > 0))
return elements[count-1];
else
if (unlikely (count < 0))
{
set_error ();
return Null(ELEM);
return Null (ELEM);
}
return elements[count - 1];
}
void unpop ()
@ -475,7 +451,7 @@ struct stack_t
void set_error () { error = true; }
unsigned int get_count () const { return count; }
bool is_empty () const { return count == 0; }
bool is_empty () const { return !count; }
static constexpr unsigned kSizeLimit = LIMIT;
@ -487,7 +463,7 @@ struct stack_t
/* argument stack */
template <typename ARG=number_t>
struct arg_stack_t : stack_t<ARG, 513>
struct arg_stack_t : cff_stack_t<ARG, 513>
{
void push_int (int v)
{
@ -519,7 +495,7 @@ struct arg_stack_t : stack_t<ARG, 513>
i = 0;
S::set_error ();
}
return (unsigned)i;
return (unsigned) i;
}
void push_longint_from_substr (byte_str_ref_t& str_ref)
@ -538,12 +514,10 @@ struct arg_stack_t : stack_t<ARG, 513>
}
hb_array_t<const ARG> get_subarray (unsigned int start) const
{
return S::elements.sub_array (start);
}
{ return S::elements.sub_array (start); }
private:
typedef stack_t<ARG, 513> S;
typedef cff_stack_t<ARG, 513> S;
};
/* an operator prefixed by its operands in a byte string */
@ -565,7 +539,7 @@ struct op_serializer_t
TRACE_SERIALIZE (this);
HBUINT8 *d = c->allocate_size<HBUINT8> (opstr.str.length);
if (unlikely (d == nullptr)) return_trace (false);
if (unlikely (!d)) return_trace (false);
memcpy (d, &opstr.str[0], opstr.str.length);
return_trace (true);
}
@ -605,7 +579,7 @@ struct parsed_values_t
}
unsigned get_count () const { return values.length; }
const VAL &get_value (unsigned int i) const { return values[i]; }
const VAL &get_value (unsigned int i) const { return values[i]; }
const VAL &operator [] (unsigned int i) const { return get_value (i); }
unsigned int opStart;
@ -644,30 +618,19 @@ struct interp_env_t
return op;
}
const ARG& eval_arg (unsigned int i)
{
return argStack[i];
}
const ARG& eval_arg (unsigned int i) { return argStack[i]; }
ARG& pop_arg ()
{
return argStack.pop ();
}
ARG& pop_arg () { return argStack.pop (); }
void pop_n_args (unsigned int n) { argStack.pop (n); }
void pop_n_args (unsigned int n)
{
argStack.pop (n);
}
void clear_args () { pop_n_args (argStack.get_count ()); }
void clear_args ()
{
pop_n_args (argStack.get_count ());
}
byte_str_ref_t str_ref;
arg_stack_t<ARG> argStack;
byte_str_ref_t
str_ref;
arg_stack_t<ARG>
argStack;
protected:
bool error;
bool error;
};
typedef interp_env_t<> num_interp_env_t;
@ -691,7 +654,7 @@ struct opset_t
case OpCode_TwoByteNegInt0: case OpCode_TwoByteNegInt1:
case OpCode_TwoByteNegInt2: case OpCode_TwoByteNegInt3:
env.argStack.push_int ((int16_t)(-(op - OpCode_TwoByteNegInt0) * 256 - env.str_ref[0] - 108));
env.argStack.push_int ((-(int16_t)(op - OpCode_TwoByteNegInt0) * 256 - env.str_ref[0] - 108));
env.str_ref.inc ();
break;
@ -711,8 +674,8 @@ struct opset_t
};
template <typename ENV>
struct interpreter_t {
struct interpreter_t
{
~interpreter_t() { fini (); }
void fini () { env.fini (); }

View File

@ -57,14 +57,14 @@ struct call_context_t
/* call stack */
const unsigned int kMaxCallLimit = 10;
struct call_stack_t : stack_t<call_context_t, kMaxCallLimit> {};
struct call_stack_t : cff_stack_t<call_context_t, kMaxCallLimit> {};
template <typename SUBRS>
struct biased_subrs_t
{
void init (const SUBRS &subrs_)
void init (const SUBRS *subrs_)
{
subrs = &subrs_;
subrs = subrs_;
unsigned int nSubrs = get_count ();
if (nSubrs < 1240)
bias = 107;
@ -76,13 +76,13 @@ struct biased_subrs_t
void fini () {}
unsigned int get_count () const { return (subrs == nullptr)? 0: subrs->count; }
unsigned int get_bias () const { return bias; }
unsigned int get_count () const { return subrs ? subrs->count : 0; }
unsigned int get_bias () const { return bias; }
byte_str_t operator [] (unsigned int index) const
{
if (unlikely ((subrs == nullptr) || index >= subrs->count))
return Null(byte_str_t);
if (unlikely (!subrs || index >= subrs->count))
return Null (byte_str_t);
else
return (*subrs)[index];
}
@ -118,7 +118,7 @@ struct point_t
template <typename ARG, typename SUBRS>
struct cs_interp_env_t : interp_env_t<ARG>
{
void init (const byte_str_t &str, const SUBRS &globalSubrs_, const SUBRS &localSubrs_)
void init (const byte_str_t &str, const SUBRS *globalSubrs_, const SUBRS *localSubrs_)
{
interp_env_t<ARG>::init (str);
@ -147,8 +147,9 @@ struct cs_interp_env_t : interp_env_t<ARG>
return callStack.in_error () || SUPER::in_error ();
}
bool popSubrNum (const biased_subrs_t<SUBRS>& biasedSubrs, unsigned int &subr_num)
bool pop_subr_num (const biased_subrs_t<SUBRS>& biasedSubrs, unsigned int &subr_num)
{
subr_num = 0;
int n = SUPER::argStack.pop_int ();
n += biasedSubrs.get_bias ();
if (unlikely ((n < 0) || ((unsigned int)n >= biasedSubrs.get_count ())))
@ -158,11 +159,11 @@ struct cs_interp_env_t : interp_env_t<ARG>
return true;
}
void callSubr (const biased_subrs_t<SUBRS>& biasedSubrs, cs_type_t type)
void call_subr (const biased_subrs_t<SUBRS>& biasedSubrs, cs_type_t type)
{
unsigned int subr_num;
unsigned int subr_num = 0;
if (unlikely (!popSubrNum (biasedSubrs, subr_num)
if (unlikely (!pop_subr_num (biasedSubrs, subr_num)
|| callStack.get_count () >= kMaxCallLimit))
{
SUPER::set_error ();
@ -175,7 +176,7 @@ struct cs_interp_env_t : interp_env_t<ARG>
SUPER::str_ref = context.str_ref;
}
void returnFromSubr ()
void return_from_subr ()
{
if (unlikely (SUPER::str_ref.in_error ()))
SUPER::set_error ();
@ -246,7 +247,7 @@ struct path_procs_null_t
static void flex1 (ENV &env, PARAM& param) {}
};
template <typename ARG, typename OPSET, typename ENV, typename PARAM, typename PATH=path_procs_null_t<ENV, PARAM> >
template <typename ARG, typename OPSET, typename ENV, typename PARAM, typename PATH=path_procs_null_t<ENV, PARAM>>
struct cs_opset_t : opset_t<ARG>
{
static void process_op (op_code_t op, ENV &env, PARAM& param)
@ -254,7 +255,7 @@ struct cs_opset_t : opset_t<ARG>
switch (op) {
case OpCode_return:
env.returnFromSubr ();
env.return_from_subr ();
break;
case OpCode_endchar:
OPSET::check_width (op, env, param);
@ -267,11 +268,11 @@ struct cs_opset_t : opset_t<ARG>
break;
case OpCode_callsubr:
env.callSubr (env.localSubrs, CSType_LocalSubr);
env.call_subr (env.localSubrs, CSType_LocalSubr);
break;
case OpCode_callgsubr:
env.callSubr (env.globalSubrs, CSType_GlobalSubr);
env.call_subr (env.globalSubrs, CSType_GlobalSubr);
break;
case OpCode_hstem:
@ -550,8 +551,13 @@ struct path_procs_t
static void rcurveline (ENV &env, PARAM& param)
{
unsigned int arg_count = env.argStack.get_count ();
if (unlikely (arg_count < 8))
return;
unsigned int i = 0;
for (; i + 6 <= env.argStack.get_count (); i += 6)
unsigned int curve_limit = arg_count - 2;
for (; i + 6 <= curve_limit; i += 6)
{
point_t pt1 = env.get_pt ();
pt1.move (env.eval_arg (i), env.eval_arg (i+1));
@ -561,34 +567,34 @@ struct path_procs_t
pt3.move (env.eval_arg (i+4), env.eval_arg (i+5));
PATH::curve (env, param, pt1, pt2, pt3);
}
for (; i + 2 <= env.argStack.get_count (); i += 2)
{
point_t pt1 = env.get_pt ();
pt1.move (env.eval_arg (i), env.eval_arg (i+1));
PATH::line (env, param, pt1);
}
point_t pt1 = env.get_pt ();
pt1.move (env.eval_arg (i), env.eval_arg (i+1));
PATH::line (env, param, pt1);
}
static void rlinecurve (ENV &env, PARAM& param)
{
unsigned int arg_count = env.argStack.get_count ();
if (unlikely (arg_count < 8))
return;
unsigned int i = 0;
unsigned int line_limit = (env.argStack.get_count () % 6);
unsigned int line_limit = arg_count - 6;
for (; i + 2 <= line_limit; i += 2)
{
point_t pt1 = env.get_pt ();
pt1.move (env.eval_arg (i), env.eval_arg (i+1));
PATH::line (env, param, pt1);
}
for (; i + 6 <= env.argStack.get_count (); i += 6)
{
point_t pt1 = env.get_pt ();
pt1.move (env.eval_arg (i), env.eval_arg (i+1));
point_t pt2 = pt1;
pt2.move (env.eval_arg (i+2), env.eval_arg (i+3));
point_t pt3 = pt2;
pt3.move (env.eval_arg (i+4), env.eval_arg (i+5));
PATH::curve (env, param, pt1, pt2, pt3);
}
point_t pt1 = env.get_pt ();
pt1.move (env.eval_arg (i), env.eval_arg (i+1));
point_t pt2 = pt1;
pt2.move (env.eval_arg (i+2), env.eval_arg (i+3));
point_t pt3 = pt2;
pt3.move (env.eval_arg (i+4), env.eval_arg (i+5));
PATH::curve (env, param, pt1, pt2, pt3);
}
static void vvcurveto (ENV &env, PARAM& param)

View File

@ -27,8 +27,6 @@
#define HB_CFF_INTERP_DICT_COMMON_HH
#include "hb-cff-interp-common.hh"
#include <math.h>
#include <float.h>
namespace CFF {
@ -58,19 +56,6 @@ struct top_dict_values_t : dict_values_t<OPSTR>
}
void fini () { dict_values_t<OPSTR>::fini (); }
unsigned int calculate_serialized_op_size (const OPSTR& opstr) const
{
switch (opstr.op)
{
case OpCode_CharStrings:
case OpCode_FDArray:
return OpCode_Size (OpCode_longintdict) + 4 + OpCode_Size (opstr.op);
default:
return opstr.str.length;
}
}
unsigned int charStringsOffset;
unsigned int FDArrayOffset;
};
@ -94,130 +79,52 @@ struct dict_opset_t : opset_t<number_t>
}
}
/* Turns CFF's BCD format into strtod understandable string */
static double parse_bcd (byte_str_ref_t& str_ref)
{
bool neg = false;
double int_part = 0;
uint64_t frac_part = 0;
uint32_t frac_count = 0;
bool exp_neg = false;
uint32_t exp_part = 0;
bool exp_overflow = false;
enum Part { INT_PART=0, FRAC_PART, EXP_PART } part = INT_PART;
enum Nibble { DECIMAL=10, EXP_POS, EXP_NEG, RESERVED, NEG, END };
const uint64_t MAX_FRACT = 0xFFFFFFFFFFFFFull; /* 1^52-1 */
const uint32_t MAX_EXP = 0x7FFu; /* 1^11-1 */
if (unlikely (str_ref.in_error ())) return .0;
double value = 0.0;
enum Nibble { DECIMAL=10, EXP_POS, EXP_NEG, RESERVED, NEG, END };
char buf[32];
unsigned char byte = 0;
for (uint32_t i = 0;; i++)
for (unsigned i = 0, count = 0; count < ARRAY_LENGTH (buf); ++i, ++count)
{
char d;
if ((i & 1) == 0)
unsigned nibble;
if (!(i & 1))
{
if (!str_ref.avail ())
{
str_ref.set_error ();
return 0.0;
}
if (unlikely (!str_ref.avail ())) break;
byte = str_ref[0];
str_ref.inc ();
d = byte >> 4;
nibble = byte >> 4;
}
else
d = byte & 0x0F;
nibble = byte & 0x0F;
switch (d)
if (unlikely (nibble == RESERVED)) break;
else if (nibble == END)
{
case RESERVED:
str_ref.set_error ();
return value;
case END:
value = (double)(neg? -int_part: int_part);
if (frac_count > 0)
{
double frac = (frac_part / pow (10.0, (double)frac_count));
if (neg) frac = -frac;
value += frac;
}
if (unlikely (exp_overflow))
{
if (value == 0.0)
return value;
if (exp_neg)
return neg? -DBL_MIN: DBL_MIN;
else
return neg? -DBL_MAX: DBL_MAX;
}
if (exp_part != 0)
{
if (exp_neg)
value /= pow (10.0, (double)exp_part);
else
value *= pow (10.0, (double)exp_part);
}
return value;
case NEG:
if (i != 0)
{
str_ref.set_error ();
return 0.0;
}
neg = true;
const char *p = buf;
double pv;
if (unlikely (!hb_parse_double (&p, p + count, &pv, true/* whole buffer */)))
break;
case DECIMAL:
if (part != INT_PART)
{
str_ref.set_error ();
return value;
}
part = FRAC_PART;
break;
case EXP_NEG:
exp_neg = true;
HB_FALLTHROUGH;
case EXP_POS:
if (part == EXP_PART)
{
str_ref.set_error ();
return value;
}
part = EXP_PART;
break;
default:
switch (part) {
default:
case INT_PART:
int_part = (int_part * 10) + d;
break;
case FRAC_PART:
if (likely (frac_part <= MAX_FRACT / 10))
{
frac_part = (frac_part * 10) + (unsigned)d;
frac_count++;
}
break;
case EXP_PART:
if (likely (exp_part * 10 + d <= MAX_EXP))
{
exp_part = (exp_part * 10) + d;
}
else
exp_overflow = true;
break;
}
return pv;
}
else
{
buf[count] = "0123456789.EE?-?"[nibble];
if (nibble == EXP_NEG)
{
++count;
if (unlikely (count == ARRAY_LENGTH (buf))) break;
buf[count] = '-';
}
}
}
return value;
str_ref.set_error ();
return .0;
}
static bool is_hint_op (op_code_t op)

View File

@ -40,7 +40,7 @@ struct cff1_cs_interp_env_t : cs_interp_env_t<number_t, CFF1Subrs>
template <typename ACC>
void init (const byte_str_t &str, ACC &acc, unsigned int fd)
{
SUPER::init (str, *acc.globalSubrs, *acc.privateDicts[fd].localSubrs);
SUPER::init (str, acc.globalSubrs, acc.privateDicts[fd].localSubrs);
processed_width = false;
has_width = false;
arg_start = 0;
@ -81,7 +81,7 @@ struct cff1_cs_interp_env_t : cs_interp_env_t<number_t, CFF1Subrs>
typedef cs_interp_env_t<number_t, CFF1Subrs> SUPER;
};
template <typename OPSET, typename PARAM, typename PATH=path_procs_null_t<cff1_cs_interp_env_t, PARAM> >
template <typename OPSET, typename PARAM, typename PATH=path_procs_null_t<cff1_cs_interp_env_t, PARAM>>
struct cff1_cs_opset_t : cs_opset_t<number_t, OPSET, cff1_cs_interp_env_t, PARAM, PATH>
{
/* PostScript-originated legacy opcodes (OpCode_add etc) are unsupported */

View File

@ -80,9 +80,9 @@ struct cff2_cs_interp_env_t : cs_interp_env_t<blend_arg_t, CFF2Subrs>
{
template <typename ACC>
void init (const byte_str_t &str, ACC &acc, unsigned int fd,
const int *coords_=nullptr, unsigned int num_coords_=0)
const int *coords_=nullptr, unsigned int num_coords_=0)
{
SUPER::init (str, *acc.globalSubrs, *acc.privateDicts[fd].localSubrs);
SUPER::init (str, acc.globalSubrs, acc.privateDicts[fd].localSubrs);
coords = coords_;
num_coords = num_coords_;
@ -90,7 +90,7 @@ struct cff2_cs_interp_env_t : cs_interp_env_t<blend_arg_t, CFF2Subrs>
seen_blend = false;
seen_vsindex_ = false;
scalars.init ();
do_blend = (coords != nullptr) && num_coords && (varStore != &Null(CFF2VariationStore));
do_blend = num_coords && coords && varStore->size;
set_ivs (acc.privateDicts[fd].ivs);
}
@ -134,8 +134,7 @@ struct cff2_cs_interp_env_t : cs_interp_env_t<blend_arg_t, CFF2Subrs>
if (do_blend)
{
scalars.resize (region_count);
varStore->varStore.get_scalars (get_ivs (),
(int *)coords, num_coords,
varStore->varStore.get_scalars (get_ivs (), coords, num_coords,
&scalars[0], region_count);
}
seen_blend = true;
@ -193,7 +192,7 @@ struct cff2_cs_interp_env_t : cs_interp_env_t<blend_arg_t, CFF2Subrs>
typedef cs_interp_env_t<blend_arg_t, CFF2Subrs> SUPER;
};
template <typename OPSET, typename PARAM, typename PATH=path_procs_null_t<cff2_cs_interp_env_t, PARAM> >
template <typename OPSET, typename PARAM, typename PATH=path_procs_null_t<cff2_cs_interp_env_t, PARAM>>
struct cff2_cs_opset_t : cs_opset_t<blend_arg_t, OPSET, cff2_cs_interp_env_t, PARAM, PATH>
{
static void process_op (op_code_t op, cff2_cs_interp_env_t &env, PARAM& param)

View File

@ -27,14 +27,13 @@
*/
#include "hb.hh"
#include "hb-machinery.hh"
#include <locale.h>
#ifdef HAVE_XLOCALE_H
#include <xlocale.h>
#endif
#ifdef HB_NO_SETLOCALE
#define setlocale(Category, Locale) "C"
#endif
/**
* SECTION:hb-common
@ -64,13 +63,12 @@ _hb_options_init ()
{
const char *p = strchr (c, ':');
if (!p)
p = c + strlen (c);
p = c + strlen (c);
#define OPTION(name, symbol) \
if (0 == strncmp (c, name, p - c) && strlen (name) == p - c) u.opts.symbol = true;
if (0 == strncmp (c, name, p - c) && strlen (name) == static_cast<size_t>(p - c)) do { u.opts.symbol = true; } while (0)
OPTION ("uniscribe-bug-compatible", uniscribe_bug_compatible);
OPTION ("aat", aat);
#undef OPTION
@ -334,14 +332,14 @@ retry:
/**
* hb_language_from_string:
* @str: (array length=len) (element-type uint8_t): a string representing
* a BCP 47 language tag
* a BCP 47 language tag
* @len: length of the @str, or -1 if it is %NULL-terminated.
*
* Converts @str representing a BCP 47 language tag to the corresponding
* Converts @str representing a BCP 47 language tag to the corresponding
* #hb_language_t.
*
* Return value: (transfer none):
* The #hb_language_t corresponding to the BCP 47 language tag.
* The #hb_language_t corresponding to the BCP 47 language tag.
*
* Since: 0.9.2
**/
@ -356,7 +354,7 @@ hb_language_from_string (const char *str, int len)
{
/* NUL-terminate it. */
char strbuf[64];
len = MIN (len, (int) sizeof (strbuf) - 1);
len = hb_min (len, (int) sizeof (strbuf) - 1);
memcpy (strbuf, str, len);
strbuf[len] = '\0';
item = lang_find_or_insert (strbuf);
@ -382,7 +380,8 @@ hb_language_from_string (const char *str, int len)
const char *
hb_language_to_string (hb_language_t language)
{
/* This is actually nullptr-safe! */
if (unlikely (!language)) return nullptr;
return language->s;
}
@ -422,12 +421,12 @@ hb_language_get_default ()
/**
* hb_script_from_iso15924_tag:
* @tag: an #hb_tag_t representing an ISO 15924 tag.
* @tag: an #hb_tag_t representing an ISO 15924 tag.
*
* Converts an ISO 15924 script tag to a corresponding #hb_script_t.
* Converts an ISO 15924 script tag to a corresponding #hb_script_t.
*
* Return value:
* An #hb_script_t corresponding to the ISO 15924 tag.
* An #hb_script_t corresponding to the ISO 15924 tag.
*
* Since: 0.9.2
**/
@ -468,15 +467,15 @@ hb_script_from_iso15924_tag (hb_tag_t tag)
/**
* hb_script_from_string:
* @str: (array length=len) (element-type uint8_t): a string representing an
* ISO 15924 tag.
* ISO 15924 tag.
* @len: length of the @str, or -1 if it is %NULL-terminated.
*
* Converts a string @str representing an ISO 15924 script tag to a
* Converts a string @str representing an ISO 15924 script tag to a
* corresponding #hb_script_t. Shorthand for hb_tag_from_string() then
* hb_script_from_iso15924_tag().
*
* Return value:
* An #hb_script_t corresponding to the ISO 15924 tag.
* An #hb_script_t corresponding to the ISO 15924 tag.
*
* Since: 0.9.2
**/
@ -488,12 +487,12 @@ hb_script_from_string (const char *str, int len)
/**
* hb_script_to_iso15924_tag:
* @script: an #hb_script_ to convert.
* @script: an #hb_script_t to convert.
*
* See hb_script_from_iso15924_tag().
*
* Return value:
* An #hb_tag_t representing an ISO 15924 script tag.
* An #hb_tag_t representing an ISO 15924 script tag.
*
* Since: 0.9.2
**/
@ -590,38 +589,6 @@ hb_script_get_horizontal_direction (hb_script_t script)
}
/* hb_user_data_array_t */
bool
hb_user_data_array_t::set (hb_user_data_key_t *key,
void * data,
hb_destroy_func_t destroy,
hb_bool_t replace)
{
if (!key)
return false;
if (replace) {
if (!data && !destroy) {
items.remove (key, lock);
return true;
}
}
hb_user_data_item_t item = {key, data, destroy};
bool ret = !!items.replace_or_insert (item, lock, (bool) replace);
return ret;
}
void *
hb_user_data_array_t::get (hb_user_data_key_t *key)
{
hb_user_data_item_t item = {nullptr, nullptr, nullptr};
return items.find (key, &item, lock) ? item.data : nullptr;
}
/* hb_version */
@ -719,131 +686,24 @@ parse_char (const char **pp, const char *end, char c)
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, 10);
if (errno || p == pend)
return false;
/* Intentionally use hb_parse_int inside instead of hb_parse_uint,
* such that -1 turns into "big number"... */
int v;
if (unlikely (!hb_parse_int (pp, end, &v))) return false;
*pv = v;
*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, 10);
if (errno || p == pend)
return false;
/* Intentionally use hb_parse_int inside instead of hb_parse_uint,
* such that -1 turns into "big number"... */
int v;
if (unlikely (!hb_parse_int (pp, end, &v))) return false;
*pv = v;
*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
#if HB_USE_ATEXIT
static void free_static_C_locale ();
#endif
static struct hb_C_locale_lazy_loader_t : hb_lazy_loader_t<hb_remove_pointer (HB_LOCALE_T),
hb_C_locale_lazy_loader_t>
{
static HB_LOCALE_T create ()
{
HB_LOCALE_T C_locale = HB_CREATE_LOCALE ("C");
#if HB_USE_ATEXIT
atexit (free_static_C_locale);
#endif
return C_locale;
}
static void destroy (HB_LOCALE_T p)
{
HB_FREE_LOCALE (p);
}
static HB_LOCALE_T get_null ()
{
return nullptr;
}
} static_C_locale;
#if HB_USE_ATEXIT
static
void free_static_C_locale ()
{
static_C_locale.free_instance ();
}
#endif
static HB_LOCALE_T
get_C_locale ()
{
return static_C_locale.get_unconst ();
}
#endif /* USE_XLOCALE */
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;
}
@ -953,7 +813,7 @@ parse_feature_value_postfix (const char **pp, const char *end, hb_feature_t *fea
{
bool had_equal = parse_char (pp, end, '=');
bool had_value = parse_uint32 (pp, end, &feature->value) ||
parse_bool (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. */
@ -1067,25 +927,25 @@ hb_feature_to_string (hb_feature_t *feature,
len += 4;
while (len && s[len - 1] == ' ')
len--;
if (feature->start != 0 || feature->end != (unsigned int) -1)
if (feature->start != HB_FEATURE_GLOBAL_START || feature->end != HB_FEATURE_GLOBAL_END)
{
s[len++] = '[';
if (feature->start)
len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->start));
len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->start));
if (feature->end != feature->start + 1) {
s[len++] = ':';
if (feature->end != (unsigned int) -1)
len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->end));
if (feature->end != HB_FEATURE_GLOBAL_END)
len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->end));
}
s[len++] = ']';
}
if (feature->value > 1)
{
s[len++] = '=';
len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->value));
len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->value));
}
assert (len < ARRAY_LENGTH (s));
len = MIN (len, size - 1);
len = hb_min (len, size - 1);
memcpy (buf, s, len);
buf[len] = '\0';
}
@ -1096,7 +956,11 @@ 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);
double v;
if (unlikely (!hb_parse_double (pp, end, &v))) return false;
variation->value = v;
return true;
}
static bool
@ -1152,14 +1016,71 @@ hb_variation_to_string (hb_variation_t *variation,
while (len && s[len - 1] == ' ')
len--;
s[len++] = '=';
len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%g", (double) variation->value));
len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%g", (double) variation->value));
assert (len < ARRAY_LENGTH (s));
len = MIN (len, size - 1);
len = hb_min (len, size - 1);
memcpy (buf, s, len);
buf[len] = '\0';
}
/**
* hb_color_get_alpha:
* color: a #hb_color_t we are interested in its channels.
*
* Return value: Alpha channel value of the given color
*
* Since: 2.1.0
*/
uint8_t
(hb_color_get_alpha) (hb_color_t color)
{
return hb_color_get_alpha (color);
}
/**
* hb_color_get_red:
* color: a #hb_color_t we are interested in its channels.
*
* Return value: Red channel value of the given color
*
* Since: 2.1.0
*/
uint8_t
(hb_color_get_red) (hb_color_t color)
{
return hb_color_get_red (color);
}
/**
* hb_color_get_green:
* color: a #hb_color_t we are interested in its channels.
*
* Return value: Green channel value of the given color
*
* Since: 2.1.0
*/
uint8_t
(hb_color_get_green) (hb_color_t color)
{
return hb_color_get_green (color);
}
/**
* hb_color_get_blue:
* color: a #hb_color_t we are interested in its channels.
*
* Return value: Blue channel value of the given color
*
* Since: 2.1.0
*/
uint8_t
(hb_color_get_blue) (hb_color_t color)
{
return hb_color_get_blue (color);
}
/* If there is no visibility control, then hb-static.cc will NOT
* define anything. Instead, we get it to define one set in here
* only, so only libharfbuzz.so defines them, not other libs. */

View File

@ -63,6 +63,8 @@ typedef __int32 int32_t;
typedef unsigned __int32 uint32_t;
typedef __int64 int64_t;
typedef unsigned __int64 uint64_t;
#elif defined (__KERNEL__)
# include <linux/types.h>
#else
# include <stdint.h>
#endif
@ -423,6 +425,21 @@ typedef void (*hb_destroy_func_t) (void *user_data);
*/
#define HB_FEATURE_GLOBAL_END ((unsigned int) -1)
/**
* hb_feature_t:
* @tag: a feature tag
* @value: 0 disables the feature, non-zero (usually 1) enables the feature.
* For features implemented as lookup type 3 (like 'salt') the @value is a one
* based index into the alternates.
* @start: the cluster to start applying this feature setting (inclusive).
* @end: the cluster to end applying this feature setting (exclusive).
*
* The #hb_feature_t is the structure that holds information about requested
* feature application. The feature will be applied with the given value to all
* glyphs which are in clusters between @start (inclusive) and @end (exclusive).
* Setting start to @HB_FEATURE_GLOBAL_START and end to @HB_FEATURE_GLOBAL_END
* specifies that the feature always applies to the entire buffer.
*/
typedef struct hb_feature_t {
hb_tag_t tag;
uint32_t value;
@ -467,39 +484,21 @@ typedef uint32_t hb_color_t;
#define HB_COLOR(b,g,r,a) ((hb_color_t) HB_TAG ((b),(g),(r),(a)))
/**
* hb_color_get_alpha:
*
*
*
* Since: 2.1.0
*/
HB_EXTERN uint8_t
hb_color_get_alpha (hb_color_t color);
#define hb_color_get_alpha(color) ((color) & 0xFF)
/**
* hb_color_get_red:
*
*
*
* Since: 2.1.0
*/
#define hb_color_get_red(color) (((color) >> 8) & 0xFF)
/**
* hb_color_get_green:
*
*
*
* Since: 2.1.0
*/
#define hb_color_get_green(color) (((color) >> 16) & 0xFF)
/**
* hb_color_get_blue:
*
*
*
* Since: 2.1.0
*/
#define hb_color_get_blue(color) (((color) >> 24) & 0xFF)
HB_EXTERN uint8_t
hb_color_get_red (hb_color_t color);
#define hb_color_get_red(color) (((color) >> 8) & 0xFF)
HB_EXTERN uint8_t
hb_color_get_green (hb_color_t color);
#define hb_color_get_green(color) (((color) >> 16) & 0xFF)
HB_EXTERN uint8_t
hb_color_get_blue (hb_color_t color);
#define hb_color_get_blue(color) (((color) >> 24) & 0xFF)
HB_END_DECLS

Some files were not shown because too many files have changed in this diff Show More