mirror of
https://github.com/opencv/opencv.git
synced 2026-07-24 21:03:03 +04:00
Compare commits
20 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 555f0901a3 | |||
| 3f45dab070 | |||
| a3a4d4adac | |||
| 8bbe8eeb86 | |||
| a18356853d | |||
| a8c0c30046 | |||
| 03539f67f5 | |||
| 9a966e96ad | |||
| 7b7a62528f | |||
| 4c8c215d59 | |||
| a3dc35d47d | |||
| 172ea5a00c | |||
| 845d95aaac | |||
| e32cab0e31 | |||
| f55665e5ca | |||
| 3f0664973c | |||
| c4caa07d62 | |||
| 410b6fdd97 | |||
| 43dd75bf43 | |||
| 8e434d68f9 |
Vendored
+70
@@ -0,0 +1,70 @@
|
||||
# ----------------------------------------------------------------------------
|
||||
# CMake file for the bundled HarfBuzz text-shaping library. See root CMakeLists.txt
|
||||
#
|
||||
# OpenCV vendors a minimal subset of HarfBuzz (core + HB_HAS_RASTER) as a
|
||||
# normal static library: each .cc is a separate translation unit. The subset
|
||||
# is produced by hb_extract.py (see that script to update HarfBuzz).
|
||||
# ----------------------------------------------------------------------------
|
||||
project(${HARFBUZZ_LIBRARY} CXX)
|
||||
|
||||
ocv_include_directories("${CMAKE_CURRENT_SOURCE_DIR}/src")
|
||||
|
||||
# Every .cc copied by hb_extract.py is a real translation unit (the script
|
||||
# never copies non-TU .cc files), so a plain recursive glob is exactly right.
|
||||
file(GLOB_RECURSE lib_srcs src/*.cc)
|
||||
file(GLOB_RECURSE lib_hdrs src/*.h src/*.hh)
|
||||
|
||||
# HarfBuzz is built with HB_TINY for minimum footprint. Because we request the
|
||||
# software rasterizer (HB_HAS_RASTER, defined below), HB_TINY does NOT disable
|
||||
# the draw/paint/color APIs nor CFF (PostScript/.otf
|
||||
# outlines) -- see hb-config.hh: HB_NO_DRAW/COLOR/PAINT and the TINY->HB_NO_CFF
|
||||
# rule only trigger when no HB_HAS_* backend is requested. So CFF (CJK/Indic),
|
||||
# COLR/CPAL color and our rasterizer all survive HB_TINY.
|
||||
#
|
||||
# Two features HB_TINY (via HB_LEAN) would otherwise drop are restored through
|
||||
# HarfBuzz's config-override hook (hb-opencv-config.hh): thread-safety (HB_NO_MT)
|
||||
# and variable fonts (HB_NO_VAR). No platform backends (CoreText/DirectWrite/...).
|
||||
|
||||
add_library(${HARFBUZZ_LIBRARY} STATIC ${OPENCV_3RDPARTY_EXCLUDE_FROM_ALL} ${lib_srcs} ${lib_hdrs})
|
||||
|
||||
target_compile_definitions(${HARFBUZZ_LIBRARY} PRIVATE
|
||||
HB_TINY
|
||||
HB_HAS_RASTER # request the software rasterizer: keeps draw/paint/color + CFF under HB_TINY
|
||||
"HB_CONFIG_OVERRIDE_H=\"hb-opencv-config.hh\"")
|
||||
|
||||
set_target_properties(${HARFBUZZ_LIBRARY} PROPERTIES
|
||||
CXX_STANDARD 17
|
||||
CXX_STANDARD_REQUIRED ON
|
||||
)
|
||||
|
||||
ocv_warnings_disable(CMAKE_CXX_FLAGS
|
||||
-Wunused-variable -Wunused-function -Wunused-parameter
|
||||
-Wunused-but-set-variable # clang15/gcc
|
||||
-Wshadow
|
||||
-Wmissing-declarations # gcc
|
||||
-Wmissing-prototypes # clang
|
||||
-Wimplicit-fallthrough
|
||||
-Wextra-semi # clang
|
||||
-Wdeprecated-copy # gcc/clang
|
||||
-Wsuggest-override
|
||||
-Wcast-function-type
|
||||
-Wclass-memaccess # gcc
|
||||
-Wexpansion-to-defined
|
||||
)
|
||||
ocv_warnings_disable(CMAKE_CXX_FLAGS /wd4244 /wd4267 /wd4127 /wd4146 /wd4456 /wd4459) # MSVC
|
||||
|
||||
set_target_properties(${HARFBUZZ_LIBRARY}
|
||||
PROPERTIES OUTPUT_NAME ${HARFBUZZ_LIBRARY}
|
||||
DEBUG_POSTFIX "${OPENCV_DEBUG_POSTFIX}"
|
||||
COMPILE_PDB_NAME ${HARFBUZZ_LIBRARY}
|
||||
COMPILE_PDB_NAME_DEBUG "${HARFBUZZ_LIBRARY}${OPENCV_DEBUG_POSTFIX}"
|
||||
ARCHIVE_OUTPUT_DIRECTORY ${3P_LIBRARY_OUTPUT_PATH}
|
||||
)
|
||||
|
||||
if(ENABLE_SOLUTION_FOLDERS)
|
||||
set_target_properties(${HARFBUZZ_LIBRARY} PROPERTIES FOLDER "3rdparty")
|
||||
endif()
|
||||
|
||||
if(NOT BUILD_SHARED_LIBS)
|
||||
ocv_install_target(${HARFBUZZ_LIBRARY} EXPORT OpenCVModules ARCHIVE DESTINATION ${OPENCV_3P_LIB_INSTALL_PATH} COMPONENT dev OPTIONAL)
|
||||
endif()
|
||||
Vendored
+42
@@ -0,0 +1,42 @@
|
||||
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-2022 Google, Inc.
|
||||
Copyright © 2015-2020 Ebrahim Byagowi
|
||||
Copyright © 2019,2020 Facebook, Inc.
|
||||
Copyright © 2012,2015 Mozilla Foundation
|
||||
Copyright © 2011 Codethink Limited
|
||||
Copyright © 2008,2010 Nokia Corporation and/or its subsidiary(-ies)
|
||||
Copyright © 2009 Keith Stribley
|
||||
Copyright © 2011 Martin Hosken and SIL International
|
||||
Copyright © 2007 Chris Wilson
|
||||
Copyright © 2005,2006,2020,2021,2022,2023 Behdad Esfahbod
|
||||
Copyright © 2004,2007,2008,2009,2010,2013,2021,2022,2023 Red Hat, Inc.
|
||||
Copyright © 1998-2005 David Turner and Werner Lemberg
|
||||
Copyright © 2016 Igalia S.L.
|
||||
Copyright © 2022 Matthias Clasen
|
||||
Copyright © 2018,2021 Khaled Hosny
|
||||
Copyright © 2018,2019,2020 Adobe, Inc
|
||||
Copyright © 2013-2015 Alexei Podtelezhnikov
|
||||
|
||||
For full copyright notices consult the individual files in the package.
|
||||
|
||||
|
||||
Permission is hereby granted, without written agreement and without
|
||||
license or royalty fees, to use, copy, modify, and distribute this
|
||||
software and its documentation for any purpose, provided that the
|
||||
above copyright notice and the following two paragraphs appear in
|
||||
all copies of this software.
|
||||
|
||||
IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
|
||||
DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
|
||||
ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
|
||||
IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||
DAMAGE.
|
||||
|
||||
THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
|
||||
BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
|
||||
ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
|
||||
PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
|
||||
Vendored
+220
@@ -0,0 +1,220 @@
|
||||
# HarfBuzz
|
||||
|
||||
<div align="center">
|
||||
|
||||
<p><img src="HarfBuzz.png" alt="HarfBuzz Logo" width="256"/></p>
|
||||
|
||||
[](https://github.com/harfbuzz/harfbuzz/actions/workflows/linux.yml)
|
||||
[](https://github.com/harfbuzz/harfbuzz/actions/workflows/macos.yml)
|
||||
[](https://github.com/harfbuzz/harfbuzz/actions/workflows/msvc.yml)
|
||||
[](https://oss-fuzz-build-logs.storage.googleapis.com/index.html#harfbuzz)
|
||||
[](https://scan.coverity.com/projects/harfbuzz)
|
||||
[](https://repology.org/project/harfbuzz/versions)
|
||||
[](https://securityscorecards.dev/viewer/?uri=github.com/harfbuzz/harfbuzz)
|
||||
|
||||
</div>
|
||||
|
||||
HarfBuzz started as a text shaping engine but has grown into a
|
||||
full font platform — the `ffmpeg` of text shaping. It primarily
|
||||
supports [OpenType][1], but also [Apple Advanced Typography][2].
|
||||
|
||||
HarfBuzz shapes the majority of text on modern screens.
|
||||
|
||||
HarfBuzz is optimized for robustness, correctness, and performance
|
||||
— in that order. Achieve all.
|
||||
|
||||
**[Try it live at harfbuzz-world.cc](https://harfbuzz-world.cc/)** — an interactive playground for shaping, subsetting, rasterization, vector output, and GPU rendering, all running in your browser.
|
||||
|
||||
Here is a quick map of its components:
|
||||
|
||||
### Core libraries
|
||||
|
||||
| Library | Description |
|
||||
|---------|-------------|
|
||||
| **libharfbuzz** | Text shaping, draw API, paint API. Highly configurable (see [CONFIG.md](CONFIG.md)). Optional integration backends compiled in: hb-ft (FreeType), hb-coretext (macOS), hb-uniscribe (Windows), hb-directwrite (Windows), hb-gdi (Windows), hb-glib, hb-graphite2. |
|
||||
| **libharfbuzz-subset** | Font subsetting and variable-font instancing. |
|
||||
|
||||
### Auxiliary libraries
|
||||
|
||||
| Library | Description |
|
||||
|---------|-------------|
|
||||
| **libharfbuzz-icu** | ICU Unicode integration. |
|
||||
| **libharfbuzz-cairo** | Cairo rendering integration. |
|
||||
| **libharfbuzz-gobject** | GObject/GI bindings. |
|
||||
|
||||
### Experimental libraries
|
||||
|
||||
| Library | Description |
|
||||
|---------|-------------|
|
||||
| **libharfbuzz-raster** | Glyph rasterization to bitmaps, including color fonts. Uses hb-draw and hb-paint. |
|
||||
| **libharfbuzz-vector** | Glyph output to vector formats (currently SVG), including color fonts. Uses hb-draw and hb-paint. |
|
||||
| **libharfbuzz-gpu** | Encodes glyph outlines for GPU rasterization (Slug algorithm). Provides shader sources in GLSL, WGSL, MSL, and HLSL. [Live demo.](https://harfbuzz.github.io/hb-gpu-demo/) |
|
||||
|
||||
Notable missing feature: font hinting (including autohinting)
|
||||
is not implemented. For hinted rasterization, use FreeType or
|
||||
Skrifa.
|
||||
|
||||
For simplified builds, amalgamated sources are available:
|
||||
`harfbuzz.cc` (just libharfbuzz), `harfbuzz-subset.cc` (just
|
||||
libharfbuzz-subset), or `harfbuzz-world.cc` (everything, driven
|
||||
by a custom `hb-features.h`). For a live in-browser playground
|
||||
plus a worked example of the world.cc single-file build, see
|
||||
[harfbuzz-world.cc][26].
|
||||
|
||||
### Command-line tools
|
||||
|
||||
| Tool | Description |
|
||||
|------|-------------|
|
||||
| **hb-shape** | Shape text and display glyph output. |
|
||||
| **hb-view** | Render shaped text to an image. |
|
||||
| **hb-subset** | Subset and optimize fonts. |
|
||||
| **hb-info** | Display font metadata. |
|
||||
| **hb-raster** | Render glyphs to bitmap images. |
|
||||
| **hb-vector** | Render glyphs to vector formats (SVG). |
|
||||
| **hb-gpu** | Interactive GPU text rendering. |
|
||||
|
||||
The canonical source tree and bug trackers are available on [github][4].
|
||||
Both development and user support discussion around HarfBuzz happen on
|
||||
[github][4] as well.
|
||||
|
||||
For license information, see [COPYING](COPYING).
|
||||
|
||||
## API stability
|
||||
|
||||
The API that comes with `hb.h` will not change incompatibly. Other, peripheral,
|
||||
headers are more likely to go through minor modifications, but again, we do our
|
||||
best to never change API in an incompatible way. We will never break the ABI.
|
||||
|
||||
The API and ABI are stable even across major version number jumps. In fact,
|
||||
current HarfBuzz is API/ABI compatible all the way back to the 0.9.x series.
|
||||
If one day we need to break the API/ABI, that would be called a new library.
|
||||
|
||||
As such, we bump the major version number only when we add major new features,
|
||||
the minor version when there is new API, and the micro version when there
|
||||
are bug fixes.
|
||||
|
||||
## Documentation
|
||||
|
||||
For user manual as well as API documentation, check: https://harfbuzz.github.io
|
||||
|
||||
## Download
|
||||
|
||||
Tarball releases and Win32/Win64 binary bundles are available on the
|
||||
[github releases][3] page.
|
||||
|
||||
## Development
|
||||
|
||||
For build information, see [BUILD.md](BUILD.md).
|
||||
|
||||
For custom configurations, see [CONFIG.md](CONFIG.md).
|
||||
|
||||
For testing and profiling, see [TESTING.md](TESTING.md).
|
||||
|
||||
For using with Python, see [README.python.md](README.python.md). There is also [uharfbuzz](https://github.com/harfbuzz/uharfbuzz).
|
||||
|
||||
For cross-compiling to Windows from Linux or macOS, see [README.mingw.md](README.mingw.md).
|
||||
|
||||
To report bugs or submit patches please use [github][4] issues and pull-requests.
|
||||
|
||||
### Developer documents
|
||||
|
||||
To get a better idea of where HarfBuzz stands in the text rendering stack you
|
||||
may want to read [State of Text Rendering 2024][6].
|
||||
Here are a few presentation slides about HarfBuzz over the years:
|
||||
|
||||
- 2026 – [HarfBuzz at 20!][25]
|
||||
- 2016 – [Ten Years of HarfBuzz][20]
|
||||
- 2014 – [Unicode, OpenType, and HarfBuzz: Closing the Circle][7]
|
||||
- 2012 – [HarfBuzz, The Free and Open Text Shaping Engine][8]
|
||||
- 2009 – [HarfBuzz: the Free and Open Shaping Engine][9]
|
||||
|
||||
More presentations and papers are available on [behdad][11]'s website.
|
||||
In particular, the following _studies_ are relevant to HarfBuzz development:
|
||||
|
||||
- 2025 – [AAT layout caches][24]
|
||||
- 2025 – [OpenType Layout lookup caches][23]
|
||||
- 2025 – [Introducing HarfRust][22]
|
||||
- 2025 – [Subsetting][21]
|
||||
- 2025 – [Caching][12]
|
||||
- 2025 – [`hb-decycler`][13]
|
||||
- 2022 – [`hb-iter`][14]
|
||||
- 2022 – [A C library written in C++][15]
|
||||
- 2022 – [The case of the slow `hb-ft` `>h_advance` function][18]
|
||||
- 2022 – [PackTab: A static integer table packer][16]
|
||||
- 2020 – [HarfBuzz OT+AAT "Unishaper"][19]
|
||||
- 2014 – [Building the Indic Shaper][17]
|
||||
- 2012 – [Memory Consumption][10]
|
||||
|
||||
|
||||
## Name
|
||||
|
||||
HarfBuzz /hærfˈbɒːz/
|
||||
|
||||
From Persian حرف (*Harf*: letter) and باز (*Buzz*: open).
|
||||
Transliteration of the Persian calque for *OpenType*.
|
||||
|
||||
As a noun: *The* Open Source *text shaping* engine.
|
||||
|
||||
As an adjective: Insincerely talkative; glib. A nod to the
|
||||
GNOME project where HarfBuzz originates from.
|
||||
|
||||
The logo shows حرفباز in the IranNastaliq font, on a Damascus
|
||||
steel background.
|
||||
|
||||
> Background: Originally there was this font format called TrueType. People and
|
||||
> companies started calling their type engines all things ending in Type:
|
||||
> FreeType, CoolType, ClearType, etc. And then came OpenType, which is the
|
||||
> successor of TrueType. So, for my OpenType implementation, I decided to stick
|
||||
> with the concept but use the Persian translation. Which is fitting given that
|
||||
> Persian is written in the Arabic script, and OpenType is an extension of
|
||||
> TrueType that adds support for complex script rendering, and HarfBuzz is an
|
||||
> implementation of OpenType text shaping.
|
||||
|
||||
## Users
|
||||
|
||||
HarfBuzz is used in Android, Chrome, ChromeOS, Firefox, Flutter, GNOME, GTK+, KDE,
|
||||
Qt, LibreOffice, OpenJDK, XeTeX, Adobe Photoshop, Illustrator, InDesign,
|
||||
Microsoft Edge, Amazon Kindle, PlayStation, Godot Engine, Unreal Engine,
|
||||
Figma, Canva, QuarkXPress, Scribus, smart TVs,
|
||||
car displays, and many other places.
|
||||
|
||||
<p align="center">
|
||||
<a href="https://xkcd.com/2347/" rel="nofollow">
|
||||
<img src="xkcd.png" width="256" alt="xkcd-derived image">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
## Distribution
|
||||
|
||||
<details>
|
||||
<summary>Packaging status of HarfBuzz</summary>
|
||||
|
||||
[](https://repology.org/project/harfbuzz/versions)
|
||||
|
||||
</details>
|
||||
|
||||
[1]: https://docs.microsoft.com/en-us/typography/opentype/spec/
|
||||
[2]: https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6AATIntro.html
|
||||
[3]: https://github.com/harfbuzz/harfbuzz/releases
|
||||
[4]: https://github.com/harfbuzz/harfbuzz
|
||||
[6]: https://behdad.org/text2024
|
||||
[7]: https://docs.google.com/presentation/d/1x97pfbB1gbD53Yhz6-_yBUozQMVJ_5yMqqR_D-R7b7I/preview
|
||||
[8]: https://docs.google.com/presentation/d/1ySTZaXP5XKFg0OpmHZM00v5b17GSr3ojnzJekl4U8qI/preview
|
||||
[9]: https://behdad.org/doc/harfbuzz2009-slides.pdf
|
||||
[10]: https://docs.google.com/document/d/12jfNpQJzeVIAxoUSpk7KziyINAa1msbGliyXqguS86M/preview
|
||||
[11]: https://behdad.org/
|
||||
[12]: https://docs.google.com/document/d/1_VgObf6Je0J8byMLsi7HCQHnKo2emGnx_ib_sHo-bt4/preview
|
||||
[13]: https://docs.google.com/document/d/1Y-u08l9YhObRVObETZt1k8f_5lQdOix9TRH3zEXaoAw/preview
|
||||
[14]: https://docs.google.com/document/d/1o-xvxCbgMe9JYFHLVnPjk01ZY_8Cj0vB9-KTI1d0nyk/preview
|
||||
[15]: https://docs.google.com/document/d/18hI56KJpvXtwWbc9QSaz9zzhJwIMnrJ-zkAaKS-W-8k/preview
|
||||
[16]: https://docs.google.com/document/d/1Xq3owVt61HVkJqbLFHl73il6pcTy6PdPJJ7bSouQiQw/preview
|
||||
[17]: https://docs.google.com/document/d/1wMPwVNBvsIriamcyBO5aNs7Cdr8lmbwLJ8GmZBAswF4/preview
|
||||
[18]: https://docs.google.com/document/d/1wskYbA-czBt57oH9gEuGf3sWbTx7bfOiEIcDs36-heo/preview
|
||||
[19]: https://prezi.com/view/THNPJGFVDUCWoM20syev/
|
||||
[20]: https://behdad.org/doc/harfbuzz10years-slides.pdf
|
||||
[21]: https://docs.google.com/document/d/1_vZrt97OorJ0jA1YzJ29LRcGr3YGrNJANdOABjVZGEs/preview
|
||||
[22]: https://docs.google.com/document/d/1aH_waagdEM5UhslQxCeFEb82ECBhPlZjy5_MwLNLBYo/preview
|
||||
[23]: https://docs.google.com/document/d/1hRd5oYQJLrt0JuwWhEJWi7wh_9rbaIJkX6IR9DW7rZQ/preview
|
||||
[24]: https://docs.google.com/document/d/1a3K6fHjsiWW36vSzwJwCwEBOgznunKs80PSpBbpfHiA/preview
|
||||
[25]: https://docs.google.com/presentation/d/1o9Exz1c-Lr-dJjA8dcBn_Vl_Y37cupmFzmclMjBE_Bc/view
|
||||
[26]: https://harfbuzz-world.cc/
|
||||
Vendored
+386
@@ -0,0 +1,386 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
hb_extract.py -- copy the minimal subset of HarfBuzz sources used by OpenCV.
|
||||
|
||||
OpenCV vendors HarfBuzz as a normal static library: every .cc is a separate
|
||||
translation unit (no unity / amalgamation build). This script reproduces the
|
||||
exact src/ tree that OpenCV ships, from a pristine HarfBuzz checkout.
|
||||
|
||||
It does three things beyond a naive copy, all to keep the vendored tree small
|
||||
and buildable with OpenCV's configuration (HB_TINY + HB_HAS_RASTER, see
|
||||
CMakeLists.txt):
|
||||
|
||||
1. Copies the translation units that upstream `harfbuzz-world.cc` would
|
||||
compile for the requested HB_HAS_* sections -- EXCEPT the ones that build
|
||||
to an empty object under our config (see EMPTY_TUS). It never emits
|
||||
`harfbuzz-world.cc` itself (we compile each .cc directly), and never
|
||||
copies non-TU .cc files found under src/OT|graph (only real TUs from the
|
||||
parsed world.cc lists are copied), so CMake's file(GLOB_RECURSE src/*.cc)
|
||||
picks up exactly the right set.
|
||||
|
||||
2. Prunes headers down to the set actually reachable by #include from the
|
||||
copied TUs (+ the public headers OpenCV includes). This automatically
|
||||
drops the large amount of HarfBuzz src/ that we never compile -- GPU and
|
||||
WASM backends, the subset/repacker graph, platform backends (CoreText/
|
||||
DirectWrite/GDI/Uniscribe/Graphite2/GObject/Cairo), the vector-paint
|
||||
backend, and headers used only by the skipped empty TUs -- without a
|
||||
hardcoded blocklist.
|
||||
|
||||
3. Generates `hb-opencv-config.hh`, the config-override header CMakeLists.txt
|
||||
points HB_CONFIG_OVERRIDE_H at. It re-enables thread-safety (HB_NO_MT) and
|
||||
variable fonts (HB_NO_VAR) that HB_TINY would otherwise switch off. (It
|
||||
does NOT generate upstream's hb-features.h: nothing we compile includes
|
||||
it.)
|
||||
|
||||
Usage (to refresh this very directory, just point it at a harfbuzz checkout):
|
||||
python hb_extract.py path/to/harfbuzz
|
||||
|
||||
The defaults already produce the OpenCV subset: output '.', features
|
||||
HB_HAS_RASTER, and it also copies README.md + COPYING. Override only if needed:
|
||||
-o DIR output root (default '.')
|
||||
-f FLAGS comma-separated HB_HAS_* (default HB_HAS_RASTER)
|
||||
-a FILES extra files (default README.md,COPYING)
|
||||
--list-flags print available HB_HAS_* flags and exit
|
||||
|
||||
-a paths are relative to the harfbuzz repo root (parent of src/):
|
||||
-a README.md -> copied to <out>/README.md
|
||||
-a COPYING -> copied to <out>/COPYING
|
||||
-a src/hb-raster.h -> copied to <out>/src/hb-raster.h
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import posixpath
|
||||
import re
|
||||
import shutil
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
# Translation units that compile to an EMPTY object (libtool: "has no symbols")
|
||||
# under OpenCV's HarfBuzz configuration (HB_TINY + HB_HAS_RASTER => AAT, legacy
|
||||
# fallback shaping, math, meta, name, buffer (de)serialize/verify and the style
|
||||
# API are all compiled out). They contribute nothing, so we do not vendor them.
|
||||
# Re-derive this list (from the libtool warnings of a clean libharfbuzz build)
|
||||
# if the HarfBuzz configuration in CMakeLists.txt changes.
|
||||
EMPTY_TUS = {
|
||||
"hb-aat-layout.cc",
|
||||
"hb-aat-map.cc",
|
||||
"hb-buffer-serialize.cc",
|
||||
"hb-buffer-verify.cc",
|
||||
"hb-fallback-shape.cc",
|
||||
"hb-ot-math.cc",
|
||||
"hb-ot-meta.cc",
|
||||
"hb-ot-name.cc",
|
||||
"hb-raster.cc",
|
||||
"hb-style.cc",
|
||||
}
|
||||
|
||||
# Public headers OpenCV's text engine (drawing_text.cpp) includes directly.
|
||||
# Used together with the copied TUs as the roots of the header-reachability
|
||||
# prune.
|
||||
SEED_PUBLIC_HEADERS = ("hb.h", "hb-ot.h", "hb-raster.h")
|
||||
|
||||
# Generated config-override header. CMakeLists.txt builds HarfBuzz with HB_TINY
|
||||
# and HB_CONFIG_OVERRIDE_H="hb-opencv-config.hh"; this file is included by
|
||||
# hb-config.hh after HB_TINY/HB_LEAN expand (so the #undef takes effect) but
|
||||
# before the option-closure derives dependent macros.
|
||||
OPENCV_CONFIG_HH = '''\
|
||||
/*
|
||||
* OpenCV-specific HarfBuzz configuration override. GENERATED by hb_extract.py.
|
||||
*
|
||||
* HarfBuzz is built with HB_TINY (see 3rdparty/harfbuzz/CMakeLists.txt) for the
|
||||
* smallest possible footprint. HB_TINY pulls in HB_LEAN + HB_MINI, which would
|
||||
* disable two features that OpenCV's text engine relies on. We restore them
|
||||
* here.
|
||||
*
|
||||
* This file is included by hb-config.hh via HB_CONFIG_OVERRIDE_H, i.e. AFTER
|
||||
* HB_TINY/HB_LEAN/HB_MINI have expanded their macros (so the #undef below takes
|
||||
* effect) but BEFORE the "closure of options" derives dependent macros (e.g.
|
||||
* HB_NO_VAR_COMPOSITES from HB_NO_VAR). That ordering is what makes a plain
|
||||
* #undef sufficient.
|
||||
*
|
||||
* HB_NO_MT - keep thread-safety. hb_font_t instances live inside cv::FontFace
|
||||
* objects, NOT in the thread_local FontRenderEngine, so a single
|
||||
* FontFace (and its hb_font_t) can be shared across threads; its
|
||||
* reference counting must stay atomic.
|
||||
*
|
||||
* HB_NO_VAR - keep variable-font support: the 'wght' axis used for synthetic
|
||||
* weights and named-instance/axis queries (hb_font_set_variations,
|
||||
* hb_ot_var_*). Variable fonts are a primary reason OpenCV adopted
|
||||
* HarfBuzz, so this must remain enabled.
|
||||
*/
|
||||
|
||||
#undef HB_NO_MT
|
||||
#undef HB_NO_VAR
|
||||
'''
|
||||
|
||||
OPENCV_CONFIG_NAME = "hb-opencv-config.hh"
|
||||
|
||||
|
||||
def parse_world_cc(path):
|
||||
"""Return (core_files, sections) parsed from harfbuzz-world.cc.
|
||||
|
||||
core_files -- list of .cc paths (relative to src/) in the unconditional
|
||||
"Core library" section.
|
||||
sections -- dict HB_HAS_XXX -> list of .cc paths from that #ifdef block.
|
||||
"""
|
||||
lines = path.read_text(encoding="utf-8").splitlines()
|
||||
include_re = re.compile(r'^\s*#include\s+"([^"]+\.cc)"')
|
||||
|
||||
core_files = []
|
||||
sections = {}
|
||||
|
||||
STATE_PREAMBLE, STATE_CORE, STATE_IFDEF = "preamble", "core", "ifdef"
|
||||
state = STATE_PREAMBLE
|
||||
current_flag = None
|
||||
ifdef_depth = 0
|
||||
|
||||
for line in lines:
|
||||
if state == STATE_PREAMBLE:
|
||||
if "/* Core library." in line:
|
||||
state = STATE_CORE
|
||||
|
||||
elif state == STATE_CORE:
|
||||
m = include_re.match(line)
|
||||
if m:
|
||||
core_files.append(m.group(1))
|
||||
else:
|
||||
m = re.match(r"^\s*#ifdef\s+(HB_HAS_\w+)", line)
|
||||
if m:
|
||||
current_flag = m.group(1)
|
||||
sections[current_flag] = []
|
||||
state = STATE_IFDEF
|
||||
ifdef_depth = 1
|
||||
|
||||
elif state == STATE_IFDEF:
|
||||
if re.match(r"^\s*#if", line):
|
||||
ifdef_depth += 1
|
||||
elif re.match(r"^\s*#endif", line):
|
||||
ifdef_depth -= 1
|
||||
if ifdef_depth == 0:
|
||||
state = STATE_CORE
|
||||
current_flag = None
|
||||
else:
|
||||
m = include_re.match(line)
|
||||
if m and current_flag is not None:
|
||||
sections[current_flag].append(m.group(1))
|
||||
|
||||
return core_files, sections
|
||||
|
||||
|
||||
def copy_files(file_list, src_dir, out_src_dir, label=""):
|
||||
skipped = []
|
||||
for rel in file_list:
|
||||
if Path(rel).name in EMPTY_TUS:
|
||||
skipped.append(rel)
|
||||
continue
|
||||
src = src_dir / rel
|
||||
dst = out_src_dir / rel
|
||||
dst.parent.mkdir(parents=True, exist_ok=True)
|
||||
if not src.exists():
|
||||
print(f" [WARN] not found: {src}", file=sys.stderr)
|
||||
continue
|
||||
shutil.copy2(src, dst)
|
||||
print(f" [{label or 'core'}] src/{rel}")
|
||||
for rel in skipped:
|
||||
print(f" [skip-empty] src/{rel}")
|
||||
|
||||
|
||||
def copy_headers(src_dir, out_src_dir, public_h=True):
|
||||
"""Copy all candidate headers: src/*.hh, src/*.h (public API), and *.h/*.hh
|
||||
from src/OT/** and src/graph/**. The unreferenced ones are removed later by
|
||||
prune_unreferenced_headers().
|
||||
|
||||
NOTE: .cc files under OT/ and graph/ are intentionally NOT copied here --
|
||||
the only ones we need are real translation units, copied via the parsed
|
||||
world.cc lists. Copying e.g. src/graph/test-classdef-graph.cc would make
|
||||
CMake's file(GLOB_RECURSE src/*.cc) try to compile a non-TU file.
|
||||
"""
|
||||
count = 0
|
||||
|
||||
def _copy(p):
|
||||
nonlocal count
|
||||
dst = out_src_dir / p.relative_to(src_dir)
|
||||
dst.parent.mkdir(parents=True, exist_ok=True)
|
||||
shutil.copy2(p, dst)
|
||||
count += 1
|
||||
|
||||
for hdr in src_dir.glob("*.hh"):
|
||||
_copy(hdr)
|
||||
|
||||
for subdir in ("OT", "graph"):
|
||||
d = src_dir / subdir
|
||||
if not d.exists():
|
||||
continue
|
||||
for hdr in d.rglob("*"):
|
||||
if hdr.is_file() and hdr.suffix in (".h", ".hh"):
|
||||
_copy(hdr)
|
||||
|
||||
if public_h:
|
||||
for hdr in src_dir.glob("*.h"):
|
||||
_copy(hdr)
|
||||
|
||||
return count
|
||||
|
||||
|
||||
def prune_unreferenced_headers(out_src_dir):
|
||||
"""Delete every header not reachable by #include from the copied TUs and the
|
||||
public seed headers, then drop any directory left empty.
|
||||
|
||||
HarfBuzz's amalgamated src/ ships many headers we never compile (GPU/WASM
|
||||
backends, subset/repacker graph, platform backends, vector-paint, and the
|
||||
headers used only by the empty TUs we skip). Reachability removes them all
|
||||
without a hardcoded list. The edge regex also treats any quoted "...h"/
|
||||
"...hh" literal as an include, which covers the `#include HB_STRING_ARRAY_LIST`
|
||||
macro-indirection trick (e.g. hb-ot-cff1-std-str.hh, hb-ot-post-macroman.hh).
|
||||
"""
|
||||
files = {p.relative_to(out_src_dir).as_posix()
|
||||
for p in out_src_dir.rglob("*") if p.is_file()}
|
||||
headers = {f for f in files if f.endswith((".h", ".hh"))}
|
||||
edge_re = re.compile(r'#\s*include\s+["<]([^">]+)[">]|"([^"]+\.hh?)"')
|
||||
|
||||
def resolve(inc, including):
|
||||
for cand in (posixpath.normpath(posixpath.join(posixpath.dirname(including), inc)),
|
||||
posixpath.normpath(inc)):
|
||||
if cand in files:
|
||||
return cand
|
||||
return None
|
||||
|
||||
seeds = {f for f in files if f.endswith(".cc")}
|
||||
seeds |= {h for h in SEED_PUBLIC_HEADERS if h in files}
|
||||
|
||||
seen, stack = set(), list(seeds)
|
||||
while stack:
|
||||
cur = stack.pop()
|
||||
if cur in seen:
|
||||
continue
|
||||
seen.add(cur)
|
||||
try:
|
||||
txt = (out_src_dir / cur).read_text(encoding="utf-8", errors="ignore")
|
||||
except OSError:
|
||||
continue
|
||||
for m in edge_re.finditer(txt):
|
||||
inc = m.group(1) or m.group(2)
|
||||
r = resolve(inc, cur)
|
||||
if r and r not in seen:
|
||||
stack.append(r)
|
||||
|
||||
needed = {f for f in seen if f.endswith((".h", ".hh"))}
|
||||
for h in sorted(headers - needed):
|
||||
(out_src_dir / h).unlink()
|
||||
print(f" [prune] src/{h}")
|
||||
|
||||
# remove directories left empty by pruning (deepest first)
|
||||
for d in sorted((p for p in out_src_dir.rglob("*") if p.is_dir()),
|
||||
key=lambda p: len(p.parts), reverse=True):
|
||||
try:
|
||||
d.rmdir()
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
return len(headers - needed), len(needed)
|
||||
|
||||
|
||||
def write_opencv_config_h(out_src_dir):
|
||||
"""Generate hb-opencv-config.hh (the HB_CONFIG_OVERRIDE_H header)."""
|
||||
(out_src_dir / OPENCV_CONFIG_NAME).write_text(OPENCV_CONFIG_HH, encoding="utf-8")
|
||||
print(f" [gen] src/{OPENCV_CONFIG_NAME}")
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Copy the OpenCV subset of HarfBuzz sources.",
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
epilog="""
|
||||
examples:
|
||||
python hb_extract.py ~/work/harfbuzz -o . -f HB_HAS_RASTER -a README.md,COPYING
|
||||
python hb_extract.py ~/work/harfbuzz --list-flags
|
||||
""",
|
||||
)
|
||||
parser.add_argument("harfbuzz_dir", metavar="harfbuzz-dir",
|
||||
help="path to the harfbuzz repo root (contains src/)")
|
||||
parser.add_argument("-o", "--output", metavar="DIR", default=".",
|
||||
help="output root directory; sources go into DIR/src/ "
|
||||
"(default: current directory)")
|
||||
parser.add_argument("-f", "--features", metavar="FLAGS", default="HB_HAS_RASTER",
|
||||
help="comma-separated HB_HAS_* flags (default: HB_HAS_RASTER)")
|
||||
parser.add_argument("-a", "--add", metavar="FILES", default="README.md,COPYING",
|
||||
help="comma-separated files relative to the harfbuzz repo "
|
||||
"root (default: README.md,COPYING)")
|
||||
parser.add_argument("--no-public-headers", action="store_true",
|
||||
help="skip copying public src/*.h API headers "
|
||||
"(OT/, graph/, *.hh are always copied)")
|
||||
parser.add_argument("--list-flags", action="store_true",
|
||||
help="list available HB_HAS_* flags and exit")
|
||||
args = parser.parse_args()
|
||||
|
||||
repo_dir = Path(args.harfbuzz_dir).resolve()
|
||||
world_cc_path = repo_dir / "src" / "harfbuzz-world.cc"
|
||||
if not world_cc_path.exists():
|
||||
print(f"error: {world_cc_path} not found", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
src_dir = world_cc_path.parent
|
||||
core_files, sections = parse_world_cc(world_cc_path)
|
||||
|
||||
if args.list_flags:
|
||||
for flag, files in sorted(sections.items()):
|
||||
print(f" {flag:<22} ({len(files)} .cc files)")
|
||||
return
|
||||
|
||||
enabled_flags = [f.strip() for f in args.features.split(",") if f.strip()]
|
||||
extra_files = [f.strip() for f in args.add.split(",") if f.strip()]
|
||||
|
||||
out_dir = Path(args.output).resolve()
|
||||
out_src_dir = out_dir / "src"
|
||||
|
||||
# Start from a clean src/ so removed-upstream files do not linger.
|
||||
if out_src_dir.exists():
|
||||
shutil.rmtree(out_src_dir)
|
||||
out_src_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
print(f"\nsource : {repo_dir}")
|
||||
print(f"output : {out_dir}")
|
||||
print(f"flags : {enabled_flags}\n")
|
||||
|
||||
print("core:")
|
||||
copy_files(core_files, src_dir, out_src_dir)
|
||||
|
||||
for flag in enabled_flags:
|
||||
flag_files = sections.get(flag)
|
||||
if not flag_files:
|
||||
print(f"\n[{flag}]: no .cc files (flag not found in harfbuzz-world.cc)")
|
||||
continue
|
||||
print(f"\n[{flag}]:")
|
||||
copy_files(flag_files, src_dir, out_src_dir, label=flag)
|
||||
|
||||
if extra_files:
|
||||
print("\nextra:")
|
||||
for rel in extra_files:
|
||||
src = repo_dir / rel
|
||||
dst = out_dir / rel
|
||||
dst.parent.mkdir(parents=True, exist_ok=True)
|
||||
if not src.exists():
|
||||
print(f" [WARN] not found: {src}", file=sys.stderr)
|
||||
continue
|
||||
shutil.copy2(src, dst)
|
||||
print(f" [add] {rel}")
|
||||
|
||||
n = copy_headers(src_dir, out_src_dir,
|
||||
public_h=not args.no_public_headers)
|
||||
print(f"\nheaders: {n} files copied")
|
||||
|
||||
print("\nprune (unreferenced headers):")
|
||||
removed, kept = prune_unreferenced_headers(out_src_dir)
|
||||
print(f" removed {removed}, kept {kept}")
|
||||
|
||||
print("\ngenerated:")
|
||||
write_opencv_config_h(out_src_dir)
|
||||
|
||||
all_cc = sorted(out_src_dir.rglob("*.cc"))
|
||||
all_h = list(out_src_dir.rglob("*.h")) + list(out_src_dir.rglob("*.hh"))
|
||||
print(f"\ndone: {len(all_cc)} .cc | {len(all_h)} headers -> {out_dir}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
+1033
File diff suppressed because it is too large
Load Diff
+2861
File diff suppressed because it is too large
Load Diff
+367
@@ -0,0 +1,367 @@
|
||||
/*
|
||||
* Copyright © 2016 Google, Inc.
|
||||
* Copyright © 2018 Ebrahim Byagowi
|
||||
*
|
||||
* This is part of HarfBuzz, a text shaping library.
|
||||
*
|
||||
* Permission is hereby granted, without written agreement and without
|
||||
* license or royalty fees, to use, copy, modify, and distribute this
|
||||
* software and its documentation for any purpose, provided that the
|
||||
* above copyright notice and the following two paragraphs appear in
|
||||
* all copies of this software.
|
||||
*
|
||||
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
|
||||
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
|
||||
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
|
||||
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||
* DAMAGE.
|
||||
*
|
||||
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
|
||||
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
|
||||
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
|
||||
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
|
||||
*
|
||||
* Google Author(s): Sascha Brawer
|
||||
*/
|
||||
|
||||
#ifndef OT_COLOR_CPAL_CPAL_HH
|
||||
#define OT_COLOR_CPAL_CPAL_HH
|
||||
|
||||
#include "../../../hb-open-type.hh"
|
||||
#include "../../../hb-ot-color.h"
|
||||
#include "../../../hb-ot-name.h"
|
||||
|
||||
|
||||
/*
|
||||
* CPAL -- Color Palette
|
||||
* https://docs.microsoft.com/en-us/typography/opentype/spec/cpal
|
||||
*/
|
||||
#define HB_OT_TAG_CPAL HB_TAG('C','P','A','L')
|
||||
|
||||
namespace OT {
|
||||
|
||||
|
||||
struct CPALV1Tail
|
||||
{
|
||||
friend struct CPAL;
|
||||
|
||||
private:
|
||||
hb_ot_color_palette_flags_t get_palette_flags (const void *base,
|
||||
unsigned int palette_index,
|
||||
unsigned int palette_count) const
|
||||
{
|
||||
if (!paletteFlagsZ) return HB_OT_COLOR_PALETTE_FLAG_DEFAULT;
|
||||
return (hb_ot_color_palette_flags_t) (uint32_t)
|
||||
(base+paletteFlagsZ).as_array (palette_count)[palette_index];
|
||||
}
|
||||
|
||||
hb_ot_name_id_t get_palette_name_id (const void *base,
|
||||
unsigned int palette_index,
|
||||
unsigned int palette_count) const
|
||||
{
|
||||
if (!paletteLabelsZ) return HB_OT_NAME_ID_INVALID;
|
||||
return (base+paletteLabelsZ).as_array (palette_count)[palette_index];
|
||||
}
|
||||
|
||||
hb_ot_name_id_t get_color_name_id (const void *base,
|
||||
unsigned int color_index,
|
||||
unsigned int color_count) const
|
||||
{
|
||||
if (!colorLabelsZ) return HB_OT_NAME_ID_INVALID;
|
||||
return (base+colorLabelsZ).as_array (color_count)[color_index];
|
||||
}
|
||||
|
||||
public:
|
||||
void collect_name_ids (const void *base,
|
||||
unsigned palette_count,
|
||||
unsigned color_count,
|
||||
const hb_map_t *color_index_map,
|
||||
hb_set_t *nameids_to_retain /* OUT */) const
|
||||
{
|
||||
if (paletteLabelsZ)
|
||||
{
|
||||
+ (base+paletteLabelsZ).as_array (palette_count)
|
||||
| hb_sink (nameids_to_retain)
|
||||
;
|
||||
}
|
||||
|
||||
if (colorLabelsZ)
|
||||
{
|
||||
const hb_array_t<const NameID> colorLabels = (base+colorLabelsZ).as_array (color_count);
|
||||
for (unsigned i = 0; i < color_count; i++)
|
||||
{
|
||||
if (!color_index_map->has (i)) continue;
|
||||
nameids_to_retain->add (colorLabels[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool serialize (hb_serialize_context_t *c,
|
||||
unsigned palette_count,
|
||||
unsigned color_count,
|
||||
const void *base,
|
||||
const hb_map_t *color_index_map) const
|
||||
{
|
||||
TRACE_SERIALIZE (this);
|
||||
auto *out = c->allocate_size<CPALV1Tail> (static_size);
|
||||
if (unlikely (!out)) return_trace (false);
|
||||
|
||||
out->paletteFlagsZ = 0;
|
||||
if (paletteFlagsZ)
|
||||
out->paletteFlagsZ.serialize_copy (c, paletteFlagsZ, base, 0, hb_serialize_context_t::Head, palette_count);
|
||||
|
||||
out->paletteLabelsZ = 0;
|
||||
if (paletteLabelsZ)
|
||||
out->paletteLabelsZ.serialize_copy (c, paletteLabelsZ, base, 0, hb_serialize_context_t::Head, palette_count);
|
||||
|
||||
const hb_array_t<const NameID> colorLabels = (base+colorLabelsZ).as_array (color_count);
|
||||
if (colorLabelsZ)
|
||||
{
|
||||
c->push ();
|
||||
for (unsigned i = 0; i < color_count; i++)
|
||||
{
|
||||
if (!color_index_map->has (i)) continue;
|
||||
if (!c->copy<NameID> (colorLabels[i]))
|
||||
{
|
||||
c->pop_discard ();
|
||||
return_trace (false);
|
||||
}
|
||||
}
|
||||
c->add_link (out->colorLabelsZ, c->pop_pack ());
|
||||
}
|
||||
return_trace (true);
|
||||
}
|
||||
|
||||
bool sanitize (hb_sanitize_context_t *c,
|
||||
const void *base,
|
||||
unsigned int palette_count,
|
||||
unsigned int color_count) const
|
||||
{
|
||||
TRACE_SANITIZE (this);
|
||||
return_trace (c->check_struct (this) &&
|
||||
(!paletteFlagsZ || (base+paletteFlagsZ).sanitize (c, palette_count)) &&
|
||||
(!paletteLabelsZ || (base+paletteLabelsZ).sanitize (c, palette_count)) &&
|
||||
(!colorLabelsZ || (base+colorLabelsZ).sanitize (c, color_count)));
|
||||
}
|
||||
|
||||
protected:
|
||||
// TODO(garretrieger): these offsets can hold nulls so we should not be using non-null offsets
|
||||
// here. Currently they are needed since UnsizedArrayOf doesn't define null_size
|
||||
NNOffset32To<UnsizedArrayOf<HBUINT32>>
|
||||
paletteFlagsZ; /* Offset from the beginning of CPAL table to
|
||||
* the Palette Type Array. Set to 0 if no array
|
||||
* is provided. */
|
||||
NNOffset32To<UnsizedArrayOf<NameID>>
|
||||
paletteLabelsZ; /* Offset from the beginning of CPAL table to
|
||||
* the palette labels array. Set to 0 if no
|
||||
* array is provided. */
|
||||
NNOffset32To<UnsizedArrayOf<NameID>>
|
||||
colorLabelsZ; /* Offset from the beginning of CPAL table to
|
||||
* the color labels array. Set to 0
|
||||
* if no array is provided. */
|
||||
public:
|
||||
DEFINE_SIZE_STATIC (12);
|
||||
};
|
||||
|
||||
typedef HBUINT32 BGRAColor;
|
||||
|
||||
struct CPAL
|
||||
{
|
||||
static constexpr hb_tag_t tableTag = HB_OT_TAG_CPAL;
|
||||
|
||||
bool has_data () const { return numPalettes; }
|
||||
|
||||
size_t get_size () const
|
||||
{ return min_size + numPalettes * sizeof (colorRecordIndicesZ[0]); }
|
||||
|
||||
unsigned int get_palette_count () const { return numPalettes; }
|
||||
unsigned int get_color_count () const { return numColors; }
|
||||
|
||||
hb_ot_color_palette_flags_t get_palette_flags (unsigned int palette_index) const
|
||||
{ return v1 ().get_palette_flags (this, palette_index, numPalettes); }
|
||||
|
||||
hb_ot_name_id_t get_palette_name_id (unsigned int palette_index) const
|
||||
{ return v1 ().get_palette_name_id (this, palette_index, numPalettes); }
|
||||
|
||||
hb_ot_name_id_t get_color_name_id (unsigned int color_index) const
|
||||
{ return v1 ().get_color_name_id (this, color_index, numColors); }
|
||||
|
||||
hb_array_t<const BGRAColor> get_palette_colors (unsigned int palette_index) const
|
||||
{
|
||||
if (unlikely (palette_index >= numPalettes))
|
||||
return hb_array_t<const BGRAColor> ();
|
||||
unsigned int start_index = colorRecordIndicesZ[palette_index];
|
||||
hb_array_t<const BGRAColor> all_colors ((this+colorRecordsZ).arrayZ, numColorRecords);
|
||||
return all_colors.sub_array (start_index, numColors);
|
||||
}
|
||||
unsigned int get_palette_colors (unsigned int palette_index,
|
||||
unsigned int start_offset,
|
||||
unsigned int *color_count, /* IN/OUT. May be NULL. */
|
||||
hb_color_t *colors /* OUT. May be NULL. */) const
|
||||
{
|
||||
if (unlikely (palette_index >= numPalettes))
|
||||
{
|
||||
if (color_count) *color_count = 0;
|
||||
return 0;
|
||||
}
|
||||
unsigned int start_index = colorRecordIndicesZ[palette_index];
|
||||
hb_array_t<const BGRAColor> all_colors ((this+colorRecordsZ).arrayZ, numColorRecords);
|
||||
hb_array_t<const BGRAColor> palette_colors = all_colors.sub_array (start_index,
|
||||
numColors);
|
||||
if (color_count)
|
||||
{
|
||||
+ palette_colors.sub_array (start_offset, color_count)
|
||||
| hb_sink (hb_array (colors, *color_count))
|
||||
;
|
||||
}
|
||||
return numColors;
|
||||
}
|
||||
|
||||
void collect_name_ids (const hb_map_t *color_index_map,
|
||||
hb_set_t *nameids_to_retain /* OUT */) const
|
||||
{
|
||||
if (version == 1)
|
||||
{
|
||||
hb_barrier ();
|
||||
v1 ().collect_name_ids (this, numPalettes, numColors, color_index_map, nameids_to_retain);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
const CPALV1Tail& v1 () const
|
||||
{
|
||||
if (version == 0) return Null (CPALV1Tail);
|
||||
hb_barrier ();
|
||||
return StructAfter<CPALV1Tail> (*this);
|
||||
}
|
||||
|
||||
public:
|
||||
bool serialize (hb_serialize_context_t *c,
|
||||
const hb_array_t<const HBUINT16> &color_record_indices,
|
||||
const hb_array_t<const BGRAColor> &color_records,
|
||||
const hb_vector_t<unsigned>& first_color_index_for_layer,
|
||||
const hb_map_t& first_color_to_layer_index,
|
||||
const hb_set_t &retained_color_indices) const
|
||||
{
|
||||
TRACE_SERIALIZE (this);
|
||||
|
||||
// TODO(grieger): limit total final size.
|
||||
|
||||
for (const auto idx : color_record_indices)
|
||||
{
|
||||
hb_codepoint_t layer_index = first_color_to_layer_index[idx];
|
||||
|
||||
HBUINT16 new_idx;
|
||||
new_idx = layer_index * retained_color_indices.get_population ();
|
||||
if (!c->copy<HBUINT16> (new_idx)) return_trace (false);
|
||||
}
|
||||
|
||||
c->push ();
|
||||
for (unsigned first_color_index : first_color_index_for_layer)
|
||||
{
|
||||
for (hb_codepoint_t color_index : retained_color_indices)
|
||||
{
|
||||
if (!c->copy<BGRAColor> (color_records[first_color_index + color_index]))
|
||||
{
|
||||
c->pop_discard ();
|
||||
return_trace (false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
c->add_link (colorRecordsZ, c->pop_pack ());
|
||||
return_trace (true);
|
||||
}
|
||||
|
||||
bool subset (hb_subset_context_t *c) const
|
||||
{
|
||||
TRACE_SUBSET (this);
|
||||
if (!numPalettes) return_trace (false);
|
||||
|
||||
const hb_map_t *color_index_map = &c->plan->colr_palettes;
|
||||
if (color_index_map->is_empty ()) return_trace (false);
|
||||
|
||||
hb_set_t retained_color_indices;
|
||||
for (const auto _ : color_index_map->keys ())
|
||||
{
|
||||
if (_ == 0xFFFF) continue;
|
||||
retained_color_indices.add (_);
|
||||
}
|
||||
if (retained_color_indices.is_empty ()) return_trace (false);
|
||||
|
||||
auto *out = c->serializer->start_embed (*this);
|
||||
if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
|
||||
|
||||
|
||||
out->version = version;
|
||||
out->numColors = retained_color_indices.get_population ();
|
||||
out->numPalettes = numPalettes;
|
||||
|
||||
hb_vector_t<unsigned> first_color_index_for_layer;
|
||||
hb_map_t first_color_to_layer_index;
|
||||
|
||||
const hb_array_t<const HBUINT16> colorRecordIndices = colorRecordIndicesZ.as_array (numPalettes);
|
||||
for (const auto first_color_record_idx : colorRecordIndices)
|
||||
{
|
||||
if (first_color_to_layer_index.has (first_color_record_idx)) continue;
|
||||
|
||||
first_color_index_for_layer.push (first_color_record_idx);
|
||||
if (unlikely (!c->serializer->propagate_error (first_color_index_for_layer))) return_trace (false);
|
||||
first_color_to_layer_index.set (first_color_record_idx,
|
||||
first_color_index_for_layer.length - 1);
|
||||
}
|
||||
|
||||
out->numColorRecords = first_color_index_for_layer.length
|
||||
* retained_color_indices.get_population ();
|
||||
|
||||
const hb_array_t<const BGRAColor> color_records = (this+colorRecordsZ).as_array (numColorRecords);
|
||||
if (!out->serialize (c->serializer,
|
||||
colorRecordIndices,
|
||||
color_records,
|
||||
first_color_index_for_layer,
|
||||
first_color_to_layer_index,
|
||||
retained_color_indices))
|
||||
return_trace (false);
|
||||
|
||||
if (version == 1)
|
||||
{
|
||||
hb_barrier ();
|
||||
return_trace (v1 ().serialize (c->serializer, numPalettes, numColors, this, color_index_map));
|
||||
}
|
||||
|
||||
return_trace (true);
|
||||
}
|
||||
|
||||
bool sanitize (hb_sanitize_context_t *c) const
|
||||
{
|
||||
TRACE_SANITIZE (this);
|
||||
return_trace (c->check_struct (this) &&
|
||||
hb_barrier () &&
|
||||
(this+colorRecordsZ).sanitize (c, numColorRecords) &&
|
||||
colorRecordIndicesZ.sanitize (c, numPalettes) &&
|
||||
(version == 0 || v1 ().sanitize (c, this, numPalettes, numColors)));
|
||||
}
|
||||
|
||||
protected:
|
||||
HBUINT16 version; /* Table version number */
|
||||
/* Version 0 */
|
||||
HBUINT16 numColors; /* Number of colors in each palette. */
|
||||
HBUINT16 numPalettes; /* Number of palettes in the table. */
|
||||
HBUINT16 numColorRecords; /* Total number of color records, combined for
|
||||
* all palettes. */
|
||||
NNOffset32To<UnsizedArrayOf<BGRAColor>>
|
||||
colorRecordsZ; /* Offset from the beginning of CPAL table to
|
||||
* the first ColorRecord. */
|
||||
UnsizedArrayOf<HBUINT16>
|
||||
colorRecordIndicesZ; /* Index of each palette’s first color record in
|
||||
* the combined color record array. */
|
||||
/*CPALV1Tail v1;*/
|
||||
public:
|
||||
DEFINE_SIZE_ARRAY (12, colorRecordIndicesZ);
|
||||
};
|
||||
|
||||
} /* namespace OT */
|
||||
|
||||
|
||||
#endif /* OT_COLOR_CPAL_CPAL_HH */
|
||||
+449
@@ -0,0 +1,449 @@
|
||||
/*
|
||||
* Copyright © 2018 Ebrahim Byagowi
|
||||
* Copyright © 2020 Google, Inc.
|
||||
*
|
||||
* This is part of HarfBuzz, a text shaping library.
|
||||
*
|
||||
* Permission is hereby granted, without written agreement and without
|
||||
* license or royalty fees, to use, copy, modify, and distribute this
|
||||
* software and its documentation for any purpose, provided that the
|
||||
* above copyright notice and the following two paragraphs appear in
|
||||
* all copies of this software.
|
||||
*
|
||||
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
|
||||
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
|
||||
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
|
||||
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||
* DAMAGE.
|
||||
*
|
||||
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
|
||||
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
|
||||
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
|
||||
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
|
||||
*
|
||||
* Google Author(s): Calder Kitagawa
|
||||
*/
|
||||
|
||||
#ifndef OT_COLOR_SBIX_SBIX_HH
|
||||
#define OT_COLOR_SBIX_SBIX_HH
|
||||
|
||||
#include "../../../hb-open-type.hh"
|
||||
#include "../../../hb-paint.hh"
|
||||
|
||||
/*
|
||||
* sbix -- Standard Bitmap Graphics
|
||||
* https://docs.microsoft.com/en-us/typography/opentype/spec/sbix
|
||||
* https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6sbix.html
|
||||
*/
|
||||
#define HB_OT_TAG_sbix HB_TAG('s','b','i','x')
|
||||
|
||||
|
||||
namespace OT {
|
||||
|
||||
|
||||
struct SBIXGlyph
|
||||
{
|
||||
SBIXGlyph* copy (hb_serialize_context_t *c, unsigned int data_length) const
|
||||
{
|
||||
TRACE_SERIALIZE (this);
|
||||
SBIXGlyph* new_glyph = c->start_embed<SBIXGlyph> ();
|
||||
if (unlikely (!c->extend_min (new_glyph))) return_trace (nullptr);
|
||||
|
||||
new_glyph->xOffset = xOffset;
|
||||
new_glyph->yOffset = yOffset;
|
||||
new_glyph->graphicType = graphicType;
|
||||
data.copy (c, data_length);
|
||||
return_trace (new_glyph);
|
||||
}
|
||||
|
||||
HBINT16 xOffset; /* The horizontal (x-axis) offset from the left
|
||||
* edge of the graphic to the glyph’s origin.
|
||||
* That is, the x-coordinate of the point on the
|
||||
* baseline at the left edge of the glyph. */
|
||||
HBINT16 yOffset; /* The vertical (y-axis) offset from the bottom
|
||||
* edge of the graphic to the glyph’s origin.
|
||||
* That is, the y-coordinate of the point on the
|
||||
* baseline at the left edge of the glyph. */
|
||||
Tag graphicType; /* Indicates the format of the embedded graphic
|
||||
* data: one of 'jpg ', 'png ' or 'tiff', or the
|
||||
* special format 'dupe'. */
|
||||
UnsizedArrayOf<HBUINT8>
|
||||
data; /* The actual embedded graphic data. The total
|
||||
* length is inferred from sequential entries in
|
||||
* the glyphDataOffsets array and the fixed size
|
||||
* (8 bytes) of the preceding fields. */
|
||||
public:
|
||||
DEFINE_SIZE_ARRAY (8, data);
|
||||
};
|
||||
|
||||
struct SBIXStrike
|
||||
{
|
||||
static size_t get_size (unsigned num_glyphs)
|
||||
{ return min_size + num_glyphs * HBUINT32::static_size; }
|
||||
|
||||
bool sanitize (hb_sanitize_context_t *c) const
|
||||
{
|
||||
TRACE_SANITIZE (this);
|
||||
return_trace (c->check_struct (this) &&
|
||||
imageOffsetsZ.sanitize_shallow (c, c->get_num_glyphs () + 1));
|
||||
}
|
||||
|
||||
hb_blob_t *get_glyph_blob (unsigned int glyph_id,
|
||||
hb_blob_t *sbix_blob,
|
||||
hb_tag_t file_type,
|
||||
int *x_offset,
|
||||
int *y_offset,
|
||||
unsigned int num_glyphs,
|
||||
unsigned int *strike_ppem) const
|
||||
{
|
||||
if (unlikely (!ppem)) return hb_blob_get_empty (); /* To get Null() object out of the way. */
|
||||
|
||||
unsigned int retry_count = 8;
|
||||
unsigned int sbix_len = sbix_blob->length;
|
||||
unsigned int strike_offset = (const char *) this - (const char *) sbix_blob->data;
|
||||
assert (strike_offset < sbix_len);
|
||||
|
||||
retry:
|
||||
if (unlikely (glyph_id >= num_glyphs ||
|
||||
imageOffsetsZ[glyph_id + 1] <= imageOffsetsZ[glyph_id] ||
|
||||
imageOffsetsZ[glyph_id + 1] - imageOffsetsZ[glyph_id] <= SBIXGlyph::min_size ||
|
||||
(unsigned int) imageOffsetsZ[glyph_id + 1] > sbix_len - strike_offset))
|
||||
return hb_blob_get_empty ();
|
||||
|
||||
unsigned int glyph_offset = strike_offset + (unsigned int) imageOffsetsZ[glyph_id] + SBIXGlyph::min_size;
|
||||
unsigned int glyph_length = imageOffsetsZ[glyph_id + 1] - imageOffsetsZ[glyph_id] - SBIXGlyph::min_size;
|
||||
|
||||
const SBIXGlyph *glyph = &(this+imageOffsetsZ[glyph_id]);
|
||||
|
||||
if (glyph->graphicType == HB_TAG ('d','u','p','e'))
|
||||
{
|
||||
if (glyph_length >= 2)
|
||||
{
|
||||
glyph_id = *((HBUINT16 *) &glyph->data);
|
||||
if (retry_count--)
|
||||
goto retry;
|
||||
}
|
||||
return hb_blob_get_empty ();
|
||||
}
|
||||
|
||||
if (unlikely (file_type != glyph->graphicType))
|
||||
return hb_blob_get_empty ();
|
||||
|
||||
if (strike_ppem) *strike_ppem = ppem;
|
||||
if (x_offset) *x_offset = glyph->xOffset;
|
||||
if (y_offset) *y_offset = glyph->yOffset;
|
||||
return hb_blob_create_sub_blob (sbix_blob, glyph_offset, glyph_length);
|
||||
}
|
||||
|
||||
bool subset (hb_subset_context_t *c, unsigned int available_len) const
|
||||
{
|
||||
TRACE_SUBSET (this);
|
||||
unsigned int num_output_glyphs = c->plan->num_output_glyphs ();
|
||||
|
||||
auto* out = c->serializer->start_embed<SBIXStrike> ();
|
||||
auto snap = c->serializer->snapshot ();
|
||||
if (unlikely (!c->serializer->extend (out, num_output_glyphs + 1))) return_trace (false);
|
||||
out->ppem = ppem;
|
||||
out->resolution = resolution;
|
||||
HBUINT32 head;
|
||||
head = get_size (num_output_glyphs + 1);
|
||||
|
||||
bool has_glyphs = false;
|
||||
for (unsigned new_gid = 0; new_gid < num_output_glyphs; new_gid++)
|
||||
{
|
||||
hb_codepoint_t old_gid;
|
||||
if (!c->plan->old_gid_for_new_gid (new_gid, &old_gid) ||
|
||||
unlikely (imageOffsetsZ[old_gid].is_null () ||
|
||||
imageOffsetsZ[old_gid + 1].is_null () ||
|
||||
imageOffsetsZ[old_gid + 1] <= imageOffsetsZ[old_gid] ||
|
||||
imageOffsetsZ[old_gid + 1] - imageOffsetsZ[old_gid] <= SBIXGlyph::min_size) ||
|
||||
(unsigned int) imageOffsetsZ[old_gid + 1] > available_len)
|
||||
{
|
||||
out->imageOffsetsZ[new_gid] = head;
|
||||
continue;
|
||||
}
|
||||
has_glyphs = true;
|
||||
unsigned int delta = imageOffsetsZ[old_gid + 1] - imageOffsetsZ[old_gid];
|
||||
unsigned int glyph_data_length = delta - SBIXGlyph::min_size;
|
||||
if (!(this+imageOffsetsZ[old_gid]).copy (c->serializer, glyph_data_length))
|
||||
return_trace (false);
|
||||
out->imageOffsetsZ[new_gid] = head;
|
||||
head += delta;
|
||||
}
|
||||
if (has_glyphs)
|
||||
out->imageOffsetsZ[num_output_glyphs] = head;
|
||||
else
|
||||
c->serializer->revert (snap);
|
||||
return_trace (has_glyphs);
|
||||
}
|
||||
|
||||
public:
|
||||
HBUINT16 ppem; /* The PPEM size for which this strike was designed. */
|
||||
HBUINT16 resolution; /* The device pixel density (in PPI) for which this
|
||||
* strike was designed. (E.g., 96 PPI, 192 PPI.) */
|
||||
protected:
|
||||
UnsizedArrayOf<Offset32To<SBIXGlyph>>
|
||||
imageOffsetsZ; /* Offset from the beginning of the strike data header
|
||||
* to bitmap data for an individual glyph ID. */
|
||||
public:
|
||||
DEFINE_SIZE_ARRAY (4, imageOffsetsZ);
|
||||
};
|
||||
|
||||
struct sbix
|
||||
{
|
||||
static constexpr hb_tag_t tableTag = HB_OT_TAG_sbix;
|
||||
|
||||
bool has_data () const { return version; }
|
||||
|
||||
const SBIXStrike &get_strike (unsigned int i) const { return this+strikes[i]; }
|
||||
|
||||
struct accelerator_t
|
||||
{
|
||||
accelerator_t (hb_face_t *face)
|
||||
{
|
||||
table = hb_sanitize_context_t ().reference_table<sbix> (face);
|
||||
num_glyphs = face->get_num_glyphs ();
|
||||
}
|
||||
~accelerator_t () { table.destroy (); }
|
||||
|
||||
bool has_data () const { return table->has_data (); }
|
||||
|
||||
bool get_extents (hb_font_t *font,
|
||||
hb_codepoint_t glyph,
|
||||
hb_glyph_extents_t *extents,
|
||||
bool scale = true) const
|
||||
{
|
||||
/* We only support PNG right now, and following function checks type. */
|
||||
return get_png_extents (font, glyph, extents, scale);
|
||||
}
|
||||
|
||||
hb_blob_t *reference_png (hb_font_t *font,
|
||||
hb_codepoint_t glyph_id,
|
||||
int *x_offset,
|
||||
int *y_offset,
|
||||
unsigned int *available_ppem) const
|
||||
{
|
||||
return choose_strike (font).get_glyph_blob (glyph_id, table.get_blob (),
|
||||
HB_TAG ('p','n','g',' '),
|
||||
x_offset, y_offset,
|
||||
num_glyphs, available_ppem);
|
||||
}
|
||||
|
||||
bool paint_glyph (hb_font_t *font, hb_codepoint_t glyph, hb_paint_funcs_t *funcs, void *data) const
|
||||
{
|
||||
if (!has_data ())
|
||||
return false;
|
||||
|
||||
int x_offset = 0, y_offset = 0;
|
||||
unsigned int strike_ppem = 0;
|
||||
hb_glyph_extents_t extents;
|
||||
hb_glyph_extents_t pixel_extents;
|
||||
|
||||
if (!font->get_glyph_extents (glyph, &extents, false))
|
||||
return false;
|
||||
|
||||
if (unlikely (!get_extents (font, glyph, &pixel_extents, false)))
|
||||
return false;
|
||||
|
||||
hb_blob_t *blob = reference_png (font, glyph, &x_offset, &y_offset, &strike_ppem);
|
||||
if (hb_blob_is_immutable (blob))
|
||||
return false;
|
||||
|
||||
bool ret = funcs->image (data,
|
||||
blob,
|
||||
pixel_extents.width, -pixel_extents.height,
|
||||
HB_PAINT_IMAGE_FORMAT_PNG,
|
||||
0.f,
|
||||
&extents);
|
||||
|
||||
hb_blob_destroy (blob);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
const SBIXStrike &choose_strike (hb_font_t *font) const
|
||||
{
|
||||
unsigned count = table->strikes.len;
|
||||
if (unlikely (!count))
|
||||
return Null (SBIXStrike);
|
||||
|
||||
unsigned int requested_ppem = hb_max (font->x_ppem, font->y_ppem);
|
||||
if (!requested_ppem)
|
||||
requested_ppem = 1<<30; /* Choose largest strike. */
|
||||
/* TODO Add DPI sensitivity as well? */
|
||||
unsigned int best_i = 0;
|
||||
unsigned int best_ppem = table->get_strike (0).ppem;
|
||||
|
||||
for (unsigned int i = 1; i < count; i++)
|
||||
{
|
||||
unsigned int ppem = (table->get_strike (i)).ppem;
|
||||
if ((requested_ppem <= ppem && ppem < best_ppem) ||
|
||||
(requested_ppem > best_ppem && ppem > best_ppem))
|
||||
{
|
||||
best_i = i;
|
||||
best_ppem = ppem;
|
||||
}
|
||||
}
|
||||
|
||||
return table->get_strike (best_i);
|
||||
}
|
||||
|
||||
struct PNGHeader
|
||||
{
|
||||
HBUINT8 signature[8];
|
||||
struct
|
||||
{
|
||||
struct
|
||||
{
|
||||
HBUINT32 length;
|
||||
Tag type;
|
||||
} header;
|
||||
HBUINT32 width;
|
||||
HBUINT32 height;
|
||||
HBUINT8 bitDepth;
|
||||
HBUINT8 colorType;
|
||||
HBUINT8 compressionMethod;
|
||||
HBUINT8 filterMethod;
|
||||
HBUINT8 interlaceMethod;
|
||||
} IHDR;
|
||||
|
||||
public:
|
||||
DEFINE_SIZE_STATIC (29);
|
||||
};
|
||||
|
||||
bool get_png_extents (hb_font_t *font,
|
||||
hb_codepoint_t glyph,
|
||||
hb_glyph_extents_t *extents,
|
||||
bool scale = true) const
|
||||
{
|
||||
/* Following code is safe to call even without data.
|
||||
* But faster to short-circuit. */
|
||||
if (!has_data ())
|
||||
return false;
|
||||
|
||||
int x_offset = 0, y_offset = 0;
|
||||
unsigned int strike_ppem = 0;
|
||||
hb_blob_t *blob = reference_png (font, glyph, &x_offset, &y_offset, &strike_ppem);
|
||||
|
||||
const PNGHeader &png = *blob->as<PNGHeader>();
|
||||
|
||||
if (png.IHDR.height >= 65536 || png.IHDR.width >= 65536)
|
||||
{
|
||||
hb_blob_destroy (blob);
|
||||
return false;
|
||||
}
|
||||
|
||||
extents->x_bearing = x_offset;
|
||||
extents->y_bearing = png.IHDR.height + y_offset;
|
||||
extents->width = png.IHDR.width;
|
||||
extents->height = -1 * png.IHDR.height;
|
||||
|
||||
/* Convert to font units. */
|
||||
if (strike_ppem && scale)
|
||||
{
|
||||
float scale = font->face->get_upem () / (float) strike_ppem;
|
||||
extents->x_bearing = roundf (extents->x_bearing * scale);
|
||||
extents->y_bearing = roundf (extents->y_bearing * scale);
|
||||
extents->width = roundf (extents->width * scale);
|
||||
extents->height = roundf (extents->height * scale);
|
||||
}
|
||||
|
||||
if (scale)
|
||||
font->scale_glyph_extents (extents);
|
||||
|
||||
hb_blob_destroy (blob);
|
||||
|
||||
return strike_ppem;
|
||||
}
|
||||
|
||||
private:
|
||||
hb_blob_ptr_t<sbix> table;
|
||||
|
||||
unsigned int num_glyphs;
|
||||
};
|
||||
|
||||
bool sanitize (hb_sanitize_context_t *c) const
|
||||
{
|
||||
TRACE_SANITIZE (this);
|
||||
return_trace (likely (c->check_struct (this) &&
|
||||
hb_barrier () &&
|
||||
version >= 1 &&
|
||||
strikes.sanitize (c, this)));
|
||||
}
|
||||
|
||||
bool
|
||||
add_strike (hb_subset_context_t *c, unsigned i) const
|
||||
{
|
||||
if (strikes[i].is_null () || c->source_blob->length < (unsigned) strikes[i])
|
||||
return false;
|
||||
|
||||
return (this+strikes[i]).subset (c, c->source_blob->length - (unsigned) strikes[i]);
|
||||
}
|
||||
|
||||
bool serialize_strike_offsets (hb_subset_context_t *c) const
|
||||
{
|
||||
TRACE_SERIALIZE (this);
|
||||
|
||||
auto *out = c->serializer->start_embed<Array32OfOffset32To<SBIXStrike>> ();
|
||||
if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
|
||||
|
||||
hb_vector_t<Offset32To<SBIXStrike>*> new_strikes;
|
||||
hb_vector_t<hb_serialize_context_t::objidx_t> objidxs;
|
||||
for (int i = strikes.len - 1; i >= 0; --i)
|
||||
{
|
||||
auto* o = out->serialize_append (c->serializer);
|
||||
if (unlikely (!o)) return_trace (false);
|
||||
*o = 0;
|
||||
auto snap = c->serializer->snapshot ();
|
||||
c->serializer->push ();
|
||||
bool ret = add_strike (c, i);
|
||||
if (!ret)
|
||||
{
|
||||
c->serializer->pop_discard ();
|
||||
out->pop ();
|
||||
c->serializer->revert (snap);
|
||||
}
|
||||
else
|
||||
{
|
||||
objidxs.push (c->serializer->pop_pack ());
|
||||
new_strikes.push (o);
|
||||
}
|
||||
}
|
||||
for (unsigned int i = 0; i < new_strikes.length; ++i)
|
||||
c->serializer->add_link (*new_strikes[i], objidxs[new_strikes.length - 1 - i]);
|
||||
|
||||
return_trace (true);
|
||||
}
|
||||
|
||||
bool subset (hb_subset_context_t* c) const
|
||||
{
|
||||
TRACE_SUBSET (this);
|
||||
|
||||
if (unlikely (!c->serializer->embed (this->version))) return_trace (false);
|
||||
if (unlikely (!c->serializer->embed (this->flags))) return_trace (false);
|
||||
|
||||
return_trace (serialize_strike_offsets (c));
|
||||
}
|
||||
|
||||
protected:
|
||||
HBUINT16 version; /* Table version number — set to 1 */
|
||||
HBUINT16 flags; /* Bit 0: Set to 1. Bit 1: Draw outlines.
|
||||
* Bits 2 to 15: reserved (set to 0). */
|
||||
Array32OfOffset32To<SBIXStrike>
|
||||
strikes; /* Offsets from the beginning of the 'sbix'
|
||||
* table to data for each individual bitmap strike. */
|
||||
public:
|
||||
DEFINE_SIZE_ARRAY (8, strikes);
|
||||
};
|
||||
|
||||
struct sbix_accelerator_t : sbix::accelerator_t {
|
||||
sbix_accelerator_t (hb_face_t *face) : sbix::accelerator_t (face) {}
|
||||
};
|
||||
|
||||
|
||||
} /* namespace OT */
|
||||
|
||||
#endif /* OT_COLOR_SBIX_SBIX_HH */
|
||||
+829
@@ -0,0 +1,829 @@
|
||||
/*
|
||||
* Copyright © 2018 Ebrahim Byagowi
|
||||
*
|
||||
* This is part of HarfBuzz, a text shaping library.
|
||||
*
|
||||
* Permission is hereby granted, without written agreement and without
|
||||
* license or royalty fees, to use, copy, modify, and distribute this
|
||||
* software and its documentation for any purpose, provided that the
|
||||
* above copyright notice and the following two paragraphs appear in
|
||||
* all copies of this software.
|
||||
*
|
||||
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
|
||||
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
|
||||
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
|
||||
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||
* DAMAGE.
|
||||
*
|
||||
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
|
||||
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
|
||||
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
|
||||
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
|
||||
*/
|
||||
|
||||
#ifndef OT_COLOR_SVG_SVG_HH
|
||||
#define OT_COLOR_SVG_SVG_HH
|
||||
|
||||
#include "../../../hb-open-type.hh"
|
||||
#include "../../../hb-blob.hh"
|
||||
#include "../../../hb-limits.hh"
|
||||
#include "../../../hb-map.hh"
|
||||
#include "../../../hb-paint.hh"
|
||||
#include "../../../hb-zlib.hh"
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
|
||||
/*
|
||||
* SVG -- SVG (Scalable Vector Graphics)
|
||||
* https://docs.microsoft.com/en-us/typography/opentype/spec/svg
|
||||
*/
|
||||
|
||||
#define HB_OT_TAG_SVG HB_TAG('S','V','G',' ')
|
||||
|
||||
|
||||
namespace OT {
|
||||
|
||||
static inline hb_blob_t *
|
||||
hb_ot_svg_reference_normalized_blob (hb_blob_t *image,
|
||||
const char **svg,
|
||||
unsigned *len)
|
||||
{
|
||||
hb_blob_t *blob = hb_blob_reference (image);
|
||||
unsigned data_len = 0;
|
||||
const char *data = hb_blob_get_data (blob, &data_len);
|
||||
|
||||
if (!data || !data_len)
|
||||
goto fail;
|
||||
|
||||
if (hb_blob_is_gzip (data, data_len))
|
||||
{
|
||||
uint32_t expected_size = 0;
|
||||
if (hb_gzip_get_uncompressed_size (data, data_len, &expected_size) &&
|
||||
unlikely ((size_t) expected_size > (size_t) HB_SVG_MAX_DOCUMENT_SIZE))
|
||||
goto fail;
|
||||
|
||||
hb_blob_t *uncompressed = hb_blob_decompress_gzip (blob,
|
||||
HB_SVG_MAX_DOCUMENT_SIZE);
|
||||
if (!uncompressed)
|
||||
goto fail;
|
||||
|
||||
hb_blob_destroy (blob);
|
||||
blob = uncompressed;
|
||||
data = hb_blob_get_data (blob, &data_len);
|
||||
if (!data || !data_len)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (unlikely ((size_t) data_len > (size_t) HB_SVG_MAX_DOCUMENT_SIZE))
|
||||
goto fail;
|
||||
|
||||
if (svg) *svg = data;
|
||||
if (len) *len = data_len;
|
||||
return blob;
|
||||
|
||||
fail:
|
||||
hb_blob_destroy (blob);
|
||||
if (svg) *svg = nullptr;
|
||||
if (len) *len = 0;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
struct SVGDocumentIndexEntry
|
||||
{
|
||||
int cmp (hb_codepoint_t g) const
|
||||
{ return g < startGlyphID ? -1 : g > endGlyphID ? 1 : 0; }
|
||||
|
||||
hb_codepoint_t get_start_glyph () const
|
||||
{ return startGlyphID; }
|
||||
|
||||
hb_codepoint_t get_end_glyph () const
|
||||
{ return endGlyphID; }
|
||||
|
||||
hb_blob_t *reference_blob (hb_blob_t *svg_blob, unsigned int index_offset) const
|
||||
{
|
||||
return hb_blob_create_sub_blob (svg_blob,
|
||||
index_offset + (unsigned int) svgDoc,
|
||||
svgDocLength);
|
||||
}
|
||||
|
||||
bool sanitize (hb_sanitize_context_t *c, const void *base) const
|
||||
{
|
||||
TRACE_SANITIZE (this);
|
||||
return_trace (c->check_struct (this) &&
|
||||
hb_barrier () &&
|
||||
svgDoc.sanitize (c, base, svgDocLength));
|
||||
}
|
||||
|
||||
protected:
|
||||
HBUINT16 startGlyphID; /* The first glyph ID in the range described by
|
||||
* this index entry. */
|
||||
HBUINT16 endGlyphID; /* The last glyph ID in the range described by
|
||||
* this index entry. Must be >= startGlyphID. */
|
||||
NNOffset32To<UnsizedArrayOf<HBUINT8>>
|
||||
svgDoc; /* Offset from the beginning of the SVG Document Index
|
||||
* to an SVG document. Must be non-zero. */
|
||||
HBUINT32 svgDocLength; /* Length of the SVG document.
|
||||
* Must be non-zero. */
|
||||
public:
|
||||
DEFINE_SIZE_STATIC (12);
|
||||
};
|
||||
|
||||
struct SVG
|
||||
{
|
||||
static constexpr hb_tag_t tableTag = HB_OT_TAG_SVG;
|
||||
|
||||
struct svg_id_span_t
|
||||
{
|
||||
const char *p;
|
||||
unsigned len;
|
||||
|
||||
bool operator == (const svg_id_span_t &o) const
|
||||
{
|
||||
return len == o.len && !memcmp (p, o.p, len);
|
||||
}
|
||||
|
||||
uint32_t hash () const
|
||||
{
|
||||
uint32_t h = hb_hash (len);
|
||||
for (unsigned i = 0; i < len; i++)
|
||||
h = h * 33u + (unsigned char) p[i];
|
||||
return h;
|
||||
}
|
||||
};
|
||||
|
||||
struct svg_defs_entry_t
|
||||
{
|
||||
svg_id_span_t id;
|
||||
unsigned start;
|
||||
unsigned end;
|
||||
};
|
||||
|
||||
struct svg_doc_cache_t
|
||||
{
|
||||
hb_blob_t *blob = nullptr;
|
||||
const char *svg = nullptr;
|
||||
unsigned len = 0;
|
||||
hb_vector_t<svg_defs_entry_t> defs_entries;
|
||||
hb_codepoint_t start_glyph = HB_CODEPOINT_INVALID;
|
||||
hb_codepoint_t end_glyph = HB_CODEPOINT_INVALID;
|
||||
hb_vector_t<hb_pair_t<uint32_t, uint32_t>> glyph_spans;
|
||||
hb_hashmap_t<svg_id_span_t, hb_pair_t<uint32_t, uint32_t>> id_spans;
|
||||
};
|
||||
|
||||
bool has_data () const { return svgDocEntries; }
|
||||
|
||||
struct accelerator_t
|
||||
{
|
||||
accelerator_t (hb_face_t *face);
|
||||
~accelerator_t ();
|
||||
|
||||
hb_blob_t *reference_blob_for_glyph (hb_codepoint_t glyph_id) const
|
||||
{
|
||||
return table->get_glyph_entry (glyph_id).reference_blob (table.get_blob (),
|
||||
table->svgDocEntries);
|
||||
}
|
||||
|
||||
unsigned get_document_count () const
|
||||
{ return table->get_document_count (); }
|
||||
|
||||
bool get_glyph_document_index (hb_codepoint_t glyph_id, unsigned *index) const
|
||||
{ return table->get_glyph_document_index (glyph_id, index); }
|
||||
|
||||
bool get_document_glyph_range (unsigned index,
|
||||
hb_codepoint_t *start_glyph,
|
||||
hb_codepoint_t *end_glyph) const
|
||||
{ return table->get_document_glyph_range (index, start_glyph, end_glyph); }
|
||||
|
||||
bool has_data () const { return table->has_data (); }
|
||||
|
||||
const svg_doc_cache_t *
|
||||
get_or_create_doc_cache (hb_blob_t *image,
|
||||
const char *svg,
|
||||
unsigned len,
|
||||
unsigned doc_index,
|
||||
hb_codepoint_t start_glyph,
|
||||
hb_codepoint_t end_glyph) const;
|
||||
|
||||
const char *
|
||||
doc_cache_get_svg (const svg_doc_cache_t *doc,
|
||||
unsigned *len) const;
|
||||
|
||||
const hb_vector_t<svg_defs_entry_t> *
|
||||
doc_cache_get_defs_entries (const svg_doc_cache_t *doc) const;
|
||||
|
||||
bool
|
||||
doc_cache_get_glyph_span (const svg_doc_cache_t *doc,
|
||||
hb_codepoint_t glyph,
|
||||
unsigned *start,
|
||||
unsigned *end) const;
|
||||
|
||||
bool
|
||||
doc_cache_find_id_span (const svg_doc_cache_t *doc,
|
||||
svg_id_span_t id,
|
||||
unsigned *start,
|
||||
unsigned *end) const;
|
||||
|
||||
bool
|
||||
doc_cache_find_id_cstr (const svg_doc_cache_t *doc,
|
||||
const char *id,
|
||||
unsigned *start,
|
||||
unsigned *end) const;
|
||||
|
||||
bool paint_glyph (hb_font_t *font HB_UNUSED, hb_codepoint_t glyph, hb_paint_funcs_t *funcs, void *data) const
|
||||
{
|
||||
if (!has_data ())
|
||||
return false;
|
||||
|
||||
hb_blob_t *blob = reference_blob_for_glyph (glyph);
|
||||
|
||||
if (blob == hb_blob_get_empty ())
|
||||
return false;
|
||||
|
||||
bool ret = funcs->image (data,
|
||||
blob,
|
||||
0, 0,
|
||||
HB_PAINT_IMAGE_FORMAT_SVG,
|
||||
0.f,
|
||||
nullptr);
|
||||
|
||||
hb_blob_destroy (blob);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
private:
|
||||
svg_doc_cache_t *
|
||||
make_doc_cache (hb_blob_t *image,
|
||||
const char *svg,
|
||||
unsigned len,
|
||||
hb_codepoint_t start_glyph,
|
||||
hb_codepoint_t end_glyph) const;
|
||||
|
||||
static void destroy_doc_cache (svg_doc_cache_t *doc);
|
||||
|
||||
hb_blob_ptr_t<SVG> table;
|
||||
mutable hb_vector_t<hb_atomic_t<svg_doc_cache_t *>> doc_caches;
|
||||
public:
|
||||
DEFINE_SIZE_STATIC (sizeof (hb_blob_ptr_t<SVG>) +
|
||||
sizeof (hb_vector_t<hb_atomic_t<svg_doc_cache_t *>>));
|
||||
};
|
||||
|
||||
const SVGDocumentIndexEntry &get_glyph_entry (hb_codepoint_t glyph_id) const
|
||||
{ return (this+svgDocEntries).bsearch (glyph_id); }
|
||||
|
||||
unsigned get_document_count () const
|
||||
{
|
||||
if (!has_data ())
|
||||
return 0;
|
||||
return (this + svgDocEntries).len;
|
||||
}
|
||||
|
||||
bool get_glyph_document_index (hb_codepoint_t glyph_id, unsigned *index) const
|
||||
{
|
||||
if (!has_data ())
|
||||
return false;
|
||||
return (this + svgDocEntries).bfind (glyph_id, index);
|
||||
}
|
||||
|
||||
bool get_document_glyph_range (unsigned index,
|
||||
hb_codepoint_t *start_glyph,
|
||||
hb_codepoint_t *end_glyph) const
|
||||
{
|
||||
if (!has_data ())
|
||||
return false;
|
||||
|
||||
const auto &entries = this + svgDocEntries;
|
||||
if (index >= entries.len)
|
||||
return false;
|
||||
|
||||
const auto &entry = entries.arrayZ[index];
|
||||
if (start_glyph) *start_glyph = entry.get_start_glyph ();
|
||||
if (end_glyph) *end_glyph = entry.get_end_glyph ();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool sanitize (hb_sanitize_context_t *c) const
|
||||
{
|
||||
TRACE_SANITIZE (this);
|
||||
return_trace (likely (c->check_struct (this) &&
|
||||
(this+svgDocEntries).sanitize_shallow (c)));
|
||||
}
|
||||
|
||||
protected:
|
||||
HBUINT16 version; /* Table version (starting at 0). */
|
||||
Offset32To<SortedArray16Of<SVGDocumentIndexEntry>>
|
||||
svgDocEntries; /* Offset (relative to the start of the SVG table) to the
|
||||
* SVG Documents Index. Must be non-zero. */
|
||||
/* Array of SVG Document Index Entries. */
|
||||
HBUINT32 reserved; /* Set to 0. */
|
||||
public:
|
||||
DEFINE_SIZE_STATIC (10);
|
||||
};
|
||||
|
||||
namespace _hb_svg_cache_impl {
|
||||
|
||||
struct glyph_entry_t
|
||||
{
|
||||
hb_codepoint_t glyph;
|
||||
uint32_t start;
|
||||
uint32_t end;
|
||||
};
|
||||
|
||||
struct id_entry_t
|
||||
{
|
||||
SVG::svg_id_span_t id;
|
||||
uint32_t start;
|
||||
uint32_t end;
|
||||
};
|
||||
|
||||
struct open_elem_t
|
||||
{
|
||||
unsigned start;
|
||||
SVG::svg_id_span_t id;
|
||||
bool in_defs_content;
|
||||
bool is_defs;
|
||||
};
|
||||
|
||||
static const unsigned MAX_DEPTH = 128;
|
||||
|
||||
static inline int
|
||||
find_substr (const char *s,
|
||||
unsigned n,
|
||||
unsigned from,
|
||||
const char *needle,
|
||||
unsigned needle_len)
|
||||
{
|
||||
if (!needle_len || from >= n || needle_len > n)
|
||||
return -1;
|
||||
for (unsigned i = from; i + needle_len <= n; i++)
|
||||
if (s[i] == needle[0] && !memcmp (s + i, needle, needle_len))
|
||||
return (int) i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
parse_id_in_start_tag (const char *svg,
|
||||
unsigned tag_start,
|
||||
unsigned tag_end,
|
||||
SVG::svg_id_span_t *id)
|
||||
{
|
||||
unsigned p = tag_start;
|
||||
while (p + 4 <= tag_end)
|
||||
{
|
||||
if (!memcmp (svg + p, "id=\"", 4))
|
||||
{
|
||||
unsigned b = p + 4;
|
||||
unsigned e = b;
|
||||
while (e < tag_end && svg[e] != '"') e++;
|
||||
if (e <= tag_end && e > b)
|
||||
{
|
||||
*id = {svg + b, e - b};
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (!memcmp (svg + p, "id='", 4))
|
||||
{
|
||||
unsigned b = p + 4;
|
||||
unsigned e = b;
|
||||
while (e < tag_end && svg[e] != '\'') e++;
|
||||
if (e <= tag_end && e > b)
|
||||
{
|
||||
*id = {svg + b, e - b};
|
||||
return true;
|
||||
}
|
||||
}
|
||||
p++;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
parse_glyph_id_span (const SVG::svg_id_span_t &id,
|
||||
hb_codepoint_t *glyph)
|
||||
{
|
||||
if (id.len <= 5 || memcmp (id.p, "glyph", 5))
|
||||
return false;
|
||||
|
||||
hb_codepoint_t gid = 0;
|
||||
for (unsigned i = 5; i < id.len; i++)
|
||||
{
|
||||
unsigned char c = (unsigned char) id.p[i];
|
||||
if (c < '0' || c > '9')
|
||||
return false;
|
||||
hb_codepoint_t digit = (hb_codepoint_t) (c - '0');
|
||||
if (unlikely (gid > HB_CODEPOINT_INVALID / 10 ||
|
||||
(gid == HB_CODEPOINT_INVALID / 10 &&
|
||||
digit > HB_CODEPOINT_INVALID % 10)))
|
||||
return false;
|
||||
gid = (hb_codepoint_t) (gid * 10 + digit);
|
||||
}
|
||||
|
||||
*glyph = gid;
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
parse_cache_entries_linear (const char *svg,
|
||||
unsigned len,
|
||||
hb_vector_t<SVG::svg_defs_entry_t> *defs_entries,
|
||||
hb_vector_t<glyph_entry_t> *glyph_spans,
|
||||
hb_vector_t<id_entry_t> *id_entries)
|
||||
{
|
||||
open_elem_t stack[MAX_DEPTH] = {};
|
||||
unsigned depth = 0;
|
||||
if (unlikely (!defs_entries->alloc (256) ||
|
||||
!glyph_spans->alloc (256) ||
|
||||
!id_entries->alloc (256)))
|
||||
return false;
|
||||
|
||||
unsigned defs_depth = 0;
|
||||
unsigned i = 0;
|
||||
while (i < len)
|
||||
{
|
||||
if (svg[i] != '<')
|
||||
{
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (i + 4 <= len && !memcmp (svg + i, "<!--", 4))
|
||||
{
|
||||
int cend = find_substr (svg, len, i + 4, "-->", 3);
|
||||
if (cend < 0) return false;
|
||||
i = (unsigned) cend + 3;
|
||||
continue;
|
||||
}
|
||||
if (i + 9 <= len && !memcmp (svg + i, "<![CDATA[", 9))
|
||||
{
|
||||
int cend = find_substr (svg, len, i + 9, "]]>", 3);
|
||||
if (cend < 0) return false;
|
||||
i = (unsigned) cend + 3;
|
||||
continue;
|
||||
}
|
||||
|
||||
bool closing = (i + 1 < len && svg[i + 1] == '/');
|
||||
bool special = (i + 1 < len && (svg[i + 1] == '!' || svg[i + 1] == '?'));
|
||||
|
||||
unsigned gt = i + 1;
|
||||
char quote = 0;
|
||||
while (gt < len)
|
||||
{
|
||||
char c = svg[gt];
|
||||
if (quote)
|
||||
{
|
||||
if (c == quote) quote = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (c == '"' || c == '\'')
|
||||
quote = c;
|
||||
else if (c == '>')
|
||||
break;
|
||||
}
|
||||
gt++;
|
||||
}
|
||||
if (gt >= len)
|
||||
return false;
|
||||
|
||||
if (special)
|
||||
{
|
||||
i = gt + 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
unsigned p = i + (closing ? 2 : 1);
|
||||
while (p < gt && isspace ((unsigned char) svg[p])) p++;
|
||||
const char *name = svg + p;
|
||||
unsigned name_len = 0;
|
||||
while (p + name_len < gt)
|
||||
{
|
||||
unsigned char c = (unsigned char) name[name_len];
|
||||
if (!(isalnum (c) || c == '_' || c == '-' || c == ':'))
|
||||
break;
|
||||
name_len++;
|
||||
}
|
||||
bool is_defs = (name_len == 4 && !memcmp (name, "defs", 4));
|
||||
|
||||
if (closing)
|
||||
{
|
||||
if (!depth)
|
||||
{
|
||||
i = gt + 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
open_elem_t e = stack[--depth];
|
||||
unsigned end = gt + 1;
|
||||
|
||||
if (e.id.len)
|
||||
{
|
||||
if (unlikely (!id_entries->push_or_fail (id_entry_t {e.id, (uint32_t) e.start, (uint32_t) end})))
|
||||
return false;
|
||||
|
||||
if (e.in_defs_content)
|
||||
{
|
||||
if (unlikely (!defs_entries->push_or_fail ()))
|
||||
return false;
|
||||
auto &slot = defs_entries->tail ();
|
||||
slot.id = e.id;
|
||||
slot.start = e.start;
|
||||
slot.end = end;
|
||||
}
|
||||
|
||||
hb_codepoint_t gid;
|
||||
if (parse_glyph_id_span (e.id, &gid))
|
||||
{
|
||||
if (unlikely (!glyph_spans->push_or_fail (glyph_entry_t {gid, (uint32_t) e.start, (uint32_t) end})))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (e.is_defs && defs_depth)
|
||||
defs_depth--;
|
||||
|
||||
i = end;
|
||||
continue;
|
||||
}
|
||||
|
||||
SVG::svg_id_span_t id = {};
|
||||
parse_id_in_start_tag (svg, i, gt, &id);
|
||||
|
||||
unsigned r = gt;
|
||||
while (r > i && isspace ((unsigned char) svg[r - 1])) r--;
|
||||
bool self_closing = (r > i && svg[r - 1] == '/');
|
||||
|
||||
open_elem_t e = {};
|
||||
e.start = i;
|
||||
e.id = id;
|
||||
e.in_defs_content = defs_depth > 0;
|
||||
e.is_defs = is_defs;
|
||||
|
||||
if (self_closing)
|
||||
{
|
||||
unsigned end = gt + 1;
|
||||
if (e.id.len)
|
||||
{
|
||||
if (unlikely (!id_entries->push_or_fail (id_entry_t {e.id, (uint32_t) e.start, (uint32_t) end})))
|
||||
return false;
|
||||
|
||||
if (e.in_defs_content)
|
||||
{
|
||||
if (unlikely (!defs_entries->push_or_fail ()))
|
||||
return false;
|
||||
auto &slot = defs_entries->tail ();
|
||||
slot.id = e.id;
|
||||
slot.start = e.start;
|
||||
slot.end = end;
|
||||
}
|
||||
|
||||
hb_codepoint_t gid;
|
||||
if (parse_glyph_id_span (e.id, &gid))
|
||||
{
|
||||
if (unlikely (!glyph_spans->push_or_fail (glyph_entry_t {gid, (uint32_t) e.start, (uint32_t) end})))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (unlikely (depth >= MAX_DEPTH))
|
||||
return false;
|
||||
stack[depth++] = e;
|
||||
if (is_defs)
|
||||
defs_depth++;
|
||||
}
|
||||
|
||||
i = gt + 1;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} /* namespace _hb_svg_cache_impl */
|
||||
|
||||
inline
|
||||
SVG::accelerator_t::accelerator_t (hb_face_t *face)
|
||||
{
|
||||
table = hb_sanitize_context_t ().reference_table<SVG> (face);
|
||||
doc_caches.init ();
|
||||
unsigned doc_count = table->get_document_count ();
|
||||
if (doc_count && unlikely (!doc_caches.resize (doc_count)))
|
||||
doc_caches.clear ();
|
||||
for (unsigned i = 0; i < doc_caches.length; i++)
|
||||
doc_caches.arrayZ[i].set_relaxed (nullptr);
|
||||
}
|
||||
|
||||
inline
|
||||
SVG::accelerator_t::~accelerator_t ()
|
||||
{
|
||||
for (unsigned i = 0; i < doc_caches.length; i++)
|
||||
destroy_doc_cache (doc_caches.arrayZ[i].get_relaxed ());
|
||||
doc_caches.fini ();
|
||||
table.destroy ();
|
||||
}
|
||||
|
||||
inline void
|
||||
SVG::accelerator_t::destroy_doc_cache (svg_doc_cache_t *doc)
|
||||
{
|
||||
if (!doc)
|
||||
return;
|
||||
doc->glyph_spans.fini ();
|
||||
doc->defs_entries.fini ();
|
||||
doc->id_spans.fini ();
|
||||
hb_blob_destroy (doc->blob);
|
||||
hb_free (doc);
|
||||
}
|
||||
|
||||
inline SVG::svg_doc_cache_t *
|
||||
SVG::accelerator_t::make_doc_cache (hb_blob_t *image,
|
||||
const char *svg,
|
||||
unsigned len,
|
||||
hb_codepoint_t start_glyph,
|
||||
hb_codepoint_t end_glyph) const
|
||||
{
|
||||
static const uint32_t INVALID_SPAN = 0xFFFFFFFFu;
|
||||
|
||||
auto *doc = (svg_doc_cache_t *) hb_malloc (sizeof (svg_doc_cache_t));
|
||||
if (!doc)
|
||||
return nullptr;
|
||||
|
||||
doc->blob = nullptr;
|
||||
doc->svg = nullptr;
|
||||
doc->len = 0;
|
||||
doc->defs_entries.init ();
|
||||
doc->start_glyph = HB_CODEPOINT_INVALID;
|
||||
doc->end_glyph = HB_CODEPOINT_INVALID;
|
||||
doc->glyph_spans.init ();
|
||||
doc->id_spans.init ();
|
||||
|
||||
doc->blob = hb_blob_reference (image);
|
||||
doc->svg = svg;
|
||||
doc->len = len;
|
||||
doc->start_glyph = start_glyph;
|
||||
doc->end_glyph = end_glyph;
|
||||
|
||||
if (unlikely (start_glyph == HB_CODEPOINT_INVALID || end_glyph < start_glyph))
|
||||
{
|
||||
destroy_doc_cache (doc);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
unsigned glyph_count = end_glyph - start_glyph + 1;
|
||||
if (!doc->glyph_spans.resize ((int) glyph_count))
|
||||
{
|
||||
destroy_doc_cache (doc);
|
||||
return nullptr;
|
||||
}
|
||||
for (unsigned i = 0; i < glyph_count; i++)
|
||||
doc->glyph_spans.arrayZ[i] = hb_pair_t<uint32_t, uint32_t> (INVALID_SPAN, INVALID_SPAN);
|
||||
|
||||
hb_vector_t<_hb_svg_cache_impl::glyph_entry_t> glyph_spans;
|
||||
glyph_spans.init ();
|
||||
hb_vector_t<_hb_svg_cache_impl::id_entry_t> id_entries;
|
||||
id_entries.init ();
|
||||
if (!_hb_svg_cache_impl::parse_cache_entries_linear (svg, len,
|
||||
&doc->defs_entries,
|
||||
&glyph_spans,
|
||||
&id_entries))
|
||||
{
|
||||
id_entries.fini ();
|
||||
glyph_spans.fini ();
|
||||
destroy_doc_cache (doc);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < glyph_spans.length; i++)
|
||||
{
|
||||
const auto &span = glyph_spans.arrayZ[i];
|
||||
if (unlikely (span.glyph < start_glyph || span.glyph > end_glyph))
|
||||
continue;
|
||||
doc->glyph_spans.arrayZ[span.glyph - start_glyph] = hb_pair_t<uint32_t, uint32_t> (span.start, span.end);
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < id_entries.length; i++)
|
||||
{
|
||||
const auto &e = id_entries.arrayZ[i];
|
||||
hb_pair_t<uint32_t, uint32_t> *out = nullptr;
|
||||
if (doc->id_spans.has (e.id, &out))
|
||||
continue;
|
||||
if (unlikely (!doc->id_spans.set (e.id, hb_pair_t<uint32_t, uint32_t> (e.start, e.end))))
|
||||
{
|
||||
id_entries.fini ();
|
||||
glyph_spans.fini ();
|
||||
destroy_doc_cache (doc);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
id_entries.fini ();
|
||||
glyph_spans.fini ();
|
||||
return doc;
|
||||
}
|
||||
|
||||
inline const SVG::svg_doc_cache_t *
|
||||
SVG::accelerator_t::get_or_create_doc_cache (hb_blob_t *image,
|
||||
const char *svg,
|
||||
unsigned len,
|
||||
unsigned doc_index,
|
||||
hb_codepoint_t start_glyph,
|
||||
hb_codepoint_t end_glyph) const
|
||||
{
|
||||
if (doc_index >= doc_caches.length)
|
||||
return nullptr;
|
||||
|
||||
auto &slot = doc_caches.arrayZ[doc_index];
|
||||
auto *doc = slot.get_acquire ();
|
||||
if (doc)
|
||||
return doc;
|
||||
|
||||
auto *fresh = make_doc_cache (image, svg, len, start_glyph, end_glyph);
|
||||
if (!fresh)
|
||||
return nullptr;
|
||||
|
||||
auto *expected = (svg_doc_cache_t *) nullptr;
|
||||
if (slot.cmpexch (expected, fresh))
|
||||
return fresh;
|
||||
|
||||
destroy_doc_cache (fresh);
|
||||
return expected;
|
||||
}
|
||||
|
||||
inline const char *
|
||||
SVG::accelerator_t::doc_cache_get_svg (const svg_doc_cache_t *doc,
|
||||
unsigned *len) const
|
||||
{
|
||||
if (!doc)
|
||||
{
|
||||
if (len) *len = 0;
|
||||
return nullptr;
|
||||
}
|
||||
if (len) *len = doc->len;
|
||||
return doc->svg;
|
||||
}
|
||||
|
||||
inline const hb_vector_t<SVG::svg_defs_entry_t> *
|
||||
SVG::accelerator_t::doc_cache_get_defs_entries (const svg_doc_cache_t *doc) const
|
||||
{
|
||||
return doc ? &doc->defs_entries : nullptr;
|
||||
}
|
||||
|
||||
inline bool
|
||||
SVG::accelerator_t::doc_cache_get_glyph_span (const svg_doc_cache_t *doc,
|
||||
hb_codepoint_t glyph,
|
||||
unsigned *start,
|
||||
unsigned *end) const
|
||||
{
|
||||
static const uint32_t INVALID_SPAN = 0xFFFFFFFFu;
|
||||
if (!doc || doc->start_glyph == HB_CODEPOINT_INVALID ||
|
||||
glyph < doc->start_glyph || glyph > doc->end_glyph)
|
||||
return false;
|
||||
|
||||
const auto &span = doc->glyph_spans.arrayZ[glyph - doc->start_glyph];
|
||||
if (span.first == INVALID_SPAN)
|
||||
return false;
|
||||
if (unlikely (span.first > span.second || span.second > doc->len))
|
||||
return false;
|
||||
|
||||
if (start) *start = span.first;
|
||||
if (end) *end = span.second;
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool
|
||||
SVG::accelerator_t::doc_cache_find_id_span (const svg_doc_cache_t *doc,
|
||||
svg_id_span_t id,
|
||||
unsigned *start,
|
||||
unsigned *end) const
|
||||
{
|
||||
if (!doc || !id.p || !id.len)
|
||||
return false;
|
||||
hb_pair_t<uint32_t, uint32_t> *span = nullptr;
|
||||
if (!doc->id_spans.has (id, &span))
|
||||
return false;
|
||||
if (unlikely (span->first > span->second || span->second > doc->len))
|
||||
return false;
|
||||
if (start) *start = span->first;
|
||||
if (end) *end = span->second;
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool
|
||||
SVG::accelerator_t::doc_cache_find_id_cstr (const svg_doc_cache_t *doc,
|
||||
const char *id,
|
||||
unsigned *start,
|
||||
unsigned *end) const
|
||||
{
|
||||
if (!id) return false;
|
||||
svg_id_span_t key = {id, (unsigned) strlen (id)};
|
||||
return doc_cache_find_id_span (doc, key, start, end);
|
||||
}
|
||||
|
||||
struct SVG_accelerator_t : SVG::accelerator_t {
|
||||
SVG_accelerator_t (hb_face_t *face) : SVG::accelerator_t (face) {}
|
||||
};
|
||||
|
||||
} /* namespace OT */
|
||||
|
||||
|
||||
#endif /* OT_COLOR_SVG_SVG_HH */
|
||||
+394
@@ -0,0 +1,394 @@
|
||||
/*
|
||||
* Copyright © 2007,2008,2009 Red Hat, Inc.
|
||||
* Copyright © 2010,2012 Google, Inc.
|
||||
*
|
||||
* This is part of HarfBuzz, a text shaping library.
|
||||
*
|
||||
* Permission is hereby granted, without written agreement and without
|
||||
* license or royalty fees, to use, copy, modify, and distribute this
|
||||
* software and its documentation for any purpose, provided that the
|
||||
* above copyright notice and the following two paragraphs appear in
|
||||
* all copies of this software.
|
||||
*
|
||||
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
|
||||
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
|
||||
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
|
||||
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||
* DAMAGE.
|
||||
*
|
||||
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
|
||||
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
|
||||
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
|
||||
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
|
||||
*
|
||||
* Red Hat Author(s): Behdad Esfahbod
|
||||
* Google Author(s): Behdad Esfahbod, Garret Rieger
|
||||
*/
|
||||
|
||||
#ifndef OT_LAYOUT_COMMON_COVERAGE_HH
|
||||
#define OT_LAYOUT_COMMON_COVERAGE_HH
|
||||
|
||||
#include "../types.hh"
|
||||
#include "CoverageFormat1.hh"
|
||||
#include "CoverageFormat2.hh"
|
||||
|
||||
namespace OT {
|
||||
namespace Layout {
|
||||
namespace Common {
|
||||
|
||||
template<typename Iterator>
|
||||
static inline void Coverage_serialize (hb_serialize_context_t *c,
|
||||
Iterator it);
|
||||
|
||||
struct Coverage
|
||||
{
|
||||
|
||||
protected:
|
||||
union {
|
||||
struct { HBUINT16 v; } format; /* Format identifier */
|
||||
CoverageFormat1_3<SmallTypes> format1;
|
||||
CoverageFormat2_4<SmallTypes> format2;
|
||||
#ifndef HB_NO_BEYOND_64K
|
||||
CoverageFormat1_3<MediumTypes>format3;
|
||||
CoverageFormat2_4<MediumTypes>format4;
|
||||
#endif
|
||||
} u;
|
||||
public:
|
||||
DEFINE_SIZE_UNION (2, format.v);
|
||||
|
||||
#ifndef HB_OPTIMIZE_SIZE
|
||||
HB_ALWAYS_INLINE
|
||||
#endif
|
||||
bool sanitize (hb_sanitize_context_t *c) const
|
||||
{
|
||||
TRACE_SANITIZE (this);
|
||||
if (!u.format.v.sanitize (c)) return_trace (false);
|
||||
hb_barrier ();
|
||||
switch (u.format.v)
|
||||
{
|
||||
case 1: return_trace (u.format1.sanitize (c));
|
||||
case 2: return_trace (u.format2.sanitize (c));
|
||||
#ifndef HB_NO_BEYOND_64K
|
||||
case 3: return_trace (u.format3.sanitize (c));
|
||||
case 4: return_trace (u.format4.sanitize (c));
|
||||
#endif
|
||||
default:return_trace (true);
|
||||
}
|
||||
}
|
||||
|
||||
/* Has interface. */
|
||||
unsigned operator [] (hb_codepoint_t k) const { return get (k); }
|
||||
bool has (hb_codepoint_t k) const { return (*this)[k] != NOT_COVERED; }
|
||||
/* Predicate. */
|
||||
bool operator () (hb_codepoint_t k) const { return has (k); }
|
||||
|
||||
unsigned int get (hb_codepoint_t k) const { return get_coverage (k); }
|
||||
unsigned int get_coverage (hb_codepoint_t glyph_id) const
|
||||
{
|
||||
switch (u.format.v) {
|
||||
case 1: return u.format1.get_coverage (glyph_id);
|
||||
case 2: return u.format2.get_coverage (glyph_id);
|
||||
#ifndef HB_NO_BEYOND_64K
|
||||
case 3: return u.format3.get_coverage (glyph_id);
|
||||
case 4: return u.format4.get_coverage (glyph_id);
|
||||
#endif
|
||||
default:return NOT_COVERED;
|
||||
}
|
||||
}
|
||||
unsigned int get_coverage (hb_codepoint_t glyph_id,
|
||||
hb_ot_layout_mapping_cache_t *cache) const
|
||||
{
|
||||
unsigned coverage;
|
||||
if (cache && cache->get (glyph_id, &coverage)) return coverage < cache->MAX_VALUE ? coverage : NOT_COVERED;
|
||||
coverage = get_coverage (glyph_id);
|
||||
if (cache) {
|
||||
if (coverage == NOT_COVERED)
|
||||
cache->set_unchecked (glyph_id, cache->MAX_VALUE);
|
||||
else if (likely (coverage < cache->MAX_VALUE))
|
||||
cache->set_unchecked (glyph_id, coverage);
|
||||
}
|
||||
return coverage;
|
||||
}
|
||||
|
||||
unsigned int get_coverage_binary (hb_codepoint_t glyph_id,
|
||||
hb_ot_layout_binary_cache_t *cache) const
|
||||
{
|
||||
unsigned coverage;
|
||||
if (cache && cache->get (glyph_id, &coverage)) return coverage < cache->MAX_VALUE ? coverage : NOT_COVERED;
|
||||
coverage = get_coverage (glyph_id);
|
||||
if (cache) {
|
||||
if (coverage == NOT_COVERED)
|
||||
cache->set_unchecked (glyph_id, cache->MAX_VALUE);
|
||||
else
|
||||
cache->set_unchecked (glyph_id, 0);
|
||||
}
|
||||
return coverage;
|
||||
}
|
||||
|
||||
unsigned get_population () const
|
||||
{
|
||||
switch (u.format.v) {
|
||||
case 1: return u.format1.get_population ();
|
||||
case 2: return u.format2.get_population ();
|
||||
#ifndef HB_NO_BEYOND_64K
|
||||
case 3: return u.format3.get_population ();
|
||||
case 4: return u.format4.get_population ();
|
||||
#endif
|
||||
default:return NOT_COVERED;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Iterator,
|
||||
hb_requires (hb_is_sorted_source_of (Iterator, hb_codepoint_t))>
|
||||
bool serialize (hb_serialize_context_t *c, Iterator glyphs)
|
||||
{
|
||||
TRACE_SERIALIZE (this);
|
||||
if (unlikely (!c->extend_min (this))) return_trace (false);
|
||||
|
||||
unsigned count = hb_len (glyphs);
|
||||
unsigned num_ranges = 0;
|
||||
hb_codepoint_t last = (hb_codepoint_t) -2;
|
||||
hb_codepoint_t max = 0;
|
||||
bool unsorted = false;
|
||||
for (auto g: glyphs)
|
||||
{
|
||||
if (last != (hb_codepoint_t) -2 && g < last)
|
||||
unsorted = true;
|
||||
if (last + 1 != g)
|
||||
num_ranges++;
|
||||
last = g;
|
||||
if (g > max) max = g;
|
||||
}
|
||||
u.format.v = !unsorted && count <= num_ranges * 3 ? 1 : 2;
|
||||
|
||||
#ifndef HB_NO_BEYOND_64K
|
||||
if (max > 0xFFFFu)
|
||||
u.format.v += 2;
|
||||
if (unlikely (max > 0xFFFFFFu))
|
||||
#else
|
||||
if (unlikely (max > 0xFFFFu))
|
||||
#endif
|
||||
{
|
||||
c->check_success (false, HB_SERIALIZE_ERROR_INT_OVERFLOW);
|
||||
return_trace (false);
|
||||
}
|
||||
|
||||
switch (u.format.v)
|
||||
{
|
||||
case 1: return_trace (u.format1.serialize (c, glyphs));
|
||||
case 2: return_trace (u.format2.serialize (c, glyphs));
|
||||
#ifndef HB_NO_BEYOND_64K
|
||||
case 3: return_trace (u.format3.serialize (c, glyphs));
|
||||
case 4: return_trace (u.format4.serialize (c, glyphs));
|
||||
#endif
|
||||
default:return_trace (false);
|
||||
}
|
||||
}
|
||||
|
||||
bool subset (hb_subset_context_t *c) const
|
||||
{
|
||||
TRACE_SUBSET (this);
|
||||
auto it =
|
||||
+ iter ()
|
||||
| hb_take (c->plan->source->get_num_glyphs ())
|
||||
| hb_map_retains_sorting (c->plan->glyph_map_gsub)
|
||||
| hb_filter ([] (hb_codepoint_t glyph) { return glyph != HB_MAP_VALUE_INVALID; })
|
||||
;
|
||||
|
||||
// Cache the iterator result as it will be iterated multiple times
|
||||
// by the serialize code below.
|
||||
hb_sorted_vector_t<hb_codepoint_t> glyphs (it);
|
||||
Coverage_serialize (c->serializer, glyphs.iter ());
|
||||
return_trace (bool (glyphs));
|
||||
}
|
||||
|
||||
bool intersects (const hb_set_t *glyphs) const
|
||||
{
|
||||
switch (u.format.v)
|
||||
{
|
||||
case 1: return u.format1.intersects (glyphs);
|
||||
case 2: return u.format2.intersects (glyphs);
|
||||
#ifndef HB_NO_BEYOND_64K
|
||||
case 3: return u.format3.intersects (glyphs);
|
||||
case 4: return u.format4.intersects (glyphs);
|
||||
#endif
|
||||
default:return false;
|
||||
}
|
||||
}
|
||||
bool intersects_coverage (const hb_set_t *glyphs, unsigned int index) const
|
||||
{
|
||||
switch (u.format.v)
|
||||
{
|
||||
case 1: return u.format1.intersects_coverage (glyphs, index);
|
||||
case 2: return u.format2.intersects_coverage (glyphs, index);
|
||||
#ifndef HB_NO_BEYOND_64K
|
||||
case 3: return u.format3.intersects_coverage (glyphs, index);
|
||||
case 4: return u.format4.intersects_coverage (glyphs, index);
|
||||
#endif
|
||||
default:return false;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned cost () const
|
||||
{
|
||||
switch (u.format.v) {
|
||||
case 1: hb_barrier (); return u.format1.cost ();
|
||||
case 2: hb_barrier (); return u.format2.cost ();
|
||||
#ifndef HB_NO_BEYOND_64K
|
||||
case 3: hb_barrier (); return u.format3.cost ();
|
||||
case 4: hb_barrier (); return u.format4.cost ();
|
||||
#endif
|
||||
default:return 0u;
|
||||
}
|
||||
}
|
||||
|
||||
/* Might return false if array looks unsorted.
|
||||
* Used for faster rejection of corrupt data. */
|
||||
template <typename set_t>
|
||||
bool collect_coverage (set_t *glyphs) const
|
||||
{
|
||||
switch (u.format.v)
|
||||
{
|
||||
case 1: return u.format1.collect_coverage (glyphs);
|
||||
case 2: return u.format2.collect_coverage (glyphs);
|
||||
#ifndef HB_NO_BEYOND_64K
|
||||
case 3: return u.format3.collect_coverage (glyphs);
|
||||
case 4: return u.format4.collect_coverage (glyphs);
|
||||
#endif
|
||||
default:return false;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename IterableOut,
|
||||
hb_requires (hb_is_sink_of (IterableOut, hb_codepoint_t))>
|
||||
void intersect_set (const hb_set_t &glyphs, IterableOut&& intersect_glyphs) const
|
||||
{
|
||||
switch (u.format.v)
|
||||
{
|
||||
case 1: return u.format1.intersect_set (glyphs, intersect_glyphs);
|
||||
case 2: return u.format2.intersect_set (glyphs, intersect_glyphs);
|
||||
#ifndef HB_NO_BEYOND_64K
|
||||
case 3: return u.format3.intersect_set (glyphs, intersect_glyphs);
|
||||
case 4: return u.format4.intersect_set (glyphs, intersect_glyphs);
|
||||
#endif
|
||||
default:return ;
|
||||
}
|
||||
}
|
||||
|
||||
struct iter_t : hb_iter_with_fallback_t<iter_t, hb_codepoint_t>
|
||||
{
|
||||
static constexpr bool is_sorted_iterator = true;
|
||||
iter_t (const Coverage &c_ = Null (Coverage))
|
||||
{
|
||||
hb_memset (this, 0, sizeof (*this));
|
||||
format = c_.u.format.v;
|
||||
switch (format)
|
||||
{
|
||||
case 1: u.format1.init (c_.u.format1); return;
|
||||
case 2: u.format2.init (c_.u.format2); return;
|
||||
#ifndef HB_NO_BEYOND_64K
|
||||
case 3: u.format3.init (c_.u.format3); return;
|
||||
case 4: u.format4.init (c_.u.format4); return;
|
||||
#endif
|
||||
default: return;
|
||||
}
|
||||
}
|
||||
bool __more__ () const
|
||||
{
|
||||
switch (format)
|
||||
{
|
||||
case 1: return u.format1.__more__ ();
|
||||
case 2: return u.format2.__more__ ();
|
||||
#ifndef HB_NO_BEYOND_64K
|
||||
case 3: return u.format3.__more__ ();
|
||||
case 4: return u.format4.__more__ ();
|
||||
#endif
|
||||
default:return false;
|
||||
}
|
||||
}
|
||||
void __next__ ()
|
||||
{
|
||||
switch (format)
|
||||
{
|
||||
case 1: u.format1.__next__ (); break;
|
||||
case 2: u.format2.__next__ (); break;
|
||||
#ifndef HB_NO_BEYOND_64K
|
||||
case 3: u.format3.__next__ (); break;
|
||||
case 4: u.format4.__next__ (); break;
|
||||
#endif
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
typedef hb_codepoint_t __item_t__;
|
||||
__item_t__ __item__ () const { return get_glyph (); }
|
||||
|
||||
hb_codepoint_t get_glyph () const
|
||||
{
|
||||
switch (format)
|
||||
{
|
||||
case 1: return u.format1.get_glyph ();
|
||||
case 2: return u.format2.get_glyph ();
|
||||
#ifndef HB_NO_BEYOND_64K
|
||||
case 3: return u.format3.get_glyph ();
|
||||
case 4: return u.format4.get_glyph ();
|
||||
#endif
|
||||
default:return 0;
|
||||
}
|
||||
}
|
||||
bool operator != (const iter_t& o) const
|
||||
{
|
||||
if (unlikely (format != o.format)) return true;
|
||||
switch (format)
|
||||
{
|
||||
case 1: return u.format1 != o.u.format1;
|
||||
case 2: return u.format2 != o.u.format2;
|
||||
#ifndef HB_NO_BEYOND_64K
|
||||
case 3: return u.format3 != o.u.format3;
|
||||
case 4: return u.format4 != o.u.format4;
|
||||
#endif
|
||||
default:return false;
|
||||
}
|
||||
}
|
||||
iter_t __end__ () const
|
||||
{
|
||||
iter_t it;
|
||||
it.format = format;
|
||||
switch (format)
|
||||
{
|
||||
case 1: it.u.format1 = u.format1.__end__ (); break;
|
||||
case 2: it.u.format2 = u.format2.__end__ (); break;
|
||||
#ifndef HB_NO_BEYOND_64K
|
||||
case 3: it.u.format3 = u.format3.__end__ (); break;
|
||||
case 4: it.u.format4 = u.format4.__end__ (); break;
|
||||
#endif
|
||||
default: break;
|
||||
}
|
||||
return it;
|
||||
}
|
||||
|
||||
private:
|
||||
unsigned int format;
|
||||
union {
|
||||
#ifndef HB_NO_BEYOND_64K
|
||||
CoverageFormat2_4<MediumTypes>::iter_t format4; /* Put this one first since it's larger; helps shut up compiler. */
|
||||
CoverageFormat1_3<MediumTypes>::iter_t format3;
|
||||
#endif
|
||||
CoverageFormat2_4<SmallTypes>::iter_t format2; /* Put this one first since it's larger; helps shut up compiler. */
|
||||
CoverageFormat1_3<SmallTypes>::iter_t format1;
|
||||
} u;
|
||||
};
|
||||
iter_t iter () const { return iter_t (*this); }
|
||||
};
|
||||
|
||||
template<typename Iterator>
|
||||
static inline void
|
||||
Coverage_serialize (hb_serialize_context_t *c,
|
||||
Iterator it)
|
||||
{ c->start_embed<Coverage> ()->serialize (c, it); }
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // #ifndef OT_LAYOUT_COMMON_COVERAGE_HH
|
||||
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
* Copyright © 2007,2008,2009 Red Hat, Inc.
|
||||
* Copyright © 2010,2012 Google, Inc.
|
||||
*
|
||||
* This is part of HarfBuzz, a text shaping library.
|
||||
*
|
||||
* Permission is hereby granted, without written agreement and without
|
||||
* license or royalty fees, to use, copy, modify, and distribute this
|
||||
* software and its documentation for any purpose, provided that the
|
||||
* above copyright notice and the following two paragraphs appear in
|
||||
* all copies of this software.
|
||||
*
|
||||
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
|
||||
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
|
||||
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
|
||||
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||
* DAMAGE.
|
||||
*
|
||||
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
|
||||
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
|
||||
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
|
||||
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
|
||||
*
|
||||
* Red Hat Author(s): Behdad Esfahbod
|
||||
* Google Author(s): Behdad Esfahbod, Garret Rieger
|
||||
*/
|
||||
|
||||
|
||||
#ifndef OT_LAYOUT_COMMON_COVERAGEFORMAT1_HH
|
||||
#define OT_LAYOUT_COMMON_COVERAGEFORMAT1_HH
|
||||
|
||||
namespace OT {
|
||||
namespace Layout {
|
||||
namespace Common {
|
||||
|
||||
#define NOT_COVERED ((unsigned int) -1)
|
||||
|
||||
template <typename Types>
|
||||
struct CoverageFormat1_3
|
||||
{
|
||||
friend struct Coverage;
|
||||
|
||||
public:
|
||||
HBUINT16 coverageFormat; /* Format identifier--format = 1 */
|
||||
SortedArray16Of<typename Types::HBGlyphID>
|
||||
glyphArray; /* Array of GlyphIDs--in numerical order */
|
||||
|
||||
DEFINE_SIZE_ARRAY (4, glyphArray);
|
||||
|
||||
private:
|
||||
bool sanitize (hb_sanitize_context_t *c) const
|
||||
{
|
||||
TRACE_SANITIZE (this);
|
||||
return_trace (glyphArray.sanitize (c));
|
||||
}
|
||||
|
||||
unsigned int get_coverage (hb_codepoint_t glyph_id) const
|
||||
{
|
||||
unsigned int i;
|
||||
glyphArray.bfind (glyph_id, &i, HB_NOT_FOUND_STORE, NOT_COVERED);
|
||||
return i;
|
||||
}
|
||||
|
||||
unsigned get_population () const
|
||||
{
|
||||
return glyphArray.len;
|
||||
}
|
||||
|
||||
template <typename Iterator,
|
||||
hb_requires (hb_is_sorted_source_of (Iterator, hb_codepoint_t))>
|
||||
bool serialize (hb_serialize_context_t *c, Iterator glyphs)
|
||||
{
|
||||
TRACE_SERIALIZE (this);
|
||||
return_trace (glyphArray.serialize (c, glyphs));
|
||||
}
|
||||
|
||||
bool intersects (const hb_set_t *glyphs) const
|
||||
{
|
||||
if (glyphArray.len > glyphs->get_population () * hb_bit_storage ((unsigned) glyphArray.len))
|
||||
{
|
||||
for (auto g : *glyphs)
|
||||
if (get_coverage (g) != NOT_COVERED)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const auto& g : glyphArray.as_array ())
|
||||
if (glyphs->has (g))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
bool intersects_coverage (const hb_set_t *glyphs, unsigned int index) const
|
||||
{ return glyphs->has (glyphArray[index]); }
|
||||
|
||||
template <typename IterableOut,
|
||||
hb_requires (hb_is_sink_of (IterableOut, hb_codepoint_t))>
|
||||
void intersect_set (const hb_set_t &glyphs, IterableOut&& intersect_glyphs) const
|
||||
{
|
||||
unsigned count = glyphArray.len;
|
||||
for (unsigned i = 0; i < count; i++)
|
||||
if (glyphs.has (glyphArray[i]))
|
||||
intersect_glyphs << glyphArray[i];
|
||||
}
|
||||
|
||||
unsigned cost () const { return hb_bit_storage ((unsigned) glyphArray.len); /* bsearch cost */ }
|
||||
|
||||
template <typename set_t>
|
||||
bool collect_coverage (set_t *glyphs) const
|
||||
{ return glyphs->add_sorted_array (glyphArray.as_array ()); }
|
||||
|
||||
public:
|
||||
/* Older compilers need this to be public. */
|
||||
struct iter_t
|
||||
{
|
||||
void init (const struct CoverageFormat1_3 &c_) { c = &c_; i = 0; }
|
||||
bool __more__ () const { return i < c->glyphArray.len; }
|
||||
void __next__ () { i++; }
|
||||
hb_codepoint_t get_glyph () const { return c->glyphArray[i]; }
|
||||
bool operator != (const iter_t& o) const
|
||||
{ return i != o.i; }
|
||||
iter_t __end__ () const { iter_t it; it.init (*c); it.i = c->glyphArray.len; return it; }
|
||||
|
||||
private:
|
||||
const struct CoverageFormat1_3 *c;
|
||||
unsigned int i;
|
||||
};
|
||||
private:
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // #ifndef OT_LAYOUT_COMMON_COVERAGEFORMAT1_HH
|
||||
@@ -0,0 +1,241 @@
|
||||
/*
|
||||
* Copyright © 2007,2008,2009 Red Hat, Inc.
|
||||
* Copyright © 2010,2012 Google, Inc.
|
||||
*
|
||||
* This is part of HarfBuzz, a text shaping library.
|
||||
*
|
||||
* Permission is hereby granted, without written agreement and without
|
||||
* license or royalty fees, to use, copy, modify, and distribute this
|
||||
* software and its documentation for any purpose, provided that the
|
||||
* above copyright notice and the following two paragraphs appear in
|
||||
* all copies of this software.
|
||||
*
|
||||
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
|
||||
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
|
||||
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
|
||||
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||
* DAMAGE.
|
||||
*
|
||||
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
|
||||
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
|
||||
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
|
||||
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
|
||||
*
|
||||
* Red Hat Author(s): Behdad Esfahbod
|
||||
* Google Author(s): Behdad Esfahbod, Garret Rieger
|
||||
*/
|
||||
|
||||
#ifndef OT_LAYOUT_COMMON_COVERAGEFORMAT2_HH
|
||||
#define OT_LAYOUT_COMMON_COVERAGEFORMAT2_HH
|
||||
|
||||
#include "RangeRecord.hh"
|
||||
|
||||
namespace OT {
|
||||
namespace Layout {
|
||||
namespace Common {
|
||||
|
||||
template <typename Types>
|
||||
struct CoverageFormat2_4
|
||||
{
|
||||
friend struct Coverage;
|
||||
|
||||
public:
|
||||
HBUINT16 coverageFormat; /* Format identifier--format = 2 */
|
||||
SortedArray16Of<RangeRecord<Types>>
|
||||
rangeRecord; /* Array of glyph ranges--ordered by
|
||||
* Start GlyphID. rangeCount entries
|
||||
* long */
|
||||
public:
|
||||
DEFINE_SIZE_ARRAY (4, rangeRecord);
|
||||
|
||||
private:
|
||||
|
||||
bool sanitize (hb_sanitize_context_t *c) const
|
||||
{
|
||||
TRACE_SANITIZE (this);
|
||||
return_trace (rangeRecord.sanitize (c));
|
||||
}
|
||||
|
||||
unsigned int get_coverage (hb_codepoint_t glyph_id) const
|
||||
{
|
||||
const RangeRecord<Types> &range = rangeRecord.bsearch (glyph_id);
|
||||
return likely (range.first <= range.last)
|
||||
? (unsigned int) range.value + (glyph_id - range.first)
|
||||
: NOT_COVERED;
|
||||
}
|
||||
|
||||
unsigned get_population () const
|
||||
{
|
||||
typename Types::large_int ret = 0;
|
||||
for (const auto &r : rangeRecord)
|
||||
ret += r.get_population ();
|
||||
return ret > UINT_MAX ? UINT_MAX : (unsigned) ret;
|
||||
}
|
||||
|
||||
template <typename Iterator,
|
||||
hb_requires (hb_is_sorted_source_of (Iterator, hb_codepoint_t))>
|
||||
bool serialize (hb_serialize_context_t *c, Iterator glyphs)
|
||||
{
|
||||
TRACE_SERIALIZE (this);
|
||||
if (unlikely (!c->extend_min (this))) return_trace (false);
|
||||
|
||||
unsigned num_ranges = 0;
|
||||
hb_codepoint_t last = (hb_codepoint_t) -2;
|
||||
for (auto g: glyphs)
|
||||
{
|
||||
if (last + 1 != g)
|
||||
num_ranges++;
|
||||
last = g;
|
||||
}
|
||||
|
||||
if (unlikely (!rangeRecord.serialize (c, num_ranges))) return_trace (false);
|
||||
if (!num_ranges) return_trace (true);
|
||||
|
||||
unsigned count = 0;
|
||||
unsigned range = (unsigned) -1;
|
||||
last = (hb_codepoint_t) -2;
|
||||
unsigned unsorted = false;
|
||||
for (auto g: glyphs)
|
||||
{
|
||||
if (last + 1 != g)
|
||||
{
|
||||
if (unlikely (last != (hb_codepoint_t) -2 && last + 1 > g))
|
||||
unsorted = true;
|
||||
|
||||
range++;
|
||||
rangeRecord.arrayZ[range].first = g;
|
||||
rangeRecord.arrayZ[range].value = count;
|
||||
}
|
||||
rangeRecord.arrayZ[range].last = g;
|
||||
last = g;
|
||||
count++;
|
||||
}
|
||||
|
||||
if (unlikely (unsorted))
|
||||
rangeRecord.as_array ().qsort (RangeRecord<Types>::cmp_range);
|
||||
|
||||
return_trace (true);
|
||||
}
|
||||
|
||||
bool intersects (const hb_set_t *glyphs) const
|
||||
{
|
||||
if (rangeRecord.len > glyphs->get_population () * hb_bit_storage ((unsigned) rangeRecord.len))
|
||||
{
|
||||
for (auto g : *glyphs)
|
||||
if (get_coverage (g) != NOT_COVERED)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
return hb_any (+ hb_iter (rangeRecord)
|
||||
| hb_map ([glyphs] (const RangeRecord<Types> &range) { return range.intersects (*glyphs); }));
|
||||
}
|
||||
bool intersects_coverage (const hb_set_t *glyphs, unsigned int index) const
|
||||
{
|
||||
auto *range = rangeRecord.as_array ().bsearch (index);
|
||||
if (range)
|
||||
return range->intersects (*glyphs);
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename IterableOut,
|
||||
hb_requires (hb_is_sink_of (IterableOut, hb_codepoint_t))>
|
||||
void intersect_set (const hb_set_t &glyphs, IterableOut&& intersect_glyphs) const
|
||||
{
|
||||
/* Break out of loop for overlapping, broken, tables,
|
||||
* to avoid fuzzer timouts. */
|
||||
hb_codepoint_t last = 0;
|
||||
for (const auto& range : rangeRecord)
|
||||
{
|
||||
if (unlikely (range.first < last))
|
||||
break;
|
||||
last = range.last;
|
||||
for (hb_codepoint_t g = range.first - 1;
|
||||
glyphs.next (&g) && g <= last;)
|
||||
intersect_glyphs << g;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned cost () const { return hb_bit_storage ((unsigned) rangeRecord.len); /* bsearch cost */ }
|
||||
|
||||
template <typename set_t>
|
||||
bool collect_coverage (set_t *glyphs) const
|
||||
{
|
||||
for (const auto& range: rangeRecord)
|
||||
if (unlikely (!range.collect_coverage (glyphs)))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public:
|
||||
/* Older compilers need this to be public. */
|
||||
struct iter_t
|
||||
{
|
||||
void init (const CoverageFormat2_4 &c_)
|
||||
{
|
||||
c = &c_;
|
||||
coverage = 0;
|
||||
i = 0;
|
||||
j = c->rangeRecord.len ? c->rangeRecord[0].first : 0;
|
||||
if (unlikely (c->rangeRecord[0].first > c->rangeRecord[0].last))
|
||||
{
|
||||
/* Broken table. Skip. */
|
||||
i = c->rangeRecord.len;
|
||||
j = 0;
|
||||
}
|
||||
}
|
||||
bool __more__ () const { return i < c->rangeRecord.len; }
|
||||
void __next__ ()
|
||||
{
|
||||
if (j >= c->rangeRecord[i].last)
|
||||
{
|
||||
i++;
|
||||
if (__more__ ())
|
||||
{
|
||||
unsigned int old = coverage;
|
||||
j = c->rangeRecord.arrayZ[i].first;
|
||||
coverage = c->rangeRecord.arrayZ[i].value;
|
||||
if (unlikely (coverage != old + 1))
|
||||
{
|
||||
/* Broken table. Skip. Important to avoid DoS.
|
||||
* Also, our callers depend on coverage being
|
||||
* consecutive and monotonically increasing,
|
||||
* ie. iota(). */
|
||||
i = c->rangeRecord.len;
|
||||
j = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
j = 0;
|
||||
return;
|
||||
}
|
||||
coverage++;
|
||||
j++;
|
||||
}
|
||||
hb_codepoint_t get_glyph () const { return j; }
|
||||
bool operator != (const iter_t& o) const
|
||||
{ return i != o.i || j != o.j; }
|
||||
iter_t __end__ () const
|
||||
{
|
||||
iter_t it;
|
||||
it.init (*c);
|
||||
it.i = c->rangeRecord.len;
|
||||
it.j = 0;
|
||||
return it;
|
||||
}
|
||||
|
||||
private:
|
||||
const struct CoverageFormat2_4 *c;
|
||||
unsigned int i, coverage;
|
||||
hb_codepoint_t j;
|
||||
};
|
||||
private:
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // #ifndef OT_LAYOUT_COMMON_COVERAGEFORMAT2_HH
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright © 2007,2008,2009 Red Hat, Inc.
|
||||
* Copyright © 2010,2012 Google, Inc.
|
||||
*
|
||||
* This is part of HarfBuzz, a text shaping library.
|
||||
*
|
||||
* Permission is hereby granted, without written agreement and without
|
||||
* license or royalty fees, to use, copy, modify, and distribute this
|
||||
* software and its documentation for any purpose, provided that the
|
||||
* above copyright notice and the following two paragraphs appear in
|
||||
* all copies of this software.
|
||||
*
|
||||
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
|
||||
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
|
||||
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
|
||||
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||
* DAMAGE.
|
||||
*
|
||||
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
|
||||
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
|
||||
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
|
||||
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
|
||||
*
|
||||
* Red Hat Author(s): Behdad Esfahbod
|
||||
* Google Author(s): Behdad Esfahbod, Garret Rieger
|
||||
*/
|
||||
|
||||
#ifndef OT_LAYOUT_COMMON_RANGERECORD_HH
|
||||
#define OT_LAYOUT_COMMON_RANGERECORD_HH
|
||||
|
||||
namespace OT {
|
||||
namespace Layout {
|
||||
namespace Common {
|
||||
|
||||
template <typename Types>
|
||||
struct RangeRecord
|
||||
{
|
||||
typename Types::HBGlyphID first; /* First GlyphID in the range */
|
||||
typename Types::HBGlyphID last; /* Last GlyphID in the range */
|
||||
HBUINT16 value; /* Value */
|
||||
|
||||
DEFINE_SIZE_STATIC (2 + 2 * Types::size);
|
||||
|
||||
bool sanitize (hb_sanitize_context_t *c) const
|
||||
{
|
||||
TRACE_SANITIZE (this);
|
||||
return_trace (c->check_struct (this));
|
||||
}
|
||||
|
||||
int cmp (hb_codepoint_t g) const
|
||||
{ return g < first ? -1 : g <= last ? 0 : +1; }
|
||||
|
||||
HB_INTERNAL static int cmp_range (const void *pa, const void *pb) {
|
||||
const RangeRecord *a = (const RangeRecord *) pa;
|
||||
const RangeRecord *b = (const RangeRecord *) pb;
|
||||
if (a->first < b->first) return -1;
|
||||
if (a->first > b->first) return +1;
|
||||
if (a->last < b->last) return -1;
|
||||
if (a->last > b->last) return +1;
|
||||
if (a->value < b->value) return -1;
|
||||
if (a->value > b->value) return +1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned get_population () const
|
||||
{
|
||||
if (unlikely (last < first)) return 0;
|
||||
return (last - first + 1);
|
||||
}
|
||||
|
||||
bool intersects (const hb_set_t &glyphs) const
|
||||
{ return glyphs.intersects (first, last); }
|
||||
|
||||
template <typename set_t>
|
||||
bool collect_coverage (set_t *glyphs) const
|
||||
{ return glyphs->add_range (first, last); }
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(garretrieger): This was previously implemented using
|
||||
// DECLARE_NULL_NAMESPACE_BYTES_TEMPLATE1 (OT, RangeRecord, 9);
|
||||
// but that only works when there is only a single namespace level.
|
||||
// The macro should probably be fixed so it can work in this situation.
|
||||
extern HB_INTERNAL const unsigned char _hb_Null_OT_RangeRecord[9];
|
||||
template <typename Spec>
|
||||
struct Null<OT::Layout::Common::RangeRecord<Spec>> {
|
||||
static OT::Layout::Common::RangeRecord<Spec> const & get_null () {
|
||||
return *reinterpret_cast<const OT::Layout::Common::RangeRecord<Spec> *> (_hb_Null_OT_RangeRecord);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#endif // #ifndef OT_LAYOUT_COMMON_RANGERECORD_HH
|
||||
+1060
File diff suppressed because it is too large
Load Diff
+84
@@ -0,0 +1,84 @@
|
||||
#ifndef OT_LAYOUT_GPOS_ANCHOR_HH
|
||||
#define OT_LAYOUT_GPOS_ANCHOR_HH
|
||||
|
||||
#include "AnchorFormat1.hh"
|
||||
#include "AnchorFormat2.hh"
|
||||
#include "AnchorFormat3.hh"
|
||||
|
||||
namespace OT {
|
||||
namespace Layout {
|
||||
namespace GPOS_impl {
|
||||
|
||||
struct Anchor
|
||||
{
|
||||
protected:
|
||||
union {
|
||||
struct { HBUINT16 v; } format; /* Format identifier */
|
||||
AnchorFormat1 format1;
|
||||
AnchorFormat2 format2;
|
||||
AnchorFormat3 format3;
|
||||
} u;
|
||||
public:
|
||||
DEFINE_SIZE_UNION (2, format.v);
|
||||
|
||||
bool sanitize (hb_sanitize_context_t *c) const
|
||||
{
|
||||
TRACE_SANITIZE (this);
|
||||
if (!u.format.v.sanitize (c)) return_trace (false);
|
||||
hb_barrier ();
|
||||
switch (u.format.v) {
|
||||
case 1: return_trace (u.format1.sanitize (c));
|
||||
case 2: return_trace (u.format2.sanitize (c));
|
||||
case 3: return_trace (u.format3.sanitize (c));
|
||||
default:return_trace (true);
|
||||
}
|
||||
}
|
||||
|
||||
void get_anchor (hb_ot_apply_context_t *c, hb_codepoint_t glyph_id,
|
||||
float *x, float *y) const
|
||||
{
|
||||
*x = *y = 0;
|
||||
switch (u.format.v) {
|
||||
case 1: u.format1.get_anchor (c, glyph_id, x, y); return;
|
||||
case 2: u.format2.get_anchor (c, glyph_id, x, y); return;
|
||||
case 3: u.format3.get_anchor (c, glyph_id, x, y); return;
|
||||
default: return;
|
||||
}
|
||||
}
|
||||
|
||||
bool subset (hb_subset_context_t *c) const
|
||||
{
|
||||
TRACE_SUBSET (this);
|
||||
switch (u.format.v) {
|
||||
case 1: return_trace (bool (reinterpret_cast<Anchor *> (u.format1.copy (c->serializer))));
|
||||
case 2:
|
||||
if (c->plan->flags & HB_SUBSET_FLAGS_NO_HINTING)
|
||||
{
|
||||
// AnchorFormat 2 just containins extra hinting information, so
|
||||
// if hints are being dropped convert to format 1.
|
||||
return_trace (bool (reinterpret_cast<Anchor *> (u.format1.copy (c->serializer))));
|
||||
}
|
||||
return_trace (bool (reinterpret_cast<Anchor *> (u.format2.copy (c->serializer))));
|
||||
case 3: return_trace (u.format3.subset (c));
|
||||
default:return_trace (false);
|
||||
}
|
||||
}
|
||||
|
||||
void collect_variation_indices (hb_collect_variation_indices_context_t *c) const
|
||||
{
|
||||
switch (u.format.v) {
|
||||
case 1: case 2:
|
||||
return;
|
||||
case 3:
|
||||
u.format3.collect_variation_indices (c);
|
||||
return;
|
||||
default: return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // OT_LAYOUT_GPOS_ANCHOR_HH
|
||||
@@ -0,0 +1,46 @@
|
||||
#ifndef OT_LAYOUT_GPOS_ANCHORFORMAT1_HH
|
||||
#define OT_LAYOUT_GPOS_ANCHORFORMAT1_HH
|
||||
|
||||
namespace OT {
|
||||
namespace Layout {
|
||||
namespace GPOS_impl {
|
||||
|
||||
struct AnchorFormat1
|
||||
{
|
||||
protected:
|
||||
HBUINT16 format; /* Format identifier--format = 1 */
|
||||
FWORD xCoordinate; /* Horizontal value--in design units */
|
||||
FWORD yCoordinate; /* Vertical value--in design units */
|
||||
public:
|
||||
DEFINE_SIZE_STATIC (6);
|
||||
|
||||
bool sanitize (hb_sanitize_context_t *c) const
|
||||
{
|
||||
TRACE_SANITIZE (this);
|
||||
return_trace (c->check_struct (this));
|
||||
}
|
||||
|
||||
void get_anchor (hb_ot_apply_context_t *c, hb_codepoint_t glyph_id HB_UNUSED,
|
||||
float *x, float *y) const
|
||||
{
|
||||
hb_font_t *font = c->font;
|
||||
*x = font->em_fscale_x (xCoordinate);
|
||||
*y = font->em_fscale_y (yCoordinate);
|
||||
}
|
||||
|
||||
AnchorFormat1* copy (hb_serialize_context_t *c) const
|
||||
{
|
||||
TRACE_SERIALIZE (this);
|
||||
AnchorFormat1* out = c->embed<AnchorFormat1> (this);
|
||||
if (!out) return_trace (out);
|
||||
out->format = 1;
|
||||
return_trace (out);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // OT_LAYOUT_GPOS_ANCHORFORMAT1_HH
|
||||
@@ -0,0 +1,58 @@
|
||||
#ifndef OT_LAYOUT_GPOS_ANCHORFORMAT2_HH
|
||||
#define OT_LAYOUT_GPOS_ANCHORFORMAT2_HH
|
||||
|
||||
namespace OT {
|
||||
namespace Layout {
|
||||
namespace GPOS_impl {
|
||||
|
||||
struct AnchorFormat2
|
||||
{
|
||||
|
||||
protected:
|
||||
HBUINT16 format; /* Format identifier--format = 2 */
|
||||
FWORD xCoordinate; /* Horizontal value--in design units */
|
||||
FWORD yCoordinate; /* Vertical value--in design units */
|
||||
HBUINT16 anchorPoint; /* Index to glyph contour point */
|
||||
public:
|
||||
DEFINE_SIZE_STATIC (8);
|
||||
|
||||
bool sanitize (hb_sanitize_context_t *c) const
|
||||
{
|
||||
TRACE_SANITIZE (this);
|
||||
return_trace (c->check_struct (this));
|
||||
}
|
||||
|
||||
void get_anchor (hb_ot_apply_context_t *c, hb_codepoint_t glyph_id,
|
||||
float *x, float *y) const
|
||||
{
|
||||
hb_font_t *font = c->font;
|
||||
|
||||
#ifdef HB_NO_HINTING
|
||||
*x = font->em_fscale_x (xCoordinate);
|
||||
*y = font->em_fscale_y (yCoordinate);
|
||||
return;
|
||||
#endif
|
||||
|
||||
unsigned int x_ppem = font->x_ppem;
|
||||
unsigned int y_ppem = font->y_ppem;
|
||||
hb_position_t cx = 0, cy = 0;
|
||||
bool ret;
|
||||
|
||||
ret = (x_ppem || y_ppem) &&
|
||||
font->get_glyph_contour_point_for_origin (glyph_id, anchorPoint, HB_DIRECTION_LTR, &cx, &cy);
|
||||
*x = ret && x_ppem ? cx : font->em_fscale_x (xCoordinate);
|
||||
*y = ret && y_ppem ? cy : font->em_fscale_y (yCoordinate);
|
||||
}
|
||||
|
||||
AnchorFormat2* copy (hb_serialize_context_t *c) const
|
||||
{
|
||||
TRACE_SERIALIZE (this);
|
||||
return_trace (c->embed<AnchorFormat2> (this));
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // OT_LAYOUT_GPOS_ANCHORFORMAT2_HH
|
||||
@@ -0,0 +1,123 @@
|
||||
#ifndef OT_LAYOUT_GPOS_ANCHORFORMAT3_HH
|
||||
#define OT_LAYOUT_GPOS_ANCHORFORMAT3_HH
|
||||
|
||||
namespace OT {
|
||||
namespace Layout {
|
||||
namespace GPOS_impl {
|
||||
|
||||
struct AnchorFormat3
|
||||
{
|
||||
protected:
|
||||
HBUINT16 format; /* Format identifier--format = 3 */
|
||||
FWORD xCoordinate; /* Horizontal value--in design units */
|
||||
FWORD yCoordinate; /* Vertical value--in design units */
|
||||
Offset16To<Device>
|
||||
xDeviceTable; /* Offset to Device table for X
|
||||
* coordinate-- from beginning of
|
||||
* Anchor table (may be NULL) */
|
||||
Offset16To<Device>
|
||||
yDeviceTable; /* Offset to Device table for Y
|
||||
* coordinate-- from beginning of
|
||||
* Anchor table (may be NULL) */
|
||||
public:
|
||||
DEFINE_SIZE_STATIC (10);
|
||||
|
||||
bool sanitize (hb_sanitize_context_t *c) const
|
||||
{
|
||||
TRACE_SANITIZE (this);
|
||||
if (unlikely (!c->check_struct (this))) return_trace (false);
|
||||
|
||||
return_trace (xDeviceTable.sanitize (c, this) && yDeviceTable.sanitize (c, this));
|
||||
}
|
||||
|
||||
void get_anchor (hb_ot_apply_context_t *c, hb_codepoint_t glyph_id HB_UNUSED,
|
||||
float *x, float *y) const
|
||||
{
|
||||
hb_font_t *font = c->font;
|
||||
*x = font->em_fscale_x (xCoordinate);
|
||||
*y = font->em_fscale_y (yCoordinate);
|
||||
|
||||
if ((font->x_ppem || font->has_nonzero_coords) && xDeviceTable.sanitize (&c->sanitizer, this))
|
||||
{
|
||||
hb_barrier ();
|
||||
*x += (this+xDeviceTable).get_x_delta (font, c->var_store, c->var_store_cache);
|
||||
}
|
||||
if ((font->y_ppem || font->has_nonzero_coords) && yDeviceTable.sanitize (&c->sanitizer, this))
|
||||
{
|
||||
hb_barrier ();
|
||||
*y += (this+yDeviceTable).get_y_delta (font, c->var_store, c->var_store_cache);
|
||||
}
|
||||
}
|
||||
|
||||
bool subset (hb_subset_context_t *c) const
|
||||
{
|
||||
TRACE_SUBSET (this);
|
||||
auto *out = c->serializer->start_embed (*this);
|
||||
if (unlikely (!c->serializer->embed (format))) return_trace (false);
|
||||
if (unlikely (!c->serializer->embed (xCoordinate))) return_trace (false);
|
||||
if (unlikely (!c->serializer->embed (yCoordinate))) return_trace (false);
|
||||
|
||||
unsigned x_varidx = xDeviceTable ? (this+xDeviceTable).get_variation_index () : HB_OT_LAYOUT_NO_VARIATIONS_INDEX;
|
||||
if (x_varidx != HB_OT_LAYOUT_NO_VARIATIONS_INDEX)
|
||||
{
|
||||
hb_pair_t<unsigned, int> *new_varidx_delta;
|
||||
if (!c->plan->layout_variation_idx_delta_map.has (x_varidx, &new_varidx_delta))
|
||||
return_trace (false);
|
||||
|
||||
x_varidx = hb_first (*new_varidx_delta);
|
||||
int delta = hb_second (*new_varidx_delta);
|
||||
if (delta != 0)
|
||||
{
|
||||
if (!c->serializer->check_assign (out->xCoordinate, xCoordinate + delta,
|
||||
HB_SERIALIZE_ERROR_INT_OVERFLOW))
|
||||
return_trace (false);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned y_varidx = yDeviceTable ? (this+yDeviceTable).get_variation_index () : HB_OT_LAYOUT_NO_VARIATIONS_INDEX;
|
||||
if (y_varidx != HB_OT_LAYOUT_NO_VARIATIONS_INDEX)
|
||||
{
|
||||
hb_pair_t<unsigned, int> *new_varidx_delta;
|
||||
if (!c->plan->layout_variation_idx_delta_map.has (y_varidx, &new_varidx_delta))
|
||||
return_trace (false);
|
||||
|
||||
y_varidx = hb_first (*new_varidx_delta);
|
||||
int delta = hb_second (*new_varidx_delta);
|
||||
if (delta != 0)
|
||||
{
|
||||
if (!c->serializer->check_assign (out->yCoordinate, yCoordinate + delta,
|
||||
HB_SERIALIZE_ERROR_INT_OVERFLOW))
|
||||
return_trace (false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool no_downgrade = (!xDeviceTable.is_null () && !(this+xDeviceTable).is_variation_device ()) ||
|
||||
x_varidx != HB_OT_LAYOUT_NO_VARIATIONS_INDEX ||
|
||||
y_varidx != HB_OT_LAYOUT_NO_VARIATIONS_INDEX ||
|
||||
(!yDeviceTable.is_null () && !(this+yDeviceTable).is_variation_device ());
|
||||
|
||||
if (!no_downgrade)
|
||||
return_trace (c->serializer->check_assign (out->format, 1, HB_SERIALIZE_ERROR_INT_OVERFLOW));
|
||||
|
||||
if (!c->serializer->embed (xDeviceTable)) return_trace (false);
|
||||
if (!c->serializer->embed (yDeviceTable)) return_trace (false);
|
||||
|
||||
out->xDeviceTable.serialize_copy (c->serializer, xDeviceTable, this, 0, hb_serialize_context_t::Head, &c->plan->layout_variation_idx_delta_map);
|
||||
out->yDeviceTable.serialize_copy (c->serializer, yDeviceTable, this, 0, hb_serialize_context_t::Head, &c->plan->layout_variation_idx_delta_map);
|
||||
return_trace (out);
|
||||
}
|
||||
|
||||
void collect_variation_indices (hb_collect_variation_indices_context_t *c) const
|
||||
{
|
||||
(this+xDeviceTable).collect_variation_indices (c);
|
||||
(this+yDeviceTable).collect_variation_indices (c);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // OT_LAYOUT_GPOS_ANCHORFORMAT3_HH
|
||||
@@ -0,0 +1,94 @@
|
||||
#ifndef OT_LAYOUT_GPOS_ANCHORMATRIX_HH
|
||||
#define OT_LAYOUT_GPOS_ANCHORMATRIX_HH
|
||||
|
||||
namespace OT {
|
||||
namespace Layout {
|
||||
namespace GPOS_impl {
|
||||
|
||||
struct AnchorMatrix
|
||||
{
|
||||
HBUINT16 rows; /* Number of rows */
|
||||
UnsizedArrayOf<Offset16To<Anchor, AnchorMatrix>>
|
||||
matrixZ; /* Matrix of offsets to Anchor tables--
|
||||
* from beginning of AnchorMatrix table */
|
||||
public:
|
||||
DEFINE_SIZE_ARRAY (2, matrixZ);
|
||||
|
||||
bool sanitize (hb_sanitize_context_t *c, unsigned int cols) const
|
||||
{
|
||||
TRACE_SANITIZE (this);
|
||||
if (!c->check_struct (this)) return_trace (false);
|
||||
hb_barrier ();
|
||||
if (unlikely (hb_unsigned_mul_overflows (rows, cols))) return_trace (false);
|
||||
unsigned int count = rows * cols;
|
||||
if (!c->check_array (matrixZ.arrayZ, count)) return_trace (false);
|
||||
|
||||
if (c->lazy_some_gpos)
|
||||
return_trace (true);
|
||||
|
||||
hb_barrier ();
|
||||
for (unsigned int i = 0; i < count; i++)
|
||||
if (!matrixZ[i].sanitize (c, this)) return_trace (false);
|
||||
return_trace (true);
|
||||
}
|
||||
|
||||
const Anchor& get_anchor (hb_ot_apply_context_t *c,
|
||||
unsigned int row, unsigned int col,
|
||||
unsigned int cols, bool *found) const
|
||||
{
|
||||
*found = false;
|
||||
if (unlikely (row >= rows || col >= cols)) return Null (Anchor);
|
||||
auto &offset = matrixZ[row * cols + col];
|
||||
if (unlikely (!offset.sanitize (&c->sanitizer, this))) return Null (Anchor);
|
||||
hb_barrier ();
|
||||
*found = !offset.is_null ();
|
||||
return this+offset;
|
||||
}
|
||||
|
||||
template <typename Iterator,
|
||||
hb_requires (hb_is_iterator (Iterator))>
|
||||
void collect_variation_indices (hb_collect_variation_indices_context_t *c,
|
||||
Iterator index_iter) const
|
||||
{
|
||||
for (unsigned i : index_iter)
|
||||
(this+matrixZ[i]).collect_variation_indices (c);
|
||||
}
|
||||
|
||||
template <typename Iterator,
|
||||
hb_requires (hb_is_iterator (Iterator))>
|
||||
bool subset (hb_subset_context_t *c,
|
||||
unsigned num_rows,
|
||||
Iterator index_iter) const
|
||||
{
|
||||
TRACE_SUBSET (this);
|
||||
|
||||
auto *out = c->serializer->start_embed (this);
|
||||
|
||||
if (!index_iter) return_trace (false);
|
||||
if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
|
||||
|
||||
out->rows = num_rows;
|
||||
for (const unsigned i : index_iter)
|
||||
{
|
||||
auto *offset = c->serializer->embed (matrixZ[i]);
|
||||
if (!offset) return_trace (false);
|
||||
offset->serialize_subset (c, matrixZ[i], this);
|
||||
}
|
||||
|
||||
return_trace (true);
|
||||
}
|
||||
|
||||
bool offset_is_null (unsigned row, unsigned col, unsigned num_cols) const
|
||||
{
|
||||
if (unlikely (row >= rows || col >= num_cols)) return true;
|
||||
auto &offset = matrixZ[row * num_cols + col];
|
||||
return offset.is_null ();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* OT_LAYOUT_GPOS_ANCHORMATRIX_HH */
|
||||
@@ -0,0 +1,14 @@
|
||||
#ifndef OT_LAYOUT_GPOS_CHAINCONTEXTPOS_HH
|
||||
#define OT_LAYOUT_GPOS_CHAINCONTEXTPOS_HH
|
||||
|
||||
namespace OT {
|
||||
namespace Layout {
|
||||
namespace GPOS_impl {
|
||||
|
||||
struct ChainContextPos : ChainContext {};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* OT_LAYOUT_GPOS_CHAINCONTEXTPOS_HH */
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
#ifndef OT_LAYOUT_GPOS_COMMON_HH
|
||||
#define OT_LAYOUT_GPOS_COMMON_HH
|
||||
|
||||
namespace OT {
|
||||
namespace Layout {
|
||||
namespace GPOS_impl {
|
||||
|
||||
enum attach_type_t {
|
||||
ATTACH_TYPE_NONE = 0X00,
|
||||
|
||||
/* Each attachment should be either a mark or a cursive; can't be both. */
|
||||
ATTACH_TYPE_MARK = 0X01,
|
||||
ATTACH_TYPE_CURSIVE = 0X02,
|
||||
};
|
||||
|
||||
/* buffer **position** var allocations */
|
||||
#define attach_chain() var.i16[0] /* glyph to which this attaches to, relative to current glyphs; negative for going back, positive for forward. */
|
||||
#define attach_type() var.u8[2] /* attachment type */
|
||||
/* Note! if attach_chain() is zero, the value of attach_type() is irrelevant. */
|
||||
|
||||
template<typename Iterator, typename SrcLookup>
|
||||
static void SinglePos_serialize (hb_serialize_context_t *c,
|
||||
const SrcLookup *src,
|
||||
Iterator it,
|
||||
const hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *layout_variation_idx_delta_map,
|
||||
unsigned new_format);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // OT_LAYOUT_GPOS_COMMON_HH
|
||||
@@ -0,0 +1,14 @@
|
||||
#ifndef OT_LAYOUT_GPOS_CONTEXTPOS_HH
|
||||
#define OT_LAYOUT_GPOS_CONTEXTPOS_HH
|
||||
|
||||
namespace OT {
|
||||
namespace Layout {
|
||||
namespace GPOS_impl {
|
||||
|
||||
struct ContextPos : Context {};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* OT_LAYOUT_GPOS_CONTEXTPOS_HH */
|
||||
@@ -0,0 +1,35 @@
|
||||
#ifndef OT_LAYOUT_GPOS_CURSIVEPOS_HH
|
||||
#define OT_LAYOUT_GPOS_CURSIVEPOS_HH
|
||||
|
||||
#include "CursivePosFormat1.hh"
|
||||
|
||||
namespace OT {
|
||||
namespace Layout {
|
||||
namespace GPOS_impl {
|
||||
|
||||
struct CursivePos
|
||||
{
|
||||
protected:
|
||||
union {
|
||||
struct { HBUINT16 v; } format; /* Format identifier */
|
||||
CursivePosFormat1 format1;
|
||||
} u;
|
||||
|
||||
public:
|
||||
template <typename context_t, typename ...Ts>
|
||||
typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
|
||||
{
|
||||
if (unlikely (!c->may_dispatch (this, &u.format.v))) return c->no_dispatch_return_value ();
|
||||
TRACE_DISPATCH (this, u.format.v);
|
||||
switch (u.format.v) {
|
||||
case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...));
|
||||
default:return_trace (c->default_return_value ());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* OT_LAYOUT_GPOS_CURSIVEPOS_HH */
|
||||
@@ -0,0 +1,332 @@
|
||||
#ifndef OT_LAYOUT_GPOS_CURSIVEPOSFORMAT1_HH
|
||||
#define OT_LAYOUT_GPOS_CURSIVEPOSFORMAT1_HH
|
||||
|
||||
#include "Anchor.hh"
|
||||
|
||||
namespace OT {
|
||||
namespace Layout {
|
||||
namespace GPOS_impl {
|
||||
|
||||
struct EntryExitRecord
|
||||
{
|
||||
friend struct CursivePosFormat1;
|
||||
|
||||
bool sanitize (hb_sanitize_context_t *c, const struct CursivePosFormat1 *base) const
|
||||
{
|
||||
TRACE_SANITIZE (this);
|
||||
return_trace (entryAnchor.sanitize (c, base) && exitAnchor.sanitize (c, base));
|
||||
}
|
||||
|
||||
void collect_variation_indices (hb_collect_variation_indices_context_t *c,
|
||||
const struct CursivePosFormat1 *src_base) const
|
||||
{
|
||||
(src_base+entryAnchor).collect_variation_indices (c);
|
||||
(src_base+exitAnchor).collect_variation_indices (c);
|
||||
}
|
||||
|
||||
bool subset (hb_subset_context_t *c,
|
||||
const struct CursivePosFormat1 *src_base) const
|
||||
{
|
||||
TRACE_SERIALIZE (this);
|
||||
auto *out = c->serializer->embed (this);
|
||||
if (unlikely (!out)) return_trace (false);
|
||||
|
||||
bool ret = false;
|
||||
ret |= out->entryAnchor.serialize_subset (c, entryAnchor, src_base);
|
||||
ret |= out->exitAnchor.serialize_subset (c, exitAnchor, src_base);
|
||||
return_trace (ret);
|
||||
}
|
||||
|
||||
protected:
|
||||
Offset16To<Anchor, struct CursivePosFormat1>
|
||||
entryAnchor; /* Offset to EntryAnchor table--from
|
||||
* beginning of CursivePos
|
||||
* subtable--may be NULL */
|
||||
Offset16To<Anchor, struct CursivePosFormat1>
|
||||
exitAnchor; /* Offset to ExitAnchor table--from
|
||||
* beginning of CursivePos
|
||||
* subtable--may be NULL */
|
||||
public:
|
||||
DEFINE_SIZE_STATIC (4);
|
||||
};
|
||||
|
||||
static inline void
|
||||
reverse_cursive_minor_offset (hb_glyph_position_t *pos,
|
||||
unsigned int len,
|
||||
unsigned int i,
|
||||
hb_direction_t direction,
|
||||
unsigned int new_parent)
|
||||
{
|
||||
int chain = pos[i].attach_chain(), type = pos[i].attach_type();
|
||||
if (likely (!chain || 0 == (type & ATTACH_TYPE_CURSIVE)))
|
||||
return;
|
||||
|
||||
pos[i].attach_chain() = 0;
|
||||
|
||||
unsigned int j = (int) i + chain;
|
||||
|
||||
if (unlikely (j >= len))
|
||||
return;
|
||||
|
||||
/* Stop if we see new parent in the chain. */
|
||||
if (j == new_parent)
|
||||
return;
|
||||
|
||||
int16_t reversed_chain = -chain;
|
||||
/* The old edge was cleared above; if the reversed distance truncates,
|
||||
* keep it detached instead of storing a poisoned chain.
|
||||
*/
|
||||
if (unlikely (reversed_chain != -chain))
|
||||
return;
|
||||
|
||||
reverse_cursive_minor_offset (pos, len, j, direction, new_parent);
|
||||
|
||||
if (HB_DIRECTION_IS_HORIZONTAL (direction))
|
||||
pos[j].y_offset = -pos[i].y_offset;
|
||||
else
|
||||
pos[j].x_offset = -pos[i].x_offset;
|
||||
|
||||
pos[j].attach_chain() = reversed_chain;
|
||||
pos[j].attach_type() = type;
|
||||
}
|
||||
|
||||
|
||||
struct CursivePosFormat1
|
||||
{
|
||||
protected:
|
||||
HBUINT16 format; /* Format identifier--format = 1 */
|
||||
Offset16To<Coverage>
|
||||
coverage; /* Offset to Coverage table--from
|
||||
* beginning of subtable */
|
||||
Array16Of<EntryExitRecord>
|
||||
entryExitRecord; /* Array of EntryExit records--in
|
||||
* Coverage Index order */
|
||||
public:
|
||||
DEFINE_SIZE_ARRAY (6, entryExitRecord);
|
||||
|
||||
bool sanitize (hb_sanitize_context_t *c) const
|
||||
{
|
||||
TRACE_SANITIZE (this);
|
||||
if (unlikely (!coverage.sanitize (c, this)))
|
||||
return_trace (false);
|
||||
|
||||
if (c->lazy_some_gpos)
|
||||
return_trace (entryExitRecord.sanitize_shallow (c));
|
||||
else
|
||||
return_trace (entryExitRecord.sanitize (c, this));
|
||||
}
|
||||
|
||||
bool intersects (const hb_set_t *glyphs) const
|
||||
{ return (this+coverage).intersects (glyphs); }
|
||||
|
||||
void closure_lookups (hb_closure_lookups_context_t *c) const {}
|
||||
|
||||
void collect_variation_indices (hb_collect_variation_indices_context_t *c) const
|
||||
{
|
||||
+ hb_zip (this+coverage, entryExitRecord)
|
||||
| hb_filter (c->glyph_set, hb_first)
|
||||
| hb_map (hb_second)
|
||||
| hb_apply ([&] (const EntryExitRecord& record) { record.collect_variation_indices (c, this); })
|
||||
;
|
||||
}
|
||||
|
||||
void collect_glyphs (hb_collect_glyphs_context_t *c) const
|
||||
{ if (unlikely (!(this+coverage).collect_coverage (c->input))) return; }
|
||||
|
||||
const Coverage &get_coverage () const { return this+coverage; }
|
||||
|
||||
bool apply (hb_ot_apply_context_t *c) const
|
||||
{
|
||||
TRACE_APPLY (this);
|
||||
hb_buffer_t *buffer = c->buffer;
|
||||
|
||||
const EntryExitRecord &this_record = entryExitRecord[(this+coverage).get_coverage (buffer->cur().codepoint)];
|
||||
if (!this_record.entryAnchor ||
|
||||
unlikely (!this_record.entryAnchor.sanitize (&c->sanitizer, this))) return_trace (false);
|
||||
hb_barrier ();
|
||||
|
||||
auto &skippy_iter = c->iter_input;
|
||||
skippy_iter.reset_fast (buffer->idx);
|
||||
unsigned unsafe_from;
|
||||
if (unlikely (!skippy_iter.prev (&unsafe_from)))
|
||||
{
|
||||
buffer->unsafe_to_concat_from_outbuffer (unsafe_from, buffer->idx + 1);
|
||||
return_trace (false);
|
||||
}
|
||||
|
||||
const EntryExitRecord &prev_record = entryExitRecord[(this+coverage).get_coverage (buffer->info[skippy_iter.idx].codepoint)];
|
||||
if (!prev_record.exitAnchor ||
|
||||
unlikely (!prev_record.exitAnchor.sanitize (&c->sanitizer, this)))
|
||||
{
|
||||
buffer->unsafe_to_concat_from_outbuffer (skippy_iter.idx, buffer->idx + 1);
|
||||
return_trace (false);
|
||||
}
|
||||
hb_barrier ();
|
||||
|
||||
unsigned int i = skippy_iter.idx;
|
||||
unsigned int j = buffer->idx;
|
||||
|
||||
if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
|
||||
{
|
||||
c->buffer->message (c->font,
|
||||
"cursive attaching glyph at %u to glyph at %u",
|
||||
i, j);
|
||||
}
|
||||
|
||||
buffer->unsafe_to_break (i, j + 1);
|
||||
float entry_x, entry_y, exit_x, exit_y;
|
||||
(this+prev_record.exitAnchor).get_anchor (c, buffer->info[i].codepoint, &exit_x, &exit_y);
|
||||
(this+this_record.entryAnchor).get_anchor (c, buffer->info[j].codepoint, &entry_x, &entry_y);
|
||||
|
||||
hb_glyph_position_t *pos = buffer->pos;
|
||||
|
||||
hb_position_t d;
|
||||
/* Main-direction adjustment */
|
||||
switch (c->direction) {
|
||||
case HB_DIRECTION_LTR:
|
||||
pos[i].x_advance = roundf (exit_x) + pos[i].x_offset;
|
||||
|
||||
d = roundf (entry_x) + pos[j].x_offset;
|
||||
pos[j].x_advance -= d;
|
||||
pos[j].x_offset -= d;
|
||||
break;
|
||||
case HB_DIRECTION_RTL:
|
||||
d = roundf (exit_x) + pos[i].x_offset;
|
||||
pos[i].x_advance -= d;
|
||||
pos[i].x_offset -= d;
|
||||
|
||||
pos[j].x_advance = roundf (entry_x) + pos[j].x_offset;
|
||||
break;
|
||||
case HB_DIRECTION_TTB:
|
||||
pos[i].y_advance = roundf (exit_y) + pos[i].y_offset;
|
||||
|
||||
d = roundf (entry_y) + pos[j].y_offset;
|
||||
pos[j].y_advance -= d;
|
||||
pos[j].y_offset -= d;
|
||||
break;
|
||||
case HB_DIRECTION_BTT:
|
||||
d = roundf (exit_y) + pos[i].y_offset;
|
||||
pos[i].y_advance -= d;
|
||||
pos[i].y_offset -= d;
|
||||
|
||||
pos[j].y_advance = roundf (entry_y);
|
||||
break;
|
||||
case HB_DIRECTION_INVALID:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* Cross-direction adjustment */
|
||||
|
||||
/* We attach child to parent (think graph theory and rooted trees whereas
|
||||
* the root stays on baseline and each node aligns itself against its
|
||||
* parent.
|
||||
*
|
||||
* Optimize things for the case of RightToLeft, as that's most common in
|
||||
* Arabic. */
|
||||
unsigned int child = i;
|
||||
unsigned int parent = j;
|
||||
hb_position_t x_offset = roundf (entry_x - exit_x);
|
||||
hb_position_t y_offset = roundf (entry_y - exit_y);
|
||||
if (!(c->lookup_props & LookupFlag::RightToLeft))
|
||||
{
|
||||
unsigned int k = child;
|
||||
child = parent;
|
||||
parent = k;
|
||||
x_offset = -x_offset;
|
||||
y_offset = -y_offset;
|
||||
}
|
||||
|
||||
/* If child was already connected to someone else, walk through its old
|
||||
* chain and reverse the link direction, such that the whole tree of its
|
||||
* previous connection now attaches to new parent. Watch out for case
|
||||
* where new parent is on the path from old chain...
|
||||
*/
|
||||
reverse_cursive_minor_offset (pos, buffer->len, child, c->direction, parent);
|
||||
|
||||
pos[child].attach_chain() = (int) parent - (int) child;
|
||||
if (pos[child].attach_chain() != (int) parent - (int) child)
|
||||
{
|
||||
pos[child].attach_chain() = 0;
|
||||
goto overflow;
|
||||
}
|
||||
pos[child].attach_type() = ATTACH_TYPE_CURSIVE;
|
||||
buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT;
|
||||
if (likely (HB_DIRECTION_IS_HORIZONTAL (c->direction)))
|
||||
pos[child].y_offset = y_offset;
|
||||
else
|
||||
pos[child].x_offset = x_offset;
|
||||
|
||||
/* If parent was attached to child, separate them.
|
||||
* https://github.com/harfbuzz/harfbuzz/issues/2469
|
||||
*/
|
||||
if (unlikely (pos[parent].attach_chain() == -pos[child].attach_chain()))
|
||||
{
|
||||
pos[parent].attach_chain() = 0;
|
||||
if (likely (HB_DIRECTION_IS_HORIZONTAL (c->direction)))
|
||||
pos[parent].y_offset = 0;
|
||||
else
|
||||
pos[parent].x_offset = 0;
|
||||
}
|
||||
|
||||
if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
|
||||
{
|
||||
c->buffer->message (c->font,
|
||||
"cursive attached glyph at %u to glyph at %u",
|
||||
i, j);
|
||||
}
|
||||
|
||||
overflow:
|
||||
buffer->idx++;
|
||||
return_trace (true);
|
||||
}
|
||||
|
||||
template <typename Iterator,
|
||||
hb_requires (hb_is_iterator (Iterator))>
|
||||
void serialize (hb_subset_context_t *c,
|
||||
Iterator it,
|
||||
const struct CursivePosFormat1 *src_base)
|
||||
{
|
||||
if (unlikely (!c->serializer->extend_min ((*this)))) return;
|
||||
this->format = 1;
|
||||
this->entryExitRecord.len = it.len ();
|
||||
|
||||
for (const EntryExitRecord& entry_record : + it
|
||||
| hb_map (hb_second))
|
||||
entry_record.subset (c, src_base);
|
||||
|
||||
auto glyphs =
|
||||
+ it
|
||||
| hb_map_retains_sorting (hb_first)
|
||||
;
|
||||
|
||||
coverage.serialize_serialize (c->serializer, glyphs);
|
||||
}
|
||||
|
||||
bool subset (hb_subset_context_t *c) const
|
||||
{
|
||||
TRACE_SUBSET (this);
|
||||
const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
|
||||
const hb_map_t &glyph_map = *c->plan->glyph_map;
|
||||
|
||||
auto *out = c->serializer->start_embed (*this);
|
||||
|
||||
auto it =
|
||||
+ hb_zip (this+coverage, entryExitRecord)
|
||||
| hb_filter (glyphset, hb_first)
|
||||
| hb_map_retains_sorting ([&] (hb_pair_t<hb_codepoint_t, const EntryExitRecord&> p) -> hb_pair_t<hb_codepoint_t, const EntryExitRecord&>
|
||||
{ return hb_pair (glyph_map[p.first], p.second);})
|
||||
;
|
||||
|
||||
bool ret = bool (it);
|
||||
out->serialize (c, it, this);
|
||||
return_trace (ret);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* OT_LAYOUT_GPOS_CURSIVEPOSFORMAT1_HH */
|
||||
@@ -0,0 +1,17 @@
|
||||
#ifndef OT_LAYOUT_GPOS_EXTENSIONPOS_HH
|
||||
#define OT_LAYOUT_GPOS_EXTENSIONPOS_HH
|
||||
|
||||
namespace OT {
|
||||
namespace Layout {
|
||||
namespace GPOS_impl {
|
||||
|
||||
struct ExtensionPos : Extension<ExtensionPos>
|
||||
{
|
||||
typedef struct PosLookupSubTable SubTable;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* OT_LAYOUT_GPOS_EXTENSIONPOS_HH */
|
||||
+206
@@ -0,0 +1,206 @@
|
||||
#ifndef OT_LAYOUT_GPOS_GPOS_HH
|
||||
#define OT_LAYOUT_GPOS_GPOS_HH
|
||||
|
||||
#include "../../../hb-ot-layout-common.hh"
|
||||
#include "../../../hb-ot-layout-gsubgpos.hh"
|
||||
#include "Common.hh"
|
||||
#include "PosLookup.hh"
|
||||
|
||||
namespace OT {
|
||||
|
||||
using Layout::GPOS_impl::PosLookup;
|
||||
|
||||
namespace Layout {
|
||||
|
||||
static void
|
||||
propagate_attachment_offsets (hb_glyph_position_t *pos,
|
||||
unsigned int len,
|
||||
unsigned int i,
|
||||
hb_direction_t direction,
|
||||
unsigned nesting_level = HB_MAX_NESTING_LEVEL);
|
||||
|
||||
/*
|
||||
* GPOS -- Glyph Positioning
|
||||
* https://docs.microsoft.com/en-us/typography/opentype/spec/gpos
|
||||
*/
|
||||
|
||||
struct GPOS : GSUBGPOS
|
||||
{
|
||||
static constexpr hb_tag_t tableTag = HB_OT_TAG_GPOS;
|
||||
|
||||
using Lookup = PosLookup;
|
||||
|
||||
const PosLookup& get_lookup (unsigned int i) const
|
||||
{ return static_cast<const PosLookup &> (GSUBGPOS::get_lookup (i)); }
|
||||
|
||||
static inline void position_start (hb_font_t *font, hb_buffer_t *buffer);
|
||||
static inline void position_finish_advances (hb_font_t *font, hb_buffer_t *buffer);
|
||||
static inline void position_finish_offsets (hb_font_t *font, hb_buffer_t *buffer);
|
||||
|
||||
bool subset (hb_subset_context_t *c) const
|
||||
{
|
||||
hb_subset_layout_context_t l (c, tableTag);
|
||||
return GSUBGPOS::subset<PosLookup> (&l);
|
||||
}
|
||||
|
||||
bool sanitize (hb_sanitize_context_t *c) const
|
||||
{
|
||||
TRACE_SANITIZE (this);
|
||||
return_trace (GSUBGPOS::sanitize<PosLookup> (c));
|
||||
}
|
||||
|
||||
HB_INTERNAL bool is_blocklisted (hb_blob_t *blob,
|
||||
hb_face_t *face) const;
|
||||
|
||||
void collect_variation_indices (hb_collect_variation_indices_context_t *c) const
|
||||
{
|
||||
for (unsigned i = 0; i < GSUBGPOS::get_lookup_count (); i++)
|
||||
{
|
||||
if (!c->gpos_lookups->has (i)) continue;
|
||||
const PosLookup &l = get_lookup (i);
|
||||
l.dispatch (c);
|
||||
}
|
||||
}
|
||||
|
||||
void closure_lookups (hb_face_t *face,
|
||||
const hb_set_t *glyphs,
|
||||
hb_set_t *lookup_indexes /* IN/OUT */) const
|
||||
{ GSUBGPOS::closure_lookups<PosLookup> (face, glyphs, lookup_indexes); }
|
||||
|
||||
typedef GSUBGPOS::accelerator_t<GPOS> accelerator_t;
|
||||
};
|
||||
|
||||
|
||||
static void
|
||||
propagate_attachment_offsets (hb_glyph_position_t *pos,
|
||||
unsigned int len,
|
||||
unsigned int i,
|
||||
hb_direction_t direction,
|
||||
unsigned nesting_level)
|
||||
{
|
||||
/* Adjusts offsets of attached glyphs (both cursive and mark) to accumulate
|
||||
* offset of glyph they are attached to. */
|
||||
int chain = pos[i].attach_chain();
|
||||
int type = pos[i].attach_type();
|
||||
|
||||
pos[i].attach_chain() = 0;
|
||||
|
||||
unsigned int j = (int) i + chain;
|
||||
|
||||
if (unlikely (j >= len))
|
||||
return;
|
||||
|
||||
if (unlikely (!nesting_level))
|
||||
return;
|
||||
|
||||
if (pos[j].attach_chain())
|
||||
propagate_attachment_offsets (pos, len, j, direction, nesting_level - 1);
|
||||
|
||||
assert (!!(type & GPOS_impl::ATTACH_TYPE_MARK) ^ !!(type & GPOS_impl::ATTACH_TYPE_CURSIVE));
|
||||
|
||||
if (type & GPOS_impl::ATTACH_TYPE_CURSIVE)
|
||||
{
|
||||
if (HB_DIRECTION_IS_HORIZONTAL (direction))
|
||||
pos[i].y_offset += pos[j].y_offset;
|
||||
else
|
||||
pos[i].x_offset += pos[j].x_offset;
|
||||
}
|
||||
else /*if (type & GPOS_impl::ATTACH_TYPE_MARK)*/
|
||||
{
|
||||
pos[i].x_offset += pos[j].x_offset;
|
||||
pos[i].y_offset += pos[j].y_offset;
|
||||
|
||||
// i is the position of the mark; j is the base.
|
||||
if (j < i)
|
||||
{
|
||||
/* This is the common case: mark follows base.
|
||||
* And currently the only way in OpenType. */
|
||||
if (HB_DIRECTION_IS_FORWARD (direction))
|
||||
for (unsigned int k = j; k < i; k++) {
|
||||
pos[i].x_offset -= pos[k].x_advance;
|
||||
pos[i].y_offset -= pos[k].y_advance;
|
||||
}
|
||||
else
|
||||
for (unsigned int k = j + 1; k < i + 1; k++) {
|
||||
pos[i].x_offset += pos[k].x_advance;
|
||||
pos[i].y_offset += pos[k].y_advance;
|
||||
}
|
||||
}
|
||||
else // j > i
|
||||
{
|
||||
/* This can happen with `kerx`: a mark attaching
|
||||
* to a base after it in the logical order. */
|
||||
if (HB_DIRECTION_IS_FORWARD (direction))
|
||||
for (unsigned int k = i; k < j; k++) {
|
||||
pos[i].x_offset += pos[k].x_advance;
|
||||
pos[i].y_offset += pos[k].y_advance;
|
||||
}
|
||||
else
|
||||
for (unsigned int k = i + 1; k < j + 1; k++) {
|
||||
pos[i].x_offset -= pos[k].x_advance;
|
||||
pos[i].y_offset -= pos[k].y_advance;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
GPOS::position_start (hb_font_t *font HB_UNUSED, hb_buffer_t *buffer)
|
||||
{
|
||||
unsigned int count = buffer->len;
|
||||
for (unsigned int i = 0; i < count; i++)
|
||||
buffer->pos[i].attach_chain() = buffer->pos[i].attach_type() = 0;
|
||||
}
|
||||
|
||||
void
|
||||
GPOS::position_finish_advances (hb_font_t *font HB_UNUSED, hb_buffer_t *buffer HB_UNUSED)
|
||||
{
|
||||
//_hb_buffer_assert_gsubgpos_vars (buffer);
|
||||
}
|
||||
|
||||
void
|
||||
GPOS::position_finish_offsets (hb_font_t *font, hb_buffer_t *buffer)
|
||||
{
|
||||
_hb_buffer_assert_gsubgpos_vars (buffer);
|
||||
|
||||
unsigned int len;
|
||||
hb_glyph_position_t *pos = hb_buffer_get_glyph_positions (buffer, &len);
|
||||
hb_direction_t direction = buffer->props.direction;
|
||||
|
||||
/* Handle attachments */
|
||||
if (buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT)
|
||||
{
|
||||
auto *pos = buffer->pos;
|
||||
// https://github.com/harfbuzz/harfbuzz/issues/5514
|
||||
if (HB_DIRECTION_IS_FORWARD (direction))
|
||||
{
|
||||
for (unsigned i = 0; i < len; i++)
|
||||
if (pos[i].attach_chain())
|
||||
propagate_attachment_offsets (pos, len, i, direction);
|
||||
} else {
|
||||
for (unsigned i = len; i-- > 0; )
|
||||
if (pos[i].attach_chain())
|
||||
propagate_attachment_offsets (pos, len, i, direction);
|
||||
}
|
||||
}
|
||||
|
||||
if (unlikely (font->slant_xy) &&
|
||||
HB_DIRECTION_IS_HORIZONTAL (direction))
|
||||
{
|
||||
/* Slanting shaping results is only supported for horizontal text,
|
||||
* as it gets weird otherwise. */
|
||||
for (unsigned i = 0; i < len; i++)
|
||||
if (unlikely (pos[i].y_offset))
|
||||
pos[i].x_offset += roundf (font->slant_xy * pos[i].y_offset);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
struct GPOS_accelerator_t : Layout::GPOS::accelerator_t {
|
||||
GPOS_accelerator_t (hb_face_t *face) : Layout::GPOS::accelerator_t (face) {}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* OT_LAYOUT_GPOS_GPOS_HH */
|
||||
@@ -0,0 +1,68 @@
|
||||
#ifndef OT_LAYOUT_GPOS_LIGATUREARRAY_HH
|
||||
#define OT_LAYOUT_GPOS_LIGATUREARRAY_HH
|
||||
|
||||
namespace OT {
|
||||
namespace Layout {
|
||||
namespace GPOS_impl {
|
||||
|
||||
|
||||
typedef AnchorMatrix LigatureAttach; /* component-major--
|
||||
* in order of writing direction--,
|
||||
* mark-minor--
|
||||
* ordered by class--zero-based. */
|
||||
|
||||
/* Array of LigatureAttach tables ordered by LigatureCoverage Index */
|
||||
struct LigatureArray : List16OfOffset16To<LigatureAttach>
|
||||
{
|
||||
template <typename Iterator,
|
||||
hb_requires (hb_is_iterator (Iterator))>
|
||||
bool subset (hb_subset_context_t *c,
|
||||
Iterator coverage,
|
||||
unsigned class_count,
|
||||
const hb_map_t *klass_mapping,
|
||||
hb_sorted_vector_t<hb_codepoint_t> &new_coverage /* OUT */) const
|
||||
{
|
||||
TRACE_SUBSET (this);
|
||||
const hb_map_t &glyph_map = c->plan->glyph_map_gsub;
|
||||
|
||||
auto *out = c->serializer->start_embed (this);
|
||||
if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
|
||||
|
||||
bool ret = false;
|
||||
for (const auto _ : + hb_zip (coverage, *this)
|
||||
| hb_filter (glyph_map, hb_first))
|
||||
{
|
||||
const LigatureAttach& src = (this + _.second);
|
||||
bool non_empty = + hb_range (src.rows * class_count)
|
||||
| hb_filter ([=] (unsigned index) { return klass_mapping->has (index % class_count); })
|
||||
| hb_map ([&] (const unsigned index) { return !src.offset_is_null (index / class_count, index % class_count, class_count); })
|
||||
| hb_any;
|
||||
|
||||
if (!non_empty) continue;
|
||||
|
||||
auto *matrix = out->serialize_append (c->serializer);
|
||||
if (unlikely (!matrix)) return_trace (false);
|
||||
|
||||
auto indexes =
|
||||
+ hb_range (src.rows * class_count)
|
||||
| hb_filter ([=] (unsigned index) { return klass_mapping->has (index % class_count); })
|
||||
;
|
||||
ret |= matrix->serialize_subset (c,
|
||||
_.second,
|
||||
this,
|
||||
src.rows,
|
||||
indexes);
|
||||
|
||||
hb_codepoint_t new_gid = glyph_map.get (_.first);
|
||||
new_coverage.push (new_gid);
|
||||
}
|
||||
return_trace (ret);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* OT_LAYOUT_GPOS_LIGATUREARRAY_HH */
|
||||
+134
@@ -0,0 +1,134 @@
|
||||
#ifndef OT_LAYOUT_GPOS_MARKARRAY_HH
|
||||
#define OT_LAYOUT_GPOS_MARKARRAY_HH
|
||||
|
||||
#include "AnchorMatrix.hh"
|
||||
#include "MarkRecord.hh"
|
||||
|
||||
namespace OT {
|
||||
namespace Layout {
|
||||
namespace GPOS_impl {
|
||||
|
||||
struct MarkArray : Array16Of<MarkRecord> /* Array of MarkRecords--in Coverage order */
|
||||
{
|
||||
bool sanitize (hb_sanitize_context_t *c) const
|
||||
{
|
||||
TRACE_SANITIZE (this);
|
||||
return_trace (Array16Of<MarkRecord>::sanitize (c, this));
|
||||
}
|
||||
|
||||
bool apply (hb_ot_apply_context_t *c,
|
||||
unsigned int mark_index, unsigned int glyph_index,
|
||||
const AnchorMatrix &anchors, unsigned int class_count,
|
||||
unsigned int glyph_pos) const
|
||||
{
|
||||
TRACE_APPLY (this);
|
||||
hb_buffer_t *buffer = c->buffer;
|
||||
const MarkRecord &record = Array16Of<MarkRecord>::operator[](mark_index);
|
||||
unsigned int mark_class = record.klass;
|
||||
|
||||
const Anchor& mark_anchor = this + record.markAnchor;
|
||||
bool found;
|
||||
const Anchor& glyph_anchor = anchors.get_anchor (c, glyph_index, mark_class, class_count, &found);
|
||||
/* If this subtable doesn't have an anchor for this base and this class,
|
||||
* return false such that the subsequent subtables have a chance at it. */
|
||||
if (unlikely (!found)) return_trace (false);
|
||||
|
||||
float mark_x, mark_y, base_x, base_y;
|
||||
|
||||
buffer->unsafe_to_break (glyph_pos, buffer->idx + 1);
|
||||
mark_anchor.get_anchor (c, buffer->cur().codepoint, &mark_x, &mark_y);
|
||||
glyph_anchor.get_anchor (c, buffer->info[glyph_pos].codepoint, &base_x, &base_y);
|
||||
|
||||
if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
|
||||
{
|
||||
c->buffer->message (c->font,
|
||||
"attaching mark glyph at %u to glyph at %u",
|
||||
c->buffer->idx, glyph_pos);
|
||||
}
|
||||
|
||||
hb_glyph_position_t &o = buffer->cur_pos();
|
||||
o.attach_chain() = (int) glyph_pos - (int) buffer->idx;
|
||||
if (o.attach_chain() != (int) glyph_pos - (int) buffer->idx)
|
||||
{
|
||||
o.attach_chain() = 0;
|
||||
goto overflow;
|
||||
}
|
||||
o.attach_type() = ATTACH_TYPE_MARK;
|
||||
o.x_offset = roundf (base_x - mark_x);
|
||||
o.y_offset = roundf (base_y - mark_y);
|
||||
buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT;
|
||||
|
||||
if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
|
||||
{
|
||||
c->buffer->message (c->font,
|
||||
"attached mark glyph at %u to glyph at %u",
|
||||
c->buffer->idx, glyph_pos);
|
||||
}
|
||||
|
||||
overflow:
|
||||
buffer->idx++;
|
||||
return_trace (true);
|
||||
}
|
||||
|
||||
template <typename Iterator,
|
||||
hb_requires (hb_is_iterator (Iterator))>
|
||||
bool subset (hb_subset_context_t *c,
|
||||
Iterator coverage,
|
||||
const hb_map_t *klass_mapping) const
|
||||
{
|
||||
TRACE_SUBSET (this);
|
||||
const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
|
||||
|
||||
auto* out = c->serializer->start_embed (this);
|
||||
if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
|
||||
|
||||
auto mark_iter =
|
||||
+ hb_zip (coverage, this->iter ())
|
||||
| hb_filter (glyphset, hb_first)
|
||||
| hb_map (hb_second)
|
||||
;
|
||||
|
||||
bool ret = false;
|
||||
unsigned new_length = 0;
|
||||
for (const auto& mark_record : mark_iter) {
|
||||
ret |= mark_record.subset (c, this, klass_mapping);
|
||||
new_length++;
|
||||
}
|
||||
|
||||
if (unlikely (!c->serializer->check_assign (out->len, new_length,
|
||||
HB_SERIALIZE_ERROR_ARRAY_OVERFLOW)))
|
||||
return_trace (false);
|
||||
|
||||
return_trace (ret);
|
||||
}
|
||||
};
|
||||
|
||||
HB_INTERNAL inline
|
||||
void Markclass_closure_and_remap_indexes (const Coverage &mark_coverage,
|
||||
const MarkArray &mark_array,
|
||||
const hb_set_t &glyphset,
|
||||
hb_map_t* klass_mapping /* INOUT */)
|
||||
{
|
||||
hb_set_t orig_classes;
|
||||
|
||||
+ hb_zip (mark_coverage, mark_array)
|
||||
| hb_filter (glyphset, hb_first)
|
||||
| hb_map (hb_second)
|
||||
| hb_map (&MarkRecord::get_class)
|
||||
| hb_sink (orig_classes)
|
||||
;
|
||||
|
||||
unsigned idx = 0;
|
||||
for (auto klass : orig_classes.iter ())
|
||||
{
|
||||
if (klass_mapping->has (klass)) continue;
|
||||
klass_mapping->set (klass, idx);
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* OT_LAYOUT_GPOS_MARKARRAY_HH */
|
||||
@@ -0,0 +1,41 @@
|
||||
#ifndef OT_LAYOUT_GPOS_MARKBASEPOS_HH
|
||||
#define OT_LAYOUT_GPOS_MARKBASEPOS_HH
|
||||
|
||||
#include "MarkBasePosFormat1.hh"
|
||||
|
||||
namespace OT {
|
||||
namespace Layout {
|
||||
namespace GPOS_impl {
|
||||
|
||||
struct MarkBasePos
|
||||
{
|
||||
protected:
|
||||
union {
|
||||
struct { HBUINT16 v; } format; /* Format identifier */
|
||||
MarkBasePosFormat1_2<SmallTypes> format1;
|
||||
#ifndef HB_NO_BEYOND_64K
|
||||
MarkBasePosFormat1_2<MediumTypes> format2;
|
||||
#endif
|
||||
} u;
|
||||
|
||||
public:
|
||||
template <typename context_t, typename ...Ts>
|
||||
typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
|
||||
{
|
||||
if (unlikely (!c->may_dispatch (this, &u.format.v))) return c->no_dispatch_return_value ();
|
||||
TRACE_DISPATCH (this, u.format.v);
|
||||
switch (u.format.v) {
|
||||
case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...));
|
||||
#ifndef HB_NO_BEYOND_64K
|
||||
case 2: return_trace (c->dispatch (u.format2, std::forward<Ts> (ds)...));
|
||||
#endif
|
||||
default:return_trace (c->default_return_value ());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* OT_LAYOUT_GPOS_MARKBASEPOS_HH */
|
||||
@@ -0,0 +1,250 @@
|
||||
#ifndef OT_LAYOUT_GPOS_MARKBASEPOSFORMAT1_HH
|
||||
#define OT_LAYOUT_GPOS_MARKBASEPOSFORMAT1_HH
|
||||
|
||||
#include "MarkArray.hh"
|
||||
|
||||
namespace OT {
|
||||
namespace Layout {
|
||||
namespace GPOS_impl {
|
||||
|
||||
typedef AnchorMatrix BaseArray; /* base-major--
|
||||
* in order of BaseCoverage Index--,
|
||||
* mark-minor--
|
||||
* ordered by class--zero-based. */
|
||||
|
||||
template <typename Types>
|
||||
struct MarkBasePosFormat1_2
|
||||
{
|
||||
protected:
|
||||
HBUINT16 format; /* Format identifier--format = 1 */
|
||||
typename Types::template OffsetTo<Coverage>
|
||||
markCoverage; /* Offset to MarkCoverage table--from
|
||||
* beginning of MarkBasePos subtable */
|
||||
typename Types::template OffsetTo<Coverage>
|
||||
baseCoverage; /* Offset to BaseCoverage table--from
|
||||
* beginning of MarkBasePos subtable */
|
||||
HBUINT16 classCount; /* Number of classes defined for marks */
|
||||
typename Types::template OffsetTo<MarkArray>
|
||||
markArray; /* Offset to MarkArray table--from
|
||||
* beginning of MarkBasePos subtable */
|
||||
typename Types::template OffsetTo<BaseArray>
|
||||
baseArray; /* Offset to BaseArray table--from
|
||||
* beginning of MarkBasePos subtable */
|
||||
|
||||
public:
|
||||
DEFINE_SIZE_STATIC (4 + 4 * Types::size);
|
||||
|
||||
bool sanitize (hb_sanitize_context_t *c) const
|
||||
{
|
||||
TRACE_SANITIZE (this);
|
||||
return_trace (c->check_struct (this) &&
|
||||
markCoverage.sanitize (c, this) &&
|
||||
baseCoverage.sanitize (c, this) &&
|
||||
markArray.sanitize (c, this) &&
|
||||
baseArray.sanitize (c, this, (unsigned int) classCount));
|
||||
}
|
||||
|
||||
bool intersects (const hb_set_t *glyphs) const
|
||||
{
|
||||
return (this+markCoverage).intersects (glyphs) &&
|
||||
(this+baseCoverage).intersects (glyphs);
|
||||
}
|
||||
|
||||
void closure_lookups (hb_closure_lookups_context_t *c) const {}
|
||||
|
||||
void collect_variation_indices (hb_collect_variation_indices_context_t *c) const
|
||||
{
|
||||
+ hb_zip (this+markCoverage, this+markArray)
|
||||
| hb_filter (c->glyph_set, hb_first)
|
||||
| hb_map (hb_second)
|
||||
| hb_apply ([&] (const MarkRecord& record) { record.collect_variation_indices (c, &(this+markArray)); })
|
||||
;
|
||||
|
||||
hb_map_t klass_mapping;
|
||||
Markclass_closure_and_remap_indexes (this+markCoverage, this+markArray, *c->glyph_set, &klass_mapping);
|
||||
|
||||
unsigned basecount = (this+baseArray).rows;
|
||||
auto base_iter =
|
||||
+ hb_zip (this+baseCoverage, hb_range (basecount))
|
||||
| hb_filter (c->glyph_set, hb_first)
|
||||
| hb_map (hb_second)
|
||||
;
|
||||
|
||||
hb_sorted_vector_t<unsigned> base_indexes;
|
||||
for (const unsigned row : base_iter)
|
||||
{
|
||||
+ hb_range ((unsigned) classCount)
|
||||
| hb_filter (klass_mapping)
|
||||
| hb_map ([&] (const unsigned col) { return row * (unsigned) classCount + col; })
|
||||
| hb_sink (base_indexes)
|
||||
;
|
||||
}
|
||||
(this+baseArray).collect_variation_indices (c, base_indexes.iter ());
|
||||
}
|
||||
|
||||
void collect_glyphs (hb_collect_glyphs_context_t *c) const
|
||||
{
|
||||
if (unlikely (!(this+markCoverage).collect_coverage (c->input))) return;
|
||||
if (unlikely (!(this+baseCoverage).collect_coverage (c->input))) return;
|
||||
}
|
||||
|
||||
const Coverage &get_coverage () const { return this+markCoverage; }
|
||||
|
||||
static inline bool accept (hb_buffer_t *buffer, unsigned idx)
|
||||
{
|
||||
/* We only want to attach to the first of a MultipleSubst sequence.
|
||||
* https://github.com/harfbuzz/harfbuzz/issues/740
|
||||
* Reject others...
|
||||
* ...but stop if we find a mark in the MultipleSubst sequence:
|
||||
* https://github.com/harfbuzz/harfbuzz/issues/1020 */
|
||||
return !_hb_glyph_info_multiplied (&buffer->info[idx]) ||
|
||||
0 == _hb_glyph_info_get_lig_comp (&buffer->info[idx]) ||
|
||||
(idx == 0 ||
|
||||
_hb_glyph_info_is_mark (&buffer->info[idx - 1]) ||
|
||||
!_hb_glyph_info_multiplied (&buffer->info[idx - 1]) ||
|
||||
_hb_glyph_info_get_lig_id (&buffer->info[idx]) !=
|
||||
_hb_glyph_info_get_lig_id (&buffer->info[idx - 1]) ||
|
||||
_hb_glyph_info_get_lig_comp (&buffer->info[idx]) !=
|
||||
_hb_glyph_info_get_lig_comp (&buffer->info[idx - 1]) + 1
|
||||
);
|
||||
}
|
||||
|
||||
bool apply (hb_ot_apply_context_t *c) const
|
||||
{
|
||||
TRACE_APPLY (this);
|
||||
hb_buffer_t *buffer = c->buffer;
|
||||
unsigned int mark_index = (this+markCoverage).get_coverage (buffer->cur().codepoint);
|
||||
if (likely (mark_index == NOT_COVERED)) return_trace (false);
|
||||
|
||||
/* Now we search backwards for a non-mark glyph.
|
||||
* We don't use skippy_iter.prev() to avoid O(n^2) behavior. */
|
||||
|
||||
auto &skippy_iter = c->iter_input;
|
||||
skippy_iter.set_lookup_props (LookupFlag::IgnoreMarks);
|
||||
|
||||
if (c->last_base_until > buffer->idx)
|
||||
{
|
||||
c->last_base_until = 0;
|
||||
c->last_base = -1;
|
||||
}
|
||||
unsigned j;
|
||||
for (j = buffer->idx; j > c->last_base_until; j--)
|
||||
{
|
||||
auto match = skippy_iter.match (buffer->info[j - 1]);
|
||||
if (match == skippy_iter.MATCH)
|
||||
{
|
||||
// https://github.com/harfbuzz/harfbuzz/issues/4124
|
||||
if (!accept (buffer, j - 1) &&
|
||||
NOT_COVERED == (this+baseCoverage).get_coverage (buffer->info[j - 1].codepoint))
|
||||
match = skippy_iter.SKIP;
|
||||
}
|
||||
if (match == skippy_iter.MATCH)
|
||||
{
|
||||
c->last_base = (signed) j - 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
c->last_base_until = buffer->idx;
|
||||
if (c->last_base == -1)
|
||||
{
|
||||
buffer->unsafe_to_concat_from_outbuffer (0, buffer->idx + 1);
|
||||
return_trace (false);
|
||||
}
|
||||
|
||||
unsigned idx = (unsigned) c->last_base;
|
||||
|
||||
/* Checking that matched glyph is actually a base glyph by GDEF is too strong; disabled */
|
||||
//if (!_hb_glyph_info_is_base_glyph (&buffer->info[idx])) { return_trace (false); }
|
||||
|
||||
unsigned int base_index = (this+baseCoverage).get_coverage (buffer->info[idx].codepoint);
|
||||
if (base_index == NOT_COVERED)
|
||||
{
|
||||
buffer->unsafe_to_concat_from_outbuffer (idx, buffer->idx + 1);
|
||||
return_trace (false);
|
||||
}
|
||||
|
||||
return_trace ((this+markArray).apply (c, mark_index, base_index, this+baseArray, classCount, idx));
|
||||
}
|
||||
|
||||
bool subset (hb_subset_context_t *c) const
|
||||
{
|
||||
TRACE_SUBSET (this);
|
||||
const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
|
||||
const hb_map_t &glyph_map = *c->plan->glyph_map;
|
||||
|
||||
auto *out = c->serializer->start_embed (*this);
|
||||
if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
|
||||
out->format = format;
|
||||
|
||||
hb_map_t klass_mapping;
|
||||
Markclass_closure_and_remap_indexes (this+markCoverage, this+markArray, glyphset, &klass_mapping);
|
||||
|
||||
if (!klass_mapping.get_population ()) return_trace (false);
|
||||
out->classCount = klass_mapping.get_population ();
|
||||
|
||||
auto mark_iter =
|
||||
+ hb_zip (this+markCoverage, this+markArray)
|
||||
| hb_filter (glyphset, hb_first)
|
||||
;
|
||||
|
||||
hb_sorted_vector_t<hb_codepoint_t> new_coverage;
|
||||
+ mark_iter
|
||||
| hb_map (hb_first)
|
||||
| hb_map (glyph_map)
|
||||
| hb_sink (new_coverage)
|
||||
;
|
||||
|
||||
if (!out->markCoverage.serialize_serialize (c->serializer, new_coverage.iter ()))
|
||||
return_trace (false);
|
||||
|
||||
if (unlikely (!out->markArray.serialize_subset (c, markArray, this,
|
||||
(this+markCoverage).iter (),
|
||||
&klass_mapping)))
|
||||
return_trace (false);
|
||||
|
||||
unsigned basecount = (this+baseArray).rows;
|
||||
auto base_iter =
|
||||
+ hb_zip (this+baseCoverage, hb_range (basecount))
|
||||
| hb_filter (glyphset, hb_first)
|
||||
;
|
||||
|
||||
new_coverage.reset ();
|
||||
hb_sorted_vector_t<unsigned> base_indexes;
|
||||
auto &base_array = (this+baseArray);
|
||||
for (const auto _ : + base_iter)
|
||||
{
|
||||
unsigned row = _.second;
|
||||
bool non_empty = + hb_range ((unsigned) classCount)
|
||||
| hb_filter (klass_mapping)
|
||||
| hb_map ([&] (const unsigned col) { return !base_array.offset_is_null (row, col, (unsigned) classCount); })
|
||||
| hb_any
|
||||
;
|
||||
|
||||
if (!non_empty) continue;
|
||||
|
||||
hb_codepoint_t new_g = glyph_map.get ( _.first);
|
||||
new_coverage.push (new_g);
|
||||
|
||||
+ hb_range ((unsigned) classCount)
|
||||
| hb_filter (klass_mapping)
|
||||
| hb_map ([&] (const unsigned col) { return row * (unsigned) classCount + col; })
|
||||
| hb_sink (base_indexes)
|
||||
;
|
||||
}
|
||||
|
||||
if (!new_coverage) return_trace (false);
|
||||
if (!out->baseCoverage.serialize_serialize (c->serializer, new_coverage.iter ()))
|
||||
return_trace (false);
|
||||
|
||||
return_trace (out->baseArray.serialize_subset (c, baseArray, this,
|
||||
new_coverage.length,
|
||||
base_indexes.iter ()));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* OT_LAYOUT_GPOS_MARKBASEPOSFORMAT1_HH */
|
||||
@@ -0,0 +1,41 @@
|
||||
#ifndef OT_LAYOUT_GPOS_MARKLIGPOS_HH
|
||||
#define OT_LAYOUT_GPOS_MARKLIGPOS_HH
|
||||
|
||||
#include "MarkLigPosFormat1.hh"
|
||||
|
||||
namespace OT {
|
||||
namespace Layout {
|
||||
namespace GPOS_impl {
|
||||
|
||||
struct MarkLigPos
|
||||
{
|
||||
protected:
|
||||
union {
|
||||
struct { HBUINT16 v; } format; /* Format identifier */
|
||||
MarkLigPosFormat1_2<SmallTypes> format1;
|
||||
#ifndef HB_NO_BEYOND_64K
|
||||
MarkLigPosFormat1_2<MediumTypes> format2;
|
||||
#endif
|
||||
} u;
|
||||
|
||||
public:
|
||||
template <typename context_t, typename ...Ts>
|
||||
typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
|
||||
{
|
||||
if (unlikely (!c->may_dispatch (this, &u.format.v))) return c->no_dispatch_return_value ();
|
||||
TRACE_DISPATCH (this, u.format.v);
|
||||
switch (u.format.v) {
|
||||
case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...));
|
||||
#ifndef HB_NO_BEYOND_64K
|
||||
case 2: return_trace (c->dispatch (u.format2, std::forward<Ts> (ds)...));
|
||||
#endif
|
||||
default:return_trace (c->default_return_value ());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* OT_LAYOUT_GPOS_MARKLIGPOS_HH */
|
||||
@@ -0,0 +1,218 @@
|
||||
#ifndef OT_LAYOUT_GPOS_MARKLIGPOSFORMAT1_HH
|
||||
#define OT_LAYOUT_GPOS_MARKLIGPOSFORMAT1_HH
|
||||
|
||||
#include "LigatureArray.hh"
|
||||
|
||||
namespace OT {
|
||||
namespace Layout {
|
||||
namespace GPOS_impl {
|
||||
|
||||
|
||||
template <typename Types>
|
||||
struct MarkLigPosFormat1_2
|
||||
{
|
||||
protected:
|
||||
HBUINT16 format; /* Format identifier--format = 1 */
|
||||
typename Types::template OffsetTo<Coverage>
|
||||
markCoverage; /* Offset to Mark Coverage table--from
|
||||
* beginning of MarkLigPos subtable */
|
||||
typename Types::template OffsetTo<Coverage>
|
||||
ligatureCoverage; /* Offset to Ligature Coverage
|
||||
* table--from beginning of MarkLigPos
|
||||
* subtable */
|
||||
HBUINT16 classCount; /* Number of defined mark classes */
|
||||
typename Types::template OffsetTo<MarkArray>
|
||||
markArray; /* Offset to MarkArray table--from
|
||||
* beginning of MarkLigPos subtable */
|
||||
typename Types::template OffsetTo<LigatureArray>
|
||||
ligatureArray; /* Offset to LigatureArray table--from
|
||||
* beginning of MarkLigPos subtable */
|
||||
public:
|
||||
DEFINE_SIZE_STATIC (4 + 4 * Types::size);
|
||||
|
||||
bool sanitize (hb_sanitize_context_t *c) const
|
||||
{
|
||||
TRACE_SANITIZE (this);
|
||||
return_trace (c->check_struct (this) &&
|
||||
markCoverage.sanitize (c, this) &&
|
||||
ligatureCoverage.sanitize (c, this) &&
|
||||
markArray.sanitize (c, this) &&
|
||||
ligatureArray.sanitize (c, this, (unsigned int) classCount));
|
||||
}
|
||||
|
||||
bool intersects (const hb_set_t *glyphs) const
|
||||
{
|
||||
return (this+markCoverage).intersects (glyphs) &&
|
||||
(this+ligatureCoverage).intersects (glyphs);
|
||||
}
|
||||
|
||||
void closure_lookups (hb_closure_lookups_context_t *c) const {}
|
||||
|
||||
void collect_variation_indices (hb_collect_variation_indices_context_t *c) const
|
||||
{
|
||||
+ hb_zip (this+markCoverage, this+markArray)
|
||||
| hb_filter (c->glyph_set, hb_first)
|
||||
| hb_map (hb_second)
|
||||
| hb_apply ([&] (const MarkRecord& record) { record.collect_variation_indices (c, &(this+markArray)); })
|
||||
;
|
||||
|
||||
hb_map_t klass_mapping;
|
||||
Markclass_closure_and_remap_indexes (this+markCoverage, this+markArray, *c->glyph_set, &klass_mapping);
|
||||
|
||||
unsigned ligcount = (this+ligatureArray).len;
|
||||
auto lig_iter =
|
||||
+ hb_zip (this+ligatureCoverage, hb_range (ligcount))
|
||||
| hb_filter (c->glyph_set, hb_first)
|
||||
| hb_map (hb_second)
|
||||
;
|
||||
|
||||
const LigatureArray& lig_array = this+ligatureArray;
|
||||
for (const unsigned i : lig_iter)
|
||||
{
|
||||
hb_sorted_vector_t<unsigned> lig_indexes;
|
||||
unsigned row_count = lig_array[i].rows;
|
||||
for (unsigned row : + hb_range (row_count))
|
||||
{
|
||||
+ hb_range ((unsigned) classCount)
|
||||
| hb_filter (klass_mapping)
|
||||
| hb_map ([&] (const unsigned col) { return row * (unsigned) classCount + col; })
|
||||
| hb_sink (lig_indexes)
|
||||
;
|
||||
}
|
||||
|
||||
lig_array[i].collect_variation_indices (c, lig_indexes.iter ());
|
||||
}
|
||||
}
|
||||
|
||||
void collect_glyphs (hb_collect_glyphs_context_t *c) const
|
||||
{
|
||||
if (unlikely (!(this+markCoverage).collect_coverage (c->input))) return;
|
||||
if (unlikely (!(this+ligatureCoverage).collect_coverage (c->input))) return;
|
||||
}
|
||||
|
||||
const Coverage &get_coverage () const { return this+markCoverage; }
|
||||
|
||||
bool apply (hb_ot_apply_context_t *c) const
|
||||
{
|
||||
TRACE_APPLY (this);
|
||||
hb_buffer_t *buffer = c->buffer;
|
||||
unsigned int mark_index = (this+markCoverage).get_coverage (buffer->cur().codepoint);
|
||||
if (likely (mark_index == NOT_COVERED)) return_trace (false);
|
||||
|
||||
/* Now we search backwards for a non-mark glyph */
|
||||
|
||||
auto &skippy_iter = c->iter_input;
|
||||
skippy_iter.set_lookup_props (LookupFlag::IgnoreMarks);
|
||||
|
||||
if (c->last_base_until > buffer->idx)
|
||||
{
|
||||
c->last_base_until = 0;
|
||||
c->last_base = -1;
|
||||
}
|
||||
unsigned j;
|
||||
for (j = buffer->idx; j > c->last_base_until; j--)
|
||||
{
|
||||
auto match = skippy_iter.match (buffer->info[j - 1]);
|
||||
if (match == skippy_iter.MATCH)
|
||||
{
|
||||
c->last_base = (signed) j - 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
c->last_base_until = buffer->idx;
|
||||
if (c->last_base == -1)
|
||||
{
|
||||
buffer->unsafe_to_concat_from_outbuffer (0, buffer->idx + 1);
|
||||
return_trace (false);
|
||||
}
|
||||
|
||||
unsigned idx = (unsigned) c->last_base;
|
||||
|
||||
/* Checking that matched glyph is actually a ligature by GDEF is too strong; disabled */
|
||||
//if (!_hb_glyph_info_is_ligature (&buffer->info[idx])) { return_trace (false); }
|
||||
|
||||
unsigned int lig_index = (this+ligatureCoverage).get_coverage (buffer->info[idx].codepoint);
|
||||
if (lig_index == NOT_COVERED)
|
||||
{
|
||||
buffer->unsafe_to_concat_from_outbuffer (idx, buffer->idx + 1);
|
||||
return_trace (false);
|
||||
}
|
||||
|
||||
const LigatureArray& lig_array = this+ligatureArray;
|
||||
const LigatureAttach& lig_attach = lig_array[lig_index];
|
||||
|
||||
/* Find component to attach to */
|
||||
unsigned int comp_count = lig_attach.rows;
|
||||
if (unlikely (!comp_count))
|
||||
{
|
||||
buffer->unsafe_to_concat_from_outbuffer (idx, buffer->idx + 1);
|
||||
return_trace (false);
|
||||
}
|
||||
|
||||
/* We must now check whether the ligature ID of the current mark glyph
|
||||
* is identical to the ligature ID of the found ligature. If yes, we
|
||||
* can directly use the component index. If not, we attach the mark
|
||||
* glyph to the last component of the ligature. */
|
||||
unsigned int comp_index;
|
||||
unsigned int lig_id = _hb_glyph_info_get_lig_id (&buffer->info[idx]);
|
||||
unsigned int mark_id = _hb_glyph_info_get_lig_id (&buffer->cur());
|
||||
unsigned int mark_comp = _hb_glyph_info_get_lig_comp (&buffer->cur());
|
||||
if (lig_id && lig_id == mark_id && mark_comp > 0)
|
||||
comp_index = hb_min (comp_count, _hb_glyph_info_get_lig_comp (&buffer->cur())) - 1;
|
||||
else
|
||||
comp_index = comp_count - 1;
|
||||
|
||||
return_trace ((this+markArray).apply (c, mark_index, comp_index, lig_attach, classCount, idx));
|
||||
}
|
||||
|
||||
bool subset (hb_subset_context_t *c) const
|
||||
{
|
||||
TRACE_SUBSET (this);
|
||||
const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
|
||||
const hb_map_t &glyph_map = c->plan->glyph_map_gsub;
|
||||
|
||||
auto *out = c->serializer->start_embed (*this);
|
||||
if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
|
||||
out->format = format;
|
||||
|
||||
hb_map_t klass_mapping;
|
||||
Markclass_closure_and_remap_indexes (this+markCoverage, this+markArray, glyphset, &klass_mapping);
|
||||
|
||||
if (!klass_mapping.get_population ()) return_trace (false);
|
||||
out->classCount = klass_mapping.get_population ();
|
||||
|
||||
auto mark_iter =
|
||||
+ hb_zip (this+markCoverage, this+markArray)
|
||||
| hb_filter (glyphset, hb_first)
|
||||
;
|
||||
|
||||
auto new_mark_coverage =
|
||||
+ mark_iter
|
||||
| hb_map_retains_sorting (hb_first)
|
||||
| hb_map_retains_sorting (glyph_map)
|
||||
;
|
||||
|
||||
if (!out->markCoverage.serialize_serialize (c->serializer, new_mark_coverage))
|
||||
return_trace (false);
|
||||
|
||||
if (unlikely (!out->markArray.serialize_subset (c, markArray, this,
|
||||
(this+markCoverage).iter (),
|
||||
&klass_mapping)))
|
||||
return_trace (false);
|
||||
|
||||
hb_sorted_vector_t<hb_codepoint_t> new_lig_coverage;
|
||||
if (!out->ligatureArray.serialize_subset (c, ligatureArray, this,
|
||||
hb_iter (this+ligatureCoverage),
|
||||
classCount, &klass_mapping, new_lig_coverage))
|
||||
return_trace (false);
|
||||
|
||||
return_trace (out->ligatureCoverage.serialize_serialize (c->serializer, new_lig_coverage.iter ()));
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* OT_LAYOUT_GPOS_MARKLIGPOSFORMAT1_HH */
|
||||
@@ -0,0 +1,42 @@
|
||||
#ifndef OT_LAYOUT_GPOS_MARKMARKPOS_HH
|
||||
#define OT_LAYOUT_GPOS_MARKMARKPOS_HH
|
||||
|
||||
#include "MarkMarkPosFormat1.hh"
|
||||
|
||||
namespace OT {
|
||||
namespace Layout {
|
||||
namespace GPOS_impl {
|
||||
|
||||
struct MarkMarkPos
|
||||
{
|
||||
protected:
|
||||
union {
|
||||
struct { HBUINT16 v; } format; /* Format identifier */
|
||||
MarkMarkPosFormat1_2<SmallTypes> format1;
|
||||
#ifndef HB_NO_BEYOND_64K
|
||||
MarkMarkPosFormat1_2<MediumTypes> format2;
|
||||
#endif
|
||||
} u;
|
||||
|
||||
public:
|
||||
template <typename context_t, typename ...Ts>
|
||||
typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
|
||||
{
|
||||
if (unlikely (!c->may_dispatch (this, &u.format.v))) return c->no_dispatch_return_value ();
|
||||
TRACE_DISPATCH (this, u.format.v);
|
||||
switch (u.format.v) {
|
||||
case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...));
|
||||
#ifndef HB_NO_BEYOND_64K
|
||||
case 2: return_trace (c->dispatch (u.format2, std::forward<Ts> (ds)...));
|
||||
#endif
|
||||
default:return_trace (c->default_return_value ());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* OT_LAYOUT_GPOS_MARKMARKPOS_HH */
|
||||
@@ -0,0 +1,239 @@
|
||||
#ifndef OT_LAYOUT_GPOS_MARKMARKPOSFORMAT1_HH
|
||||
#define OT_LAYOUT_GPOS_MARKMARKPOSFORMAT1_HH
|
||||
|
||||
#include "MarkMarkPosFormat1.hh"
|
||||
|
||||
namespace OT {
|
||||
namespace Layout {
|
||||
namespace GPOS_impl {
|
||||
|
||||
typedef AnchorMatrix Mark2Array; /* mark2-major--
|
||||
* in order of Mark2Coverage Index--,
|
||||
* mark1-minor--
|
||||
* ordered by class--zero-based. */
|
||||
|
||||
template <typename Types>
|
||||
struct MarkMarkPosFormat1_2
|
||||
{
|
||||
protected:
|
||||
HBUINT16 format; /* Format identifier--format = 1 */
|
||||
typename Types::template OffsetTo<Coverage>
|
||||
mark1Coverage; /* Offset to Combining Mark1 Coverage
|
||||
* table--from beginning of MarkMarkPos
|
||||
* subtable */
|
||||
typename Types::template OffsetTo<Coverage>
|
||||
mark2Coverage; /* Offset to Combining Mark2 Coverage
|
||||
* table--from beginning of MarkMarkPos
|
||||
* subtable */
|
||||
HBUINT16 classCount; /* Number of defined mark classes */
|
||||
typename Types::template OffsetTo<MarkArray>
|
||||
mark1Array; /* Offset to Mark1Array table--from
|
||||
* beginning of MarkMarkPos subtable */
|
||||
typename Types::template OffsetTo<Mark2Array>
|
||||
mark2Array; /* Offset to Mark2Array table--from
|
||||
* beginning of MarkMarkPos subtable */
|
||||
public:
|
||||
DEFINE_SIZE_STATIC (4 + 4 * Types::size);
|
||||
|
||||
bool sanitize (hb_sanitize_context_t *c) const
|
||||
{
|
||||
TRACE_SANITIZE (this);
|
||||
return_trace (c->check_struct (this) &&
|
||||
mark1Coverage.sanitize (c, this) &&
|
||||
mark2Coverage.sanitize (c, this) &&
|
||||
mark1Array.sanitize (c, this) &&
|
||||
hb_barrier () &&
|
||||
mark2Array.sanitize (c, this, (unsigned int) classCount));
|
||||
}
|
||||
|
||||
bool intersects (const hb_set_t *glyphs) const
|
||||
{
|
||||
return (this+mark1Coverage).intersects (glyphs) &&
|
||||
(this+mark2Coverage).intersects (glyphs);
|
||||
}
|
||||
|
||||
void closure_lookups (hb_closure_lookups_context_t *c) const {}
|
||||
|
||||
void collect_variation_indices (hb_collect_variation_indices_context_t *c) const
|
||||
{
|
||||
+ hb_zip (this+mark1Coverage, this+mark1Array)
|
||||
| hb_filter (c->glyph_set, hb_first)
|
||||
| hb_map (hb_second)
|
||||
| hb_apply ([&] (const MarkRecord& record) { record.collect_variation_indices (c, &(this+mark1Array)); })
|
||||
;
|
||||
|
||||
hb_map_t klass_mapping;
|
||||
Markclass_closure_and_remap_indexes (this+mark1Coverage, this+mark1Array, *c->glyph_set, &klass_mapping);
|
||||
|
||||
unsigned mark2_count = (this+mark2Array).rows;
|
||||
auto mark2_iter =
|
||||
+ hb_zip (this+mark2Coverage, hb_range (mark2_count))
|
||||
| hb_filter (c->glyph_set, hb_first)
|
||||
| hb_map (hb_second)
|
||||
;
|
||||
|
||||
hb_sorted_vector_t<unsigned> mark2_indexes;
|
||||
for (const unsigned row : mark2_iter)
|
||||
{
|
||||
+ hb_range ((unsigned) classCount)
|
||||
| hb_filter (klass_mapping)
|
||||
| hb_map ([&] (const unsigned col) { return row * (unsigned) classCount + col; })
|
||||
| hb_sink (mark2_indexes)
|
||||
;
|
||||
}
|
||||
(this+mark2Array).collect_variation_indices (c, mark2_indexes.iter ());
|
||||
}
|
||||
|
||||
void collect_glyphs (hb_collect_glyphs_context_t *c) const
|
||||
{
|
||||
if (unlikely (!(this+mark1Coverage).collect_coverage (c->input))) return;
|
||||
if (unlikely (!(this+mark2Coverage).collect_coverage (c->input))) return;
|
||||
}
|
||||
|
||||
const Coverage &get_coverage () const { return this+mark1Coverage; }
|
||||
|
||||
bool apply (hb_ot_apply_context_t *c) const
|
||||
{
|
||||
TRACE_APPLY (this);
|
||||
hb_buffer_t *buffer = c->buffer;
|
||||
unsigned int mark1_index = (this+mark1Coverage).get_coverage (buffer->cur().codepoint);
|
||||
if (likely (mark1_index == NOT_COVERED)) return_trace (false);
|
||||
|
||||
/* now we search backwards for a suitable mark glyph until a non-mark glyph */
|
||||
auto &skippy_iter = c->iter_input;
|
||||
skippy_iter.reset_fast (buffer->idx);
|
||||
skippy_iter.set_lookup_props (c->lookup_props & ~(uint32_t)LookupFlag::IgnoreFlags);
|
||||
unsigned unsafe_from;
|
||||
if (unlikely (!skippy_iter.prev (&unsafe_from)))
|
||||
{
|
||||
buffer->unsafe_to_concat_from_outbuffer (unsafe_from, buffer->idx + 1);
|
||||
return_trace (false);
|
||||
}
|
||||
|
||||
if (likely (!_hb_glyph_info_is_mark (&buffer->info[skippy_iter.idx])))
|
||||
{
|
||||
buffer->unsafe_to_concat_from_outbuffer (skippy_iter.idx, buffer->idx + 1);
|
||||
return_trace (false);
|
||||
}
|
||||
|
||||
unsigned int j = skippy_iter.idx;
|
||||
|
||||
unsigned int id1 = _hb_glyph_info_get_lig_id (&buffer->cur());
|
||||
unsigned int id2 = _hb_glyph_info_get_lig_id (&buffer->info[j]);
|
||||
unsigned int comp1 = _hb_glyph_info_get_lig_comp (&buffer->cur());
|
||||
unsigned int comp2 = _hb_glyph_info_get_lig_comp (&buffer->info[j]);
|
||||
|
||||
if (likely (id1 == id2))
|
||||
{
|
||||
if (id1 == 0) /* Marks belonging to the same base. */
|
||||
goto good;
|
||||
else if (comp1 == comp2) /* Marks belonging to the same ligature component. */
|
||||
goto good;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* If ligature ids don't match, it may be the case that one of the marks
|
||||
* itself is a ligature. In which case match. */
|
||||
if ((id1 > 0 && !comp1) || (id2 > 0 && !comp2))
|
||||
goto good;
|
||||
}
|
||||
|
||||
/* Didn't match. */
|
||||
buffer->unsafe_to_concat_from_outbuffer (skippy_iter.idx, buffer->idx + 1);
|
||||
return_trace (false);
|
||||
|
||||
good:
|
||||
unsigned int mark2_index = (this+mark2Coverage).get_coverage (buffer->info[j].codepoint);
|
||||
if (mark2_index == NOT_COVERED)
|
||||
{
|
||||
buffer->unsafe_to_concat_from_outbuffer (skippy_iter.idx, buffer->idx + 1);
|
||||
return_trace (false);
|
||||
}
|
||||
|
||||
return_trace ((this+mark1Array).apply (c, mark1_index, mark2_index, this+mark2Array, classCount, j));
|
||||
}
|
||||
|
||||
bool subset (hb_subset_context_t *c) const
|
||||
{
|
||||
TRACE_SUBSET (this);
|
||||
const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
|
||||
const hb_map_t &glyph_map = *c->plan->glyph_map;
|
||||
|
||||
auto *out = c->serializer->start_embed (*this);
|
||||
if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
|
||||
out->format = format;
|
||||
|
||||
hb_map_t klass_mapping;
|
||||
Markclass_closure_and_remap_indexes (this+mark1Coverage, this+mark1Array, glyphset, &klass_mapping);
|
||||
|
||||
if (!klass_mapping.get_population ()) return_trace (false);
|
||||
out->classCount = klass_mapping.get_population ();
|
||||
|
||||
auto mark1_iter =
|
||||
+ hb_zip (this+mark1Coverage, this+mark1Array)
|
||||
| hb_filter (glyphset, hb_first)
|
||||
;
|
||||
|
||||
hb_sorted_vector_t<hb_codepoint_t> new_coverage;
|
||||
+ mark1_iter
|
||||
| hb_map (hb_first)
|
||||
| hb_map (glyph_map)
|
||||
| hb_sink (new_coverage)
|
||||
;
|
||||
|
||||
if (!out->mark1Coverage.serialize_serialize (c->serializer, new_coverage.iter ()))
|
||||
return_trace (false);
|
||||
|
||||
if (unlikely (!out->mark1Array.serialize_subset (c, mark1Array, this,
|
||||
(this+mark1Coverage).iter (),
|
||||
&klass_mapping)))
|
||||
return_trace (false);
|
||||
|
||||
unsigned mark2count = (this+mark2Array).rows;
|
||||
auto mark2_iter =
|
||||
+ hb_zip (this+mark2Coverage, hb_range (mark2count))
|
||||
| hb_filter (glyphset, hb_first)
|
||||
;
|
||||
|
||||
new_coverage.reset ();
|
||||
hb_sorted_vector_t<unsigned> mark2_indexes;
|
||||
auto &mark2_array = (this+mark2Array);
|
||||
for (const auto _ : + mark2_iter)
|
||||
{
|
||||
unsigned row = _.second;
|
||||
|
||||
bool non_empty = + hb_range ((unsigned) classCount)
|
||||
| hb_filter (klass_mapping)
|
||||
| hb_map ([&] (const unsigned col) { return !mark2_array.offset_is_null (row, col, (unsigned) classCount); })
|
||||
| hb_any
|
||||
;
|
||||
|
||||
if (!non_empty) continue;
|
||||
|
||||
hb_codepoint_t new_g = glyph_map.get ( _.first);
|
||||
new_coverage.push (new_g);
|
||||
|
||||
+ hb_range ((unsigned) classCount)
|
||||
| hb_filter (klass_mapping)
|
||||
| hb_map ([&] (const unsigned col) { return row * (unsigned) classCount + col; })
|
||||
| hb_sink (mark2_indexes)
|
||||
;
|
||||
}
|
||||
|
||||
if (!new_coverage) return_trace (false);
|
||||
if (!out->mark2Coverage.serialize_serialize (c->serializer, new_coverage.iter ()))
|
||||
return_trace (false);
|
||||
|
||||
return_trace (out->mark2Array.serialize_subset (c, mark2Array, this,
|
||||
mark2_iter.len (),
|
||||
mark2_indexes.iter ()));
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* OT_LAYOUT_GPOS_MARKMARKPOSFORMAT1_HH */
|
||||
@@ -0,0 +1,51 @@
|
||||
#ifndef OT_LAYOUT_GPOS_MARKRECORD_HH
|
||||
#define OT_LAYOUT_GPOS_MARKRECORD_HH
|
||||
|
||||
namespace OT {
|
||||
namespace Layout {
|
||||
namespace GPOS_impl {
|
||||
|
||||
struct MarkRecord
|
||||
{
|
||||
friend struct MarkArray;
|
||||
|
||||
public:
|
||||
HBUINT16 klass; /* Class defined for this mark */
|
||||
Offset16To<Anchor>
|
||||
markAnchor; /* Offset to Anchor table--from
|
||||
* beginning of MarkArray table */
|
||||
public:
|
||||
DEFINE_SIZE_STATIC (4);
|
||||
|
||||
unsigned get_class () const { return (unsigned) klass; }
|
||||
bool sanitize (hb_sanitize_context_t *c, const void *base) const
|
||||
{
|
||||
TRACE_SANITIZE (this);
|
||||
return_trace (c->check_struct (this) && markAnchor.sanitize (c, base));
|
||||
}
|
||||
|
||||
bool subset (hb_subset_context_t *c,
|
||||
const void *src_base,
|
||||
const hb_map_t *klass_mapping) const
|
||||
{
|
||||
TRACE_SUBSET (this);
|
||||
auto *out = c->serializer->embed (this);
|
||||
if (unlikely (!out)) return_trace (false);
|
||||
|
||||
out->klass = klass_mapping->get (klass);
|
||||
return_trace (out->markAnchor.serialize_subset (c, markAnchor, src_base));
|
||||
}
|
||||
|
||||
void collect_variation_indices (hb_collect_variation_indices_context_t *c,
|
||||
const void *src_base) const
|
||||
{
|
||||
(src_base+markAnchor).collect_variation_indices (c);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* OT_LAYOUT_GPOS_MARKRECORD_HH */
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
#ifndef OT_LAYOUT_GPOS_PAIRPOS_HH
|
||||
#define OT_LAYOUT_GPOS_PAIRPOS_HH
|
||||
|
||||
#include "PairPosFormat1.hh"
|
||||
#include "PairPosFormat2.hh"
|
||||
|
||||
namespace OT {
|
||||
namespace Layout {
|
||||
namespace GPOS_impl {
|
||||
|
||||
struct PairPos
|
||||
{
|
||||
protected:
|
||||
union {
|
||||
struct { HBUINT16 v; } format; /* Format identifier */
|
||||
PairPosFormat1_3<SmallTypes> format1;
|
||||
PairPosFormat2_4<SmallTypes> format2;
|
||||
#ifndef HB_NO_BEYOND_64K
|
||||
PairPosFormat1_3<MediumTypes> format3;
|
||||
PairPosFormat2_4<MediumTypes> format4;
|
||||
#endif
|
||||
} u;
|
||||
|
||||
public:
|
||||
template <typename context_t, typename ...Ts>
|
||||
typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
|
||||
{
|
||||
if (unlikely (!c->may_dispatch (this, &u.format.v))) return c->no_dispatch_return_value ();
|
||||
TRACE_DISPATCH (this, u.format.v);
|
||||
switch (u.format.v) {
|
||||
case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...));
|
||||
case 2: return_trace (c->dispatch (u.format2, std::forward<Ts> (ds)...));
|
||||
#ifndef HB_NO_BEYOND_64K
|
||||
case 3: return_trace (c->dispatch (u.format3, std::forward<Ts> (ds)...));
|
||||
case 4: return_trace (c->dispatch (u.format4, std::forward<Ts> (ds)...));
|
||||
#endif
|
||||
default:return_trace (c->default_return_value ());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // OT_LAYOUT_GPOS_PAIRPOS_HH
|
||||
@@ -0,0 +1,254 @@
|
||||
#ifndef OT_LAYOUT_GPOS_PAIRPOSFORMAT1_HH
|
||||
#define OT_LAYOUT_GPOS_PAIRPOSFORMAT1_HH
|
||||
|
||||
#include "PairSet.hh"
|
||||
|
||||
namespace OT {
|
||||
namespace Layout {
|
||||
namespace GPOS_impl {
|
||||
|
||||
|
||||
template <typename Types>
|
||||
struct PairPosFormat1_3
|
||||
{
|
||||
using PairSet = GPOS_impl::PairSet<Types>;
|
||||
using PairValueRecord = GPOS_impl::PairValueRecord<Types>;
|
||||
|
||||
protected:
|
||||
HBUINT16 format; /* Format identifier--format = 1 */
|
||||
typename Types::template OffsetTo<Coverage>
|
||||
coverage; /* Offset to Coverage table--from
|
||||
* beginning of subtable */
|
||||
ValueFormat valueFormat[2]; /* [0] Defines the types of data in
|
||||
* ValueRecord1--for the first glyph
|
||||
* in the pair--may be zero (0) */
|
||||
/* [1] Defines the types of data in
|
||||
* ValueRecord2--for the second glyph
|
||||
* in the pair--may be zero (0) */
|
||||
Array16Of<typename Types::template OffsetTo<PairSet>>
|
||||
pairSet; /* Array of PairSet tables
|
||||
* ordered by Coverage Index */
|
||||
public:
|
||||
DEFINE_SIZE_ARRAY (8 + Types::size, pairSet);
|
||||
|
||||
bool sanitize (hb_sanitize_context_t *c) const
|
||||
{
|
||||
TRACE_SANITIZE (this);
|
||||
|
||||
if (!c->check_struct (this)) return_trace (false);
|
||||
hb_barrier ();
|
||||
|
||||
unsigned int len1 = valueFormat[0].get_len ();
|
||||
unsigned int len2 = valueFormat[1].get_len ();
|
||||
typename PairSet::sanitize_closure_t closure =
|
||||
{
|
||||
valueFormat,
|
||||
len1,
|
||||
PairSet::get_size (len1, len2)
|
||||
};
|
||||
|
||||
return_trace (coverage.sanitize (c, this) && pairSet.sanitize (c, this, &closure));
|
||||
}
|
||||
|
||||
bool intersects (const hb_set_t *glyphs) const
|
||||
{
|
||||
auto &cov = this+coverage;
|
||||
|
||||
if (pairSet.len > glyphs->get_population () * hb_bit_storage ((unsigned) pairSet.len))
|
||||
{
|
||||
for (hb_codepoint_t g : glyphs->iter())
|
||||
{
|
||||
unsigned i = cov.get_coverage (g);
|
||||
if ((this+pairSet[i]).intersects (glyphs, valueFormat))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return
|
||||
+ hb_zip (cov, pairSet)
|
||||
| hb_filter (*glyphs, hb_first)
|
||||
| hb_map (hb_second)
|
||||
| hb_map ([glyphs, this] (const typename Types::template OffsetTo<PairSet> &_)
|
||||
{ return (this+_).intersects (glyphs, valueFormat); })
|
||||
| hb_any
|
||||
;
|
||||
}
|
||||
|
||||
void closure_lookups (hb_closure_lookups_context_t *c) const {}
|
||||
void collect_variation_indices (hb_collect_variation_indices_context_t *c) const
|
||||
{
|
||||
if ((!valueFormat[0].has_device ()) && (!valueFormat[1].has_device ())) return;
|
||||
|
||||
auto it =
|
||||
+ hb_zip (this+coverage, pairSet)
|
||||
| hb_filter (c->glyph_set, hb_first)
|
||||
| hb_map (hb_second)
|
||||
;
|
||||
|
||||
if (!it) return;
|
||||
+ it
|
||||
| hb_map (hb_add (this))
|
||||
| hb_apply ([&] (const PairSet& _) { _.collect_variation_indices (c, valueFormat); })
|
||||
;
|
||||
}
|
||||
|
||||
void collect_glyphs (hb_collect_glyphs_context_t *c) const
|
||||
{
|
||||
if (unlikely (!(this+coverage).collect_coverage (c->input))) return;
|
||||
unsigned int count = pairSet.len;
|
||||
for (unsigned int i = 0; i < count; i++)
|
||||
(this+pairSet[i]).collect_glyphs (c, valueFormat);
|
||||
}
|
||||
|
||||
const Coverage &get_coverage () const { return this+coverage; }
|
||||
|
||||
struct external_cache_t
|
||||
{
|
||||
hb_ot_layout_mapping_cache_t coverage;
|
||||
};
|
||||
void *external_cache_create () const
|
||||
{
|
||||
external_cache_t *cache = (external_cache_t *) hb_malloc (sizeof (external_cache_t));
|
||||
if (likely (cache))
|
||||
{
|
||||
cache->coverage.clear ();
|
||||
}
|
||||
return cache;
|
||||
}
|
||||
|
||||
bool apply (hb_ot_apply_context_t *c, void *external_cache) const
|
||||
{
|
||||
TRACE_APPLY (this);
|
||||
|
||||
hb_buffer_t *buffer = c->buffer;
|
||||
|
||||
#ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE
|
||||
external_cache_t *cache = (external_cache_t *) external_cache;
|
||||
unsigned int index = (this+coverage).get_coverage (buffer->cur().codepoint, cache ? &cache->coverage : nullptr);
|
||||
#else
|
||||
unsigned int index = (this+coverage).get_coverage (buffer->cur().codepoint);
|
||||
#endif
|
||||
if (index == NOT_COVERED) return_trace (false);
|
||||
|
||||
auto &skippy_iter = c->iter_input;
|
||||
skippy_iter.reset_fast (buffer->idx);
|
||||
unsigned unsafe_to;
|
||||
if (unlikely (!skippy_iter.next (&unsafe_to)))
|
||||
{
|
||||
buffer->unsafe_to_concat (buffer->idx, unsafe_to);
|
||||
return_trace (false);
|
||||
}
|
||||
|
||||
return_trace ((this+pairSet[index]).apply (c, valueFormat, skippy_iter.idx));
|
||||
}
|
||||
|
||||
bool subset (hb_subset_context_t *c) const
|
||||
{
|
||||
TRACE_SUBSET (this);
|
||||
|
||||
const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
|
||||
const hb_map_t &glyph_map = *c->plan->glyph_map;
|
||||
|
||||
auto *out = c->serializer->start_embed (*this);
|
||||
if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
|
||||
out->format = format;
|
||||
|
||||
hb_pair_t<unsigned, unsigned> newFormats = hb_pair (valueFormat[0], valueFormat[1]);
|
||||
|
||||
if (c->plan->normalized_coords)
|
||||
{
|
||||
/* all device flags will be dropped when full instancing, no need to strip
|
||||
* hints, also do not strip emtpy cause we don't compute the new default
|
||||
* value during stripping */
|
||||
newFormats = compute_effective_value_formats (glyphset, false, false, &c->plan->layout_variation_idx_delta_map);
|
||||
}
|
||||
/* do not strip hints for VF */
|
||||
else if (c->plan->flags & HB_SUBSET_FLAGS_NO_HINTING)
|
||||
{
|
||||
hb_blob_t* blob = hb_face_reference_table (c->plan->source, HB_TAG ('f','v','a','r'));
|
||||
bool has_fvar = (blob != hb_blob_get_empty ());
|
||||
hb_blob_destroy (blob);
|
||||
|
||||
bool strip = !has_fvar;
|
||||
/* special case: strip hints when a VF has no GDEF varstore after
|
||||
* subsetting*/
|
||||
if (has_fvar && !c->plan->has_gdef_varstore)
|
||||
strip = true;
|
||||
newFormats = compute_effective_value_formats (glyphset, strip, true);
|
||||
}
|
||||
|
||||
out->valueFormat[0] = newFormats.first;
|
||||
out->valueFormat[1] = newFormats.second;
|
||||
|
||||
hb_sorted_vector_t<hb_codepoint_t> new_coverage;
|
||||
|
||||
+ hb_zip (this+coverage, pairSet)
|
||||
| hb_filter (glyphset, hb_first)
|
||||
| hb_filter ([this, c, out] (const typename Types::template OffsetTo<PairSet>& _)
|
||||
{
|
||||
auto snap = c->serializer->snapshot ();
|
||||
auto *o = out->pairSet.serialize_append (c->serializer);
|
||||
if (unlikely (!o)) return false;
|
||||
bool ret = o->serialize_subset (c, _, this, valueFormat, out->valueFormat);
|
||||
if (!ret)
|
||||
{
|
||||
out->pairSet.pop ();
|
||||
c->serializer->revert (snap);
|
||||
}
|
||||
return ret;
|
||||
},
|
||||
hb_second)
|
||||
| hb_map (hb_first)
|
||||
| hb_map (glyph_map)
|
||||
| hb_sink (new_coverage)
|
||||
;
|
||||
|
||||
out->coverage.serialize_serialize (c->serializer, new_coverage.iter ());
|
||||
|
||||
return_trace (bool (new_coverage));
|
||||
}
|
||||
|
||||
|
||||
hb_pair_t<unsigned, unsigned> compute_effective_value_formats (const hb_set_t& glyphset,
|
||||
bool strip_hints, bool strip_empty,
|
||||
const hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *varidx_delta_map = nullptr) const
|
||||
{
|
||||
unsigned record_size = PairSet::get_size (valueFormat);
|
||||
|
||||
unsigned format1 = 0;
|
||||
unsigned format2 = 0;
|
||||
for (const auto & _ :
|
||||
+ hb_zip (this+coverage, pairSet)
|
||||
| hb_filter (glyphset, hb_first)
|
||||
| hb_map (hb_second)
|
||||
)
|
||||
{
|
||||
const PairSet& set = (this + _);
|
||||
const PairValueRecord *record = &set.firstPairValueRecord;
|
||||
|
||||
unsigned count = set.len;
|
||||
for (unsigned i = 0; i < count; i++)
|
||||
{
|
||||
if (record->intersects (glyphset))
|
||||
{
|
||||
format1 = format1 | valueFormat[0].get_effective_format (record->get_values_1 (), strip_hints, strip_empty, &set, varidx_delta_map);
|
||||
format2 = format2 | valueFormat[1].get_effective_format (record->get_values_2 (valueFormat[0]), strip_hints, strip_empty, &set, varidx_delta_map);
|
||||
}
|
||||
record = &StructAtOffset<const PairValueRecord> (record, record_size);
|
||||
}
|
||||
|
||||
if (format1 == valueFormat[0] && format2 == valueFormat[1])
|
||||
break;
|
||||
}
|
||||
|
||||
return hb_pair (format1, format2);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // OT_LAYOUT_GPOS_PAIRPOSFORMAT1_HH
|
||||
@@ -0,0 +1,389 @@
|
||||
#ifndef OT_LAYOUT_GPOS_PAIRPOSFORMAT2_HH
|
||||
#define OT_LAYOUT_GPOS_PAIRPOSFORMAT2_HH
|
||||
|
||||
#include "ValueFormat.hh"
|
||||
|
||||
namespace OT {
|
||||
namespace Layout {
|
||||
namespace GPOS_impl {
|
||||
|
||||
template <typename Types>
|
||||
struct PairPosFormat2_4 : ValueBase
|
||||
{
|
||||
protected:
|
||||
HBUINT16 format; /* Format identifier--format = 2 */
|
||||
typename Types::template OffsetTo<Coverage>
|
||||
coverage; /* Offset to Coverage table--from
|
||||
* beginning of subtable */
|
||||
ValueFormat valueFormat1; /* ValueRecord definition--for the
|
||||
* first glyph of the pair--may be zero
|
||||
* (0) */
|
||||
ValueFormat valueFormat2; /* ValueRecord definition--for the
|
||||
* second glyph of the pair--may be
|
||||
* zero (0) */
|
||||
typename Types::template OffsetTo<ClassDef>
|
||||
classDef1; /* Offset to ClassDef table--from
|
||||
* beginning of PairPos subtable--for
|
||||
* the first glyph of the pair */
|
||||
typename Types::template OffsetTo<ClassDef>
|
||||
classDef2; /* Offset to ClassDef table--from
|
||||
* beginning of PairPos subtable--for
|
||||
* the second glyph of the pair */
|
||||
HBUINT16 class1Count; /* Number of classes in ClassDef1
|
||||
* table--includes Class0 */
|
||||
HBUINT16 class2Count; /* Number of classes in ClassDef2
|
||||
* table--includes Class0 */
|
||||
ValueRecord values; /* Matrix of value pairs:
|
||||
* class1-major, class2-minor,
|
||||
* Each entry has value1 and value2 */
|
||||
public:
|
||||
DEFINE_SIZE_ARRAY (10 + 3 * Types::size, values);
|
||||
|
||||
bool sanitize (hb_sanitize_context_t *c) const
|
||||
{
|
||||
TRACE_SANITIZE (this);
|
||||
if (!(c->check_struct (this)
|
||||
&& coverage.sanitize (c, this)
|
||||
&& classDef1.sanitize (c, this)
|
||||
&& classDef2.sanitize (c, this))) return_trace (false);
|
||||
|
||||
unsigned int len1 = valueFormat1.get_len ();
|
||||
unsigned int len2 = valueFormat2.get_len ();
|
||||
unsigned int stride = HBUINT16::static_size * (len1 + len2);
|
||||
unsigned int count = (unsigned int) class1Count * (unsigned int) class2Count;
|
||||
return_trace (c->check_range ((const void *) values,
|
||||
count,
|
||||
stride) &&
|
||||
(c->lazy_some_gpos ||
|
||||
(valueFormat1.sanitize_values_stride_unsafe (c, this, &values[0], count, stride) &&
|
||||
valueFormat2.sanitize_values_stride_unsafe (c, this, &values[len1], count, stride))));
|
||||
}
|
||||
|
||||
bool intersects (const hb_set_t *glyphs) const
|
||||
{
|
||||
return (this+coverage).intersects (glyphs) &&
|
||||
(this+classDef2).intersects (glyphs);
|
||||
}
|
||||
|
||||
void closure_lookups (hb_closure_lookups_context_t *c) const {}
|
||||
void collect_variation_indices (hb_collect_variation_indices_context_t *c) const
|
||||
{
|
||||
if (!intersects (c->glyph_set)) return;
|
||||
if ((!valueFormat1.has_device ()) && (!valueFormat2.has_device ())) return;
|
||||
|
||||
hb_set_t klass1_glyphs, klass2_glyphs;
|
||||
if (!(this+classDef1).collect_coverage (&klass1_glyphs)) return;
|
||||
if (!(this+classDef2).collect_coverage (&klass2_glyphs)) return;
|
||||
|
||||
hb_set_t class1_set, class2_set;
|
||||
for (const unsigned cp : + c->glyph_set->iter () | hb_filter (this + coverage))
|
||||
{
|
||||
if (!klass1_glyphs.has (cp)) class1_set.add (0);
|
||||
else
|
||||
{
|
||||
unsigned klass1 = (this+classDef1).get (cp);
|
||||
class1_set.add (klass1);
|
||||
}
|
||||
}
|
||||
|
||||
class2_set.add (0);
|
||||
for (const unsigned cp : + c->glyph_set->iter () | hb_filter (klass2_glyphs))
|
||||
{
|
||||
unsigned klass2 = (this+classDef2).get (cp);
|
||||
class2_set.add (klass2);
|
||||
}
|
||||
|
||||
if (class1_set.is_empty ()
|
||||
|| class2_set.is_empty ()
|
||||
|| (class2_set.get_population() == 1 && class2_set.has(0)))
|
||||
return;
|
||||
|
||||
unsigned len1 = valueFormat1.get_len ();
|
||||
unsigned len2 = valueFormat2.get_len ();
|
||||
const hb_array_t<const Value> values_array = values.as_array ((unsigned)class1Count * (unsigned) class2Count * (len1 + len2));
|
||||
for (const unsigned class1_idx : class1_set.iter ())
|
||||
{
|
||||
for (const unsigned class2_idx : class2_set.iter ())
|
||||
{
|
||||
unsigned start_offset = (class1_idx * (unsigned) class2Count + class2_idx) * (len1 + len2);
|
||||
if (valueFormat1.has_device ())
|
||||
valueFormat1.collect_variation_indices (c, this, values_array.sub_array (start_offset, len1));
|
||||
|
||||
if (valueFormat2.has_device ())
|
||||
valueFormat2.collect_variation_indices (c, this, values_array.sub_array (start_offset+len1, len2));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void collect_glyphs (hb_collect_glyphs_context_t *c) const
|
||||
{
|
||||
if (unlikely (!(this+coverage).collect_coverage (c->input))) return;
|
||||
if (unlikely (!(this+classDef2).collect_coverage (c->input))) return;
|
||||
}
|
||||
|
||||
const Coverage &get_coverage () const { return this+coverage; }
|
||||
|
||||
struct external_cache_t
|
||||
{
|
||||
hb_ot_layout_mapping_cache_t coverage;
|
||||
hb_ot_layout_mapping_cache_t first;
|
||||
hb_ot_layout_mapping_cache_t second;
|
||||
};
|
||||
void *external_cache_create () const
|
||||
{
|
||||
external_cache_t *cache = (external_cache_t *) hb_malloc (sizeof (external_cache_t));
|
||||
if (likely (cache))
|
||||
{
|
||||
cache->coverage.clear ();
|
||||
cache->first.clear ();
|
||||
cache->second.clear ();
|
||||
}
|
||||
return cache;
|
||||
}
|
||||
|
||||
bool apply (hb_ot_apply_context_t *c, void *external_cache) const
|
||||
{
|
||||
TRACE_APPLY (this);
|
||||
|
||||
hb_buffer_t *buffer = c->buffer;
|
||||
|
||||
#ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE
|
||||
external_cache_t *cache = (external_cache_t *) external_cache;
|
||||
unsigned int index = (this+coverage).get_coverage (buffer->cur().codepoint, cache ? &cache->coverage : nullptr);
|
||||
#else
|
||||
unsigned int index = (this+coverage).get_coverage (buffer->cur().codepoint);
|
||||
#endif
|
||||
if (index == NOT_COVERED) return_trace (false);
|
||||
|
||||
auto &skippy_iter = c->iter_input;
|
||||
skippy_iter.reset_fast (buffer->idx);
|
||||
unsigned unsafe_to;
|
||||
if (unlikely (!skippy_iter.next (&unsafe_to)))
|
||||
{
|
||||
buffer->unsafe_to_concat (buffer->idx, unsafe_to);
|
||||
return_trace (false);
|
||||
}
|
||||
|
||||
#ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE
|
||||
unsigned int klass1 = (this+classDef1).get_class (buffer->cur().codepoint, cache ? &cache->first : nullptr);
|
||||
unsigned int klass2 = (this+classDef2).get_class (buffer->info[skippy_iter.idx].codepoint, cache ? &cache->second : nullptr);
|
||||
#else
|
||||
unsigned int klass1 = (this+classDef1).get_class (buffer->cur().codepoint);
|
||||
unsigned int klass2 = (this+classDef2).get_class (buffer->info[skippy_iter.idx].codepoint);
|
||||
#endif
|
||||
if (unlikely (klass1 >= class1Count || klass2 >= class2Count))
|
||||
{
|
||||
buffer->unsafe_to_concat (buffer->idx, skippy_iter.idx + 1);
|
||||
return_trace (false);
|
||||
}
|
||||
|
||||
unsigned int len1 = valueFormat1.get_len ();
|
||||
unsigned int len2 = valueFormat2.get_len ();
|
||||
unsigned int record_len = len1 + len2;
|
||||
|
||||
const Value *v = &values[record_len * (klass1 * class2Count + klass2)];
|
||||
|
||||
bool applied_first = false, applied_second = false;
|
||||
|
||||
|
||||
/* Isolate simple kerning and apply it half to each side.
|
||||
* Results in better cursor positioning / underline drawing.
|
||||
*
|
||||
* Disabled, because causes issues... :-(
|
||||
* https://github.com/harfbuzz/harfbuzz/issues/3408
|
||||
* https://github.com/harfbuzz/harfbuzz/pull/3235#issuecomment-1029814978
|
||||
*/
|
||||
#ifndef HB_SPLIT_KERN
|
||||
if (false)
|
||||
#endif
|
||||
{
|
||||
if (!len2)
|
||||
{
|
||||
const hb_direction_t dir = buffer->props.direction;
|
||||
const bool horizontal = HB_DIRECTION_IS_HORIZONTAL (dir);
|
||||
const bool backward = HB_DIRECTION_IS_BACKWARD (dir);
|
||||
unsigned mask = horizontal ? ValueFormat::xAdvance : ValueFormat::yAdvance;
|
||||
if (backward)
|
||||
mask |= mask >> 2; /* Add eg. xPlacement in RTL. */
|
||||
/* Add Devices. */
|
||||
mask |= mask << 4;
|
||||
|
||||
if (valueFormat1 & ~mask)
|
||||
goto bail;
|
||||
|
||||
/* Is simple kern. Apply value on an empty position slot,
|
||||
* then split it between sides. */
|
||||
|
||||
hb_glyph_position_t pos{};
|
||||
if (valueFormat1.apply_value (c, this, v, pos))
|
||||
{
|
||||
hb_position_t *src = &pos.x_advance;
|
||||
hb_position_t *dst1 = &buffer->cur_pos().x_advance;
|
||||
hb_position_t *dst2 = &buffer->pos[skippy_iter.idx].x_advance;
|
||||
unsigned i = horizontal ? 0 : 1;
|
||||
|
||||
hb_position_t kern = src[i];
|
||||
hb_position_t kern1 = kern >> 1;
|
||||
hb_position_t kern2 = kern - kern1;
|
||||
|
||||
if (!backward)
|
||||
{
|
||||
dst1[i] += kern1;
|
||||
dst2[i] += kern2;
|
||||
dst2[i + 2] += kern2;
|
||||
}
|
||||
else
|
||||
{
|
||||
dst1[i] += kern1;
|
||||
dst1[i + 2] += src[i + 2] - kern2;
|
||||
dst2[i] += kern2;
|
||||
}
|
||||
|
||||
applied_first = applied_second = kern != 0;
|
||||
goto success;
|
||||
}
|
||||
goto boring;
|
||||
}
|
||||
}
|
||||
bail:
|
||||
|
||||
if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
|
||||
{
|
||||
c->buffer->message (c->font,
|
||||
"try kerning glyphs at %u,%u",
|
||||
c->buffer->idx, skippy_iter.idx);
|
||||
}
|
||||
|
||||
applied_first = len1 && valueFormat1.apply_value (c, this, v, buffer->cur_pos());
|
||||
applied_second = len2 && valueFormat2.apply_value (c, this, v + len1, buffer->pos[skippy_iter.idx]);
|
||||
|
||||
if (applied_first || applied_second)
|
||||
if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
|
||||
{
|
||||
c->buffer->message (c->font,
|
||||
"kerned glyphs at %u,%u",
|
||||
c->buffer->idx, skippy_iter.idx);
|
||||
}
|
||||
|
||||
if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
|
||||
{
|
||||
c->buffer->message (c->font,
|
||||
"tried kerning glyphs at %u,%u",
|
||||
c->buffer->idx, skippy_iter.idx);
|
||||
}
|
||||
|
||||
success:
|
||||
if (applied_first || applied_second)
|
||||
buffer->unsafe_to_break (buffer->idx, skippy_iter.idx + 1);
|
||||
else
|
||||
boring:
|
||||
buffer->unsafe_to_concat (buffer->idx, skippy_iter.idx + 1);
|
||||
|
||||
if (len2)
|
||||
{
|
||||
skippy_iter.idx++;
|
||||
// https://github.com/harfbuzz/harfbuzz/issues/3824
|
||||
// https://github.com/harfbuzz/harfbuzz/issues/3888#issuecomment-1326781116
|
||||
buffer->unsafe_to_break (buffer->idx, skippy_iter.idx + 1);
|
||||
}
|
||||
|
||||
buffer->idx = skippy_iter.idx;
|
||||
|
||||
return_trace (true);
|
||||
}
|
||||
|
||||
bool subset (hb_subset_context_t *c) const
|
||||
{
|
||||
TRACE_SUBSET (this);
|
||||
auto *out = c->serializer->start_embed (*this);
|
||||
if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
|
||||
out->format = format;
|
||||
|
||||
hb_map_t klass1_map;
|
||||
out->classDef1.serialize_subset (c, classDef1, this, &klass1_map, true, true, &(this + coverage));
|
||||
out->class1Count = klass1_map.get_population ();
|
||||
|
||||
hb_map_t klass2_map;
|
||||
out->classDef2.serialize_subset (c, classDef2, this, &klass2_map, true, false);
|
||||
out->class2Count = klass2_map.get_population ();
|
||||
|
||||
unsigned len1 = valueFormat1.get_len ();
|
||||
unsigned len2 = valueFormat2.get_len ();
|
||||
|
||||
hb_pair_t<unsigned, unsigned> newFormats = hb_pair (valueFormat1, valueFormat2);
|
||||
|
||||
if (c->plan->normalized_coords)
|
||||
{
|
||||
/* in case of full instancing, all var device flags will be dropped so no
|
||||
* need to strip hints here */
|
||||
newFormats = compute_effective_value_formats (klass1_map, klass2_map, false, false, &c->plan->layout_variation_idx_delta_map);
|
||||
}
|
||||
/* do not strip hints for VF */
|
||||
else if (c->plan->flags & HB_SUBSET_FLAGS_NO_HINTING)
|
||||
{
|
||||
hb_blob_t* blob = hb_face_reference_table (c->plan->source, HB_TAG ('f','v','a','r'));
|
||||
bool has_fvar = (blob != hb_blob_get_empty ());
|
||||
hb_blob_destroy (blob);
|
||||
|
||||
bool strip = !has_fvar;
|
||||
/* special case: strip hints when a VF has no GDEF varstore after
|
||||
* subsetting*/
|
||||
if (has_fvar && !c->plan->has_gdef_varstore)
|
||||
strip = true;
|
||||
newFormats = compute_effective_value_formats (klass1_map, klass2_map, strip, true);
|
||||
}
|
||||
|
||||
out->valueFormat1 = newFormats.first;
|
||||
out->valueFormat2 = newFormats.second;
|
||||
|
||||
unsigned total_len = len1 + len2;
|
||||
hb_vector_t<unsigned> class2_idxs (+ hb_range ((unsigned) class2Count) | hb_filter (klass2_map));
|
||||
for (unsigned class1_idx : + hb_range ((unsigned) class1Count) | hb_filter (klass1_map))
|
||||
{
|
||||
for (unsigned class2_idx : class2_idxs)
|
||||
{
|
||||
unsigned idx = (class1_idx * (unsigned) class2Count + class2_idx) * total_len;
|
||||
valueFormat1.copy_values (c->serializer, out->valueFormat1, this, &values[idx], &c->plan->layout_variation_idx_delta_map);
|
||||
valueFormat2.copy_values (c->serializer, out->valueFormat2, this, &values[idx + len1], &c->plan->layout_variation_idx_delta_map);
|
||||
}
|
||||
}
|
||||
|
||||
bool ret = out->coverage.serialize_subset(c, coverage, this);
|
||||
return_trace (out->class1Count && out->class2Count && ret);
|
||||
}
|
||||
|
||||
|
||||
hb_pair_t<unsigned, unsigned> compute_effective_value_formats (const hb_map_t& klass1_map,
|
||||
const hb_map_t& klass2_map,
|
||||
bool strip_hints, bool strip_empty,
|
||||
const hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *varidx_delta_map = nullptr) const
|
||||
{
|
||||
unsigned len1 = valueFormat1.get_len ();
|
||||
unsigned len2 = valueFormat2.get_len ();
|
||||
unsigned record_size = len1 + len2;
|
||||
|
||||
unsigned format1 = 0;
|
||||
unsigned format2 = 0;
|
||||
|
||||
for (unsigned class1_idx : + hb_range ((unsigned) class1Count) | hb_filter (klass1_map))
|
||||
{
|
||||
for (unsigned class2_idx : + hb_range ((unsigned) class2Count) | hb_filter (klass2_map))
|
||||
{
|
||||
unsigned idx = (class1_idx * (unsigned) class2Count + class2_idx) * record_size;
|
||||
format1 = format1 | valueFormat1.get_effective_format (&values[idx], strip_hints, strip_empty, this, varidx_delta_map);
|
||||
format2 = format2 | valueFormat2.get_effective_format (&values[idx + len1], strip_hints, strip_empty, this, varidx_delta_map);
|
||||
}
|
||||
|
||||
if (format1 == valueFormat1 && format2 == valueFormat2)
|
||||
break;
|
||||
}
|
||||
|
||||
return hb_pair (format1, format2);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // OT_LAYOUT_GPOS_PAIRPOSFORMAT2_HH
|
||||
+210
@@ -0,0 +1,210 @@
|
||||
#ifndef OT_LAYOUT_GPOS_PAIRSET_HH
|
||||
#define OT_LAYOUT_GPOS_PAIRSET_HH
|
||||
|
||||
#include "PairValueRecord.hh"
|
||||
|
||||
namespace OT {
|
||||
namespace Layout {
|
||||
namespace GPOS_impl {
|
||||
|
||||
|
||||
template <typename Types>
|
||||
struct PairSet : ValueBase
|
||||
{
|
||||
template <typename Types2>
|
||||
friend struct PairPosFormat1_3;
|
||||
|
||||
using PairValueRecord = GPOS_impl::PairValueRecord<Types>;
|
||||
|
||||
protected:
|
||||
HBUINT16 len; /* Number of PairValueRecords */
|
||||
PairValueRecord firstPairValueRecord;
|
||||
/* Array of PairValueRecords--ordered
|
||||
* by GlyphID of the second glyph */
|
||||
public:
|
||||
DEFINE_SIZE_MIN (2);
|
||||
|
||||
static size_t get_size (unsigned len1, unsigned len2)
|
||||
{
|
||||
return Types::HBGlyphID::static_size + Value::static_size * (len1 + len2);
|
||||
}
|
||||
static size_t get_size (const ValueFormat valueFormats[2])
|
||||
{
|
||||
unsigned len1 = valueFormats[0].get_len ();
|
||||
unsigned len2 = valueFormats[1].get_len ();
|
||||
return get_size (len1, len2);
|
||||
}
|
||||
|
||||
struct sanitize_closure_t
|
||||
{
|
||||
const ValueFormat *valueFormats;
|
||||
unsigned int len1; /* valueFormats[0].get_len() */
|
||||
size_t stride; /* bytes */
|
||||
};
|
||||
|
||||
bool sanitize (hb_sanitize_context_t *c, const sanitize_closure_t *closure) const
|
||||
{
|
||||
TRACE_SANITIZE (this);
|
||||
if (!(c->check_struct (this) &&
|
||||
hb_barrier () &&
|
||||
c->check_range (&firstPairValueRecord,
|
||||
len,
|
||||
closure->stride))) return_trace (false);
|
||||
hb_barrier ();
|
||||
|
||||
unsigned int count = len;
|
||||
const PairValueRecord *record = &firstPairValueRecord;
|
||||
return_trace (c->lazy_some_gpos ||
|
||||
(closure->valueFormats[0].sanitize_values_stride_unsafe (c, this, &record->values[0], count, closure->stride) &&
|
||||
closure->valueFormats[1].sanitize_values_stride_unsafe (c, this, &record->values[closure->len1], count, closure->stride)));
|
||||
}
|
||||
|
||||
bool intersects (const hb_set_t *glyphs,
|
||||
const ValueFormat *valueFormats) const
|
||||
{
|
||||
unsigned record_size = get_size (valueFormats);
|
||||
|
||||
const PairValueRecord *record = &firstPairValueRecord;
|
||||
unsigned int count = len;
|
||||
for (unsigned int i = 0; i < count; i++)
|
||||
{
|
||||
if (glyphs->has (record->secondGlyph))
|
||||
return true;
|
||||
record = &StructAtOffset<const PairValueRecord> (record, record_size);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void collect_glyphs (hb_collect_glyphs_context_t *c,
|
||||
const ValueFormat *valueFormats) const
|
||||
{
|
||||
unsigned record_size = get_size (valueFormats);
|
||||
|
||||
const PairValueRecord *record = &firstPairValueRecord;
|
||||
c->input->add_array (&record->secondGlyph, len, record_size);
|
||||
}
|
||||
|
||||
void collect_variation_indices (hb_collect_variation_indices_context_t *c,
|
||||
const ValueFormat *valueFormats) const
|
||||
{
|
||||
unsigned record_size = get_size (valueFormats);
|
||||
|
||||
const PairValueRecord *record = &firstPairValueRecord;
|
||||
unsigned count = len;
|
||||
for (unsigned i = 0; i < count; i++)
|
||||
{
|
||||
if (c->glyph_set->has (record->secondGlyph))
|
||||
{ record->collect_variation_indices (c, valueFormats, this); }
|
||||
|
||||
record = &StructAtOffset<const PairValueRecord> (record, record_size);
|
||||
}
|
||||
}
|
||||
|
||||
bool apply (hb_ot_apply_context_t *c,
|
||||
const ValueFormat *valueFormats,
|
||||
unsigned int pos) const
|
||||
{
|
||||
TRACE_APPLY (this);
|
||||
hb_buffer_t *buffer = c->buffer;
|
||||
unsigned int len1 = valueFormats[0].get_len ();
|
||||
unsigned int len2 = valueFormats[1].get_len ();
|
||||
unsigned record_size = get_size (len1, len2);
|
||||
|
||||
const PairValueRecord *record = hb_bsearch (buffer->info[pos].codepoint,
|
||||
&firstPairValueRecord,
|
||||
len,
|
||||
record_size);
|
||||
if (record)
|
||||
{
|
||||
if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
|
||||
{
|
||||
c->buffer->message (c->font,
|
||||
"try kerning glyphs at %u,%u",
|
||||
c->buffer->idx, pos);
|
||||
}
|
||||
|
||||
bool applied_first = len1 && valueFormats[0].apply_value (c, this, &record->values[0], buffer->cur_pos());
|
||||
bool applied_second = len2 && valueFormats[1].apply_value (c, this, &record->values[len1], buffer->pos[pos]);
|
||||
|
||||
if (applied_first || applied_second)
|
||||
if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
|
||||
{
|
||||
c->buffer->message (c->font,
|
||||
"kerned glyphs at %u,%u",
|
||||
c->buffer->idx, pos);
|
||||
}
|
||||
|
||||
if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
|
||||
{
|
||||
c->buffer->message (c->font,
|
||||
"tried kerning glyphs at %u,%u",
|
||||
c->buffer->idx, pos);
|
||||
}
|
||||
|
||||
if (applied_first || applied_second)
|
||||
buffer->unsafe_to_break (buffer->idx, pos + 1);
|
||||
|
||||
if (len2)
|
||||
{
|
||||
pos++;
|
||||
// https://github.com/harfbuzz/harfbuzz/issues/3824
|
||||
// https://github.com/harfbuzz/harfbuzz/issues/3888#issuecomment-1326781116
|
||||
buffer->unsafe_to_break (buffer->idx, pos + 1);
|
||||
}
|
||||
|
||||
buffer->idx = pos;
|
||||
return_trace (true);
|
||||
}
|
||||
buffer->unsafe_to_concat (buffer->idx, pos + 1);
|
||||
return_trace (false);
|
||||
}
|
||||
|
||||
bool subset (hb_subset_context_t *c,
|
||||
const ValueFormat valueFormats[2],
|
||||
const ValueFormat newFormats[2]) const
|
||||
{
|
||||
TRACE_SUBSET (this);
|
||||
auto snap = c->serializer->snapshot ();
|
||||
|
||||
auto *out = c->serializer->start_embed (*this);
|
||||
if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
|
||||
out->len = 0;
|
||||
|
||||
const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
|
||||
const hb_map_t &glyph_map = *c->plan->glyph_map;
|
||||
|
||||
unsigned len1 = valueFormats[0].get_len ();
|
||||
unsigned len2 = valueFormats[1].get_len ();
|
||||
unsigned record_size = get_size (len1, len2);
|
||||
|
||||
typename PairValueRecord::context_t context =
|
||||
{
|
||||
this,
|
||||
valueFormats,
|
||||
newFormats,
|
||||
len1,
|
||||
&glyph_map,
|
||||
&c->plan->layout_variation_idx_delta_map
|
||||
};
|
||||
|
||||
const PairValueRecord *record = &firstPairValueRecord;
|
||||
unsigned count = len, num = 0;
|
||||
for (unsigned i = 0; i < count; i++)
|
||||
{
|
||||
if (glyphset.has (record->secondGlyph)
|
||||
&& record->subset (c, &context)) num++;
|
||||
record = &StructAtOffset<const PairValueRecord> (record, record_size);
|
||||
}
|
||||
|
||||
out->len = num;
|
||||
if (!num) c->serializer->revert (snap);
|
||||
return_trace (num);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // OT_LAYOUT_GPOS_PAIRSET_HH
|
||||
@@ -0,0 +1,99 @@
|
||||
#ifndef OT_LAYOUT_GPOS_PAIRVALUERECORD_HH
|
||||
#define OT_LAYOUT_GPOS_PAIRVALUERECORD_HH
|
||||
|
||||
#include "ValueFormat.hh"
|
||||
|
||||
namespace OT {
|
||||
namespace Layout {
|
||||
namespace GPOS_impl {
|
||||
|
||||
|
||||
template <typename Types>
|
||||
struct PairValueRecord
|
||||
{
|
||||
template <typename Types2>
|
||||
friend struct PairSet;
|
||||
|
||||
protected:
|
||||
typename Types::HBGlyphID
|
||||
secondGlyph; /* GlyphID of second glyph in the
|
||||
* pair--first glyph is listed in the
|
||||
* Coverage table */
|
||||
ValueRecord values; /* Positioning data for the first glyph
|
||||
* followed by for second glyph */
|
||||
public:
|
||||
DEFINE_SIZE_ARRAY (Types::HBGlyphID::static_size, values);
|
||||
|
||||
int cmp (hb_codepoint_t k) const
|
||||
{ return secondGlyph.cmp (k); }
|
||||
|
||||
struct context_t
|
||||
{
|
||||
const ValueBase *base;
|
||||
const ValueFormat *valueFormats;
|
||||
const ValueFormat *newFormats;
|
||||
unsigned len1; /* valueFormats[0].get_len() */
|
||||
const hb_map_t *glyph_map;
|
||||
const hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *layout_variation_idx_delta_map;
|
||||
};
|
||||
|
||||
bool subset (hb_subset_context_t *c,
|
||||
context_t *closure) const
|
||||
{
|
||||
TRACE_SERIALIZE (this);
|
||||
auto *s = c->serializer;
|
||||
auto *out = s->start_embed (*this);
|
||||
if (unlikely (!s->extend_min (out))) return_trace (false);
|
||||
|
||||
out->secondGlyph = (*closure->glyph_map)[secondGlyph];
|
||||
|
||||
closure->valueFormats[0].copy_values (s,
|
||||
closure->newFormats[0],
|
||||
closure->base, &values[0],
|
||||
closure->layout_variation_idx_delta_map);
|
||||
closure->valueFormats[1].copy_values (s,
|
||||
closure->newFormats[1],
|
||||
closure->base,
|
||||
&values[closure->len1],
|
||||
closure->layout_variation_idx_delta_map);
|
||||
|
||||
return_trace (true);
|
||||
}
|
||||
|
||||
void collect_variation_indices (hb_collect_variation_indices_context_t *c,
|
||||
const ValueFormat *valueFormats,
|
||||
const ValueBase *base) const
|
||||
{
|
||||
unsigned record1_len = valueFormats[0].get_len ();
|
||||
unsigned record2_len = valueFormats[1].get_len ();
|
||||
const hb_array_t<const Value> values_array = values.as_array (record1_len + record2_len);
|
||||
|
||||
if (valueFormats[0].has_device ())
|
||||
valueFormats[0].collect_variation_indices (c, base, values_array.sub_array (0, record1_len));
|
||||
|
||||
if (valueFormats[1].has_device ())
|
||||
valueFormats[1].collect_variation_indices (c, base, values_array.sub_array (record1_len, record2_len));
|
||||
}
|
||||
|
||||
bool intersects (const hb_set_t& glyphset) const
|
||||
{
|
||||
return glyphset.has(secondGlyph);
|
||||
}
|
||||
|
||||
const Value* get_values_1 () const
|
||||
{
|
||||
return &values[0];
|
||||
}
|
||||
|
||||
const Value* get_values_2 (ValueFormat format1) const
|
||||
{
|
||||
return &values[format1.get_len ()];
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // OT_LAYOUT_GPOS_PAIRVALUERECORD_HH
|
||||
@@ -0,0 +1,79 @@
|
||||
#ifndef OT_LAYOUT_GPOS_POSLOOKUP_HH
|
||||
#define OT_LAYOUT_GPOS_POSLOOKUP_HH
|
||||
|
||||
#include "PosLookupSubTable.hh"
|
||||
#include "../../../hb-ot-layout-common.hh"
|
||||
|
||||
namespace OT {
|
||||
namespace Layout {
|
||||
namespace GPOS_impl {
|
||||
|
||||
struct PosLookup : Lookup
|
||||
{
|
||||
using SubTable = PosLookupSubTable;
|
||||
|
||||
const SubTable& get_subtable (unsigned int i) const
|
||||
{ return Lookup::get_subtable<SubTable> (i); }
|
||||
|
||||
bool is_reverse () const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool apply (hb_ot_apply_context_t *c) const
|
||||
{
|
||||
TRACE_APPLY (this);
|
||||
return_trace (dispatch (c));
|
||||
}
|
||||
|
||||
bool intersects (const hb_set_t *glyphs) const
|
||||
{
|
||||
hb_intersects_context_t c (glyphs);
|
||||
return dispatch (&c);
|
||||
}
|
||||
|
||||
hb_collect_glyphs_context_t::return_t collect_glyphs (hb_collect_glyphs_context_t *c) const
|
||||
{ return dispatch (c); }
|
||||
|
||||
hb_closure_lookups_context_t::return_t closure_lookups (hb_closure_lookups_context_t *c, unsigned this_index) const
|
||||
{
|
||||
if (c->is_lookup_visited (this_index))
|
||||
return hb_closure_lookups_context_t::default_return_value ();
|
||||
|
||||
c->set_lookup_visited (this_index);
|
||||
if (!intersects (c->glyphs))
|
||||
{
|
||||
c->set_lookup_inactive (this_index);
|
||||
return hb_closure_lookups_context_t::default_return_value ();
|
||||
}
|
||||
|
||||
hb_closure_lookups_context_t::return_t ret = dispatch (c);
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <typename set_t>
|
||||
void collect_coverage (set_t *glyphs) const
|
||||
{
|
||||
hb_collect_coverage_context_t<set_t> c (glyphs);
|
||||
dispatch (&c);
|
||||
}
|
||||
|
||||
template <typename context_t>
|
||||
static typename context_t::return_t dispatch_recurse_func (context_t *c, unsigned int lookup_index);
|
||||
|
||||
template <typename context_t, typename ...Ts>
|
||||
typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
|
||||
{ return Lookup::dispatch<SubTable> (c, std::forward<Ts> (ds)...); }
|
||||
|
||||
bool subset (hb_subset_context_t *c) const
|
||||
{ return Lookup::subset<SubTable> (c); }
|
||||
|
||||
bool sanitize (hb_sanitize_context_t *c) const
|
||||
{ return Lookup::sanitize<SubTable> (c); }
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* OT_LAYOUT_GPOS_POSLOOKUP_HH */
|
||||
@@ -0,0 +1,79 @@
|
||||
#ifndef OT_LAYOUT_GPOS_POSLOOKUPSUBTABLE_HH
|
||||
#define OT_LAYOUT_GPOS_POSLOOKUPSUBTABLE_HH
|
||||
|
||||
#include "SinglePos.hh"
|
||||
#include "PairPos.hh"
|
||||
#include "CursivePos.hh"
|
||||
#include "MarkBasePos.hh"
|
||||
#include "MarkLigPos.hh"
|
||||
#include "MarkMarkPos.hh"
|
||||
#include "ContextPos.hh"
|
||||
#include "ChainContextPos.hh"
|
||||
#include "ExtensionPos.hh"
|
||||
|
||||
namespace OT {
|
||||
namespace Layout {
|
||||
namespace GPOS_impl {
|
||||
|
||||
struct PosLookupSubTable
|
||||
{
|
||||
friend struct ::OT::Lookup;
|
||||
friend struct PosLookup;
|
||||
|
||||
enum Type {
|
||||
Single = 1,
|
||||
Pair = 2,
|
||||
Cursive = 3,
|
||||
MarkBase = 4,
|
||||
MarkLig = 5,
|
||||
MarkMark = 6,
|
||||
Context = 7,
|
||||
ChainContext = 8,
|
||||
Extension = 9
|
||||
};
|
||||
|
||||
template <typename context_t, typename ...Ts>
|
||||
typename context_t::return_t dispatch (context_t *c, unsigned int lookup_type, Ts&&... ds) const
|
||||
{
|
||||
TRACE_DISPATCH (this, lookup_type);
|
||||
switch (lookup_type) {
|
||||
case Single: return_trace (u.single.dispatch (c, std::forward<Ts> (ds)...));
|
||||
case Pair: return_trace (u.pair.dispatch (c, std::forward<Ts> (ds)...));
|
||||
case Cursive: return_trace (u.cursive.dispatch (c, std::forward<Ts> (ds)...));
|
||||
case MarkBase: return_trace (u.markBase.dispatch (c, std::forward<Ts> (ds)...));
|
||||
case MarkLig: return_trace (u.markLig.dispatch (c, std::forward<Ts> (ds)...));
|
||||
case MarkMark: return_trace (u.markMark.dispatch (c, std::forward<Ts> (ds)...));
|
||||
case Context: return_trace (u.context.dispatch (c, std::forward<Ts> (ds)...));
|
||||
case ChainContext: return_trace (u.chainContext.dispatch (c, std::forward<Ts> (ds)...));
|
||||
case Extension: return_trace (u.extension.dispatch (c, std::forward<Ts> (ds)...));
|
||||
default: return_trace (c->default_return_value ());
|
||||
}
|
||||
}
|
||||
|
||||
bool intersects (const hb_set_t *glyphs, unsigned int lookup_type) const
|
||||
{
|
||||
hb_intersects_context_t c (glyphs);
|
||||
return dispatch (&c, lookup_type);
|
||||
}
|
||||
|
||||
protected:
|
||||
union {
|
||||
SinglePos single;
|
||||
PairPos pair;
|
||||
CursivePos cursive;
|
||||
MarkBasePos markBase;
|
||||
MarkLigPos markLig;
|
||||
MarkMarkPos markMark;
|
||||
ContextPos context;
|
||||
ChainContextPos chainContext;
|
||||
ExtensionPos extension;
|
||||
} u;
|
||||
public:
|
||||
DEFINE_SIZE_MIN (0);
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* HB_OT_LAYOUT_GPOS_POSLOOKUPSUBTABLE_HH */
|
||||
@@ -0,0 +1,98 @@
|
||||
#ifndef OT_LAYOUT_GPOS_SINGLEPOS_HH
|
||||
#define OT_LAYOUT_GPOS_SINGLEPOS_HH
|
||||
|
||||
#include "SinglePosFormat1.hh"
|
||||
#include "SinglePosFormat2.hh"
|
||||
|
||||
namespace OT {
|
||||
namespace Layout {
|
||||
namespace GPOS_impl {
|
||||
|
||||
struct SinglePos
|
||||
{
|
||||
protected:
|
||||
union {
|
||||
struct { HBUINT16 v; } format; /* Format identifier */
|
||||
SinglePosFormat1 format1;
|
||||
SinglePosFormat2 format2;
|
||||
} u;
|
||||
|
||||
public:
|
||||
template<typename Iterator,
|
||||
hb_requires (hb_is_iterator (Iterator))>
|
||||
unsigned get_format (Iterator glyph_val_iter_pairs)
|
||||
{
|
||||
hb_array_t<const Value> first_val_iter = hb_second (*glyph_val_iter_pairs);
|
||||
|
||||
for (const auto iter : glyph_val_iter_pairs)
|
||||
for (const auto _ : hb_zip (iter.second, first_val_iter))
|
||||
if (_.first != _.second)
|
||||
return 2;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
template<typename Iterator,
|
||||
typename SrcLookup,
|
||||
hb_requires (hb_is_iterator (Iterator))>
|
||||
void serialize (hb_serialize_context_t *c,
|
||||
const SrcLookup* src,
|
||||
Iterator glyph_val_iter_pairs,
|
||||
const hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *layout_variation_idx_delta_map,
|
||||
unsigned newFormat)
|
||||
{
|
||||
if (unlikely (!c->extend_min (u.format.v))) return;
|
||||
unsigned format = 2;
|
||||
ValueFormat new_format;
|
||||
new_format = newFormat;
|
||||
|
||||
if (glyph_val_iter_pairs)
|
||||
format = get_format (glyph_val_iter_pairs);
|
||||
|
||||
u.format.v = format;
|
||||
switch (u.format.v) {
|
||||
case 1: u.format1.serialize (c,
|
||||
src,
|
||||
glyph_val_iter_pairs,
|
||||
new_format,
|
||||
layout_variation_idx_delta_map);
|
||||
return;
|
||||
case 2: u.format2.serialize (c,
|
||||
src,
|
||||
glyph_val_iter_pairs,
|
||||
new_format,
|
||||
layout_variation_idx_delta_map);
|
||||
return;
|
||||
default:return;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename context_t, typename ...Ts>
|
||||
typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
|
||||
{
|
||||
if (unlikely (!c->may_dispatch (this, &u.format.v))) return c->no_dispatch_return_value ();
|
||||
TRACE_DISPATCH (this, u.format.v);
|
||||
switch (u.format.v) {
|
||||
case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...));
|
||||
case 2: return_trace (c->dispatch (u.format2, std::forward<Ts> (ds)...));
|
||||
default:return_trace (c->default_return_value ());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template<typename Iterator, typename SrcLookup>
|
||||
static void
|
||||
SinglePos_serialize (hb_serialize_context_t *c,
|
||||
const SrcLookup *src,
|
||||
Iterator it,
|
||||
const hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *layout_variation_idx_delta_map,
|
||||
unsigned new_format)
|
||||
{ c->start_embed<SinglePos> ()->serialize (c, src, it, layout_variation_idx_delta_map, new_format); }
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* OT_LAYOUT_GPOS_SINGLEPOS_HH */
|
||||
@@ -0,0 +1,190 @@
|
||||
#ifndef OT_LAYOUT_GPOS_SINGLEPOSFORMAT1_HH
|
||||
#define OT_LAYOUT_GPOS_SINGLEPOSFORMAT1_HH
|
||||
|
||||
#include "Common.hh"
|
||||
#include "ValueFormat.hh"
|
||||
|
||||
namespace OT {
|
||||
namespace Layout {
|
||||
namespace GPOS_impl {
|
||||
|
||||
struct SinglePosFormat1 : ValueBase
|
||||
{
|
||||
protected:
|
||||
HBUINT16 format; /* Format identifier--format = 1 */
|
||||
Offset16To<Coverage>
|
||||
coverage; /* Offset to Coverage table--from
|
||||
* beginning of subtable */
|
||||
ValueFormat valueFormat; /* Defines the types of data in the
|
||||
* ValueRecord */
|
||||
ValueRecord values; /* Defines positioning
|
||||
* value(s)--applied to all glyphs in
|
||||
* the Coverage table */
|
||||
public:
|
||||
DEFINE_SIZE_ARRAY (6, values);
|
||||
|
||||
bool sanitize (hb_sanitize_context_t *c) const
|
||||
{
|
||||
TRACE_SANITIZE (this);
|
||||
return_trace (c->check_struct (this) &&
|
||||
coverage.sanitize (c, this) &&
|
||||
hb_barrier () &&
|
||||
/* The coverage table may use a range to represent a set
|
||||
* of glyphs, which means a small number of bytes can
|
||||
* generate a large glyph set. Manually modify the
|
||||
* sanitizer max ops to take this into account.
|
||||
*
|
||||
* Note: This check *must* be right after coverage sanitize. */
|
||||
c->check_ops ((this + coverage).get_population () >> 1) &&
|
||||
valueFormat.sanitize_value (c, this, values));
|
||||
|
||||
}
|
||||
|
||||
bool intersects (const hb_set_t *glyphs) const
|
||||
{ return (this+coverage).intersects (glyphs); }
|
||||
|
||||
void closure_lookups (hb_closure_lookups_context_t *c) const {}
|
||||
void collect_variation_indices (hb_collect_variation_indices_context_t *c) const
|
||||
{
|
||||
if (!valueFormat.has_device ()) return;
|
||||
|
||||
hb_set_t intersection;
|
||||
(this+coverage).intersect_set (*c->glyph_set, intersection);
|
||||
if (!intersection) return;
|
||||
|
||||
valueFormat.collect_variation_indices (c, this, values.as_array (valueFormat.get_len ()));
|
||||
}
|
||||
|
||||
void collect_glyphs (hb_collect_glyphs_context_t *c) const
|
||||
{ if (unlikely (!(this+coverage).collect_coverage (c->input))) return; }
|
||||
|
||||
const Coverage &get_coverage () const { return this+coverage; }
|
||||
|
||||
ValueFormat get_value_format () const { return valueFormat; }
|
||||
|
||||
bool apply (hb_ot_apply_context_t *c) const
|
||||
{
|
||||
TRACE_APPLY (this);
|
||||
hb_buffer_t *buffer = c->buffer;
|
||||
unsigned int index = (this+coverage).get_coverage (buffer->cur().codepoint);
|
||||
if (index == NOT_COVERED) return_trace (false);
|
||||
|
||||
if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
|
||||
{
|
||||
c->buffer->message (c->font,
|
||||
"positioning glyph at %u",
|
||||
c->buffer->idx);
|
||||
}
|
||||
|
||||
valueFormat.apply_value (c, this, values, buffer->cur_pos());
|
||||
|
||||
if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
|
||||
{
|
||||
c->buffer->message (c->font,
|
||||
"positioned glyph at %u",
|
||||
c->buffer->idx);
|
||||
}
|
||||
|
||||
buffer->idx++;
|
||||
return_trace (true);
|
||||
}
|
||||
|
||||
bool
|
||||
position_single (hb_font_t *font,
|
||||
hb_blob_t *table_blob,
|
||||
hb_direction_t direction,
|
||||
hb_codepoint_t gid,
|
||||
hb_glyph_position_t &pos) const
|
||||
{
|
||||
unsigned int index = (this+coverage).get_coverage (gid);
|
||||
if (likely (index == NOT_COVERED)) return false;
|
||||
|
||||
/* This is ugly... */
|
||||
hb_buffer_t buffer {};
|
||||
buffer.props.direction = direction;
|
||||
OT::hb_ot_apply_context_t c (1, font, &buffer, table_blob);
|
||||
|
||||
valueFormat.apply_value (&c, this, values, pos);
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename Iterator,
|
||||
typename SrcLookup,
|
||||
hb_requires (hb_is_iterator (Iterator))>
|
||||
void serialize (hb_serialize_context_t *c,
|
||||
const SrcLookup *src,
|
||||
Iterator it,
|
||||
ValueFormat newFormat,
|
||||
const hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *layout_variation_idx_delta_map)
|
||||
{
|
||||
if (unlikely (!c->extend_min (this))) return;
|
||||
if (unlikely (!c->check_assign (valueFormat,
|
||||
newFormat,
|
||||
HB_SERIALIZE_ERROR_INT_OVERFLOW))) return;
|
||||
|
||||
for (const hb_array_t<const Value>& _ : + it | hb_map (hb_second))
|
||||
{
|
||||
src->get_value_format ().copy_values (c, newFormat, src, &_, layout_variation_idx_delta_map);
|
||||
// Only serialize the first entry in the iterator, the rest are assumed to
|
||||
// be the same.
|
||||
break;
|
||||
}
|
||||
|
||||
auto glyphs =
|
||||
+ it
|
||||
| hb_map_retains_sorting (hb_first)
|
||||
;
|
||||
|
||||
coverage.serialize_serialize (c, glyphs);
|
||||
}
|
||||
|
||||
bool subset (hb_subset_context_t *c) const
|
||||
{
|
||||
TRACE_SUBSET (this);
|
||||
const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
|
||||
const hb_map_t &glyph_map = *c->plan->glyph_map;
|
||||
|
||||
hb_set_t intersection;
|
||||
(this+coverage).intersect_set (glyphset, intersection);
|
||||
|
||||
unsigned new_format = valueFormat;
|
||||
|
||||
if (c->plan->normalized_coords)
|
||||
{
|
||||
new_format = valueFormat.get_effective_format (values.arrayZ, false, false, this, &c->plan->layout_variation_idx_delta_map);
|
||||
}
|
||||
/* do not strip hints for VF */
|
||||
else if (c->plan->flags & HB_SUBSET_FLAGS_NO_HINTING)
|
||||
{
|
||||
hb_blob_t* blob = hb_face_reference_table (c->plan->source, HB_TAG ('f','v','a','r'));
|
||||
bool has_fvar = (blob != hb_blob_get_empty ());
|
||||
hb_blob_destroy (blob);
|
||||
|
||||
bool strip = !has_fvar;
|
||||
/* special case: strip hints when a VF has no GDEF varstore after
|
||||
* subsetting*/
|
||||
if (has_fvar && !c->plan->has_gdef_varstore)
|
||||
strip = true;
|
||||
new_format = valueFormat.get_effective_format (values.arrayZ,
|
||||
strip, /* strip hints */
|
||||
true, /* strip empty */
|
||||
this, nullptr);
|
||||
}
|
||||
|
||||
auto it =
|
||||
+ hb_iter (intersection)
|
||||
| hb_map_retains_sorting (glyph_map)
|
||||
| hb_zip (hb_repeat (values.as_array (valueFormat.get_len ())))
|
||||
;
|
||||
|
||||
bool ret = bool (it);
|
||||
SinglePos_serialize (c->serializer, this, it, &c->plan->layout_variation_idx_delta_map, new_format);
|
||||
return_trace (ret);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* OT_LAYOUT_GPOS_SINGLEPOSFORMAT1_HH */
|
||||
@@ -0,0 +1,213 @@
|
||||
#ifndef OT_LAYOUT_GPOS_SINGLEPOSFORMAT2_HH
|
||||
#define OT_LAYOUT_GPOS_SINGLEPOSFORMAT2_HH
|
||||
|
||||
#include "Common.hh"
|
||||
|
||||
namespace OT {
|
||||
namespace Layout {
|
||||
namespace GPOS_impl {
|
||||
|
||||
struct SinglePosFormat2 : ValueBase
|
||||
{
|
||||
protected:
|
||||
HBUINT16 format; /* Format identifier--format = 2 */
|
||||
Offset16To<Coverage>
|
||||
coverage; /* Offset to Coverage table--from
|
||||
* beginning of subtable */
|
||||
ValueFormat valueFormat; /* Defines the types of data in the
|
||||
* ValueRecord */
|
||||
HBUINT16 valueCount; /* Number of ValueRecords */
|
||||
ValueRecord values; /* Array of ValueRecords--positioning
|
||||
* values applied to glyphs */
|
||||
public:
|
||||
DEFINE_SIZE_ARRAY (8, values);
|
||||
|
||||
bool sanitize (hb_sanitize_context_t *c) const
|
||||
{
|
||||
TRACE_SANITIZE (this);
|
||||
return_trace (c->check_struct (this) &&
|
||||
coverage.sanitize (c, this) &&
|
||||
valueFormat.sanitize_values (c, this, values, valueCount));
|
||||
}
|
||||
|
||||
bool intersects (const hb_set_t *glyphs) const
|
||||
{ return (this+coverage).intersects (glyphs); }
|
||||
|
||||
void closure_lookups (hb_closure_lookups_context_t *c) const {}
|
||||
void collect_variation_indices (hb_collect_variation_indices_context_t *c) const
|
||||
{
|
||||
if (!valueFormat.has_device ()) return;
|
||||
|
||||
auto it =
|
||||
+ hb_zip (this+coverage, hb_range ((unsigned) valueCount))
|
||||
| hb_filter (c->glyph_set, hb_first)
|
||||
;
|
||||
|
||||
if (!it) return;
|
||||
|
||||
unsigned sub_length = valueFormat.get_len ();
|
||||
const hb_array_t<const Value> values_array = values.as_array (valueCount * sub_length);
|
||||
|
||||
for (unsigned i : + it
|
||||
| hb_map (hb_second))
|
||||
valueFormat.collect_variation_indices (c, this, values_array.sub_array (i * sub_length, sub_length));
|
||||
|
||||
}
|
||||
|
||||
void collect_glyphs (hb_collect_glyphs_context_t *c) const
|
||||
{ if (unlikely (!(this+coverage).collect_coverage (c->input))) return; }
|
||||
|
||||
const Coverage &get_coverage () const { return this+coverage; }
|
||||
|
||||
ValueFormat get_value_format () const { return valueFormat; }
|
||||
|
||||
bool apply (hb_ot_apply_context_t *c) const
|
||||
{
|
||||
TRACE_APPLY (this);
|
||||
hb_buffer_t *buffer = c->buffer;
|
||||
unsigned int index = (this+coverage).get_coverage (buffer->cur().codepoint);
|
||||
if (index == NOT_COVERED) return_trace (false);
|
||||
|
||||
if (unlikely (index >= valueCount)) return_trace (false);
|
||||
|
||||
if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
|
||||
{
|
||||
c->buffer->message (c->font,
|
||||
"positioning glyph at %u",
|
||||
c->buffer->idx);
|
||||
}
|
||||
|
||||
valueFormat.apply_value (c, this,
|
||||
&values[index * valueFormat.get_len ()],
|
||||
buffer->cur_pos());
|
||||
|
||||
if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
|
||||
{
|
||||
c->buffer->message (c->font,
|
||||
"positioned glyph at %u",
|
||||
c->buffer->idx);
|
||||
}
|
||||
|
||||
buffer->idx++;
|
||||
return_trace (true);
|
||||
}
|
||||
|
||||
bool
|
||||
position_single (hb_font_t *font,
|
||||
hb_blob_t *table_blob,
|
||||
hb_direction_t direction,
|
||||
hb_codepoint_t gid,
|
||||
hb_glyph_position_t &pos) const
|
||||
{
|
||||
unsigned int index = (this+coverage).get_coverage (gid);
|
||||
if (likely (index == NOT_COVERED)) return false;
|
||||
if (unlikely (index >= valueCount)) return false;
|
||||
|
||||
/* This is ugly... */
|
||||
hb_buffer_t buffer {};
|
||||
buffer.props.direction = direction;
|
||||
OT::hb_ot_apply_context_t c (1, font, &buffer, table_blob);
|
||||
|
||||
valueFormat.apply_value (&c, this,
|
||||
&values[index * valueFormat.get_len ()],
|
||||
pos);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
template<typename Iterator,
|
||||
typename SrcLookup,
|
||||
hb_requires (hb_is_iterator (Iterator))>
|
||||
void serialize (hb_serialize_context_t *c,
|
||||
const SrcLookup *src,
|
||||
Iterator it,
|
||||
ValueFormat newFormat,
|
||||
const hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *layout_variation_idx_delta_map)
|
||||
{
|
||||
auto out = c->extend_min (this);
|
||||
if (unlikely (!out)) return;
|
||||
if (unlikely (!c->check_assign (valueFormat, newFormat, HB_SERIALIZE_ERROR_INT_OVERFLOW))) return;
|
||||
if (unlikely (!c->check_assign (valueCount, it.len (), HB_SERIALIZE_ERROR_ARRAY_OVERFLOW))) return;
|
||||
|
||||
+ it
|
||||
| hb_map (hb_second)
|
||||
| hb_apply ([&] (hb_array_t<const Value> _)
|
||||
{ src->get_value_format ().copy_values (c, newFormat, src, &_, layout_variation_idx_delta_map); })
|
||||
;
|
||||
|
||||
auto glyphs =
|
||||
+ it
|
||||
| hb_map_retains_sorting (hb_first)
|
||||
;
|
||||
|
||||
coverage.serialize_serialize (c, glyphs);
|
||||
}
|
||||
|
||||
template<typename Iterator,
|
||||
hb_requires (hb_is_iterator (Iterator))>
|
||||
unsigned compute_effective_format (const hb_face_t *face,
|
||||
Iterator it,
|
||||
bool is_instancing, bool strip_hints,
|
||||
bool has_gdef_varstore,
|
||||
const hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *varidx_delta_map) const
|
||||
{
|
||||
hb_blob_t* blob = hb_face_reference_table (face, HB_TAG ('f','v','a','r'));
|
||||
bool has_fvar = (blob != hb_blob_get_empty ());
|
||||
hb_blob_destroy (blob);
|
||||
|
||||
unsigned new_format = 0;
|
||||
if (is_instancing)
|
||||
{
|
||||
new_format = new_format | valueFormat.get_effective_format (+ it | hb_map (hb_second), false, false, this, varidx_delta_map);
|
||||
}
|
||||
/* do not strip hints for VF */
|
||||
else if (strip_hints)
|
||||
{
|
||||
bool strip = !has_fvar;
|
||||
if (has_fvar && !has_gdef_varstore)
|
||||
strip = true;
|
||||
new_format = new_format | valueFormat.get_effective_format (+ it | hb_map (hb_second), strip, true, this, nullptr);
|
||||
}
|
||||
else
|
||||
new_format = valueFormat;
|
||||
|
||||
return new_format;
|
||||
}
|
||||
|
||||
bool subset (hb_subset_context_t *c) const
|
||||
{
|
||||
TRACE_SUBSET (this);
|
||||
const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
|
||||
const hb_map_t &glyph_map = *c->plan->glyph_map;
|
||||
|
||||
unsigned sub_length = valueFormat.get_len ();
|
||||
auto values_array = values.as_array (valueCount * sub_length);
|
||||
|
||||
auto it =
|
||||
+ hb_zip (this+coverage, hb_range ((unsigned) valueCount))
|
||||
| hb_filter (glyphset, hb_first)
|
||||
| hb_map_retains_sorting ([&] (const hb_pair_t<hb_codepoint_t, unsigned>& _)
|
||||
{
|
||||
return hb_pair (glyph_map[_.first],
|
||||
values_array.sub_array (_.second * sub_length,
|
||||
sub_length));
|
||||
})
|
||||
;
|
||||
|
||||
unsigned new_format = compute_effective_format (c->plan->source, it,
|
||||
bool (c->plan->normalized_coords),
|
||||
bool (c->plan->flags & HB_SUBSET_FLAGS_NO_HINTING),
|
||||
c->plan->has_gdef_varstore,
|
||||
&c->plan->layout_variation_idx_delta_map);
|
||||
bool ret = bool (it);
|
||||
SinglePos_serialize (c->serializer, this, it, &c->plan->layout_variation_idx_delta_map, new_format);
|
||||
return_trace (ret);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* OT_LAYOUT_GPOS_SINGLEPOSFORMAT2_HH */
|
||||
+446
@@ -0,0 +1,446 @@
|
||||
#ifndef OT_LAYOUT_GPOS_VALUEFORMAT_HH
|
||||
#define OT_LAYOUT_GPOS_VALUEFORMAT_HH
|
||||
|
||||
#include "../../../hb-ot-layout-gsubgpos.hh"
|
||||
|
||||
namespace OT {
|
||||
namespace Layout {
|
||||
namespace GPOS_impl {
|
||||
|
||||
typedef HBUINT16 Value;
|
||||
|
||||
struct ValueBase {}; // Dummy base class tag for OffsetTo<Value> bases.
|
||||
|
||||
typedef UnsizedArrayOf<Value> ValueRecord;
|
||||
|
||||
struct ValueFormat : HBUINT16
|
||||
{
|
||||
enum Flags {
|
||||
xPlacement = 0x0001u, /* Includes horizontal adjustment for placement */
|
||||
yPlacement = 0x0002u, /* Includes vertical adjustment for placement */
|
||||
xAdvance = 0x0004u, /* Includes horizontal adjustment for advance */
|
||||
yAdvance = 0x0008u, /* Includes vertical adjustment for advance */
|
||||
xPlaDevice = 0x0010u, /* Includes horizontal Device table for placement */
|
||||
yPlaDevice = 0x0020u, /* Includes vertical Device table for placement */
|
||||
xAdvDevice = 0x0040u, /* Includes horizontal Device table for advance */
|
||||
yAdvDevice = 0x0080u, /* Includes vertical Device table for advance */
|
||||
ignored = 0x0F00u, /* Was used in TrueType Open for MM fonts */
|
||||
reserved = 0xF000u, /* For future use */
|
||||
|
||||
devices = 0x00F0u /* Mask for having any Device table */
|
||||
};
|
||||
|
||||
/* All fields are options. Only those available advance the value pointer. */
|
||||
#if 0
|
||||
HBINT16 xPlacement; /* Horizontal adjustment for
|
||||
* placement--in design units */
|
||||
HBINT16 yPlacement; /* Vertical adjustment for
|
||||
* placement--in design units */
|
||||
HBINT16 xAdvance; /* Horizontal adjustment for
|
||||
* advance--in design units (only used
|
||||
* for horizontal writing) */
|
||||
HBINT16 yAdvance; /* Vertical adjustment for advance--in
|
||||
* design units (only used for vertical
|
||||
* writing) */
|
||||
Offset16To<Device> xPlaDevice; /* Offset to Device table for
|
||||
* horizontal placement--measured from
|
||||
* beginning of PosTable (may be NULL) */
|
||||
Offset16To<Device> yPlaDevice; /* Offset to Device table for vertical
|
||||
* placement--measured from beginning
|
||||
* of PosTable (may be NULL) */
|
||||
Offset16To<Device> xAdvDevice; /* Offset to Device table for
|
||||
* horizontal advance--measured from
|
||||
* beginning of PosTable (may be NULL) */
|
||||
Offset16To<Device> yAdvDevice; /* Offset to Device table for vertical
|
||||
* advance--measured from beginning of
|
||||
* PosTable (may be NULL) */
|
||||
#endif
|
||||
|
||||
NumType& operator = (uint16_t i) { v = i; return *this; }
|
||||
|
||||
// Note: spec says skip 2 bytes per bit in the valueformat. But reports
|
||||
// from Microsoft developers indicate that only the fields that are
|
||||
// currently defined are counted. We don't expect any new fields to
|
||||
// be added to ValueFormat. As such, we use the faster hb_popcount8
|
||||
// that only processes the lowest 8 bits.
|
||||
unsigned int get_len () const { return hb_popcount8 ((uint8_t) *this); }
|
||||
size_t get_size () const { return get_len () * Value::static_size; }
|
||||
|
||||
hb_vector_t<unsigned> get_device_table_indices () const {
|
||||
unsigned i = 0;
|
||||
hb_vector_t<unsigned> result;
|
||||
unsigned format = *this;
|
||||
|
||||
if (format & xPlacement) i++;
|
||||
if (format & yPlacement) i++;
|
||||
if (format & xAdvance) i++;
|
||||
if (format & yAdvance) i++;
|
||||
|
||||
if (format & xPlaDevice) result.push (i++);
|
||||
if (format & yPlaDevice) result.push (i++);
|
||||
if (format & xAdvDevice) result.push (i++);
|
||||
if (format & yAdvDevice) result.push (i++);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool apply_value (hb_ot_apply_context_t *c,
|
||||
const ValueBase *base,
|
||||
const Value *values,
|
||||
hb_glyph_position_t &glyph_pos) const
|
||||
{
|
||||
bool ret = false;
|
||||
unsigned int format = *this;
|
||||
if (!format) return ret;
|
||||
|
||||
hb_font_t *font = c->font;
|
||||
bool horizontal =
|
||||
#ifndef HB_NO_VERTICAL
|
||||
HB_DIRECTION_IS_HORIZONTAL (c->direction)
|
||||
#else
|
||||
true
|
||||
#endif
|
||||
;
|
||||
|
||||
if (format & xPlacement) glyph_pos.x_offset += font->em_scale_x (get_short (values++, &ret));
|
||||
if (format & yPlacement) glyph_pos.y_offset += font->em_scale_y (get_short (values++, &ret));
|
||||
if (format & xAdvance) {
|
||||
if (likely (horizontal)) glyph_pos.x_advance += font->em_scale_x (get_short (values, &ret));
|
||||
values++;
|
||||
}
|
||||
/* y_advance values grow downward but font-space grows upward, hence negation */
|
||||
if (format & yAdvance) {
|
||||
if (unlikely (!horizontal)) glyph_pos.y_advance -= font->em_scale_y (get_short (values, &ret));
|
||||
values++;
|
||||
}
|
||||
|
||||
if (!has_device ()) return ret;
|
||||
|
||||
bool use_x_device = font->x_ppem || font->has_nonzero_coords;
|
||||
bool use_y_device = font->y_ppem || font->has_nonzero_coords;
|
||||
|
||||
if (!use_x_device && !use_y_device) return ret;
|
||||
|
||||
const ItemVariationStore &store = c->var_store;
|
||||
auto *cache = c->var_store_cache;
|
||||
|
||||
/* pixel -> fractional pixel */
|
||||
if (format & xPlaDevice)
|
||||
{
|
||||
if (use_x_device) glyph_pos.x_offset += get_device (values, &ret, base, c->sanitizer).get_x_delta (font, store, cache);
|
||||
values++;
|
||||
}
|
||||
if (format & yPlaDevice)
|
||||
{
|
||||
if (use_y_device) glyph_pos.y_offset += get_device (values, &ret, base, c->sanitizer).get_y_delta (font, store, cache);
|
||||
values++;
|
||||
}
|
||||
if (format & xAdvDevice)
|
||||
{
|
||||
if (horizontal && use_x_device) glyph_pos.x_advance += get_device (values, &ret, base, c->sanitizer).get_x_delta (font, store, cache);
|
||||
values++;
|
||||
}
|
||||
if (format & yAdvDevice)
|
||||
{
|
||||
/* y_advance values grow downward but font-space grows upward, hence negation */
|
||||
if (!horizontal && use_y_device) glyph_pos.y_advance -= get_device (values, &ret, base, c->sanitizer).get_y_delta (font, store, cache);
|
||||
values++;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
unsigned int get_effective_format (const Value *values, bool strip_hints, bool strip_empty, const ValueBase *base,
|
||||
const hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *varidx_delta_map) const
|
||||
{
|
||||
unsigned int format = *this;
|
||||
for (unsigned flag = xPlacement; flag <= yAdvDevice; flag = flag << 1) {
|
||||
if (format & flag)
|
||||
{
|
||||
if (strip_hints && flag >= xPlaDevice)
|
||||
{
|
||||
format = format & ~flag;
|
||||
values++;
|
||||
continue;
|
||||
}
|
||||
if (varidx_delta_map && flag >= xPlaDevice)
|
||||
{
|
||||
update_var_flag (values++, (Flags) flag, &format, base, varidx_delta_map);
|
||||
continue;
|
||||
}
|
||||
/* do not strip empty when instancing, cause we don't know whether the new
|
||||
* default value is 0 or not */
|
||||
if (strip_empty) should_drop (*values, (Flags) flag, &format);
|
||||
values++;
|
||||
}
|
||||
}
|
||||
|
||||
return format;
|
||||
}
|
||||
|
||||
template<typename Iterator,
|
||||
hb_requires (hb_is_iterator (Iterator))>
|
||||
unsigned int get_effective_format (Iterator it, bool strip_hints, bool strip_empty, const ValueBase *base,
|
||||
const hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *varidx_delta_map) const {
|
||||
unsigned int new_format = 0;
|
||||
|
||||
for (const hb_array_t<const Value>& values : it)
|
||||
new_format = new_format | get_effective_format (&values, strip_hints, strip_empty, base, varidx_delta_map);
|
||||
|
||||
return new_format;
|
||||
}
|
||||
|
||||
void copy_values (hb_serialize_context_t *c,
|
||||
unsigned int new_format,
|
||||
const ValueBase *base,
|
||||
const Value *values,
|
||||
const hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *layout_variation_idx_delta_map) const
|
||||
{
|
||||
unsigned int format = *this;
|
||||
if (!format) return;
|
||||
|
||||
HBINT16 *x_placement = nullptr, *y_placement = nullptr, *x_adv = nullptr, *y_adv = nullptr;
|
||||
if (format & xPlacement) x_placement = copy_value (c, new_format, xPlacement, *values++);
|
||||
if (format & yPlacement) y_placement = copy_value (c, new_format, yPlacement, *values++);
|
||||
if (format & xAdvance) x_adv = copy_value (c, new_format, xAdvance, *values++);
|
||||
if (format & yAdvance) y_adv = copy_value (c, new_format, yAdvance, *values++);
|
||||
|
||||
if (!has_device ())
|
||||
return;
|
||||
|
||||
if (format & xPlaDevice)
|
||||
{
|
||||
add_delta_to_value (x_placement, base, values, layout_variation_idx_delta_map);
|
||||
copy_device (c, base, values++, layout_variation_idx_delta_map, new_format, xPlaDevice);
|
||||
}
|
||||
|
||||
if (format & yPlaDevice)
|
||||
{
|
||||
add_delta_to_value (y_placement, base, values, layout_variation_idx_delta_map);
|
||||
copy_device (c, base, values++, layout_variation_idx_delta_map, new_format, yPlaDevice);
|
||||
}
|
||||
|
||||
if (format & xAdvDevice)
|
||||
{
|
||||
add_delta_to_value (x_adv, base, values, layout_variation_idx_delta_map);
|
||||
copy_device (c, base, values++, layout_variation_idx_delta_map, new_format, xAdvDevice);
|
||||
}
|
||||
|
||||
if (format & yAdvDevice)
|
||||
{
|
||||
add_delta_to_value (y_adv, base, values, layout_variation_idx_delta_map);
|
||||
copy_device (c, base, values++, layout_variation_idx_delta_map, new_format, yAdvDevice);
|
||||
}
|
||||
}
|
||||
|
||||
HBINT16* copy_value (hb_serialize_context_t *c,
|
||||
unsigned int new_format,
|
||||
Flags flag,
|
||||
Value value) const
|
||||
{
|
||||
// Filter by new format.
|
||||
if (!(new_format & flag)) return nullptr;
|
||||
return reinterpret_cast<HBINT16 *> (c->copy (value));
|
||||
}
|
||||
|
||||
void collect_variation_indices (hb_collect_variation_indices_context_t *c,
|
||||
const ValueBase *base,
|
||||
const hb_array_t<const Value>& values) const
|
||||
{
|
||||
unsigned format = *this;
|
||||
unsigned i = 0;
|
||||
if (format & xPlacement) i++;
|
||||
if (format & yPlacement) i++;
|
||||
if (format & xAdvance) i++;
|
||||
if (format & yAdvance) i++;
|
||||
if (format & xPlaDevice)
|
||||
{
|
||||
(base + get_device (&(values[i]))).collect_variation_indices (c);
|
||||
i++;
|
||||
}
|
||||
|
||||
if (format & ValueFormat::yPlaDevice)
|
||||
{
|
||||
(base + get_device (&(values[i]))).collect_variation_indices (c);
|
||||
i++;
|
||||
}
|
||||
|
||||
if (format & ValueFormat::xAdvDevice)
|
||||
{
|
||||
(base + get_device (&(values[i]))).collect_variation_indices (c);
|
||||
i++;
|
||||
}
|
||||
|
||||
if (format & ValueFormat::yAdvDevice)
|
||||
{
|
||||
(base + get_device (&(values[i]))).collect_variation_indices (c);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
bool sanitize_value_devices (hb_sanitize_context_t *c, const ValueBase *base, const Value *values) const
|
||||
{
|
||||
unsigned int format = *this;
|
||||
|
||||
if (format & xPlacement) values++;
|
||||
if (format & yPlacement) values++;
|
||||
if (format & xAdvance) values++;
|
||||
if (format & yAdvance) values++;
|
||||
|
||||
if ((format & xPlaDevice) && !get_device (values++).sanitize (c, base)) return false;
|
||||
if ((format & yPlaDevice) && !get_device (values++).sanitize (c, base)) return false;
|
||||
if ((format & xAdvDevice) && !get_device (values++).sanitize (c, base)) return false;
|
||||
if ((format & yAdvDevice) && !get_device (values++).sanitize (c, base)) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline Offset16To<Device, ValueBase>& get_device (Value* value)
|
||||
{
|
||||
return *static_cast<Offset16To<Device, ValueBase> *> (value);
|
||||
}
|
||||
static inline const Offset16To<Device, ValueBase>& get_device (const Value* value)
|
||||
{
|
||||
return *static_cast<const Offset16To<Device, ValueBase> *> (value);
|
||||
}
|
||||
static inline const Device& get_device (const Value* value,
|
||||
bool *worked,
|
||||
const ValueBase *base,
|
||||
hb_sanitize_context_t &c)
|
||||
{
|
||||
if (worked) *worked |= bool (*value);
|
||||
auto &offset = *static_cast<const Offset16To<Device> *> (value);
|
||||
|
||||
if (unlikely (!offset.sanitize (&c, base)))
|
||||
return Null(Device);
|
||||
hb_barrier ();
|
||||
|
||||
return base + offset;
|
||||
}
|
||||
|
||||
void add_delta_to_value (HBINT16 *value,
|
||||
const ValueBase *base,
|
||||
const Value *src_value,
|
||||
const hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *layout_variation_idx_delta_map) const
|
||||
{
|
||||
if (!value) return;
|
||||
unsigned varidx = (base + get_device (src_value)).get_variation_index ();
|
||||
hb_pair_t<unsigned, int> *varidx_delta;
|
||||
if (!layout_variation_idx_delta_map->has (varidx, &varidx_delta)) return;
|
||||
|
||||
*value += hb_second (*varidx_delta);
|
||||
}
|
||||
|
||||
bool copy_device (hb_serialize_context_t *c,
|
||||
const ValueBase *base,
|
||||
const Value *src_value,
|
||||
const hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *layout_variation_idx_delta_map,
|
||||
unsigned int new_format, Flags flag) const
|
||||
{
|
||||
// Filter by new format.
|
||||
if (!(new_format & flag)) return true;
|
||||
|
||||
Value *dst_value = c->copy (*src_value);
|
||||
|
||||
if (!dst_value) return false;
|
||||
if (*dst_value == 0) return true;
|
||||
|
||||
*dst_value = 0;
|
||||
c->push ();
|
||||
if ((base + get_device (src_value)).copy (c, layout_variation_idx_delta_map))
|
||||
{
|
||||
c->add_link (*dst_value, c->pop_pack ());
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
c->pop_discard ();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static inline const HBINT16& get_short (const Value* value, bool *worked=nullptr)
|
||||
{
|
||||
if (worked) *worked |= bool (*value);
|
||||
return *reinterpret_cast<const HBINT16 *> (value);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
bool has_device () const
|
||||
{
|
||||
unsigned int format = *this;
|
||||
return (format & devices) != 0;
|
||||
}
|
||||
|
||||
bool sanitize_value (hb_sanitize_context_t *c, const ValueBase *base, const Value *values) const
|
||||
{
|
||||
TRACE_SANITIZE (this);
|
||||
|
||||
if (unlikely (!c->check_range (values, get_size ()))) return_trace (false);
|
||||
|
||||
if (c->lazy_some_gpos)
|
||||
return_trace (true);
|
||||
|
||||
return_trace (!has_device () || sanitize_value_devices (c, base, values));
|
||||
}
|
||||
|
||||
bool sanitize_values (hb_sanitize_context_t *c, const ValueBase *base, const Value *values, unsigned int count) const
|
||||
{
|
||||
TRACE_SANITIZE (this);
|
||||
unsigned size = get_size ();
|
||||
|
||||
if (!c->check_range (values, count, size)) return_trace (false);
|
||||
|
||||
if (c->lazy_some_gpos)
|
||||
return_trace (true);
|
||||
|
||||
hb_barrier ();
|
||||
return_trace (sanitize_values_stride_unsafe (c, base, values, count, size));
|
||||
}
|
||||
|
||||
/* Just sanitize referenced Device tables. Doesn't check the values themselves. */
|
||||
bool sanitize_values_stride_unsafe (hb_sanitize_context_t *c, const ValueBase *base, const Value *values, unsigned int count, unsigned int stride) const
|
||||
{
|
||||
TRACE_SANITIZE (this);
|
||||
|
||||
if (!has_device ()) return_trace (true);
|
||||
|
||||
for (unsigned int i = 0; i < count; i++) {
|
||||
if (!sanitize_value_devices (c, base, values))
|
||||
return_trace (false);
|
||||
values = &StructAtOffset<const Value> (values, stride);
|
||||
}
|
||||
|
||||
return_trace (true);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void should_drop (Value value, Flags flag, unsigned int* format) const
|
||||
{
|
||||
if (value) return;
|
||||
*format = *format & ~flag;
|
||||
}
|
||||
|
||||
void update_var_flag (const Value* value, Flags flag,
|
||||
unsigned int* format, const ValueBase *base,
|
||||
const hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *varidx_delta_map) const
|
||||
{
|
||||
if (*value)
|
||||
{
|
||||
unsigned varidx = (base + get_device (value)).get_variation_index ();
|
||||
hb_pair_t<unsigned, int> *varidx_delta;
|
||||
if (varidx_delta_map->has (varidx, &varidx_delta) &&
|
||||
varidx_delta->first != HB_OT_LAYOUT_NO_VARIATIONS_INDEX)
|
||||
return;
|
||||
}
|
||||
*format = *format & ~flag;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // #ifndef OT_LAYOUT_GPOS_VALUEFORMAT_HH
|
||||
@@ -0,0 +1,139 @@
|
||||
#ifndef OT_LAYOUT_GSUB_ALTERNATESET_HH
|
||||
#define OT_LAYOUT_GSUB_ALTERNATESET_HH
|
||||
|
||||
#include "Common.hh"
|
||||
|
||||
namespace OT {
|
||||
namespace Layout {
|
||||
namespace GSUB_impl {
|
||||
|
||||
template <typename Types>
|
||||
struct AlternateSet
|
||||
{
|
||||
protected:
|
||||
Array16Of<typename Types::HBGlyphID>
|
||||
alternates; /* Array of alternate GlyphIDs--in
|
||||
* arbitrary order */
|
||||
public:
|
||||
DEFINE_SIZE_ARRAY (2, alternates);
|
||||
|
||||
bool sanitize (hb_sanitize_context_t *c) const
|
||||
{
|
||||
TRACE_SANITIZE (this);
|
||||
return_trace (alternates.sanitize (c));
|
||||
}
|
||||
|
||||
bool intersects (const hb_set_t *glyphs) const
|
||||
{ return hb_any (alternates, glyphs); }
|
||||
|
||||
void closure (hb_closure_context_t *c) const
|
||||
{ c->output->add_array (alternates.arrayZ, alternates.len); }
|
||||
|
||||
void collect_glyphs (hb_collect_glyphs_context_t *c) const
|
||||
{ c->output->add_array (alternates.arrayZ, alternates.len); }
|
||||
|
||||
bool apply (hb_ot_apply_context_t *c) const
|
||||
{
|
||||
TRACE_APPLY (this);
|
||||
unsigned int count = alternates.len;
|
||||
|
||||
if (unlikely (!count)) return_trace (false);
|
||||
|
||||
hb_mask_t glyph_mask = c->buffer->cur().mask;
|
||||
hb_mask_t lookup_mask = c->lookup_mask;
|
||||
|
||||
/* Note: This breaks badly if two features enabled this lookup together. */
|
||||
unsigned int shift = hb_ctz (lookup_mask);
|
||||
unsigned int alt_index = ((lookup_mask & glyph_mask) >> shift);
|
||||
|
||||
/* If alt_index is MAX_VALUE, randomize feature if it is the rand feature. */
|
||||
if (alt_index == HB_OT_MAP_MAX_VALUE && c->random)
|
||||
{
|
||||
/* Maybe we can do better than unsafe-to-break all; but since we are
|
||||
* changing random state, it would be hard to track that. Good 'nough. */
|
||||
c->buffer->unsafe_to_break (0, c->buffer->len);
|
||||
alt_index = c->random_number () % count + 1;
|
||||
}
|
||||
|
||||
if (unlikely (alt_index > count || alt_index == 0)) return_trace (false);
|
||||
|
||||
if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
|
||||
{
|
||||
c->buffer->sync_so_far ();
|
||||
c->buffer->message (c->font,
|
||||
"replacing glyph at %u (alternate substitution)",
|
||||
c->buffer->idx);
|
||||
}
|
||||
|
||||
c->replace_glyph (alternates[alt_index - 1]);
|
||||
|
||||
if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
|
||||
{
|
||||
c->buffer->message (c->font,
|
||||
"replaced glyph at %u (alternate substitution)",
|
||||
c->buffer->idx - 1u);
|
||||
}
|
||||
|
||||
return_trace (true);
|
||||
}
|
||||
|
||||
unsigned
|
||||
get_alternates (unsigned start_offset,
|
||||
unsigned *alternate_count /* IN/OUT. May be NULL. */,
|
||||
hb_codepoint_t *alternate_glyphs /* OUT. May be NULL. */) const
|
||||
{
|
||||
if (alternates.len && alternate_count)
|
||||
{
|
||||
+ alternates.as_array ().sub_array (start_offset, alternate_count)
|
||||
| hb_sink (hb_array (alternate_glyphs, *alternate_count))
|
||||
;
|
||||
}
|
||||
return alternates.len;
|
||||
}
|
||||
|
||||
void
|
||||
collect_alternates (hb_codepoint_t gid,
|
||||
hb_map_t *alternate_count /* IN/OUT */,
|
||||
hb_map_t *alternate_glyphs /* IN/OUT */) const
|
||||
{
|
||||
+ hb_enumerate (alternates)
|
||||
| hb_map ([gid] (hb_pair_t<unsigned, hb_codepoint_t> _) { return hb_pair (gid + (_.first << 24), _.second); })
|
||||
| hb_apply ([&] (const hb_pair_t<hb_codepoint_t, hb_codepoint_t> &p) -> void
|
||||
{ _hb_collect_glyph_alternates_add (p.first, p.second,
|
||||
alternate_count, alternate_glyphs); })
|
||||
;
|
||||
}
|
||||
|
||||
template <typename Iterator,
|
||||
hb_requires (hb_is_source_of (Iterator, hb_codepoint_t))>
|
||||
bool serialize (hb_serialize_context_t *c,
|
||||
Iterator alts)
|
||||
{
|
||||
TRACE_SERIALIZE (this);
|
||||
return_trace (alternates.serialize (c, alts));
|
||||
}
|
||||
|
||||
bool subset (hb_subset_context_t *c) const
|
||||
{
|
||||
TRACE_SUBSET (this);
|
||||
const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
|
||||
const hb_map_t &glyph_map = *c->plan->glyph_map;
|
||||
|
||||
auto it =
|
||||
+ hb_iter (alternates)
|
||||
| hb_filter (glyphset)
|
||||
| hb_map (glyph_map)
|
||||
;
|
||||
|
||||
auto *out = c->serializer->start_embed (*this);
|
||||
return_trace (out->serialize (c->serializer, it) &&
|
||||
out->alternates);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif /* OT_LAYOUT_GSUB_ALTERNATESET_HH */
|
||||
@@ -0,0 +1,62 @@
|
||||
#ifndef OT_LAYOUT_GSUB_ALTERNATESUBST_HH
|
||||
#define OT_LAYOUT_GSUB_ALTERNATESUBST_HH
|
||||
|
||||
#include "AlternateSubstFormat1.hh"
|
||||
#include "Common.hh"
|
||||
|
||||
namespace OT {
|
||||
namespace Layout {
|
||||
namespace GSUB_impl {
|
||||
|
||||
struct AlternateSubst
|
||||
{
|
||||
protected:
|
||||
union {
|
||||
struct { HBUINT16 v; } format; /* Format identifier */
|
||||
AlternateSubstFormat1_2<SmallTypes> format1;
|
||||
#ifndef HB_NO_BEYOND_64K
|
||||
AlternateSubstFormat1_2<MediumTypes> format2;
|
||||
#endif
|
||||
} u;
|
||||
public:
|
||||
|
||||
template <typename context_t, typename ...Ts>
|
||||
typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
|
||||
{
|
||||
if (unlikely (!c->may_dispatch (this, &u.format.v))) return c->no_dispatch_return_value ();
|
||||
TRACE_DISPATCH (this, u.format.v);
|
||||
switch (u.format.v) {
|
||||
case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...));
|
||||
#ifndef HB_NO_BEYOND_64K
|
||||
case 2: return_trace (c->dispatch (u.format2, std::forward<Ts> (ds)...));
|
||||
#endif
|
||||
default:return_trace (c->default_return_value ());
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO This function is unused and not updated to 24bit GIDs. Should be done by using
|
||||
* iterators. While at it perhaps using iterator of arrays of hb_codepoint_t instead. */
|
||||
bool serialize (hb_serialize_context_t *c,
|
||||
hb_sorted_array_t<const HBGlyphID16> glyphs,
|
||||
hb_array_t<const unsigned int> alternate_len_list,
|
||||
hb_array_t<const HBGlyphID16> alternate_glyphs_list)
|
||||
{
|
||||
TRACE_SERIALIZE (this);
|
||||
if (unlikely (!c->extend_min (u.format.v))) return_trace (false);
|
||||
unsigned int format = 1;
|
||||
u.format.v = format;
|
||||
switch (u.format.v) {
|
||||
case 1: return_trace (u.format1.serialize (c, glyphs, alternate_len_list, alternate_glyphs_list));
|
||||
default:return_trace (false);
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO subset() should choose format. */
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* OT_LAYOUT_GSUB_ALTERNATESUBST_HH */
|
||||
@@ -0,0 +1,141 @@
|
||||
#ifndef OT_LAYOUT_GSUB_ALTERNATESUBSTFORMAT1_HH
|
||||
#define OT_LAYOUT_GSUB_ALTERNATESUBSTFORMAT1_HH
|
||||
|
||||
#include "AlternateSet.hh"
|
||||
#include "Common.hh"
|
||||
|
||||
namespace OT {
|
||||
namespace Layout {
|
||||
namespace GSUB_impl {
|
||||
|
||||
template <typename Types>
|
||||
struct AlternateSubstFormat1_2
|
||||
{
|
||||
protected:
|
||||
HBUINT16 format; /* Format identifier--format = 1 */
|
||||
typename Types::template OffsetTo<Coverage>
|
||||
coverage; /* Offset to Coverage table--from
|
||||
* beginning of Substitution table */
|
||||
Array16Of<typename Types::template OffsetTo<AlternateSet<Types>>>
|
||||
alternateSet; /* Array of AlternateSet tables
|
||||
* ordered by Coverage Index */
|
||||
public:
|
||||
DEFINE_SIZE_ARRAY (2 + 2 * Types::size, alternateSet);
|
||||
|
||||
bool sanitize (hb_sanitize_context_t *c) const
|
||||
{
|
||||
TRACE_SANITIZE (this);
|
||||
return_trace (coverage.sanitize (c, this) && alternateSet.sanitize (c, this));
|
||||
}
|
||||
|
||||
bool intersects (const hb_set_t *glyphs) const
|
||||
{ return (this+coverage).intersects (glyphs); }
|
||||
|
||||
bool may_have_non_1to1 () const
|
||||
{ return false; }
|
||||
|
||||
void closure (hb_closure_context_t *c) const
|
||||
{
|
||||
+ hb_zip (this+coverage, alternateSet)
|
||||
| hb_filter (c->parent_active_glyphs (), hb_first)
|
||||
| hb_map (hb_second)
|
||||
| hb_map (hb_add (this))
|
||||
| hb_apply ([c] (const AlternateSet<Types> &_) { _.closure (c); })
|
||||
;
|
||||
}
|
||||
|
||||
void closure_lookups (hb_closure_lookups_context_t *c) const {}
|
||||
|
||||
void collect_glyphs (hb_collect_glyphs_context_t *c) const
|
||||
{
|
||||
if (unlikely (!(this+coverage).collect_coverage (c->input))) return;
|
||||
+ hb_zip (this+coverage, alternateSet)
|
||||
| hb_map (hb_second)
|
||||
| hb_map (hb_add (this))
|
||||
| hb_apply ([c] (const AlternateSet<Types> &_) { _.collect_glyphs (c); })
|
||||
;
|
||||
}
|
||||
|
||||
const Coverage &get_coverage () const { return this+coverage; }
|
||||
|
||||
bool would_apply (hb_would_apply_context_t *c) const
|
||||
{ return c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED; }
|
||||
|
||||
unsigned
|
||||
get_glyph_alternates (hb_codepoint_t gid,
|
||||
unsigned start_offset,
|
||||
unsigned *alternate_count /* IN/OUT. May be NULL. */,
|
||||
hb_codepoint_t *alternate_glyphs /* OUT. May be NULL. */) const
|
||||
{ return (this+alternateSet[(this+coverage).get_coverage (gid)])
|
||||
.get_alternates (start_offset, alternate_count, alternate_glyphs); }
|
||||
|
||||
void
|
||||
collect_glyph_alternates (hb_map_t *alternate_count /* IN/OUT */,
|
||||
hb_map_t *alternate_glyphs /* IN/OUT */) const
|
||||
{
|
||||
+ hb_iter (alternateSet)
|
||||
| hb_map (hb_add (this))
|
||||
| hb_zip (this+coverage)
|
||||
| hb_apply ([&] (const hb_pair_t<const AlternateSet<Types> &, hb_codepoint_t> _) {
|
||||
_.first.collect_alternates (_.second, alternate_count, alternate_glyphs);
|
||||
})
|
||||
;
|
||||
}
|
||||
|
||||
bool apply (hb_ot_apply_context_t *c) const
|
||||
{
|
||||
TRACE_APPLY (this);
|
||||
|
||||
unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
|
||||
if (index == NOT_COVERED) return_trace (false);
|
||||
|
||||
return_trace ((this+alternateSet[index]).apply (c));
|
||||
}
|
||||
|
||||
bool serialize (hb_serialize_context_t *c,
|
||||
hb_sorted_array_t<const HBGlyphID16> glyphs,
|
||||
hb_array_t<const unsigned int> alternate_len_list,
|
||||
hb_array_t<const HBGlyphID16> alternate_glyphs_list)
|
||||
{
|
||||
TRACE_SERIALIZE (this);
|
||||
if (unlikely (!c->extend_min (this))) return_trace (false);
|
||||
if (unlikely (!alternateSet.serialize (c, glyphs.length))) return_trace (false);
|
||||
for (unsigned int i = 0; i < glyphs.length; i++)
|
||||
{
|
||||
unsigned int alternate_len = alternate_len_list[i];
|
||||
if (unlikely (!alternateSet[i]
|
||||
.serialize_serialize (c, alternate_glyphs_list.sub_array (0, alternate_len))))
|
||||
return_trace (false);
|
||||
alternate_glyphs_list += alternate_len;
|
||||
}
|
||||
return_trace (coverage.serialize_serialize (c, glyphs));
|
||||
}
|
||||
|
||||
bool subset (hb_subset_context_t *c) const
|
||||
{
|
||||
TRACE_SUBSET (this);
|
||||
const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
|
||||
const hb_map_t &glyph_map = *c->plan->glyph_map;
|
||||
|
||||
auto *out = c->serializer->start_embed (*this);
|
||||
if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
|
||||
out->format = format;
|
||||
|
||||
hb_sorted_vector_t<hb_codepoint_t> new_coverage;
|
||||
+ hb_zip (this+coverage, alternateSet)
|
||||
| hb_filter (glyphset, hb_first)
|
||||
| hb_filter (subset_offset_array (c, out->alternateSet, this), hb_second)
|
||||
| hb_map (hb_first)
|
||||
| hb_map (glyph_map)
|
||||
| hb_sink (new_coverage)
|
||||
;
|
||||
out->coverage.serialize_serialize (c->serializer, new_coverage.iter ());
|
||||
return_trace (bool (new_coverage));
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* OT_LAYOUT_GSUB_ALTERNATESUBSTFORMAT1_HH */
|
||||
@@ -0,0 +1,18 @@
|
||||
#ifndef OT_LAYOUT_GSUB_CHAINCONTEXTSUBST_HH
|
||||
#define OT_LAYOUT_GSUB_CHAINCONTEXTSUBST_HH
|
||||
|
||||
// TODO(garretrieger): move to new layout.
|
||||
#include "../../../hb-ot-layout-gsubgpos.hh"
|
||||
#include "Common.hh"
|
||||
|
||||
namespace OT {
|
||||
namespace Layout {
|
||||
namespace GSUB_impl {
|
||||
|
||||
struct ChainContextSubst : ChainContext {};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* OT_LAYOUT_GSUB_CHAINCONTEXTSUBST_HH */
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
#ifndef OT_LAYOUT_GSUB_COMMON_HH
|
||||
#define OT_LAYOUT_GSUB_COMMON_HH
|
||||
|
||||
#include "../../../hb-serialize.hh"
|
||||
#include "../../../hb-ot-layout-gsubgpos.hh"
|
||||
|
||||
namespace OT {
|
||||
namespace Layout {
|
||||
namespace GSUB_impl {
|
||||
|
||||
template<typename Iterator>
|
||||
static void SingleSubst_serialize (hb_serialize_context_t *c,
|
||||
Iterator it);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* OT_LAYOUT_GSUB_COMMON_HH */
|
||||
@@ -0,0 +1,18 @@
|
||||
#ifndef OT_LAYOUT_GSUB_CONTEXTSUBST_HH
|
||||
#define OT_LAYOUT_GSUB_CONTEXTSUBST_HH
|
||||
|
||||
// TODO(garretrieger): move to new layout.
|
||||
#include "../../../hb-ot-layout-gsubgpos.hh"
|
||||
#include "Common.hh"
|
||||
|
||||
namespace OT {
|
||||
namespace Layout {
|
||||
namespace GSUB_impl {
|
||||
|
||||
struct ContextSubst : Context {};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* OT_LAYOUT_GSUB_CONTEXTSUBST_HH */
|
||||
@@ -0,0 +1,22 @@
|
||||
#ifndef OT_LAYOUT_GSUB_EXTENSIONSUBST_HH
|
||||
#define OT_LAYOUT_GSUB_EXTENSIONSUBST_HH
|
||||
|
||||
// TODO(garretrieger): move to new layout.
|
||||
#include "../../../hb-ot-layout-gsubgpos.hh"
|
||||
#include "Common.hh"
|
||||
|
||||
namespace OT {
|
||||
namespace Layout {
|
||||
namespace GSUB_impl {
|
||||
|
||||
struct ExtensionSubst : Extension<ExtensionSubst>
|
||||
{
|
||||
typedef struct SubstLookupSubTable SubTable;
|
||||
bool is_reverse () const;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* OT_LAYOUT_GSUB_EXTENSIONSUBST_HH */
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
#ifndef OT_LAYOUT_GSUB_GSUB_HH
|
||||
#define OT_LAYOUT_GSUB_GSUB_HH
|
||||
|
||||
#include "../../../hb-ot-layout-gsubgpos.hh"
|
||||
#include "Common.hh"
|
||||
#include "SubstLookup.hh"
|
||||
|
||||
namespace OT {
|
||||
|
||||
using Layout::GSUB_impl::SubstLookup;
|
||||
|
||||
namespace Layout {
|
||||
|
||||
/*
|
||||
* GSUB -- Glyph Substitution
|
||||
* https://docs.microsoft.com/en-us/typography/opentype/spec/gsub
|
||||
*/
|
||||
|
||||
struct GSUB : GSUBGPOS
|
||||
{
|
||||
using Lookup = SubstLookup;
|
||||
|
||||
static constexpr hb_tag_t tableTag = HB_OT_TAG_GSUB;
|
||||
|
||||
const SubstLookup& get_lookup (unsigned int i) const
|
||||
{ return static_cast<const SubstLookup &> (GSUBGPOS::get_lookup (i)); }
|
||||
|
||||
bool subset (hb_subset_context_t *c) const
|
||||
{
|
||||
hb_subset_layout_context_t l (c, tableTag);
|
||||
return GSUBGPOS::subset<SubstLookup> (&l);
|
||||
}
|
||||
|
||||
bool sanitize (hb_sanitize_context_t *c) const
|
||||
{
|
||||
TRACE_SANITIZE (this);
|
||||
return_trace (GSUBGPOS::sanitize<SubstLookup> (c));
|
||||
}
|
||||
|
||||
HB_INTERNAL bool is_blocklisted (hb_blob_t *blob,
|
||||
hb_face_t *face) const;
|
||||
|
||||
void closure_lookups (hb_face_t *face,
|
||||
const hb_set_t *glyphs,
|
||||
hb_set_t *lookup_indexes /* IN/OUT */) const
|
||||
{ GSUBGPOS::closure_lookups<SubstLookup> (face, glyphs, lookup_indexes); }
|
||||
|
||||
typedef GSUBGPOS::accelerator_t<GSUB> accelerator_t;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
struct GSUB_accelerator_t : Layout::GSUB::accelerator_t {
|
||||
GSUB_accelerator_t (hb_face_t *face) : Layout::GSUB::accelerator_t (face) {}
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif /* OT_LAYOUT_GSUB_GSUB_HH */
|
||||
+200
@@ -0,0 +1,200 @@
|
||||
#ifndef OT_LAYOUT_GSUB_LIGATURE_HH
|
||||
#define OT_LAYOUT_GSUB_LIGATURE_HH
|
||||
|
||||
#include "Common.hh"
|
||||
|
||||
namespace OT {
|
||||
namespace Layout {
|
||||
namespace GSUB_impl {
|
||||
|
||||
template <typename Types>
|
||||
struct Ligature
|
||||
{
|
||||
public:
|
||||
typename Types::HBGlyphID
|
||||
ligGlyph; /* GlyphID of ligature to substitute */
|
||||
HeadlessArray16Of<typename Types::HBGlyphID>
|
||||
component; /* Array of component GlyphIDs--start
|
||||
* with the second component--ordered
|
||||
* in writing direction */
|
||||
public:
|
||||
DEFINE_SIZE_ARRAY (Types::size + 2, component);
|
||||
|
||||
bool sanitize (hb_sanitize_context_t *c) const
|
||||
{
|
||||
TRACE_SANITIZE (this);
|
||||
return_trace (ligGlyph.sanitize (c) && component.sanitize (c));
|
||||
}
|
||||
|
||||
bool intersects (const hb_set_t *glyphs) const
|
||||
{ return hb_all (component, glyphs); }
|
||||
|
||||
bool intersects_lig_glyph (const hb_set_t *glyphs) const
|
||||
{ return glyphs->has(ligGlyph); }
|
||||
|
||||
void closure (hb_closure_context_t *c) const
|
||||
{
|
||||
if (!intersects (c->glyphs)) return;
|
||||
c->output->add (ligGlyph);
|
||||
}
|
||||
|
||||
void collect_glyphs (hb_collect_glyphs_context_t *c) const
|
||||
{
|
||||
c->input->add_array (component.arrayZ, component.get_length ());
|
||||
c->output->add (ligGlyph);
|
||||
}
|
||||
|
||||
template <typename set_t>
|
||||
void collect_second (set_t &s) const
|
||||
{
|
||||
if (unlikely (!component.get_length ()))
|
||||
{
|
||||
// A ligature without any components. Anything matches.
|
||||
s = set_t::full ();
|
||||
return;
|
||||
}
|
||||
s.add (component.arrayZ[0]);
|
||||
}
|
||||
|
||||
bool would_apply (hb_would_apply_context_t *c) const
|
||||
{
|
||||
if (c->len != component.lenP1)
|
||||
return false;
|
||||
|
||||
for (unsigned int i = 1; i < c->len; i++)
|
||||
if (likely (c->glyphs[i] != component[i]))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool apply (hb_ot_apply_context_t *c) const
|
||||
{
|
||||
TRACE_APPLY (this);
|
||||
unsigned int count = component.lenP1;
|
||||
|
||||
if (unlikely (!count)) return_trace (false);
|
||||
|
||||
/* Special-case to make it in-place and not consider this
|
||||
* as a "ligated" substitution. */
|
||||
if (unlikely (count == 1))
|
||||
{
|
||||
|
||||
if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
|
||||
{
|
||||
c->buffer->sync_so_far ();
|
||||
c->buffer->message (c->font,
|
||||
"replacing glyph at %u (ligature substitution)",
|
||||
c->buffer->idx);
|
||||
}
|
||||
|
||||
c->replace_glyph (ligGlyph);
|
||||
|
||||
if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
|
||||
{
|
||||
c->buffer->message (c->font,
|
||||
"replaced glyph at %u (ligature substitution)",
|
||||
c->buffer->idx - 1u);
|
||||
}
|
||||
|
||||
return_trace (true);
|
||||
}
|
||||
|
||||
unsigned int total_component_count = 0;
|
||||
|
||||
if (unlikely (count > HB_MAX_CONTEXT_LENGTH)) return false;
|
||||
unsigned int match_end = 0;
|
||||
|
||||
if (likely (!match_input (c, count,
|
||||
&component[1],
|
||||
match_glyph,
|
||||
nullptr,
|
||||
&match_end,
|
||||
&total_component_count)))
|
||||
{
|
||||
c->buffer->unsafe_to_concat (c->buffer->idx, match_end);
|
||||
return_trace (false);
|
||||
}
|
||||
|
||||
unsigned pos = 0;
|
||||
if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
|
||||
{
|
||||
unsigned delta = c->buffer->sync_so_far ();
|
||||
|
||||
pos = c->buffer->idx;
|
||||
|
||||
char buf[HB_MAX_CONTEXT_LENGTH * 16] = {0};
|
||||
char *p = buf;
|
||||
|
||||
match_end += delta;
|
||||
for (unsigned i = 0; i < count; i++)
|
||||
{
|
||||
c->match_positions[i] += delta;
|
||||
if (i)
|
||||
*p++ = ',';
|
||||
snprintf (p, sizeof(buf) - (p - buf), "%u", c->match_positions[i]);
|
||||
p += strlen(p);
|
||||
}
|
||||
|
||||
c->buffer->message (c->font,
|
||||
"ligating glyphs at %s",
|
||||
buf);
|
||||
}
|
||||
|
||||
ligate_input (c,
|
||||
count,
|
||||
match_end,
|
||||
ligGlyph,
|
||||
total_component_count);
|
||||
|
||||
if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
|
||||
{
|
||||
c->buffer->sync_so_far ();
|
||||
c->buffer->message (c->font,
|
||||
"ligated glyph at %u",
|
||||
pos);
|
||||
}
|
||||
|
||||
return_trace (true);
|
||||
}
|
||||
|
||||
template <typename Iterator,
|
||||
hb_requires (hb_is_source_of (Iterator, hb_codepoint_t))>
|
||||
bool serialize (hb_serialize_context_t *c,
|
||||
hb_codepoint_t ligature,
|
||||
Iterator components /* Starting from second */)
|
||||
{
|
||||
TRACE_SERIALIZE (this);
|
||||
if (unlikely (!c->extend_min (this))) return_trace (false);
|
||||
ligGlyph = ligature;
|
||||
if (unlikely (!component.serialize (c, components))) return_trace (false);
|
||||
return_trace (true);
|
||||
}
|
||||
|
||||
bool subset (hb_subset_context_t *c, unsigned coverage_idx) const
|
||||
{
|
||||
TRACE_SUBSET (this);
|
||||
const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
|
||||
const hb_map_t &glyph_map = *c->plan->glyph_map;
|
||||
|
||||
if (!intersects (&glyphset) || !glyphset.has (ligGlyph)) return_trace (false);
|
||||
// Ensure Coverage table is always packed after this.
|
||||
c->serializer->add_virtual_link (coverage_idx);
|
||||
|
||||
auto it =
|
||||
+ hb_iter (component)
|
||||
| hb_map (glyph_map)
|
||||
;
|
||||
|
||||
auto *out = c->serializer->start_embed (*this);
|
||||
return_trace (out->serialize (c->serializer,
|
||||
glyph_map[ligGlyph],
|
||||
it)); }
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* OT_LAYOUT_GSUB_LIGATURE_HH */
|
||||
+198
@@ -0,0 +1,198 @@
|
||||
#ifndef OT_LAYOUT_GSUB_LIGATURESET_HH
|
||||
#define OT_LAYOUT_GSUB_LIGATURESET_HH
|
||||
|
||||
#include "Common.hh"
|
||||
#include "Ligature.hh"
|
||||
|
||||
namespace OT {
|
||||
namespace Layout {
|
||||
namespace GSUB_impl {
|
||||
|
||||
template <typename Types>
|
||||
struct LigatureSet
|
||||
{
|
||||
public:
|
||||
Array16OfOffset16To<Ligature<Types>>
|
||||
ligature; /* Array LigatureSet tables
|
||||
* ordered by preference */
|
||||
|
||||
DEFINE_SIZE_ARRAY (2, ligature);
|
||||
|
||||
bool sanitize (hb_sanitize_context_t *c) const
|
||||
{
|
||||
TRACE_SANITIZE (this);
|
||||
return_trace (ligature.sanitize (c, this));
|
||||
}
|
||||
|
||||
bool intersects (const hb_set_t *glyphs) const
|
||||
{
|
||||
return
|
||||
+ hb_iter (ligature)
|
||||
| hb_map (hb_add (this))
|
||||
| hb_map ([glyphs] (const Ligature<Types> &_) { return _.intersects (glyphs); })
|
||||
| hb_any
|
||||
;
|
||||
}
|
||||
|
||||
bool intersects_lig_glyph (const hb_set_t *glyphs) const
|
||||
{
|
||||
return
|
||||
+ hb_iter (ligature)
|
||||
| hb_map (hb_add (this))
|
||||
| hb_map ([glyphs] (const Ligature<Types> &_) {
|
||||
return _.intersects_lig_glyph (glyphs) && _.intersects (glyphs);
|
||||
})
|
||||
| hb_any
|
||||
;
|
||||
}
|
||||
|
||||
void closure (hb_closure_context_t *c) const
|
||||
{
|
||||
+ hb_iter (ligature)
|
||||
| hb_map (hb_add (this))
|
||||
| hb_apply ([c] (const Ligature<Types> &_) { _.closure (c); })
|
||||
;
|
||||
}
|
||||
|
||||
void collect_glyphs (hb_collect_glyphs_context_t *c) const
|
||||
{
|
||||
+ hb_iter (ligature)
|
||||
| hb_map (hb_add (this))
|
||||
| hb_apply ([c] (const Ligature<Types> &_) { _.collect_glyphs (c); })
|
||||
;
|
||||
}
|
||||
|
||||
template <typename set_t>
|
||||
void collect_seconds (set_t &s) const
|
||||
{
|
||||
+ hb_iter (ligature)
|
||||
| hb_map (hb_add (this))
|
||||
| hb_apply ([&s] (const Ligature<Types> &_) { _.collect_second (s); })
|
||||
;
|
||||
}
|
||||
|
||||
bool would_apply (hb_would_apply_context_t *c) const
|
||||
{
|
||||
return
|
||||
+ hb_iter (ligature)
|
||||
| hb_map (hb_add (this))
|
||||
| hb_map ([c] (const Ligature<Types> &_) { return _.would_apply (c); })
|
||||
| hb_any
|
||||
;
|
||||
}
|
||||
|
||||
bool apply (hb_ot_apply_context_t *c, const hb_set_digest_t *seconds = nullptr) const
|
||||
{
|
||||
TRACE_APPLY (this);
|
||||
|
||||
unsigned int num_ligs = ligature.len;
|
||||
|
||||
#ifndef HB_NO_OT_RULESETS_FAST_PATH
|
||||
if (HB_OPTIMIZE_SIZE_VAL || num_ligs <= 1)
|
||||
#endif
|
||||
{
|
||||
slow:
|
||||
for (unsigned int i = 0; i < num_ligs; i++)
|
||||
{
|
||||
const auto &lig = this+ligature.arrayZ[i];
|
||||
if (lig.apply (c)) return_trace (true);
|
||||
}
|
||||
return_trace (false);
|
||||
}
|
||||
|
||||
/* This version is optimized for speed by matching the second component
|
||||
* of the ligature here, instead of calling into the ligation code.
|
||||
*
|
||||
* This is replicated in ChainRuleSet and RuleSet. */
|
||||
|
||||
auto &skippy_iter = c->iter_context;
|
||||
skippy_iter.reset (c->buffer->idx);
|
||||
skippy_iter.set_match_func (match_always, nullptr);
|
||||
skippy_iter.set_glyph_data ((HBUINT16 *) nullptr);
|
||||
unsigned unsafe_to;
|
||||
hb_codepoint_t second = (unsigned) -1;
|
||||
bool matched = skippy_iter.next (&unsafe_to);
|
||||
if (likely (matched))
|
||||
{
|
||||
second = c->buffer->info[skippy_iter.idx].codepoint;
|
||||
unsafe_to = skippy_iter.idx + 1;
|
||||
|
||||
if (skippy_iter.may_skip (c->buffer->info[skippy_iter.idx]))
|
||||
{
|
||||
/* Can't use the fast path if eg. the next char is a default-ignorable
|
||||
* or other skippable. */
|
||||
goto slow;
|
||||
}
|
||||
}
|
||||
else
|
||||
goto slow;
|
||||
|
||||
if (seconds && !seconds->may_have (second))
|
||||
return_trace (false);
|
||||
bool unsafe_to_concat = false;
|
||||
for (unsigned int i = 0; i < num_ligs; i++)
|
||||
{
|
||||
const auto &lig = this+ligature.arrayZ[i];
|
||||
if (unlikely (lig.component.lenP1 <= 1) ||
|
||||
lig.component.arrayZ[0] == second)
|
||||
{
|
||||
if (lig.apply (c))
|
||||
{
|
||||
if (unsafe_to_concat)
|
||||
c->buffer->unsafe_to_concat (c->buffer->idx, unsafe_to);
|
||||
return_trace (true);
|
||||
}
|
||||
}
|
||||
else if (likely (lig.component.lenP1 > 1))
|
||||
unsafe_to_concat = true;
|
||||
}
|
||||
if (likely (unsafe_to_concat))
|
||||
c->buffer->unsafe_to_concat (c->buffer->idx, unsafe_to);
|
||||
|
||||
return_trace (false);
|
||||
}
|
||||
|
||||
bool serialize (hb_serialize_context_t *c,
|
||||
hb_array_t<const HBGlyphID16> ligatures,
|
||||
hb_array_t<const unsigned int> component_count_list,
|
||||
hb_array_t<const HBGlyphID16> &component_list /* Starting from second for each ligature */)
|
||||
{
|
||||
TRACE_SERIALIZE (this);
|
||||
if (unlikely (!c->extend_min (this))) return_trace (false);
|
||||
if (unlikely (!ligature.serialize (c, ligatures.length))) return_trace (false);
|
||||
for (unsigned int i = 0; i < ligatures.length; i++)
|
||||
{
|
||||
unsigned int component_count = (unsigned) hb_max ((int) component_count_list[i] - 1, 0);
|
||||
if (unlikely (!ligature[i].serialize_serialize (c,
|
||||
ligatures[i],
|
||||
component_list.sub_array (0, component_count))))
|
||||
return_trace (false);
|
||||
component_list += component_count;
|
||||
}
|
||||
return_trace (true);
|
||||
}
|
||||
|
||||
bool subset (hb_subset_context_t *c, unsigned coverage_idx) const
|
||||
{
|
||||
TRACE_SUBSET (this);
|
||||
auto *out = c->serializer->start_embed (*this);
|
||||
if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
|
||||
|
||||
+ hb_iter (ligature)
|
||||
| hb_filter (subset_offset_array (c, out->ligature, this, coverage_idx))
|
||||
| hb_drain
|
||||
;
|
||||
|
||||
if (bool (out->ligature))
|
||||
// Ensure Coverage table is always packed after this.
|
||||
c->serializer->add_virtual_link (coverage_idx);
|
||||
|
||||
return_trace (bool (out->ligature));
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* OT_LAYOUT_GSUB_LIGATURESET_HH */
|
||||
@@ -0,0 +1,71 @@
|
||||
#ifndef OT_LAYOUT_GSUB_LIGATURESUBST_HH
|
||||
#define OT_LAYOUT_GSUB_LIGATURESUBST_HH
|
||||
|
||||
#include "Common.hh"
|
||||
#include "LigatureSubstFormat1.hh"
|
||||
|
||||
namespace OT {
|
||||
namespace Layout {
|
||||
namespace GSUB_impl {
|
||||
|
||||
struct LigatureSubst
|
||||
{
|
||||
protected:
|
||||
union {
|
||||
struct { HBUINT16 v; } format; /* Format identifier */
|
||||
LigatureSubstFormat1_2<SmallTypes> format1;
|
||||
#ifndef HB_NO_BEYOND_64K
|
||||
LigatureSubstFormat1_2<MediumTypes> format2;
|
||||
#endif
|
||||
} u;
|
||||
|
||||
public:
|
||||
template <typename context_t, typename ...Ts>
|
||||
typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
|
||||
{
|
||||
if (unlikely (!c->may_dispatch (this, &u.format.v))) return c->no_dispatch_return_value ();
|
||||
TRACE_DISPATCH (this, u.format.v);
|
||||
switch (u.format.v) {
|
||||
case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...));
|
||||
#ifndef HB_NO_BEYOND_64K
|
||||
case 2: return_trace (c->dispatch (u.format2, std::forward<Ts> (ds)...));
|
||||
#endif
|
||||
default:return_trace (c->default_return_value ());
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO This function is only used by small GIDs, and not updated to 24bit GIDs. Should
|
||||
* be done by using iterators. While at it perhaps using iterator of arrays of hb_codepoint_t
|
||||
* instead. */
|
||||
bool serialize (hb_serialize_context_t *c,
|
||||
hb_sorted_array_t<const HBGlyphID16> first_glyphs,
|
||||
hb_array_t<const unsigned int> ligature_per_first_glyph_count_list,
|
||||
hb_array_t<const HBGlyphID16> ligatures_list,
|
||||
hb_array_t<const unsigned int> component_count_list,
|
||||
hb_array_t<const HBGlyphID16> component_list /* Starting from second for each ligature */)
|
||||
{
|
||||
TRACE_SERIALIZE (this);
|
||||
if (unlikely (!c->extend_min (u.format.v))) return_trace (false);
|
||||
unsigned int format = 1;
|
||||
u.format.v = format;
|
||||
switch (u.format.v) {
|
||||
case 1: return_trace (u.format1.serialize (c,
|
||||
first_glyphs,
|
||||
ligature_per_first_glyph_count_list,
|
||||
ligatures_list,
|
||||
component_count_list,
|
||||
component_list));
|
||||
default:return_trace (false);
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO subset() should choose format. */
|
||||
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* OT_LAYOUT_GSUB_LIGATURESUBST_HH */
|
||||
@@ -0,0 +1,195 @@
|
||||
#ifndef OT_LAYOUT_GSUB_LIGATURESUBSTFORMAT1_HH
|
||||
#define OT_LAYOUT_GSUB_LIGATURESUBSTFORMAT1_HH
|
||||
|
||||
#include "Common.hh"
|
||||
#include "LigatureSet.hh"
|
||||
|
||||
namespace OT {
|
||||
namespace Layout {
|
||||
namespace GSUB_impl {
|
||||
|
||||
template <typename Types>
|
||||
struct LigatureSubstFormat1_2
|
||||
{
|
||||
protected:
|
||||
HBUINT16 format; /* Format identifier--format = 1 */
|
||||
typename Types::template OffsetTo<Coverage>
|
||||
coverage; /* Offset to Coverage table--from
|
||||
* beginning of Substitution table */
|
||||
Array16Of<typename Types::template OffsetTo<LigatureSet<Types>>>
|
||||
ligatureSet; /* Array LigatureSet tables
|
||||
* ordered by Coverage Index */
|
||||
public:
|
||||
DEFINE_SIZE_ARRAY (4 + Types::size, ligatureSet);
|
||||
|
||||
bool sanitize (hb_sanitize_context_t *c) const
|
||||
{
|
||||
TRACE_SANITIZE (this);
|
||||
return_trace (coverage.sanitize (c, this) && ligatureSet.sanitize (c, this));
|
||||
}
|
||||
|
||||
bool intersects (const hb_set_t *glyphs) const
|
||||
{
|
||||
return
|
||||
+ hb_zip (this+coverage, ligatureSet)
|
||||
| hb_filter (*glyphs, hb_first)
|
||||
| hb_map (hb_second)
|
||||
| hb_map ([this, glyphs] (const typename Types::template OffsetTo<LigatureSet<Types>> &_)
|
||||
{ return (this+_).intersects (glyphs); })
|
||||
| hb_any
|
||||
;
|
||||
}
|
||||
|
||||
bool may_have_non_1to1 () const
|
||||
{ return true; }
|
||||
|
||||
void closure (hb_closure_context_t *c) const
|
||||
{
|
||||
+ hb_zip (this+coverage, ligatureSet)
|
||||
| hb_filter (c->parent_active_glyphs (), hb_first)
|
||||
| hb_map (hb_second)
|
||||
| hb_map (hb_add (this))
|
||||
| hb_apply ([c] (const LigatureSet<Types> &_) { _.closure (c); })
|
||||
;
|
||||
|
||||
}
|
||||
|
||||
void closure_lookups (hb_closure_lookups_context_t *c) const {}
|
||||
|
||||
void collect_glyphs (hb_collect_glyphs_context_t *c) const
|
||||
{
|
||||
if (unlikely (!(this+coverage).collect_coverage (c->input))) return;
|
||||
|
||||
+ hb_zip (this+coverage, ligatureSet)
|
||||
| hb_map (hb_second)
|
||||
| hb_map (hb_add (this))
|
||||
| hb_apply ([c] (const LigatureSet<Types> &_) { _.collect_glyphs (c); })
|
||||
;
|
||||
}
|
||||
|
||||
const Coverage &get_coverage () const { return this+coverage; }
|
||||
|
||||
bool would_apply (hb_would_apply_context_t *c) const
|
||||
{
|
||||
unsigned int index = (this+coverage).get_coverage (c->glyphs[0]);
|
||||
if (likely (index == NOT_COVERED)) return false;
|
||||
|
||||
const auto &lig_set = this+ligatureSet[index];
|
||||
return lig_set.would_apply (c);
|
||||
}
|
||||
|
||||
struct external_cache_t
|
||||
{
|
||||
hb_ot_layout_mapping_cache_t coverage;
|
||||
hb_set_digest_t seconds;
|
||||
};
|
||||
void *external_cache_create () const
|
||||
{
|
||||
external_cache_t *cache = (external_cache_t *) hb_malloc (sizeof (external_cache_t));
|
||||
if (likely (cache))
|
||||
{
|
||||
cache->coverage.clear ();
|
||||
|
||||
cache->seconds.init ();
|
||||
+ hb_iter (ligatureSet)
|
||||
| hb_map (hb_add (this))
|
||||
| hb_apply ([cache] (const LigatureSet<Types> &_) { _.collect_seconds (cache->seconds); })
|
||||
;
|
||||
}
|
||||
return cache;
|
||||
}
|
||||
|
||||
bool apply (hb_ot_apply_context_t *c, void *external_cache) const
|
||||
{
|
||||
TRACE_APPLY (this);
|
||||
hb_buffer_t *buffer = c->buffer;
|
||||
|
||||
#ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE
|
||||
external_cache_t *cache = (external_cache_t *) external_cache;
|
||||
const hb_set_digest_t *seconds = cache ? &cache->seconds : nullptr;
|
||||
unsigned int index = (this+coverage).get_coverage (buffer->cur().codepoint, cache ? &cache->coverage : nullptr);
|
||||
#else
|
||||
const hb_set_digest_t *seconds = nullptr;
|
||||
unsigned int index = (this+coverage).get_coverage (buffer->cur().codepoint);
|
||||
#endif
|
||||
if (index == NOT_COVERED) return_trace (false);
|
||||
|
||||
const auto &lig_set = this+ligatureSet[index];
|
||||
return_trace (lig_set.apply (c, seconds));
|
||||
}
|
||||
|
||||
bool serialize (hb_serialize_context_t *c,
|
||||
hb_sorted_array_t<const HBGlyphID16> first_glyphs,
|
||||
hb_array_t<const unsigned int> ligature_per_first_glyph_count_list,
|
||||
hb_array_t<const HBGlyphID16> ligatures_list,
|
||||
hb_array_t<const unsigned int> component_count_list,
|
||||
hb_array_t<const HBGlyphID16> component_list /* Starting from second for each ligature */)
|
||||
{
|
||||
TRACE_SERIALIZE (this);
|
||||
if (unlikely (!c->extend_min (this))) return_trace (false);
|
||||
if (unlikely (!ligatureSet.serialize (c, first_glyphs.length))) return_trace (false);
|
||||
for (unsigned int i = 0; i < first_glyphs.length; i++)
|
||||
{
|
||||
unsigned int ligature_count = ligature_per_first_glyph_count_list[i];
|
||||
if (unlikely (!ligatureSet[i]
|
||||
.serialize_serialize (c,
|
||||
ligatures_list.sub_array (0, ligature_count),
|
||||
component_count_list.sub_array (0, ligature_count),
|
||||
component_list))) return_trace (false);
|
||||
ligatures_list += ligature_count;
|
||||
component_count_list += ligature_count;
|
||||
}
|
||||
return_trace (coverage.serialize_serialize (c, first_glyphs));
|
||||
}
|
||||
|
||||
bool subset (hb_subset_context_t *c) const
|
||||
{
|
||||
TRACE_SUBSET (this);
|
||||
const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
|
||||
const hb_map_t &glyph_map = *c->plan->glyph_map;
|
||||
|
||||
auto *out = c->serializer->start_embed (*this);
|
||||
if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
|
||||
out->format = format;
|
||||
|
||||
// Due to a bug in some older versions of windows 7 the Coverage table must be
|
||||
// packed after the LigatureSet and Ligature tables, so serialize Coverage first
|
||||
// which places it last in the packed order.
|
||||
hb_set_t new_coverage;
|
||||
+ hb_zip (this+coverage, hb_iter (ligatureSet) | hb_map (hb_add (this)))
|
||||
| hb_filter (glyphset, hb_first)
|
||||
| hb_filter ([&] (const LigatureSet<Types>& _) {
|
||||
return _.intersects_lig_glyph (&glyphset);
|
||||
}, hb_second)
|
||||
| hb_map (hb_first)
|
||||
| hb_sink (new_coverage);
|
||||
|
||||
if (!c->serializer->push<Coverage> ()
|
||||
->serialize (c->serializer,
|
||||
+ new_coverage.iter () | hb_map_retains_sorting (glyph_map)))
|
||||
{
|
||||
c->serializer->pop_discard ();
|
||||
return_trace (false);
|
||||
}
|
||||
|
||||
unsigned coverage_idx = c->serializer->pop_pack ();
|
||||
c->serializer->add_link (out->coverage, coverage_idx);
|
||||
|
||||
+ hb_zip (this+coverage, ligatureSet)
|
||||
| hb_filter (new_coverage, hb_first)
|
||||
| hb_map (hb_second)
|
||||
// to ensure that the repacker always orders the coverage table after the LigatureSet
|
||||
// and LigatureSubtable's they will be linked to the Coverage table via a virtual link
|
||||
// the coverage table object idx is passed down to facilitate this.
|
||||
| hb_apply (subset_offset_array (c, out->ligatureSet, this, coverage_idx))
|
||||
;
|
||||
|
||||
return_trace (bool (new_coverage));
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* OT_LAYOUT_GSUB_LIGATURESUBSTFORMAT1_HH */
|
||||
@@ -0,0 +1,62 @@
|
||||
#ifndef OT_LAYOUT_GSUB_MULTIPLESUBST_HH
|
||||
#define OT_LAYOUT_GSUB_MULTIPLESUBST_HH
|
||||
|
||||
#include "Common.hh"
|
||||
#include "MultipleSubstFormat1.hh"
|
||||
|
||||
namespace OT {
|
||||
namespace Layout {
|
||||
namespace GSUB_impl {
|
||||
|
||||
struct MultipleSubst
|
||||
{
|
||||
protected:
|
||||
union {
|
||||
struct { HBUINT16 v; } format; /* Format identifier */
|
||||
MultipleSubstFormat1_2<SmallTypes> format1;
|
||||
#ifndef HB_NO_BEYOND_64K
|
||||
MultipleSubstFormat1_2<MediumTypes> format2;
|
||||
#endif
|
||||
} u;
|
||||
|
||||
public:
|
||||
|
||||
template <typename context_t, typename ...Ts>
|
||||
typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
|
||||
{
|
||||
if (unlikely (!c->may_dispatch (this, &u.format.v))) return c->no_dispatch_return_value ();
|
||||
TRACE_DISPATCH (this, u.format.v);
|
||||
switch (u.format.v) {
|
||||
case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...));
|
||||
#ifndef HB_NO_BEYOND_64K
|
||||
case 2: return_trace (c->dispatch (u.format2, std::forward<Ts> (ds)...));
|
||||
#endif
|
||||
default:return_trace (c->default_return_value ());
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Iterator,
|
||||
hb_requires (hb_is_sorted_iterator (Iterator))>
|
||||
bool serialize (hb_serialize_context_t *c,
|
||||
Iterator it)
|
||||
{
|
||||
TRACE_SERIALIZE (this);
|
||||
if (unlikely (!c->extend_min (u.format.v))) return_trace (false);
|
||||
unsigned int format = 1;
|
||||
u.format.v = format;
|
||||
switch (u.format.v) {
|
||||
case 1: return_trace (u.format1.serialize (c, it));
|
||||
default:return_trace (false);
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO subset() should choose format. */
|
||||
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* OT_LAYOUT_GSUB_MULTIPLESUBST_HH */
|
||||
@@ -0,0 +1,130 @@
|
||||
#ifndef OT_LAYOUT_GSUB_MULTIPLESUBSTFORMAT1_HH
|
||||
#define OT_LAYOUT_GSUB_MULTIPLESUBSTFORMAT1_HH
|
||||
|
||||
#include "Common.hh"
|
||||
#include "Sequence.hh"
|
||||
|
||||
namespace OT {
|
||||
namespace Layout {
|
||||
namespace GSUB_impl {
|
||||
|
||||
template <typename Types>
|
||||
struct MultipleSubstFormat1_2
|
||||
{
|
||||
protected:
|
||||
HBUINT16 format; /* Format identifier--format = 1 */
|
||||
typename Types::template OffsetTo<Coverage>
|
||||
coverage; /* Offset to Coverage table--from
|
||||
* beginning of Substitution table */
|
||||
Array16Of<typename Types::template OffsetTo<Sequence<Types>>>
|
||||
sequence; /* Array of Sequence tables
|
||||
* ordered by Coverage Index */
|
||||
public:
|
||||
DEFINE_SIZE_ARRAY (4 + Types::size, sequence);
|
||||
|
||||
bool sanitize (hb_sanitize_context_t *c) const
|
||||
{
|
||||
TRACE_SANITIZE (this);
|
||||
return_trace (coverage.sanitize (c, this) && sequence.sanitize (c, this));
|
||||
}
|
||||
|
||||
bool intersects (const hb_set_t *glyphs) const
|
||||
{ return (this+coverage).intersects (glyphs); }
|
||||
|
||||
bool may_have_non_1to1 () const
|
||||
{ return true; }
|
||||
|
||||
void closure (hb_closure_context_t *c) const
|
||||
{
|
||||
+ hb_zip (this+coverage, sequence)
|
||||
| hb_filter (c->parent_active_glyphs (), hb_first)
|
||||
| hb_map (hb_second)
|
||||
| hb_map (hb_add (this))
|
||||
| hb_apply ([c] (const Sequence<Types> &_) { _.closure (c); })
|
||||
;
|
||||
}
|
||||
|
||||
void closure_lookups (hb_closure_lookups_context_t *c) const {}
|
||||
|
||||
void collect_glyphs (hb_collect_glyphs_context_t *c) const
|
||||
{
|
||||
if (unlikely (!(this+coverage).collect_coverage (c->input))) return;
|
||||
+ hb_zip (this+coverage, sequence)
|
||||
| hb_map (hb_second)
|
||||
| hb_map (hb_add (this))
|
||||
| hb_apply ([c] (const Sequence<Types> &_) { _.collect_glyphs (c); })
|
||||
;
|
||||
}
|
||||
|
||||
const Coverage &get_coverage () const { return this+coverage; }
|
||||
|
||||
bool would_apply (hb_would_apply_context_t *c) const
|
||||
{ return c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED; }
|
||||
|
||||
bool apply (hb_ot_apply_context_t *c) const
|
||||
{
|
||||
TRACE_APPLY (this);
|
||||
|
||||
unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
|
||||
if (index == NOT_COVERED) return_trace (false);
|
||||
|
||||
return_trace ((this+sequence[index]).apply (c));
|
||||
}
|
||||
|
||||
template<typename Iterator,
|
||||
hb_requires (hb_is_sorted_iterator (Iterator))>
|
||||
bool serialize (hb_serialize_context_t *c,
|
||||
Iterator it)
|
||||
{
|
||||
TRACE_SERIALIZE (this);
|
||||
auto sequences =
|
||||
+ it
|
||||
| hb_map (hb_second)
|
||||
;
|
||||
auto glyphs =
|
||||
+ it
|
||||
| hb_map_retains_sorting (hb_first)
|
||||
;
|
||||
if (unlikely (!c->extend_min (this))) return_trace (false);
|
||||
|
||||
if (unlikely (!sequence.serialize (c, sequences.length))) return_trace (false);
|
||||
|
||||
for (auto& pair : hb_zip (sequences, sequence))
|
||||
{
|
||||
if (unlikely (!pair.second
|
||||
.serialize_serialize (c, pair.first)))
|
||||
return_trace (false);
|
||||
}
|
||||
|
||||
return_trace (coverage.serialize_serialize (c, glyphs));
|
||||
}
|
||||
|
||||
bool subset (hb_subset_context_t *c) const
|
||||
{
|
||||
TRACE_SUBSET (this);
|
||||
const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
|
||||
const hb_map_t &glyph_map = *c->plan->glyph_map;
|
||||
|
||||
auto *out = c->serializer->start_embed (*this);
|
||||
if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
|
||||
out->format = format;
|
||||
|
||||
hb_sorted_vector_t<hb_codepoint_t> new_coverage;
|
||||
+ hb_zip (this+coverage, sequence)
|
||||
| hb_filter (glyphset, hb_first)
|
||||
| hb_filter (subset_offset_array (c, out->sequence, this), hb_second)
|
||||
| hb_map (hb_first)
|
||||
| hb_map (glyph_map)
|
||||
| hb_sink (new_coverage)
|
||||
;
|
||||
out->coverage.serialize_serialize (c->serializer, new_coverage.iter ());
|
||||
return_trace (bool (new_coverage));
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif /* OT_LAYOUT_GSUB_MULTIPLESUBSTFORMAT1_HH */
|
||||
@@ -0,0 +1,36 @@
|
||||
#ifndef OT_LAYOUT_GSUB_REVERSECHAINSINGLESUBST_HH
|
||||
#define OT_LAYOUT_GSUB_REVERSECHAINSINGLESUBST_HH
|
||||
|
||||
#include "Common.hh"
|
||||
#include "ReverseChainSingleSubstFormat1.hh"
|
||||
|
||||
namespace OT {
|
||||
namespace Layout {
|
||||
namespace GSUB_impl {
|
||||
|
||||
struct ReverseChainSingleSubst
|
||||
{
|
||||
protected:
|
||||
union {
|
||||
struct { HBUINT16 v; } format; /* Format identifier */
|
||||
ReverseChainSingleSubstFormat1 format1;
|
||||
} u;
|
||||
|
||||
public:
|
||||
template <typename context_t, typename ...Ts>
|
||||
typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
|
||||
{
|
||||
if (unlikely (!c->may_dispatch (this, &u.format.v))) return c->no_dispatch_return_value ();
|
||||
TRACE_DISPATCH (this, u.format.v);
|
||||
switch (u.format.v) {
|
||||
case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...));
|
||||
default:return_trace (c->default_return_value ());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* HB_OT_LAYOUT_GSUB_REVERSECHAINSINGLESUBST_HH */
|
||||
@@ -0,0 +1,245 @@
|
||||
#ifndef OT_LAYOUT_GSUB_REVERSECHAINSINGLESUBSTFORMAT1_HH
|
||||
#define OT_LAYOUT_GSUB_REVERSECHAINSINGLESUBSTFORMAT1_HH
|
||||
|
||||
#include "Common.hh"
|
||||
|
||||
namespace OT {
|
||||
namespace Layout {
|
||||
namespace GSUB_impl {
|
||||
|
||||
struct ReverseChainSingleSubstFormat1
|
||||
{
|
||||
protected:
|
||||
HBUINT16 format; /* Format identifier--format = 1 */
|
||||
Offset16To<Coverage>
|
||||
coverage; /* Offset to Coverage table--from
|
||||
* beginning of table */
|
||||
Array16OfOffset16To<Coverage>
|
||||
backtrack; /* Array of coverage tables
|
||||
* in backtracking sequence, in glyph
|
||||
* sequence order */
|
||||
Array16OfOffset16To<Coverage>
|
||||
lookaheadX; /* Array of coverage tables
|
||||
* in lookahead sequence, in glyph
|
||||
* sequence order */
|
||||
Array16Of<HBGlyphID16>
|
||||
substituteX; /* Array of substitute
|
||||
* GlyphIDs--ordered by Coverage Index */
|
||||
public:
|
||||
DEFINE_SIZE_MIN (10);
|
||||
|
||||
bool sanitize (hb_sanitize_context_t *c) const
|
||||
{
|
||||
TRACE_SANITIZE (this);
|
||||
if (!(coverage.sanitize (c, this) && backtrack.sanitize (c, this)))
|
||||
return_trace (false);
|
||||
hb_barrier ();
|
||||
const auto &lookahead = StructAfter<decltype (lookaheadX)> (backtrack);
|
||||
if (!lookahead.sanitize (c, this))
|
||||
return_trace (false);
|
||||
hb_barrier ();
|
||||
const auto &substitute = StructAfter<decltype (substituteX)> (lookahead);
|
||||
return_trace (substitute.sanitize (c));
|
||||
}
|
||||
|
||||
bool intersects (const hb_set_t *glyphs) const
|
||||
{
|
||||
if (!(this+coverage).intersects (glyphs))
|
||||
return false;
|
||||
|
||||
const auto &lookahead = StructAfter<decltype (lookaheadX)> (backtrack);
|
||||
|
||||
unsigned int count;
|
||||
|
||||
count = backtrack.len;
|
||||
for (unsigned int i = 0; i < count; i++)
|
||||
if (!(this+backtrack[i]).intersects (glyphs))
|
||||
return false;
|
||||
|
||||
count = lookahead.len;
|
||||
for (unsigned int i = 0; i < count; i++)
|
||||
if (!(this+lookahead[i]).intersects (glyphs))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool may_have_non_1to1 () const
|
||||
{ return false; }
|
||||
|
||||
void closure (hb_closure_context_t *c) const
|
||||
{
|
||||
if (!intersects (c->glyphs)) return;
|
||||
|
||||
const auto &lookahead = StructAfter<decltype (lookaheadX)> (backtrack);
|
||||
const auto &substitute = StructAfter<decltype (substituteX)> (lookahead);
|
||||
|
||||
+ hb_zip (this+coverage, substitute)
|
||||
| hb_filter (c->parent_active_glyphs (), hb_first)
|
||||
| hb_map (hb_second)
|
||||
| hb_sink (c->output)
|
||||
;
|
||||
}
|
||||
|
||||
void closure_lookups (hb_closure_lookups_context_t *c) const {}
|
||||
|
||||
void collect_glyphs (hb_collect_glyphs_context_t *c) const
|
||||
{
|
||||
if (unlikely (!(this+coverage).collect_coverage (c->input))) return;
|
||||
|
||||
unsigned int count;
|
||||
|
||||
count = backtrack.len;
|
||||
for (unsigned int i = 0; i < count; i++)
|
||||
if (unlikely (!(this+backtrack[i]).collect_coverage (c->before))) return;
|
||||
|
||||
const auto &lookahead = StructAfter<decltype (lookaheadX)> (backtrack);
|
||||
count = lookahead.len;
|
||||
for (unsigned int i = 0; i < count; i++)
|
||||
if (unlikely (!(this+lookahead[i]).collect_coverage (c->after))) return;
|
||||
|
||||
const auto &substitute = StructAfter<decltype (substituteX)> (lookahead);
|
||||
count = substitute.len;
|
||||
c->output->add_array (substitute.arrayZ, substitute.len);
|
||||
}
|
||||
|
||||
const Coverage &get_coverage () const { return this+coverage; }
|
||||
|
||||
bool would_apply (hb_would_apply_context_t *c) const
|
||||
{ return c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED; }
|
||||
|
||||
bool apply (hb_ot_apply_context_t *c) const
|
||||
{
|
||||
TRACE_APPLY (this);
|
||||
unsigned int index = (this+coverage).get_coverage (c->buffer->cur ().codepoint);
|
||||
if (index == NOT_COVERED) return_trace (false);
|
||||
|
||||
if (unlikely (c->nesting_level_left != HB_MAX_NESTING_LEVEL))
|
||||
return_trace (false); /* No chaining to this type */
|
||||
|
||||
const auto &lookahead = StructAfter<decltype (lookaheadX)> (backtrack);
|
||||
const auto &substitute = StructAfter<decltype (substituteX)> (lookahead);
|
||||
|
||||
if (unlikely (index >= substitute.len)) return_trace (false);
|
||||
|
||||
unsigned int start_index = 0, end_index = 0;
|
||||
if (match_backtrack (c,
|
||||
backtrack.len, (HBUINT16 *) backtrack.arrayZ,
|
||||
match_coverage, this,
|
||||
&start_index) &&
|
||||
match_lookahead (c,
|
||||
lookahead.len, (HBUINT16 *) lookahead.arrayZ,
|
||||
match_coverage, this,
|
||||
c->buffer->idx + 1, &end_index))
|
||||
{
|
||||
c->buffer->unsafe_to_break_from_outbuffer (start_index, end_index);
|
||||
|
||||
if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
|
||||
{
|
||||
c->buffer->message (c->font,
|
||||
"replacing glyph at %u (reverse chaining substitution)",
|
||||
c->buffer->idx);
|
||||
}
|
||||
|
||||
c->replace_glyph_inplace (substitute[index]);
|
||||
|
||||
if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
|
||||
{
|
||||
c->buffer->message (c->font,
|
||||
"replaced glyph at %u (reverse chaining substitution)",
|
||||
c->buffer->idx);
|
||||
}
|
||||
|
||||
/* Note: We DON'T decrease buffer->idx. The main loop does it
|
||||
* for us. This is useful for preventing surprises if someone
|
||||
* calls us through a Context lookup. */
|
||||
return_trace (true);
|
||||
}
|
||||
else
|
||||
{
|
||||
c->buffer->unsafe_to_concat_from_outbuffer (start_index, end_index);
|
||||
return_trace (false);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Iterator,
|
||||
hb_requires (hb_is_iterator (Iterator))>
|
||||
bool serialize_coverage_offset_array (hb_subset_context_t *c, Iterator it) const
|
||||
{
|
||||
TRACE_SERIALIZE (this);
|
||||
auto *out = c->serializer->start_embed<Array16OfOffset16To<Coverage>> ();
|
||||
|
||||
if (unlikely (!c->serializer->allocate_size<HBUINT16> (HBUINT16::static_size)))
|
||||
return_trace (false);
|
||||
|
||||
for (auto& offset : it) {
|
||||
auto *o = out->serialize_append (c->serializer);
|
||||
if (unlikely (!o) || !o->serialize_subset (c, offset, this))
|
||||
return_trace (false);
|
||||
}
|
||||
|
||||
return_trace (true);
|
||||
}
|
||||
|
||||
template<typename Iterator, typename BacktrackIterator, typename LookaheadIterator,
|
||||
hb_requires (hb_is_sorted_source_of (Iterator, hb_codepoint_pair_t)),
|
||||
hb_requires (hb_is_iterator (BacktrackIterator)),
|
||||
hb_requires (hb_is_iterator (LookaheadIterator))>
|
||||
bool serialize (hb_subset_context_t *c,
|
||||
Iterator coverage_subst_iter,
|
||||
BacktrackIterator backtrack_iter,
|
||||
LookaheadIterator lookahead_iter) const
|
||||
{
|
||||
TRACE_SERIALIZE (this);
|
||||
|
||||
auto *out = c->serializer->start_embed (this);
|
||||
if (unlikely (!c->serializer->embed (this->format))) return_trace (false);
|
||||
if (unlikely (!c->serializer->embed (this->coverage))) return_trace (false);
|
||||
|
||||
if (!serialize_coverage_offset_array (c, backtrack_iter)) return_trace (false);
|
||||
if (!serialize_coverage_offset_array (c, lookahead_iter)) return_trace (false);
|
||||
|
||||
auto *substitute_out = c->serializer->start_embed<Array16Of<HBGlyphID16>> ();
|
||||
auto substitutes =
|
||||
+ coverage_subst_iter
|
||||
| hb_map (hb_second)
|
||||
;
|
||||
|
||||
auto glyphs =
|
||||
+ coverage_subst_iter
|
||||
| hb_map_retains_sorting (hb_first)
|
||||
;
|
||||
if (unlikely (! c->serializer->check_success (substitute_out->serialize (c->serializer, substitutes))))
|
||||
return_trace (false);
|
||||
|
||||
if (unlikely (!out->coverage.serialize_serialize (c->serializer, glyphs)))
|
||||
return_trace (false);
|
||||
return_trace (true);
|
||||
}
|
||||
|
||||
bool subset (hb_subset_context_t *c) const
|
||||
{
|
||||
TRACE_SUBSET (this);
|
||||
const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
|
||||
const hb_map_t &glyph_map = *c->plan->glyph_map;
|
||||
|
||||
const auto &lookahead = StructAfter<decltype (lookaheadX)> (backtrack);
|
||||
const auto &substitute = StructAfter<decltype (substituteX)> (lookahead);
|
||||
|
||||
auto it =
|
||||
+ hb_zip (this+coverage, substitute)
|
||||
| hb_filter (glyphset, hb_first)
|
||||
| hb_filter (glyphset, hb_second)
|
||||
| hb_map_retains_sorting ([&] (hb_pair_t<hb_codepoint_t, const HBGlyphID16 &> p) -> hb_codepoint_pair_t
|
||||
{ return hb_pair (glyph_map[p.first], glyph_map[p.second]); })
|
||||
;
|
||||
|
||||
return_trace (bool (it) && serialize (c, it, backtrack.iter (), lookahead.iter ()));
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* HB_OT_LAYOUT_GSUB_REVERSECHAINSINGLESUBSTFORMAT1_HH */
|
||||
+165
@@ -0,0 +1,165 @@
|
||||
#ifndef OT_LAYOUT_GSUB_SEQUENCE_HH
|
||||
#define OT_LAYOUT_GSUB_SEQUENCE_HH
|
||||
|
||||
#include "Common.hh"
|
||||
|
||||
namespace OT {
|
||||
namespace Layout {
|
||||
namespace GSUB_impl {
|
||||
|
||||
template <typename Types>
|
||||
struct Sequence
|
||||
{
|
||||
protected:
|
||||
Array16Of<typename Types::HBGlyphID>
|
||||
substitute; /* String of GlyphIDs to substitute */
|
||||
public:
|
||||
DEFINE_SIZE_ARRAY (2, substitute);
|
||||
|
||||
bool sanitize (hb_sanitize_context_t *c) const
|
||||
{
|
||||
TRACE_SANITIZE (this);
|
||||
return_trace (substitute.sanitize (c));
|
||||
}
|
||||
|
||||
bool intersects (const hb_set_t *glyphs) const
|
||||
{ return hb_all (substitute, glyphs); }
|
||||
|
||||
void closure (hb_closure_context_t *c) const
|
||||
{ c->output->add_array (substitute.arrayZ, substitute.len); }
|
||||
|
||||
void collect_glyphs (hb_collect_glyphs_context_t *c) const
|
||||
{ c->output->add_array (substitute.arrayZ, substitute.len); }
|
||||
|
||||
bool apply (hb_ot_apply_context_t *c) const
|
||||
{
|
||||
TRACE_APPLY (this);
|
||||
unsigned int count = substitute.len;
|
||||
|
||||
/* Special-case to make it in-place and not consider this
|
||||
* as a "multiplied" substitution. */
|
||||
if (unlikely (count == 1))
|
||||
{
|
||||
if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
|
||||
{
|
||||
c->buffer->sync_so_far ();
|
||||
c->buffer->message (c->font,
|
||||
"replacing glyph at %u (multiple substitution)",
|
||||
c->buffer->idx);
|
||||
}
|
||||
|
||||
c->replace_glyph (substitute.arrayZ[0]);
|
||||
|
||||
if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
|
||||
{
|
||||
c->buffer->message (c->font,
|
||||
"replaced glyph at %u (multiple substitution)",
|
||||
c->buffer->idx - 1u);
|
||||
}
|
||||
|
||||
return_trace (true);
|
||||
}
|
||||
/* Spec disallows this, but Uniscribe allows it.
|
||||
* https://github.com/harfbuzz/harfbuzz/issues/253 */
|
||||
else if (unlikely (count == 0))
|
||||
{
|
||||
if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
|
||||
{
|
||||
c->buffer->sync_so_far ();
|
||||
c->buffer->message (c->font,
|
||||
"deleting glyph at %u (multiple substitution)",
|
||||
c->buffer->idx);
|
||||
}
|
||||
|
||||
c->buffer->delete_glyph ();
|
||||
|
||||
if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
|
||||
{
|
||||
c->buffer->sync_so_far ();
|
||||
c->buffer->message (c->font,
|
||||
"deleted glyph at %u (multiple substitution)",
|
||||
c->buffer->idx);
|
||||
}
|
||||
|
||||
return_trace (true);
|
||||
}
|
||||
|
||||
if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
|
||||
{
|
||||
c->buffer->sync_so_far ();
|
||||
c->buffer->message (c->font,
|
||||
"multiplying glyph at %u",
|
||||
c->buffer->idx);
|
||||
}
|
||||
|
||||
unsigned int klass = _hb_glyph_info_is_ligature (&c->buffer->cur()) ?
|
||||
HB_OT_LAYOUT_GLYPH_PROPS_BASE_GLYPH : 0;
|
||||
unsigned lig_id = _hb_glyph_info_get_lig_id (&c->buffer->cur());
|
||||
|
||||
for (unsigned int i = 0; i < count; i++)
|
||||
{
|
||||
/* If is attached to a ligature, don't disturb that.
|
||||
* https://github.com/harfbuzz/harfbuzz/issues/3069 */
|
||||
if (!lig_id)
|
||||
_hb_glyph_info_set_lig_props_for_component (&c->buffer->cur(), i);
|
||||
c->output_glyph_for_component (substitute.arrayZ[i], klass);
|
||||
}
|
||||
c->buffer->skip_glyph ();
|
||||
|
||||
if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
|
||||
{
|
||||
c->buffer->sync_so_far ();
|
||||
|
||||
char buf[HB_MAX_CONTEXT_LENGTH * 16] = {0};
|
||||
char *p = buf;
|
||||
|
||||
for (unsigned i = c->buffer->idx - count; i < c->buffer->idx; i++)
|
||||
{
|
||||
if (buf < p && sizeof(buf) - 1u > unsigned (p - buf))
|
||||
*p++ = ',';
|
||||
snprintf (p, sizeof(buf) - (p - buf), "%u", i);
|
||||
p += strlen(p);
|
||||
}
|
||||
|
||||
c->buffer->message (c->font,
|
||||
"multiplied glyphs at %s",
|
||||
buf);
|
||||
}
|
||||
|
||||
return_trace (true);
|
||||
}
|
||||
|
||||
template <typename Iterator,
|
||||
hb_requires (hb_is_source_of (Iterator, hb_codepoint_t))>
|
||||
bool serialize (hb_serialize_context_t *c,
|
||||
Iterator subst)
|
||||
{
|
||||
TRACE_SERIALIZE (this);
|
||||
return_trace (substitute.serialize (c, subst));
|
||||
}
|
||||
|
||||
bool subset (hb_subset_context_t *c) const
|
||||
{
|
||||
TRACE_SUBSET (this);
|
||||
const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
|
||||
const hb_map_t &glyph_map = *c->plan->glyph_map;
|
||||
|
||||
if (!intersects (&glyphset)) return_trace (false);
|
||||
|
||||
auto it =
|
||||
+ hb_iter (substitute)
|
||||
| hb_map (glyph_map)
|
||||
;
|
||||
|
||||
auto *out = c->serializer->start_embed (*this);
|
||||
return_trace (out->serialize (c->serializer, it));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif /* OT_LAYOUT_GSUB_SEQUENCE_HH */
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
#ifndef OT_LAYOUT_GSUB_SINGLESUBST_HH
|
||||
#define OT_LAYOUT_GSUB_SINGLESUBST_HH
|
||||
|
||||
#include "Common.hh"
|
||||
#include "SingleSubstFormat1.hh"
|
||||
#include "SingleSubstFormat2.hh"
|
||||
|
||||
namespace OT {
|
||||
namespace Layout {
|
||||
namespace GSUB_impl {
|
||||
|
||||
struct SingleSubst
|
||||
{
|
||||
protected:
|
||||
union {
|
||||
struct { HBUINT16 v; } format; /* Format identifier */
|
||||
SingleSubstFormat1_3<SmallTypes> format1;
|
||||
SingleSubstFormat2_4<SmallTypes> format2;
|
||||
#ifndef HB_NO_BEYOND_64K
|
||||
SingleSubstFormat1_3<MediumTypes> format3;
|
||||
SingleSubstFormat2_4<MediumTypes> format4;
|
||||
#endif
|
||||
} u;
|
||||
|
||||
public:
|
||||
|
||||
template <typename context_t, typename ...Ts>
|
||||
typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
|
||||
{
|
||||
if (unlikely (!c->may_dispatch (this, &u.format.v))) return c->no_dispatch_return_value ();
|
||||
TRACE_DISPATCH (this, u.format.v);
|
||||
switch (u.format.v) {
|
||||
case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...));
|
||||
case 2: return_trace (c->dispatch (u.format2, std::forward<Ts> (ds)...));
|
||||
#ifndef HB_NO_BEYOND_64K
|
||||
case 3: return_trace (c->dispatch (u.format3, std::forward<Ts> (ds)...));
|
||||
case 4: return_trace (c->dispatch (u.format4, std::forward<Ts> (ds)...));
|
||||
#endif
|
||||
default:return_trace (c->default_return_value ());
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Iterator,
|
||||
hb_requires (hb_is_sorted_source_of (Iterator,
|
||||
const hb_codepoint_pair_t))>
|
||||
bool serialize (hb_serialize_context_t *c,
|
||||
Iterator glyphs)
|
||||
{
|
||||
TRACE_SERIALIZE (this);
|
||||
if (unlikely (!c->extend_min (u.format.v))) return_trace (false);
|
||||
unsigned format = 2;
|
||||
unsigned delta = 0;
|
||||
if (glyphs)
|
||||
{
|
||||
format = 1;
|
||||
hb_codepoint_t mask = 0xFFFFu;
|
||||
|
||||
#ifndef HB_NO_BEYOND_64K
|
||||
if (+ glyphs
|
||||
| hb_map_retains_sorting (hb_second)
|
||||
| hb_filter ([] (hb_codepoint_t gid) { return gid > 0xFFFFu; }))
|
||||
{
|
||||
format += 2;
|
||||
mask = 0xFFFFFFu;
|
||||
}
|
||||
#endif
|
||||
|
||||
auto get_delta = [=] (hb_codepoint_pair_t _)
|
||||
{ return (unsigned) (_.second - _.first) & mask; };
|
||||
delta = get_delta (*glyphs);
|
||||
if (!hb_all (++(+glyphs), delta, get_delta)) format += 1;
|
||||
}
|
||||
|
||||
u.format.v = format;
|
||||
switch (u.format.v) {
|
||||
case 1: return_trace (u.format1.serialize (c,
|
||||
+ glyphs
|
||||
| hb_map_retains_sorting (hb_first),
|
||||
delta));
|
||||
case 2: return_trace (u.format2.serialize (c, glyphs));
|
||||
#ifndef HB_NO_BEYOND_64K
|
||||
case 3: return_trace (u.format3.serialize (c,
|
||||
+ glyphs
|
||||
| hb_map_retains_sorting (hb_first),
|
||||
delta));
|
||||
case 4: return_trace (u.format4.serialize (c, glyphs));
|
||||
#endif
|
||||
default:return_trace (false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Iterator>
|
||||
static void
|
||||
SingleSubst_serialize (hb_serialize_context_t *c,
|
||||
Iterator it)
|
||||
{ c->start_embed<SingleSubst> ()->serialize (c, it); }
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* OT_LAYOUT_GSUB_SINGLESUBST_HH */
|
||||
@@ -0,0 +1,219 @@
|
||||
#ifndef OT_LAYOUT_GSUB_SINGLESUBSTFORMAT1_HH
|
||||
#define OT_LAYOUT_GSUB_SINGLESUBSTFORMAT1_HH
|
||||
|
||||
#include "Common.hh"
|
||||
|
||||
namespace OT {
|
||||
namespace Layout {
|
||||
namespace GSUB_impl {
|
||||
|
||||
template <typename Types>
|
||||
struct SingleSubstFormat1_3
|
||||
{
|
||||
protected:
|
||||
HBUINT16 format; /* Format identifier--format = 1 */
|
||||
typename Types::template OffsetTo<Coverage>
|
||||
coverage; /* Offset to Coverage table--from
|
||||
* beginning of Substitution table */
|
||||
typename Types::HBUINT
|
||||
deltaGlyphID; /* Add to original GlyphID to get
|
||||
* substitute GlyphID, modulo 0x10000 */
|
||||
|
||||
public:
|
||||
DEFINE_SIZE_STATIC (2 + 2 * Types::size);
|
||||
|
||||
bool sanitize (hb_sanitize_context_t *c) const
|
||||
{
|
||||
TRACE_SANITIZE (this);
|
||||
return_trace (c->check_struct (this) &&
|
||||
coverage.sanitize (c, this) &&
|
||||
/* The coverage table may use a range to represent a set
|
||||
* of glyphs, which means a small number of bytes can
|
||||
* generate a large glyph set. Manually modify the
|
||||
* sanitizer max ops to take this into account.
|
||||
*
|
||||
* Note: This check *must* be right after coverage sanitize. */
|
||||
c->check_ops ((this + coverage).get_population () >> 1));
|
||||
}
|
||||
|
||||
hb_codepoint_t get_mask () const
|
||||
{ return (1 << (8 * Types::size)) - 1; }
|
||||
|
||||
bool intersects (const hb_set_t *glyphs) const
|
||||
{ return (this+coverage).intersects (glyphs); }
|
||||
|
||||
bool may_have_non_1to1 () const
|
||||
{ return false; }
|
||||
|
||||
void closure (hb_closure_context_t *c) const
|
||||
{
|
||||
hb_codepoint_t d = deltaGlyphID;
|
||||
hb_codepoint_t mask = get_mask ();
|
||||
|
||||
/* Help fuzzer avoid this function as much. */
|
||||
unsigned pop = (this+coverage).get_population ();
|
||||
if (pop >= mask)
|
||||
return;
|
||||
|
||||
hb_set_t intersection;
|
||||
(this+coverage).intersect_set (c->parent_active_glyphs (), intersection);
|
||||
|
||||
/* In degenerate fuzzer-found fonts, but not real fonts,
|
||||
* this table can keep adding new glyphs in each round of closure.
|
||||
* Refuse to close-over, if it maps glyph range to overlapping range. */
|
||||
hb_codepoint_t min_before = intersection.get_min ();
|
||||
hb_codepoint_t max_before = intersection.get_max ();
|
||||
hb_codepoint_t min_after = (min_before + d) & mask;
|
||||
hb_codepoint_t max_after = (max_before + d) & mask;
|
||||
if (intersection.get_population () == max_before - min_before + 1 &&
|
||||
((min_before <= min_after && min_after <= max_before) ||
|
||||
(min_before <= max_after && max_after <= max_before)))
|
||||
return;
|
||||
|
||||
+ hb_iter (intersection)
|
||||
| hb_map ([d, mask] (hb_codepoint_t g) { return (g + d) & mask; })
|
||||
| hb_sink (c->output)
|
||||
;
|
||||
}
|
||||
|
||||
void closure_lookups (hb_closure_lookups_context_t *c) const {}
|
||||
|
||||
void collect_glyphs (hb_collect_glyphs_context_t *c) const
|
||||
{
|
||||
if (unlikely (!(this+coverage).collect_coverage (c->input))) return;
|
||||
hb_codepoint_t d = deltaGlyphID;
|
||||
hb_codepoint_t mask = get_mask ();
|
||||
|
||||
+ hb_iter (this+coverage)
|
||||
| hb_map ([d, mask] (hb_codepoint_t g) { return (g + d) & mask; })
|
||||
| hb_sink (c->output)
|
||||
;
|
||||
}
|
||||
|
||||
const Coverage &get_coverage () const { return this+coverage; }
|
||||
|
||||
bool would_apply (hb_would_apply_context_t *c) const
|
||||
{ return c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED; }
|
||||
|
||||
unsigned
|
||||
get_glyph_alternates (hb_codepoint_t glyph_id,
|
||||
unsigned start_offset,
|
||||
unsigned *alternate_count /* IN/OUT. May be NULL. */,
|
||||
hb_codepoint_t *alternate_glyphs /* OUT. May be NULL. */) const
|
||||
{
|
||||
unsigned int index = (this+coverage).get_coverage (glyph_id);
|
||||
if (likely (index == NOT_COVERED))
|
||||
{
|
||||
if (alternate_count)
|
||||
*alternate_count = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (alternate_count && *alternate_count)
|
||||
{
|
||||
hb_codepoint_t d = deltaGlyphID;
|
||||
hb_codepoint_t mask = get_mask ();
|
||||
|
||||
glyph_id = (glyph_id + d) & mask;
|
||||
|
||||
*alternate_glyphs = glyph_id;
|
||||
*alternate_count = 1;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void
|
||||
collect_glyph_alternates (hb_map_t *alternate_count /* IN/OUT */,
|
||||
hb_map_t *alternate_glyphs /* IN/OUT */) const
|
||||
{
|
||||
hb_codepoint_t d = deltaGlyphID;
|
||||
hb_codepoint_t mask = get_mask ();
|
||||
|
||||
+ hb_iter (this+coverage)
|
||||
| hb_map ([d, mask] (hb_codepoint_t g) { return hb_pair (g, (g + d) & mask); })
|
||||
| hb_apply ([&] (const hb_pair_t<hb_codepoint_t, hb_codepoint_t> &p) -> void
|
||||
{ _hb_collect_glyph_alternates_add (p.first, p.second,
|
||||
alternate_count, alternate_glyphs); })
|
||||
;
|
||||
}
|
||||
|
||||
bool apply (hb_ot_apply_context_t *c) const
|
||||
{
|
||||
TRACE_APPLY (this);
|
||||
hb_codepoint_t glyph_id = c->buffer->cur().codepoint;
|
||||
unsigned int index = (this+coverage).get_coverage (glyph_id);
|
||||
if (index == NOT_COVERED) return_trace (false);
|
||||
|
||||
hb_codepoint_t d = deltaGlyphID;
|
||||
hb_codepoint_t mask = get_mask ();
|
||||
|
||||
glyph_id = (glyph_id + d) & mask;
|
||||
|
||||
if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
|
||||
{
|
||||
c->buffer->sync_so_far ();
|
||||
c->buffer->message (c->font,
|
||||
"replacing glyph at %u (single substitution)",
|
||||
c->buffer->idx);
|
||||
}
|
||||
|
||||
c->replace_glyph (glyph_id);
|
||||
|
||||
if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
|
||||
{
|
||||
c->buffer->message (c->font,
|
||||
"replaced glyph at %u (single substitution)",
|
||||
c->buffer->idx - 1u);
|
||||
}
|
||||
|
||||
return_trace (true);
|
||||
}
|
||||
|
||||
template<typename Iterator,
|
||||
hb_requires (hb_is_sorted_source_of (Iterator, hb_codepoint_t))>
|
||||
bool serialize (hb_serialize_context_t *c,
|
||||
Iterator glyphs,
|
||||
unsigned delta)
|
||||
{
|
||||
TRACE_SERIALIZE (this);
|
||||
if (unlikely (!c->extend_min (this))) return_trace (false);
|
||||
if (unlikely (!coverage.serialize_serialize (c, glyphs))) return_trace (false);
|
||||
c->check_assign (deltaGlyphID, delta, HB_SERIALIZE_ERROR_INT_OVERFLOW);
|
||||
return_trace (true);
|
||||
}
|
||||
|
||||
bool subset (hb_subset_context_t *c) const
|
||||
{
|
||||
TRACE_SUBSET (this);
|
||||
const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
|
||||
const hb_map_t &glyph_map = *c->plan->glyph_map;
|
||||
|
||||
hb_codepoint_t d = deltaGlyphID;
|
||||
hb_codepoint_t mask = get_mask ();
|
||||
|
||||
hb_set_t intersection;
|
||||
(this+coverage).intersect_set (glyphset, intersection);
|
||||
|
||||
auto it =
|
||||
+ hb_iter (intersection)
|
||||
| hb_map_retains_sorting ([d, mask] (hb_codepoint_t g) {
|
||||
return hb_codepoint_pair_t (g,
|
||||
(g + d) & mask); })
|
||||
| hb_filter (glyphset, hb_second)
|
||||
| hb_map_retains_sorting ([&] (hb_codepoint_pair_t p) -> hb_codepoint_pair_t
|
||||
{ return hb_pair (glyph_map[p.first], glyph_map[p.second]); })
|
||||
;
|
||||
|
||||
bool ret = bool (it);
|
||||
SingleSubst_serialize (c->serializer, it);
|
||||
return_trace (ret);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif /* OT_LAYOUT_GSUB_SINGLESUBSTFORMAT1_HH */
|
||||
@@ -0,0 +1,187 @@
|
||||
#ifndef OT_LAYOUT_GSUB_SINGLESUBSTFORMAT2_HH
|
||||
#define OT_LAYOUT_GSUB_SINGLESUBSTFORMAT2_HH
|
||||
|
||||
#include "Common.hh"
|
||||
|
||||
namespace OT {
|
||||
namespace Layout {
|
||||
namespace GSUB_impl {
|
||||
|
||||
template <typename Types>
|
||||
struct SingleSubstFormat2_4
|
||||
{
|
||||
protected:
|
||||
HBUINT16 format; /* Format identifier--format = 2 */
|
||||
typename Types::template OffsetTo<Coverage>
|
||||
coverage; /* Offset to Coverage table--from
|
||||
* beginning of Substitution table */
|
||||
Array16Of<typename Types::HBGlyphID>
|
||||
substitute; /* Array of substitute
|
||||
* GlyphIDs--ordered by Coverage Index */
|
||||
|
||||
public:
|
||||
DEFINE_SIZE_ARRAY (4 + Types::size, substitute);
|
||||
|
||||
bool sanitize (hb_sanitize_context_t *c) const
|
||||
{
|
||||
TRACE_SANITIZE (this);
|
||||
return_trace (coverage.sanitize (c, this) && substitute.sanitize (c));
|
||||
}
|
||||
|
||||
bool intersects (const hb_set_t *glyphs) const
|
||||
{ return (this+coverage).intersects (glyphs); }
|
||||
|
||||
bool may_have_non_1to1 () const
|
||||
{ return false; }
|
||||
|
||||
void closure (hb_closure_context_t *c) const
|
||||
{
|
||||
auto &cov = this+coverage;
|
||||
auto &glyph_set = c->parent_active_glyphs ();
|
||||
|
||||
if (substitute.len > glyph_set.get_population () * 4)
|
||||
{
|
||||
for (auto g : glyph_set)
|
||||
{
|
||||
unsigned i = cov.get_coverage (g);
|
||||
if (i == NOT_COVERED || i >= substitute.len)
|
||||
continue;
|
||||
c->output->add (substitute.arrayZ[i]);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
+ hb_zip (cov, substitute)
|
||||
| hb_filter (glyph_set, hb_first)
|
||||
| hb_map (hb_second)
|
||||
| hb_sink (c->output)
|
||||
;
|
||||
}
|
||||
|
||||
void closure_lookups (hb_closure_lookups_context_t *c) const {}
|
||||
|
||||
void collect_glyphs (hb_collect_glyphs_context_t *c) const
|
||||
{
|
||||
if (unlikely (!(this+coverage).collect_coverage (c->input))) return;
|
||||
+ hb_zip (this+coverage, substitute)
|
||||
| hb_map (hb_second)
|
||||
| hb_sink (c->output)
|
||||
;
|
||||
}
|
||||
|
||||
const Coverage &get_coverage () const { return this+coverage; }
|
||||
|
||||
bool would_apply (hb_would_apply_context_t *c) const
|
||||
{ return c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED; }
|
||||
|
||||
unsigned
|
||||
get_glyph_alternates (hb_codepoint_t glyph_id,
|
||||
unsigned start_offset,
|
||||
unsigned *alternate_count /* IN/OUT. May be NULL. */,
|
||||
hb_codepoint_t *alternate_glyphs /* OUT. May be NULL. */) const
|
||||
{
|
||||
unsigned int index = (this+coverage).get_coverage (glyph_id);
|
||||
if (likely (index == NOT_COVERED))
|
||||
{
|
||||
if (alternate_count)
|
||||
*alternate_count = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (alternate_count && *alternate_count)
|
||||
{
|
||||
glyph_id = substitute[index];
|
||||
|
||||
*alternate_glyphs = glyph_id;
|
||||
*alternate_count = 1;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void
|
||||
collect_glyph_alternates (hb_map_t *alternate_count /* IN/OUT */,
|
||||
hb_map_t *alternate_glyphs /* IN/OUT */) const
|
||||
{
|
||||
+ hb_zip (this+coverage, substitute)
|
||||
| hb_apply ([&] (const hb_pair_t<hb_codepoint_t, hb_codepoint_t> &p) -> void
|
||||
{ _hb_collect_glyph_alternates_add (p.first, p.second,
|
||||
alternate_count, alternate_glyphs); })
|
||||
;
|
||||
}
|
||||
|
||||
bool apply (hb_ot_apply_context_t *c) const
|
||||
{
|
||||
TRACE_APPLY (this);
|
||||
unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
|
||||
if (index == NOT_COVERED) return_trace (false);
|
||||
|
||||
if (unlikely (index >= substitute.len)) return_trace (false);
|
||||
|
||||
if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
|
||||
{
|
||||
c->buffer->sync_so_far ();
|
||||
c->buffer->message (c->font,
|
||||
"replacing glyph at %u (single substitution)",
|
||||
c->buffer->idx);
|
||||
}
|
||||
|
||||
c->replace_glyph (substitute[index]);
|
||||
|
||||
if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
|
||||
{
|
||||
c->buffer->message (c->font,
|
||||
"replaced glyph at %u (single substitution)",
|
||||
c->buffer->idx - 1u);
|
||||
}
|
||||
|
||||
return_trace (true);
|
||||
}
|
||||
|
||||
template<typename Iterator,
|
||||
hb_requires (hb_is_sorted_source_of (Iterator,
|
||||
hb_codepoint_pair_t))>
|
||||
bool serialize (hb_serialize_context_t *c,
|
||||
Iterator it)
|
||||
{
|
||||
TRACE_SERIALIZE (this);
|
||||
auto substitutes =
|
||||
+ it
|
||||
| hb_map (hb_second)
|
||||
;
|
||||
auto glyphs =
|
||||
+ it
|
||||
| hb_map_retains_sorting (hb_first)
|
||||
;
|
||||
if (unlikely (!c->extend_min (this))) return_trace (false);
|
||||
if (unlikely (!substitute.serialize (c, substitutes))) return_trace (false);
|
||||
if (unlikely (!coverage.serialize_serialize (c, glyphs))) return_trace (false);
|
||||
return_trace (true);
|
||||
}
|
||||
|
||||
bool subset (hb_subset_context_t *c) const
|
||||
{
|
||||
TRACE_SUBSET (this);
|
||||
const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
|
||||
const hb_map_t &glyph_map = *c->plan->glyph_map;
|
||||
|
||||
auto it =
|
||||
+ hb_zip (this+coverage, substitute)
|
||||
| hb_filter (glyphset, hb_first)
|
||||
| hb_filter (glyphset, hb_second)
|
||||
| hb_map_retains_sorting ([&] (hb_pair_t<hb_codepoint_t, const typename Types::HBGlyphID &> p) -> hb_codepoint_pair_t
|
||||
{ return hb_pair (glyph_map[p.first], glyph_map[p.second]); })
|
||||
;
|
||||
|
||||
bool ret = bool (it);
|
||||
SingleSubst_serialize (c->serializer, it);
|
||||
return_trace (ret);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* OT_LAYOUT_GSUB_SINGLESUBSTFORMAT2_HH */
|
||||
+220
@@ -0,0 +1,220 @@
|
||||
#ifndef OT_LAYOUT_GSUB_SUBSTLOOKUP_HH
|
||||
#define OT_LAYOUT_GSUB_SUBSTLOOKUP_HH
|
||||
|
||||
#include "Common.hh"
|
||||
#include "SubstLookupSubTable.hh"
|
||||
|
||||
namespace OT {
|
||||
namespace Layout {
|
||||
namespace GSUB_impl {
|
||||
|
||||
struct SubstLookup : Lookup
|
||||
{
|
||||
using SubTable = SubstLookupSubTable;
|
||||
|
||||
bool sanitize (hb_sanitize_context_t *c) const
|
||||
{ return Lookup::sanitize<SubTable> (c); }
|
||||
|
||||
const SubTable& get_subtable (unsigned int i) const
|
||||
{ return Lookup::get_subtable<SubTable> (i); }
|
||||
|
||||
static inline bool lookup_type_is_reverse (unsigned int lookup_type)
|
||||
{ return lookup_type == SubTable::ReverseChainSingle; }
|
||||
|
||||
bool is_reverse () const
|
||||
{
|
||||
unsigned int type = get_type ();
|
||||
if (unlikely (type == SubTable::Extension))
|
||||
return get_subtable (0).u.extension.is_reverse ();
|
||||
return lookup_type_is_reverse (type);
|
||||
}
|
||||
|
||||
bool may_have_non_1to1 () const
|
||||
{
|
||||
hb_have_non_1to1_context_t c;
|
||||
return dispatch (&c);
|
||||
}
|
||||
|
||||
bool apply (hb_ot_apply_context_t *c) const
|
||||
{
|
||||
TRACE_APPLY (this);
|
||||
return_trace (dispatch (c));
|
||||
}
|
||||
|
||||
bool intersects (const hb_set_t *glyphs) const
|
||||
{
|
||||
hb_intersects_context_t c (glyphs);
|
||||
return dispatch (&c);
|
||||
}
|
||||
|
||||
hb_closure_context_t::return_t closure (hb_closure_context_t *c, unsigned int this_index) const
|
||||
{
|
||||
if (!c->should_visit_lookup (this_index))
|
||||
return hb_closure_context_t::default_return_value ();
|
||||
|
||||
c->set_recurse_func (dispatch_closure_recurse_func);
|
||||
|
||||
hb_closure_context_t::return_t ret = dispatch (c);
|
||||
|
||||
c->flush ();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
hb_closure_lookups_context_t::return_t closure_lookups (hb_closure_lookups_context_t *c, unsigned this_index) const
|
||||
{
|
||||
if (c->is_lookup_visited (this_index))
|
||||
return hb_closure_lookups_context_t::default_return_value ();
|
||||
|
||||
c->set_lookup_visited (this_index);
|
||||
if (!intersects (c->glyphs))
|
||||
{
|
||||
c->set_lookup_inactive (this_index);
|
||||
return hb_closure_lookups_context_t::default_return_value ();
|
||||
}
|
||||
|
||||
hb_closure_lookups_context_t::return_t ret = dispatch (c);
|
||||
return ret;
|
||||
}
|
||||
|
||||
hb_collect_glyphs_context_t::return_t collect_glyphs (hb_collect_glyphs_context_t *c) const
|
||||
{
|
||||
c->set_recurse_func (dispatch_recurse_func<hb_collect_glyphs_context_t>);
|
||||
return dispatch (c);
|
||||
}
|
||||
|
||||
template <typename set_t>
|
||||
void collect_coverage (set_t *glyphs) const
|
||||
{
|
||||
hb_collect_coverage_context_t<set_t> c (glyphs);
|
||||
dispatch (&c);
|
||||
}
|
||||
|
||||
bool would_apply (hb_would_apply_context_t *c,
|
||||
const hb_ot_layout_lookup_accelerator_t *accel) const
|
||||
{
|
||||
if (unlikely (!c->len)) return false;
|
||||
if (!accel->may_have (c->glyphs[0])) return false;
|
||||
return dispatch (c);
|
||||
}
|
||||
|
||||
template<typename Glyphs, typename Substitutes,
|
||||
hb_requires (hb_is_sorted_source_of (Glyphs,
|
||||
const hb_codepoint_t) &&
|
||||
hb_is_source_of (Substitutes,
|
||||
const hb_codepoint_t))>
|
||||
bool serialize_single (hb_serialize_context_t *c,
|
||||
uint32_t lookup_props,
|
||||
Glyphs glyphs,
|
||||
Substitutes substitutes)
|
||||
{
|
||||
TRACE_SERIALIZE (this);
|
||||
if (unlikely (!Lookup::serialize (c, SubTable::Single, lookup_props, 1))) return_trace (false);
|
||||
if (c->push<SubTable> ()->u.single.serialize (c, hb_zip (glyphs, substitutes)))
|
||||
{
|
||||
c->add_link (get_subtables<SubTable> ()[0], c->pop_pack ());
|
||||
return_trace (true);
|
||||
}
|
||||
c->pop_discard ();
|
||||
return_trace (false);
|
||||
}
|
||||
|
||||
template<typename Iterator,
|
||||
hb_requires (hb_is_sorted_iterator (Iterator))>
|
||||
bool serialize (hb_serialize_context_t *c,
|
||||
uint32_t lookup_props,
|
||||
Iterator it)
|
||||
{
|
||||
TRACE_SERIALIZE (this);
|
||||
if (unlikely (!Lookup::serialize (c, SubTable::Multiple, lookup_props, 1))) return_trace (false);
|
||||
if (c->push<SubTable> ()->u.multiple.
|
||||
serialize (c, it))
|
||||
{
|
||||
c->add_link (get_subtables<SubTable> ()[0], c->pop_pack ());
|
||||
return_trace (true);
|
||||
}
|
||||
c->pop_discard ();
|
||||
return_trace (false);
|
||||
}
|
||||
|
||||
bool serialize_alternate (hb_serialize_context_t *c,
|
||||
uint32_t lookup_props,
|
||||
hb_sorted_array_t<const HBGlyphID16> glyphs,
|
||||
hb_array_t<const unsigned int> alternate_len_list,
|
||||
hb_array_t<const HBGlyphID16> alternate_glyphs_list)
|
||||
{
|
||||
TRACE_SERIALIZE (this);
|
||||
if (unlikely (!Lookup::serialize (c, SubTable::Alternate, lookup_props, 1))) return_trace (false);
|
||||
|
||||
if (c->push<SubTable> ()->u.alternate.
|
||||
serialize (c,
|
||||
glyphs,
|
||||
alternate_len_list,
|
||||
alternate_glyphs_list))
|
||||
{
|
||||
c->add_link (get_subtables<SubTable> ()[0], c->pop_pack ());
|
||||
return_trace (true);
|
||||
}
|
||||
c->pop_discard ();
|
||||
return_trace (false);
|
||||
}
|
||||
|
||||
bool serialize_ligature (hb_serialize_context_t *c,
|
||||
uint32_t lookup_props,
|
||||
hb_sorted_array_t<const HBGlyphID16> first_glyphs,
|
||||
hb_array_t<const unsigned int> ligature_per_first_glyph_count_list,
|
||||
hb_array_t<const HBGlyphID16> ligatures_list,
|
||||
hb_array_t<const unsigned int> component_count_list,
|
||||
hb_array_t<const HBGlyphID16> component_list /* Starting from second for each ligature */)
|
||||
{
|
||||
TRACE_SERIALIZE (this);
|
||||
if (unlikely (!Lookup::serialize (c, SubTable::Ligature, lookup_props, 1))) return_trace (false);
|
||||
if (c->push<SubTable> ()->u.ligature.
|
||||
serialize (c,
|
||||
first_glyphs,
|
||||
ligature_per_first_glyph_count_list,
|
||||
ligatures_list,
|
||||
component_count_list,
|
||||
component_list))
|
||||
{
|
||||
c->add_link (get_subtables<SubTable> ()[0], c->pop_pack ());
|
||||
return_trace (true);
|
||||
}
|
||||
c->pop_discard ();
|
||||
return_trace (false);
|
||||
}
|
||||
|
||||
template <typename context_t>
|
||||
static inline typename context_t::return_t dispatch_recurse_func (context_t *c, unsigned int lookup_index);
|
||||
|
||||
static inline typename hb_closure_context_t::return_t closure_glyphs_recurse_func (hb_closure_context_t *c, unsigned lookup_index, hb_set_t *covered_seq_indices, unsigned seq_index, unsigned end_index);
|
||||
|
||||
static inline hb_closure_context_t::return_t dispatch_closure_recurse_func (hb_closure_context_t *c, unsigned lookup_index, hb_set_t *covered_seq_indices, unsigned seq_index, unsigned end_index)
|
||||
{
|
||||
if (!c->should_visit_lookup (lookup_index))
|
||||
return hb_empty_t ();
|
||||
|
||||
hb_closure_context_t::return_t ret = closure_glyphs_recurse_func (c, lookup_index, covered_seq_indices, seq_index, end_index);
|
||||
|
||||
/* While in theory we should flush here, it will cause timeouts because a recursive
|
||||
* lookup can keep growing the glyph set. Skip, and outer loop will retry up to
|
||||
* HB_CLOSURE_MAX_STAGES time, which should be enough for every realistic font. */
|
||||
//c->flush ();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <typename context_t, typename ...Ts>
|
||||
typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
|
||||
{ return Lookup::dispatch<SubTable> (c, std::forward<Ts> (ds)...); }
|
||||
|
||||
bool subset (hb_subset_context_t *c) const
|
||||
{ return Lookup::subset<SubTable> (c); }
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* OT_LAYOUT_GSUB_SUBSTLOOKUP_HH */
|
||||
@@ -0,0 +1,77 @@
|
||||
#ifndef OT_LAYOUT_GSUB_SUBSTLOOKUPSUBTABLE_HH
|
||||
#define OT_LAYOUT_GSUB_SUBSTLOOKUPSUBTABLE_HH
|
||||
|
||||
#include "Common.hh"
|
||||
#include "SingleSubst.hh"
|
||||
#include "MultipleSubst.hh"
|
||||
#include "AlternateSubst.hh"
|
||||
#include "LigatureSubst.hh"
|
||||
#include "ContextSubst.hh"
|
||||
#include "ChainContextSubst.hh"
|
||||
#include "ExtensionSubst.hh"
|
||||
#include "ReverseChainSingleSubst.hh"
|
||||
|
||||
namespace OT {
|
||||
namespace Layout {
|
||||
namespace GSUB_impl {
|
||||
|
||||
struct SubstLookupSubTable
|
||||
{
|
||||
friend struct ::OT::Lookup;
|
||||
friend struct SubstLookup;
|
||||
|
||||
protected:
|
||||
union {
|
||||
SingleSubst single;
|
||||
MultipleSubst multiple;
|
||||
AlternateSubst alternate;
|
||||
LigatureSubst ligature;
|
||||
ContextSubst context;
|
||||
ChainContextSubst chainContext;
|
||||
ExtensionSubst extension;
|
||||
ReverseChainSingleSubst reverseChainContextSingle;
|
||||
} u;
|
||||
public:
|
||||
DEFINE_SIZE_MIN (0);
|
||||
|
||||
enum Type {
|
||||
Single = 1,
|
||||
Multiple = 2,
|
||||
Alternate = 3,
|
||||
Ligature = 4,
|
||||
Context = 5,
|
||||
ChainContext = 6,
|
||||
Extension = 7,
|
||||
ReverseChainSingle = 8
|
||||
};
|
||||
|
||||
template <typename context_t, typename ...Ts>
|
||||
typename context_t::return_t dispatch (context_t *c, unsigned int lookup_type, Ts&&... ds) const
|
||||
{
|
||||
TRACE_DISPATCH (this, lookup_type);
|
||||
switch (lookup_type) {
|
||||
case Single: return_trace (u.single.dispatch (c, std::forward<Ts> (ds)...));
|
||||
case Multiple: return_trace (u.multiple.dispatch (c, std::forward<Ts> (ds)...));
|
||||
case Alternate: return_trace (u.alternate.dispatch (c, std::forward<Ts> (ds)...));
|
||||
case Ligature: return_trace (u.ligature.dispatch (c, std::forward<Ts> (ds)...));
|
||||
case Context: return_trace (u.context.dispatch (c, std::forward<Ts> (ds)...));
|
||||
case ChainContext: return_trace (u.chainContext.dispatch (c, std::forward<Ts> (ds)...));
|
||||
case Extension: return_trace (u.extension.dispatch (c, std::forward<Ts> (ds)...));
|
||||
case ReverseChainSingle: return_trace (u.reverseChainContextSingle.dispatch (c, std::forward<Ts> (ds)...));
|
||||
default: return_trace (c->default_return_value ());
|
||||
}
|
||||
}
|
||||
|
||||
bool intersects (const hb_set_t *glyphs, unsigned int lookup_type) const
|
||||
{
|
||||
hb_intersects_context_t c (glyphs);
|
||||
return dispatch (&c, lookup_type);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* HB_OT_LAYOUT_GSUB_SUBSTLOOKUPSUBTABLE_HH */
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright © 2007,2008,2009 Red Hat, Inc.
|
||||
* Copyright © 2010,2012 Google, Inc.
|
||||
*
|
||||
* This is part of HarfBuzz, a text shaping library.
|
||||
*
|
||||
* Permission is hereby granted, without written agreement and without
|
||||
* license or royalty fees, to use, copy, modify, and distribute this
|
||||
* software and its documentation for any purpose, provided that the
|
||||
* above copyright notice and the following two paragraphs appear in
|
||||
* all copies of this software.
|
||||
*
|
||||
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
|
||||
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
|
||||
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
|
||||
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||
* DAMAGE.
|
||||
*
|
||||
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
|
||||
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
|
||||
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
|
||||
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
|
||||
*
|
||||
* Red Hat Author(s): Behdad Esfahbod
|
||||
* Google Author(s): Behdad Esfahbod, Garret Rieger
|
||||
*/
|
||||
|
||||
#ifndef OT_LAYOUT_TYPES_HH
|
||||
#define OT_LAYOUT_TYPES_HH
|
||||
|
||||
using hb_ot_layout_mapping_cache_t = hb_cache_t<16, 8, 8>;
|
||||
static_assert (sizeof (hb_ot_layout_mapping_cache_t) == 512, "");
|
||||
|
||||
using hb_ot_layout_binary_cache_t = hb_cache_t<14, 1, 8>;
|
||||
static_assert (sizeof (hb_ot_layout_binary_cache_t) == 256, "");
|
||||
|
||||
namespace OT {
|
||||
namespace Layout {
|
||||
|
||||
struct SmallTypes {
|
||||
static constexpr unsigned size = 2;
|
||||
using large_int = uint32_t;
|
||||
using HBUINT = HBUINT16;
|
||||
using HBGlyphID = HBGlyphID16;
|
||||
using Offset = Offset16;
|
||||
template <typename Type, typename BaseType=void, bool has_null=true>
|
||||
using OffsetTo = OT::Offset16To<Type, BaseType, has_null>;
|
||||
template <typename Type>
|
||||
using ArrayOf = OT::Array16Of<Type>;
|
||||
template <typename Type>
|
||||
using SortedArrayOf = OT::SortedArray16Of<Type>;
|
||||
};
|
||||
|
||||
struct MediumTypes {
|
||||
static constexpr unsigned size = 3;
|
||||
using large_int = uint64_t;
|
||||
using HBUINT = HBUINT24;
|
||||
using HBGlyphID = HBGlyphID24;
|
||||
using Offset = Offset24;
|
||||
template <typename Type, typename BaseType=void, bool has_null=true>
|
||||
using OffsetTo = OT::Offset24To<Type, BaseType, has_null>;
|
||||
template <typename Type>
|
||||
using ArrayOf = OT::Array24Of<Type>;
|
||||
template <typename Type>
|
||||
using SortedArrayOf = OT::SortedArray24Of<Type>;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* OT_LAYOUT_TYPES_HH */
|
||||
+421
@@ -0,0 +1,421 @@
|
||||
#include "VARC.hh"
|
||||
|
||||
#ifndef HB_NO_VAR_COMPOSITES
|
||||
|
||||
#include "../../../hb-draw.hh"
|
||||
#include "../../../hb-ot-layout-common.hh"
|
||||
#include "../../../hb-ot-layout-gdef-table.hh"
|
||||
|
||||
namespace OT {
|
||||
|
||||
//namespace Var {
|
||||
|
||||
|
||||
#ifndef HB_NO_DRAW
|
||||
|
||||
struct hb_transforming_pen_context_t
|
||||
{
|
||||
hb_transform_t<> transform;
|
||||
hb_draw_funcs_t *dfuncs;
|
||||
void *data;
|
||||
hb_draw_state_t *st;
|
||||
};
|
||||
|
||||
static void
|
||||
hb_transforming_pen_move_to (hb_draw_funcs_t *dfuncs HB_UNUSED,
|
||||
void *data,
|
||||
hb_draw_state_t *st,
|
||||
float to_x, float to_y,
|
||||
void *user_data HB_UNUSED)
|
||||
{
|
||||
hb_transforming_pen_context_t *c = (hb_transforming_pen_context_t *) data;
|
||||
|
||||
c->transform.transform_point (to_x, to_y);
|
||||
|
||||
c->dfuncs->move_to (c->data, *c->st, to_x, to_y);
|
||||
}
|
||||
|
||||
static void
|
||||
hb_transforming_pen_line_to (hb_draw_funcs_t *dfuncs HB_UNUSED,
|
||||
void *data,
|
||||
hb_draw_state_t *st,
|
||||
float to_x, float to_y,
|
||||
void *user_data HB_UNUSED)
|
||||
{
|
||||
hb_transforming_pen_context_t *c = (hb_transforming_pen_context_t *) data;
|
||||
|
||||
c->transform.transform_point (to_x, to_y);
|
||||
|
||||
c->dfuncs->line_to (c->data, *c->st, to_x, to_y);
|
||||
}
|
||||
|
||||
static void
|
||||
hb_transforming_pen_quadratic_to (hb_draw_funcs_t *dfuncs HB_UNUSED,
|
||||
void *data,
|
||||
hb_draw_state_t *st,
|
||||
float control_x, float control_y,
|
||||
float to_x, float to_y,
|
||||
void *user_data HB_UNUSED)
|
||||
{
|
||||
hb_transforming_pen_context_t *c = (hb_transforming_pen_context_t *) data;
|
||||
|
||||
c->transform.transform_point (control_x, control_y);
|
||||
c->transform.transform_point (to_x, to_y);
|
||||
|
||||
c->dfuncs->quadratic_to (c->data, *c->st, control_x, control_y, to_x, to_y);
|
||||
}
|
||||
|
||||
static void
|
||||
hb_transforming_pen_cubic_to (hb_draw_funcs_t *dfuncs HB_UNUSED,
|
||||
void *data,
|
||||
hb_draw_state_t *st,
|
||||
float control1_x, float control1_y,
|
||||
float control2_x, float control2_y,
|
||||
float to_x, float to_y,
|
||||
void *user_data HB_UNUSED)
|
||||
{
|
||||
hb_transforming_pen_context_t *c = (hb_transforming_pen_context_t *) data;
|
||||
|
||||
c->transform.transform_point (control1_x, control1_y);
|
||||
c->transform.transform_point (control2_x, control2_y);
|
||||
c->transform.transform_point (to_x, to_y);
|
||||
|
||||
c->dfuncs->cubic_to (c->data, *c->st, control1_x, control1_y, control2_x, control2_y, to_x, to_y);
|
||||
}
|
||||
|
||||
static void
|
||||
hb_transforming_pen_close_path (hb_draw_funcs_t *dfuncs HB_UNUSED,
|
||||
void *data,
|
||||
hb_draw_state_t *st,
|
||||
void *user_data HB_UNUSED)
|
||||
{
|
||||
hb_transforming_pen_context_t *c = (hb_transforming_pen_context_t *) data;
|
||||
|
||||
c->dfuncs->close_path (c->data, *c->st);
|
||||
}
|
||||
|
||||
static inline void free_static_transforming_pen_funcs ();
|
||||
|
||||
static struct hb_transforming_pen_funcs_lazy_loader_t : hb_draw_funcs_lazy_loader_t<hb_transforming_pen_funcs_lazy_loader_t>
|
||||
{
|
||||
static hb_draw_funcs_t *create ()
|
||||
{
|
||||
hb_draw_funcs_t *funcs = hb_draw_funcs_create ();
|
||||
|
||||
hb_draw_funcs_set_move_to_func (funcs, hb_transforming_pen_move_to, nullptr, nullptr);
|
||||
hb_draw_funcs_set_line_to_func (funcs, hb_transforming_pen_line_to, nullptr, nullptr);
|
||||
hb_draw_funcs_set_quadratic_to_func (funcs, hb_transforming_pen_quadratic_to, nullptr, nullptr);
|
||||
hb_draw_funcs_set_cubic_to_func (funcs, hb_transforming_pen_cubic_to, nullptr, nullptr);
|
||||
hb_draw_funcs_set_close_path_func (funcs, hb_transforming_pen_close_path, nullptr, nullptr);
|
||||
|
||||
hb_draw_funcs_make_immutable (funcs);
|
||||
|
||||
hb_atexit (free_static_transforming_pen_funcs);
|
||||
|
||||
return funcs;
|
||||
}
|
||||
} static_transforming_pen_funcs;
|
||||
|
||||
static inline
|
||||
void free_static_transforming_pen_funcs ()
|
||||
{
|
||||
static_transforming_pen_funcs.free_instance ();
|
||||
}
|
||||
|
||||
static hb_draw_funcs_t *
|
||||
hb_transforming_pen_get_funcs ()
|
||||
{
|
||||
return static_transforming_pen_funcs.get_unconst ();
|
||||
}
|
||||
|
||||
hb_ubytes_t
|
||||
VarComponent::get_path_at (const hb_varc_context_t &c,
|
||||
hb_codepoint_t parent_gid,
|
||||
hb_array_t<const int> coords,
|
||||
hb_transform_t<> total_transform,
|
||||
hb_ubytes_t total_record,
|
||||
hb_scalar_cache_t *cache) const
|
||||
{
|
||||
const unsigned char *end = total_record.arrayZ + total_record.length;
|
||||
const unsigned char *record = total_record.arrayZ;
|
||||
|
||||
auto &VARC = *c.font->face->table.VARC->table;
|
||||
auto &varStore = &VARC+VARC.varStore;
|
||||
|
||||
#define READ_UINT32VAR(name) \
|
||||
HB_STMT_START { \
|
||||
if (unlikely (unsigned (end - record) < HBUINT32VAR::min_size)) return hb_ubytes_t (); \
|
||||
hb_barrier (); \
|
||||
auto &varint = * (const HBUINT32VAR *) record; \
|
||||
unsigned size = varint.get_size (); \
|
||||
if (unlikely (unsigned (end - record) < size)) return hb_ubytes_t (); \
|
||||
name = (uint32_t) varint; \
|
||||
record += size; \
|
||||
} HB_STMT_END
|
||||
|
||||
uint32_t flags;
|
||||
READ_UINT32VAR (flags);
|
||||
|
||||
// gid
|
||||
|
||||
hb_codepoint_t gid = 0;
|
||||
if (flags & (unsigned) flags_t::GID_IS_24BIT)
|
||||
{
|
||||
if (unlikely (unsigned (end - record) < HBGlyphID24::static_size))
|
||||
return hb_ubytes_t ();
|
||||
hb_barrier ();
|
||||
gid = * (const HBGlyphID24 *) record;
|
||||
record += HBGlyphID24::static_size;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (unlikely (unsigned (end - record) < HBGlyphID16::static_size))
|
||||
return hb_ubytes_t ();
|
||||
hb_barrier ();
|
||||
gid = * (const HBGlyphID16 *) record;
|
||||
record += HBGlyphID16::static_size;
|
||||
}
|
||||
|
||||
// Condition
|
||||
bool show = true;
|
||||
if (flags & (unsigned) flags_t::HAVE_CONDITION)
|
||||
{
|
||||
unsigned conditionIndex;
|
||||
READ_UINT32VAR (conditionIndex);
|
||||
const auto &condition = (&VARC+VARC.conditionList)[conditionIndex];
|
||||
auto instancer = MultiItemVarStoreInstancer(&varStore, nullptr, coords, cache);
|
||||
show = condition.evaluate (coords.arrayZ, coords.length, &instancer);
|
||||
}
|
||||
|
||||
// Axis values
|
||||
|
||||
auto &axisIndices = c.scratch.axisIndices;
|
||||
axisIndices.clear ();
|
||||
auto &axisValues = c.scratch.axisValues;
|
||||
axisValues.clear ();
|
||||
if (flags & (unsigned) flags_t::HAVE_AXES)
|
||||
{
|
||||
unsigned axisIndicesIndex;
|
||||
READ_UINT32VAR (axisIndicesIndex);
|
||||
axisIndices.extend ((&VARC+VARC.axisIndicesList)[axisIndicesIndex]);
|
||||
axisValues.resize (axisIndices.length);
|
||||
const HBUINT8 *p = (const HBUINT8 *) record;
|
||||
TupleValues::decompile (p, axisValues, (const HBUINT8 *) end);
|
||||
record = (const unsigned char *) p;
|
||||
}
|
||||
|
||||
// Apply variations if any
|
||||
if (flags & (unsigned) flags_t::AXIS_VALUES_HAVE_VARIATION)
|
||||
{
|
||||
uint32_t axisValuesVarIdx;
|
||||
READ_UINT32VAR (axisValuesVarIdx);
|
||||
if (show && coords && !axisValues.in_error ())
|
||||
varStore.get_delta (axisValuesVarIdx, coords, axisValues.as_array (), cache);
|
||||
}
|
||||
|
||||
auto component_coords = coords;
|
||||
/* Copying coords is expensive; so we have put an arbitrary
|
||||
* limit on the max number of coords for now. */
|
||||
if ((flags & (unsigned) flags_t::RESET_UNSPECIFIED_AXES) ||
|
||||
coords.length > HB_VAR_COMPOSITE_MAX_AXES)
|
||||
component_coords = hb_array (c.font->coords, c.font->num_coords);
|
||||
|
||||
// Transform
|
||||
|
||||
uint32_t transformVarIdx = VarIdx::NO_VARIATION;
|
||||
if (flags & (unsigned) flags_t::TRANSFORM_HAS_VARIATION)
|
||||
READ_UINT32VAR (transformVarIdx);
|
||||
|
||||
#define PROCESS_TRANSFORM_COMPONENTS \
|
||||
HB_STMT_START { \
|
||||
PROCESS_TRANSFORM_COMPONENT ( 0, FWORD, HAVE_TRANSLATE_X, translateX); \
|
||||
PROCESS_TRANSFORM_COMPONENT ( 0, FWORD, HAVE_TRANSLATE_Y, translateY); \
|
||||
PROCESS_TRANSFORM_COMPONENT (12, F4DOT12, HAVE_ROTATION, rotation); \
|
||||
PROCESS_TRANSFORM_COMPONENT (10, F6DOT10, HAVE_SCALE_X, scaleX); \
|
||||
PROCESS_TRANSFORM_COMPONENT (10, F6DOT10, HAVE_SCALE_Y, scaleY); \
|
||||
PROCESS_TRANSFORM_COMPONENT (12, F4DOT12, HAVE_SKEW_X, skewX); \
|
||||
PROCESS_TRANSFORM_COMPONENT (12, F4DOT12, HAVE_SKEW_Y, skewY); \
|
||||
PROCESS_TRANSFORM_COMPONENT ( 0, FWORD, HAVE_TCENTER_X, tCenterX); \
|
||||
PROCESS_TRANSFORM_COMPONENT ( 0, FWORD, HAVE_TCENTER_Y, tCenterY); \
|
||||
} HB_STMT_END
|
||||
|
||||
hb_transform_decomposed_t<> transform;
|
||||
|
||||
// Read transform components
|
||||
#define PROCESS_TRANSFORM_COMPONENT(shift, type, flag, name) \
|
||||
if (flags & (unsigned) flags_t::flag) \
|
||||
{ \
|
||||
static_assert (type::static_size == HBINT16::static_size, ""); \
|
||||
if (unlikely (unsigned (end - record) < HBINT16::static_size)) \
|
||||
return hb_ubytes_t (); \
|
||||
hb_barrier (); \
|
||||
transform.name = * (const HBINT16 *) record; \
|
||||
record += HBINT16::static_size; \
|
||||
}
|
||||
PROCESS_TRANSFORM_COMPONENTS;
|
||||
#undef PROCESS_TRANSFORM_COMPONENT
|
||||
|
||||
// Read reserved records
|
||||
unsigned i = flags & (unsigned) flags_t::RESERVED_MASK;
|
||||
while (i)
|
||||
{
|
||||
HB_UNUSED uint32_t discard;
|
||||
READ_UINT32VAR (discard);
|
||||
i &= i - 1;
|
||||
}
|
||||
|
||||
/* Parsing is over now. */
|
||||
|
||||
if (show)
|
||||
{
|
||||
// Only use coord_setter if there's actually any axis overrides.
|
||||
coord_setter_t coord_setter (axisIndices ? component_coords : hb_array<int> ());
|
||||
for (unsigned i = 0; i < axisIndices.length; i++)
|
||||
coord_setter[axisIndices[i]] = roundf (axisValues[i]);
|
||||
if (axisIndices)
|
||||
component_coords = coord_setter.get_coords ();
|
||||
|
||||
// Apply transform variations if any
|
||||
if (transformVarIdx != VarIdx::NO_VARIATION && coords)
|
||||
{
|
||||
float transformValues[9];
|
||||
unsigned numTransformValues = 0;
|
||||
#define PROCESS_TRANSFORM_COMPONENT(shift, type, flag, name) \
|
||||
if (flags & (unsigned) flags_t::flag) \
|
||||
transformValues[numTransformValues++] = transform.name;
|
||||
PROCESS_TRANSFORM_COMPONENTS;
|
||||
#undef PROCESS_TRANSFORM_COMPONENT
|
||||
varStore.get_delta (transformVarIdx, coords, hb_array (transformValues, numTransformValues), cache);
|
||||
numTransformValues = 0;
|
||||
#define PROCESS_TRANSFORM_COMPONENT(shift, type, flag, name) \
|
||||
if (flags & (unsigned) flags_t::flag) \
|
||||
transform.name = transformValues[numTransformValues++];
|
||||
PROCESS_TRANSFORM_COMPONENTS;
|
||||
#undef PROCESS_TRANSFORM_COMPONENT
|
||||
}
|
||||
|
||||
// Divide them by their divisors
|
||||
#define PROCESS_TRANSFORM_COMPONENT(shift, type, flag, name) \
|
||||
if (shift && (flags & (unsigned) flags_t::flag)) \
|
||||
transform.name *= 1.f / (1 << shift);
|
||||
PROCESS_TRANSFORM_COMPONENTS;
|
||||
#undef PROCESS_TRANSFORM_COMPONENT
|
||||
|
||||
if (!(flags & (unsigned) flags_t::HAVE_SCALE_Y))
|
||||
transform.scaleY = transform.scaleX;
|
||||
|
||||
transform.rotation *= HB_PI;
|
||||
transform.skewX *= HB_PI;
|
||||
transform.skewY *= HB_PI;
|
||||
|
||||
total_transform.transform (transform.to_transform ());
|
||||
|
||||
bool same_coords = component_coords.length == coords.length &&
|
||||
component_coords.arrayZ == coords.arrayZ;
|
||||
|
||||
c.depth_left--;
|
||||
VARC.get_path_at (c, gid,
|
||||
component_coords, total_transform,
|
||||
parent_gid,
|
||||
same_coords ? cache : nullptr);
|
||||
c.depth_left++;
|
||||
}
|
||||
|
||||
#undef PROCESS_TRANSFORM_COMPONENTS
|
||||
#undef READ_UINT32VAR
|
||||
|
||||
return hb_ubytes_t (record, end - record);
|
||||
}
|
||||
|
||||
bool
|
||||
VARC::get_path_at (const hb_varc_context_t &c,
|
||||
hb_codepoint_t glyph,
|
||||
hb_array_t<const int> coords,
|
||||
hb_transform_t<> transform,
|
||||
hb_codepoint_t parent_glyph,
|
||||
hb_scalar_cache_t *parent_cache) const
|
||||
{
|
||||
// Don't recurse on the same glyph.
|
||||
unsigned idx = glyph == parent_glyph ?
|
||||
NOT_COVERED :
|
||||
(this+coverage).get_coverage (glyph);
|
||||
if (idx == NOT_COVERED)
|
||||
{
|
||||
if (c.draw_session)
|
||||
{
|
||||
hb_transform_t<> leaf_transform = transform;
|
||||
leaf_transform.x0 *= c.font->x_multf;
|
||||
leaf_transform.y0 *= c.font->y_multf;
|
||||
|
||||
// Build a transforming pen to apply the transform.
|
||||
hb_draw_funcs_t *transformer_funcs = hb_transforming_pen_get_funcs ();
|
||||
hb_transforming_pen_context_t context {leaf_transform,
|
||||
c.draw_session->funcs,
|
||||
c.draw_session->draw_data,
|
||||
&c.draw_session->st};
|
||||
hb_draw_session_t transformer_session {transformer_funcs, &context};
|
||||
hb_draw_session_t &shape_draw_session = leaf_transform.is_identity () ? *c.draw_session : transformer_session;
|
||||
|
||||
if (c.font->face->table.glyf->get_path_at (c.font, glyph, shape_draw_session, coords, c.scratch.glyf_scratch)) return true;
|
||||
#ifndef HB_NO_CFF
|
||||
if (c.font->face->table.cff2->get_path_at (c.font, glyph, shape_draw_session, coords)) return true;
|
||||
if (c.font->face->table.cff1->get_path (c.font, glyph, shape_draw_session)) return true; // Doesn't have variations
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
else if (c.extents)
|
||||
{
|
||||
hb_glyph_extents_t glyph_extents;
|
||||
if (!c.font->face->table.glyf->get_extents_at (c.font, glyph, &glyph_extents, coords))
|
||||
#ifndef HB_NO_CFF
|
||||
if (!c.font->face->table.cff2->get_extents_at (c.font, glyph, &glyph_extents, coords))
|
||||
if (!c.font->face->table.cff1->get_extents (c.font, glyph, &glyph_extents)) // Doesn't have variations
|
||||
#endif
|
||||
return false;
|
||||
|
||||
hb_extents_t<> comp_extents (glyph_extents);
|
||||
hb_transform_t<> leaf_transform = transform;
|
||||
leaf_transform.x0 *= c.font->x_multf;
|
||||
leaf_transform.y0 *= c.font->y_multf;
|
||||
leaf_transform.transform_extents (comp_extents);
|
||||
c.extents->union_ (comp_extents);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (c.depth_left <= 0)
|
||||
return true;
|
||||
|
||||
if (c.edges_left <= 0)
|
||||
return true;
|
||||
(c.edges_left)--;
|
||||
|
||||
hb_decycler_node_t node (c.decycler);
|
||||
if (unlikely (!node.visit (glyph)))
|
||||
return true;
|
||||
|
||||
hb_ubytes_t record = (this+glyphRecords)[idx];
|
||||
|
||||
hb_scalar_cache_t static_cache;
|
||||
hb_scalar_cache_t *cache = parent_cache ?
|
||||
parent_cache :
|
||||
(this+varStore).create_cache (&static_cache);
|
||||
|
||||
VarCompositeGlyph::get_path_at (c,
|
||||
glyph,
|
||||
coords, transform,
|
||||
record,
|
||||
cache);
|
||||
|
||||
if (cache != parent_cache)
|
||||
(this+varStore).destroy_cache (cache, &static_cache);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
//} // namespace Var
|
||||
} // namespace OT
|
||||
|
||||
#endif
|
||||
+268
@@ -0,0 +1,268 @@
|
||||
#ifndef OT_VAR_VARC_VARC_HH
|
||||
#define OT_VAR_VARC_VARC_HH
|
||||
|
||||
#include "../../../hb-decycler.hh"
|
||||
#include "../../../hb-geometry.hh"
|
||||
#include "../../../hb-ot-layout-common.hh"
|
||||
#include "../../../hb-ot-glyf-table.hh"
|
||||
#include "../../../hb-ot-cff2-table.hh"
|
||||
#include "../../../hb-ot-cff1-table.hh"
|
||||
|
||||
#include "coord-setter.hh"
|
||||
|
||||
namespace OT {
|
||||
|
||||
//namespace Var {
|
||||
|
||||
/*
|
||||
* VARC -- Variable Composites
|
||||
* https://github.com/harfbuzz/boring-expansion-spec/blob/main/VARC.md
|
||||
*/
|
||||
|
||||
#ifndef HB_NO_VAR_COMPOSITES
|
||||
|
||||
struct hb_varc_scratch_t
|
||||
{
|
||||
hb_vector_t<unsigned> axisIndices;
|
||||
hb_vector_t<float> axisValues;
|
||||
hb_glyf_scratch_t glyf_scratch;
|
||||
};
|
||||
|
||||
struct hb_varc_context_t
|
||||
{
|
||||
hb_font_t *font;
|
||||
hb_draw_session_t *draw_session;
|
||||
hb_extents_t<> *extents;
|
||||
mutable hb_decycler_t decycler;
|
||||
mutable signed edges_left;
|
||||
mutable signed depth_left;
|
||||
hb_varc_scratch_t &scratch;
|
||||
};
|
||||
|
||||
struct VarComponent
|
||||
{
|
||||
enum class flags_t : uint32_t
|
||||
{
|
||||
RESET_UNSPECIFIED_AXES = 1u << 0,
|
||||
HAVE_AXES = 1u << 1,
|
||||
AXIS_VALUES_HAVE_VARIATION = 1u << 2,
|
||||
TRANSFORM_HAS_VARIATION = 1u << 3,
|
||||
HAVE_TRANSLATE_X = 1u << 4,
|
||||
HAVE_TRANSLATE_Y = 1u << 5,
|
||||
HAVE_ROTATION = 1u << 6,
|
||||
HAVE_CONDITION = 1u << 7,
|
||||
HAVE_SCALE_X = 1u << 8,
|
||||
HAVE_SCALE_Y = 1u << 9,
|
||||
HAVE_TCENTER_X = 1u << 10,
|
||||
HAVE_TCENTER_Y = 1u << 11,
|
||||
GID_IS_24BIT = 1u << 12,
|
||||
HAVE_SKEW_X = 1u << 13,
|
||||
HAVE_SKEW_Y = 1u << 14,
|
||||
RESERVED_MASK = ~((1u << 15) - 1),
|
||||
};
|
||||
|
||||
HB_INTERNAL hb_ubytes_t
|
||||
get_path_at (const hb_varc_context_t &c,
|
||||
hb_codepoint_t parent_gid,
|
||||
hb_array_t<const int> coords,
|
||||
hb_transform_t<> transform,
|
||||
hb_ubytes_t record,
|
||||
hb_scalar_cache_t *cache = nullptr) const;
|
||||
};
|
||||
|
||||
struct VarCompositeGlyph
|
||||
{
|
||||
static void
|
||||
get_path_at (const hb_varc_context_t &c,
|
||||
hb_codepoint_t gid,
|
||||
hb_array_t<const int> coords,
|
||||
hb_transform_t<> transform,
|
||||
hb_ubytes_t record,
|
||||
hb_scalar_cache_t *cache)
|
||||
{
|
||||
while (record)
|
||||
{
|
||||
const VarComponent &comp = * (const VarComponent *) (record.arrayZ);
|
||||
record = comp.get_path_at (c,
|
||||
gid,
|
||||
coords, transform,
|
||||
record,
|
||||
cache);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
HB_MARK_AS_FLAG_T (VarComponent::flags_t);
|
||||
|
||||
struct VARC
|
||||
{
|
||||
friend struct VarComponent;
|
||||
|
||||
static constexpr hb_tag_t tableTag = HB_TAG ('V', 'A', 'R', 'C');
|
||||
|
||||
HB_INTERNAL bool
|
||||
get_path_at (const hb_varc_context_t &c,
|
||||
hb_codepoint_t gid,
|
||||
hb_array_t<const int> coords,
|
||||
hb_transform_t<> transform = HB_TRANSFORM_IDENTITY,
|
||||
hb_codepoint_t parent_gid = HB_CODEPOINT_INVALID,
|
||||
hb_scalar_cache_t *parent_cache = nullptr) const;
|
||||
|
||||
bool
|
||||
get_path (hb_font_t *font,
|
||||
hb_codepoint_t gid,
|
||||
hb_draw_session_t &draw_session,
|
||||
hb_varc_scratch_t &scratch) const
|
||||
{
|
||||
hb_varc_context_t c {font,
|
||||
&draw_session,
|
||||
nullptr,
|
||||
hb_decycler_t {},
|
||||
HB_MAX_GRAPH_EDGE_COUNT,
|
||||
HB_MAX_NESTING_LEVEL,
|
||||
scratch};
|
||||
|
||||
return get_path_at (c, gid,
|
||||
hb_array (font->coords, font->num_coords));
|
||||
}
|
||||
|
||||
bool
|
||||
get_extents (hb_font_t *font,
|
||||
hb_codepoint_t gid,
|
||||
hb_extents_t<> *extents,
|
||||
hb_varc_scratch_t &scratch) const
|
||||
{
|
||||
hb_varc_context_t c {font,
|
||||
nullptr,
|
||||
extents,
|
||||
hb_decycler_t {},
|
||||
HB_MAX_GRAPH_EDGE_COUNT,
|
||||
HB_MAX_NESTING_LEVEL,
|
||||
scratch};
|
||||
|
||||
return get_path_at (c, gid,
|
||||
hb_array (font->coords, font->num_coords));
|
||||
}
|
||||
|
||||
bool sanitize (hb_sanitize_context_t *c) const
|
||||
{
|
||||
TRACE_SANITIZE (this);
|
||||
return_trace (version.sanitize (c) &&
|
||||
hb_barrier () &&
|
||||
version.major == 1 &&
|
||||
coverage.sanitize (c, this) &&
|
||||
varStore.sanitize (c, this) &&
|
||||
conditionList.sanitize (c, this) &&
|
||||
axisIndicesList.sanitize (c, this) &&
|
||||
glyphRecords.sanitize (c, this));
|
||||
}
|
||||
|
||||
struct accelerator_t
|
||||
{
|
||||
friend struct VarComponent;
|
||||
|
||||
accelerator_t (hb_face_t *face)
|
||||
{
|
||||
table = hb_sanitize_context_t ().reference_table<VARC> (face);
|
||||
}
|
||||
~accelerator_t ()
|
||||
{
|
||||
auto *scratch = cached_scratch.get_relaxed ();
|
||||
if (scratch)
|
||||
{
|
||||
scratch->~hb_varc_scratch_t ();
|
||||
hb_free (scratch);
|
||||
}
|
||||
|
||||
table.destroy ();
|
||||
}
|
||||
|
||||
bool
|
||||
get_path (hb_font_t *font, hb_codepoint_t gid, hb_draw_session_t &draw_session) const
|
||||
{
|
||||
if (!table->has_data ()) return false;
|
||||
|
||||
auto *scratch = acquire_scratch ();
|
||||
if (unlikely (!scratch)) return true;
|
||||
bool ret = table->get_path (font, gid, draw_session, *scratch);
|
||||
release_scratch (scratch);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool
|
||||
get_extents (hb_font_t *font,
|
||||
hb_codepoint_t gid,
|
||||
hb_glyph_extents_t *extents) const
|
||||
{
|
||||
#ifndef HB_NO_DRAW
|
||||
if (!table->has_data ()) return false;
|
||||
|
||||
hb_extents_t<> f_extents;
|
||||
|
||||
auto *scratch = acquire_scratch ();
|
||||
if (unlikely (!scratch)) return true;
|
||||
bool ret = table->get_extents (font, gid, &f_extents, *scratch);
|
||||
release_scratch (scratch);
|
||||
|
||||
if (ret)
|
||||
*extents = f_extents.to_glyph_extents (font->x_scale < 0, font->y_scale < 0);
|
||||
|
||||
return ret;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
hb_varc_scratch_t *acquire_scratch () const
|
||||
{
|
||||
hb_varc_scratch_t *scratch = cached_scratch.get_acquire ();
|
||||
|
||||
if (!scratch || unlikely (!cached_scratch.cmpexch (scratch, nullptr)))
|
||||
{
|
||||
scratch = (hb_varc_scratch_t *) hb_calloc (1, sizeof (hb_varc_scratch_t));
|
||||
if (unlikely (!scratch))
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return scratch;
|
||||
}
|
||||
void release_scratch (hb_varc_scratch_t *scratch) const
|
||||
{
|
||||
if (!cached_scratch.cmpexch (nullptr, scratch))
|
||||
{
|
||||
scratch->~hb_varc_scratch_t ();
|
||||
hb_free (scratch);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
hb_blob_ptr_t<VARC> table;
|
||||
mutable hb_atomic_t<hb_varc_scratch_t *> cached_scratch;
|
||||
};
|
||||
|
||||
bool has_data () const { return version.major != 0; }
|
||||
|
||||
protected:
|
||||
FixedVersion<> version; /* Version identifier */
|
||||
Offset32To<Coverage> coverage;
|
||||
Offset32To<MultiItemVariationStore> varStore;
|
||||
Offset32To<ConditionList> conditionList;
|
||||
Offset32To<TupleList> axisIndicesList;
|
||||
Offset32To<CFF2Index/*Of<VarCompositeGlyph>*/> glyphRecords;
|
||||
public:
|
||||
DEFINE_SIZE_STATIC (24);
|
||||
};
|
||||
|
||||
struct VARC_accelerator_t : VARC::accelerator_t {
|
||||
VARC_accelerator_t (hb_face_t *face) : VARC::accelerator_t (face) {}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
//}
|
||||
|
||||
}
|
||||
|
||||
#endif /* OT_VAR_VARC_VARC_HH */
|
||||
@@ -0,0 +1,63 @@
|
||||
#ifndef OT_VAR_VARC_COORD_SETTER_HH
|
||||
#define OT_VAR_VARC_COORD_SETTER_HH
|
||||
|
||||
|
||||
#include "../../../hb.hh"
|
||||
|
||||
|
||||
namespace OT {
|
||||
//namespace Var {
|
||||
|
||||
|
||||
struct coord_setter_t
|
||||
{
|
||||
coord_setter_t (hb_array_t<const int> coords_)
|
||||
{
|
||||
length = coords_.length;
|
||||
if (length <= ARRAY_LENGTH (static_coords))
|
||||
hb_memcpy (static_coords, coords_.arrayZ, length * sizeof (int));
|
||||
else
|
||||
dynamic_coords.extend (coords_);
|
||||
}
|
||||
|
||||
int& operator [] (unsigned idx)
|
||||
{
|
||||
if (unlikely (idx >= HB_VAR_COMPOSITE_MAX_AXES))
|
||||
return Crap(int);
|
||||
|
||||
if (length <= ARRAY_LENGTH (static_coords))
|
||||
{
|
||||
if (idx < ARRAY_LENGTH (static_coords))
|
||||
{
|
||||
while (length <= idx)
|
||||
static_coords[length++] = 0;
|
||||
return static_coords[idx];
|
||||
}
|
||||
else
|
||||
dynamic_coords.extend (hb_array (static_coords, length));
|
||||
}
|
||||
|
||||
if (dynamic_coords.length <= idx)
|
||||
{
|
||||
if (unlikely (!dynamic_coords.resize (idx + 1)))
|
||||
return Crap(int);
|
||||
length = idx + 1;
|
||||
}
|
||||
return dynamic_coords.arrayZ[idx];
|
||||
}
|
||||
|
||||
hb_array_t<int> get_coords ()
|
||||
{ return length <= ARRAY_LENGTH (static_coords) ? hb_array (static_coords, length) : dynamic_coords.as_array (); }
|
||||
|
||||
private:
|
||||
hb_vector_t<int> dynamic_coords;
|
||||
unsigned length;
|
||||
int static_coords[sizeof (void *) * 8];
|
||||
};
|
||||
|
||||
|
||||
//} // namespace Var
|
||||
|
||||
} // namespace OT
|
||||
|
||||
#endif /* OT_VAR_VARC_COORD_SETTER_HH */
|
||||
+435
@@ -0,0 +1,435 @@
|
||||
#ifndef OT_GLYF_COMPOSITEGLYPH_HH
|
||||
#define OT_GLYF_COMPOSITEGLYPH_HH
|
||||
|
||||
|
||||
#include "../../hb-open-type.hh"
|
||||
#include "composite-iter.hh"
|
||||
|
||||
|
||||
namespace OT {
|
||||
namespace glyf_impl {
|
||||
|
||||
|
||||
struct CompositeGlyphRecord
|
||||
{
|
||||
protected:
|
||||
enum composite_glyph_flag_t
|
||||
{
|
||||
ARG_1_AND_2_ARE_WORDS = 0x0001,
|
||||
ARGS_ARE_XY_VALUES = 0x0002,
|
||||
ROUND_XY_TO_GRID = 0x0004,
|
||||
WE_HAVE_A_SCALE = 0x0008,
|
||||
MORE_COMPONENTS = 0x0020,
|
||||
WE_HAVE_AN_X_AND_Y_SCALE = 0x0040,
|
||||
WE_HAVE_A_TWO_BY_TWO = 0x0080,
|
||||
WE_HAVE_INSTRUCTIONS = 0x0100,
|
||||
USE_MY_METRICS = 0x0200,
|
||||
OVERLAP_COMPOUND = 0x0400,
|
||||
SCALED_COMPONENT_OFFSET = 0x0800,
|
||||
UNSCALED_COMPONENT_OFFSET = 0x1000,
|
||||
#ifndef HB_NO_BEYOND_64K
|
||||
GID_IS_24BIT = 0x2000
|
||||
#endif
|
||||
};
|
||||
|
||||
public:
|
||||
size_t get_size () const
|
||||
{
|
||||
unsigned int size = min_size;
|
||||
/* glyphIndex is 24bit instead of 16bit */
|
||||
#ifndef HB_NO_BEYOND_64K
|
||||
if (flags & GID_IS_24BIT) size += HBGlyphID24::static_size - HBGlyphID16::static_size;
|
||||
#endif
|
||||
/* arg1 and 2 are int16 */
|
||||
if (flags & ARG_1_AND_2_ARE_WORDS) size += 4;
|
||||
/* arg1 and 2 are int8 */
|
||||
else size += 2;
|
||||
|
||||
/* One x 16 bit (scale) */
|
||||
if (flags & WE_HAVE_A_SCALE) size += 2;
|
||||
/* Two x 16 bit (xscale, yscale) */
|
||||
else if (flags & WE_HAVE_AN_X_AND_Y_SCALE) size += 4;
|
||||
/* Four x 16 bit (xscale, scale01, scale10, yscale) */
|
||||
else if (flags & WE_HAVE_A_TWO_BY_TWO) size += 8;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
void drop_instructions_flag () { flags = (uint16_t) flags & ~WE_HAVE_INSTRUCTIONS; }
|
||||
void set_overlaps_flag ()
|
||||
{
|
||||
flags = (uint16_t) flags | OVERLAP_COMPOUND;
|
||||
}
|
||||
|
||||
bool has_instructions () const { return flags & WE_HAVE_INSTRUCTIONS; }
|
||||
|
||||
bool has_more () const { return flags & MORE_COMPONENTS; }
|
||||
bool is_use_my_metrics () const { return flags & USE_MY_METRICS; }
|
||||
bool is_anchored () const { return !(flags & ARGS_ARE_XY_VALUES); }
|
||||
void get_anchor_points (unsigned int &point1, unsigned int &point2) const
|
||||
{
|
||||
const auto *p = &StructAfter<const HBUINT8> (flags);
|
||||
#ifndef HB_NO_BEYOND_64K
|
||||
if (flags & GID_IS_24BIT)
|
||||
p += HBGlyphID24::static_size;
|
||||
else
|
||||
#endif
|
||||
p += HBGlyphID16::static_size;
|
||||
if (flags & ARG_1_AND_2_ARE_WORDS)
|
||||
{
|
||||
point1 = ((const HBUINT16 *) p)[0];
|
||||
point2 = ((const HBUINT16 *) p)[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
point1 = p[0];
|
||||
point2 = p[1];
|
||||
}
|
||||
}
|
||||
|
||||
static void transform (const float (&matrix)[4],
|
||||
hb_array_t<contour_point_t> points)
|
||||
{
|
||||
if (matrix[0] != 1.f || matrix[1] != 0.f ||
|
||||
matrix[2] != 0.f || matrix[3] != 1.f)
|
||||
for (auto &point : points)
|
||||
point.transform (matrix);
|
||||
}
|
||||
|
||||
static void translate (const contour_point_t &trans,
|
||||
hb_array_t<contour_point_t> points)
|
||||
{
|
||||
if (HB_OPTIMIZE_SIZE_VAL)
|
||||
{
|
||||
if (trans.x != 0.f || trans.y != 0.f)
|
||||
for (auto &point : points)
|
||||
point.translate (trans);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (trans.x != 0.f && trans.y != 0.f)
|
||||
for (auto &point : points)
|
||||
point.translate (trans);
|
||||
else
|
||||
{
|
||||
if (trans.x != 0.f)
|
||||
for (auto &point : points)
|
||||
point.x += trans.x;
|
||||
else if (trans.y != 0.f)
|
||||
for (auto &point : points)
|
||||
point.y += trans.y;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void transform_points (hb_array_t<contour_point_t> points,
|
||||
const float (&matrix)[4],
|
||||
const contour_point_t &trans) const
|
||||
{
|
||||
if (scaled_offsets ())
|
||||
{
|
||||
translate (trans, points);
|
||||
transform (matrix, points);
|
||||
}
|
||||
else
|
||||
{
|
||||
transform (matrix, points);
|
||||
translate (trans, points);
|
||||
}
|
||||
}
|
||||
|
||||
bool get_points (contour_point_vector_t &points) const
|
||||
{
|
||||
float matrix[4];
|
||||
contour_point_t trans;
|
||||
get_transformation (matrix, trans);
|
||||
if (unlikely (!points.alloc (points.length + 1 + 4))) return false; // For phantom points
|
||||
points.push (trans);
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned compile_with_point (const contour_point_t &point,
|
||||
char *out) const
|
||||
{
|
||||
const HBINT8 *p = &StructAfter<const HBINT8> (flags);
|
||||
#ifndef HB_NO_BEYOND_64K
|
||||
if (flags & GID_IS_24BIT)
|
||||
p += HBGlyphID24::static_size;
|
||||
else
|
||||
#endif
|
||||
p += HBGlyphID16::static_size;
|
||||
|
||||
unsigned len = get_size ();
|
||||
unsigned len_before_val = (const char *)p - (const char *)this;
|
||||
if (flags & ARG_1_AND_2_ARE_WORDS)
|
||||
{
|
||||
// no overflow, copy value
|
||||
hb_memcpy (out, this, len);
|
||||
|
||||
HBINT16 *o = reinterpret_cast<HBINT16 *> (out + len_before_val);
|
||||
o[0] = roundf (point.x);
|
||||
o[1] = roundf (point.y);
|
||||
}
|
||||
else
|
||||
{
|
||||
int new_x = roundf (point.x);
|
||||
int new_y = roundf (point.y);
|
||||
if (new_x <= 127 && new_x >= -128 &&
|
||||
new_y <= 127 && new_y >= -128)
|
||||
{
|
||||
hb_memcpy (out, this, len);
|
||||
HBINT8 *o = reinterpret_cast<HBINT8 *> (out + len_before_val);
|
||||
o[0] = new_x;
|
||||
o[1] = new_y;
|
||||
}
|
||||
else
|
||||
{
|
||||
// new point value has an int8 overflow
|
||||
hb_memcpy (out, this, len_before_val);
|
||||
|
||||
//update flags
|
||||
CompositeGlyphRecord *o = reinterpret_cast<CompositeGlyphRecord *> (out);
|
||||
o->flags = flags | ARG_1_AND_2_ARE_WORDS;
|
||||
out += len_before_val;
|
||||
|
||||
HBINT16 new_value;
|
||||
new_value = new_x;
|
||||
hb_memcpy (out, &new_value, HBINT16::static_size);
|
||||
out += HBINT16::static_size;
|
||||
|
||||
new_value = new_y;
|
||||
hb_memcpy (out, &new_value, HBINT16::static_size);
|
||||
out += HBINT16::static_size;
|
||||
|
||||
hb_memcpy (out, p+2, len - len_before_val - 2);
|
||||
len += 2;
|
||||
}
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
protected:
|
||||
bool scaled_offsets () const
|
||||
{ return (flags & (SCALED_COMPONENT_OFFSET | UNSCALED_COMPONENT_OFFSET)) == SCALED_COMPONENT_OFFSET; }
|
||||
|
||||
public:
|
||||
bool get_transformation (float (&matrix)[4], contour_point_t &trans) const
|
||||
{
|
||||
matrix[0] = matrix[3] = 1.f;
|
||||
matrix[1] = matrix[2] = 0.f;
|
||||
|
||||
const auto *p = &StructAfter<const HBINT8> (flags);
|
||||
#ifndef HB_NO_BEYOND_64K
|
||||
if (flags & GID_IS_24BIT)
|
||||
p += HBGlyphID24::static_size;
|
||||
else
|
||||
#endif
|
||||
p += HBGlyphID16::static_size;
|
||||
int tx, ty;
|
||||
if (flags & ARG_1_AND_2_ARE_WORDS)
|
||||
{
|
||||
tx = *(const HBINT16 *) p;
|
||||
p += HBINT16::static_size;
|
||||
ty = *(const HBINT16 *) p;
|
||||
p += HBINT16::static_size;
|
||||
}
|
||||
else
|
||||
{
|
||||
tx = *p++;
|
||||
ty = *p++;
|
||||
}
|
||||
if (is_anchored ()) tx = ty = 0;
|
||||
|
||||
/* set is_end_point flag to true, used by IUP delta optimization */
|
||||
trans.init ((float) tx, (float) ty, true);
|
||||
|
||||
{
|
||||
const F2DOT14 *points = (const F2DOT14 *) p;
|
||||
if (flags & WE_HAVE_A_SCALE)
|
||||
{
|
||||
matrix[0] = matrix[3] = points[0].to_float ();
|
||||
return true;
|
||||
}
|
||||
else if (flags & WE_HAVE_AN_X_AND_Y_SCALE)
|
||||
{
|
||||
matrix[0] = points[0].to_float ();
|
||||
matrix[3] = points[1].to_float ();
|
||||
return true;
|
||||
}
|
||||
else if (flags & WE_HAVE_A_TWO_BY_TWO)
|
||||
{
|
||||
matrix[0] = points[0].to_float ();
|
||||
matrix[1] = points[1].to_float ();
|
||||
matrix[2] = points[2].to_float ();
|
||||
matrix[3] = points[3].to_float ();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return tx || ty;
|
||||
}
|
||||
|
||||
hb_codepoint_t get_gid () const
|
||||
{
|
||||
#ifndef HB_NO_BEYOND_64K
|
||||
if (flags & GID_IS_24BIT)
|
||||
return StructAfter<const HBGlyphID24> (flags);
|
||||
else
|
||||
#endif
|
||||
return StructAfter<const HBGlyphID16> (flags);
|
||||
}
|
||||
void set_gid (hb_codepoint_t gid)
|
||||
{
|
||||
#ifndef HB_NO_BEYOND_64K
|
||||
if (flags & GID_IS_24BIT)
|
||||
StructAfter<HBGlyphID24> (flags) = gid;
|
||||
else
|
||||
#endif
|
||||
/* TODO assert? */
|
||||
StructAfter<HBGlyphID16> (flags) = gid;
|
||||
}
|
||||
|
||||
#ifndef HB_NO_BEYOND_64K
|
||||
void lower_gid_24_to_16 ()
|
||||
{
|
||||
hb_codepoint_t gid = get_gid ();
|
||||
if (!(flags & GID_IS_24BIT) || gid > 0xFFFFu)
|
||||
return;
|
||||
|
||||
/* Lower the flag and move the rest of the struct down. */
|
||||
|
||||
unsigned size = get_size ();
|
||||
char *end = (char *) this + size;
|
||||
char *p = &StructAfter<char> (flags);
|
||||
p += HBGlyphID24::static_size;
|
||||
|
||||
flags = flags & ~GID_IS_24BIT;
|
||||
set_gid (gid);
|
||||
|
||||
memmove (p - HBGlyphID24::static_size + HBGlyphID16::static_size, p, end - p);
|
||||
}
|
||||
#endif
|
||||
|
||||
protected:
|
||||
HBUINT16 flags;
|
||||
HBUINT24 pad;
|
||||
public:
|
||||
DEFINE_SIZE_MIN (4);
|
||||
};
|
||||
|
||||
using composite_iter_t = composite_iter_tmpl<CompositeGlyphRecord>;
|
||||
|
||||
struct CompositeGlyph
|
||||
{
|
||||
const GlyphHeader &header;
|
||||
hb_bytes_t bytes;
|
||||
CompositeGlyph (const GlyphHeader &header_, hb_bytes_t bytes_) :
|
||||
header (header_), bytes (bytes_) {}
|
||||
|
||||
composite_iter_t iter () const
|
||||
{ return composite_iter_t (bytes, &StructAfter<CompositeGlyphRecord, GlyphHeader> (header)); }
|
||||
|
||||
unsigned int instructions_length (hb_bytes_t bytes) const
|
||||
{
|
||||
unsigned int start = bytes.length;
|
||||
unsigned int end = bytes.length;
|
||||
const CompositeGlyphRecord *last = nullptr;
|
||||
for (auto &item : iter ())
|
||||
last = &item;
|
||||
if (unlikely (!last)) return 0;
|
||||
|
||||
if (last->has_instructions ())
|
||||
start = (char *) last - &bytes + last->get_size ();
|
||||
if (unlikely (start > end)) return 0;
|
||||
return end - start;
|
||||
}
|
||||
|
||||
/* Trimming for composites not implemented.
|
||||
* If removing hints it falls out of that. */
|
||||
const hb_bytes_t trim_padding () const { return bytes; }
|
||||
|
||||
void drop_hints ()
|
||||
{
|
||||
for (const auto &_ : iter ())
|
||||
const_cast<CompositeGlyphRecord &> (_).drop_instructions_flag ();
|
||||
}
|
||||
|
||||
/* Chop instructions off the end */
|
||||
void drop_hints_bytes (hb_bytes_t &dest_start) const
|
||||
{ dest_start = bytes.sub_array (0, bytes.length - instructions_length (bytes)); }
|
||||
|
||||
void set_overlaps_flag ()
|
||||
{
|
||||
CompositeGlyphRecord& glyph_chain = const_cast<CompositeGlyphRecord &> (
|
||||
StructAfter<CompositeGlyphRecord, GlyphHeader> (header));
|
||||
if (!bytes.check_range(&glyph_chain, CompositeGlyphRecord::min_size))
|
||||
return;
|
||||
glyph_chain.set_overlaps_flag ();
|
||||
}
|
||||
|
||||
bool compile_bytes_with_deltas (const hb_bytes_t &source_bytes,
|
||||
const contour_point_vector_t &points_with_deltas,
|
||||
hb_bytes_t &dest_bytes /* OUT */)
|
||||
{
|
||||
if (source_bytes.length <= GlyphHeader::static_size ||
|
||||
header.numberOfContours != -1)
|
||||
{
|
||||
dest_bytes = hb_bytes_t ();
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned source_len = source_bytes.length - GlyphHeader::static_size;
|
||||
|
||||
/* try to allocate more memories than source glyph bytes
|
||||
* in case that there might be an overflow for int8 value
|
||||
* and we would need to use int16 instead */
|
||||
char *o = (char *) hb_calloc (source_len * 2, sizeof (char));
|
||||
if (unlikely (!o)) return false;
|
||||
|
||||
const CompositeGlyphRecord *c = reinterpret_cast<const CompositeGlyphRecord *> (source_bytes.arrayZ + GlyphHeader::static_size);
|
||||
auto it = composite_iter_t (hb_bytes_t ((const char *)c, source_len), c);
|
||||
|
||||
char *p = o;
|
||||
unsigned i = 0, source_comp_len = 0;
|
||||
for (const auto &component : it)
|
||||
{
|
||||
/* last 4 points in points_with_deltas are phantom points and should not be included */
|
||||
if (i >= points_with_deltas.length - 4) {
|
||||
hb_free (o);
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned comp_len = component.get_size ();
|
||||
if (component.is_anchored ())
|
||||
{
|
||||
hb_memcpy (p, &component, comp_len);
|
||||
p += comp_len;
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned new_len = component.compile_with_point (points_with_deltas[i], p);
|
||||
p += new_len;
|
||||
}
|
||||
i++;
|
||||
source_comp_len += comp_len;
|
||||
}
|
||||
|
||||
//copy instructions if any
|
||||
if (source_len > source_comp_len)
|
||||
{
|
||||
unsigned instr_len = source_len - source_comp_len;
|
||||
hb_memcpy (p, (const char *)c + source_comp_len, instr_len);
|
||||
p += instr_len;
|
||||
}
|
||||
|
||||
unsigned len = p - o;
|
||||
dest_bytes = hb_bytes_t (o, len);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
} /* namespace glyf_impl */
|
||||
} /* namespace OT */
|
||||
|
||||
|
||||
#endif /* OT_GLYF_COMPOSITEGLYPH_HH */
|
||||
+582
@@ -0,0 +1,582 @@
|
||||
#ifndef OT_GLYF_GLYPH_HH
|
||||
#define OT_GLYF_GLYPH_HH
|
||||
|
||||
|
||||
#include "../../hb-open-type.hh"
|
||||
|
||||
#include "GlyphHeader.hh"
|
||||
#include "SimpleGlyph.hh"
|
||||
#include "CompositeGlyph.hh"
|
||||
|
||||
|
||||
namespace OT {
|
||||
|
||||
struct glyf_accelerator_t;
|
||||
|
||||
namespace glyf_impl {
|
||||
|
||||
|
||||
enum phantom_point_index_t
|
||||
{
|
||||
PHANTOM_LEFT = 0,
|
||||
PHANTOM_RIGHT = 1,
|
||||
PHANTOM_TOP = 2,
|
||||
PHANTOM_BOTTOM = 3,
|
||||
PHANTOM_COUNT = 4
|
||||
};
|
||||
|
||||
struct Glyph
|
||||
{
|
||||
enum glyph_type_t {
|
||||
EMPTY,
|
||||
SIMPLE,
|
||||
COMPOSITE,
|
||||
};
|
||||
|
||||
public:
|
||||
composite_iter_t get_composite_iterator () const
|
||||
{
|
||||
if (type != COMPOSITE) return composite_iter_t ();
|
||||
return CompositeGlyph (*header, bytes).iter ();
|
||||
}
|
||||
|
||||
const hb_bytes_t trim_padding () const
|
||||
{
|
||||
switch (type) {
|
||||
case COMPOSITE: return CompositeGlyph (*header, bytes).trim_padding ();
|
||||
case SIMPLE: return SimpleGlyph (*header, bytes).trim_padding ();
|
||||
case EMPTY: return bytes;
|
||||
default: return bytes;
|
||||
}
|
||||
}
|
||||
|
||||
void drop_hints ()
|
||||
{
|
||||
switch (type) {
|
||||
case COMPOSITE: CompositeGlyph (*header, bytes).drop_hints (); return;
|
||||
case SIMPLE: SimpleGlyph (*header, bytes).drop_hints (); return;
|
||||
case EMPTY: return;
|
||||
}
|
||||
}
|
||||
|
||||
void set_overlaps_flag ()
|
||||
{
|
||||
switch (type) {
|
||||
case COMPOSITE: CompositeGlyph (*header, bytes).set_overlaps_flag (); return;
|
||||
case SIMPLE: SimpleGlyph (*header, bytes).set_overlaps_flag (); return;
|
||||
case EMPTY: return;
|
||||
}
|
||||
}
|
||||
|
||||
void drop_hints_bytes (hb_bytes_t &dest_start, hb_bytes_t &dest_end) const
|
||||
{
|
||||
switch (type) {
|
||||
case COMPOSITE: CompositeGlyph (*header, bytes).drop_hints_bytes (dest_start); return;
|
||||
case SIMPLE: SimpleGlyph (*header, bytes).drop_hints_bytes (dest_start, dest_end); return;
|
||||
case EMPTY: return;
|
||||
}
|
||||
}
|
||||
|
||||
bool is_composite () const
|
||||
{ return type == COMPOSITE; }
|
||||
|
||||
bool get_all_points_without_var (const hb_face_t *face,
|
||||
contour_point_vector_t &points /* OUT */) const
|
||||
{
|
||||
switch (type) {
|
||||
case SIMPLE:
|
||||
if (unlikely (!SimpleGlyph (*header, bytes).get_contour_points (points)))
|
||||
return false;
|
||||
break;
|
||||
case COMPOSITE:
|
||||
{
|
||||
for (auto &item : get_composite_iterator ())
|
||||
if (unlikely (!item.get_points (points))) return false;
|
||||
break;
|
||||
}
|
||||
case EMPTY:
|
||||
break;
|
||||
}
|
||||
|
||||
/* Init phantom points */
|
||||
if (unlikely (!points.resize (points.length + PHANTOM_COUNT))) return false;
|
||||
hb_array_t<contour_point_t> phantoms = points.as_array ().sub_array (points.length - PHANTOM_COUNT, PHANTOM_COUNT);
|
||||
{
|
||||
// Duplicated code.
|
||||
int lsb = 0;
|
||||
face->table.hmtx->get_leading_bearing_without_var_unscaled (gid, &lsb);
|
||||
int h_delta = (int) header->xMin - lsb;
|
||||
HB_UNUSED int tsb = 0;
|
||||
#ifndef HB_NO_VERTICAL
|
||||
face->table.vmtx->get_leading_bearing_without_var_unscaled (gid, &tsb);
|
||||
#endif
|
||||
int v_orig = (int) header->yMax + tsb;
|
||||
unsigned h_adv = face->table.hmtx->get_advance_without_var_unscaled (gid);
|
||||
unsigned v_adv =
|
||||
#ifndef HB_NO_VERTICAL
|
||||
face->table.vmtx->get_advance_without_var_unscaled (gid)
|
||||
#else
|
||||
- face->get_upem ()
|
||||
#endif
|
||||
;
|
||||
phantoms[PHANTOM_LEFT].x = h_delta;
|
||||
phantoms[PHANTOM_RIGHT].x = (int) h_adv + h_delta;
|
||||
phantoms[PHANTOM_TOP].y = v_orig;
|
||||
phantoms[PHANTOM_BOTTOM].y = v_orig - (int) v_adv;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void update_mtx (const hb_subset_plan_t *plan,
|
||||
int xMin, int xMax,
|
||||
int yMin, int yMax,
|
||||
const contour_point_vector_t &all_points) const
|
||||
{
|
||||
hb_codepoint_t new_gid = 0;
|
||||
if (!plan->new_gid_for_old_gid (gid, &new_gid))
|
||||
return;
|
||||
|
||||
if (type != EMPTY)
|
||||
{
|
||||
plan->bounds_width_vec[new_gid] = xMax - xMin;
|
||||
plan->bounds_height_vec[new_gid] = yMax - yMin;
|
||||
}
|
||||
|
||||
unsigned len = all_points.length;
|
||||
float leftSideX = all_points[len - 4].x;
|
||||
float rightSideX = all_points[len - 3].x;
|
||||
float topSideY = all_points[len - 2].y;
|
||||
float bottomSideY = all_points[len - 1].y;
|
||||
|
||||
uint32_t hash = hb_hash (new_gid);
|
||||
|
||||
signed hori_aw = roundf (rightSideX - leftSideX);
|
||||
if (hori_aw < 0) hori_aw = 0;
|
||||
int lsb = roundf (xMin - leftSideX);
|
||||
plan->hmtx_map.set_with_hash (new_gid, hash, hb_pair ((unsigned) hori_aw, lsb));
|
||||
//flag value should be computed using non-empty glyphs
|
||||
if (type != EMPTY && lsb != xMin)
|
||||
plan->head_maxp_info.allXMinIsLsb = false;
|
||||
|
||||
signed vert_aw = roundf (topSideY - bottomSideY);
|
||||
if (vert_aw < 0) vert_aw = 0;
|
||||
int tsb = roundf (topSideY - yMax);
|
||||
plan->vmtx_map.set_with_hash (new_gid, hash, hb_pair ((unsigned) vert_aw, tsb));
|
||||
}
|
||||
|
||||
bool compile_header_bytes (const hb_subset_plan_t *plan,
|
||||
const contour_point_vector_t &all_points,
|
||||
hb_bytes_t &dest_bytes /* OUT */) const
|
||||
{
|
||||
GlyphHeader *glyph_header = nullptr;
|
||||
if (!plan->pinned_at_default && type != EMPTY && all_points.length >= 4)
|
||||
{
|
||||
glyph_header = (GlyphHeader *) hb_calloc (1, GlyphHeader::static_size);
|
||||
if (unlikely (!glyph_header)) return false;
|
||||
}
|
||||
|
||||
float xMin = 0, xMax = 0;
|
||||
float yMin = 0, yMax = 0;
|
||||
if (all_points.length > 4)
|
||||
{
|
||||
xMin = xMax = all_points[0].x;
|
||||
yMin = yMax = all_points[0].y;
|
||||
|
||||
unsigned count = all_points.length - 4;
|
||||
for (unsigned i = 1; i < count; i++)
|
||||
{
|
||||
float x = all_points[i].x;
|
||||
float y = all_points[i].y;
|
||||
xMin = hb_min (xMin, x);
|
||||
xMax = hb_max (xMax, x);
|
||||
yMin = hb_min (yMin, y);
|
||||
yMax = hb_max (yMax, y);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// These are destined for storage in a 16 bit field to clamp the values to
|
||||
// fit into a 16 bit signed integer.
|
||||
int rounded_xMin = hb_clamp (roundf (xMin), -32768.0f, 32767.0f);
|
||||
int rounded_xMax = hb_clamp (roundf (xMax), -32768.0f, 32767.0f);
|
||||
int rounded_yMin = hb_clamp (roundf (yMin), -32768.0f, 32767.0f);
|
||||
int rounded_yMax = hb_clamp (roundf (yMax), -32768.0f, 32767.0f);
|
||||
|
||||
update_mtx (plan, rounded_xMin, rounded_xMax, rounded_yMin, rounded_yMax, all_points);
|
||||
|
||||
if (type != EMPTY)
|
||||
{
|
||||
plan->head_maxp_info.xMin = hb_min (plan->head_maxp_info.xMin, rounded_xMin);
|
||||
plan->head_maxp_info.yMin = hb_min (plan->head_maxp_info.yMin, rounded_yMin);
|
||||
plan->head_maxp_info.xMax = hb_max (plan->head_maxp_info.xMax, rounded_xMax);
|
||||
plan->head_maxp_info.yMax = hb_max (plan->head_maxp_info.yMax, rounded_yMax);
|
||||
}
|
||||
|
||||
/* when pinned at default, no need to compile glyph header
|
||||
* and for empty glyphs: all_points only include phantom points.
|
||||
* just update metrics and then return */
|
||||
if (!glyph_header)
|
||||
return true;
|
||||
|
||||
glyph_header->numberOfContours = header->numberOfContours;
|
||||
|
||||
glyph_header->xMin = rounded_xMin;
|
||||
glyph_header->yMin = rounded_yMin;
|
||||
glyph_header->xMax = rounded_xMax;
|
||||
glyph_header->yMax = rounded_yMax;
|
||||
|
||||
dest_bytes = hb_bytes_t ((const char *)glyph_header, GlyphHeader::static_size);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool compile_bytes_with_deltas (const hb_subset_plan_t *plan,
|
||||
hb_font_t *font,
|
||||
const glyf_accelerator_t &glyf,
|
||||
hb_bytes_t &dest_start, /* IN/OUT */
|
||||
hb_bytes_t &dest_end /* OUT */)
|
||||
{
|
||||
contour_point_vector_t all_points, points_with_deltas;
|
||||
unsigned composite_contours = 0;
|
||||
head_maxp_info_t *head_maxp_info_p = &plan->head_maxp_info;
|
||||
unsigned *composite_contours_p = &composite_contours;
|
||||
|
||||
// don't compute head/maxp values when glyph has no contours(type is EMPTY)
|
||||
// also ignore .notdef glyph when --notdef-outline is not enabled
|
||||
if (type == EMPTY ||
|
||||
(gid == 0 && !(plan->flags & HB_SUBSET_FLAGS_NOTDEF_OUTLINE)))
|
||||
{
|
||||
head_maxp_info_p = nullptr;
|
||||
composite_contours_p = nullptr;
|
||||
}
|
||||
|
||||
hb_glyf_scratch_t scratch;
|
||||
if (!get_points (font, glyf, all_points, scratch, &points_with_deltas, head_maxp_info_p, composite_contours_p, false, false))
|
||||
return false;
|
||||
|
||||
// .notdef, set type to empty so we only update metrics and don't compile bytes for
|
||||
// it
|
||||
if (gid == 0 &&
|
||||
!(plan->flags & HB_SUBSET_FLAGS_NOTDEF_OUTLINE))
|
||||
{
|
||||
type = EMPTY;
|
||||
dest_start = hb_bytes_t ();
|
||||
dest_end = hb_bytes_t ();
|
||||
}
|
||||
|
||||
//dont compile bytes when pinned at default, just recalculate bounds
|
||||
if (!plan->pinned_at_default)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case COMPOSITE:
|
||||
if (!CompositeGlyph (*header, bytes).compile_bytes_with_deltas (dest_start,
|
||||
points_with_deltas,
|
||||
dest_end))
|
||||
return false;
|
||||
break;
|
||||
case SIMPLE:
|
||||
if (!SimpleGlyph (*header, bytes).compile_bytes_with_deltas (all_points,
|
||||
plan->flags & HB_SUBSET_FLAGS_NO_HINTING,
|
||||
dest_end))
|
||||
return false;
|
||||
break;
|
||||
case EMPTY:
|
||||
/* set empty bytes for empty glyph
|
||||
* do not use source glyph's pointers */
|
||||
dest_start = hb_bytes_t ();
|
||||
dest_end = hb_bytes_t ();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!compile_header_bytes (plan, all_points, dest_start))
|
||||
{
|
||||
dest_end.fini ();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/* Note: Recursively calls itself.
|
||||
* all_points includes phantom points
|
||||
*/
|
||||
template <typename accelerator_t>
|
||||
bool get_points (hb_font_t *font, const accelerator_t &glyf_accelerator,
|
||||
contour_point_vector_t &all_points /* OUT */,
|
||||
hb_glyf_scratch_t &scratch,
|
||||
contour_point_vector_t *points_with_deltas = nullptr, /* OUT */
|
||||
head_maxp_info_t * head_maxp_info = nullptr, /* OUT */
|
||||
unsigned *composite_contours = nullptr, /* OUT */
|
||||
bool shift_points_hori = true,
|
||||
bool use_my_metrics = true,
|
||||
bool phantom_only = false,
|
||||
hb_array_t<const int> coords = hb_array_t<const int> (),
|
||||
hb_scalar_cache_t *gvar_cache = nullptr,
|
||||
unsigned int depth = 0,
|
||||
unsigned *edge_count = nullptr) const
|
||||
{
|
||||
if (unlikely (depth > HB_MAX_NESTING_LEVEL)) return false;
|
||||
unsigned stack_edge_count = 0;
|
||||
if (!edge_count) edge_count = &stack_edge_count;
|
||||
if (unlikely (*edge_count > HB_MAX_GRAPH_EDGE_COUNT)) return false;
|
||||
(*edge_count)++;
|
||||
|
||||
if (head_maxp_info)
|
||||
{
|
||||
head_maxp_info->maxComponentDepth = hb_max (head_maxp_info->maxComponentDepth, depth);
|
||||
}
|
||||
|
||||
if (!coords && font->has_nonzero_coords)
|
||||
coords = hb_array (font->coords, font->num_coords);
|
||||
|
||||
contour_point_vector_t &points = type == SIMPLE ? all_points : scratch.comp_points;
|
||||
unsigned old_length = points.length;
|
||||
|
||||
switch (type) {
|
||||
case SIMPLE:
|
||||
if (depth == 0 && head_maxp_info)
|
||||
head_maxp_info->maxContours = hb_max (head_maxp_info->maxContours, (unsigned) header->numberOfContours);
|
||||
if (depth > 0 && composite_contours)
|
||||
*composite_contours += (unsigned) header->numberOfContours;
|
||||
if (unlikely (!SimpleGlyph (*header, bytes).get_contour_points (all_points, phantom_only)))
|
||||
return false;
|
||||
break;
|
||||
case COMPOSITE:
|
||||
{
|
||||
for (auto &item : get_composite_iterator ())
|
||||
if (unlikely (!item.get_points (points))) return false;
|
||||
break;
|
||||
}
|
||||
case EMPTY:
|
||||
break;
|
||||
}
|
||||
|
||||
/* Init phantom points */
|
||||
if (unlikely (!points.resize (points.length + PHANTOM_COUNT))) return false;
|
||||
hb_array_t<contour_point_t> phantoms = points.as_array ().sub_array (points.length - PHANTOM_COUNT, PHANTOM_COUNT);
|
||||
{
|
||||
// Duplicated code.
|
||||
int lsb = 0;
|
||||
glyf_accelerator.hmtx->get_leading_bearing_without_var_unscaled (gid, &lsb);
|
||||
int h_delta = (int) header->xMin - lsb;
|
||||
HB_UNUSED int tsb = 0;
|
||||
#ifndef HB_NO_VERTICAL
|
||||
glyf_accelerator.vmtx->get_leading_bearing_without_var_unscaled (gid, &tsb);
|
||||
#endif
|
||||
int v_orig = (int) header->yMax + tsb;
|
||||
unsigned h_adv = glyf_accelerator.hmtx->get_advance_without_var_unscaled (gid);
|
||||
unsigned v_adv =
|
||||
#ifndef HB_NO_VERTICAL
|
||||
glyf_accelerator.vmtx->get_advance_without_var_unscaled (gid)
|
||||
#else
|
||||
- font->face->get_upem ()
|
||||
#endif
|
||||
;
|
||||
phantoms[PHANTOM_LEFT].x = h_delta;
|
||||
phantoms[PHANTOM_RIGHT].x = (int) h_adv + h_delta;
|
||||
phantoms[PHANTOM_TOP].y = v_orig;
|
||||
phantoms[PHANTOM_BOTTOM].y = v_orig - (int) v_adv;
|
||||
}
|
||||
|
||||
#ifndef HB_NO_VAR
|
||||
if (hb_any (coords))
|
||||
{
|
||||
#ifndef HB_NO_BEYOND_64K
|
||||
if (glyf_accelerator.GVAR->has_data ())
|
||||
{
|
||||
if (!glyf_accelerator.GVAR->apply_deltas_to_points (gid,
|
||||
coords,
|
||||
points.as_array ().sub_array (old_length),
|
||||
scratch,
|
||||
gvar_cache,
|
||||
phantom_only && type == SIMPLE))
|
||||
return false;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
if (!glyf_accelerator.gvar->apply_deltas_to_points (gid,
|
||||
coords,
|
||||
points.as_array ().sub_array (old_length),
|
||||
scratch,
|
||||
gvar_cache,
|
||||
phantom_only && type == SIMPLE))
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
// mainly used by CompositeGlyph calculating new X/Y offset value so no need to extend it
|
||||
// with child glyphs' points
|
||||
if (points_with_deltas != nullptr && depth == 0 && type == COMPOSITE)
|
||||
{
|
||||
assert (old_length == 0);
|
||||
*points_with_deltas = points;
|
||||
}
|
||||
|
||||
float shift = 0;
|
||||
switch (type) {
|
||||
case SIMPLE:
|
||||
if (depth == 0 && head_maxp_info)
|
||||
head_maxp_info->maxPoints = hb_max (head_maxp_info->maxPoints, all_points.length - old_length - 4);
|
||||
shift = phantoms[PHANTOM_LEFT].x;
|
||||
break;
|
||||
case COMPOSITE:
|
||||
{
|
||||
hb_decycler_node_t decycler_node (scratch.decycler);
|
||||
|
||||
unsigned int comp_index = 0;
|
||||
for (auto &item : get_composite_iterator ())
|
||||
{
|
||||
hb_codepoint_t item_gid = item.get_gid ();
|
||||
|
||||
if (unlikely (!decycler_node.visit (item_gid)))
|
||||
{
|
||||
comp_index++;
|
||||
continue;
|
||||
}
|
||||
|
||||
unsigned old_count = all_points.length;
|
||||
|
||||
if (unlikely ((!phantom_only || (use_my_metrics && item.is_use_my_metrics ())) &&
|
||||
!glyf_accelerator.glyph_for_gid (item_gid)
|
||||
.get_points (font,
|
||||
glyf_accelerator,
|
||||
all_points,
|
||||
scratch,
|
||||
points_with_deltas,
|
||||
head_maxp_info,
|
||||
composite_contours,
|
||||
shift_points_hori,
|
||||
use_my_metrics,
|
||||
phantom_only,
|
||||
coords,
|
||||
gvar_cache,
|
||||
depth + 1,
|
||||
edge_count)))
|
||||
{
|
||||
points.resize (old_length);
|
||||
return false;
|
||||
}
|
||||
|
||||
// points might have been reallocated. Relocate phantoms.
|
||||
phantoms = points.as_array ().sub_array (points.length - PHANTOM_COUNT, PHANTOM_COUNT);
|
||||
|
||||
auto comp_points = all_points.as_array ().sub_array (old_count);
|
||||
|
||||
/* Copy phantom points from component if USE_MY_METRICS flag set */
|
||||
if (use_my_metrics && item.is_use_my_metrics ())
|
||||
for (unsigned int i = 0; i < PHANTOM_COUNT; i++)
|
||||
phantoms[i] = comp_points[comp_points.length - PHANTOM_COUNT + i];
|
||||
|
||||
if (comp_points) // Empty in case of phantom_only
|
||||
{
|
||||
float matrix[4];
|
||||
contour_point_t default_trans;
|
||||
item.get_transformation (matrix, default_trans);
|
||||
|
||||
/* Apply component transformation & translation (with deltas applied) */
|
||||
item.transform_points (comp_points, matrix, points[old_length + comp_index]);
|
||||
}
|
||||
|
||||
if (item.is_anchored () && !phantom_only)
|
||||
{
|
||||
unsigned int p1, p2;
|
||||
item.get_anchor_points (p1, p2);
|
||||
if (likely (p1 < all_points.length && p2 < comp_points.length))
|
||||
{
|
||||
contour_point_t delta;
|
||||
delta.init (all_points[p1].x - comp_points[p2].x,
|
||||
all_points[p1].y - comp_points[p2].y);
|
||||
|
||||
item.translate (delta, comp_points);
|
||||
}
|
||||
}
|
||||
|
||||
all_points.resize (all_points.length - PHANTOM_COUNT);
|
||||
|
||||
if (all_points.length > HB_GLYF_MAX_POINTS)
|
||||
{
|
||||
points.resize (old_length);
|
||||
return false;
|
||||
}
|
||||
|
||||
comp_index++;
|
||||
}
|
||||
|
||||
if (head_maxp_info && depth == 0)
|
||||
{
|
||||
if (composite_contours)
|
||||
head_maxp_info->maxCompositeContours = hb_max (head_maxp_info->maxCompositeContours, *composite_contours);
|
||||
head_maxp_info->maxCompositePoints = hb_max (head_maxp_info->maxCompositePoints, all_points.length);
|
||||
head_maxp_info->maxComponentElements = hb_max (head_maxp_info->maxComponentElements, comp_index);
|
||||
}
|
||||
all_points.extend (phantoms);
|
||||
shift = phantoms[PHANTOM_LEFT].x;
|
||||
points.resize (old_length);
|
||||
} break;
|
||||
case EMPTY:
|
||||
all_points.extend (phantoms);
|
||||
shift = phantoms[PHANTOM_LEFT].x;
|
||||
points.resize (old_length);
|
||||
break;
|
||||
}
|
||||
|
||||
if (depth == 0 && shift_points_hori) /* Apply at top level */
|
||||
{
|
||||
/* Undocumented rasterizer behavior:
|
||||
* Shift points horizontally by the updated left side bearing
|
||||
*/
|
||||
if (shift)
|
||||
for (auto &point : all_points)
|
||||
point.x -= shift;
|
||||
}
|
||||
|
||||
return !all_points.in_error ();
|
||||
}
|
||||
|
||||
bool get_extents_without_var_scaled (hb_font_t *font, const glyf_accelerator_t &glyf_accelerator,
|
||||
hb_glyph_extents_t *extents) const
|
||||
{
|
||||
if (type == EMPTY)
|
||||
{
|
||||
*extents = {0, 0, 0, 0};
|
||||
return true; /* Empty glyph; zero extents. */
|
||||
}
|
||||
return header->get_extents_without_var_scaled (font, glyf_accelerator, gid, extents);
|
||||
}
|
||||
|
||||
hb_bytes_t get_bytes () const { return bytes; }
|
||||
glyph_type_t get_type () const { return type; }
|
||||
const GlyphHeader *get_header () const { return header; }
|
||||
|
||||
Glyph () : bytes (),
|
||||
header (bytes.as<GlyphHeader> ()),
|
||||
gid (-1),
|
||||
type(EMPTY)
|
||||
{}
|
||||
|
||||
Glyph (hb_bytes_t bytes_,
|
||||
hb_codepoint_t gid_ = (unsigned) -1) : bytes (bytes_),
|
||||
header (bytes.as<GlyphHeader> ()),
|
||||
gid (gid_)
|
||||
{
|
||||
int num_contours = header->numberOfContours;
|
||||
if (unlikely (num_contours == 0)) type = EMPTY;
|
||||
else if (num_contours > 0) type = SIMPLE;
|
||||
else if (num_contours <= -1) type = COMPOSITE;
|
||||
else type = EMPTY; // Spec deviation; Spec says COMPOSITE, but not seen in the wild.
|
||||
}
|
||||
|
||||
protected:
|
||||
hb_bytes_t bytes;
|
||||
const GlyphHeader *header;
|
||||
hb_codepoint_t gid;
|
||||
glyph_type_t type;
|
||||
};
|
||||
|
||||
|
||||
} /* namespace glyf_impl */
|
||||
} /* namespace OT */
|
||||
|
||||
|
||||
#endif /* OT_GLYF_GLYPH_HH */
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
#ifndef OT_GLYF_GLYPHHEADER_HH
|
||||
#define OT_GLYF_GLYPHHEADER_HH
|
||||
|
||||
|
||||
#include "../../hb-open-type.hh"
|
||||
|
||||
|
||||
namespace OT {
|
||||
namespace glyf_impl {
|
||||
|
||||
|
||||
struct GlyphHeader
|
||||
{
|
||||
bool has_data () const { return numberOfContours; }
|
||||
|
||||
template <typename accelerator_t>
|
||||
bool get_extents_without_var_scaled (hb_font_t *font, const accelerator_t &glyf_accelerator,
|
||||
hb_codepoint_t gid, hb_glyph_extents_t *extents) const
|
||||
{
|
||||
/* Undocumented rasterizer behavior: shift glyph to the left by (lsb - xMin), i.e., xMin = lsb */
|
||||
/* extents->x_bearing = hb_min (glyph_header.xMin, glyph_header.xMax); */
|
||||
int lsb = hb_min (xMin, xMax);
|
||||
(void) glyf_accelerator.hmtx->get_leading_bearing_without_var_unscaled (gid, &lsb);
|
||||
extents->x_bearing = lsb;
|
||||
extents->y_bearing = hb_max (yMin, yMax);
|
||||
extents->width = hb_max (xMin, xMax) - hb_min (xMin, xMax);
|
||||
extents->height = hb_min (yMin, yMax) - hb_max (yMin, yMax);
|
||||
|
||||
font->scale_glyph_extents (extents);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
HBINT16 numberOfContours;
|
||||
/* If the number of contours is
|
||||
* greater than or equal to zero,
|
||||
* this is a simple glyph; if negative,
|
||||
* this is a composite glyph. */
|
||||
FWORD xMin; /* Minimum x for coordinate data. */
|
||||
FWORD yMin; /* Minimum y for coordinate data. */
|
||||
FWORD xMax; /* Maximum x for coordinate data. */
|
||||
FWORD yMax; /* Maximum y for coordinate data. */
|
||||
public:
|
||||
DEFINE_SIZE_STATIC (10);
|
||||
};
|
||||
|
||||
|
||||
} /* namespace glyf_impl */
|
||||
} /* namespace OT */
|
||||
|
||||
|
||||
#endif /* OT_GLYF_GLYPHHEADER_HH */
|
||||
+347
@@ -0,0 +1,347 @@
|
||||
#ifndef OT_GLYF_SIMPLEGLYPH_HH
|
||||
#define OT_GLYF_SIMPLEGLYPH_HH
|
||||
|
||||
|
||||
#include "../../hb-open-type.hh"
|
||||
|
||||
|
||||
namespace OT {
|
||||
namespace glyf_impl {
|
||||
|
||||
|
||||
struct SimpleGlyph
|
||||
{
|
||||
enum simple_glyph_flag_t
|
||||
{
|
||||
FLAG_ON_CURVE = 0x01,
|
||||
FLAG_X_SHORT = 0x02,
|
||||
FLAG_Y_SHORT = 0x04,
|
||||
FLAG_REPEAT = 0x08,
|
||||
FLAG_X_SAME = 0x10,
|
||||
FLAG_Y_SAME = 0x20,
|
||||
FLAG_OVERLAP_SIMPLE = 0x40,
|
||||
FLAG_CUBIC = 0x80
|
||||
};
|
||||
|
||||
const GlyphHeader &header;
|
||||
hb_bytes_t bytes;
|
||||
SimpleGlyph (const GlyphHeader &header_, hb_bytes_t bytes_) :
|
||||
header (header_), bytes (bytes_) {}
|
||||
|
||||
unsigned int instruction_len_offset () const
|
||||
{ return GlyphHeader::static_size + 2 * header.numberOfContours; }
|
||||
|
||||
unsigned int length (unsigned int instruction_len) const
|
||||
{ return instruction_len_offset () + 2 + instruction_len; }
|
||||
|
||||
bool has_instructions_length () const
|
||||
{
|
||||
return instruction_len_offset () + 2 <= bytes.length;
|
||||
}
|
||||
|
||||
unsigned int instructions_length () const
|
||||
{
|
||||
unsigned int instruction_length_offset = instruction_len_offset ();
|
||||
if (unlikely (instruction_length_offset + 2 > bytes.length)) return 0;
|
||||
|
||||
const HBUINT16 &instructionLength = StructAtOffset<HBUINT16> (&bytes, instruction_length_offset);
|
||||
/* Out of bounds of the current glyph */
|
||||
if (unlikely (length (instructionLength) > bytes.length)) return 0;
|
||||
return instructionLength;
|
||||
}
|
||||
|
||||
const hb_bytes_t trim_padding () const
|
||||
{
|
||||
/* based on FontTools _g_l_y_f.py::trim */
|
||||
const uint8_t *glyph = (uint8_t*) bytes.arrayZ;
|
||||
const uint8_t *glyph_end = glyph + bytes.length;
|
||||
/* simple glyph w/contours, possibly trimmable */
|
||||
glyph += instruction_len_offset ();
|
||||
|
||||
if (unlikely (glyph + 2 >= glyph_end)) return hb_bytes_t ();
|
||||
unsigned int num_coordinates = StructAtOffset<HBUINT16> (glyph - 2, 0) + 1;
|
||||
unsigned int num_instructions = StructAtOffset<HBUINT16> (glyph, 0);
|
||||
|
||||
glyph += 2 + num_instructions;
|
||||
|
||||
unsigned int coord_bytes = 0;
|
||||
unsigned int coords_with_flags = 0;
|
||||
while (glyph < glyph_end)
|
||||
{
|
||||
uint8_t flag = *glyph;
|
||||
glyph++;
|
||||
|
||||
unsigned int repeat = 1;
|
||||
if (flag & FLAG_REPEAT)
|
||||
{
|
||||
if (unlikely (glyph >= glyph_end)) return hb_bytes_t ();
|
||||
repeat = *glyph + 1;
|
||||
glyph++;
|
||||
}
|
||||
|
||||
unsigned int xBytes, yBytes;
|
||||
xBytes = yBytes = 0;
|
||||
if (flag & FLAG_X_SHORT) xBytes = 1;
|
||||
else if ((flag & FLAG_X_SAME) == 0) xBytes = 2;
|
||||
|
||||
if (flag & FLAG_Y_SHORT) yBytes = 1;
|
||||
else if ((flag & FLAG_Y_SAME) == 0) yBytes = 2;
|
||||
|
||||
coord_bytes += (xBytes + yBytes) * repeat;
|
||||
coords_with_flags += repeat;
|
||||
if (coords_with_flags >= num_coordinates) break;
|
||||
}
|
||||
|
||||
if (unlikely (coords_with_flags != num_coordinates)) return hb_bytes_t ();
|
||||
return bytes.sub_array (0, bytes.length + coord_bytes - (glyph_end - glyph));
|
||||
}
|
||||
|
||||
/* zero instruction length */
|
||||
void drop_hints ()
|
||||
{
|
||||
if (!has_instructions_length ()) return;
|
||||
GlyphHeader &glyph_header = const_cast<GlyphHeader &> (header);
|
||||
(HBUINT16 &) StructAtOffset<HBUINT16> (&glyph_header, instruction_len_offset ()) = 0;
|
||||
}
|
||||
|
||||
void drop_hints_bytes (hb_bytes_t &dest_start, hb_bytes_t &dest_end) const
|
||||
{
|
||||
unsigned int instructions_len = instructions_length ();
|
||||
unsigned int glyph_length = length (instructions_len);
|
||||
dest_start = bytes.sub_array (0, glyph_length - instructions_len);
|
||||
dest_end = bytes.sub_array (glyph_length, bytes.length - glyph_length);
|
||||
}
|
||||
|
||||
void set_overlaps_flag ()
|
||||
{
|
||||
if (unlikely (!header.numberOfContours)) return;
|
||||
|
||||
unsigned flags_offset = length (instructions_length ());
|
||||
if (unlikely (flags_offset + 1 > bytes.length)) return;
|
||||
|
||||
HBUINT8 &first_flag = (HBUINT8 &) StructAtOffset<HBUINT16> (&bytes, flags_offset);
|
||||
first_flag = (uint8_t) first_flag | FLAG_OVERLAP_SIMPLE;
|
||||
}
|
||||
|
||||
static bool read_flags (const HBUINT8 *&p /* IN/OUT */,
|
||||
hb_array_t<contour_point_t> points_ /* IN/OUT */,
|
||||
const HBUINT8 *end)
|
||||
{
|
||||
auto *points = points_.arrayZ;
|
||||
unsigned count = points_.length;
|
||||
for (unsigned int i = 0; i < count;)
|
||||
{
|
||||
if (unlikely (p + 1 > end)) return false;
|
||||
uint8_t flag = *p++;
|
||||
points[i++].flag = flag;
|
||||
if (flag & FLAG_REPEAT)
|
||||
{
|
||||
if (unlikely (p + 1 > end)) return false;
|
||||
unsigned int repeat_count = *p++;
|
||||
unsigned stop = hb_min (i + repeat_count, count);
|
||||
for (; i < stop; i++)
|
||||
points[i].flag = flag;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool read_points (const HBUINT8 *&p /* IN/OUT */,
|
||||
hb_array_t<contour_point_t> points_ /* IN/OUT */,
|
||||
const HBUINT8 *end,
|
||||
float contour_point_t::*m,
|
||||
const simple_glyph_flag_t short_flag,
|
||||
const simple_glyph_flag_t same_flag)
|
||||
{
|
||||
int v = 0;
|
||||
|
||||
for (auto &point : points_)
|
||||
{
|
||||
unsigned flag = point.flag;
|
||||
if (flag & short_flag)
|
||||
{
|
||||
if (unlikely (p + 1 > end)) return false;
|
||||
v += (bool(flag & same_flag) * 2 - 1) * *p++;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!(flag & same_flag))
|
||||
{
|
||||
if (unlikely (p + HBINT16::static_size > end)) return false;
|
||||
v += *(const HBINT16 *) p;
|
||||
p += HBINT16::static_size;
|
||||
}
|
||||
}
|
||||
point.*m = v;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool get_contour_points (contour_point_vector_t &points /* OUT */,
|
||||
bool phantom_only = false) const
|
||||
{
|
||||
const HBUINT16 *endPtsOfContours = &StructAfter<HBUINT16> (header);
|
||||
int num_contours = header.numberOfContours;
|
||||
assert (num_contours > 0);
|
||||
/* One extra item at the end, for the instruction-count below. */
|
||||
if (unlikely (!bytes.check_range (&endPtsOfContours[num_contours]))) return false;
|
||||
unsigned int num_points = endPtsOfContours[num_contours - 1] + 1;
|
||||
if (unlikely (num_points < (unsigned) num_contours)) return false;
|
||||
|
||||
unsigned old_length = points.length;
|
||||
points.alloc (points.length + num_points + 4); // Allocate for phantom points, to avoid a possible copy
|
||||
if (unlikely (!points.resize_dirty (points.length + num_points))) return false;
|
||||
auto points_ = points.as_array ().sub_array (old_length);
|
||||
if (!phantom_only)
|
||||
hb_memset (points_.arrayZ, 0, sizeof (contour_point_t) * num_points);
|
||||
if (phantom_only) return true;
|
||||
|
||||
for (int i = 0; i < num_contours; i++)
|
||||
points_[endPtsOfContours[i]].is_end_point = true;
|
||||
|
||||
/* Skip instructions */
|
||||
const HBUINT8 *p = &StructAtOffset<HBUINT8> (&endPtsOfContours[num_contours + 1],
|
||||
endPtsOfContours[num_contours]);
|
||||
|
||||
if (unlikely ((const char *) p < bytes.arrayZ)) return false; /* Unlikely overflow */
|
||||
const HBUINT8 *end = (const HBUINT8 *) (bytes.arrayZ + bytes.length);
|
||||
if (unlikely (p >= end)) return false;
|
||||
|
||||
/* Read x & y coordinates */
|
||||
return read_flags (p, points_, end)
|
||||
&& read_points (p, points_, end, &contour_point_t::x,
|
||||
FLAG_X_SHORT, FLAG_X_SAME)
|
||||
&& read_points (p, points_, end, &contour_point_t::y,
|
||||
FLAG_Y_SHORT, FLAG_Y_SAME);
|
||||
}
|
||||
|
||||
static void encode_coord (int value,
|
||||
unsigned &flag,
|
||||
const simple_glyph_flag_t short_flag,
|
||||
const simple_glyph_flag_t same_flag,
|
||||
hb_vector_t<uint8_t> &coords /* OUT */)
|
||||
{
|
||||
if (value == 0)
|
||||
{
|
||||
flag |= same_flag;
|
||||
}
|
||||
else if (value >= -255 && value <= 255)
|
||||
{
|
||||
flag |= short_flag;
|
||||
if (value > 0) flag |= same_flag;
|
||||
else value = -value;
|
||||
|
||||
coords.arrayZ[coords.length++] = (uint8_t) value;
|
||||
}
|
||||
else
|
||||
{
|
||||
int16_t val = value;
|
||||
coords.arrayZ[coords.length++] = val >> 8;
|
||||
coords.arrayZ[coords.length++] = val & 0xff;
|
||||
}
|
||||
}
|
||||
|
||||
static void encode_flag (unsigned flag,
|
||||
unsigned &repeat,
|
||||
unsigned lastflag,
|
||||
hb_vector_t<uint8_t> &flags /* OUT */)
|
||||
{
|
||||
if (flag == lastflag && repeat != 255)
|
||||
{
|
||||
repeat++;
|
||||
if (repeat == 1)
|
||||
{
|
||||
/* We know there's room. */
|
||||
flags.arrayZ[flags.length++] = flag;
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned len = flags.length;
|
||||
flags.arrayZ[len-2] = flag | FLAG_REPEAT;
|
||||
flags.arrayZ[len-1] = repeat;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
repeat = 0;
|
||||
flags.arrayZ[flags.length++] = flag;
|
||||
}
|
||||
}
|
||||
|
||||
bool compile_bytes_with_deltas (const contour_point_vector_t &all_points,
|
||||
bool no_hinting,
|
||||
hb_bytes_t &dest_bytes /* OUT */)
|
||||
{
|
||||
if (header.numberOfContours == 0 || all_points.length <= 4)
|
||||
{
|
||||
dest_bytes = hb_bytes_t ();
|
||||
return true;
|
||||
}
|
||||
unsigned num_points = all_points.length - 4;
|
||||
|
||||
hb_vector_t<uint8_t> flags, x_coords, y_coords;
|
||||
if (unlikely (!flags.alloc_exact (num_points))) return false;
|
||||
if (unlikely (!x_coords.alloc_exact (2*num_points))) return false;
|
||||
if (unlikely (!y_coords.alloc_exact (2*num_points))) return false;
|
||||
|
||||
unsigned lastflag = 255, repeat = 0;
|
||||
int prev_x = 0, prev_y = 0;
|
||||
|
||||
for (unsigned i = 0; i < num_points; i++)
|
||||
{
|
||||
unsigned flag = all_points.arrayZ[i].flag;
|
||||
flag &= FLAG_ON_CURVE | FLAG_OVERLAP_SIMPLE | FLAG_CUBIC;
|
||||
|
||||
int cur_x = roundf (all_points.arrayZ[i].x);
|
||||
int cur_y = roundf (all_points.arrayZ[i].y);
|
||||
encode_coord (cur_x - prev_x, flag, FLAG_X_SHORT, FLAG_X_SAME, x_coords);
|
||||
encode_coord (cur_y - prev_y, flag, FLAG_Y_SHORT, FLAG_Y_SAME, y_coords);
|
||||
encode_flag (flag, repeat, lastflag, flags);
|
||||
|
||||
prev_x = cur_x;
|
||||
prev_y = cur_y;
|
||||
lastflag = flag;
|
||||
}
|
||||
|
||||
unsigned len_before_instrs = 2 * header.numberOfContours + 2;
|
||||
unsigned len_instrs = instructions_length ();
|
||||
unsigned total_len = len_before_instrs + flags.length + x_coords.length + y_coords.length;
|
||||
|
||||
if (!no_hinting)
|
||||
total_len += len_instrs;
|
||||
|
||||
char *p = (char *) hb_malloc (total_len);
|
||||
if (unlikely (!p)) return false;
|
||||
|
||||
const char *src = bytes.arrayZ + GlyphHeader::static_size;
|
||||
char *cur = p;
|
||||
hb_memcpy (p, src, len_before_instrs);
|
||||
|
||||
cur += len_before_instrs;
|
||||
src += len_before_instrs;
|
||||
|
||||
if (!no_hinting)
|
||||
{
|
||||
hb_memcpy (cur, src, len_instrs);
|
||||
cur += len_instrs;
|
||||
}
|
||||
|
||||
hb_memcpy (cur, flags.arrayZ, flags.length);
|
||||
cur += flags.length;
|
||||
|
||||
hb_memcpy (cur, x_coords.arrayZ, x_coords.length);
|
||||
cur += x_coords.length;
|
||||
|
||||
hb_memcpy (cur, y_coords.arrayZ, y_coords.length);
|
||||
|
||||
dest_bytes = hb_bytes_t (p, total_len);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
} /* namespace glyf_impl */
|
||||
} /* namespace OT */
|
||||
|
||||
|
||||
#endif /* OT_GLYF_SIMPLEGLYPH_HH */
|
||||
+141
@@ -0,0 +1,141 @@
|
||||
#ifndef OT_GLYF_SUBSETGLYPH_HH
|
||||
#define OT_GLYF_SUBSETGLYPH_HH
|
||||
|
||||
|
||||
#include "../../hb-open-type.hh"
|
||||
|
||||
|
||||
namespace OT {
|
||||
|
||||
struct glyf_accelerator_t;
|
||||
|
||||
namespace glyf_impl {
|
||||
|
||||
|
||||
struct SubsetGlyph
|
||||
{
|
||||
hb_codepoint_t old_gid;
|
||||
Glyph source_glyph;
|
||||
hb_bytes_t dest_start; /* region of source_glyph to copy first */
|
||||
hb_bytes_t dest_end; /* region of source_glyph to copy second */
|
||||
bool allocated;
|
||||
|
||||
bool serialize (hb_serialize_context_t *c,
|
||||
bool use_short_loca,
|
||||
const hb_subset_plan_t *plan) const
|
||||
{
|
||||
TRACE_SERIALIZE (this);
|
||||
|
||||
hb_bytes_t dest_glyph = dest_start.copy (c);
|
||||
hb_bytes_t end_copy = dest_end.copy (c);
|
||||
if (!end_copy.arrayZ || !dest_glyph.arrayZ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
dest_glyph = hb_bytes_t (&dest_glyph, dest_glyph.length + end_copy.length);
|
||||
unsigned int pad_length = use_short_loca ? padding () : 0;
|
||||
DEBUG_MSG (SUBSET, nullptr, "serialize %u byte glyph, width %u pad %u", dest_glyph.length, dest_glyph.length + pad_length, pad_length);
|
||||
|
||||
HBUINT8 pad;
|
||||
pad = 0;
|
||||
while (pad_length > 0)
|
||||
{
|
||||
(void) c->embed (pad);
|
||||
pad_length--;
|
||||
}
|
||||
|
||||
if (unlikely (!dest_glyph.length)) return_trace (true);
|
||||
|
||||
/* update components gids. */
|
||||
for (auto &_ : Glyph (dest_glyph).get_composite_iterator ())
|
||||
{
|
||||
hb_codepoint_t new_gid;
|
||||
if (plan->new_gid_for_old_gid (_.get_gid(), &new_gid))
|
||||
const_cast<CompositeGlyphRecord &> (_).set_gid (new_gid);
|
||||
}
|
||||
|
||||
#ifndef HB_NO_BEYOND_64K
|
||||
auto it = Glyph (dest_glyph).get_composite_iterator ();
|
||||
if (it)
|
||||
{
|
||||
/* lower GID24 to GID16 in components if possible. */
|
||||
char *p = it ? (char *) &*it : nullptr;
|
||||
char *q = p;
|
||||
const char *end = dest_glyph.arrayZ + dest_glyph.length;
|
||||
while (it)
|
||||
{
|
||||
auto &rec = const_cast<CompositeGlyphRecord &> (*it);
|
||||
++it;
|
||||
|
||||
q += rec.get_size ();
|
||||
|
||||
rec.lower_gid_24_to_16 ();
|
||||
|
||||
unsigned size = rec.get_size ();
|
||||
|
||||
memmove (p, &rec, size);
|
||||
|
||||
p += size;
|
||||
}
|
||||
memmove (p, q, end - q);
|
||||
p += end - q;
|
||||
|
||||
/* We want to shorten the glyph, but we can't do that without
|
||||
* updating the length in the loca table, which is already
|
||||
* written out :-(. So we just fill the rest of the glyph with
|
||||
* harmless instructions, since that's what they will be
|
||||
* interpreted as.
|
||||
*
|
||||
* Should move the lowering to _populate_subset_glyphs() to
|
||||
* fix this issue. */
|
||||
|
||||
hb_memset (p, 0x7A /* TrueType instruction ROFF; harmless */, end - p);
|
||||
p += end - p;
|
||||
dest_glyph = hb_bytes_t (dest_glyph.arrayZ, p - (char *) dest_glyph.arrayZ);
|
||||
|
||||
// TODO: Padding; & trim serialized bytes.
|
||||
// TODO: Update length in loca. Ugh.
|
||||
}
|
||||
#endif
|
||||
|
||||
if (plan->flags & HB_SUBSET_FLAGS_NO_HINTING)
|
||||
Glyph (dest_glyph).drop_hints ();
|
||||
|
||||
if (plan->flags & HB_SUBSET_FLAGS_SET_OVERLAPS_FLAG)
|
||||
Glyph (dest_glyph).set_overlaps_flag ();
|
||||
|
||||
return_trace (true);
|
||||
}
|
||||
|
||||
bool compile_bytes_with_deltas (const hb_subset_plan_t *plan,
|
||||
hb_font_t *font,
|
||||
const glyf_accelerator_t &glyf)
|
||||
{
|
||||
allocated = source_glyph.compile_bytes_with_deltas (plan, font, glyf, dest_start, dest_end);
|
||||
return allocated;
|
||||
}
|
||||
|
||||
void free_compiled_bytes ()
|
||||
{
|
||||
if (likely (allocated)) {
|
||||
allocated = false;
|
||||
dest_start.fini ();
|
||||
dest_end.fini ();
|
||||
}
|
||||
}
|
||||
|
||||
void drop_hints_bytes ()
|
||||
{ source_glyph.drop_hints_bytes (dest_start, dest_end); }
|
||||
|
||||
unsigned int length () const { return dest_start.length + dest_end.length; }
|
||||
/* pad to 2 to ensure 2-byte loca will be ok */
|
||||
unsigned int padding () const { return length () % 2; }
|
||||
unsigned int padded_size () const { return length () + padding (); }
|
||||
};
|
||||
|
||||
|
||||
} /* namespace glyf_impl */
|
||||
} /* namespace OT */
|
||||
|
||||
|
||||
#endif /* OT_GLYF_SUBSETGLYPH_HH */
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
#ifndef OT_GLYF_COMPOSITE_ITER_HH
|
||||
#define OT_GLYF_COMPOSITE_ITER_HH
|
||||
|
||||
|
||||
#include "../../hb.hh"
|
||||
|
||||
|
||||
namespace OT {
|
||||
namespace glyf_impl {
|
||||
|
||||
|
||||
template <typename CompositeGlyphRecord>
|
||||
struct composite_iter_tmpl : hb_iter_with_fallback_t<composite_iter_tmpl<CompositeGlyphRecord>,
|
||||
const CompositeGlyphRecord &>
|
||||
{
|
||||
typedef const CompositeGlyphRecord *__item_t__;
|
||||
composite_iter_tmpl (hb_bytes_t glyph_, __item_t__ current_) :
|
||||
glyph (glyph_), current (nullptr), current_size (0)
|
||||
{
|
||||
set_current (current_);
|
||||
}
|
||||
|
||||
composite_iter_tmpl () : glyph (hb_bytes_t ()), current (nullptr), current_size (0) {}
|
||||
|
||||
const CompositeGlyphRecord & __item__ () const { return *current; }
|
||||
bool __more__ () const { return current; }
|
||||
void __next__ ()
|
||||
{
|
||||
if (!current->has_more ()) { current = nullptr; return; }
|
||||
|
||||
set_current (&StructAtOffset<CompositeGlyphRecord> (current, current_size));
|
||||
}
|
||||
composite_iter_tmpl __end__ () const { return composite_iter_tmpl (); }
|
||||
bool operator != (const composite_iter_tmpl& o) const
|
||||
{ return current != o.current; }
|
||||
|
||||
|
||||
void set_current (__item_t__ current_)
|
||||
{
|
||||
if (!glyph.check_range (current_, CompositeGlyphRecord::min_size))
|
||||
{
|
||||
current = nullptr;
|
||||
current_size = 0;
|
||||
return;
|
||||
}
|
||||
unsigned size = current_->get_size ();
|
||||
if (!glyph.check_range (current_, size))
|
||||
{
|
||||
current = nullptr;
|
||||
current_size = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
current = current_;
|
||||
current_size = size;
|
||||
}
|
||||
|
||||
private:
|
||||
hb_bytes_t glyph;
|
||||
__item_t__ current;
|
||||
unsigned current_size;
|
||||
};
|
||||
|
||||
|
||||
} /* namespace glyf_impl */
|
||||
} /* namespace OT */
|
||||
|
||||
#endif /* OT_GLYF_COMPOSITE_ITER_HH */
|
||||
+127
@@ -0,0 +1,127 @@
|
||||
#ifndef OT_GLYF_GLYF_HELPERS_HH
|
||||
#define OT_GLYF_GLYF_HELPERS_HH
|
||||
|
||||
|
||||
#include "../../hb-open-type.hh"
|
||||
#include "../../hb-subset-plan.hh"
|
||||
|
||||
#include "loca.hh"
|
||||
|
||||
|
||||
namespace OT {
|
||||
namespace glyf_impl {
|
||||
|
||||
|
||||
template<typename IteratorIn, typename TypeOut,
|
||||
hb_requires (hb_is_source_of (IteratorIn, unsigned int))>
|
||||
static void
|
||||
_write_loca (IteratorIn&& it,
|
||||
const hb_sorted_vector_t<hb_codepoint_pair_t> new_to_old_gid_list,
|
||||
bool short_offsets,
|
||||
TypeOut *dest,
|
||||
unsigned num_offsets)
|
||||
{
|
||||
unsigned right_shift = short_offsets ? 1 : 0;
|
||||
unsigned offset = 0;
|
||||
TypeOut value;
|
||||
value = 0;
|
||||
*dest++ = value;
|
||||
hb_codepoint_t last = 0;
|
||||
for (auto _ : new_to_old_gid_list)
|
||||
{
|
||||
hb_codepoint_t gid = _.first;
|
||||
for (; last < gid; last++)
|
||||
{
|
||||
DEBUG_MSG (SUBSET, nullptr, "loca entry empty offset %u", offset);
|
||||
*dest++ = value;
|
||||
}
|
||||
|
||||
unsigned padded_size = *it++;
|
||||
offset += padded_size;
|
||||
DEBUG_MSG (SUBSET, nullptr, "loca entry gid %" PRIu32 " offset %u padded-size %u", gid, offset, padded_size);
|
||||
value = offset >> right_shift;
|
||||
*dest++ = value;
|
||||
|
||||
last++; // Skip over gid
|
||||
}
|
||||
unsigned num_glyphs = num_offsets - 1;
|
||||
for (; last < num_glyphs; last++)
|
||||
{
|
||||
DEBUG_MSG (SUBSET, nullptr, "loca entry empty offset %u", offset);
|
||||
*dest++ = value;
|
||||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
_add_head_and_set_loca_version (hb_subset_plan_t *plan, bool use_short_loca)
|
||||
{
|
||||
hb_blob_t *head_blob = hb_sanitize_context_t ().reference_table<head> (plan->source);
|
||||
hb_blob_t *head_prime_blob = hb_blob_copy_writable_or_fail (head_blob);
|
||||
hb_blob_destroy (head_blob);
|
||||
|
||||
if (unlikely (!head_prime_blob))
|
||||
return false;
|
||||
|
||||
head *head_prime = (head *) hb_blob_get_data_writable (head_prime_blob, nullptr);
|
||||
head_prime->indexToLocFormat = use_short_loca ? 0 : 1;
|
||||
if (plan->normalized_coords)
|
||||
{
|
||||
head_prime->xMin = plan->head_maxp_info.xMin;
|
||||
head_prime->xMax = plan->head_maxp_info.xMax;
|
||||
head_prime->yMin = plan->head_maxp_info.yMin;
|
||||
head_prime->yMax = plan->head_maxp_info.yMax;
|
||||
|
||||
unsigned orig_flag = head_prime->flags;
|
||||
if (plan->head_maxp_info.allXMinIsLsb)
|
||||
orig_flag |= 1 << 1;
|
||||
else
|
||||
orig_flag &= ~(1 << 1);
|
||||
head_prime->flags = orig_flag;
|
||||
}
|
||||
bool success = plan->add_table (HB_OT_TAG_head, head_prime_blob);
|
||||
|
||||
hb_blob_destroy (head_prime_blob);
|
||||
return success;
|
||||
}
|
||||
|
||||
template<typename Iterator,
|
||||
hb_requires (hb_is_source_of (Iterator, unsigned int))>
|
||||
static bool
|
||||
_add_loca_and_head (hb_subset_context_t *c,
|
||||
Iterator padded_offsets,
|
||||
bool use_short_loca)
|
||||
{
|
||||
unsigned num_offsets = c->plan->num_output_glyphs () + 1;
|
||||
unsigned entry_size = use_short_loca ? 2 : 4;
|
||||
|
||||
char *loca_prime_data = (char *) hb_malloc (entry_size * num_offsets);
|
||||
|
||||
if (unlikely (!loca_prime_data)) return false;
|
||||
|
||||
DEBUG_MSG (SUBSET, nullptr, "loca entry_size %u num_offsets %u size %u",
|
||||
entry_size, num_offsets, entry_size * num_offsets);
|
||||
|
||||
if (use_short_loca)
|
||||
_write_loca (padded_offsets, c->plan->new_to_old_gid_list, true, (HBUINT16 *) loca_prime_data, num_offsets);
|
||||
else
|
||||
_write_loca (padded_offsets, c->plan->new_to_old_gid_list, false, (HBUINT32 *) loca_prime_data, num_offsets);
|
||||
|
||||
hb_blob_t *loca_blob = hb_blob_create (loca_prime_data,
|
||||
entry_size * num_offsets,
|
||||
HB_MEMORY_MODE_WRITABLE,
|
||||
loca_prime_data,
|
||||
hb_free);
|
||||
|
||||
bool result = c->plan->add_table (HB_OT_TAG_loca, loca_blob)
|
||||
&& _add_head_and_set_loca_version (c->plan, use_short_loca);
|
||||
|
||||
hb_blob_destroy (loca_blob);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
} /* namespace glyf_impl */
|
||||
} /* namespace OT */
|
||||
|
||||
|
||||
#endif /* OT_GLYF_GLYF_HELPERS_HH */
|
||||
+642
@@ -0,0 +1,642 @@
|
||||
#ifndef OT_GLYF_GLYF_HH
|
||||
#define OT_GLYF_GLYF_HH
|
||||
|
||||
|
||||
#include "../../hb-open-type.hh"
|
||||
#include "../../hb-ot-head-table.hh"
|
||||
#include "../../hb-ot-hmtx-table.hh"
|
||||
#include "../../hb-ot-var-gvar-table.hh"
|
||||
#include "../../hb-draw.hh"
|
||||
#include "../../hb-paint.hh"
|
||||
|
||||
#include "glyf-helpers.hh"
|
||||
#include "Glyph.hh"
|
||||
#include "SubsetGlyph.hh"
|
||||
#include "loca.hh"
|
||||
#include "path-builder.hh"
|
||||
|
||||
|
||||
namespace OT {
|
||||
|
||||
|
||||
/*
|
||||
* glyf -- TrueType Glyph Data
|
||||
* https://docs.microsoft.com/en-us/typography/opentype/spec/glyf
|
||||
*/
|
||||
#define HB_OT_TAG_glyf HB_TAG('g','l','y','f')
|
||||
|
||||
struct glyf
|
||||
{
|
||||
friend struct glyf_accelerator_t;
|
||||
|
||||
static constexpr hb_tag_t tableTag = HB_OT_TAG_glyf;
|
||||
|
||||
static bool has_valid_glyf_format(const hb_face_t* face)
|
||||
{
|
||||
const OT::head &head = *face->table.head;
|
||||
return head.indexToLocFormat <= 1 && head.glyphDataFormat <= 1;
|
||||
}
|
||||
|
||||
bool sanitize (hb_sanitize_context_t *c HB_UNUSED) const
|
||||
{
|
||||
TRACE_SANITIZE (this);
|
||||
/* Runtime checks as eager sanitizing each glyph is costy */
|
||||
return_trace (true);
|
||||
}
|
||||
|
||||
/* requires source of SubsetGlyph complains the identifier isn't declared */
|
||||
template <typename Iterator>
|
||||
bool serialize (hb_serialize_context_t *c,
|
||||
Iterator it,
|
||||
bool use_short_loca,
|
||||
const hb_subset_plan_t *plan)
|
||||
{
|
||||
TRACE_SERIALIZE (this);
|
||||
|
||||
unsigned init_len = c->length ();
|
||||
for (auto &_ : it)
|
||||
if (unlikely (!_.serialize (c, use_short_loca, plan)))
|
||||
return false;
|
||||
|
||||
/* As a special case when all glyph in the font are empty, add a zero byte
|
||||
* to the table, so that OTS doesn’t reject it, and to make the table work
|
||||
* on Windows as well.
|
||||
* See https://github.com/khaledhosny/ots/issues/52 */
|
||||
if (init_len == c->length ())
|
||||
{
|
||||
HBUINT8 empty_byte;
|
||||
empty_byte = 0;
|
||||
c->copy (empty_byte);
|
||||
}
|
||||
return_trace (true);
|
||||
}
|
||||
|
||||
/* Byte region(s) per glyph to output
|
||||
unpadded, hints removed if so requested
|
||||
If we fail to process a glyph we produce an empty (0-length) glyph */
|
||||
bool subset (hb_subset_context_t *c) const
|
||||
{
|
||||
TRACE_SUBSET (this);
|
||||
|
||||
if (!has_valid_glyf_format (c->plan->source)) {
|
||||
// glyf format is unknown don't attempt to subset it.
|
||||
DEBUG_MSG (SUBSET, nullptr,
|
||||
"unkown glyf format, dropping from subset.");
|
||||
return_trace (false);
|
||||
}
|
||||
|
||||
hb_font_t *font = nullptr;
|
||||
if (c->plan->normalized_coords)
|
||||
{
|
||||
font = _create_font_for_instancing (c->plan);
|
||||
if (unlikely (!font))
|
||||
return_trace (false);
|
||||
}
|
||||
|
||||
hb_vector_t<unsigned> padded_offsets;
|
||||
if (unlikely (!padded_offsets.alloc_exact (c->plan->new_to_old_gid_list.length)))
|
||||
return_trace (false);
|
||||
|
||||
hb_vector_t<glyf_impl::SubsetGlyph> glyphs;
|
||||
if (!_populate_subset_glyphs (c->plan, font, glyphs))
|
||||
{
|
||||
hb_font_destroy (font);
|
||||
return_trace (false);
|
||||
}
|
||||
|
||||
if (font)
|
||||
hb_font_destroy (font);
|
||||
|
||||
unsigned max_offset = 0;
|
||||
for (auto &g : glyphs)
|
||||
{
|
||||
unsigned size = g.padded_size ();
|
||||
padded_offsets.push (size);
|
||||
max_offset += size;
|
||||
}
|
||||
|
||||
bool use_short_loca = false;
|
||||
if (likely (!c->plan->force_long_loca))
|
||||
use_short_loca = max_offset < 0x1FFFF;
|
||||
|
||||
if (!use_short_loca)
|
||||
{
|
||||
padded_offsets.clear ();
|
||||
for (auto &g : glyphs)
|
||||
padded_offsets.push (g.length ());
|
||||
}
|
||||
|
||||
auto *glyf_prime = c->serializer->start_embed <glyf> ();
|
||||
bool result = glyf_prime->serialize (c->serializer, hb_iter (glyphs), use_short_loca, c->plan);
|
||||
if (c->plan->normalized_coords && !c->plan->pinned_at_default)
|
||||
_free_compiled_subset_glyphs (glyphs);
|
||||
|
||||
if (unlikely (!c->serializer->check_success (glyf_impl::_add_loca_and_head (c,
|
||||
padded_offsets.iter (),
|
||||
use_short_loca))))
|
||||
return_trace (false);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool
|
||||
_populate_subset_glyphs (const hb_subset_plan_t *plan,
|
||||
hb_font_t *font,
|
||||
hb_vector_t<glyf_impl::SubsetGlyph>& glyphs /* OUT */) const;
|
||||
|
||||
hb_font_t *
|
||||
_create_font_for_instancing (const hb_subset_plan_t *plan) const;
|
||||
|
||||
void _free_compiled_subset_glyphs (hb_vector_t<glyf_impl::SubsetGlyph> &glyphs) const
|
||||
{
|
||||
for (auto &g : glyphs)
|
||||
g.free_compiled_bytes ();
|
||||
}
|
||||
|
||||
protected:
|
||||
UnsizedArrayOf<HBUINT8>
|
||||
dataZ; /* Glyphs data. */
|
||||
public:
|
||||
DEFINE_SIZE_MIN (0); /* In reality, this is UNBOUNDED() type; but since we always
|
||||
* check the size externally, allow Null() object of it by
|
||||
* defining it _MIN instead. */
|
||||
};
|
||||
|
||||
struct glyf_accelerator_t
|
||||
{
|
||||
glyf_accelerator_t (hb_face_t *face)
|
||||
{
|
||||
short_offset = false;
|
||||
num_glyphs = 0;
|
||||
loca_table = nullptr;
|
||||
glyf_table = nullptr;
|
||||
#ifndef HB_NO_VAR
|
||||
gvar = nullptr;
|
||||
#ifndef HB_NO_BEYOND_64K
|
||||
GVAR = nullptr;
|
||||
#endif
|
||||
#endif
|
||||
hmtx = nullptr;
|
||||
#ifndef HB_NO_VERTICAL
|
||||
vmtx = nullptr;
|
||||
#endif
|
||||
const OT::head &head = *face->table.head;
|
||||
if (!glyf::has_valid_glyf_format (face))
|
||||
/* Unknown format. Leave num_glyphs=0, that takes care of disabling us. */
|
||||
return;
|
||||
short_offset = 0 == head.indexToLocFormat;
|
||||
|
||||
loca_table = face->table.loca.get_blob (); // Needs no destruct!
|
||||
glyf_table = hb_sanitize_context_t ().reference_table<glyf> (face);
|
||||
#ifndef HB_NO_VAR
|
||||
gvar = face->table.gvar;
|
||||
#ifndef HB_NO_BEYOND_64K
|
||||
GVAR = face->table.GVAR;
|
||||
#endif
|
||||
#endif
|
||||
hmtx = face->table.hmtx;
|
||||
#ifndef HB_NO_VERTICAL
|
||||
vmtx = face->table.vmtx;
|
||||
#endif
|
||||
|
||||
num_glyphs = hb_max (1u, loca_table.get_length () / (short_offset ? 2 : 4)) - 1;
|
||||
num_glyphs = hb_min (num_glyphs, face->get_num_glyphs ());
|
||||
}
|
||||
~glyf_accelerator_t ()
|
||||
{
|
||||
auto *scratch = cached_scratch.get_relaxed ();
|
||||
if (scratch)
|
||||
{
|
||||
scratch->~hb_glyf_scratch_t ();
|
||||
hb_free (scratch);
|
||||
}
|
||||
|
||||
glyf_table.destroy ();
|
||||
}
|
||||
|
||||
bool has_data () const { return num_glyphs; }
|
||||
|
||||
protected:
|
||||
template<typename T>
|
||||
bool get_points (hb_font_t *font, hb_codepoint_t gid, T consumer,
|
||||
hb_array_t<const int> coords,
|
||||
hb_glyf_scratch_t &scratch,
|
||||
hb_scalar_cache_t *gvar_cache = nullptr) const
|
||||
{
|
||||
if (gid >= num_glyphs) return false;
|
||||
|
||||
auto &all_points = scratch.all_points;
|
||||
all_points.clear ();
|
||||
|
||||
bool phantom_only = !consumer.is_consuming_contour_points ();
|
||||
if (unlikely (!glyph_for_gid (gid).get_points (font, *this, all_points, scratch, nullptr, nullptr, nullptr, true, true, phantom_only, coords, gvar_cache)))
|
||||
return false;
|
||||
|
||||
unsigned count = all_points.length;
|
||||
assert (count >= glyf_impl::PHANTOM_COUNT);
|
||||
count -= glyf_impl::PHANTOM_COUNT;
|
||||
|
||||
if (consumer.is_consuming_contour_points ())
|
||||
{
|
||||
auto *points = all_points.arrayZ;
|
||||
|
||||
if (false)
|
||||
{
|
||||
/* Our path-builder was designed to work with this simple loop.
|
||||
* But FreeType and CoreText do it differently, so we match those
|
||||
* with the other, more complicated, code branch below. */
|
||||
for (unsigned i = 0; i < count; i++)
|
||||
{
|
||||
consumer.consume_point (points[i]);
|
||||
if (points[i].is_end_point)
|
||||
consumer.contour_end ();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (unsigned i = 0; i < count; i++)
|
||||
{
|
||||
// Start of a contour.
|
||||
if (points[i].flag & glyf_impl::SimpleGlyph::FLAG_ON_CURVE)
|
||||
{
|
||||
// First point is on-curve. Draw the contour.
|
||||
for (; i < count; i++)
|
||||
{
|
||||
consumer.consume_point (points[i]);
|
||||
if (points[i].is_end_point)
|
||||
{
|
||||
consumer.contour_end ();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned start = i;
|
||||
|
||||
// Find end of the contour.
|
||||
for (; i < count; i++)
|
||||
if (points[i].is_end_point)
|
||||
break;
|
||||
|
||||
unsigned end = i;
|
||||
|
||||
// Enough to start from the end. Our path-builder takes care of the rest.
|
||||
if (likely (end < count)) // Can only fail in case of alloc failure *maybe*.
|
||||
consumer.consume_point (points[end]);
|
||||
|
||||
for (i = start; i < end; i++)
|
||||
consumer.consume_point (points[i]);
|
||||
|
||||
consumer.contour_end ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
consumer.points_end ();
|
||||
}
|
||||
|
||||
/* Where to write phantoms, nullptr if not requested */
|
||||
contour_point_t *phantoms = consumer.get_phantoms_sink ();
|
||||
if (phantoms)
|
||||
for (unsigned i = 0; i < glyf_impl::PHANTOM_COUNT; ++i)
|
||||
phantoms[i] = all_points.arrayZ[count + i];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
#ifndef HB_NO_VAR
|
||||
struct points_aggregator_t
|
||||
{
|
||||
hb_font_t *font;
|
||||
hb_glyph_extents_t *extents;
|
||||
contour_point_t *phantoms;
|
||||
bool scaled;
|
||||
|
||||
struct contour_bounds_t
|
||||
{
|
||||
contour_bounds_t () { min_x = min_y = FLT_MAX; max_x = max_y = -FLT_MAX; }
|
||||
|
||||
void add (const contour_point_t &p)
|
||||
{
|
||||
min_x = hb_min (min_x, p.x);
|
||||
min_y = hb_min (min_y, p.y);
|
||||
max_x = hb_max (max_x, p.x);
|
||||
max_y = hb_max (max_y, p.y);
|
||||
}
|
||||
|
||||
bool empty () const { return (min_x >= max_x) || (min_y >= max_y); }
|
||||
|
||||
void get_extents (hb_font_t *font, hb_glyph_extents_t *extents, bool scaled)
|
||||
{
|
||||
if (unlikely (empty ()))
|
||||
{
|
||||
extents->width = 0;
|
||||
extents->x_bearing = 0;
|
||||
extents->height = 0;
|
||||
extents->y_bearing = 0;
|
||||
return;
|
||||
}
|
||||
{
|
||||
extents->x_bearing = roundf (min_x);
|
||||
extents->width = roundf (max_x - extents->x_bearing);
|
||||
extents->y_bearing = roundf (max_y);
|
||||
extents->height = roundf (min_y - extents->y_bearing);
|
||||
|
||||
if (scaled)
|
||||
font->scale_glyph_extents (extents);
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
float min_x, min_y, max_x, max_y;
|
||||
} bounds;
|
||||
|
||||
points_aggregator_t (hb_font_t *font_, hb_glyph_extents_t *extents_, contour_point_t *phantoms_, bool scaled_)
|
||||
{
|
||||
font = font_;
|
||||
extents = extents_;
|
||||
phantoms = phantoms_;
|
||||
scaled = scaled_;
|
||||
if (extents) bounds = contour_bounds_t ();
|
||||
}
|
||||
|
||||
HB_ALWAYS_INLINE
|
||||
void consume_point (const contour_point_t &point) { bounds.add (point); }
|
||||
void contour_end () {}
|
||||
void points_end () { bounds.get_extents (font, extents, scaled); }
|
||||
|
||||
bool is_consuming_contour_points () { return extents; }
|
||||
contour_point_t *get_phantoms_sink () { return phantoms; }
|
||||
};
|
||||
|
||||
#ifndef HB_NO_VAR
|
||||
unsigned
|
||||
get_advance_with_var_unscaled (hb_codepoint_t gid,
|
||||
hb_font_t *font,
|
||||
bool is_vertical,
|
||||
hb_glyf_scratch_t &scratch,
|
||||
hb_scalar_cache_t *gvar_cache = nullptr) const
|
||||
{
|
||||
if (unlikely (gid >= num_glyphs)) return 0;
|
||||
|
||||
bool success = false;
|
||||
|
||||
contour_point_t phantoms[glyf_impl::PHANTOM_COUNT];
|
||||
success = get_points (font, gid, points_aggregator_t (font, nullptr, phantoms, false),
|
||||
hb_array (font->coords,
|
||||
font->has_nonzero_coords ? font->num_coords : 0),
|
||||
scratch, gvar_cache);
|
||||
if (unlikely (!success))
|
||||
{
|
||||
unsigned upem = font->face->get_upem ();
|
||||
return is_vertical ? upem : upem / 2;
|
||||
}
|
||||
|
||||
float result = is_vertical
|
||||
? phantoms[glyf_impl::PHANTOM_TOP].y - phantoms[glyf_impl::PHANTOM_BOTTOM].y
|
||||
: phantoms[glyf_impl::PHANTOM_RIGHT].x - phantoms[glyf_impl::PHANTOM_LEFT].x;
|
||||
return hb_clamp (roundf (result), 0.f, (float) UINT_MAX / 2);
|
||||
}
|
||||
|
||||
float
|
||||
get_v_origin_with_var_unscaled (hb_codepoint_t gid,
|
||||
hb_font_t *font,
|
||||
hb_glyf_scratch_t &scratch,
|
||||
hb_scalar_cache_t *gvar_cache = nullptr) const
|
||||
{
|
||||
if (unlikely (gid >= num_glyphs)) return 0;
|
||||
|
||||
bool success = false;
|
||||
|
||||
contour_point_t phantoms[glyf_impl::PHANTOM_COUNT];
|
||||
success = get_points (font, gid, points_aggregator_t (font, nullptr, phantoms, false),
|
||||
hb_array (font->coords,
|
||||
font->has_nonzero_coords ? font->num_coords : 0),
|
||||
scratch, gvar_cache);
|
||||
if (unlikely (!success))
|
||||
{
|
||||
return font->face->get_upem ();
|
||||
}
|
||||
|
||||
return phantoms[glyf_impl::PHANTOM_TOP].y;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
public:
|
||||
|
||||
bool get_extents (hb_font_t *font,
|
||||
hb_codepoint_t gid,
|
||||
hb_glyph_extents_t *extents) const
|
||||
{ return get_extents_at (font, gid, extents, hb_array (font->coords,
|
||||
font->has_nonzero_coords ? font->num_coords : 0)); }
|
||||
|
||||
bool get_extents_at (hb_font_t *font,
|
||||
hb_codepoint_t gid,
|
||||
hb_glyph_extents_t *extents,
|
||||
hb_array_t<const int> coords) const
|
||||
{
|
||||
if (unlikely (gid >= num_glyphs)) return false;
|
||||
|
||||
#ifndef HB_NO_VAR
|
||||
if (coords)
|
||||
{
|
||||
hb_glyf_scratch_t *scratch = acquire_scratch ();
|
||||
if (unlikely (!scratch)) return false;
|
||||
bool ret = get_points (font,
|
||||
gid,
|
||||
points_aggregator_t (font, extents, nullptr, true),
|
||||
coords,
|
||||
*scratch);
|
||||
release_scratch (scratch);
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
return glyph_for_gid (gid).get_extents_without_var_scaled (font, *this, extents);
|
||||
}
|
||||
|
||||
const glyf_impl::Glyph
|
||||
glyph_for_gid (hb_codepoint_t gid, bool needs_padding_removal = false) const
|
||||
{
|
||||
if (unlikely (gid >= num_glyphs)) return glyf_impl::Glyph ();
|
||||
|
||||
unsigned int start_offset, end_offset;
|
||||
|
||||
if (short_offset)
|
||||
{
|
||||
const HBUINT16 *offsets = (const HBUINT16 *) loca_table->dataZ.arrayZ;
|
||||
start_offset = 2 * offsets[gid];
|
||||
end_offset = 2 * offsets[gid + 1];
|
||||
}
|
||||
else
|
||||
{
|
||||
const HBUINT32 *offsets = (const HBUINT32 *) loca_table->dataZ.arrayZ;
|
||||
start_offset = offsets[gid];
|
||||
end_offset = offsets[gid + 1];
|
||||
}
|
||||
|
||||
if (unlikely (start_offset > end_offset || end_offset > glyf_table.get_length ()))
|
||||
return glyf_impl::Glyph ();
|
||||
|
||||
glyf_impl::Glyph glyph (hb_bytes_t ((const char *) this->glyf_table + start_offset,
|
||||
end_offset - start_offset), gid);
|
||||
return needs_padding_removal ? glyf_impl::Glyph (glyph.trim_padding (), gid) : glyph;
|
||||
}
|
||||
|
||||
bool
|
||||
get_path (hb_font_t *font, hb_codepoint_t gid, hb_draw_session_t &draw_session, hb_scalar_cache_t *gvar_cache = nullptr) const
|
||||
{
|
||||
if (!has_data ()) return false;
|
||||
|
||||
hb_glyf_scratch_t *scratch = acquire_scratch ();
|
||||
if (unlikely (!scratch)) return true;
|
||||
|
||||
bool ret = get_points (font, gid, glyf_impl::path_builder_t (font, draw_session),
|
||||
hb_array (font->coords,
|
||||
font->has_nonzero_coords ? font->num_coords : 0),
|
||||
*scratch,
|
||||
gvar_cache);
|
||||
|
||||
release_scratch (scratch);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool
|
||||
get_path_at (hb_font_t *font, hb_codepoint_t gid, hb_draw_session_t &draw_session,
|
||||
hb_array_t<const int> coords,
|
||||
hb_glyf_scratch_t &scratch,
|
||||
hb_scalar_cache_t *gvar_cache = nullptr) const
|
||||
{
|
||||
if (!has_data ()) return false;
|
||||
return get_points (font, gid, glyf_impl::path_builder_t (font, draw_session),
|
||||
coords,
|
||||
scratch,
|
||||
gvar_cache);
|
||||
}
|
||||
|
||||
|
||||
hb_glyf_scratch_t *acquire_scratch () const
|
||||
{
|
||||
if (!has_data ()) return nullptr;
|
||||
hb_glyf_scratch_t *scratch = cached_scratch.get_acquire ();
|
||||
if (!scratch || unlikely (!cached_scratch.cmpexch (scratch, nullptr)))
|
||||
{
|
||||
scratch = (hb_glyf_scratch_t *) hb_calloc (1, sizeof (hb_glyf_scratch_t));
|
||||
if (unlikely (!scratch))
|
||||
return nullptr;
|
||||
}
|
||||
return scratch;
|
||||
}
|
||||
void release_scratch (hb_glyf_scratch_t *scratch) const
|
||||
{
|
||||
if (!scratch)
|
||||
return;
|
||||
if (!cached_scratch.cmpexch (nullptr, scratch))
|
||||
{
|
||||
scratch->~hb_glyf_scratch_t ();
|
||||
hb_free (scratch);
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef HB_NO_VAR
|
||||
const gvar_accelerator_t *gvar;
|
||||
#ifndef HB_NO_BEYOND_64K
|
||||
const GVAR_accelerator_t *GVAR;
|
||||
#endif
|
||||
#endif
|
||||
const hmtx_accelerator_t *hmtx;
|
||||
#ifndef HB_NO_VERTICAL
|
||||
const vmtx_accelerator_t *vmtx;
|
||||
#endif
|
||||
|
||||
private:
|
||||
bool short_offset;
|
||||
unsigned int num_glyphs;
|
||||
hb_blob_ptr_t<loca> loca_table;
|
||||
hb_blob_ptr_t<glyf> glyf_table;
|
||||
mutable hb_atomic_t<hb_glyf_scratch_t *> cached_scratch;
|
||||
};
|
||||
|
||||
|
||||
inline bool
|
||||
glyf::_populate_subset_glyphs (const hb_subset_plan_t *plan,
|
||||
hb_font_t *font,
|
||||
hb_vector_t<glyf_impl::SubsetGlyph>& glyphs /* OUT */) const
|
||||
{
|
||||
OT::glyf_accelerator_t glyf (plan->source);
|
||||
if (!glyphs.alloc_exact (plan->new_to_old_gid_list.length)) return false;
|
||||
|
||||
for (const auto &pair : plan->new_to_old_gid_list)
|
||||
{
|
||||
hb_codepoint_t new_gid = pair.first;
|
||||
hb_codepoint_t old_gid = pair.second;
|
||||
glyf_impl::SubsetGlyph *p = glyphs.push ();
|
||||
glyf_impl::SubsetGlyph& subset_glyph = *p;
|
||||
subset_glyph.old_gid = old_gid;
|
||||
|
||||
if (unlikely (old_gid == 0 && new_gid == 0 &&
|
||||
!(plan->flags & HB_SUBSET_FLAGS_NOTDEF_OUTLINE)) &&
|
||||
!plan->normalized_coords)
|
||||
subset_glyph.source_glyph = glyf_impl::Glyph ();
|
||||
else
|
||||
{
|
||||
/* If plan has an accelerator, the preprocessing step already trimmed glyphs.
|
||||
* Don't trim them again! */
|
||||
subset_glyph.source_glyph = glyf.glyph_for_gid (subset_glyph.old_gid, !plan->accelerator);
|
||||
}
|
||||
|
||||
if (plan->flags & HB_SUBSET_FLAGS_NO_HINTING)
|
||||
subset_glyph.drop_hints_bytes ();
|
||||
else
|
||||
subset_glyph.dest_start = subset_glyph.source_glyph.get_bytes ();
|
||||
|
||||
if (font)
|
||||
{
|
||||
if (unlikely (!subset_glyph.compile_bytes_with_deltas (plan, font, glyf)))
|
||||
{
|
||||
// when pinned at default, only bounds are updated, thus no need to free
|
||||
if (!plan->pinned_at_default)
|
||||
_free_compiled_subset_glyphs (glyphs);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
inline hb_font_t *
|
||||
glyf::_create_font_for_instancing (const hb_subset_plan_t *plan) const
|
||||
{
|
||||
hb_font_t *font = hb_font_create (plan->source);
|
||||
if (unlikely (font == hb_font_get_empty ())) return nullptr;
|
||||
|
||||
hb_vector_t<hb_variation_t> vars;
|
||||
if (unlikely (!vars.alloc (plan->user_axes_location.get_population (), true)))
|
||||
{
|
||||
hb_font_destroy (font);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
for (auto _ : plan->user_axes_location)
|
||||
{
|
||||
hb_variation_t var;
|
||||
var.tag = _.first;
|
||||
var.value = _.second.middle;
|
||||
vars.push (var);
|
||||
}
|
||||
|
||||
#ifndef HB_NO_VAR
|
||||
hb_font_set_variations (font, vars.arrayZ, plan->user_axes_location.get_population ());
|
||||
#endif
|
||||
return font;
|
||||
}
|
||||
|
||||
|
||||
} /* namespace OT */
|
||||
|
||||
|
||||
#endif /* OT_GLYF_GLYF_HH */
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
#ifndef OT_GLYF_LOCA_HH
|
||||
#define OT_GLYF_LOCA_HH
|
||||
|
||||
|
||||
#include "../../hb-open-type.hh"
|
||||
|
||||
|
||||
namespace OT {
|
||||
|
||||
|
||||
/*
|
||||
* loca -- Index to Location
|
||||
* https://docs.microsoft.com/en-us/typography/opentype/spec/loca
|
||||
*/
|
||||
#define HB_OT_TAG_loca HB_TAG('l','o','c','a')
|
||||
|
||||
struct loca
|
||||
{
|
||||
friend struct glyf;
|
||||
friend struct glyf_accelerator_t;
|
||||
|
||||
static constexpr hb_tag_t tableTag = HB_OT_TAG_loca;
|
||||
|
||||
bool sanitize (hb_sanitize_context_t *c HB_UNUSED) const
|
||||
{
|
||||
TRACE_SANITIZE (this);
|
||||
return_trace (true);
|
||||
}
|
||||
|
||||
protected:
|
||||
UnsizedArrayOf<HBUINT8>
|
||||
dataZ; /* Location data. */
|
||||
public:
|
||||
DEFINE_SIZE_MIN (0); /* In reality, this is UNBOUNDED() type; but since we always
|
||||
* check the size externally, allow Null() object of it by
|
||||
* defining it _MIN instead. */
|
||||
};
|
||||
|
||||
|
||||
} /* namespace OT */
|
||||
|
||||
|
||||
#endif /* OT_GLYF_LOCA_HH */
|
||||
+192
@@ -0,0 +1,192 @@
|
||||
#ifndef OT_GLYF_PATH_BUILDER_HH
|
||||
#define OT_GLYF_PATH_BUILDER_HH
|
||||
|
||||
|
||||
#include "../../hb.hh"
|
||||
|
||||
|
||||
namespace OT {
|
||||
namespace glyf_impl {
|
||||
|
||||
|
||||
struct path_builder_t
|
||||
{
|
||||
hb_font_t *font;
|
||||
hb_draw_session_t *draw_session;
|
||||
|
||||
struct optional_point_t
|
||||
{
|
||||
optional_point_t () {}
|
||||
optional_point_t (float x_, float y_) : has_data (true), x (x_), y (y_) {}
|
||||
operator bool () const { return has_data; }
|
||||
|
||||
bool has_data = false;
|
||||
float x;
|
||||
float y;
|
||||
|
||||
optional_point_t mid (optional_point_t p)
|
||||
{ return optional_point_t ((x + p.x) * 0.5f, (y + p.y) * 0.5f); }
|
||||
} first_oncurve, first_offcurve, first_offcurve2, last_offcurve, last_offcurve2;
|
||||
|
||||
path_builder_t (hb_font_t *font_, hb_draw_session_t &draw_session_) :
|
||||
font (font_), draw_session (&draw_session_) {}
|
||||
|
||||
/* based on https://github.com/RazrFalcon/ttf-parser/blob/4f32821/src/glyf.rs#L287
|
||||
See also:
|
||||
* https://developer.apple.com/fonts/TrueType-Reference-Manual/RM01/Chap1.html
|
||||
* https://stackoverflow.com/a/20772557
|
||||
*
|
||||
* Cubic support added. */
|
||||
HB_ALWAYS_INLINE
|
||||
void consume_point (const contour_point_t &point)
|
||||
{
|
||||
bool is_on_curve = point.flag & glyf_impl::SimpleGlyph::FLAG_ON_CURVE;
|
||||
#ifdef HB_NO_CUBIC_GLYF
|
||||
constexpr bool is_cubic = false;
|
||||
#else
|
||||
bool is_cubic = !is_on_curve && (point.flag & glyf_impl::SimpleGlyph::FLAG_CUBIC);
|
||||
#endif
|
||||
optional_point_t p (font->em_fscalef_x (point.x), font->em_fscalef_y (point.y));
|
||||
if (unlikely (!first_oncurve))
|
||||
{
|
||||
if (is_on_curve)
|
||||
{
|
||||
first_oncurve = p;
|
||||
draw_session->move_to (p.x, p.y);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (is_cubic && !first_offcurve2)
|
||||
{
|
||||
first_offcurve2 = first_offcurve;
|
||||
first_offcurve = p;
|
||||
}
|
||||
else if (first_offcurve)
|
||||
{
|
||||
optional_point_t mid = first_offcurve.mid (p);
|
||||
first_oncurve = mid;
|
||||
last_offcurve = p;
|
||||
draw_session->move_to (mid.x, mid.y);
|
||||
}
|
||||
else
|
||||
first_offcurve = p;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (last_offcurve)
|
||||
{
|
||||
if (is_on_curve)
|
||||
{
|
||||
if (last_offcurve2)
|
||||
{
|
||||
draw_session->cubic_to (last_offcurve2.x, last_offcurve2.y,
|
||||
last_offcurve.x, last_offcurve.y,
|
||||
p.x, p.y);
|
||||
last_offcurve2 = optional_point_t ();
|
||||
}
|
||||
else
|
||||
draw_session->quadratic_to (last_offcurve.x, last_offcurve.y,
|
||||
p.x, p.y);
|
||||
last_offcurve = optional_point_t ();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (is_cubic && !last_offcurve2)
|
||||
{
|
||||
last_offcurve2 = last_offcurve;
|
||||
last_offcurve = p;
|
||||
}
|
||||
else
|
||||
{
|
||||
optional_point_t mid = last_offcurve.mid (p);
|
||||
|
||||
if (is_cubic)
|
||||
{
|
||||
draw_session->cubic_to (last_offcurve2.x, last_offcurve2.y,
|
||||
last_offcurve.x, last_offcurve.y,
|
||||
mid.x, mid.y);
|
||||
last_offcurve2 = optional_point_t ();
|
||||
}
|
||||
else
|
||||
draw_session->quadratic_to (last_offcurve.x, last_offcurve.y,
|
||||
mid.x, mid.y);
|
||||
last_offcurve = p;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (is_on_curve)
|
||||
draw_session->line_to (p.x, p.y);
|
||||
else
|
||||
last_offcurve = p;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void contour_end ()
|
||||
{
|
||||
if (first_offcurve && last_offcurve)
|
||||
{
|
||||
optional_point_t mid = last_offcurve.mid (first_offcurve2 ?
|
||||
first_offcurve2 :
|
||||
first_offcurve);
|
||||
if (last_offcurve2)
|
||||
draw_session->cubic_to (last_offcurve2.x, last_offcurve2.y,
|
||||
last_offcurve.x, last_offcurve.y,
|
||||
mid.x, mid.y);
|
||||
else
|
||||
draw_session->quadratic_to (last_offcurve.x, last_offcurve.y,
|
||||
mid.x, mid.y);
|
||||
last_offcurve = optional_point_t ();
|
||||
}
|
||||
/* now check the rest */
|
||||
|
||||
if (first_offcurve && first_oncurve)
|
||||
{
|
||||
if (first_offcurve2)
|
||||
draw_session->cubic_to (first_offcurve2.x, first_offcurve2.y,
|
||||
first_offcurve.x, first_offcurve.y,
|
||||
first_oncurve.x, first_oncurve.y);
|
||||
else
|
||||
draw_session->quadratic_to (first_offcurve.x, first_offcurve.y,
|
||||
first_oncurve.x, first_oncurve.y);
|
||||
}
|
||||
else if (last_offcurve && first_oncurve)
|
||||
{
|
||||
if (last_offcurve2)
|
||||
draw_session->cubic_to (last_offcurve2.x, last_offcurve2.y,
|
||||
last_offcurve.x, last_offcurve.y,
|
||||
first_oncurve.x, first_oncurve.y);
|
||||
else
|
||||
draw_session->quadratic_to (last_offcurve.x, last_offcurve.y,
|
||||
first_oncurve.x, first_oncurve.y);
|
||||
}
|
||||
else if (first_oncurve)
|
||||
draw_session->line_to (first_oncurve.x, first_oncurve.y);
|
||||
else if (first_offcurve)
|
||||
{
|
||||
float x = first_offcurve.x, y = first_offcurve.y;
|
||||
draw_session->move_to (x, y);
|
||||
draw_session->quadratic_to (x, y, x, y);
|
||||
}
|
||||
|
||||
/* Getting ready for the next contour */
|
||||
first_oncurve = first_offcurve = last_offcurve = last_offcurve2 = optional_point_t ();
|
||||
draw_session->close_path ();
|
||||
}
|
||||
|
||||
void points_end () {}
|
||||
|
||||
bool is_consuming_contour_points () { return true; }
|
||||
contour_point_t *get_phantoms_sink () { return nullptr; }
|
||||
};
|
||||
|
||||
|
||||
} /* namespace glyf_impl */
|
||||
} /* namespace OT */
|
||||
|
||||
|
||||
#endif /* OT_GLYF_PATH_BUILDER_HH */
|
||||
+589
@@ -0,0 +1,589 @@
|
||||
/*
|
||||
* Copyright © 2011,2012 Google, Inc.
|
||||
*
|
||||
* This is part of HarfBuzz, a text shaping library.
|
||||
*
|
||||
* Permission is hereby granted, without written agreement and without
|
||||
* license or royalty fees, to use, copy, modify, and distribute this
|
||||
* software and its documentation for any purpose, provided that the
|
||||
* above copyright notice and the following two paragraphs appear in
|
||||
* all copies of this software.
|
||||
*
|
||||
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
|
||||
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
|
||||
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
|
||||
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||
* DAMAGE.
|
||||
*
|
||||
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
|
||||
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
|
||||
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
|
||||
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
|
||||
*
|
||||
* Google Author(s): Behdad Esfahbod
|
||||
*/
|
||||
|
||||
#ifndef OT_NAME_NAME_HH
|
||||
#define OT_NAME_NAME_HH
|
||||
|
||||
#include "../../hb-open-type.hh"
|
||||
#include "../../hb-ot-name-language.hh"
|
||||
#include "../../hb-aat-layout.hh"
|
||||
#include "../../hb-utf.hh"
|
||||
|
||||
|
||||
namespace OT {
|
||||
|
||||
template <typename in_utf_t, typename out_utf_t>
|
||||
inline unsigned int
|
||||
hb_ot_name_convert_utf (hb_bytes_t bytes,
|
||||
unsigned int *text_size /* IN/OUT */,
|
||||
typename out_utf_t::codepoint_t *text /* OUT */)
|
||||
{
|
||||
unsigned int src_len = bytes.length / sizeof (typename in_utf_t::codepoint_t);
|
||||
const typename in_utf_t::codepoint_t *src = (const typename in_utf_t::codepoint_t *) bytes.arrayZ;
|
||||
const typename in_utf_t::codepoint_t *src_end = src + src_len;
|
||||
|
||||
typename out_utf_t::codepoint_t *dst = text;
|
||||
|
||||
hb_codepoint_t unicode;
|
||||
const hb_codepoint_t replacement = HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT;
|
||||
|
||||
if (text_size && *text_size)
|
||||
{
|
||||
(*text_size)--; /* Save room for NUL-termination. */
|
||||
const typename out_utf_t::codepoint_t *dst_end = text + *text_size;
|
||||
|
||||
while (src < src_end && dst < dst_end)
|
||||
{
|
||||
const typename in_utf_t::codepoint_t *src_next = in_utf_t::next (src, src_end, &unicode, replacement);
|
||||
typename out_utf_t::codepoint_t *dst_next = out_utf_t::encode (dst, dst_end, unicode);
|
||||
if (dst_next == dst)
|
||||
break; /* Out-of-room. */
|
||||
|
||||
dst = dst_next;
|
||||
src = src_next;
|
||||
}
|
||||
|
||||
*text_size = dst - text;
|
||||
*dst = 0; /* NUL-terminate. */
|
||||
}
|
||||
|
||||
/* Accumulate length of rest. */
|
||||
unsigned int dst_len = dst - text;
|
||||
while (src < src_end)
|
||||
{
|
||||
src = in_utf_t::next (src, src_end, &unicode, replacement);
|
||||
dst_len += out_utf_t::encode_len (unicode);
|
||||
}
|
||||
return dst_len;
|
||||
}
|
||||
|
||||
#define entry_score var.u16[0]
|
||||
#define entry_index var.u16[1]
|
||||
|
||||
|
||||
/*
|
||||
* name -- Naming
|
||||
* https://docs.microsoft.com/en-us/typography/opentype/spec/name
|
||||
*/
|
||||
#define HB_OT_TAG_name HB_TAG('n','a','m','e')
|
||||
|
||||
#define UNSUPPORTED 42
|
||||
|
||||
struct NameRecord
|
||||
{
|
||||
hb_language_t language (hb_face_t *face) const
|
||||
{
|
||||
#ifndef HB_NO_OT_NAME_LANGUAGE
|
||||
unsigned int p = platformID;
|
||||
unsigned int l = languageID;
|
||||
|
||||
if (p == 3)
|
||||
return _hb_ot_name_language_for_ms_code (l);
|
||||
|
||||
if (p == 1)
|
||||
return _hb_ot_name_language_for_mac_code (l);
|
||||
|
||||
#ifndef HB_NO_OT_NAME_LANGUAGE_AAT
|
||||
if (p == 0)
|
||||
return face->table.ltag->get_language (l);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
return HB_LANGUAGE_INVALID;
|
||||
}
|
||||
|
||||
uint16_t score () const
|
||||
{
|
||||
/* Same order as in cmap::find_best_subtable(). */
|
||||
unsigned int p = platformID;
|
||||
unsigned int e = encodingID;
|
||||
|
||||
/* 32-bit. */
|
||||
if (p == 3 && e == 10) return 0;
|
||||
if (p == 0 && e == 6) return 1;
|
||||
if (p == 0 && e == 4) return 2;
|
||||
|
||||
/* 16-bit. */
|
||||
if (p == 3 && e == 1) return 3;
|
||||
if (p == 0 && e == 3) return 4;
|
||||
if (p == 0 && e == 2) return 5;
|
||||
if (p == 0 && e == 1) return 6;
|
||||
if (p == 0 && e == 0) return 7;
|
||||
|
||||
/* Symbol. */
|
||||
if (p == 3 && e == 0) return 8;
|
||||
|
||||
/* We treat all Mac Latin names as ASCII only. */
|
||||
if (p == 1 && e == 0) return 10; /* 10 is magic number :| */
|
||||
|
||||
return UNSUPPORTED;
|
||||
}
|
||||
|
||||
NameRecord* copy (hb_serialize_context_t *c, const void *base
|
||||
#ifdef HB_EXPERIMENTAL_API
|
||||
, const hb_hashmap_t<hb_ot_name_record_ids_t, hb_bytes_t> *name_table_overrides
|
||||
#endif
|
||||
) const
|
||||
{
|
||||
TRACE_SERIALIZE (this);
|
||||
HB_UNUSED auto snap = c->snapshot ();
|
||||
auto *out = c->embed (this);
|
||||
if (unlikely (!out)) return_trace (nullptr);
|
||||
#ifdef HB_EXPERIMENTAL_API
|
||||
hb_ot_name_record_ids_t record_ids (platformID, encodingID, languageID, nameID);
|
||||
hb_bytes_t* name_bytes;
|
||||
|
||||
if (name_table_overrides->has (record_ids, &name_bytes)) {
|
||||
hb_bytes_t encoded_bytes = *name_bytes;
|
||||
char *name_str_utf16_be = nullptr;
|
||||
|
||||
if (platformID != 1)
|
||||
{
|
||||
unsigned text_size = hb_ot_name_convert_utf<hb_utf8_t, hb_utf16_be_t> (*name_bytes, nullptr, nullptr);
|
||||
|
||||
text_size++; // needs to consider NULL terminator for use in hb_ot_name_convert_utf()
|
||||
unsigned byte_len = text_size * hb_utf16_be_t::codepoint_t::static_size;
|
||||
name_str_utf16_be = (char *) hb_calloc (byte_len, 1);
|
||||
if (!name_str_utf16_be)
|
||||
{
|
||||
c->revert (snap);
|
||||
return_trace (nullptr);
|
||||
}
|
||||
hb_ot_name_convert_utf<hb_utf8_t, hb_utf16_be_t> (*name_bytes, &text_size,
|
||||
(hb_utf16_be_t::codepoint_t *) name_str_utf16_be);
|
||||
|
||||
unsigned encoded_byte_len = text_size * hb_utf16_be_t::codepoint_t::static_size;
|
||||
if (!encoded_byte_len || !c->check_assign (out->length, encoded_byte_len, HB_SERIALIZE_ERROR_INT_OVERFLOW)) {
|
||||
c->revert (snap);
|
||||
hb_free (name_str_utf16_be);
|
||||
return_trace (nullptr);
|
||||
}
|
||||
|
||||
encoded_bytes = hb_bytes_t (name_str_utf16_be, encoded_byte_len);
|
||||
}
|
||||
else
|
||||
{
|
||||
// mac platform, copy the UTF-8 string(all ascii characters) as is
|
||||
if (!c->check_assign (out->length, encoded_bytes.length, HB_SERIALIZE_ERROR_INT_OVERFLOW)) {
|
||||
c->revert (snap);
|
||||
return_trace (nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
out->offset = 0;
|
||||
c->push ();
|
||||
encoded_bytes.copy (c);
|
||||
c->add_link (out->offset, c->pop_pack (), hb_serialize_context_t::Tail, 0);
|
||||
hb_free (name_str_utf16_be);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
out->offset.serialize_copy (c, offset, base, 0, hb_serialize_context_t::Tail, length);
|
||||
}
|
||||
return_trace (out);
|
||||
}
|
||||
|
||||
bool isUnicode () const
|
||||
{
|
||||
unsigned int p = platformID;
|
||||
unsigned int e = encodingID;
|
||||
|
||||
return (p == 0 ||
|
||||
(p == 3 && (e == 0 || e == 1 || e == 10)));
|
||||
}
|
||||
|
||||
static int cmp (const void *pa, const void *pb)
|
||||
{
|
||||
const NameRecord *a = (const NameRecord *)pa;
|
||||
const NameRecord *b = (const NameRecord *)pb;
|
||||
|
||||
if (a->platformID != b->platformID)
|
||||
return a->platformID - b->platformID;
|
||||
|
||||
if (a->encodingID != b->encodingID)
|
||||
return a->encodingID - b->encodingID;
|
||||
|
||||
if (a->languageID != b->languageID)
|
||||
return a->languageID - b->languageID;
|
||||
|
||||
if (a->nameID != b->nameID)
|
||||
return a->nameID - b->nameID;
|
||||
|
||||
if (a->length != b->length)
|
||||
return a->length - b->length;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool sanitize (hb_sanitize_context_t *c, const void *base) const
|
||||
{
|
||||
TRACE_SANITIZE (this);
|
||||
return_trace (c->check_struct (this) &&
|
||||
hb_barrier () &&
|
||||
offset.sanitize (c, base, length));
|
||||
}
|
||||
|
||||
HBUINT16 platformID; /* Platform ID. */
|
||||
HBUINT16 encodingID; /* Platform-specific encoding ID. */
|
||||
HBUINT16 languageID; /* Language ID. */
|
||||
HBUINT16 nameID; /* Name ID. */
|
||||
HBUINT16 length; /* String length (in bytes). */
|
||||
NNOffset16To<UnsizedArrayOf<HBUINT8>>
|
||||
offset; /* String offset from start of storage area (in bytes). */
|
||||
public:
|
||||
DEFINE_SIZE_STATIC (12);
|
||||
};
|
||||
|
||||
static int
|
||||
_hb_ot_name_entry_cmp_key (const void *pa, const void *pb, bool exact)
|
||||
{
|
||||
const hb_ot_name_entry_t *a = (const hb_ot_name_entry_t *) pa;
|
||||
const hb_ot_name_entry_t *b = (const hb_ot_name_entry_t *) pb;
|
||||
|
||||
/* Compare by name_id, then language. */
|
||||
|
||||
if (a->name_id != b->name_id)
|
||||
return a->name_id - b->name_id;
|
||||
|
||||
if (a->language == b->language) return 0;
|
||||
if (!a->language) return -1;
|
||||
if (!b->language) return +1;
|
||||
|
||||
const char *astr = hb_language_to_string (a->language);
|
||||
const char *bstr = hb_language_to_string (b->language);
|
||||
|
||||
signed c = strcmp (astr, bstr);
|
||||
|
||||
// 'a' is the user request, and 'b' is string in the font.
|
||||
// If eg. user asks for "en-us" and font has "en", approve.
|
||||
if (!exact && c &&
|
||||
hb_language_matches (b->language, a->language))
|
||||
return 0;
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
static int
|
||||
_hb_ot_name_entry_cmp (const void *pa, const void *pb)
|
||||
{
|
||||
/* Compare by name_id, then language, then score, then index. */
|
||||
|
||||
int v = _hb_ot_name_entry_cmp_key (pa, pb, true);
|
||||
if (v)
|
||||
return v;
|
||||
|
||||
const hb_ot_name_entry_t *a = (const hb_ot_name_entry_t *) pa;
|
||||
const hb_ot_name_entry_t *b = (const hb_ot_name_entry_t *) pb;
|
||||
|
||||
if (a->entry_score != b->entry_score)
|
||||
return a->entry_score - b->entry_score;
|
||||
|
||||
if (a->entry_index != b->entry_index)
|
||||
return a->entry_index - b->entry_index;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct name
|
||||
{
|
||||
static constexpr hb_tag_t tableTag = HB_OT_TAG_name;
|
||||
|
||||
size_t get_size () const
|
||||
{ return min_size + count * nameRecordZ.item_size; }
|
||||
|
||||
template <typename Iterator,
|
||||
hb_requires (hb_is_source_of (Iterator, const NameRecord &))>
|
||||
bool serialize (hb_serialize_context_t *c,
|
||||
Iterator it,
|
||||
const void *src_string_pool
|
||||
#ifdef HB_EXPERIMENTAL_API
|
||||
, const hb_vector_t<hb_ot_name_record_ids_t>& insert_name_records
|
||||
, const hb_hashmap_t<hb_ot_name_record_ids_t, hb_bytes_t> *name_table_overrides
|
||||
#endif
|
||||
)
|
||||
{
|
||||
TRACE_SERIALIZE (this);
|
||||
|
||||
if (unlikely (!c->extend_min ((*this)))) return_trace (false);
|
||||
|
||||
unsigned total_count = it.len ()
|
||||
#ifdef HB_EXPERIMENTAL_API
|
||||
+ insert_name_records.length
|
||||
#endif
|
||||
;
|
||||
this->format = 0;
|
||||
if (!c->check_assign (this->count, total_count, HB_SERIALIZE_ERROR_INT_OVERFLOW))
|
||||
return false;
|
||||
|
||||
NameRecord *name_records = (NameRecord *) hb_calloc (total_count, NameRecord::static_size);
|
||||
if (unlikely (!name_records)) return_trace (false);
|
||||
|
||||
hb_array_t<NameRecord> records (name_records, total_count);
|
||||
|
||||
for (const NameRecord& record : it)
|
||||
{
|
||||
hb_memcpy (name_records, &record, NameRecord::static_size);
|
||||
name_records++;
|
||||
}
|
||||
|
||||
#ifdef HB_EXPERIMENTAL_API
|
||||
for (unsigned i = 0; i < insert_name_records.length; i++)
|
||||
{
|
||||
const hb_ot_name_record_ids_t& ids = insert_name_records[i];
|
||||
NameRecord record;
|
||||
record.platformID = ids.platform_id;
|
||||
record.encodingID = ids.encoding_id;
|
||||
record.languageID = ids.language_id;
|
||||
record.nameID = ids.name_id;
|
||||
record.length = 0; // handled in NameRecord copy()
|
||||
record.offset = 0;
|
||||
hb_memcpy (name_records, &record, NameRecord::static_size);
|
||||
name_records++;
|
||||
}
|
||||
#endif
|
||||
|
||||
records.qsort ();
|
||||
|
||||
c->copy_all (records,
|
||||
src_string_pool
|
||||
#ifdef HB_EXPERIMENTAL_API
|
||||
, name_table_overrides
|
||||
#endif
|
||||
);
|
||||
hb_free (records.arrayZ);
|
||||
|
||||
|
||||
if (unlikely (c->ran_out_of_room ())) return_trace (false);
|
||||
|
||||
this->stringOffset = c->length ();
|
||||
|
||||
return_trace (true);
|
||||
}
|
||||
|
||||
bool subset (hb_subset_context_t *c) const
|
||||
{
|
||||
auto *name_prime = c->serializer->start_embed<name> ();
|
||||
|
||||
#ifdef HB_EXPERIMENTAL_API
|
||||
const hb_hashmap_t<hb_ot_name_record_ids_t, hb_bytes_t> *name_table_overrides =
|
||||
&c->plan->name_table_overrides;
|
||||
#endif
|
||||
|
||||
auto it =
|
||||
+ nameRecordZ.as_array (count)
|
||||
| hb_filter (c->plan->name_ids, &NameRecord::nameID)
|
||||
| hb_filter (c->plan->name_languages, &NameRecord::languageID)
|
||||
| hb_filter ([&] (const NameRecord& namerecord) {
|
||||
return
|
||||
(c->plan->flags & HB_SUBSET_FLAGS_NAME_LEGACY)
|
||||
|| namerecord.isUnicode ();
|
||||
})
|
||||
#ifdef HB_EXPERIMENTAL_API
|
||||
| hb_filter ([&] (const NameRecord& namerecord) {
|
||||
if (name_table_overrides->is_empty ())
|
||||
return true;
|
||||
hb_ot_name_record_ids_t rec_ids (namerecord.platformID,
|
||||
namerecord.encodingID,
|
||||
namerecord.languageID,
|
||||
namerecord.nameID);
|
||||
|
||||
hb_bytes_t *p;
|
||||
if (name_table_overrides->has (rec_ids, &p) &&
|
||||
(*p).length == 0)
|
||||
return false;
|
||||
return true;
|
||||
})
|
||||
#endif
|
||||
;
|
||||
|
||||
#ifdef HB_EXPERIMENTAL_API
|
||||
hb_hashmap_t<hb_ot_name_record_ids_t, unsigned> retained_name_record_ids;
|
||||
for (const NameRecord& rec : it)
|
||||
{
|
||||
hb_ot_name_record_ids_t rec_ids (rec.platformID,
|
||||
rec.encodingID,
|
||||
rec.languageID,
|
||||
rec.nameID);
|
||||
retained_name_record_ids.set (rec_ids, 1);
|
||||
}
|
||||
|
||||
hb_vector_t<hb_ot_name_record_ids_t> insert_name_records;
|
||||
if (!name_table_overrides->is_empty ())
|
||||
{
|
||||
if (unlikely (!insert_name_records.alloc (name_table_overrides->get_population (), true)))
|
||||
return false;
|
||||
for (const auto& record_ids : name_table_overrides->keys ())
|
||||
{
|
||||
if (name_table_overrides->get (record_ids).length == 0)
|
||||
continue;
|
||||
if (retained_name_record_ids.has (record_ids))
|
||||
continue;
|
||||
insert_name_records.push (record_ids);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return name_prime->serialize (c->serializer, it,
|
||||
std::addressof (this + stringOffset)
|
||||
#ifdef HB_EXPERIMENTAL_API
|
||||
, insert_name_records
|
||||
, name_table_overrides
|
||||
#endif
|
||||
);
|
||||
}
|
||||
|
||||
bool sanitize_records (hb_sanitize_context_t *c) const
|
||||
{
|
||||
TRACE_SANITIZE (this);
|
||||
const void *string_pool = (this+stringOffset).arrayZ;
|
||||
return_trace (nameRecordZ.sanitize (c, count, string_pool));
|
||||
}
|
||||
|
||||
bool sanitize (hb_sanitize_context_t *c) const
|
||||
{
|
||||
TRACE_SANITIZE (this);
|
||||
return_trace (c->check_struct (this) &&
|
||||
hb_barrier () &&
|
||||
likely (format == 0 || format == 1) &&
|
||||
c->check_array (nameRecordZ.arrayZ, count) &&
|
||||
c->check_range (this, stringOffset) &&
|
||||
sanitize_records (c));
|
||||
}
|
||||
|
||||
struct accelerator_t
|
||||
{
|
||||
accelerator_t (hb_face_t *face)
|
||||
{
|
||||
this->table = hb_sanitize_context_t ().reference_table<name> (face);
|
||||
assert (this->table.get_length () >= this->table->stringOffset);
|
||||
this->pool = (const char *) (const void *) (this->table+this->table->stringOffset);
|
||||
this->pool_len = this->table.get_length () - this->table->stringOffset;
|
||||
const hb_array_t<const NameRecord> all_names (this->table->nameRecordZ.arrayZ,
|
||||
this->table->count);
|
||||
|
||||
this->names.alloc_exact (all_names.length);
|
||||
|
||||
for (unsigned int i = 0; i < all_names.length; i++)
|
||||
{
|
||||
hb_ot_name_entry_t *entry = this->names.push ();
|
||||
|
||||
entry->name_id = all_names[i].nameID;
|
||||
entry->language = all_names[i].language (face);
|
||||
entry->entry_score = all_names[i].score ();
|
||||
entry->entry_index = i;
|
||||
}
|
||||
|
||||
this->names.qsort (_hb_ot_name_entry_cmp);
|
||||
/* Walk and pick best only for each name_id,language pair,
|
||||
* while dropping unsupported encodings. */
|
||||
unsigned int j = 0;
|
||||
for (unsigned int i = 0; i < this->names.length; i++)
|
||||
{
|
||||
if (this->names[i].entry_score == UNSUPPORTED ||
|
||||
this->names[i].language == HB_LANGUAGE_INVALID)
|
||||
continue;
|
||||
if (i &&
|
||||
this->names[i - 1].name_id == this->names[i].name_id &&
|
||||
this->names[i - 1].language == this->names[i].language)
|
||||
continue;
|
||||
this->names[j++] = this->names[i];
|
||||
}
|
||||
this->names.resize (j);
|
||||
}
|
||||
~accelerator_t ()
|
||||
{
|
||||
this->table.destroy ();
|
||||
}
|
||||
|
||||
int get_index (hb_ot_name_id_t name_id,
|
||||
hb_language_t language,
|
||||
unsigned int *width=nullptr) const
|
||||
{
|
||||
const hb_ot_name_entry_t key = {name_id, {0}, language};
|
||||
const hb_ot_name_entry_t *entry = hb_bsearch (key, (const hb_ot_name_entry_t *) this->names,
|
||||
this->names.length,
|
||||
sizeof (hb_ot_name_entry_t),
|
||||
_hb_ot_name_entry_cmp_key,
|
||||
true);
|
||||
|
||||
if (!entry)
|
||||
{
|
||||
entry = hb_bsearch (key, (const hb_ot_name_entry_t *) this->names,
|
||||
this->names.length,
|
||||
sizeof (hb_ot_name_entry_t),
|
||||
_hb_ot_name_entry_cmp_key,
|
||||
false);
|
||||
}
|
||||
|
||||
if (!entry)
|
||||
return -1;
|
||||
|
||||
if (width)
|
||||
*width = entry->entry_score < 10 ? 2 : 1;
|
||||
|
||||
return entry->entry_index;
|
||||
}
|
||||
|
||||
hb_bytes_t get_name (unsigned int idx) const
|
||||
{
|
||||
const hb_array_t<const NameRecord> all_names (table->nameRecordZ.arrayZ, table->count);
|
||||
const NameRecord &record = all_names[idx];
|
||||
const hb_bytes_t string_pool (pool, pool_len);
|
||||
return string_pool.sub_array (record.offset, record.length);
|
||||
}
|
||||
|
||||
private:
|
||||
const char *pool;
|
||||
unsigned int pool_len;
|
||||
public:
|
||||
hb_blob_ptr_t<name> table;
|
||||
hb_vector_t<hb_ot_name_entry_t> names;
|
||||
};
|
||||
|
||||
public:
|
||||
/* We only implement format 0 for now. */
|
||||
HBUINT16 format; /* Format selector (=0/1). */
|
||||
HBUINT16 count; /* Number of name records. */
|
||||
NNOffset16To<UnsizedArrayOf<HBUINT8>>
|
||||
stringOffset; /* Offset to start of string storage (from start of table). */
|
||||
UnsizedArrayOf<NameRecord>
|
||||
nameRecordZ; /* The name records where count is the number of records. */
|
||||
public:
|
||||
DEFINE_SIZE_ARRAY (6, nameRecordZ);
|
||||
};
|
||||
|
||||
#undef entry_index
|
||||
#undef entry_score
|
||||
|
||||
struct name_accelerator_t : name::accelerator_t {
|
||||
name_accelerator_t (hb_face_t *face) : name::accelerator_t (face) {}
|
||||
};
|
||||
|
||||
} /* namespace OT */
|
||||
|
||||
|
||||
#endif /* OT_NAME_NAME_HH */
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright © 2018 Ebrahim Byagowi
|
||||
*
|
||||
* This is part of HarfBuzz, a text shaping library.
|
||||
*
|
||||
* Permission is hereby granted, without written agreement and without
|
||||
* license or royalty fees, to use, copy, modify, and distribute this
|
||||
* software and its documentation for any purpose, provided that the
|
||||
* above copyright notice and the following two paragraphs appear in
|
||||
* all copies of this software.
|
||||
*
|
||||
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
|
||||
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
|
||||
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
|
||||
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||
* DAMAGE.
|
||||
*
|
||||
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
|
||||
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
|
||||
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
|
||||
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
|
||||
*/
|
||||
|
||||
#ifndef HB_AAT_LAYOUT_ANKR_TABLE_HH
|
||||
#define HB_AAT_LAYOUT_ANKR_TABLE_HH
|
||||
|
||||
#include "hb-aat-layout-common.hh"
|
||||
|
||||
/*
|
||||
* ankr -- Anchor Point
|
||||
* https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6ankr.html
|
||||
*/
|
||||
#define HB_AAT_TAG_ankr HB_TAG('a','n','k','r')
|
||||
|
||||
|
||||
namespace AAT {
|
||||
|
||||
using namespace OT;
|
||||
|
||||
|
||||
struct Anchor
|
||||
{
|
||||
bool sanitize (hb_sanitize_context_t *c) const
|
||||
{
|
||||
TRACE_SANITIZE (this);
|
||||
return_trace (c->check_struct (this));
|
||||
}
|
||||
|
||||
public:
|
||||
FWORD xCoordinate;
|
||||
FWORD yCoordinate;
|
||||
public:
|
||||
DEFINE_SIZE_STATIC (4);
|
||||
};
|
||||
|
||||
typedef Array32Of<Anchor> GlyphAnchors;
|
||||
|
||||
struct ankr
|
||||
{
|
||||
static constexpr hb_tag_t tableTag = HB_AAT_TAG_ankr;
|
||||
|
||||
const Anchor &get_anchor (hb_codepoint_t glyph_id,
|
||||
unsigned int i,
|
||||
unsigned int num_glyphs) const
|
||||
{
|
||||
const NNOffset16To<GlyphAnchors> *offset = (this+lookupTable).get_value (glyph_id, num_glyphs);
|
||||
if (!offset)
|
||||
return Null (Anchor);
|
||||
const GlyphAnchors &anchors = &(this+anchorData) + *offset;
|
||||
return anchors[i];
|
||||
}
|
||||
|
||||
bool sanitize (hb_sanitize_context_t *c) const
|
||||
{
|
||||
TRACE_SANITIZE (this);
|
||||
return_trace (likely (c->check_struct (this) &&
|
||||
hb_barrier () &&
|
||||
version == 0 &&
|
||||
c->check_range (this, anchorData) &&
|
||||
lookupTable.sanitize (c, this, &(this+anchorData))));
|
||||
}
|
||||
|
||||
protected:
|
||||
HBUINT16 version; /* Version number (set to zero) */
|
||||
HBUINT16 flags; /* Flags (currently unused; set to zero) */
|
||||
Offset32To<Lookup<NNOffset16To<GlyphAnchors>>>
|
||||
lookupTable; /* Offset to the table's lookup table */
|
||||
NNOffset32To<HBUINT8>
|
||||
anchorData; /* Offset to the glyph data table */
|
||||
|
||||
public:
|
||||
DEFINE_SIZE_STATIC (12);
|
||||
};
|
||||
|
||||
} /* namespace AAT */
|
||||
|
||||
|
||||
#endif /* HB_AAT_LAYOUT_ANKR_TABLE_HH */
|
||||
+1391
File diff suppressed because it is too large
Load Diff
+224
@@ -0,0 +1,224 @@
|
||||
/*
|
||||
* Copyright © 2018 Ebrahim Byagowi
|
||||
*
|
||||
* This is part of HarfBuzz, a text shaping library.
|
||||
*
|
||||
* Permission is hereby granted, without written agreement and without
|
||||
* license or royalty fees, to use, copy, modify, and distribute this
|
||||
* software and its documentation for any purpose, provided that the
|
||||
* above copyright notice and the following two paragraphs appear in
|
||||
* all copies of this software.
|
||||
*
|
||||
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
|
||||
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
|
||||
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
|
||||
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||
* DAMAGE.
|
||||
*
|
||||
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
|
||||
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
|
||||
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
|
||||
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
|
||||
*/
|
||||
|
||||
#ifndef HB_AAT_LAYOUT_FEAT_TABLE_HH
|
||||
#define HB_AAT_LAYOUT_FEAT_TABLE_HH
|
||||
|
||||
#include "hb-aat-layout-common.hh"
|
||||
|
||||
/*
|
||||
* feat -- Feature Name
|
||||
* https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6feat.html
|
||||
*/
|
||||
#define HB_AAT_TAG_feat HB_TAG('f','e','a','t')
|
||||
|
||||
|
||||
namespace AAT {
|
||||
|
||||
|
||||
struct SettingName
|
||||
{
|
||||
friend struct FeatureName;
|
||||
|
||||
int cmp (hb_aat_layout_feature_selector_t key) const
|
||||
{ return (int) key - (int) setting; }
|
||||
|
||||
hb_aat_layout_feature_selector_t get_selector () const
|
||||
{ return (hb_aat_layout_feature_selector_t) (unsigned) setting; }
|
||||
|
||||
hb_aat_layout_feature_selector_info_t get_info (hb_aat_layout_feature_selector_t default_selector) const
|
||||
{
|
||||
return {
|
||||
nameIndex,
|
||||
(hb_aat_layout_feature_selector_t) (unsigned int) setting,
|
||||
default_selector == HB_AAT_LAYOUT_FEATURE_SELECTOR_INVALID
|
||||
? (hb_aat_layout_feature_selector_t) (setting + 1)
|
||||
: default_selector,
|
||||
0
|
||||
};
|
||||
}
|
||||
|
||||
bool sanitize (hb_sanitize_context_t *c) const
|
||||
{
|
||||
TRACE_SANITIZE (this);
|
||||
return_trace (c->check_struct (this));
|
||||
}
|
||||
|
||||
protected:
|
||||
HBUINT16 setting; /* The setting. */
|
||||
NameID nameIndex; /* The name table index for the setting's name. */
|
||||
public:
|
||||
DEFINE_SIZE_STATIC (4);
|
||||
};
|
||||
DECLARE_NULL_NAMESPACE_BYTES (AAT, SettingName);
|
||||
|
||||
struct feat;
|
||||
|
||||
struct FeatureName
|
||||
{
|
||||
int cmp (hb_aat_layout_feature_type_t key) const
|
||||
{ return (int) key - (int) feature; }
|
||||
|
||||
enum {
|
||||
Exclusive = 0x8000, /* If set, the feature settings are mutually exclusive. */
|
||||
NotDefault = 0x4000, /* If clear, then the setting with an index of 0 in
|
||||
* the setting name array for this feature should
|
||||
* be taken as the default for the feature
|
||||
* (if one is required). If set, then bits 0-15 of this
|
||||
* featureFlags field contain the index of the setting
|
||||
* which is to be taken as the default. */
|
||||
IndexMask = 0x00FF /* If bits 30 and 31 are set, then these sixteen bits
|
||||
* indicate the index of the setting in the setting name
|
||||
* array for this feature which should be taken
|
||||
* as the default. */
|
||||
};
|
||||
|
||||
unsigned int get_selector_infos (unsigned int start_offset,
|
||||
unsigned int *selectors_count, /* IN/OUT. May be NULL. */
|
||||
hb_aat_layout_feature_selector_info_t *selectors, /* OUT. May be NULL. */
|
||||
unsigned int *pdefault_index, /* OUT. May be NULL. */
|
||||
const void *base) const
|
||||
{
|
||||
hb_array_t< const SettingName> settings_table = (base+settingTableZ).as_array (nSettings);
|
||||
|
||||
static_assert (Index::NOT_FOUND_INDEX == HB_AAT_LAYOUT_NO_SELECTOR_INDEX, "");
|
||||
|
||||
hb_aat_layout_feature_selector_t default_selector = HB_AAT_LAYOUT_FEATURE_SELECTOR_INVALID;
|
||||
unsigned int default_index = Index::NOT_FOUND_INDEX;
|
||||
if (featureFlags & Exclusive)
|
||||
{
|
||||
default_index = (featureFlags & NotDefault) ? featureFlags & IndexMask : 0;
|
||||
default_selector = settings_table[default_index].get_selector ();
|
||||
}
|
||||
if (pdefault_index)
|
||||
*pdefault_index = default_index;
|
||||
|
||||
if (selectors_count)
|
||||
{
|
||||
+ settings_table.sub_array (start_offset, selectors_count)
|
||||
| hb_map ([=] (const SettingName& setting) { return setting.get_info (default_selector); })
|
||||
| hb_sink (hb_array (selectors, *selectors_count))
|
||||
;
|
||||
}
|
||||
return settings_table.length;
|
||||
}
|
||||
|
||||
hb_aat_layout_feature_type_t get_feature_type () const
|
||||
{ return (hb_aat_layout_feature_type_t) (unsigned int) feature; }
|
||||
|
||||
hb_ot_name_id_t get_feature_name_id () const { return nameIndex; }
|
||||
|
||||
bool is_exclusive () const { return featureFlags & Exclusive; }
|
||||
|
||||
/* A FeatureName with no settings is meaningless */
|
||||
bool has_data () const { return nSettings; }
|
||||
|
||||
bool sanitize (hb_sanitize_context_t *c, const void *base) const
|
||||
{
|
||||
TRACE_SANITIZE (this);
|
||||
return_trace (likely (c->check_struct (this) &&
|
||||
hb_barrier () &&
|
||||
(base+settingTableZ).sanitize (c, nSettings)));
|
||||
}
|
||||
|
||||
protected:
|
||||
HBUINT16 feature; /* Feature type. */
|
||||
HBUINT16 nSettings; /* The number of records in the setting name array. */
|
||||
NNOffset32To<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
|
||||
* exclusivity value, as described below. */
|
||||
HBUINT16 featureFlags; /* Single-bit flags associated with the feature type. */
|
||||
HBINT16 nameIndex; /* The name table index for the feature's name.
|
||||
* This index has values greater than 255 and
|
||||
* less than 32768. */
|
||||
public:
|
||||
DEFINE_SIZE_STATIC (12);
|
||||
};
|
||||
|
||||
struct feat
|
||||
{
|
||||
static constexpr hb_tag_t tableTag = HB_AAT_TAG_feat;
|
||||
|
||||
bool has_data () const { return version.to_int (); }
|
||||
|
||||
unsigned int get_feature_types (unsigned int start_offset,
|
||||
unsigned int *count,
|
||||
hb_aat_layout_feature_type_t *features) const
|
||||
{
|
||||
if (count)
|
||||
{
|
||||
+ namesZ.as_array (featureNameCount).sub_array (start_offset, count)
|
||||
| hb_map (&FeatureName::get_feature_type)
|
||||
| hb_sink (hb_array (features, *count))
|
||||
;
|
||||
}
|
||||
return featureNameCount;
|
||||
}
|
||||
|
||||
bool exposes_feature (hb_aat_layout_feature_type_t feature_type) const
|
||||
{ return get_feature (feature_type).has_data (); }
|
||||
|
||||
const FeatureName& get_feature (hb_aat_layout_feature_type_t feature_type) const
|
||||
{ return namesZ.bsearch (featureNameCount, feature_type); }
|
||||
|
||||
hb_ot_name_id_t get_feature_name_id (hb_aat_layout_feature_type_t feature) const
|
||||
{ return get_feature (feature).get_feature_name_id (); }
|
||||
|
||||
unsigned int get_selector_infos (hb_aat_layout_feature_type_t feature_type,
|
||||
unsigned int start_offset,
|
||||
unsigned int *selectors_count, /* IN/OUT. May be NULL. */
|
||||
hb_aat_layout_feature_selector_info_t *selectors, /* OUT. May be NULL. */
|
||||
unsigned int *default_index /* OUT. May be NULL. */) const
|
||||
{
|
||||
return get_feature (feature_type).get_selector_infos (start_offset, selectors_count, selectors,
|
||||
default_index, this);
|
||||
}
|
||||
|
||||
bool sanitize (hb_sanitize_context_t *c) const
|
||||
{
|
||||
TRACE_SANITIZE (this);
|
||||
return_trace (likely (c->check_struct (this) &&
|
||||
hb_barrier () &&
|
||||
version.major == 1 &&
|
||||
namesZ.sanitize (c, featureNameCount, this)));
|
||||
}
|
||||
|
||||
protected:
|
||||
FixedVersion<>version; /* Version number of the feature name table
|
||||
* (0x00010000 for the current version). */
|
||||
HBUINT16 featureNameCount;
|
||||
/* The number of entries in the feature name array. */
|
||||
HBUINT16 reserved1; /* Reserved (set to zero). */
|
||||
HBUINT32 reserved2; /* Reserved (set to zero). */
|
||||
SortedUnsizedArrayOf<FeatureName>
|
||||
namesZ; /* The feature name array. */
|
||||
public:
|
||||
DEFINE_SIZE_ARRAY (12, namesZ);
|
||||
};
|
||||
|
||||
} /* namespace AAT */
|
||||
|
||||
#endif /* HB_AAT_LAYOUT_FEAT_TABLE_HH */
|
||||
+1203
File diff suppressed because it is too large
Load Diff
+1499
File diff suppressed because it is too large
Load Diff
+174
@@ -0,0 +1,174 @@
|
||||
/*
|
||||
* Copyright © 2019 Ebrahim Byagowi
|
||||
*
|
||||
* This is part of HarfBuzz, a text shaping library.
|
||||
*
|
||||
* Permission is hereby granted, without written agreement and without
|
||||
* license or royalty fees, to use, copy, modify, and distribute this
|
||||
* software and its documentation for any purpose, provided that the
|
||||
* above copyright notice and the following two paragraphs appear in
|
||||
* all copies of this software.
|
||||
*
|
||||
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
|
||||
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
|
||||
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
|
||||
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||
* DAMAGE.
|
||||
*
|
||||
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
|
||||
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
|
||||
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
|
||||
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
|
||||
*/
|
||||
|
||||
#ifndef HB_AAT_LAYOUT_OPBD_TABLE_HH
|
||||
#define HB_AAT_LAYOUT_OPBD_TABLE_HH
|
||||
|
||||
#include "hb-aat-layout-common.hh"
|
||||
#include "hb-open-type.hh"
|
||||
|
||||
/*
|
||||
* opbd -- Optical Bounds
|
||||
* https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6opbd.html
|
||||
*/
|
||||
#define HB_AAT_TAG_opbd HB_TAG('o','p','b','d')
|
||||
|
||||
|
||||
namespace AAT {
|
||||
|
||||
struct OpticalBounds
|
||||
{
|
||||
bool sanitize (hb_sanitize_context_t *c) const
|
||||
{
|
||||
TRACE_SANITIZE (this);
|
||||
return_trace (c->check_struct (this));
|
||||
}
|
||||
|
||||
FWORD leftSide;
|
||||
FWORD topSide;
|
||||
FWORD rightSide;
|
||||
FWORD bottomSide;
|
||||
public:
|
||||
DEFINE_SIZE_STATIC (8);
|
||||
};
|
||||
|
||||
struct opbdFormat0
|
||||
{
|
||||
bool get_bounds (hb_font_t *font, hb_codepoint_t glyph_id,
|
||||
hb_glyph_extents_t *extents, const void *base) const
|
||||
{
|
||||
const Offset16To<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<Offset16To<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 Offset16To<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<Offset16To<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: hb_barrier (); return u.format0.get_bounds (font, glyph_id, extents, this);
|
||||
case 1: hb_barrier (); 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);
|
||||
hb_barrier ();
|
||||
|
||||
switch (format)
|
||||
{
|
||||
case 0: hb_barrier (); return_trace (u.format0.sanitize (c, this));
|
||||
case 1: hb_barrier (); 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 */
|
||||
+795
@@ -0,0 +1,795 @@
|
||||
/*
|
||||
* Copyright © 2018 Ebrahim Byagowi
|
||||
*
|
||||
* This is part of HarfBuzz, a text shaping library.
|
||||
*
|
||||
* Permission is hereby granted, without written agreement and without
|
||||
* license or royalty fees, to use, copy, modify, and distribute this
|
||||
* software and its documentation for any purpose, provided that the
|
||||
* above copyright notice and the following two paragraphs appear in
|
||||
* all copies of this software.
|
||||
*
|
||||
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
|
||||
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
|
||||
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
|
||||
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||
* DAMAGE.
|
||||
*
|
||||
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
|
||||
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
|
||||
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
|
||||
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
|
||||
*/
|
||||
|
||||
#if !defined(HB_AAT_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR)
|
||||
#error "Include <hb-aat.h> instead."
|
||||
#endif
|
||||
|
||||
#ifndef HB_AAT_LAYOUT_H
|
||||
#define HB_AAT_LAYOUT_H
|
||||
|
||||
#include "hb.h"
|
||||
|
||||
#include "hb-ot.h"
|
||||
|
||||
HB_BEGIN_DECLS
|
||||
|
||||
/**
|
||||
* hb_aat_layout_feature_type_t:
|
||||
* @HB_AAT_LAYOUT_FEATURE_TYPE_INVALID: Initial, unset feature type
|
||||
* @HB_AAT_LAYOUT_FEATURE_TYPE_ALL_TYPOGRAPHIC: [All Typographic Features](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type0)
|
||||
* @HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES: [Ligatures](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type1)
|
||||
* @HB_AAT_LAYOUT_FEATURE_TYPE_CURSIVE_CONNECTION: [Cursive Connection](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type2)
|
||||
* @HB_AAT_LAYOUT_FEATURE_TYPE_LETTER_CASE: [Letter Case](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type3)
|
||||
* @HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_SUBSTITUTION: [Vertical Substitution](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type4)
|
||||
* @HB_AAT_LAYOUT_FEATURE_TYPE_LINGUISTIC_REARRANGEMENT: [Linguistic Rearrangement](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type5)
|
||||
* @HB_AAT_LAYOUT_FEATURE_TYPE_NUMBER_SPACING: [Number Spacing](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type6)
|
||||
* @HB_AAT_LAYOUT_FEATURE_TYPE_SMART_SWASH_TYPE: [Smart Swash](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type8)
|
||||
* @HB_AAT_LAYOUT_FEATURE_TYPE_DIACRITICS_TYPE: [Diacritics](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type9)
|
||||
* @HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_POSITION: [Vertical Position](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type10)
|
||||
* @HB_AAT_LAYOUT_FEATURE_TYPE_FRACTIONS: [Fractions](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type11)
|
||||
* @HB_AAT_LAYOUT_FEATURE_TYPE_OVERLAPPING_CHARACTERS_TYPE: [Overlapping Characters](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type13)
|
||||
* @HB_AAT_LAYOUT_FEATURE_TYPE_TYPOGRAPHIC_EXTRAS: [Typographic Extras](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type14)
|
||||
* @HB_AAT_LAYOUT_FEATURE_TYPE_MATHEMATICAL_EXTRAS: [Mathematical Extras](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type15)
|
||||
* @HB_AAT_LAYOUT_FEATURE_TYPE_ORNAMENT_SETS_TYPE: [Ornament Sets](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type16)
|
||||
* @HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_ALTERNATIVES: [Character Alternatives](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type17)
|
||||
* @HB_AAT_LAYOUT_FEATURE_TYPE_DESIGN_COMPLEXITY_TYPE: [Design Complexity](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type18)
|
||||
* @HB_AAT_LAYOUT_FEATURE_TYPE_STYLE_OPTIONS: [Style Options](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type19)
|
||||
* @HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE: [Character Shape](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type20)
|
||||
* @HB_AAT_LAYOUT_FEATURE_TYPE_NUMBER_CASE: [Number Case](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type21)
|
||||
* @HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING: [Text Spacing](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type22)
|
||||
* @HB_AAT_LAYOUT_FEATURE_TYPE_TRANSLITERATION: [Transliteration](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type23)
|
||||
* @HB_AAT_LAYOUT_FEATURE_TYPE_ANNOTATION_TYPE: [Annotation](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type24)
|
||||
* @HB_AAT_LAYOUT_FEATURE_TYPE_KANA_SPACING_TYPE: [Kana Spacing](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type25)
|
||||
* @HB_AAT_LAYOUT_FEATURE_TYPE_IDEOGRAPHIC_SPACING_TYPE: [Ideographic Spacing](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type26)
|
||||
* @HB_AAT_LAYOUT_FEATURE_TYPE_UNICODE_DECOMPOSITION_TYPE: [Unicode Decomposition](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type27)
|
||||
* @HB_AAT_LAYOUT_FEATURE_TYPE_RUBY_KANA: [Ruby Kana](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type28)
|
||||
* @HB_AAT_LAYOUT_FEATURE_TYPE_CJK_SYMBOL_ALTERNATIVES_TYPE: [CJK Symbol Alternatives](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type29)
|
||||
* @HB_AAT_LAYOUT_FEATURE_TYPE_IDEOGRAPHIC_ALTERNATIVES_TYPE: [Ideographic Alternatives](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type30)
|
||||
* @HB_AAT_LAYOUT_FEATURE_TYPE_CJK_VERTICAL_ROMAN_PLACEMENT_TYPE: [CJK Vertical Roman Placement](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type31)
|
||||
* @HB_AAT_LAYOUT_FEATURE_TYPE_ITALIC_CJK_ROMAN: [Italic CJK Roman](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type32)
|
||||
* @HB_AAT_LAYOUT_FEATURE_TYPE_CASE_SENSITIVE_LAYOUT: [Case Sensitive Layout](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type33)
|
||||
* @HB_AAT_LAYOUT_FEATURE_TYPE_ALTERNATE_KANA: [Alternate Kana](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type34)
|
||||
* @HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES: [Stylistic Alternatives](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type35)
|
||||
* @HB_AAT_LAYOUT_FEATURE_TYPE_CONTEXTUAL_ALTERNATIVES: [Contextual Alternatives](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type36)
|
||||
* @HB_AAT_LAYOUT_FEATURE_TYPE_LOWER_CASE: [Lower Case](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type37)
|
||||
* @HB_AAT_LAYOUT_FEATURE_TYPE_UPPER_CASE: [Upper Case](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type38)
|
||||
* @HB_AAT_LAYOUT_FEATURE_TYPE_LANGUAGE_TAG_TYPE: [Language Tag](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type39)
|
||||
* @HB_AAT_LAYOUT_FEATURE_TYPE_CJK_ROMAN_SPACING_TYPE: [CJK Roman Spacing](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html#Type103)
|
||||
*
|
||||
* The possible feature types defined for AAT shaping, from Apple [Font Feature Registry](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html).
|
||||
*
|
||||
* Since: 2.2.0
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
HB_AAT_LAYOUT_FEATURE_TYPE_INVALID = 0xFFFF,
|
||||
|
||||
HB_AAT_LAYOUT_FEATURE_TYPE_ALL_TYPOGRAPHIC = 0,
|
||||
HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES = 1,
|
||||
HB_AAT_LAYOUT_FEATURE_TYPE_CURSIVE_CONNECTION = 2,
|
||||
HB_AAT_LAYOUT_FEATURE_TYPE_LETTER_CASE = 3,
|
||||
HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_SUBSTITUTION = 4,
|
||||
HB_AAT_LAYOUT_FEATURE_TYPE_LINGUISTIC_REARRANGEMENT = 5,
|
||||
HB_AAT_LAYOUT_FEATURE_TYPE_NUMBER_SPACING = 6,
|
||||
HB_AAT_LAYOUT_FEATURE_TYPE_SMART_SWASH_TYPE = 8,
|
||||
HB_AAT_LAYOUT_FEATURE_TYPE_DIACRITICS_TYPE = 9,
|
||||
HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_POSITION = 10,
|
||||
HB_AAT_LAYOUT_FEATURE_TYPE_FRACTIONS = 11,
|
||||
HB_AAT_LAYOUT_FEATURE_TYPE_OVERLAPPING_CHARACTERS_TYPE = 13,
|
||||
HB_AAT_LAYOUT_FEATURE_TYPE_TYPOGRAPHIC_EXTRAS = 14,
|
||||
HB_AAT_LAYOUT_FEATURE_TYPE_MATHEMATICAL_EXTRAS = 15,
|
||||
HB_AAT_LAYOUT_FEATURE_TYPE_ORNAMENT_SETS_TYPE = 16,
|
||||
HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_ALTERNATIVES = 17,
|
||||
HB_AAT_LAYOUT_FEATURE_TYPE_DESIGN_COMPLEXITY_TYPE = 18,
|
||||
HB_AAT_LAYOUT_FEATURE_TYPE_STYLE_OPTIONS = 19,
|
||||
HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE = 20,
|
||||
HB_AAT_LAYOUT_FEATURE_TYPE_NUMBER_CASE = 21,
|
||||
HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING = 22,
|
||||
HB_AAT_LAYOUT_FEATURE_TYPE_TRANSLITERATION = 23,
|
||||
HB_AAT_LAYOUT_FEATURE_TYPE_ANNOTATION_TYPE = 24,
|
||||
HB_AAT_LAYOUT_FEATURE_TYPE_KANA_SPACING_TYPE = 25,
|
||||
HB_AAT_LAYOUT_FEATURE_TYPE_IDEOGRAPHIC_SPACING_TYPE = 26,
|
||||
HB_AAT_LAYOUT_FEATURE_TYPE_UNICODE_DECOMPOSITION_TYPE = 27,
|
||||
HB_AAT_LAYOUT_FEATURE_TYPE_RUBY_KANA = 28,
|
||||
HB_AAT_LAYOUT_FEATURE_TYPE_CJK_SYMBOL_ALTERNATIVES_TYPE = 29,
|
||||
HB_AAT_LAYOUT_FEATURE_TYPE_IDEOGRAPHIC_ALTERNATIVES_TYPE = 30,
|
||||
HB_AAT_LAYOUT_FEATURE_TYPE_CJK_VERTICAL_ROMAN_PLACEMENT_TYPE = 31,
|
||||
HB_AAT_LAYOUT_FEATURE_TYPE_ITALIC_CJK_ROMAN = 32,
|
||||
HB_AAT_LAYOUT_FEATURE_TYPE_CASE_SENSITIVE_LAYOUT = 33,
|
||||
HB_AAT_LAYOUT_FEATURE_TYPE_ALTERNATE_KANA = 34,
|
||||
HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES = 35,
|
||||
HB_AAT_LAYOUT_FEATURE_TYPE_CONTEXTUAL_ALTERNATIVES = 36,
|
||||
HB_AAT_LAYOUT_FEATURE_TYPE_LOWER_CASE = 37,
|
||||
HB_AAT_LAYOUT_FEATURE_TYPE_UPPER_CASE = 38,
|
||||
HB_AAT_LAYOUT_FEATURE_TYPE_LANGUAGE_TAG_TYPE = 39,
|
||||
HB_AAT_LAYOUT_FEATURE_TYPE_CJK_ROMAN_SPACING_TYPE = 103,
|
||||
|
||||
/*< private >*/
|
||||
_HB_AAT_LAYOUT_FEATURE_TYPE_MAX_VALUE = HB_TAG_MAX_SIGNED /*< skip >*/
|
||||
} hb_aat_layout_feature_type_t;
|
||||
|
||||
/**
|
||||
* hb_aat_layout_feature_selector_t:
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_INVALID: Initial, unset feature selector
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_ALL_TYPE_FEATURES_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_ALL_TYPOGRAPHIC
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_ALL_TYPE_FEATURES_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_ALL_TYPOGRAPHIC
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_REQUIRED_LIGATURES_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_REQUIRED_LIGATURES_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_COMMON_LIGATURES_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_COMMON_LIGATURES_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_RARE_LIGATURES_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_RARE_LIGATURES_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_LOGOS_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_LOGOS_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_REBUS_PICTURES_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_REBUS_PICTURES_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_DIPHTHONG_LIGATURES_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_DIPHTHONG_LIGATURES_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_SQUARED_LIGATURES_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_SQUARED_LIGATURES_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_ABBREV_SQUARED_LIGATURES_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_ABBREV_SQUARED_LIGATURES_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_SYMBOL_LIGATURES_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_SYMBOL_LIGATURES_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_LIGATURES_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_LIGATURES_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_HISTORICAL_LIGATURES_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_HISTORICAL_LIGATURES_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_UNCONNECTED: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_PARTIALLY_CONNECTED: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_CURSIVE: for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_UPPER_AND_LOWER_CASE: Deprecated
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_ALL_CAPS: Deprecated
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_ALL_LOWER_CASE: Deprecated
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_SMALL_CAPS: Deprecated
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_INITIAL_CAPS: Deprecated
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_INITIAL_CAPS_AND_SMALL_CAPS: Deprecated
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_SUBSTITUTE_VERTICAL_FORMS_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_SUBSTITUTION
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_SUBSTITUTE_VERTICAL_FORMS_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_SUBSTITUTION
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_LINGUISTIC_REARRANGEMENT_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_LINGUISTIC_REARRANGEMENT
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_LINGUISTIC_REARRANGEMENT_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_LINGUISTIC_REARRANGEMENT
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_MONOSPACED_NUMBERS: for #HB_AAT_LAYOUT_FEATURE_TYPE_NUMBER_SPACING
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_PROPORTIONAL_NUMBERS: for #HB_AAT_LAYOUT_FEATURE_TYPE_NUMBER_SPACING
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_THIRD_WIDTH_NUMBERS: for #HB_AAT_LAYOUT_FEATURE_TYPE_NUMBER_SPACING
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_QUARTER_WIDTH_NUMBERS: for #HB_AAT_LAYOUT_FEATURE_TYPE_NUMBER_SPACING
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_WORD_INITIAL_SWASHES_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_SMART_SWASH_TYPE
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_WORD_INITIAL_SWASHES_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_SMART_SWASH_TYPE
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_WORD_FINAL_SWASHES_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_SMART_SWASH_TYPE
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_WORD_FINAL_SWASHES_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_SMART_SWASH_TYPE
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_LINE_INITIAL_SWASHES_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_SMART_SWASH_TYPE
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_LINE_INITIAL_SWASHES_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_SMART_SWASH_TYPE
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_LINE_FINAL_SWASHES_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_SMART_SWASH_TYPE
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_LINE_FINAL_SWASHES_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_SMART_SWASH_TYPE
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_NON_FINAL_SWASHES_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_SMART_SWASH_TYPE
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_NON_FINAL_SWASHES_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_SMART_SWASH_TYPE
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_SHOW_DIACRITICS: for #HB_AAT_LAYOUT_FEATURE_TYPE_DIACRITICS_TYPE
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_HIDE_DIACRITICS: for #HB_AAT_LAYOUT_FEATURE_TYPE_DIACRITICS_TYPE
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_DECOMPOSE_DIACRITICS: for #HB_AAT_LAYOUT_FEATURE_TYPE_DIACRITICS_TYPE
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_NORMAL_POSITION: for #HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_POSITION
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_SUPERIORS: for #HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_POSITION
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_INFERIORS: for #HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_POSITION
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_ORDINALS: for #HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_POSITION
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_SCIENTIFIC_INFERIORS: for #HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_POSITION
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_FRACTIONS: for #HB_AAT_LAYOUT_FEATURE_TYPE_FRACTIONS
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_VERTICAL_FRACTIONS: for #HB_AAT_LAYOUT_FEATURE_TYPE_FRACTIONS
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_DIAGONAL_FRACTIONS: for #HB_AAT_LAYOUT_FEATURE_TYPE_FRACTIONS
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_PREVENT_OVERLAP_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_OVERLAPPING_CHARACTERS_TYPE
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_PREVENT_OVERLAP_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_OVERLAPPING_CHARACTERS_TYPE
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_HYPHENS_TO_EM_DASH_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_TYPOGRAPHIC_EXTRAS
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_HYPHENS_TO_EM_DASH_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_TYPOGRAPHIC_EXTRAS
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_HYPHEN_TO_EN_DASH_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_TYPOGRAPHIC_EXTRAS
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_HYPHEN_TO_EN_DASH_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_TYPOGRAPHIC_EXTRAS
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_SLASHED_ZERO_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_TYPOGRAPHIC_EXTRAS
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_SLASHED_ZERO_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_TYPOGRAPHIC_EXTRAS
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_FORM_INTERROBANG_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_TYPOGRAPHIC_EXTRAS
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_FORM_INTERROBANG_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_TYPOGRAPHIC_EXTRAS
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_SMART_QUOTES_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_TYPOGRAPHIC_EXTRAS
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_SMART_QUOTES_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_TYPOGRAPHIC_EXTRAS
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_PERIODS_TO_ELLIPSIS_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_TYPOGRAPHIC_EXTRAS
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_PERIODS_TO_ELLIPSIS_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_TYPOGRAPHIC_EXTRAS
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_HYPHEN_TO_MINUS_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_MATHEMATICAL_EXTRAS
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_HYPHEN_TO_MINUS_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_MATHEMATICAL_EXTRAS
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_ASTERISK_TO_MULTIPLY_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_MATHEMATICAL_EXTRAS
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_ASTERISK_TO_MULTIPLY_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_MATHEMATICAL_EXTRAS
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_SLASH_TO_DIVIDE_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_MATHEMATICAL_EXTRAS
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_SLASH_TO_DIVIDE_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_MATHEMATICAL_EXTRAS
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_INEQUALITY_LIGATURES_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_MATHEMATICAL_EXTRAS
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_INEQUALITY_LIGATURES_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_MATHEMATICAL_EXTRAS
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_EXPONENTS_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_MATHEMATICAL_EXTRAS
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_EXPONENTS_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_MATHEMATICAL_EXTRAS
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_MATHEMATICAL_GREEK_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_MATHEMATICAL_EXTRAS
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_MATHEMATICAL_GREEK_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_MATHEMATICAL_EXTRAS
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_ORNAMENTS: for #HB_AAT_LAYOUT_FEATURE_TYPE_ORNAMENT_SETS_TYPE
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_DINGBATS: for #HB_AAT_LAYOUT_FEATURE_TYPE_ORNAMENT_SETS_TYPE
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_PI_CHARACTERS: for #HB_AAT_LAYOUT_FEATURE_TYPE_ORNAMENT_SETS_TYPE
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_FLEURONS: for #HB_AAT_LAYOUT_FEATURE_TYPE_ORNAMENT_SETS_TYPE
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_DECORATIVE_BORDERS: for #HB_AAT_LAYOUT_FEATURE_TYPE_ORNAMENT_SETS_TYPE
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_INTERNATIONAL_SYMBOLS: for #HB_AAT_LAYOUT_FEATURE_TYPE_ORNAMENT_SETS_TYPE
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_MATH_SYMBOLS: for #HB_AAT_LAYOUT_FEATURE_TYPE_ORNAMENT_SETS_TYPE
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_ALTERNATES: for #HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_ALTERNATIVES
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_DESIGN_LEVEL1: for #HB_AAT_LAYOUT_FEATURE_TYPE_DESIGN_COMPLEXITY_TYPE
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_DESIGN_LEVEL2: for #HB_AAT_LAYOUT_FEATURE_TYPE_DESIGN_COMPLEXITY_TYPE
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_DESIGN_LEVEL3: for #HB_AAT_LAYOUT_FEATURE_TYPE_DESIGN_COMPLEXITY_TYPE
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_DESIGN_LEVEL4: for #HB_AAT_LAYOUT_FEATURE_TYPE_DESIGN_COMPLEXITY_TYPE
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_DESIGN_LEVEL5: for #HB_AAT_LAYOUT_FEATURE_TYPE_DESIGN_COMPLEXITY_TYPE
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_STYLE_OPTIONS: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLE_OPTIONS
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_DISPLAY_TEXT: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLE_OPTIONS
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_ENGRAVED_TEXT: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLE_OPTIONS
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_ILLUMINATED_CAPS: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLE_OPTIONS
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_TITLING_CAPS: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLE_OPTIONS
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_TALL_CAPS: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLE_OPTIONS
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_TRADITIONAL_CHARACTERS: for #HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_SIMPLIFIED_CHARACTERS: for #HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_JIS1978_CHARACTERS: for #HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_JIS1983_CHARACTERS: for #HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_JIS1990_CHARACTERS: for #HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_TRADITIONAL_ALT_ONE: for #HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_TRADITIONAL_ALT_TWO: for #HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_TRADITIONAL_ALT_THREE: for #HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_TRADITIONAL_ALT_FOUR: for #HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_TRADITIONAL_ALT_FIVE: for #HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_EXPERT_CHARACTERS: for #HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_JIS2004_CHARACTERS: for #HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_HOJO_CHARACTERS: for #HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_NLCCHARACTERS: for #HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_TRADITIONAL_NAMES_CHARACTERS: for #HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_LOWER_CASE_NUMBERS: for #HB_AAT_LAYOUT_FEATURE_TYPE_NUMBER_CASE
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_UPPER_CASE_NUMBERS: for #HB_AAT_LAYOUT_FEATURE_TYPE_NUMBER_CASE
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_PROPORTIONAL_TEXT: for #HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_MONOSPACED_TEXT: for #HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_HALF_WIDTH_TEXT: for #HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_THIRD_WIDTH_TEXT: for #HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_QUARTER_WIDTH_TEXT: for #HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_ALT_PROPORTIONAL_TEXT: for #HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_ALT_HALF_WIDTH_TEXT: for #HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_TRANSLITERATION: for #HB_AAT_LAYOUT_FEATURE_TYPE_TRANSLITERATION
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_HANJA_TO_HANGUL: for #HB_AAT_LAYOUT_FEATURE_TYPE_TRANSLITERATION
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_HIRAGANA_TO_KATAKANA: for #HB_AAT_LAYOUT_FEATURE_TYPE_TRANSLITERATION
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_KATAKANA_TO_HIRAGANA: for #HB_AAT_LAYOUT_FEATURE_TYPE_TRANSLITERATION
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_KANA_TO_ROMANIZATION: for #HB_AAT_LAYOUT_FEATURE_TYPE_TRANSLITERATION
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_ROMANIZATION_TO_HIRAGANA: for #HB_AAT_LAYOUT_FEATURE_TYPE_TRANSLITERATION
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_ROMANIZATION_TO_KATAKANA: for #HB_AAT_LAYOUT_FEATURE_TYPE_TRANSLITERATION
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_HANJA_TO_HANGUL_ALT_ONE: for #HB_AAT_LAYOUT_FEATURE_TYPE_TRANSLITERATION
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_HANJA_TO_HANGUL_ALT_TWO: for #HB_AAT_LAYOUT_FEATURE_TYPE_TRANSLITERATION
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_HANJA_TO_HANGUL_ALT_THREE: for #HB_AAT_LAYOUT_FEATURE_TYPE_TRANSLITERATION
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_ANNOTATION: for #HB_AAT_LAYOUT_FEATURE_TYPE_ANNOTATION_TYPE
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_BOX_ANNOTATION: for #HB_AAT_LAYOUT_FEATURE_TYPE_ANNOTATION_TYPE
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_ROUNDED_BOX_ANNOTATION: for #HB_AAT_LAYOUT_FEATURE_TYPE_ANNOTATION_TYPE
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_CIRCLE_ANNOTATION: for #HB_AAT_LAYOUT_FEATURE_TYPE_ANNOTATION_TYPE
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_INVERTED_CIRCLE_ANNOTATION: for #HB_AAT_LAYOUT_FEATURE_TYPE_ANNOTATION_TYPE
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_PARENTHESIS_ANNOTATION: for #HB_AAT_LAYOUT_FEATURE_TYPE_ANNOTATION_TYPE
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_PERIOD_ANNOTATION: for #HB_AAT_LAYOUT_FEATURE_TYPE_ANNOTATION_TYPE
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_ROMAN_NUMERAL_ANNOTATION: for #HB_AAT_LAYOUT_FEATURE_TYPE_ANNOTATION_TYPE
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_DIAMOND_ANNOTATION: for #HB_AAT_LAYOUT_FEATURE_TYPE_ANNOTATION_TYPE
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_INVERTED_BOX_ANNOTATION: for #HB_AAT_LAYOUT_FEATURE_TYPE_ANNOTATION_TYPE
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_INVERTED_ROUNDED_BOX_ANNOTATION: for #HB_AAT_LAYOUT_FEATURE_TYPE_ANNOTATION_TYPE
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_FULL_WIDTH_KANA: for #HB_AAT_LAYOUT_FEATURE_TYPE_KANA_SPACING_TYPE
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_PROPORTIONAL_KANA: for #HB_AAT_LAYOUT_FEATURE_TYPE_KANA_SPACING_TYPE
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_FULL_WIDTH_IDEOGRAPHS: for #HB_AAT_LAYOUT_FEATURE_TYPE_IDEOGRAPHIC_SPACING_TYPE
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_PROPORTIONAL_IDEOGRAPHS: for #HB_AAT_LAYOUT_FEATURE_TYPE_IDEOGRAPHIC_SPACING_TYPE
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_HALF_WIDTH_IDEOGRAPHS: for #HB_AAT_LAYOUT_FEATURE_TYPE_IDEOGRAPHIC_SPACING_TYPE
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_CANONICAL_COMPOSITION_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_UNICODE_DECOMPOSITION_TYPE
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_CANONICAL_COMPOSITION_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_UNICODE_DECOMPOSITION_TYPE
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_COMPATIBILITY_COMPOSITION_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_UNICODE_DECOMPOSITION_TYPE
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_COMPATIBILITY_COMPOSITION_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_UNICODE_DECOMPOSITION_TYPE
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_TRANSCODING_COMPOSITION_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_UNICODE_DECOMPOSITION_TYPE
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_TRANSCODING_COMPOSITION_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_UNICODE_DECOMPOSITION_TYPE
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_RUBY_KANA: Deprecated; use #HB_AAT_LAYOUT_FEATURE_SELECTOR_RUBY_KANA_OFF instead
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_RUBY_KANA: Deprecated; use #HB_AAT_LAYOUT_FEATURE_SELECTOR_RUBY_KANA_ON instead
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_RUBY_KANA_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_RUBY_KANA
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_RUBY_KANA_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_RUBY_KANA
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_CJK_SYMBOL_ALTERNATIVES: for #HB_AAT_LAYOUT_FEATURE_TYPE_CJK_SYMBOL_ALTERNATIVES_TYPE
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_SYMBOL_ALT_ONE: for #HB_AAT_LAYOUT_FEATURE_TYPE_CJK_SYMBOL_ALTERNATIVES_TYPE
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_SYMBOL_ALT_TWO: for #HB_AAT_LAYOUT_FEATURE_TYPE_CJK_SYMBOL_ALTERNATIVES_TYPE
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_SYMBOL_ALT_THREE: for #HB_AAT_LAYOUT_FEATURE_TYPE_CJK_SYMBOL_ALTERNATIVES_TYPE
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_SYMBOL_ALT_FOUR: for #HB_AAT_LAYOUT_FEATURE_TYPE_CJK_SYMBOL_ALTERNATIVES_TYPE
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_SYMBOL_ALT_FIVE: for #HB_AAT_LAYOUT_FEATURE_TYPE_CJK_SYMBOL_ALTERNATIVES_TYPE
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_IDEOGRAPHIC_ALTERNATIVES: for #HB_AAT_LAYOUT_FEATURE_TYPE_IDEOGRAPHIC_ALTERNATIVES_TYPE
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_IDEOGRAPHIC_ALT_ONE: for #HB_AAT_LAYOUT_FEATURE_TYPE_IDEOGRAPHIC_ALTERNATIVES_TYPE
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_IDEOGRAPHIC_ALT_TWO: for #HB_AAT_LAYOUT_FEATURE_TYPE_IDEOGRAPHIC_ALTERNATIVES_TYPE
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_IDEOGRAPHIC_ALT_THREE: for #HB_AAT_LAYOUT_FEATURE_TYPE_IDEOGRAPHIC_ALTERNATIVES_TYPE
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_IDEOGRAPHIC_ALT_FOUR: for #HB_AAT_LAYOUT_FEATURE_TYPE_IDEOGRAPHIC_ALTERNATIVES_TYPE
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_IDEOGRAPHIC_ALT_FIVE: for #HB_AAT_LAYOUT_FEATURE_TYPE_IDEOGRAPHIC_ALTERNATIVES_TYPE
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_VERTICAL_ROMAN_CENTERED: for #HB_AAT_LAYOUT_FEATURE_TYPE_CJK_VERTICAL_ROMAN_PLACEMENT_TYPE
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_VERTICAL_ROMAN_HBASELINE: for #HB_AAT_LAYOUT_FEATURE_TYPE_CJK_VERTICAL_ROMAN_PLACEMENT_TYPE
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_CJK_ITALIC_ROMAN: Deprecated; use #HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_ITALIC_ROMAN_OFF instead
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_ITALIC_ROMAN: Deprecated; use #HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_ITALIC_ROMAN_ON instead
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_ITALIC_ROMAN_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_ITALIC_CJK_ROMAN
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_ITALIC_ROMAN_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_ITALIC_CJK_ROMAN
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_CASE_SENSITIVE_LAYOUT_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_CASE_SENSITIVE_LAYOUT
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_CASE_SENSITIVE_LAYOUT_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_CASE_SENSITIVE_LAYOUT
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_CASE_SENSITIVE_SPACING_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_CASE_SENSITIVE_LAYOUT
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_CASE_SENSITIVE_SPACING_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_CASE_SENSITIVE_LAYOUT
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_ALTERNATE_HORIZ_KANA_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_ALTERNATE_KANA
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_ALTERNATE_HORIZ_KANA_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_ALTERNATE_KANA
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_ALTERNATE_VERT_KANA_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_ALTERNATE_KANA
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_ALTERNATE_VERT_KANA_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_ALTERNATE_KANA
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_STYLISTIC_ALTERNATES: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_ONE_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_ONE_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWO_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWO_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_THREE_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_THREE_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FOUR_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FOUR_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FIVE_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FIVE_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SIX_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SIX_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SEVEN_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SEVEN_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_EIGHT_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_EIGHT_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_NINE_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_NINE_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TEN_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TEN_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_ELEVEN_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_ELEVEN_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWELVE_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWELVE_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_THIRTEEN_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_THIRTEEN_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FOURTEEN_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FOURTEEN_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FIFTEEN_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FIFTEEN_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SIXTEEN_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SIXTEEN_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SEVENTEEN_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SEVENTEEN_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_EIGHTEEN_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_EIGHTEEN_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_NINETEEN_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_NINETEEN_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWENTY_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWENTY_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_ALTERNATES_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_CONTEXTUAL_ALTERNATIVES
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_ALTERNATES_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_CONTEXTUAL_ALTERNATIVES
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_SWASH_ALTERNATES_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_CONTEXTUAL_ALTERNATIVES
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_SWASH_ALTERNATES_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_CONTEXTUAL_ALTERNATIVES
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_SWASH_ALTERNATES_ON: for #HB_AAT_LAYOUT_FEATURE_TYPE_CONTEXTUAL_ALTERNATIVES
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_SWASH_ALTERNATES_OFF: for #HB_AAT_LAYOUT_FEATURE_TYPE_CONTEXTUAL_ALTERNATIVES
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_DEFAULT_LOWER_CASE: for #HB_AAT_LAYOUT_FEATURE_TYPE_LOWER_CASE
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_LOWER_CASE_SMALL_CAPS: for #HB_AAT_LAYOUT_FEATURE_TYPE_LOWER_CASE
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_LOWER_CASE_PETITE_CAPS: for #HB_AAT_LAYOUT_FEATURE_TYPE_LOWER_CASE
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_DEFAULT_UPPER_CASE: for #HB_AAT_LAYOUT_FEATURE_TYPE_UPPER_CASE
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_UPPER_CASE_SMALL_CAPS: for #HB_AAT_LAYOUT_FEATURE_TYPE_UPPER_CASE
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_UPPER_CASE_PETITE_CAPS: for #HB_AAT_LAYOUT_FEATURE_TYPE_UPPER_CASE
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_HALF_WIDTH_CJK_ROMAN: for #HB_AAT_LAYOUT_FEATURE_TYPE_CJK_ROMAN_SPACING_TYPE
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_PROPORTIONAL_CJK_ROMAN: for #HB_AAT_LAYOUT_FEATURE_TYPE_CJK_ROMAN_SPACING_TYPE
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_DEFAULT_CJK_ROMAN: for #HB_AAT_LAYOUT_FEATURE_TYPE_CJK_ROMAN_SPACING_TYPE
|
||||
* @HB_AAT_LAYOUT_FEATURE_SELECTOR_FULL_WIDTH_CJK_ROMAN: for #HB_AAT_LAYOUT_FEATURE_TYPE_CJK_ROMAN_SPACING_TYPE
|
||||
*
|
||||
* The selectors defined for specifying AAT feature settings.
|
||||
*
|
||||
* Since: 2.2.0
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_INVALID = 0xFFFF,
|
||||
|
||||
/* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_ALL_TYPOGRAPHIC */
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_ALL_TYPE_FEATURES_ON = 0,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_ALL_TYPE_FEATURES_OFF = 1,
|
||||
|
||||
/* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES */
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_REQUIRED_LIGATURES_ON = 0,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_REQUIRED_LIGATURES_OFF = 1,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_COMMON_LIGATURES_ON = 2,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_COMMON_LIGATURES_OFF = 3,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_RARE_LIGATURES_ON = 4,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_RARE_LIGATURES_OFF = 5,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_LOGOS_ON = 6,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_LOGOS_OFF = 7,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_REBUS_PICTURES_ON = 8,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_REBUS_PICTURES_OFF = 9,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_DIPHTHONG_LIGATURES_ON = 10,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_DIPHTHONG_LIGATURES_OFF = 11,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_SQUARED_LIGATURES_ON = 12,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_SQUARED_LIGATURES_OFF = 13,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_ABBREV_SQUARED_LIGATURES_ON = 14,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_ABBREV_SQUARED_LIGATURES_OFF = 15,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_SYMBOL_LIGATURES_ON = 16,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_SYMBOL_LIGATURES_OFF = 17,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_LIGATURES_ON = 18,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_LIGATURES_OFF = 19,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_HISTORICAL_LIGATURES_ON = 20,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_HISTORICAL_LIGATURES_OFF = 21,
|
||||
|
||||
/* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES */
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_UNCONNECTED = 0,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_PARTIALLY_CONNECTED = 1,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_CURSIVE = 2,
|
||||
|
||||
/* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_LETTER_CASE */
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_UPPER_AND_LOWER_CASE = 0, /* deprecated */
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_ALL_CAPS = 1, /* deprecated */
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_ALL_LOWER_CASE = 2, /* deprecated */
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_SMALL_CAPS = 3, /* deprecated */
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_INITIAL_CAPS = 4, /* deprecated */
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_INITIAL_CAPS_AND_SMALL_CAPS = 5, /* deprecated */
|
||||
|
||||
/* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_SUBSTITUTION */
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_SUBSTITUTE_VERTICAL_FORMS_ON = 0,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_SUBSTITUTE_VERTICAL_FORMS_OFF = 1,
|
||||
|
||||
/* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_LINGUISTIC_REARRANGEMENT */
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_LINGUISTIC_REARRANGEMENT_ON = 0,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_LINGUISTIC_REARRANGEMENT_OFF = 1,
|
||||
|
||||
/* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_NUMBER_SPACING */
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_MONOSPACED_NUMBERS = 0,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_PROPORTIONAL_NUMBERS = 1,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_THIRD_WIDTH_NUMBERS = 2,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_QUARTER_WIDTH_NUMBERS = 3,
|
||||
|
||||
/* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_SMART_SWASH_TYPE */
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_WORD_INITIAL_SWASHES_ON = 0,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_WORD_INITIAL_SWASHES_OFF = 1,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_WORD_FINAL_SWASHES_ON = 2,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_WORD_FINAL_SWASHES_OFF = 3,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_LINE_INITIAL_SWASHES_ON = 4,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_LINE_INITIAL_SWASHES_OFF = 5,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_LINE_FINAL_SWASHES_ON = 6,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_LINE_FINAL_SWASHES_OFF = 7,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_NON_FINAL_SWASHES_ON = 8,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_NON_FINAL_SWASHES_OFF = 9,
|
||||
|
||||
/* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_DIACRITICS_TYPE */
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_SHOW_DIACRITICS = 0,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_HIDE_DIACRITICS = 1,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_DECOMPOSE_DIACRITICS = 2,
|
||||
|
||||
/* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_POSITION */
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_NORMAL_POSITION = 0,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_SUPERIORS = 1,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_INFERIORS = 2,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_ORDINALS = 3,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_SCIENTIFIC_INFERIORS = 4,
|
||||
|
||||
/* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_FRACTIONS */
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_FRACTIONS = 0,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_VERTICAL_FRACTIONS = 1,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_DIAGONAL_FRACTIONS = 2,
|
||||
|
||||
/* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_OVERLAPPING_CHARACTERS_TYPE */
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_PREVENT_OVERLAP_ON = 0,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_PREVENT_OVERLAP_OFF = 1,
|
||||
|
||||
/* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_TYPOGRAPHIC_EXTRAS */
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_HYPHENS_TO_EM_DASH_ON = 0,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_HYPHENS_TO_EM_DASH_OFF = 1,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_HYPHEN_TO_EN_DASH_ON = 2,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_HYPHEN_TO_EN_DASH_OFF = 3,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_SLASHED_ZERO_ON = 4,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_SLASHED_ZERO_OFF = 5,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_FORM_INTERROBANG_ON = 6,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_FORM_INTERROBANG_OFF = 7,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_SMART_QUOTES_ON = 8,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_SMART_QUOTES_OFF = 9,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_PERIODS_TO_ELLIPSIS_ON = 10,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_PERIODS_TO_ELLIPSIS_OFF = 11,
|
||||
|
||||
/* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_MATHEMATICAL_EXTRAS */
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_HYPHEN_TO_MINUS_ON = 0,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_HYPHEN_TO_MINUS_OFF = 1,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_ASTERISK_TO_MULTIPLY_ON = 2,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_ASTERISK_TO_MULTIPLY_OFF = 3,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_SLASH_TO_DIVIDE_ON = 4,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_SLASH_TO_DIVIDE_OFF = 5,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_INEQUALITY_LIGATURES_ON = 6,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_INEQUALITY_LIGATURES_OFF = 7,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_EXPONENTS_ON = 8,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_EXPONENTS_OFF = 9,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_MATHEMATICAL_GREEK_ON = 10,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_MATHEMATICAL_GREEK_OFF = 11,
|
||||
|
||||
/* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_ORNAMENT_SETS_TYPE */
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_ORNAMENTS = 0,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_DINGBATS = 1,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_PI_CHARACTERS = 2,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_FLEURONS = 3,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_DECORATIVE_BORDERS = 4,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_INTERNATIONAL_SYMBOLS = 5,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_MATH_SYMBOLS = 6,
|
||||
|
||||
/* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_ALTERNATIVES */
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_ALTERNATES = 0,
|
||||
|
||||
/* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_DESIGN_COMPLEXITY_TYPE */
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_DESIGN_LEVEL1 = 0,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_DESIGN_LEVEL2 = 1,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_DESIGN_LEVEL3 = 2,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_DESIGN_LEVEL4 = 3,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_DESIGN_LEVEL5 = 4,
|
||||
|
||||
/* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLE_OPTIONS */
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_STYLE_OPTIONS = 0,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_DISPLAY_TEXT = 1,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_ENGRAVED_TEXT = 2,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_ILLUMINATED_CAPS = 3,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_TITLING_CAPS = 4,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_TALL_CAPS = 5,
|
||||
|
||||
/* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE */
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_TRADITIONAL_CHARACTERS = 0,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_SIMPLIFIED_CHARACTERS = 1,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_JIS1978_CHARACTERS = 2,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_JIS1983_CHARACTERS = 3,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_JIS1990_CHARACTERS = 4,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_TRADITIONAL_ALT_ONE = 5,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_TRADITIONAL_ALT_TWO = 6,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_TRADITIONAL_ALT_THREE = 7,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_TRADITIONAL_ALT_FOUR = 8,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_TRADITIONAL_ALT_FIVE = 9,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_EXPERT_CHARACTERS = 10,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_JIS2004_CHARACTERS = 11,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_HOJO_CHARACTERS = 12,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_NLCCHARACTERS = 13,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_TRADITIONAL_NAMES_CHARACTERS = 14,
|
||||
|
||||
/* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_NUMBER_CASE */
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_LOWER_CASE_NUMBERS = 0,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_UPPER_CASE_NUMBERS = 1,
|
||||
|
||||
/* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING */
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_PROPORTIONAL_TEXT = 0,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_MONOSPACED_TEXT = 1,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_HALF_WIDTH_TEXT = 2,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_THIRD_WIDTH_TEXT = 3,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_QUARTER_WIDTH_TEXT = 4,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_ALT_PROPORTIONAL_TEXT = 5,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_ALT_HALF_WIDTH_TEXT = 6,
|
||||
|
||||
/* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_TRANSLITERATION */
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_TRANSLITERATION = 0,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_HANJA_TO_HANGUL = 1,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_HIRAGANA_TO_KATAKANA = 2,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_KATAKANA_TO_HIRAGANA = 3,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_KANA_TO_ROMANIZATION = 4,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_ROMANIZATION_TO_HIRAGANA = 5,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_ROMANIZATION_TO_KATAKANA = 6,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_HANJA_TO_HANGUL_ALT_ONE = 7,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_HANJA_TO_HANGUL_ALT_TWO = 8,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_HANJA_TO_HANGUL_ALT_THREE = 9,
|
||||
|
||||
/* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_ANNOTATION_TYPE */
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_ANNOTATION = 0,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_BOX_ANNOTATION = 1,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_ROUNDED_BOX_ANNOTATION = 2,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_CIRCLE_ANNOTATION = 3,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_INVERTED_CIRCLE_ANNOTATION = 4,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_PARENTHESIS_ANNOTATION = 5,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_PERIOD_ANNOTATION = 6,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_ROMAN_NUMERAL_ANNOTATION = 7,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_DIAMOND_ANNOTATION = 8,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_INVERTED_BOX_ANNOTATION = 9,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_INVERTED_ROUNDED_BOX_ANNOTATION= 10,
|
||||
|
||||
/* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_KANA_SPACING_TYPE */
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_FULL_WIDTH_KANA = 0,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_PROPORTIONAL_KANA = 1,
|
||||
|
||||
/* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_IDEOGRAPHIC_SPACING_TYPE */
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_FULL_WIDTH_IDEOGRAPHS = 0,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_PROPORTIONAL_IDEOGRAPHS = 1,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_HALF_WIDTH_IDEOGRAPHS = 2,
|
||||
|
||||
/* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_UNICODE_DECOMPOSITION_TYPE */
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_CANONICAL_COMPOSITION_ON = 0,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_CANONICAL_COMPOSITION_OFF = 1,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_COMPATIBILITY_COMPOSITION_ON = 2,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_COMPATIBILITY_COMPOSITION_OFF = 3,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_TRANSCODING_COMPOSITION_ON = 4,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_TRANSCODING_COMPOSITION_OFF = 5,
|
||||
|
||||
/* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_RUBY_KANA */
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_RUBY_KANA = 0, /* deprecated - use HB_AAT_LAYOUT_FEATURE_SELECTOR_RUBY_KANA_OFF instead */
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_RUBY_KANA = 1, /* deprecated - use HB_AAT_LAYOUT_FEATURE_SELECTOR_RUBY_KANA_ON instead */
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_RUBY_KANA_ON = 2,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_RUBY_KANA_OFF = 3,
|
||||
|
||||
/* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_CJK_SYMBOL_ALTERNATIVES_TYPE */
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_CJK_SYMBOL_ALTERNATIVES = 0,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_SYMBOL_ALT_ONE = 1,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_SYMBOL_ALT_TWO = 2,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_SYMBOL_ALT_THREE = 3,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_SYMBOL_ALT_FOUR = 4,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_SYMBOL_ALT_FIVE = 5,
|
||||
|
||||
/* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_IDEOGRAPHIC_ALTERNATIVES_TYPE */
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_IDEOGRAPHIC_ALTERNATIVES = 0,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_IDEOGRAPHIC_ALT_ONE = 1,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_IDEOGRAPHIC_ALT_TWO = 2,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_IDEOGRAPHIC_ALT_THREE = 3,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_IDEOGRAPHIC_ALT_FOUR = 4,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_IDEOGRAPHIC_ALT_FIVE = 5,
|
||||
|
||||
/* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_CJK_VERTICAL_ROMAN_PLACEMENT_TYPE */
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_VERTICAL_ROMAN_CENTERED = 0,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_VERTICAL_ROMAN_HBASELINE = 1,
|
||||
|
||||
/* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_ITALIC_CJK_ROMAN */
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_CJK_ITALIC_ROMAN = 0, /* deprecated - use HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_ITALIC_ROMAN_OFF instead */
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_ITALIC_ROMAN = 1, /* deprecated - use HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_ITALIC_ROMAN_ON instead */
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_ITALIC_ROMAN_ON = 2,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_ITALIC_ROMAN_OFF = 3,
|
||||
|
||||
/* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_CASE_SENSITIVE_LAYOUT */
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_CASE_SENSITIVE_LAYOUT_ON = 0,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_CASE_SENSITIVE_LAYOUT_OFF = 1,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_CASE_SENSITIVE_SPACING_ON = 2,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_CASE_SENSITIVE_SPACING_OFF = 3,
|
||||
|
||||
/* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_ALTERNATE_KANA */
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_ALTERNATE_HORIZ_KANA_ON = 0,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_ALTERNATE_HORIZ_KANA_OFF = 1,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_ALTERNATE_VERT_KANA_ON = 2,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_ALTERNATE_VERT_KANA_OFF = 3,
|
||||
|
||||
/* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES */
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_STYLISTIC_ALTERNATES = 0,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_ONE_ON = 2,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_ONE_OFF = 3,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWO_ON = 4,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWO_OFF = 5,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_THREE_ON = 6,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_THREE_OFF = 7,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FOUR_ON = 8,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FOUR_OFF = 9,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FIVE_ON = 10,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FIVE_OFF = 11,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SIX_ON = 12,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SIX_OFF = 13,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SEVEN_ON = 14,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SEVEN_OFF = 15,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_EIGHT_ON = 16,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_EIGHT_OFF = 17,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_NINE_ON = 18,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_NINE_OFF = 19,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TEN_ON = 20,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TEN_OFF = 21,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_ELEVEN_ON = 22,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_ELEVEN_OFF = 23,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWELVE_ON = 24,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWELVE_OFF = 25,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_THIRTEEN_ON = 26,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_THIRTEEN_OFF = 27,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FOURTEEN_ON = 28,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FOURTEEN_OFF = 29,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FIFTEEN_ON = 30,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FIFTEEN_OFF = 31,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SIXTEEN_ON = 32,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SIXTEEN_OFF = 33,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SEVENTEEN_ON = 34,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SEVENTEEN_OFF = 35,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_EIGHTEEN_ON = 36,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_EIGHTEEN_OFF = 37,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_NINETEEN_ON = 38,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_NINETEEN_OFF = 39,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWENTY_ON = 40,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWENTY_OFF = 41,
|
||||
|
||||
/* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_CONTEXTUAL_ALTERNATIVES */
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_ALTERNATES_ON = 0,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_ALTERNATES_OFF = 1,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_SWASH_ALTERNATES_ON = 2,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_SWASH_ALTERNATES_OFF = 3,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_SWASH_ALTERNATES_ON = 4,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_SWASH_ALTERNATES_OFF= 5,
|
||||
|
||||
/* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_LOWER_CASE */
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_DEFAULT_LOWER_CASE = 0,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_LOWER_CASE_SMALL_CAPS = 1,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_LOWER_CASE_PETITE_CAPS = 2,
|
||||
|
||||
/* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_UPPER_CASE */
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_DEFAULT_UPPER_CASE = 0,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_UPPER_CASE_SMALL_CAPS = 1,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_UPPER_CASE_PETITE_CAPS = 2,
|
||||
|
||||
/* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_CJK_ROMAN_SPACING_TYPE */
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_HALF_WIDTH_CJK_ROMAN = 0,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_PROPORTIONAL_CJK_ROMAN = 1,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_DEFAULT_CJK_ROMAN = 2,
|
||||
HB_AAT_LAYOUT_FEATURE_SELECTOR_FULL_WIDTH_CJK_ROMAN = 3,
|
||||
|
||||
/*< private >*/
|
||||
_HB_AAT_LAYOUT_FEATURE_SELECTOR_MAX_VALUE = HB_TAG_MAX_SIGNED /*< skip >*/
|
||||
} hb_aat_layout_feature_selector_t;
|
||||
|
||||
HB_EXTERN unsigned int
|
||||
hb_aat_layout_get_feature_types (hb_face_t *face,
|
||||
unsigned int start_offset,
|
||||
unsigned int *feature_count, /* IN/OUT. May be NULL. */
|
||||
hb_aat_layout_feature_type_t *features /* OUT. May be NULL. */);
|
||||
|
||||
HB_EXTERN hb_ot_name_id_t
|
||||
hb_aat_layout_feature_type_get_name_id (hb_face_t *face,
|
||||
hb_aat_layout_feature_type_t feature_type);
|
||||
|
||||
/**
|
||||
* hb_aat_layout_feature_selector_info_t:
|
||||
* @name_id: The selector's name identifier
|
||||
* @enable: The value to turn the selector on
|
||||
* @disable: The value to turn the selector off
|
||||
*
|
||||
* Structure representing a setting for an #hb_aat_layout_feature_type_t.
|
||||
*/
|
||||
typedef struct hb_aat_layout_feature_selector_info_t {
|
||||
hb_ot_name_id_t name_id;
|
||||
hb_aat_layout_feature_selector_t enable;
|
||||
hb_aat_layout_feature_selector_t disable;
|
||||
/*< private >*/
|
||||
unsigned int reserved;
|
||||
} hb_aat_layout_feature_selector_info_t;
|
||||
|
||||
/**
|
||||
* HB_AAT_LAYOUT_NO_SELECTOR_INDEX
|
||||
*
|
||||
* Used when getting or setting AAT feature selectors. Indicates that
|
||||
* there is no selector index corresponding to the selector of interest.
|
||||
*
|
||||
*/
|
||||
#define HB_AAT_LAYOUT_NO_SELECTOR_INDEX 0xFFFFu
|
||||
|
||||
HB_EXTERN unsigned int
|
||||
hb_aat_layout_feature_type_get_selector_infos (hb_face_t *face,
|
||||
hb_aat_layout_feature_type_t feature_type,
|
||||
unsigned int start_offset,
|
||||
unsigned int *selector_count, /* IN/OUT. May be NULL. */
|
||||
hb_aat_layout_feature_selector_info_t *selectors, /* OUT. May be NULL. */
|
||||
unsigned int *default_index /* OUT. May be NULL. */);
|
||||
|
||||
|
||||
/*
|
||||
* morx/mort
|
||||
*/
|
||||
|
||||
HB_EXTERN hb_bool_t
|
||||
hb_aat_layout_has_substitution (hb_face_t *face);
|
||||
|
||||
|
||||
/*
|
||||
* kerx
|
||||
*/
|
||||
|
||||
HB_EXTERN hb_bool_t
|
||||
hb_aat_layout_has_positioning (hb_face_t *face);
|
||||
|
||||
|
||||
/*
|
||||
* trak
|
||||
*/
|
||||
|
||||
HB_EXTERN hb_bool_t
|
||||
hb_aat_layout_has_tracking (hb_face_t *face);
|
||||
|
||||
|
||||
HB_END_DECLS
|
||||
|
||||
#endif /* HB_AAT_LAYOUT_H */
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright © 2017 Google, Inc.
|
||||
*
|
||||
* This is part of HarfBuzz, a text shaping library.
|
||||
*
|
||||
* Permission is hereby granted, without written agreement and without
|
||||
* license or royalty fees, to use, copy, modify, and distribute this
|
||||
* software and its documentation for any purpose, provided that the
|
||||
* above copyright notice and the following two paragraphs appear in
|
||||
* all copies of this software.
|
||||
*
|
||||
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
|
||||
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
|
||||
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
|
||||
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||
* DAMAGE.
|
||||
*
|
||||
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
|
||||
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
|
||||
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
|
||||
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
|
||||
*
|
||||
* Google Author(s): Behdad Esfahbod
|
||||
*/
|
||||
|
||||
#ifndef HB_AAT_LAYOUT_HH
|
||||
#define HB_AAT_LAYOUT_HH
|
||||
|
||||
#include "hb.hh"
|
||||
|
||||
#include "hb-ot-shape.hh"
|
||||
#include "hb-aat-ltag-table.hh"
|
||||
|
||||
/* https://developer.apple.com/documentation/coretext/1508745-ctfontcreatewithgraphicsfont */
|
||||
#define HB_CORETEXT_DEFAULT_FONT_SIZE 12.f
|
||||
|
||||
struct hb_aat_feature_mapping_t
|
||||
{
|
||||
hb_tag_t otFeatureTag;
|
||||
hb_aat_layout_feature_type_t aatFeatureType;
|
||||
hb_aat_layout_feature_selector_t selectorToEnable;
|
||||
hb_aat_layout_feature_selector_t selectorToDisable;
|
||||
|
||||
int cmp (hb_tag_t key) const
|
||||
{ return key < otFeatureTag ? -1 : key > otFeatureTag ? 1 : 0; }
|
||||
};
|
||||
|
||||
HB_INTERNAL const hb_aat_feature_mapping_t *
|
||||
hb_aat_layout_find_feature_mapping (hb_tag_t tag);
|
||||
|
||||
HB_INTERNAL void
|
||||
hb_aat_layout_compile_map (const hb_aat_map_builder_t *mapper,
|
||||
hb_aat_map_t *map);
|
||||
|
||||
HB_INTERNAL void
|
||||
hb_aat_layout_substitute (const hb_ot_shape_plan_t *plan,
|
||||
hb_font_t *font,
|
||||
hb_buffer_t *buffer,
|
||||
const hb_feature_t *features,
|
||||
unsigned num_features);
|
||||
|
||||
HB_INTERNAL void
|
||||
hb_aat_layout_remove_deleted_glyphs (hb_buffer_t *buffer);
|
||||
|
||||
HB_INTERNAL void
|
||||
hb_aat_layout_position (const hb_ot_shape_plan_t *plan,
|
||||
hb_font_t *font,
|
||||
hb_buffer_t *buffer);
|
||||
|
||||
HB_INTERNAL void
|
||||
hb_aat_layout_track (const hb_ot_shape_plan_t *plan,
|
||||
hb_font_t *font,
|
||||
hb_buffer_t *buffer);
|
||||
|
||||
|
||||
#endif /* HB_AAT_LAYOUT_HH */
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright © 2018 Ebrahim Byagowi
|
||||
*
|
||||
* This is part of HarfBuzz, a text shaping library.
|
||||
*
|
||||
* Permission is hereby granted, without written agreement and without
|
||||
* license or royalty fees, to use, copy, modify, and distribute this
|
||||
* software and its documentation for any purpose, provided that the
|
||||
* above copyright notice and the following two paragraphs appear in
|
||||
* all copies of this software.
|
||||
*
|
||||
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
|
||||
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
|
||||
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
|
||||
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||
* DAMAGE.
|
||||
*
|
||||
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
|
||||
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
|
||||
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
|
||||
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
|
||||
*/
|
||||
|
||||
#ifndef HB_AAT_LTAG_TABLE_HH
|
||||
#define HB_AAT_LTAG_TABLE_HH
|
||||
|
||||
#include "hb-open-type.hh"
|
||||
|
||||
/*
|
||||
* ltag -- Language Tag
|
||||
* https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6ltag.html
|
||||
*/
|
||||
#define HB_AAT_TAG_ltag HB_TAG('l','t','a','g')
|
||||
|
||||
|
||||
namespace AAT {
|
||||
|
||||
using namespace OT;
|
||||
|
||||
|
||||
struct FTStringRange
|
||||
{
|
||||
friend struct ltag;
|
||||
|
||||
bool sanitize (hb_sanitize_context_t *c, const void *base) const
|
||||
{
|
||||
TRACE_SANITIZE (this);
|
||||
return_trace (c->check_struct (this) &&
|
||||
hb_barrier () &&
|
||||
(base+tag).sanitize (c, length));
|
||||
}
|
||||
|
||||
protected:
|
||||
NNOffset16To<UnsizedArrayOf<HBUINT8>>
|
||||
tag; /* Offset from the start of the table to
|
||||
* the beginning of the string */
|
||||
HBUINT16 length; /* String length (in bytes) */
|
||||
public:
|
||||
DEFINE_SIZE_STATIC (4);
|
||||
};
|
||||
|
||||
struct ltag
|
||||
{
|
||||
static constexpr hb_tag_t tableTag = HB_AAT_TAG_ltag;
|
||||
|
||||
hb_language_t get_language (unsigned int i) const
|
||||
{
|
||||
const FTStringRange &range = tagRanges[i];
|
||||
return hb_language_from_string ((const char *) (this+range.tag).arrayZ,
|
||||
range.length);
|
||||
}
|
||||
|
||||
bool sanitize (hb_sanitize_context_t *c) const
|
||||
{
|
||||
TRACE_SANITIZE (this);
|
||||
return_trace (likely (c->check_struct (this) &&
|
||||
hb_barrier () &&
|
||||
version >= 1 &&
|
||||
tagRanges.sanitize (c, this)));
|
||||
}
|
||||
|
||||
protected:
|
||||
HBUINT32 version; /* Table version; currently 1 */
|
||||
HBUINT32 flags; /* Table flags; currently none defined */
|
||||
Array32Of<FTStringRange>
|
||||
tagRanges; /* Range for each tag's string */
|
||||
public:
|
||||
DEFINE_SIZE_ARRAY (12, tagRanges);
|
||||
};
|
||||
|
||||
} /* namespace AAT */
|
||||
|
||||
|
||||
#endif /* HB_AAT_LTAG_TABLE_HH */
|
||||
Vendored
+123
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright © 2018 Google, Inc.
|
||||
*
|
||||
* This is part of HarfBuzz, a text shaping library.
|
||||
*
|
||||
* Permission is hereby granted, without written agreement and without
|
||||
* license or royalty fees, to use, copy, modify, and distribute this
|
||||
* software and its documentation for any purpose, provided that the
|
||||
* above copyright notice and the following two paragraphs appear in
|
||||
* all copies of this software.
|
||||
*
|
||||
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
|
||||
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
|
||||
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
|
||||
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||
* DAMAGE.
|
||||
*
|
||||
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
|
||||
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
|
||||
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
|
||||
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
|
||||
*
|
||||
* Google Author(s): Behdad Esfahbod
|
||||
*/
|
||||
|
||||
#ifndef HB_AAT_MAP_HH
|
||||
#define HB_AAT_MAP_HH
|
||||
|
||||
#include "hb.hh"
|
||||
|
||||
|
||||
struct hb_aat_map_t
|
||||
{
|
||||
friend struct hb_aat_map_builder_t;
|
||||
|
||||
public:
|
||||
struct range_flags_t
|
||||
{
|
||||
hb_mask_t flags;
|
||||
unsigned cluster_first;
|
||||
unsigned cluster_last; // end - 1
|
||||
};
|
||||
|
||||
public:
|
||||
hb_vector_t<hb_sorted_vector_t<range_flags_t>> chain_flags;
|
||||
};
|
||||
|
||||
struct hb_aat_map_builder_t
|
||||
{
|
||||
public:
|
||||
|
||||
HB_INTERNAL hb_aat_map_builder_t (hb_face_t *face_,
|
||||
const hb_segment_properties_t props_) :
|
||||
face (face_),
|
||||
props (props_) {}
|
||||
|
||||
HB_INTERNAL void add_feature (const hb_feature_t &feature);
|
||||
|
||||
HB_INTERNAL void compile (hb_aat_map_t &m);
|
||||
|
||||
public:
|
||||
struct feature_info_t
|
||||
{
|
||||
hb_aat_layout_feature_type_t type;
|
||||
hb_aat_layout_feature_selector_t setting;
|
||||
bool is_exclusive;
|
||||
unsigned seq; /* For stable sorting only. */
|
||||
|
||||
HB_INTERNAL static int cmp (const void *pa, const void *pb)
|
||||
{
|
||||
const feature_info_t *a = (const feature_info_t *) pa;
|
||||
const feature_info_t *b = (const feature_info_t *) pb;
|
||||
if (a->type != b->type) return (a->type < b->type ? -1 : 1);
|
||||
if (!a->is_exclusive &&
|
||||
(a->setting & ~1) != (b->setting & ~1)) return (a->setting < b->setting ? -1 : 1);
|
||||
return (a->seq < b->seq ? -1 : a->seq > b->seq ? 1 : 0);
|
||||
}
|
||||
|
||||
/* compares type & setting only */
|
||||
int cmp (const feature_info_t& f) const
|
||||
{
|
||||
return (f.type != type) ? (f.type < type ? -1 : 1) :
|
||||
(f.setting != setting) ? (f.setting < setting ? -1 : 1) : 0;
|
||||
}
|
||||
};
|
||||
|
||||
struct feature_range_t
|
||||
{
|
||||
feature_info_t info;
|
||||
unsigned start;
|
||||
unsigned end;
|
||||
};
|
||||
|
||||
private:
|
||||
struct feature_event_t
|
||||
{
|
||||
unsigned int index;
|
||||
bool start;
|
||||
feature_info_t feature;
|
||||
|
||||
HB_INTERNAL static int cmp (const void *pa, const void *pb) {
|
||||
const feature_event_t *a = (const feature_event_t *) pa;
|
||||
const feature_event_t *b = (const feature_event_t *) pb;
|
||||
return a->index < b->index ? -1 : a->index > b->index ? 1 :
|
||||
a->start < b->start ? -1 : a->start > b->start ? 1 :
|
||||
feature_info_t::cmp (&a->feature, &b->feature);
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
hb_face_t *face;
|
||||
hb_segment_properties_t props;
|
||||
|
||||
public:
|
||||
hb_sorted_vector_t<feature_range_t> features;
|
||||
hb_sorted_vector_t<feature_info_t> current_features;
|
||||
unsigned range_first = HB_FEATURE_GLOBAL_START;
|
||||
unsigned range_last = HB_FEATURE_GLOBAL_END;
|
||||
};
|
||||
|
||||
|
||||
#endif /* HB_AAT_MAP_HH */
|
||||
Vendored
+38
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright © 2018 Ebrahim Byagowi
|
||||
*
|
||||
* This is part of HarfBuzz, a text shaping library.
|
||||
*
|
||||
* Permission is hereby granted, without written agreement and without
|
||||
* license or royalty fees, to use, copy, modify, and distribute this
|
||||
* software and its documentation for any purpose, provided that the
|
||||
* above copyright notice and the following two paragraphs appear in
|
||||
* all copies of this software.
|
||||
*
|
||||
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
|
||||
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
|
||||
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
|
||||
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||
* DAMAGE.
|
||||
*
|
||||
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
|
||||
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
|
||||
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
|
||||
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
|
||||
*/
|
||||
|
||||
#ifndef HB_AAT_H
|
||||
#define HB_AAT_H
|
||||
#define HB_AAT_H_IN
|
||||
|
||||
#include "hb.h"
|
||||
|
||||
#include "hb-aat-layout.h"
|
||||
|
||||
HB_BEGIN_DECLS
|
||||
|
||||
HB_END_DECLS
|
||||
|
||||
#undef HB_AAT_H_IN
|
||||
#endif /* HB_AAT_H */
|
||||
Vendored
+1723
File diff suppressed because it is too large
Load Diff
+105
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* This is part of HarfBuzz, a text shaping library.
|
||||
*
|
||||
* Permission is hereby granted, without written agreement and without
|
||||
* license or royalty fees, to use, copy, modify, and distribute this
|
||||
* software and its documentation for any purpose, provided that the
|
||||
* above copyright notice and the following two paragraphs appear in
|
||||
* all copies of this software.
|
||||
*
|
||||
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
|
||||
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
|
||||
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
|
||||
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||
* DAMAGE.
|
||||
*
|
||||
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
|
||||
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
|
||||
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
|
||||
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
|
||||
*
|
||||
* Author(s): Behdad Esfahbod
|
||||
*/
|
||||
|
||||
#ifndef HB_ALLOC_POOL_HH
|
||||
#define HB_ALLOC_POOL_HH
|
||||
|
||||
#include "hb-vector.hh"
|
||||
|
||||
/* Memory pool for persistent small- to medium-sized allocations.
|
||||
*
|
||||
* Some AI musings on this, not necessarily true:
|
||||
*
|
||||
* This is a very simple implementation, but it's good enough for our
|
||||
* purposes. It's not thread-safe. It's not very fast. It's not
|
||||
* very memory efficient. It's not very cache efficient. It's not
|
||||
* very anything efficient. But it's simple and it works. And it's
|
||||
* good enough for our purposes. If you need something more
|
||||
* sophisticated, use a real allocator. Or use a real language. */
|
||||
|
||||
struct hb_alloc_pool_t
|
||||
{
|
||||
unsigned ChunkSize = 65536 - 2 * sizeof (void *);
|
||||
|
||||
void *alloc (size_t size, unsigned alignment = 2 * sizeof (void *))
|
||||
{
|
||||
if (unlikely (chunks.in_error ())) return nullptr;
|
||||
|
||||
assert (alignment > 0);
|
||||
assert (alignment <= 2 * sizeof (void *));
|
||||
assert ((alignment & (alignment - 1)) == 0); /* power of two */
|
||||
|
||||
if (size > (ChunkSize) / 4)
|
||||
{
|
||||
/* Big chunk, allocate separately. */
|
||||
hb_vector_t<char> chunk;
|
||||
if (unlikely (!chunk.resize (size))) return nullptr;
|
||||
void *ret = chunk.arrayZ;
|
||||
chunks.push (std::move (chunk));
|
||||
if (chunks.in_error ()) return nullptr;
|
||||
if (chunks.length > 1)
|
||||
{
|
||||
// Bring back the previous last chunk to the end, so that
|
||||
// we can continue to allocate from it.
|
||||
hb_swap (chunks.arrayZ[chunks.length - 1], chunks.arrayZ[chunks.length - 2]);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
unsigned pad = (unsigned) ((alignment - ((uintptr_t) current_chunk.arrayZ & (alignment - 1))) & (alignment - 1));
|
||||
|
||||
// Small chunk, allocate from the last chunk.
|
||||
if (current_chunk.length < pad + size)
|
||||
{
|
||||
chunks.push ();
|
||||
if (unlikely (chunks.in_error ())) return nullptr;
|
||||
hb_vector_t<char> &chunk = chunks.arrayZ[chunks.length - 1];
|
||||
if (unlikely (!chunk.resize (ChunkSize))) return nullptr;
|
||||
current_chunk = chunk;
|
||||
pad = (unsigned) ((alignment - ((uintptr_t) current_chunk.arrayZ & (alignment - 1))) & (alignment - 1));
|
||||
}
|
||||
|
||||
current_chunk += pad;
|
||||
|
||||
assert (current_chunk.length >= size);
|
||||
void *ret = current_chunk.arrayZ;
|
||||
current_chunk += size;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void discard (void *p_, size_t size)
|
||||
{
|
||||
// Reclaim memory if we can.
|
||||
char *p = (char *) p_;
|
||||
if (current_chunk.arrayZ == p + size && current_chunk.backwards_length >= size)
|
||||
current_chunk -= size;
|
||||
}
|
||||
|
||||
private:
|
||||
hb_vector_t<hb_vector_t<char>> chunks;
|
||||
hb_array_t<char> current_chunk;
|
||||
};
|
||||
|
||||
|
||||
#endif /* HB_ALLOC_POOL_HH */
|
||||
Vendored
+532
@@ -0,0 +1,532 @@
|
||||
/*
|
||||
* Copyright © 2018 Google, Inc.
|
||||
*
|
||||
* This is part of HarfBuzz, a text shaping library.
|
||||
*
|
||||
* Permission is hereby granted, without written agreement and without
|
||||
* license or royalty fees, to use, copy, modify, and distribute this
|
||||
* software and its documentation for any purpose, provided that the
|
||||
* above copyright notice and the following two paragraphs appear in
|
||||
* all copies of this software.
|
||||
*
|
||||
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
|
||||
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
|
||||
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
|
||||
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||
* DAMAGE.
|
||||
*
|
||||
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
|
||||
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
|
||||
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
|
||||
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
|
||||
*
|
||||
* Google Author(s): Behdad Esfahbod
|
||||
*/
|
||||
|
||||
#ifndef HB_ARRAY_HH
|
||||
#define HB_ARRAY_HH
|
||||
|
||||
#include "hb.hh"
|
||||
#include "hb-algs.hh"
|
||||
#include "hb-iter.hh"
|
||||
#include "hb-null.hh"
|
||||
|
||||
|
||||
template <typename Type>
|
||||
struct hb_sorted_array_t;
|
||||
|
||||
enum hb_not_found_t
|
||||
{
|
||||
HB_NOT_FOUND_DONT_STORE,
|
||||
HB_NOT_FOUND_STORE,
|
||||
HB_NOT_FOUND_STORE_CLOSEST,
|
||||
};
|
||||
|
||||
|
||||
template <typename Type>
|
||||
struct hb_array_t : hb_iter_with_fallback_t<hb_array_t<Type>, Type&>
|
||||
{
|
||||
static constexpr bool realloc_move = true;
|
||||
|
||||
/*
|
||||
* Constructors.
|
||||
*/
|
||||
hb_array_t () = default;
|
||||
hb_array_t (const hb_array_t&) = default;
|
||||
~hb_array_t () = default;
|
||||
hb_array_t& operator= (const hb_array_t&) = default;
|
||||
hb_array_t& operator= (hb_array_t&&) = default;
|
||||
|
||||
constexpr hb_array_t (Type *array_, unsigned int length_) : arrayZ (array_), length (length_) {}
|
||||
template <unsigned int length_>
|
||||
constexpr hb_array_t (Type (&array_)[length_]) : hb_array_t (array_, length_) {}
|
||||
|
||||
template <typename U,
|
||||
hb_enable_if (hb_is_cr_convertible(U, Type))>
|
||||
constexpr 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; }
|
||||
|
||||
/*
|
||||
* Iterator implementation.
|
||||
*/
|
||||
typedef Type& __item_t__;
|
||||
static constexpr bool is_random_access_iterator = true;
|
||||
static constexpr bool has_fast_len = true;
|
||||
Type& __item__ () const
|
||||
{
|
||||
if (unlikely (!length)) return CrapOrNull (Type);
|
||||
return *arrayZ;
|
||||
}
|
||||
Type& __item_at__ (unsigned i) const
|
||||
{
|
||||
if (unlikely (i >= length)) return CrapOrNull (Type);
|
||||
return arrayZ[i];
|
||||
}
|
||||
void __next__ ()
|
||||
{
|
||||
if (unlikely (!length))
|
||||
return;
|
||||
length--;
|
||||
backwards_length++;
|
||||
arrayZ++;
|
||||
}
|
||||
void __forward__ (unsigned n)
|
||||
{
|
||||
if (unlikely (n > length))
|
||||
n = length;
|
||||
length -= n;
|
||||
backwards_length += n;
|
||||
arrayZ += n;
|
||||
}
|
||||
void __prev__ ()
|
||||
{
|
||||
if (unlikely (!backwards_length))
|
||||
return;
|
||||
length++;
|
||||
backwards_length--;
|
||||
arrayZ--;
|
||||
}
|
||||
void __rewind__ (unsigned n)
|
||||
{
|
||||
if (unlikely (n > backwards_length))
|
||||
n = backwards_length;
|
||||
length += n;
|
||||
backwards_length -= n;
|
||||
arrayZ -= n;
|
||||
}
|
||||
unsigned __len__ () const { return length; }
|
||||
/* Ouch. The operator== compares the contents of the array. For range-based for loops,
|
||||
* it's best if we can just compare arrayZ, though comparing contents is still fast,
|
||||
* but also would require that Type has operator==. As such, we optimize this operator
|
||||
* for range-based for loop and just compare arrayZ and length.
|
||||
*
|
||||
* The above comment is outdated now because we implemented separate begin/end to
|
||||
* objects that were using hb_array_t for range-based loop before. */
|
||||
bool operator != (const hb_array_t& o) const
|
||||
{ return this->arrayZ != o.arrayZ || this->length != o.length; }
|
||||
|
||||
/* Faster range-based for loop without bounds-check. */
|
||||
Type *begin () const { return arrayZ; }
|
||||
Type *end () const { return arrayZ + length; }
|
||||
|
||||
|
||||
/* Extra operators.
|
||||
*/
|
||||
Type * operator & () const { return arrayZ; }
|
||||
operator hb_array_t<const Type> () { return hb_array_t<const Type> (arrayZ, length); }
|
||||
template <typename T> operator T * () const { return arrayZ; }
|
||||
|
||||
HB_INTERNAL bool operator == (const hb_array_t &o) const;
|
||||
|
||||
uint32_t hash () const
|
||||
{
|
||||
// FNV-1a hash function
|
||||
// https://github.com/harfbuzz/harfbuzz/pull/4228
|
||||
uint32_t current = /*cbf29ce4*/0x84222325;
|
||||
for (auto &v : *this)
|
||||
{
|
||||
current = current ^ hb_hash (v);
|
||||
current = current * 16777619;
|
||||
}
|
||||
return current;
|
||||
}
|
||||
|
||||
/*
|
||||
* Compare, Sort, and Search.
|
||||
*/
|
||||
|
||||
/* Note: our compare is NOT lexicographic; it also does NOT call Type::cmp. */
|
||||
int cmp (const hb_array_t &a) const
|
||||
{
|
||||
if (length != a.length)
|
||||
return (int) a.length - (int) length;
|
||||
return hb_memcmp (a.arrayZ, arrayZ, get_size ());
|
||||
}
|
||||
HB_INTERNAL static int cmp (const void *pa, const void *pb)
|
||||
{
|
||||
hb_array_t *a = (hb_array_t *) pa;
|
||||
hb_array_t *b = (hb_array_t *) pb;
|
||||
return b->cmp (*a);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Type *lsearch (const T &x, Type *not_found = nullptr)
|
||||
{
|
||||
unsigned i;
|
||||
return lfind (x, &i) ? &this->arrayZ[i] : not_found;
|
||||
}
|
||||
template <typename T>
|
||||
const Type *lsearch (const T &x, const Type *not_found = nullptr) const
|
||||
{
|
||||
unsigned i;
|
||||
return lfind (x, &i) ? &this->arrayZ[i] : not_found;
|
||||
}
|
||||
template <typename T>
|
||||
bool lfind (const T &x, unsigned *pos = nullptr,
|
||||
hb_not_found_t not_found = HB_NOT_FOUND_DONT_STORE,
|
||||
unsigned int to_store = (unsigned int) -1) const
|
||||
{
|
||||
for (unsigned i = 0; i < length; ++i)
|
||||
if (hb_equal (x, this->arrayZ[i]))
|
||||
{
|
||||
if (pos)
|
||||
*pos = i;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (pos)
|
||||
{
|
||||
switch (not_found)
|
||||
{
|
||||
case HB_NOT_FOUND_DONT_STORE:
|
||||
break;
|
||||
|
||||
case HB_NOT_FOUND_STORE:
|
||||
*pos = to_store;
|
||||
break;
|
||||
|
||||
case HB_NOT_FOUND_STORE_CLOSEST:
|
||||
*pos = length;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
hb_sorted_array_t<Type> qsort (int (*cmp)(const void*, const void*))
|
||||
{
|
||||
if (likely (length))
|
||||
hb_qsort (arrayZ, length, this->get_item_size (), cmp);
|
||||
return hb_sorted_array_t<Type> (*this);
|
||||
}
|
||||
|
||||
/* Comparator follows the C qsort convention: returns
|
||||
* negative / zero / positive int. */
|
||||
template <typename Compar>
|
||||
hb_sorted_array_t<Type> qsort (Compar compar)
|
||||
{
|
||||
if (likely (length))
|
||||
hb_qsort_inline (arrayZ, length, compar);
|
||||
return hb_sorted_array_t<Type> (*this);
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename T = Type,
|
||||
hb_enable_if (std::is_move_assignable<T>::value)>
|
||||
hb_sorted_array_t<Type> _qsort (hb_priority<1>)
|
||||
{
|
||||
return qsort ([] (const Type &a, const Type &b) { return Type::cmp (&a, &b); });
|
||||
}
|
||||
hb_sorted_array_t<Type> _qsort (hb_priority<0>)
|
||||
{
|
||||
return qsort ((int(*)(const void*, const void*)) Type::cmp);
|
||||
}
|
||||
public:
|
||||
|
||||
hb_sorted_array_t<Type> qsort ()
|
||||
{
|
||||
return _qsort (hb_prioritize);
|
||||
}
|
||||
|
||||
/*
|
||||
* Other methods.
|
||||
*/
|
||||
|
||||
size_t get_size () const { return length * this->get_item_size (); }
|
||||
|
||||
/*
|
||||
* Reverse the order of items in this array in the range [start, end).
|
||||
*/
|
||||
void reverse (unsigned start = 0, unsigned end = -1)
|
||||
{
|
||||
start = hb_min (start, length);
|
||||
end = hb_min (end, length);
|
||||
|
||||
if (end < start + 2)
|
||||
return;
|
||||
|
||||
unsigned stop = start + (end - start) / 2;
|
||||
for (unsigned lhs = start, rhs = end - 1; lhs < stop; lhs++, rhs--)
|
||||
hb_swap (arrayZ[rhs], arrayZ[lhs]);
|
||||
}
|
||||
|
||||
hb_array_t sub_array (unsigned int start_offset = 0, unsigned int *seg_count = nullptr /* IN/OUT */) const
|
||||
{
|
||||
if (!start_offset && !seg_count)
|
||||
return *this;
|
||||
|
||||
unsigned int count = length;
|
||||
if (unlikely (start_offset > count))
|
||||
count = 0;
|
||||
else
|
||||
count -= start_offset;
|
||||
if (seg_count)
|
||||
count = *seg_count = hb_min (count, *seg_count);
|
||||
return hb_array_t (arrayZ + start_offset, count);
|
||||
}
|
||||
hb_array_t sub_array (unsigned int start_offset, unsigned int seg_count) const
|
||||
{ return sub_array (start_offset, &seg_count); }
|
||||
|
||||
hb_array_t truncate (unsigned length) const { return sub_array (0, length); }
|
||||
|
||||
template <typename T,
|
||||
unsigned P = sizeof (Type),
|
||||
hb_enable_if (P == 1)>
|
||||
const T *as () const
|
||||
{ return length < hb_min_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;
|
||||
}
|
||||
|
||||
template <unsigned P = sizeof (Type),
|
||||
hb_enable_if (P == 1)>
|
||||
bool check_end (const void *p) const
|
||||
{
|
||||
return (uintptr_t) (((const char *) p) - arrayZ) <= length;
|
||||
}
|
||||
|
||||
/* Only call if you allocated the underlying array using hb_malloc() or similar. */
|
||||
void fini ()
|
||||
{ hb_free ((void *) arrayZ); arrayZ = nullptr; length = 0; }
|
||||
|
||||
template <typename hb_serialize_context_t,
|
||||
typename U = Type,
|
||||
hb_enable_if (!(sizeof (U) < sizeof (long long) && hb_is_trivially_copy_assignable(hb_decay<Type>)))>
|
||||
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 (), false))) 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_serialize_context_t,
|
||||
typename U = Type,
|
||||
hb_enable_if (sizeof (U) < sizeof (long long) && hb_is_trivially_copy_assignable(hb_decay<Type>))>
|
||||
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 (), false))) return_trace (hb_array_t ());
|
||||
hb_memcpy (out, arrayZ, get_size ());
|
||||
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); }
|
||||
|
||||
/*
|
||||
* Members
|
||||
*/
|
||||
|
||||
public:
|
||||
Type *arrayZ = nullptr;
|
||||
unsigned int length = 0;
|
||||
unsigned int backwards_length = 0;
|
||||
};
|
||||
template <typename T> inline hb_array_t<T>
|
||||
hb_array ()
|
||||
{ return hb_array_t<T> (); }
|
||||
template <typename T> inline hb_array_t<T>
|
||||
hb_array (T *array, unsigned int length)
|
||||
{ return hb_array_t<T> (array, length); }
|
||||
template <typename T, unsigned int length_> inline hb_array_t<T>
|
||||
hb_array (T (&array_)[length_])
|
||||
{ return hb_array_t<T> (array_); }
|
||||
|
||||
template <typename Type>
|
||||
struct hb_sorted_array_t :
|
||||
hb_array_t<Type>,
|
||||
hb_iter_t<hb_sorted_array_t<Type>, Type&>
|
||||
{
|
||||
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;
|
||||
static constexpr bool has_fast_len = true;
|
||||
|
||||
hb_sorted_array_t () = default;
|
||||
hb_sorted_array_t (const hb_sorted_array_t&) = default;
|
||||
~hb_sorted_array_t () = default;
|
||||
hb_sorted_array_t& operator= (const hb_sorted_array_t&) = default;
|
||||
hb_sorted_array_t& operator= (hb_sorted_array_t&&) = default;
|
||||
|
||||
constexpr hb_sorted_array_t (Type *array_, unsigned int length_) : hb_array_t<Type> (array_, length_) {}
|
||||
template <unsigned int length_>
|
||||
constexpr hb_sorted_array_t (Type (&array_)[length_]) : hb_array_t<Type> (array_) {}
|
||||
|
||||
template <typename U,
|
||||
hb_enable_if (hb_is_cr_convertible(U, Type))>
|
||||
constexpr hb_sorted_array_t (const hb_array_t<U> &o) :
|
||||
hb_array_t<Type> (o),
|
||||
hb_iter_t<hb_sorted_array_t, Type&> () {}
|
||||
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. */
|
||||
|
||||
/* See comment in hb_array_of::operator != */
|
||||
bool operator != (const hb_sorted_array_t& o) const
|
||||
{ return this->arrayZ != o.arrayZ || this->length != o.length; }
|
||||
|
||||
/* Faster range-based for loop without bounds-check. */
|
||||
Type *begin () const { return this->arrayZ; }
|
||||
Type *end () const { return this->arrayZ + this->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)
|
||||
{
|
||||
unsigned int i;
|
||||
return bfind (x, &i) ? &this->arrayZ[i] : not_found;
|
||||
}
|
||||
template <typename T>
|
||||
const Type *bsearch (const T &x, const Type *not_found = nullptr) const
|
||||
{
|
||||
unsigned int i;
|
||||
return bfind (x, &i) ? &this->arrayZ[i] : not_found;
|
||||
}
|
||||
template <typename T>
|
||||
bool bfind (const T &x, unsigned int *i = nullptr,
|
||||
hb_not_found_t not_found = HB_NOT_FOUND_DONT_STORE,
|
||||
unsigned int to_store = (unsigned int) -1) const
|
||||
{
|
||||
unsigned pos;
|
||||
|
||||
if (bsearch_impl (x, &pos))
|
||||
{
|
||||
if (i)
|
||||
*i = pos;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (i)
|
||||
{
|
||||
switch (not_found)
|
||||
{
|
||||
case HB_NOT_FOUND_DONT_STORE:
|
||||
break;
|
||||
|
||||
case HB_NOT_FOUND_STORE:
|
||||
*i = to_store;
|
||||
break;
|
||||
|
||||
case HB_NOT_FOUND_STORE_CLOSEST:
|
||||
*i = pos;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
template <typename T, typename ...Ts>
|
||||
bool bsearch_impl (const T &x, unsigned *pos, Ts... ds) const
|
||||
{
|
||||
return hb_bsearch_impl (pos,
|
||||
x,
|
||||
this->arrayZ,
|
||||
this->length,
|
||||
sizeof (Type),
|
||||
_hb_cmp_method<T, Type, Ts...>,
|
||||
std::forward<Ts> (ds)...);
|
||||
}
|
||||
};
|
||||
template <typename T> inline hb_sorted_array_t<T>
|
||||
hb_sorted_array (T *array, unsigned int length)
|
||||
{ return hb_sorted_array_t<T> (array, length); }
|
||||
template <typename T, unsigned int length_> inline hb_sorted_array_t<T>
|
||||
hb_sorted_array (T (&array_)[length_])
|
||||
{ return hb_sorted_array_t<T> (array_); }
|
||||
|
||||
template <typename T>
|
||||
inline bool hb_array_t<T>::operator == (const hb_array_t<T> &o) const
|
||||
{
|
||||
if (o.length != this->length) return false;
|
||||
for (unsigned int i = 0; i < this->length; i++) {
|
||||
if (this->arrayZ[i] != o.arrayZ[i]) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
template <>
|
||||
inline bool hb_array_t<const char>::operator == (const hb_array_t<const char> &o) const
|
||||
{
|
||||
if (o.length != this->length) return false;
|
||||
return 0 == hb_memcmp (arrayZ, o.arrayZ, length);
|
||||
}
|
||||
template <>
|
||||
inline bool hb_array_t<const unsigned char>::operator == (const hb_array_t<const unsigned char> &o) const
|
||||
{
|
||||
if (o.length != this->length) return false;
|
||||
return 0 == hb_memcmp (arrayZ, o.arrayZ, length);
|
||||
}
|
||||
|
||||
|
||||
/* Specialize hash() for byte arrays. */
|
||||
|
||||
#ifndef HB_OPTIMIZE_SIZE_MORE
|
||||
template <>
|
||||
inline uint32_t hb_array_t<const char>::hash () const
|
||||
{
|
||||
// https://github.com/harfbuzz/harfbuzz/pull/4228
|
||||
return fasthash32(arrayZ, length, 0xf437ffe6 /* magic? */);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline uint32_t hb_array_t<const unsigned char>::hash () const
|
||||
{
|
||||
// https://github.com/harfbuzz/harfbuzz/pull/4228
|
||||
return fasthash32(arrayZ, length, 0xf437ffe6 /* magic? */);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
typedef hb_array_t<const char> hb_bytes_t;
|
||||
typedef hb_array_t<const unsigned char> hb_ubytes_t;
|
||||
|
||||
|
||||
|
||||
#endif /* HB_ARRAY_HH */
|
||||
Vendored
+270
@@ -0,0 +1,270 @@
|
||||
/*
|
||||
* Copyright © 2007 Chris Wilson
|
||||
* Copyright © 2009,2010 Red Hat, Inc.
|
||||
* Copyright © 2011,2012 Google, Inc.
|
||||
*
|
||||
* This is part of HarfBuzz, a text shaping library.
|
||||
*
|
||||
* Permission is hereby granted, without written agreement and without
|
||||
* license or royalty fees, to use, copy, modify, and distribute this
|
||||
* software and its documentation for any purpose, provided that the
|
||||
* above copyright notice and the following two paragraphs appear in
|
||||
* all copies of this software.
|
||||
*
|
||||
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
|
||||
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
|
||||
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
|
||||
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||
* DAMAGE.
|
||||
*
|
||||
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
|
||||
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
|
||||
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
|
||||
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Chris Wilson <chris@chris-wilson.co.uk>
|
||||
* Red Hat Author(s): Behdad Esfahbod
|
||||
* Google Author(s): Behdad Esfahbod
|
||||
*/
|
||||
|
||||
#ifndef HB_ATOMIC_HH
|
||||
#define HB_ATOMIC_HH
|
||||
|
||||
#include "hb.hh"
|
||||
#include "hb-meta.hh"
|
||||
|
||||
|
||||
/*
|
||||
* Atomic integers and pointers.
|
||||
*/
|
||||
|
||||
/* We need external help for these */
|
||||
|
||||
#if defined(hb_atomic_int_impl_add) \
|
||||
&& defined(hb_atomic_ptr_impl_get) \
|
||||
&& defined(hb_atomic_ptr_impl_cmpexch)
|
||||
|
||||
/* Defined externally, i.e. in config.h. */
|
||||
|
||||
|
||||
#elif !defined(HB_NO_MT) && defined(__ATOMIC_ACQUIRE)
|
||||
|
||||
/* C++11-style GCC primitives. We prefer these as they don't require linking to libstdc++ / libc++. */
|
||||
|
||||
#define _hb_memory_barrier() __sync_synchronize ()
|
||||
|
||||
#define hb_atomic_int_impl_add(AI, V) __atomic_fetch_add ((AI), (V), __ATOMIC_ACQ_REL)
|
||||
#define hb_atomic_int_impl_set_relaxed(AI, V) __atomic_store_n ((AI), (V), __ATOMIC_RELAXED)
|
||||
#define hb_atomic_int_impl_set(AI, V) __atomic_store_n ((AI), (V), __ATOMIC_RELEASE)
|
||||
#define hb_atomic_int_impl_get_relaxed(AI) __atomic_load_n ((AI), __ATOMIC_RELAXED)
|
||||
#define hb_atomic_int_impl_get(AI) __atomic_load_n ((AI), __ATOMIC_ACQUIRE)
|
||||
|
||||
#define hb_atomic_ptr_impl_set_relaxed(P, V) __atomic_store_n ((P), (V), __ATOMIC_RELAXED)
|
||||
#define hb_atomic_ptr_impl_get_relaxed(P) __atomic_load_n ((P), __ATOMIC_RELAXED)
|
||||
#define hb_atomic_ptr_impl_get(P) __atomic_load_n ((P), __ATOMIC_ACQUIRE)
|
||||
static inline bool
|
||||
_hb_atomic_ptr_impl_cmplexch (const void **P, const void *O_, const void *N)
|
||||
{
|
||||
const void *O = O_; // Need lvalue
|
||||
return __atomic_compare_exchange_n ((void **) P, (void **) &O, (void *) N, true, __ATOMIC_ACQ_REL, __ATOMIC_RELAXED);
|
||||
}
|
||||
#define hb_atomic_ptr_impl_cmpexch(P,O,N) _hb_atomic_ptr_impl_cmplexch ((const void **) (P), (O), (N))
|
||||
|
||||
|
||||
#elif !defined(HB_NO_MT)
|
||||
|
||||
/* C++11 atomics. */
|
||||
|
||||
#include <atomic>
|
||||
|
||||
#define HB_STL_ATOMIC_IMPL
|
||||
|
||||
#define _hb_memory_r_barrier() std::atomic_thread_fence(std::memory_order_acquire)
|
||||
#define _hb_memory_w_barrier() std::atomic_thread_fence(std::memory_order_release)
|
||||
|
||||
#else /* defined(HB_NO_MT) */
|
||||
|
||||
#define hb_atomic_int_impl_add(AI, V) ((*(AI) += (V)) - (V))
|
||||
#define _hb_memory_barrier() do {} while (0)
|
||||
#define hb_atomic_ptr_impl_cmpexch(P,O,N) (* (void **) (P) == (void *) (O) ? (* (void **) (P) = (void *) (N), true) : false)
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/* This should never be disabled, even under HB_NO_MT.
|
||||
* except that MSVC gives me an internal compiler error, so disabled there.
|
||||
*
|
||||
* https://github.com/harfbuzz/harfbuzz/pull/4119
|
||||
*/
|
||||
#ifndef _hb_compiler_memory_r_barrier
|
||||
#if defined(__ATOMIC_ACQUIRE) // gcc-like
|
||||
static inline void _hb_compiler_memory_r_barrier () { asm volatile("": : :"memory"); }
|
||||
#elif !defined(_MSC_VER)
|
||||
#include <atomic>
|
||||
#define _hb_compiler_memory_r_barrier() std::atomic_signal_fence (std::memory_order_acquire)
|
||||
#else
|
||||
static inline void _hb_compiler_memory_r_barrier () {}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#ifndef _hb_memory_r_barrier
|
||||
#define _hb_memory_r_barrier() _hb_memory_barrier ()
|
||||
#endif
|
||||
#ifndef _hb_memory_w_barrier
|
||||
#define _hb_memory_w_barrier() _hb_memory_barrier ()
|
||||
#endif
|
||||
#ifndef hb_atomic_int_impl_set_relaxed
|
||||
#define hb_atomic_int_impl_set_relaxed(AI, V) (*(AI) = (V))
|
||||
#endif
|
||||
#ifndef hb_atomic_int_impl_get_relaxed
|
||||
#define hb_atomic_int_impl_get_relaxed(AI) (*(AI))
|
||||
#endif
|
||||
|
||||
#ifndef hb_atomic_ptr_impl_set_relaxed
|
||||
#define hb_atomic_ptr_impl_set_relaxed(P, V) (*(P) = (V))
|
||||
#endif
|
||||
#ifndef hb_atomic_ptr_impl_get_relaxed
|
||||
#define hb_atomic_ptr_impl_get_relaxed(P) (*(P))
|
||||
#endif
|
||||
#ifndef hb_atomic_int_impl_set
|
||||
template <typename T>
|
||||
inline void hb_atomic_int_impl_set (T *AI, T v) { _hb_memory_w_barrier (); *AI = v; }
|
||||
#endif
|
||||
#ifndef hb_atomic_int_impl_get
|
||||
template <typename T>
|
||||
inline T hb_atomic_int_impl_get (const T *AI) { T v = *AI; _hb_memory_r_barrier (); return v; }
|
||||
#endif
|
||||
#ifndef hb_atomic_ptr_impl_get
|
||||
inline void *hb_atomic_ptr_impl_get (void ** const P) { void *v = *P; _hb_memory_r_barrier (); return v; }
|
||||
#endif
|
||||
|
||||
#ifdef HB_STL_ATOMIC_IMPL
|
||||
template <typename T>
|
||||
struct hb_atomic_t
|
||||
{
|
||||
hb_atomic_t () = default;
|
||||
constexpr hb_atomic_t (T v) : v (v) {}
|
||||
constexpr hb_atomic_t (const hb_atomic_t& o) : v (o.get_relaxed ()) {}
|
||||
constexpr hb_atomic_t (hb_atomic_t&& o) : v (o.get_relaxed ()) { o.set_relaxed ({}); }
|
||||
|
||||
hb_atomic_t &operator= (const hb_atomic_t& o) { set_relaxed (o.get_relaxed ()); return *this; }
|
||||
hb_atomic_t &operator= (hb_atomic_t&& o){ set_relaxed (o.get_relaxed ()); o.set_relaxed ({}); return *this; }
|
||||
hb_atomic_t &operator= (T v_)
|
||||
{
|
||||
set_relaxed (v_);
|
||||
return *this;
|
||||
}
|
||||
operator T () const { return get_relaxed (); }
|
||||
|
||||
void set_relaxed (T v_) { v.store (v_, std::memory_order_relaxed); }
|
||||
void set_release (T v_) { v.store (v_, std::memory_order_release); }
|
||||
T get_relaxed () const { return v.load (std::memory_order_relaxed); }
|
||||
T get_acquire () const { return v.load (std::memory_order_acquire); }
|
||||
T inc () { return v.fetch_add (1, std::memory_order_acq_rel); }
|
||||
T dec () { return v.fetch_add (-1, std::memory_order_acq_rel); }
|
||||
|
||||
int operator++ (int) { return inc (); }
|
||||
int operator-- (int) { return dec (); }
|
||||
|
||||
friend void swap (hb_atomic_t &a, hb_atomic_t &b) noexcept
|
||||
{
|
||||
T v = a.get_acquire ();
|
||||
a.set_relaxed (b.get_acquire ());
|
||||
b.set_relaxed (v);
|
||||
}
|
||||
|
||||
std::atomic<T> v = 0;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct hb_atomic_t<T *>
|
||||
{
|
||||
hb_atomic_t () = default;
|
||||
constexpr hb_atomic_t (T *v) : v (v) {}
|
||||
hb_atomic_t (const hb_atomic_t &other) = delete;
|
||||
|
||||
void init (T *v_ = nullptr) { set_relaxed (v_); }
|
||||
void set_relaxed (T *v_) { v.store (v_, std::memory_order_relaxed); }
|
||||
T *get_relaxed () const { return v.load (std::memory_order_relaxed); }
|
||||
T *get_acquire () const { return v.load (std::memory_order_acquire); }
|
||||
bool cmpexch (T *old, T *new_) { return v.compare_exchange_weak (old, new_, std::memory_order_acq_rel, std::memory_order_relaxed); }
|
||||
|
||||
hb_atomic_t &operator= (const hb_atomic_t& o) { set_relaxed (o.get_relaxed ()); return *this; }
|
||||
hb_atomic_t &operator= (hb_atomic_t&& o){ set_relaxed (o.get_relaxed ()); o.set_relaxed ({}); return *this; }
|
||||
|
||||
operator bool () const { return get_acquire () != nullptr; }
|
||||
T *operator->() const { return get_acquire (); }
|
||||
template <typename C>
|
||||
operator C * () const
|
||||
{
|
||||
return get_acquire ();
|
||||
}
|
||||
|
||||
friend void swap (hb_atomic_t &a, hb_atomic_t &b) noexcept
|
||||
{
|
||||
T *p = a.get_acquire ();
|
||||
a.set_relaxed (b.get_acquire ());
|
||||
b.set_relaxed (p);
|
||||
}
|
||||
|
||||
std::atomic<T *> v = nullptr;
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
template <typename T>
|
||||
struct hb_atomic_t
|
||||
{
|
||||
hb_atomic_t () = default;
|
||||
constexpr hb_atomic_t (T v) : v (v) {}
|
||||
|
||||
hb_atomic_t& operator = (T v_) { set_relaxed (v_); return *this; }
|
||||
operator T () const { return get_relaxed (); }
|
||||
|
||||
void set_relaxed (T v_) { hb_atomic_int_impl_set_relaxed (&v, v_); }
|
||||
void set_release (T v_) { hb_atomic_int_impl_set (&v, v_); }
|
||||
T get_relaxed () const { return hb_atomic_int_impl_get_relaxed (&v); }
|
||||
T get_acquire () const { return hb_atomic_int_impl_get (&v); }
|
||||
T inc () { return hb_atomic_int_impl_add (&v, 1); }
|
||||
T dec () { return hb_atomic_int_impl_add (&v, -1); }
|
||||
|
||||
int operator ++ (int) { return inc (); }
|
||||
int operator -- (int) { return dec (); }
|
||||
|
||||
T v = 0;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct hb_atomic_t<T*>
|
||||
{
|
||||
hb_atomic_t () = default;
|
||||
constexpr hb_atomic_t (T* v) : v (v) {}
|
||||
hb_atomic_t (const hb_atomic_t &other) = delete;
|
||||
|
||||
void init (T* v_ = nullptr) { set_relaxed (v_); }
|
||||
void set_relaxed (T* v_) { hb_atomic_ptr_impl_set_relaxed (&v, v_); }
|
||||
T *get_relaxed () const { return (T *) hb_atomic_ptr_impl_get_relaxed (&v); }
|
||||
T *get_acquire () const { return (T *) hb_atomic_ptr_impl_get ((void **) &v); }
|
||||
bool cmpexch (T *old, T *new_) { return hb_atomic_ptr_impl_cmpexch ((void **) &v, (void *) old, (void *) new_); }
|
||||
|
||||
operator bool () const { return get_acquire () != nullptr; }
|
||||
T * operator -> () const { return get_acquire (); }
|
||||
template <typename C> operator C * () const { return get_acquire (); }
|
||||
|
||||
T *v = nullptr;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
static inline bool hb_barrier ()
|
||||
{
|
||||
_hb_compiler_memory_r_barrier ();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
#endif /* HB_ATOMIC_HH */
|
||||
Vendored
+205
@@ -0,0 +1,205 @@
|
||||
/*
|
||||
* Copyright © 2019 Adobe Inc.
|
||||
*
|
||||
* This is part of HarfBuzz, a text shaping library.
|
||||
*
|
||||
* Permission is hereby granted, without written agreement and without
|
||||
* license or royalty fees, to use, copy, modify, and distribute this
|
||||
* software and its documentation for any purpose, provided that the
|
||||
* above copyright notice and the following two paragraphs appear in
|
||||
* all copies of this software.
|
||||
*
|
||||
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
|
||||
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
|
||||
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
|
||||
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||
* DAMAGE.
|
||||
*
|
||||
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
|
||||
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
|
||||
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
|
||||
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
|
||||
*
|
||||
* Adobe Author(s): Michiharu Ariza
|
||||
*/
|
||||
|
||||
#ifndef HB_BIMAP_HH
|
||||
#define HB_BIMAP_HH
|
||||
|
||||
#include "hb.hh"
|
||||
#include "hb-map.hh"
|
||||
|
||||
/* Bi-directional map */
|
||||
struct hb_bimap_t
|
||||
{
|
||||
void reset ()
|
||||
{
|
||||
forw_map.reset ();
|
||||
back_map.reset ();
|
||||
}
|
||||
|
||||
void alloc (unsigned pop)
|
||||
{
|
||||
forw_map.alloc (pop);
|
||||
back_map.alloc (pop);
|
||||
}
|
||||
|
||||
bool in_error () const { return forw_map.in_error () || back_map.in_error (); }
|
||||
|
||||
void set (hb_codepoint_t lhs, hb_codepoint_t rhs)
|
||||
{
|
||||
if (in_error ()) return;
|
||||
if (unlikely (lhs == HB_MAP_VALUE_INVALID)) return;
|
||||
if (unlikely (rhs == HB_MAP_VALUE_INVALID)) { del (lhs); return; }
|
||||
|
||||
forw_map.set (lhs, rhs);
|
||||
if (unlikely (in_error ())) return;
|
||||
|
||||
back_map.set (rhs, lhs);
|
||||
if (unlikely (in_error ())) forw_map.del (lhs);
|
||||
}
|
||||
|
||||
hb_codepoint_t get (hb_codepoint_t lhs) const { return forw_map.get (lhs); }
|
||||
hb_codepoint_t backward (hb_codepoint_t rhs) const { return back_map.get (rhs); }
|
||||
|
||||
hb_codepoint_t operator [] (hb_codepoint_t lhs) const { return get (lhs); }
|
||||
bool has (hb_codepoint_t lhs) const { return forw_map.has (lhs); }
|
||||
|
||||
|
||||
void del (hb_codepoint_t lhs)
|
||||
{
|
||||
back_map.del (get (lhs));
|
||||
forw_map.del (lhs);
|
||||
}
|
||||
|
||||
void clear ()
|
||||
{
|
||||
forw_map.clear ();
|
||||
back_map.clear ();
|
||||
}
|
||||
|
||||
bool is_empty () const { return forw_map.is_empty (); }
|
||||
|
||||
unsigned int get_population () const { return forw_map.get_population (); }
|
||||
|
||||
protected:
|
||||
hb_map_t forw_map;
|
||||
hb_map_t back_map;
|
||||
|
||||
public:
|
||||
auto keys () const HB_AUTO_RETURN (+ forw_map.keys())
|
||||
auto values () const HB_AUTO_RETURN (+ forw_map.values())
|
||||
auto iter () const HB_AUTO_RETURN (+ forw_map.iter())
|
||||
};
|
||||
|
||||
/* Incremental bimap: only lhs is given, rhs is incrementally assigned */
|
||||
struct hb_inc_bimap_t
|
||||
{
|
||||
bool in_error () const { return forw_map.in_error () || back_map.in_error (); }
|
||||
|
||||
unsigned int get_population () const { return forw_map.get_population (); }
|
||||
|
||||
void reset ()
|
||||
{
|
||||
forw_map.reset ();
|
||||
back_map.reset ();
|
||||
}
|
||||
|
||||
void alloc (unsigned pop)
|
||||
{
|
||||
forw_map.alloc (pop);
|
||||
back_map.alloc (pop);
|
||||
}
|
||||
|
||||
void clear ()
|
||||
{
|
||||
forw_map.clear ();
|
||||
back_map.clear ();
|
||||
}
|
||||
|
||||
/* 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 = back_map.length;
|
||||
forw_map.set (lhs, rhs);
|
||||
back_map.push (lhs);
|
||||
}
|
||||
return rhs;
|
||||
}
|
||||
|
||||
hb_codepoint_t skip ()
|
||||
{
|
||||
hb_codepoint_t start = back_map.length;
|
||||
back_map.push (HB_MAP_VALUE_INVALID);
|
||||
return start;
|
||||
}
|
||||
|
||||
hb_codepoint_t skip (unsigned count)
|
||||
{
|
||||
hb_codepoint_t start = back_map.length;
|
||||
back_map.alloc (back_map.length + count);
|
||||
for (unsigned i = 0; i < count; i++)
|
||||
back_map.push (HB_MAP_VALUE_INVALID);
|
||||
return start;
|
||||
}
|
||||
|
||||
hb_codepoint_t get_next_value () const
|
||||
{ return back_map.length; }
|
||||
|
||||
void add_set (const hb_set_t *set)
|
||||
{
|
||||
for (auto i : *set) add (i);
|
||||
}
|
||||
|
||||
/* Create an identity map. */
|
||||
bool identity (unsigned int size)
|
||||
{
|
||||
clear ();
|
||||
for (hb_codepoint_t i = 0; i < size; i++) add (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;
|
||||
if (unlikely (!work.resize_dirty (count))) return;
|
||||
|
||||
for (hb_codepoint_t rhs = 0; rhs < count; rhs++)
|
||||
work.arrayZ[rhs] = back_map[rhs];
|
||||
|
||||
work.qsort (cmp_id);
|
||||
|
||||
clear ();
|
||||
for (hb_codepoint_t rhs = 0; rhs < count; rhs++)
|
||||
add (work.arrayZ[rhs]);
|
||||
}
|
||||
|
||||
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[rhs]; }
|
||||
|
||||
hb_codepoint_t operator [] (hb_codepoint_t lhs) const { return get (lhs); }
|
||||
bool has (hb_codepoint_t lhs) const { return forw_map.has (lhs); }
|
||||
|
||||
protected:
|
||||
hb_map_t forw_map;
|
||||
hb_vector_t<hb_codepoint_t> back_map;
|
||||
|
||||
public:
|
||||
auto keys () const HB_AUTO_RETURN (+ back_map.iter())
|
||||
};
|
||||
|
||||
#endif /* HB_BIMAP_HH */
|
||||
Vendored
+410
@@ -0,0 +1,410 @@
|
||||
/*
|
||||
* Copyright © 2012,2017 Google, Inc.
|
||||
* Copyright © 2021 Behdad Esfahbod
|
||||
*
|
||||
* This is part of HarfBuzz, a text shaping library.
|
||||
*
|
||||
* Permission is hereby granted, without written agreement and without
|
||||
* license or royalty fees, to use, copy, modify, and distribute this
|
||||
* software and its documentation for any purpose, provided that the
|
||||
* above copyright notice and the following two paragraphs appear in
|
||||
* all copies of this software.
|
||||
*
|
||||
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
|
||||
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
|
||||
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
|
||||
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||
* DAMAGE.
|
||||
*
|
||||
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
|
||||
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
|
||||
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
|
||||
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
|
||||
*
|
||||
* Google Author(s): Behdad Esfahbod
|
||||
*/
|
||||
|
||||
#ifndef HB_BIT_PAGE_HH
|
||||
#define HB_BIT_PAGE_HH
|
||||
|
||||
#include "hb.hh"
|
||||
|
||||
|
||||
/* Compiler-assisted vectorization. */
|
||||
|
||||
/* Type behaving similar to vectorized vars defined using __attribute__((vector_size(...))),
|
||||
* basically a fixed-size bitset. We can't use the compiler type because hb_vector_t cannot
|
||||
* guarantee alignment requirements. */
|
||||
template <typename elt_t, unsigned int byte_size>
|
||||
struct hb_vector_size_t
|
||||
{
|
||||
elt_t& operator [] (unsigned int i) { return v[i]; }
|
||||
const elt_t& operator [] (unsigned int i) const { return v[i]; }
|
||||
|
||||
void init0 ()
|
||||
{
|
||||
for (unsigned int i = 0; i < ARRAY_LENGTH (v); i++)
|
||||
v[i] = 0;
|
||||
}
|
||||
void init1 ()
|
||||
{
|
||||
for (unsigned int i = 0; i < ARRAY_LENGTH (v); i++)
|
||||
v[i] = (elt_t) -1;
|
||||
}
|
||||
|
||||
template <typename Op>
|
||||
hb_vector_size_t process (const Op& op) const
|
||||
{
|
||||
hb_vector_size_t r;
|
||||
for (unsigned int i = 0; i < ARRAY_LENGTH (v); i++)
|
||||
r.v[i] = op (v[i]);
|
||||
return r;
|
||||
}
|
||||
template <typename Op>
|
||||
hb_vector_size_t process (const Op& op, const hb_vector_size_t &o) const
|
||||
{
|
||||
hb_vector_size_t r;
|
||||
for (unsigned int i = 0; i < ARRAY_LENGTH (v); i++)
|
||||
r.v[i] = op (v[i], o.v[i]);
|
||||
return r;
|
||||
}
|
||||
hb_vector_size_t operator | (const hb_vector_size_t &o) const
|
||||
{ return process (hb_bitwise_or, o); }
|
||||
hb_vector_size_t operator & (const hb_vector_size_t &o) const
|
||||
{ return process (hb_bitwise_and, o); }
|
||||
hb_vector_size_t operator ^ (const hb_vector_size_t &o) const
|
||||
{ return process (hb_bitwise_xor, o); }
|
||||
hb_vector_size_t operator ~ () const
|
||||
{ return process (hb_bitwise_neg); }
|
||||
|
||||
operator bool () const
|
||||
{
|
||||
for (unsigned int i = 0; i < ARRAY_LENGTH (v); i++)
|
||||
if (v[i])
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
operator unsigned int () const
|
||||
{
|
||||
unsigned int r = 0;
|
||||
for (unsigned int i = 0; i < ARRAY_LENGTH (v); i++)
|
||||
r += hb_popcount (v[i]);
|
||||
return r;
|
||||
}
|
||||
bool operator == (const hb_vector_size_t &o) const
|
||||
{
|
||||
for (unsigned int i = 0; i < ARRAY_LENGTH (v); i++)
|
||||
if (v[i] != o.v[i])
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
hb_array_t<const elt_t> iter () const
|
||||
{ return hb_array (v); }
|
||||
|
||||
private:
|
||||
static_assert (0 == byte_size % sizeof (elt_t), "");
|
||||
elt_t v[byte_size / sizeof (elt_t)];
|
||||
};
|
||||
|
||||
|
||||
struct hb_bit_page_t
|
||||
{
|
||||
hb_bit_page_t () { init0 (); }
|
||||
|
||||
void init0 () { v.init0 (); population = 0; }
|
||||
void init1 () { v.init1 (); population = PAGE_BITS; }
|
||||
|
||||
void dirty () { population = UINT_MAX; }
|
||||
|
||||
static inline constexpr unsigned len ()
|
||||
{ return ARRAY_LENGTH_CONST (v); }
|
||||
|
||||
operator bool () const { return !is_empty (); }
|
||||
bool is_empty () const
|
||||
{
|
||||
if (has_population ()) return !population;
|
||||
bool empty = !v;
|
||||
if (empty) population = 0;
|
||||
return empty;
|
||||
}
|
||||
uint32_t hash () const
|
||||
{
|
||||
return hb_bytes_t ((const char *) &v, sizeof (v)).hash ();
|
||||
}
|
||||
|
||||
void add (hb_codepoint_t g) { elt (g) |= mask (g); dirty (); }
|
||||
void del (hb_codepoint_t g) { elt (g) &= ~mask (g); dirty (); }
|
||||
void set (hb_codepoint_t g, bool value) { if (value) add (g); else del (g); }
|
||||
bool get (hb_codepoint_t g) const { return elt (g) & mask (g); }
|
||||
bool may_have (hb_codepoint_t g) const { return get (g); }
|
||||
|
||||
bool operator [] (hb_codepoint_t g) const { return get (g); }
|
||||
bool operator () (hb_codepoint_t g) const { return get (g); }
|
||||
bool has (hb_codepoint_t g) const { return get (g); }
|
||||
|
||||
void add_range (hb_codepoint_t a, hb_codepoint_t b)
|
||||
{
|
||||
elt_t *la = &elt (a);
|
||||
elt_t *lb = &elt (b);
|
||||
if (la == lb)
|
||||
*la |= (mask (b) << 1) - mask(a);
|
||||
else
|
||||
{
|
||||
*la |= ~(mask (a) - 1llu);
|
||||
la++;
|
||||
|
||||
hb_memset (la, 0xff, (char *) lb - (char *) la);
|
||||
|
||||
*lb |= ((mask (b) << 1) - 1llu);
|
||||
}
|
||||
dirty ();
|
||||
}
|
||||
void del_range (hb_codepoint_t a, hb_codepoint_t b)
|
||||
{
|
||||
elt_t *la = &elt (a);
|
||||
elt_t *lb = &elt (b);
|
||||
if (la == lb)
|
||||
*la &= ~((mask (b) << 1llu) - mask(a));
|
||||
else
|
||||
{
|
||||
*la &= mask (a) - 1;
|
||||
la++;
|
||||
|
||||
hb_memset (la, 0, (char *) lb - (char *) la);
|
||||
|
||||
*lb &= ~((mask (b) << 1) - 1llu);
|
||||
}
|
||||
dirty ();
|
||||
}
|
||||
void set_range (hb_codepoint_t a, hb_codepoint_t b, bool v)
|
||||
{ if (v) add_range (a, b); else del_range (a, b); }
|
||||
|
||||
|
||||
// Writes out page values to the array p. Returns the number of values
|
||||
// written. At most size codepoints will be written.
|
||||
unsigned int write (uint32_t base,
|
||||
unsigned int start_value,
|
||||
hb_codepoint_t *p,
|
||||
unsigned int size) const
|
||||
{
|
||||
unsigned int start_v = start_value / ELT_BITS;
|
||||
unsigned int start_bit = start_value & ELT_MASK;
|
||||
unsigned int count = 0;
|
||||
for (unsigned i = start_v; i < len () && count < size; i++)
|
||||
{
|
||||
elt_t bits = v[i];
|
||||
uint32_t v_base = base | (i * ELT_BITS);
|
||||
for (unsigned int j = start_bit; j < ELT_BITS && count < size; j++)
|
||||
{
|
||||
if ((elt_t(1) << j) & bits) {
|
||||
*p++ = v_base | j;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
start_bit = 0;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
// Writes out the values NOT in this page to the array p. Returns the
|
||||
// number of values written. At most size codepoints will be written.
|
||||
// Returns the number of codepoints written. next_value holds the next value
|
||||
// that should be written (if not present in this page). This is used to fill
|
||||
// any missing value gaps between this page and the previous page, if any.
|
||||
// next_value is updated to one more than the last value present in this page.
|
||||
unsigned int write_inverted (uint32_t base,
|
||||
unsigned int start_value,
|
||||
hb_codepoint_t *p,
|
||||
unsigned int size,
|
||||
hb_codepoint_t *next_value) const
|
||||
{
|
||||
unsigned int start_v = start_value / ELT_BITS;
|
||||
unsigned int start_bit = start_value & ELT_MASK;
|
||||
unsigned int count = 0;
|
||||
for (unsigned i = start_v; i < len () && count < size; i++)
|
||||
{
|
||||
elt_t bits = v[i];
|
||||
uint32_t v_offset = i * ELT_BITS;
|
||||
for (unsigned int j = start_bit; j < ELT_BITS && count < size; j++)
|
||||
{
|
||||
if ((elt_t(1) << j) & bits)
|
||||
{
|
||||
hb_codepoint_t value = base | v_offset | j;
|
||||
// Emit all the missing values from next_value up to value - 1.
|
||||
for (hb_codepoint_t k = *next_value; k < value && count < size; k++)
|
||||
{
|
||||
*p++ = k;
|
||||
count++;
|
||||
}
|
||||
// Skip over this value;
|
||||
*next_value = value + 1;
|
||||
}
|
||||
}
|
||||
start_bit = 0;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
bool operator == (const hb_bit_page_t &other) const { return is_equal (other); }
|
||||
bool is_equal (const hb_bit_page_t &other) const { return v == other.v; }
|
||||
bool intersects (const hb_bit_page_t &other) const
|
||||
{
|
||||
for (unsigned i = 0; i < len (); i++)
|
||||
if (v[i] & other.v[i])
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
bool may_intersect (const hb_bit_page_t &other) const
|
||||
{ return intersects (other); }
|
||||
|
||||
bool operator <= (const hb_bit_page_t &larger_page) const { return is_subset (larger_page); }
|
||||
bool is_subset (const hb_bit_page_t &larger_page) const
|
||||
{
|
||||
if (has_population () && larger_page.has_population () &&
|
||||
population > larger_page.population)
|
||||
return false;
|
||||
|
||||
for (unsigned i = 0; i < len (); i++)
|
||||
if (~larger_page.v[i] & v[i])
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool has_population () const { return population != UINT_MAX; }
|
||||
unsigned get_population () const
|
||||
{
|
||||
if (has_population ()) return population;
|
||||
return population = v;
|
||||
}
|
||||
|
||||
bool next (hb_codepoint_t *codepoint) const
|
||||
{
|
||||
unsigned int m = (*codepoint + 1) & MASK;
|
||||
if (!m)
|
||||
{
|
||||
*codepoint = INVALID;
|
||||
return false;
|
||||
}
|
||||
unsigned int i = m / ELT_BITS;
|
||||
unsigned int j = m & ELT_MASK;
|
||||
|
||||
const elt_t vv = v[i] & ~((elt_t (1) << j) - 1);
|
||||
for (const elt_t *p = &vv; i < len (); p = ((const elt_t *) &v[0]) + (++i))
|
||||
if (*p)
|
||||
{
|
||||
*codepoint = i * ELT_BITS + elt_get_min (*p);
|
||||
return true;
|
||||
}
|
||||
|
||||
*codepoint = INVALID;
|
||||
return false;
|
||||
}
|
||||
bool previous (hb_codepoint_t *codepoint) const
|
||||
{
|
||||
unsigned int m = (*codepoint - 1) & MASK;
|
||||
if (m == MASK)
|
||||
{
|
||||
*codepoint = INVALID;
|
||||
return false;
|
||||
}
|
||||
unsigned int i = m / ELT_BITS;
|
||||
unsigned int j = m & ELT_MASK;
|
||||
|
||||
/* Fancy mask to avoid shifting by elt_t bitsize, which is undefined. */
|
||||
const elt_t mask = j < 8 * sizeof (elt_t) - 1 ?
|
||||
((elt_t (1) << (j + 1)) - 1) :
|
||||
(elt_t) -1;
|
||||
const elt_t vv = v[i] & mask;
|
||||
const elt_t *p = &vv;
|
||||
while (true)
|
||||
{
|
||||
if (*p)
|
||||
{
|
||||
*codepoint = i * ELT_BITS + elt_get_max (*p);
|
||||
return true;
|
||||
}
|
||||
if ((int) i <= 0) break;
|
||||
p = &v[--i];
|
||||
}
|
||||
|
||||
*codepoint = INVALID;
|
||||
return false;
|
||||
}
|
||||
hb_codepoint_t get_min () const
|
||||
{
|
||||
for (unsigned int i = 0; i < len (); i++)
|
||||
if (v[i])
|
||||
return i * ELT_BITS + elt_get_min (v[i]);
|
||||
return INVALID;
|
||||
}
|
||||
hb_codepoint_t get_max () const
|
||||
{
|
||||
for (int i = len () - 1; i >= 0; i--)
|
||||
if (v[i])
|
||||
return i * ELT_BITS + elt_get_max (v[i]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Iterator implementation.
|
||||
*/
|
||||
struct iter_t : hb_iter_with_fallback_t<iter_t, hb_codepoint_t>
|
||||
{
|
||||
static constexpr bool is_sorted_iterator = true;
|
||||
iter_t (const hb_bit_page_t &s_ = Null (hb_bit_page_t), bool init = true) : s (&s_), v (INVALID)
|
||||
{
|
||||
if (init)
|
||||
v = s->get_min ();
|
||||
}
|
||||
|
||||
typedef hb_codepoint_t __item_t__;
|
||||
hb_codepoint_t __item__ () const { return v; }
|
||||
bool __more__ () const { return v != INVALID; }
|
||||
void __next__ () {
|
||||
s->next (&v);
|
||||
}
|
||||
void __prev__ () { s->previous (&v); }
|
||||
iter_t end () const { return iter_t (*s, false); }
|
||||
bool operator != (const iter_t& o) const
|
||||
{ return v != o.v; }
|
||||
|
||||
protected:
|
||||
const hb_bit_page_t *s;
|
||||
hb_codepoint_t v;
|
||||
};
|
||||
iter_t iter () const { return iter_t (*this); }
|
||||
operator iter_t () const { return iter (); }
|
||||
|
||||
static constexpr hb_codepoint_t INVALID = HB_SET_VALUE_INVALID;
|
||||
|
||||
typedef unsigned long long elt_t;
|
||||
static constexpr unsigned PAGE_BITS_LOG_2 = 9; // 512 bits
|
||||
static constexpr unsigned PAGE_BITS = 1 << PAGE_BITS_LOG_2;
|
||||
static_assert (1 << PAGE_BITS_LOG_2 == PAGE_BITS, "");
|
||||
static_assert ((PAGE_BITS & ((PAGE_BITS) - 1)) == 0, "");
|
||||
static constexpr unsigned PAGE_BITMASK = PAGE_BITS - 1;
|
||||
|
||||
static unsigned int elt_get_min (const elt_t &elt) { return hb_ctz (elt); }
|
||||
static unsigned int elt_get_max (const elt_t &elt) { return hb_bit_storage (elt) - 1; }
|
||||
|
||||
typedef hb_vector_size_t<elt_t, PAGE_BITS / 8> vector_t;
|
||||
|
||||
static constexpr unsigned ELT_BITS = sizeof (elt_t) * 8;
|
||||
static constexpr unsigned ELT_MASK = ELT_BITS - 1;
|
||||
|
||||
static constexpr unsigned BITS = sizeof (vector_t) * 8;
|
||||
static constexpr unsigned MASK = BITS - 1;
|
||||
static_assert ((unsigned) PAGE_BITS == (unsigned) BITS, "");
|
||||
|
||||
elt_t &elt (hb_codepoint_t g) { return v[(g & MASK) / ELT_BITS]; }
|
||||
const elt_t& elt (hb_codepoint_t g) const { return v[(g & MASK) / ELT_BITS]; }
|
||||
static constexpr elt_t mask (hb_codepoint_t g) { return elt_t (1) << (g & ELT_MASK); }
|
||||
|
||||
mutable unsigned population;
|
||||
vector_t v;
|
||||
};
|
||||
|
||||
|
||||
#endif /* HB_BIT_PAGE_HH */
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user