Update Harfbuzz to version 6.0.0

Note: This requires an update to the tst_qtextlayout test, because
the test assumed that the Arabic string would always yield a run
of two glyphs. This was a side effect of how Harfbuzz handled
Qt's test font, which has zero font tables and cannot be used for
shaping. With the Harfbuzz update, the Arabic text here yields a
single cluster instead, which actually makes more sense, so the
test has been made a bit more robust to support both cases.

Pick-to: 6.2 6.5
Task-number: QTBUG-110338
Change-Id: I93d4cf8e3046dc93224e144d4c81d86bef4918d1
Reviewed-by: Lars Knoll <lars@knoll.priv.no>
Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
bb10
Eskil Abrahamsen Blomfeldt 2023-01-25 08:16:27 +01:00
parent da898fca02
commit bfe080debb
294 changed files with 34630 additions and 23848 deletions

View File

@ -137,27 +137,32 @@ qt_internal_extend_target(BundledHarfbuzz CONDITION TRUE # special case
src/hb-ot-post-table.hh
src/hb-ot-post-table-v2subset.hh
src/hb-ot-shape.cc src/hb-ot-shape.h src/hb-ot-shape.hh
src/hb-ot-shape-complex-arabic.cc src/hb-ot-shape-complex-arabic.hh
src/hb-ot-shape-complex-arabic-fallback.hh
src/hb-ot-shape-complex-arabic-joining-list.hh
src/hb-ot-shape-complex-arabic-table.hh
src/hb-ot-shape-complex-default.cc
src/hb-ot-shape-complex-hangul.cc
src/hb-ot-shape-complex-hebrew.cc
src/hb-ot-shape-complex-indic.cc src/hb-ot-shape-complex-indic.hh
src/hb-ot-shape-complex-indic-machine.hh
src/hb-ot-shape-complex-indic-table.cc
src/hb-ot-shape-complex-khmer.cc
src/hb-ot-shape-complex-myanmar.cc
src/hb-ot-shape-complex-myanmar-machine.hh
src/hb-ot-shape-complex-syllabic.cc src/hb-ot-shape-complex-syllabic.hh
src/hb-ot-shape-complex-thai.cc
src/hb-ot-shape-complex-use.cc
src/hb-ot-shape-complex-use-machine.hh
src/hb-ot-shape-complex-use-table.hh
src/hb-ot-shape-complex-vowel-constraints.cc
src/hb-ot-shape-fallback.cc src/hb-ot-shape-fallback.hh
src/hb-ot-shape-normalize.cc src/hb-ot-shape-normalize.hh
src/hb-ot-shaper-arabic-fallback.hh
src/hb-ot-shaper-arabic-joining-list.hh
src/hb-ot-shaper-arabic-pua.hh
src/hb-ot-shaper-arabic-table.hh
src/hb-ot-shaper-arabic.cc src/hb-ot-shaper-arabic.hh
src/hb-ot-shaper-default.cc
src/hb-ot-shaper-hangul.cc
src/hb-ot-shaper-hebrew.cc
src/hb-ot-shaper-indic-machine.hh
src/hb-ot-shaper-indic-table.cc
src/hb-ot-shaper-indic.cc src/hb-ot-shaper-indic.hh
src/hb-ot-shaper-khmer-machine.hh
src/hb-ot-shaper-khmer.cc
src/hb-ot-shaper-myanmar-machine.hh
src/hb-ot-shaper-myanmar.cc
src/hb-ot-shaper-syllabic.cc src/hb-ot-shaper-syllabic.hh
src/hb-ot-shaper-thai.cc
src/hb-ot-shaper-use-machine.hh
src/hb-ot-shaper-use-table.hh
src/hb-ot-shaper-use.cc
src/hb-ot-shaper-vowel-constraints.cc
src/hb-ot-shaper-vowel-constraints.hh
src/hb-ot-shaper.hh
src/hb-subset-accelerator.hh
src/hb-ot-tag.cc
src/hb-ot-var.cc src/hb-ot-var.h
src/hb-ot-var-common.hh

View File

@ -1,3 +1,233 @@
Overview of changes leading to 6.0.0
Friday, December 16, 2022
====================================
- A new API have been added to pre-process the face and speed up future
subsetting operations on that face. Provides up to a 95% reduction in
subsetting times when the same face is subset more than once.
For more details and benchmarks, see:
https://github.com/harfbuzz/harfbuzz/blob/main/docs/subset-preprocessing.md
(Garret Rieger, Behdad Esfahbod)
- Shaping have been speedup by skipping entire lookups when the buffer contents
don't intersect with the lookup. Shows up to a 10% speedup in shaping some
fonts. (Behdad Esfahbod)
- A new experimental feature, “Variable Composites” (enabled by passing
-Dexperimental_api=true to meson), is also featured in this release.
This technology enables drastic compression of fonts in the Chinese,
Japanese, Korean, and other writing systems, by reusing the OpenType Font
Variations technology for encoding “smart components” into the font.
The specification for these extensions to the font format can be found in:
https://github.com/harfbuzz/boring-expansion-spec/blob/glyf1/glyf1.md
A test variable-font with ~7160 Hangul syllables derived from the
NotoSerifKR-VF font has been built, with existing OpenType technology, as
well as with the new Variable Composites (VarComposites) technology. The
VarComposites font is over 90% smaller than the OpenType version of the font!
Both fonts can be obtained from the “smarties” repository:
https://github.com/behdad/smarties/tree/3.0/fonts/hangul/serif
When building HarfBuzz with experimental features enabled, you can test
the “smarties” font with a sample character like this:
$ hb-view butchered-hangul-serif-smarties-variable.ttf -u AE01 --variations=wght=700
(Behdad Esfahbod)
- The HarfBuzz subsetter can now drop axes by pinning them to specific values
(also referred to as instancing). There are a couple of restrictions
currently:
- Only works with TrueType (“glyf”) based fonts. “CFF2” fonts are not yet
supported.
- Only supports the case where all axes in a font are pinned.
(Garret Rieger, Qunxin Liu)
- Miscellaneous fixes and improvements.
(Behdad Esfahbod, Christoph Reiter, David Corbett, Eli Schwartz, Garret
Rieger, Joel Auterson, Jordan Petridis, Khaled Hosny, Lorenz Wildberg,
Marco Rebhan, Martin Storsjö, Matthias Clasen, Qunxin Liu, Satadru Pramanik)
- New API
+hb_subset_input_pin_axis_location()
+hb_subset_input_pin_axis_to_default()
+hb_subset_preprocess()
Overview of changes leading to 5.3.1
Wednesday, October 19, 2022
====================================
- Subsetter repacker fixes. (Garret Rieger)
- Adjust Grapheme clusters for Katakana voiced sound marks. (Behdad Esfahbod)
- New “hb-subset” option “--preprocess-face”. (Garret Rieger)
Overview of changes leading to 5.3.0
Saturday, October 8, 2022
"Women, Life, Freedom" #MahsaAmini
====================================
- Dont add glyphs from dropped MATH or COLR tables to the subset glyphs.
(Khaled Hosny)
- Map “rlig” to appropriate AAT feature selectors. (Jonathan Kew)
- Update USE data files to latest version. (David Corbett)
- Check “CBDT” extents first before outline tables, to help with fonts that
also include an empty “glyf” table. (Khaled Hosny)
- More work towards variable font instancing in the subsetter. (Qunxin Liu)
- Subsetter repacker improvements. (Garret Rieger)
- New API:
+hb_ot_layout_lookup_get_optical_bound()
+hb_face_builder_sort_tables()
Overview of changes leading to 5.2.0
Saturday, September 17, 2022
====================================
- Fix regressions in hb-ft font functions for FT_Faces with transformation
matrix. (Behdad Esfahbod)
- The experimental hb-repacker API now supports splitting several GPOS subtable
types when needed. (Garret Rieger)
- The HarfBuzz extensions to OpenType font format are now opt-in behind
build-time flags. (Behdad Esfahbod)
- The experimental hb-subset variable fonts instantiation API can now
instantiate more font tables and arbitrary axis locations. (Qunxin Liu)
- Unicode 15 support. (David Corbett)
- Various documentation improvements. (Behdad Esfahbod, Matthias Clasen)
- The hb-view command line tool now detects WezTerm inline images support.
(Wez Furlong)
- Fix FreeType and ICU dependency lookup with meson. (Xavier Claessens)
- New API:
+HB_SCRIPT_KAWI
+HB_SCRIPT_NAG_MUNDARI
Overview of changes leading to 5.1.0
Sunday, July 31, 2022
====================================
- More extensive buffer tracing messages. (Behdad Esfahbod)
- Fix hb-ft regression in bitmap fonts rendering. (Behdad Esfahbod)
- Support extension promotion of lookups in hb-subset-repacker. (Garret Rieger)
- A new HB_GLYPH_FLAG_SAFE_TO_INSERT_TATWEEL for scripts that use elongation
(e.g. Arabic) to signify where it is safe to insert tatweel glyph without
interrupting shaping. (Behdad Esfahbod)
- Add “--safe-to-insert-tatweel” to “hb-shape” tool. (Behdad Esfahbod)
- New API
+HB_GLYPH_FLAG_SAFE_TO_INSERT_TATWEEL
+HB_BUFFER_FLAG_PRODUCE_SAFE_TO_INSERT_TATWEEL
Overview of changes leading to 5.0.1
Saturday, July 23, 2022
====================================
- Fix version 2 “avar” table with hb-ft. (Behdad Esfahbod)
Overview of changes leading to 5.0.0
Saturday, July 23, 2022
====================================
- Support fonts with more than 65535 glyphs in “GDEF”, “GSUB”, and “GPOS”
tables. This is part of https://github.com/be-fonts/boring-expansion-spec to
extend OpenType in a backward-compatible way.
(Behdad Esfahbod, Garret Rieger)
- Complete support for more than 65535 glyphs in “glyf” table that started in
4.0.0 release. Part of boring-expansion-spec. (Behdad Esfahbod)
- Support version 2 of “avar” table. Part of boring-expansion-spec.
(Behdad Esfahbod)
- Fix mark attachment on multiple substitutions in some cases.
(Behdad Esfahbod)
- Fix application of “calt”, “rclt”, and “ccmp” features to better match
Uniscribe behaviour with some Arabic fonts. (Behdad Esfahbod)
- Improvement to interaction between multiple cursive attachments.
(Behdad Esfahbod)
- Improve multiple mark interactions in Hebrew. (Behdad Esfahbod)
- Implement language-specific forms in AAT shaping. (Behdad Esfahbod)
- Fix variation of “VORG” table. (Behdad Esfahbod)
- Support for specific script tags to be retained in the subsetter, and add
“--layout-scripts” option to “hb-subset” tool. (Garret Rieger)
- Accept space as delimiter for --features/--variations in command line tools.
- Improve subsetting of “COLR” table. (Qunxin Liu)
- Improved fuzzing coverage for ot-math API. (Frédéric Wang)
- Fix “kern” table version 2 (AAT) sanitization on 32-bit systems.
(Behdad Esfahbod)
- Allow negative glyph advances from “graphite2” shaper. (Stephan Bergmann)
- Implement loading (color) bitmap fonts with hb-ft. (Behdad Esfahbod)
- Fix regression in hb-ft when changing font size. (Behdad Esfahbod)
- Fix build on GCC < 7. (Kleis Auke Wolthuizen)
- Dynamically load dwrite.dll on windows if “directwrite” shaper is enabled.
(Luca Bacci)
- Provide a single-file harfbuzz-subset.cc file for easier alternate building
of hb-subset library, similar to harfbuzz.cc. (Khaled Hosny)
- New API
+HB_SUBSET_SETS_LAYOUT_SCRIPT_TAG
+hb_language_matches()
Overview of changes leading to 4.4.1
Wednesday, June 29, 2022
====================================
- Fix test failure with some compilers.
- Fix Telugu and Kannada kerning regression.
Overview of changes leading to 4.4.0
Monday, June 27, 2022
====================================
- Caching of variable fonts shaping, in particular when using HarfBuzzs own
font loading functions (ot). Bringing performance of variable shaping in par
with non-variable fonts shaping. (Behdad Esfahbod)
- Caching of format 2 “Contextual Substitution” and “Chained Contexts
Substitution” lookups. Resulting in up to 20% speedup of lookup-heavy fonts
like Gulzar or Noto Nastaliq Urdu. (Behdad Esfahbod)
- Improved ANSI output from hb-view. (Behdad Esfahbod)
- Support for shaping legacy, pre-OpenType Windows 3.1-era, Arabic fonts that
relied on a fixed PUA encoding. (Khaled Hosny, Behdad Esfahbod)
- Sinhala script is now shaped by the USE shaper instead of “indic” one.
(Behdad Esfahbod, David Corbett)
- Thai shaper improvements. (David Corbett)
- hb-ot-name API supports approximate BCP-47 language matching, for example
asking for “en_US” in a font that has only “en” names will return them.
(Behdad Esfahbod)
- Optimized TrueType glyph shape loading. (Behdad Esfahbod)
- Fix subsetting of HarfBuzz faces created via hb_face_create_for_tables().
(Garret Rieger)
- Add 32 bit var store support to the subsetter. (Garret Rieger)
- New API
+HB_BUFFER_FLAG_DEFINED
+HB_BUFFER_SERIALIZE_FLAG_DEFINED
+hb_font_changed()
+hb_font_get_serial()
+hb_ft_hb_font_changed()
+hb_set_hash()
+hb_map_copy()
+hb_map_hash()
Overview of changes leading to 4.3.0
Friday, May 20, 2022
====================================
- Major speed up in loading and subsetting fonts, especially in
handling CFF table. Subsetting some fonts is now 3 times faster.
(Behdad Esfahbod, Garret Rieger)
- Speed up blending CFF2 table. (Behdad Esfahbod)
- Speed up hb_ot_tags_from_language(). (Behdad Esfahbod, David Corbett)
- Fix USE classification of U+10A38 to fix multiple marks on single Kharoshthi
base. (David Corbett)
- Fix parsing of empty CFF Index. (Behdad Esfahbod)
- Fix subsetting CPAL table with partial palette overlaps. (Garret Rieger)
- New API
+hb_map_is_equal() (Behdad Esfahbod)
Overview of changes leading to 4.2.1
Sunday, April 24, 2022
====================================

View File

@ -6,7 +6,12 @@
[![Codecov Code Coverage](https://codecov.io/gh/harfbuzz/harfbuzz/branch/main/graph/badge.svg)](https://codecov.io/gh/harfbuzz/harfbuzz)
[![Packaging status](https://repology.org/badge/tiny-repos/harfbuzz.svg)](https://repology.org/project/harfbuzz/versions)
This is HarfBuzz, a text shaping library.
# HarfBuzz
HarfBuzz is a text shaping engine. It primarily supports [OpenType][1], but also
[Apple Advanced Typography][2]. HarfBuzz is used in Android, Chrome,
ChromeOS, Firefox, GNOME, GTK+, KDE, LibreOffice, OpenJDK, PlayStation, Qt,
XeTeX, and other places.
For bug reports, mailing list, and other information please visit:
@ -14,14 +19,66 @@ For bug reports, mailing list, and other information please visit:
For license information, see [COPYING](COPYING).
## Documentation
For user manual as well as API documentation, check: https://harfbuzz.github.io
## Download
For tarball releases of HarfBuzz, look [here][3]. At the same place you
will also find Win32/Win64 binary bundles that include libharfbuzz DLL,
hb-view.exe, hb-shape.exe, and all dependencies.
The canonical source tree is available on [github][4].
The API that comes with `hb.h` will not change incompatibly. Other, peripheral,
headers are more likely to go through minor modifications, but again, we do our
best to never change API in an incompatible way. We will never break the ABI.
If you are not sure whether Pango or HarfBuzz is right for you, read [Pango vs
HarfBuzz][5].
## Development
For build information, see [BUILD.md](BUILD.md).
For custom configurations, see [CONFIG.md](CONFIG.md).
For test execution, see [TESTING.md](TESTING.md).
For testing and profiling, see [TESTING.md](TESTING.md).
Documentation: https://harfbuzz.github.io
To get a better idea of where HarfBuzz stands in the text rendering stack you
may want to read [State of Text Rendering][6], though, that document is many
years old. Here are a few presentation slides about HarfBuzz at the
Internationalization and Unicode Conference over the years:
* November 2014, [Unicode, OpenType, and HarfBuzz: Closing the Circle][7],
* October 2012, [HarfBuzz, The Free and Open Text Shaping Engine][8],
* October 2009, [HarfBuzz: the Free and Open Shaping Engine][9].
Both development and user support discussion around HarfBuzz happens on the
[github][4].
To report bugs or submit patches please use [github][4] issues and
pull-requests.
For a comparison of old vs new HarfBuzz memory consumption see [this][10].
<!--See past and upcoming [HarfBuzz Hackfests](https://freedesktop.org/wiki/Software/HarfBuzz/Hackfests/)!-->
## Name
HarfBuzz (حرف‌باز) is my Persian translation of “[OpenType][1]”,
transliterated using the Latin script. It sports a second meaning, but that
aint translatable.
> Background: Originally there was this font format called TrueType. People and
> companies started calling their type engines all things ending in Type:
> FreeType, CoolType, ClearType, etc. And then came OpenType, which is the
> successor of TrueType. So, for my OpenType implementation, I decided to stick
> with the concept but use the Persian translation. Which is fitting given that
> Persian is written in the Arabic script, and OpenType is an extension of
> TrueType that adds support for complex script rendering, and HarfBuzz is an
> implementation of OpenType complex text shaping.
<details>
<summary>Packaging status of HarfBuzz</summary>
@ -29,3 +86,14 @@ Documentation: https://harfbuzz.github.io
[![Packaging status](https://repology.org/badge/vertical-allrepos/harfbuzz.svg?header=harfbuzz)](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
[5]: http://mces.blogspot.com/2009/11/pango-vs-harfbuzz.html
[6]: http://behdad.org/text/
[7]: https://goo.gl/FSIQuC
[8]: https://goo.gl/2wSRu
[9]: http://behdad.org/download/Presentations/slippy/harfbuzz_slides.pdf
[10]: https://goo.gl/woyty

View File

@ -90,16 +90,4 @@ for i in ${FILES[*]}; do
copy_file_or_dir "$i"
done
CODEFILES=($HB_DIR/src/*.cc
$HB_DIR/src/*.hh
$HB_DIR/src/*.h)
for i in ${CODEFILES[*]}; do
cp $i $TARGET_DIR/src/
done
GSUBFILES=($HB_DIR/src/OT/Layout/GSUB/*.hh)
for i in ${GSUBFILES[*]}; do
cp $i $TARGET_DIR/src/OT/Layout/GSUB/
done
cp -R $HB_DIR/src $TARGET_DIR

View File

@ -6,7 +6,7 @@
"Description": "HarfBuzz is an OpenType text shaping engine.",
"Homepage": "http://harfbuzz.org",
"Version": "4.2.1",
"Version": "6.0.0",
"License": "MIT License",
"LicenseId": "MIT",

View File

@ -0,0 +1,336 @@
/*
* 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 {
HBUINT16 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);
bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
if (!u.format.sanitize (c)) return_trace (false);
switch (u.format)
{
case 1: return_trace (u.format1.sanitize (c));
case 2: return_trace (u.format2.sanitize (c));
#ifndef HB_NO_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) {
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 get_population () const
{
switch (u.format) {
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 = 0;
unsigned num_ranges = 0;
hb_codepoint_t last = (hb_codepoint_t) -2;
for (auto g: glyphs)
{
if (last + 1 != g)
num_ranges++;
last = g;
count++;
}
u.format = count <= num_ranges * 3 ? 1 : 2;
#ifndef HB_NO_BEYOND_64K
if (count && last > 0xFFFFu)
u.format += 2;
#endif
switch (u.format)
{
case 1: return_trace (u.format1.serialize (c, glyphs));
case 2: return_trace (u.format2.serialize (c, glyphs));
#ifndef HB_NO_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_filter (c->plan->glyph_map_gsub)
| hb_map_retains_sorting (c->plan->glyph_map_gsub)
;
// Cache the iterator result as it will be iterated multiple times
// by the serialize code below.
hb_sorted_vector_t<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)
{
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)
{
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;
}
}
/* 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)
{
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)
{
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;
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

View File

@ -0,0 +1,133 @@
/*
* 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;
protected:
HBUINT16 coverageFormat; /* Format identifier--format = 1 */
SortedArray16Of<typename Types::HBGlyphID>
glyphArray; /* Array of GlyphIDs--in numerical order */
public:
DEFINE_SIZE_ARRAY (4, glyphArray);
private:
bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
return_trace (glyphArray.sanitize (c));
}
unsigned int get_coverage (hb_codepoint_t glyph_id) const
{
unsigned int i;
glyphArray.bfind (glyph_id, &i, HB_NOT_FOUND_STORE, NOT_COVERED);
return i;
}
unsigned get_population () const
{
return glyphArray.len;
}
template <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) / 2)
{
for (hb_codepoint_t g = HB_SET_VALUE_INVALID; glyphs->next (&g);)
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];
}
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

View File

@ -0,0 +1,232 @@
/*
* 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;
protected:
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;
for (auto g: glyphs)
{
if (last + 1 != g)
{
range++;
rangeRecord[range].first = g;
rangeRecord[range].value = count;
}
rangeRecord[range].last = g;
last = g;
count++;
}
return_trace (true);
}
bool intersects (const hb_set_t *glyphs) const
{
if (rangeRecord.len > glyphs->get_population () * hb_bit_storage ((unsigned) rangeRecord.len) / 2)
{
for (hb_codepoint_t g = HB_SET_VALUE_INVALID; glyphs->next (&g);)
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;
}
}
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[i].first;
coverage = c->rangeRecord[i].value;
if (unlikely (coverage != old + 1))
{
/* Broken table. Skip. Important to avoid DoS.
* Also, our callers depend on coverage being
* consecutive and monotonically increasing,
* ie. iota(). */
i = c->rangeRecord.len;
j = 0;
return;
}
}
else
j = 0;
return;
}
coverage++;
j++;
}
hb_codepoint_t get_glyph () const { return j; }
bool operator != (const iter_t& o) const
{ return i != o.i || j != o.j; }
iter_t __end__ () const
{
iter_t it;
it.init (*c);
it.i = c->rangeRecord.len;
it.j = 0;
return it;
}
private:
const struct CoverageFormat2_4 *c;
unsigned int i, coverage;
hb_codepoint_t j;
};
private:
};
}
}
}
#endif // #ifndef OT_LAYOUT_COMMON_COVERAGEFORMAT2_HH

View File

@ -0,0 +1,85 @@
/*
* Copyright © 2007,2008,2009 Red Hat, Inc.
* Copyright © 2010,2012 Google, Inc.
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Red Hat Author(s): Behdad Esfahbod
* Google Author(s): Behdad Esfahbod, Garret Rieger
*/
#ifndef OT_LAYOUT_COMMON_RANGERECORD_HH
#define OT_LAYOUT_COMMON_RANGERECORD_HH
namespace OT {
namespace Layout {
namespace Common {
template <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; }
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

View File

@ -0,0 +1,83 @@
#ifndef OT_LAYOUT_GPOS_ANCHOR_HH
#define OT_LAYOUT_GPOS_ANCHOR_HH
#include "AnchorFormat1.hh"
#include "AnchorFormat2.hh"
#include "AnchorFormat3.hh"
namespace OT {
namespace Layout {
namespace GPOS_impl {
struct Anchor
{
protected:
union {
HBUINT16 format; /* Format identifier */
AnchorFormat1 format1;
AnchorFormat2 format2;
AnchorFormat3 format3;
} u;
public:
DEFINE_SIZE_UNION (2, format);
bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
if (!u.format.sanitize (c)) return_trace (false);
switch (u.format) {
case 1: return_trace (u.format1.sanitize (c));
case 2: return_trace (u.format2.sanitize (c));
case 3: return_trace (u.format3.sanitize (c));
default:return_trace (true);
}
}
void get_anchor (hb_ot_apply_context_t *c, hb_codepoint_t glyph_id,
float *x, float *y) const
{
*x = *y = 0;
switch (u.format) {
case 1: u.format1.get_anchor (c, glyph_id, x, y); return;
case 2: u.format2.get_anchor (c, glyph_id, x, y); return;
case 3: u.format3.get_anchor (c, glyph_id, x, y); return;
default: return;
}
}
bool subset (hb_subset_context_t *c) const
{
TRACE_SUBSET (this);
switch (u.format) {
case 1: return_trace (bool (reinterpret_cast<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) {
case 1: case 2:
return;
case 3:
u.format3.collect_variation_indices (c);
return;
default: return;
}
}
};
}
}
}
#endif // OT_LAYOUT_GPOS_ANCHOR_HH

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,100 @@
#ifndef OT_LAYOUT_GPOS_ANCHORFORMAT3_HH
#define OT_LAYOUT_GPOS_ANCHORFORMAT3_HH
namespace OT {
namespace Layout {
namespace GPOS_impl {
struct AnchorFormat3
{
protected:
HBUINT16 format; /* Format identifier--format = 3 */
FWORD xCoordinate; /* Horizontal value--in design units */
FWORD yCoordinate; /* Vertical value--in design units */
Offset16To<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);
return_trace (c->check_struct (this) && xDeviceTable.sanitize (c, this) && yDeviceTable.sanitize (c, this));
}
void get_anchor (hb_ot_apply_context_t *c, hb_codepoint_t glyph_id HB_UNUSED,
float *x, float *y) const
{
hb_font_t *font = c->font;
*x = font->em_fscale_x (xCoordinate);
*y = font->em_fscale_y (yCoordinate);
if (font->x_ppem || font->num_coords)
*x += (this+xDeviceTable).get_x_delta (font, c->var_store, c->var_store_cache);
if (font->y_ppem || font->num_coords)
*y += (this+yDeviceTable).get_y_delta (font, c->var_store, c->var_store_cache);
}
bool subset (hb_subset_context_t *c) const
{
TRACE_SUBSET (this);
auto *out = c->serializer->start_embed (*this);
if (unlikely (!out)) return_trace (false);
if (unlikely (!c->serializer->embed (format))) return_trace (false);
if (unlikely (!c->serializer->embed (xCoordinate))) return_trace (false);
if (unlikely (!c->serializer->embed (yCoordinate))) return_trace (false);
unsigned x_varidx = xDeviceTable ? (this+xDeviceTable).get_variation_index () : HB_OT_LAYOUT_NO_VARIATIONS_INDEX;
if (c->plan->layout_variation_idx_delta_map->has (x_varidx))
{
int delta = hb_second (c->plan->layout_variation_idx_delta_map->get (x_varidx));
if (delta != 0)
{
if (!c->serializer->check_assign (out->xCoordinate, xCoordinate + delta,
HB_SERIALIZE_ERROR_INT_OVERFLOW))
return_trace (false);
}
}
unsigned y_varidx = yDeviceTable ? (this+yDeviceTable).get_variation_index () : HB_OT_LAYOUT_NO_VARIATIONS_INDEX;
if (c->plan->layout_variation_idx_delta_map->has (y_varidx))
{
int delta = hb_second (c->plan->layout_variation_idx_delta_map->get (y_varidx));
if (delta != 0)
{
if (!c->serializer->check_assign (out->yCoordinate, yCoordinate + delta,
HB_SERIALIZE_ERROR_INT_OVERFLOW))
return_trace (false);
}
}
if (c->plan->all_axes_pinned)
return_trace (c->serializer->check_assign (out->format, 1, HB_SERIALIZE_ERROR_INT_OVERFLOW));
if (!c->serializer->embed (xDeviceTable)) return_trace (false);
if (!c->serializer->embed (yDeviceTable)) return_trace (false);
out->xDeviceTable.serialize_copy (c->serializer, xDeviceTable, this, 0, hb_serialize_context_t::Head, c->plan->layout_variation_idx_delta_map);
out->yDeviceTable.serialize_copy (c->serializer, yDeviceTable, this, 0, hb_serialize_context_t::Head, c->plan->layout_variation_idx_delta_map);
return_trace (out);
}
void collect_variation_indices (hb_collect_variation_indices_context_t *c) const
{
(this+xDeviceTable).collect_variation_indices (c);
(this+yDeviceTable).collect_variation_indices (c);
}
};
}
}
}
#endif // OT_LAYOUT_GPOS_ANCHORFORMAT3_HH

View File

@ -0,0 +1,77 @@
#ifndef OT_LAYOUT_GPOS_ANCHORMATRIX_HH
#define OT_LAYOUT_GPOS_ANCHORMATRIX_HH
namespace OT {
namespace Layout {
namespace GPOS_impl {
struct AnchorMatrix
{
HBUINT16 rows; /* Number of rows */
UnsizedArrayOf<Offset16To<Anchor>>
matrixZ; /* Matrix of offsets to Anchor tables--
* from beginning of AnchorMatrix table */
public:
DEFINE_SIZE_ARRAY (2, matrixZ);
bool sanitize (hb_sanitize_context_t *c, unsigned int cols) const
{
TRACE_SANITIZE (this);
if (!c->check_struct (this)) return_trace (false);
if (unlikely (hb_unsigned_mul_overflows (rows, cols))) return_trace (false);
unsigned int count = rows * cols;
if (!c->check_array (matrixZ.arrayZ, count)) return_trace (false);
for (unsigned int i = 0; i < count; i++)
if (!matrixZ[i].sanitize (c, this)) return_trace (false);
return_trace (true);
}
const Anchor& get_anchor (unsigned int row, unsigned int col,
unsigned int cols, bool *found) const
{
*found = false;
if (unlikely (row >= rows || col >= cols)) return Null (Anchor);
*found = !matrixZ[row * cols + col].is_null ();
return this+matrixZ[row * cols + col];
}
template <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);
}
};
}
}
}
#endif /* OT_LAYOUT_GPOS_ANCHORMATRIX_HH */

View File

@ -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 */

View File

@ -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,
bool all_axes_pinned);
}
}
}
#endif // OT_LAYOUT_GPOS_COMMON_HH

View File

@ -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 */

View File

@ -0,0 +1,35 @@
#ifndef OT_LAYOUT_GPOS_CURSIVEPOS_HH
#define OT_LAYOUT_GPOS_CURSIVEPOS_HH
#include "CursivePosFormat1.hh"
namespace OT {
namespace Layout {
namespace GPOS_impl {
struct CursivePos
{
protected:
union {
HBUINT16 format; /* Format identifier */
CursivePosFormat1 format1;
} u;
public:
template <typename context_t, typename ...Ts>
typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
{
TRACE_DISPATCH (this, u.format);
if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
switch (u.format) {
case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...));
default:return_trace (c->default_return_value ());
}
}
};
}
}
}
#endif /* OT_LAYOUT_GPOS_CURSIVEPOS_HH */

View File

@ -0,0 +1,301 @@
#ifndef OT_LAYOUT_GPOS_CURSIVEPOSFORMAT1_HH
#define OT_LAYOUT_GPOS_CURSIVEPOSFORMAT1_HH
#include "Anchor.hh"
namespace OT {
namespace Layout {
namespace GPOS_impl {
struct EntryExitRecord
{
friend struct CursivePosFormat1;
bool sanitize (hb_sanitize_context_t *c, const void *base) const
{
TRACE_SANITIZE (this);
return_trace (entryAnchor.sanitize (c, base) && exitAnchor.sanitize (c, base));
}
void collect_variation_indices (hb_collect_variation_indices_context_t *c,
const void *src_base) const
{
(src_base+entryAnchor).collect_variation_indices (c);
(src_base+exitAnchor).collect_variation_indices (c);
}
EntryExitRecord* subset (hb_subset_context_t *c,
const void *src_base) const
{
TRACE_SERIALIZE (this);
auto *out = c->serializer->embed (this);
if (unlikely (!out)) return_trace (nullptr);
out->entryAnchor.serialize_subset (c, entryAnchor, src_base);
out->exitAnchor.serialize_subset (c, exitAnchor, src_base);
return_trace (out);
}
protected:
Offset16To<Anchor>
entryAnchor; /* Offset to EntryAnchor table--from
* beginning of CursivePos
* subtable--may be NULL */
Offset16To<Anchor>
exitAnchor; /* Offset to ExitAnchor table--from
* beginning of CursivePos
* subtable--may be NULL */
public:
DEFINE_SIZE_STATIC (4);
};
static void
reverse_cursive_minor_offset (hb_glyph_position_t *pos, unsigned int i, hb_direction_t direction, unsigned int new_parent) {
int chain = pos[i].attach_chain(), type = pos[i].attach_type();
if (likely (!chain || 0 == (type & ATTACH_TYPE_CURSIVE)))
return;
pos[i].attach_chain() = 0;
unsigned int j = (int) i + chain;
/* Stop if we see new parent in the chain. */
if (j == new_parent)
return;
reverse_cursive_minor_offset (pos, j, direction, new_parent);
if (HB_DIRECTION_IS_HORIZONTAL (direction))
pos[j].y_offset = -pos[i].y_offset;
else
pos[j].x_offset = -pos[i].x_offset;
pos[j].attach_chain() = -chain;
pos[j].attach_type() = type;
}
struct CursivePosFormat1
{
protected:
HBUINT16 format; /* Format identifier--format = 1 */
Offset16To<Coverage>
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);
return_trace (coverage.sanitize (c, this) && entryExitRecord.sanitize (c, this));
}
bool intersects (const hb_set_t *glyphs) const
{ return (this+coverage).intersects (glyphs); }
void closure_lookups (hb_closure_lookups_context_t *c) const {}
void collect_variation_indices (hb_collect_variation_indices_context_t *c) const
{
+ hb_zip (this+coverage, entryExitRecord)
| hb_filter (c->glyph_set, hb_first)
| hb_map (hb_second)
| hb_apply ([&] (const EntryExitRecord& record) { record.collect_variation_indices (c, this); })
;
}
void collect_glyphs (hb_collect_glyphs_context_t *c) const
{ if (unlikely (!(this+coverage).collect_coverage (c->input))) return; }
const Coverage &get_coverage () const { return this+coverage; }
bool apply (hb_ot_apply_context_t *c) const
{
TRACE_APPLY (this);
hb_buffer_t *buffer = c->buffer;
const EntryExitRecord &this_record = entryExitRecord[(this+coverage).get_coverage (buffer->cur().codepoint)];
if (!this_record.entryAnchor) return_trace (false);
hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
skippy_iter.reset (buffer->idx, 1);
unsigned unsafe_from;
if (!skippy_iter.prev (&unsafe_from))
{
buffer->unsafe_to_concat_from_outbuffer (unsafe_from, buffer->idx + 1);
return_trace (false);
}
const EntryExitRecord &prev_record = entryExitRecord[(this+coverage).get_coverage (buffer->info[skippy_iter.idx].codepoint)];
if (!prev_record.exitAnchor)
{
buffer->unsafe_to_concat_from_outbuffer (skippy_iter.idx, buffer->idx + 1);
return_trace (false);
}
unsigned int i = skippy_iter.idx;
unsigned int j = buffer->idx;
if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
{
c->buffer->message (c->font,
"cursive attaching glyph at %d to glyph at %d",
i, j);
}
buffer->unsafe_to_break (i, j + 1);
float entry_x, entry_y, exit_x, exit_y;
(this+prev_record.exitAnchor).get_anchor (c, buffer->info[i].codepoint, &exit_x, &exit_y);
(this+this_record.entryAnchor).get_anchor (c, buffer->info[j].codepoint, &entry_x, &entry_y);
hb_glyph_position_t *pos = buffer->pos;
hb_position_t d;
/* Main-direction adjustment */
switch (c->direction) {
case HB_DIRECTION_LTR:
pos[i].x_advance = roundf (exit_x) + pos[i].x_offset;
d = roundf (entry_x) + pos[j].x_offset;
pos[j].x_advance -= d;
pos[j].x_offset -= d;
break;
case HB_DIRECTION_RTL:
d = roundf (exit_x) + pos[i].x_offset;
pos[i].x_advance -= d;
pos[i].x_offset -= d;
pos[j].x_advance = roundf (entry_x) + pos[j].x_offset;
break;
case HB_DIRECTION_TTB:
pos[i].y_advance = roundf (exit_y) + pos[i].y_offset;
d = roundf (entry_y) + pos[j].y_offset;
pos[j].y_advance -= d;
pos[j].y_offset -= d;
break;
case HB_DIRECTION_BTT:
d = roundf (exit_y) + pos[i].y_offset;
pos[i].y_advance -= d;
pos[i].y_offset -= d;
pos[j].y_advance = roundf (entry_y);
break;
case HB_DIRECTION_INVALID:
default:
break;
}
/* Cross-direction adjustment */
/* We attach child to parent (think graph theory and rooted trees whereas
* the root stays on baseline and each node aligns itself against its
* parent.
*
* Optimize things for the case of RightToLeft, as that's most common in
* Arabic. */
unsigned int child = i;
unsigned int parent = j;
hb_position_t x_offset = entry_x - exit_x;
hb_position_t y_offset = entry_y - exit_y;
if (!(c->lookup_props & LookupFlag::RightToLeft))
{
unsigned int k = child;
child = parent;
parent = k;
x_offset = -x_offset;
y_offset = -y_offset;
}
/* If child was already connected to someone else, walk through its old
* chain and reverse the link direction, such that the whole tree of its
* previous connection now attaches to new parent. Watch out for case
* where new parent is on the path from old chain...
*/
reverse_cursive_minor_offset (pos, child, c->direction, parent);
pos[child].attach_type() = ATTACH_TYPE_CURSIVE;
pos[child].attach_chain() = (int) parent - (int) child;
buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT;
if (likely (HB_DIRECTION_IS_HORIZONTAL (c->direction)))
pos[child].y_offset = y_offset;
else
pos[child].x_offset = x_offset;
/* If parent was attached to child, separate them.
* https://github.com/harfbuzz/harfbuzz/issues/2469
*/
if (unlikely (pos[parent].attach_chain() == -pos[child].attach_chain()))
{
pos[parent].attach_chain() = 0;
if (likely (HB_DIRECTION_IS_HORIZONTAL (c->direction)))
pos[parent].y_offset = 0;
else
pos[parent].x_offset = 0;
}
if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
{
c->buffer->message (c->font,
"cursive attached glyph at %d to glyph at %d",
i, j);
}
buffer->idx++;
return_trace (true);
}
template <typename Iterator,
hb_requires (hb_is_iterator (Iterator))>
void serialize (hb_subset_context_t *c,
Iterator it,
const void *src_base)
{
if (unlikely (!c->serializer->extend_min ((*this)))) return;
this->format = 1;
this->entryExitRecord.len = it.len ();
for (const EntryExitRecord& entry_record : + it
| hb_map (hb_second))
entry_record.subset (c, src_base);
auto glyphs =
+ it
| hb_map_retains_sorting (hb_first)
;
coverage.serialize_serialize (c->serializer, glyphs);
}
bool subset (hb_subset_context_t *c) const
{
TRACE_SUBSET (this);
const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
const hb_map_t &glyph_map = *c->plan->glyph_map;
auto *out = c->serializer->start_embed (*this);
if (unlikely (!out)) return_trace (false);
auto it =
+ hb_zip (this+coverage, entryExitRecord)
| hb_filter (glyphset, hb_first)
| hb_map_retains_sorting ([&] (hb_pair_t<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 */

View File

@ -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 */

View File

@ -0,0 +1,171 @@
#ifndef OT_LAYOUT_GPOS_GPOS_HH
#define OT_LAYOUT_GPOS_GPOS_HH
#include "../../../hb-ot-layout-common.hh"
#include "../../../hb-ot-layout-gsubgpos.hh"
#include "Common.hh"
#include "PosLookup.hh"
namespace OT {
using Layout::GPOS_impl::PosLookup;
namespace Layout {
static void
propagate_attachment_offsets (hb_glyph_position_t *pos,
unsigned int len,
unsigned int i,
hb_direction_t direction,
unsigned nesting_level = HB_MAX_NESTING_LEVEL);
/*
* GPOS -- Glyph Positioning
* https://docs.microsoft.com/en-us/typography/opentype/spec/gpos
*/
struct GPOS : GSUBGPOS
{
static constexpr hb_tag_t tableTag = HB_OT_TAG_GPOS;
using Lookup = PosLookup;
const PosLookup& get_lookup (unsigned int i) const
{ return static_cast<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(), type = pos[i].attach_type();
if (likely (!chain))
return;
pos[i].attach_chain() = 0;
unsigned int j = (int) i + chain;
if (unlikely (j >= len))
return;
if (unlikely (!nesting_level))
return;
propagate_attachment_offsets (pos, len, j, direction, nesting_level - 1);
assert (!!(type & GPOS_impl::ATTACH_TYPE_MARK) ^ !!(type & GPOS_impl::ATTACH_TYPE_CURSIVE));
if (type & GPOS_impl::ATTACH_TYPE_CURSIVE)
{
if (HB_DIRECTION_IS_HORIZONTAL (direction))
pos[i].y_offset += pos[j].y_offset;
else
pos[i].x_offset += pos[j].x_offset;
}
else /*if (type & GPOS_impl::ATTACH_TYPE_MARK)*/
{
pos[i].x_offset += pos[j].x_offset;
pos[i].y_offset += pos[j].y_offset;
assert (j < i);
if (HB_DIRECTION_IS_FORWARD (direction))
for (unsigned int k = j; k < i; k++) {
pos[i].x_offset -= pos[k].x_advance;
pos[i].y_offset -= pos[k].y_advance;
}
else
for (unsigned int k = j + 1; k < i + 1; k++) {
pos[i].x_offset += pos[k].x_advance;
pos[i].y_offset += pos[k].y_advance;
}
}
}
void
GPOS::position_start (hb_font_t *font HB_UNUSED, hb_buffer_t *buffer)
{
unsigned int count = buffer->len;
for (unsigned int i = 0; i < count; i++)
buffer->pos[i].attach_chain() = buffer->pos[i].attach_type() = 0;
}
void
GPOS::position_finish_advances (hb_font_t *font HB_UNUSED, hb_buffer_t *buffer HB_UNUSED)
{
//_hb_buffer_assert_gsubgpos_vars (buffer);
}
void
GPOS::position_finish_offsets (hb_font_t *font, hb_buffer_t *buffer)
{
_hb_buffer_assert_gsubgpos_vars (buffer);
unsigned int len;
hb_glyph_position_t *pos = hb_buffer_get_glyph_positions (buffer, &len);
hb_direction_t direction = buffer->props.direction;
/* Handle attachments */
if (buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT)
for (unsigned i = 0; i < len; i++)
propagate_attachment_offsets (pos, len, i, direction);
if (unlikely (font->slant))
{
for (unsigned i = 0; i < len; i++)
if (unlikely (pos[i].y_offset))
pos[i].x_offset += _hb_roundf (font->slant_xy * pos[i].y_offset);
}
}
}
struct GPOS_accelerator_t : Layout::GPOS::accelerator_t {
GPOS_accelerator_t (hb_face_t *face) : Layout::GPOS::accelerator_t (face) {}
};
}
#endif /* OT_LAYOUT_GPOS_GPOS_HH */

View File

@ -0,0 +1,56 @@
#ifndef OT_LAYOUT_GPOS_LIGATUREARRAY_HH
#define OT_LAYOUT_GPOS_LIGATUREARRAY_HH
namespace OT {
namespace Layout {
namespace GPOS_impl {
typedef AnchorMatrix LigatureAttach; /* component-major--
* in order of writing direction--,
* mark-minor--
* ordered by class--zero-based. */
/* Array of LigatureAttach tables ordered by LigatureCoverage Index */
struct LigatureArray : List16OfOffset16To<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) const
{
TRACE_SUBSET (this);
const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
auto *out = c->serializer->start_embed (this);
if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
for (const auto _ : + hb_zip (coverage, *this)
| hb_filter (glyphset, hb_first))
{
auto *matrix = out->serialize_append (c->serializer);
if (unlikely (!matrix)) return_trace (false);
const LigatureAttach& src = (this + _.second);
auto indexes =
+ hb_range (src.rows * class_count)
| hb_filter ([=] (unsigned index) { return klass_mapping->has (index % class_count); })
;
matrix->serialize_subset (c,
_.second,
this,
src.rows,
indexes);
}
return_trace (this->len);
}
};
}
}
}
#endif /* OT_LAYOUT_GPOS_LIGATUREARRAY_HH */

View File

@ -0,0 +1,128 @@
#ifndef OT_LAYOUT_GPOS_MARKARRAY_HH
#define OT_LAYOUT_GPOS_MARKARRAY_HH
#include "AnchorMatrix.hh"
#include "MarkRecord.hh"
namespace OT {
namespace Layout {
namespace GPOS_impl {
struct MarkArray : Array16Of<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 (glyph_index, mark_class, class_count, &found);
/* If this subtable doesn't have an anchor for this base and this class,
* return false such that the subsequent subtables have a chance at it. */
if (unlikely (!found)) return_trace (false);
float mark_x, mark_y, base_x, base_y;
buffer->unsafe_to_break (glyph_pos, buffer->idx + 1);
mark_anchor.get_anchor (c, buffer->cur().codepoint, &mark_x, &mark_y);
glyph_anchor.get_anchor (c, buffer->info[glyph_pos].codepoint, &base_x, &base_y);
if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
{
c->buffer->message (c->font,
"attaching mark glyph at %d to glyph at %d",
c->buffer->idx, glyph_pos);
}
hb_glyph_position_t &o = buffer->cur_pos();
o.x_offset = roundf (base_x - mark_x);
o.y_offset = roundf (base_y - mark_y);
o.attach_type() = ATTACH_TYPE_MARK;
o.attach_chain() = (int) glyph_pos - (int) buffer->idx;
buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT;
if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
{
c->buffer->message (c->font,
"attached mark glyph at %d to glyph at %d",
c->buffer->idx, glyph_pos);
}
buffer->idx++;
return_trace (true);
}
template <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)
;
unsigned new_length = 0;
for (const auto& mark_record : mark_iter) {
if (unlikely (!mark_record.subset (c, this, klass_mapping)))
return_trace (false);
new_length++;
}
if (unlikely (!c->serializer->check_assign (out->len, new_length,
HB_SERIALIZE_ERROR_ARRAY_OVERFLOW)))
return_trace (false);
return_trace (true);
}
};
HB_INTERNAL inline
void Markclass_closure_and_remap_indexes (const Coverage &mark_coverage,
const MarkArray &mark_array,
const hb_set_t &glyphset,
hb_map_t* klass_mapping /* INOUT */)
{
hb_set_t orig_classes;
+ hb_zip (mark_coverage, mark_array)
| hb_filter (glyphset, hb_first)
| hb_map (hb_second)
| hb_map (&MarkRecord::get_class)
| hb_sink (orig_classes)
;
unsigned idx = 0;
for (auto klass : orig_classes.iter ())
{
if (klass_mapping->has (klass)) continue;
klass_mapping->set (klass, idx);
idx++;
}
}
}
}
}
#endif /* OT_LAYOUT_GPOS_MARKARRAY_HH */

View File

@ -0,0 +1,41 @@
#ifndef OT_LAYOUT_GPOS_MARKBASEPOS_HH
#define OT_LAYOUT_GPOS_MARKBASEPOS_HH
#include "MarkBasePosFormat1.hh"
namespace OT {
namespace Layout {
namespace GPOS_impl {
struct MarkBasePos
{
protected:
union {
HBUINT16 format; /* Format identifier */
MarkBasePosFormat1_2<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
{
TRACE_DISPATCH (this, u.format);
if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
switch (u.format) {
case 1: return_trace (c->dispatch (u.format1, std::forward<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 */

View File

@ -0,0 +1,219 @@
#ifndef OT_LAYOUT_GPOS_MARKBASEPOSFORMAT1_HH
#define OT_LAYOUT_GPOS_MARKBASEPOSFORMAT1_HH
#include "MarkArray.hh"
namespace OT {
namespace Layout {
namespace GPOS_impl {
typedef AnchorMatrix BaseArray; /* base-major--
* in order of BaseCoverage Index--,
* mark-minor--
* ordered by class--zero-based. */
template <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; }
bool apply (hb_ot_apply_context_t *c) const
{
TRACE_APPLY (this);
hb_buffer_t *buffer = c->buffer;
unsigned int mark_index = (this+markCoverage).get_coverage (buffer->cur().codepoint);
if (likely (mark_index == NOT_COVERED)) return_trace (false);
/* Now we search backwards for a non-mark glyph */
hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
skippy_iter.reset (buffer->idx, 1);
skippy_iter.set_lookup_props (LookupFlag::IgnoreMarks);
do {
unsigned unsafe_from;
if (!skippy_iter.prev (&unsafe_from))
{
buffer->unsafe_to_concat_from_outbuffer (unsafe_from, buffer->idx + 1);
return_trace (false);
}
/* We only want to attach to the first of a MultipleSubst sequence.
* https://github.com/harfbuzz/harfbuzz/issues/740
* Reject others...
* ...but stop if we find a mark in the MultipleSubst sequence:
* https://github.com/harfbuzz/harfbuzz/issues/1020 */
if (!_hb_glyph_info_multiplied (&buffer->info[skippy_iter.idx]) ||
0 == _hb_glyph_info_get_lig_comp (&buffer->info[skippy_iter.idx]) ||
(skippy_iter.idx == 0 ||
_hb_glyph_info_is_mark (&buffer->info[skippy_iter.idx - 1]) ||
!_hb_glyph_info_multiplied (&buffer->info[skippy_iter.idx - 1]) ||
_hb_glyph_info_get_lig_id (&buffer->info[skippy_iter.idx]) !=
_hb_glyph_info_get_lig_id (&buffer->info[skippy_iter.idx - 1]) ||
_hb_glyph_info_get_lig_comp (&buffer->info[skippy_iter.idx]) !=
_hb_glyph_info_get_lig_comp (&buffer->info[skippy_iter.idx - 1]) + 1
))
break;
skippy_iter.reject ();
} while (true);
/* Checking that matched glyph is actually a base glyph by GDEF is too strong; disabled */
//if (!_hb_glyph_info_is_base_glyph (&buffer->info[skippy_iter.idx])) { return_trace (false); }
unsigned int base_index = (this+baseCoverage).get_coverage (buffer->info[skippy_iter.idx].codepoint);
if (base_index == NOT_COVERED)
{
buffer->unsafe_to_concat_from_outbuffer (skippy_iter.idx, buffer->idx + 1);
return_trace (false);
}
return_trace ((this+markArray).apply (c, mark_index, base_index, this+baseArray, classCount, skippy_iter.idx));
}
bool subset (hb_subset_context_t *c) const
{
TRACE_SUBSET (this);
const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
const hb_map_t &glyph_map = *c->plan->glyph_map;
auto *out = c->serializer->start_embed (*this);
if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
out->format = format;
hb_map_t klass_mapping;
Markclass_closure_and_remap_indexes (this+markCoverage, this+markArray, glyphset, &klass_mapping);
if (!klass_mapping.get_population ()) return_trace (false);
out->classCount = klass_mapping.get_population ();
auto mark_iter =
+ hb_zip (this+markCoverage, this+markArray)
| hb_filter (glyphset, hb_first)
;
hb_sorted_vector_t<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);
out->markArray.serialize_subset (c, markArray, this,
(this+markCoverage).iter (),
&klass_mapping);
unsigned basecount = (this+baseArray).rows;
auto base_iter =
+ hb_zip (this+baseCoverage, hb_range (basecount))
| hb_filter (glyphset, hb_first)
;
new_coverage.reset ();
+ base_iter
| hb_map (hb_first)
| hb_map (glyph_map)
| hb_sink (new_coverage)
;
if (!out->baseCoverage.serialize_serialize (c->serializer, new_coverage.iter ()))
return_trace (false);
hb_sorted_vector_t<unsigned> base_indexes;
for (const unsigned row : + base_iter
| hb_map (hb_second))
{
+ hb_range ((unsigned) classCount)
| hb_filter (klass_mapping)
| hb_map ([&] (const unsigned col) { return row * (unsigned) classCount + col; })
| hb_sink (base_indexes)
;
}
out->baseArray.serialize_subset (c, baseArray, this,
base_iter.len (),
base_indexes.iter ());
return_trace (true);
}
};
}
}
}
#endif /* OT_LAYOUT_GPOS_MARKBASEPOSFORMAT1_HH */

View File

@ -0,0 +1,41 @@
#ifndef OT_LAYOUT_GPOS_MARKLIGPOS_HH
#define OT_LAYOUT_GPOS_MARKLIGPOS_HH
#include "MarkLigPosFormat1.hh"
namespace OT {
namespace Layout {
namespace GPOS_impl {
struct MarkLigPos
{
protected:
union {
HBUINT16 format; /* Format identifier */
MarkLigPosFormat1_2<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
{
TRACE_DISPATCH (this, u.format);
if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
switch (u.format) {
case 1: return_trace (c->dispatch (u.format1, std::forward<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 */

View File

@ -0,0 +1,206 @@
#ifndef OT_LAYOUT_GPOS_MARKLIGPOSFORMAT1_HH
#define OT_LAYOUT_GPOS_MARKLIGPOSFORMAT1_HH
#include "LigatureArray.hh"
namespace OT {
namespace Layout {
namespace GPOS_impl {
template <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 */
hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
skippy_iter.reset (buffer->idx, 1);
skippy_iter.set_lookup_props (LookupFlag::IgnoreMarks);
unsigned unsafe_from;
if (!skippy_iter.prev (&unsafe_from))
{
buffer->unsafe_to_concat_from_outbuffer (unsafe_from, buffer->idx + 1);
return_trace (false);
}
/* Checking that matched glyph is actually a ligature by GDEF is too strong; disabled */
//if (!_hb_glyph_info_is_ligature (&buffer->info[skippy_iter.idx])) { return_trace (false); }
unsigned int j = skippy_iter.idx;
unsigned int lig_index = (this+ligatureCoverage).get_coverage (buffer->info[j].codepoint);
if (lig_index == NOT_COVERED)
{
buffer->unsafe_to_concat_from_outbuffer (skippy_iter.idx, buffer->idx + 1);
return_trace (false);
}
const LigatureArray& lig_array = this+ligatureArray;
const LigatureAttach& lig_attach = lig_array[lig_index];
/* Find component to attach to */
unsigned int comp_count = lig_attach.rows;
if (unlikely (!comp_count))
{
buffer->unsafe_to_concat_from_outbuffer (skippy_iter.idx, buffer->idx + 1);
return_trace (false);
}
/* We must now check whether the ligature ID of the current mark glyph
* is identical to the ligature ID of the found ligature. If yes, we
* can directly use the component index. If not, we attach the mark
* glyph to the last component of the ligature. */
unsigned int comp_index;
unsigned int lig_id = _hb_glyph_info_get_lig_id (&buffer->info[j]);
unsigned int mark_id = _hb_glyph_info_get_lig_id (&buffer->cur());
unsigned int mark_comp = _hb_glyph_info_get_lig_comp (&buffer->cur());
if (lig_id && lig_id == mark_id && mark_comp > 0)
comp_index = hb_min (comp_count, _hb_glyph_info_get_lig_comp (&buffer->cur())) - 1;
else
comp_index = comp_count - 1;
return_trace ((this+markArray).apply (c, mark_index, comp_index, lig_attach, classCount, j));
}
bool subset (hb_subset_context_t *c) const
{
TRACE_SUBSET (this);
const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
const hb_map_t &glyph_map = *c->plan->glyph_map;
auto *out = c->serializer->start_embed (*this);
if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
out->format = format;
hb_map_t klass_mapping;
Markclass_closure_and_remap_indexes (this+markCoverage, this+markArray, glyphset, &klass_mapping);
if (!klass_mapping.get_population ()) return_trace (false);
out->classCount = klass_mapping.get_population ();
auto mark_iter =
+ hb_zip (this+markCoverage, this+markArray)
| hb_filter (glyphset, hb_first)
;
auto new_mark_coverage =
+ mark_iter
| hb_map_retains_sorting (hb_first)
| hb_map_retains_sorting (glyph_map)
;
if (!out->markCoverage.serialize_serialize (c->serializer, new_mark_coverage))
return_trace (false);
out->markArray.serialize_subset (c, markArray, this,
(this+markCoverage).iter (),
&klass_mapping);
auto new_ligature_coverage =
+ hb_iter (this + ligatureCoverage)
| hb_filter (glyphset)
| hb_map_retains_sorting (glyph_map)
;
if (!out->ligatureCoverage.serialize_serialize (c->serializer, new_ligature_coverage))
return_trace (false);
out->ligatureArray.serialize_subset (c, ligatureArray, this,
hb_iter (this+ligatureCoverage), classCount, &klass_mapping);
return_trace (true);
}
};
}
}
}
#endif /* OT_LAYOUT_GPOS_MARKLIGPOSFORMAT1_HH */

View File

@ -0,0 +1,42 @@
#ifndef OT_LAYOUT_GPOS_MARKMARKPOS_HH
#define OT_LAYOUT_GPOS_MARKMARKPOS_HH
#include "MarkMarkPosFormat1.hh"
namespace OT {
namespace Layout {
namespace GPOS_impl {
struct MarkMarkPos
{
protected:
union {
HBUINT16 format; /* Format identifier */
MarkMarkPosFormat1_2<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
{
TRACE_DISPATCH (this, u.format);
if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
switch (u.format) {
case 1: return_trace (c->dispatch (u.format1, std::forward<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 */

View File

@ -0,0 +1,228 @@
#ifndef OT_LAYOUT_GPOS_MARKMARKPOSFORMAT1_HH
#define OT_LAYOUT_GPOS_MARKMARKPOSFORMAT1_HH
#include "MarkMarkPosFormat1.hh"
namespace OT {
namespace Layout {
namespace GPOS_impl {
typedef AnchorMatrix Mark2Array; /* mark2-major--
* in order of Mark2Coverage Index--,
* mark1-minor--
* ordered by class--zero-based. */
template <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) &&
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 */
hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
skippy_iter.reset (buffer->idx, 1);
skippy_iter.set_lookup_props (c->lookup_props & ~(uint32_t)LookupFlag::IgnoreFlags);
unsigned unsafe_from;
if (!skippy_iter.prev (&unsafe_from))
{
buffer->unsafe_to_concat_from_outbuffer (unsafe_from, buffer->idx + 1);
return_trace (false);
}
if (!_hb_glyph_info_is_mark (&buffer->info[skippy_iter.idx]))
{
buffer->unsafe_to_concat_from_outbuffer (skippy_iter.idx, buffer->idx + 1);
return_trace (false);
}
unsigned int j = skippy_iter.idx;
unsigned int id1 = _hb_glyph_info_get_lig_id (&buffer->cur());
unsigned int id2 = _hb_glyph_info_get_lig_id (&buffer->info[j]);
unsigned int comp1 = _hb_glyph_info_get_lig_comp (&buffer->cur());
unsigned int comp2 = _hb_glyph_info_get_lig_comp (&buffer->info[j]);
if (likely (id1 == id2))
{
if (id1 == 0) /* Marks belonging to the same base. */
goto good;
else if (comp1 == comp2) /* Marks belonging to the same ligature component. */
goto good;
}
else
{
/* If ligature ids don't match, it may be the case that one of the marks
* itself is a ligature. In which case match. */
if ((id1 > 0 && !comp1) || (id2 > 0 && !comp2))
goto good;
}
/* Didn't match. */
buffer->unsafe_to_concat_from_outbuffer (skippy_iter.idx, buffer->idx + 1);
return_trace (false);
good:
unsigned int mark2_index = (this+mark2Coverage).get_coverage (buffer->info[j].codepoint);
if (mark2_index == NOT_COVERED)
{
buffer->unsafe_to_concat_from_outbuffer (skippy_iter.idx, buffer->idx + 1);
return_trace (false);
}
return_trace ((this+mark1Array).apply (c, mark1_index, mark2_index, this+mark2Array, classCount, j));
}
bool subset (hb_subset_context_t *c) const
{
TRACE_SUBSET (this);
const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
const hb_map_t &glyph_map = *c->plan->glyph_map;
auto *out = c->serializer->start_embed (*this);
if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
out->format = format;
hb_map_t klass_mapping;
Markclass_closure_and_remap_indexes (this+mark1Coverage, this+mark1Array, glyphset, &klass_mapping);
if (!klass_mapping.get_population ()) return_trace (false);
out->classCount = klass_mapping.get_population ();
auto mark1_iter =
+ hb_zip (this+mark1Coverage, this+mark1Array)
| hb_filter (glyphset, hb_first)
;
hb_sorted_vector_t<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);
out->mark1Array.serialize_subset (c, mark1Array, this,
(this+mark1Coverage).iter (),
&klass_mapping);
unsigned mark2count = (this+mark2Array).rows;
auto mark2_iter =
+ hb_zip (this+mark2Coverage, hb_range (mark2count))
| hb_filter (glyphset, hb_first)
;
new_coverage.reset ();
+ mark2_iter
| hb_map (hb_first)
| hb_map (glyph_map)
| hb_sink (new_coverage)
;
if (!out->mark2Coverage.serialize_serialize (c->serializer, new_coverage.iter ()))
return_trace (false);
hb_sorted_vector_t<unsigned> mark2_indexes;
for (const unsigned row : + mark2_iter
| hb_map (hb_second))
{
+ hb_range ((unsigned) classCount)
| hb_filter (klass_mapping)
| hb_map ([&] (const unsigned col) { return row * (unsigned) classCount + col; })
| hb_sink (mark2_indexes)
;
}
out->mark2Array.serialize_subset (c, mark2Array, this, mark2_iter.len (), mark2_indexes.iter ());
return_trace (true);
}
};
}
}
}
#endif /* OT_LAYOUT_GPOS_MARKMARKPOSFORMAT1_HH */

View File

@ -0,0 +1,52 @@
#ifndef OT_LAYOUT_GPOS_MARKRECORD_HH
#define OT_LAYOUT_GPOS_MARKRECORD_HH
namespace OT {
namespace Layout {
namespace GPOS_impl {
struct MarkRecord
{
friend struct MarkArray;
public:
HBUINT16 klass; /* Class defined for this mark */
Offset16To<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));
}
MarkRecord *subset (hb_subset_context_t *c,
const void *src_base,
const hb_map_t *klass_mapping) const
{
TRACE_SUBSET (this);
auto *out = c->serializer->embed (this);
if (unlikely (!out)) return_trace (nullptr);
out->klass = klass_mapping->get (klass);
out->markAnchor.serialize_subset (c, markAnchor, src_base);
return_trace (out);
}
void collect_variation_indices (hb_collect_variation_indices_context_t *c,
const void *src_base) const
{
(src_base+markAnchor).collect_variation_indices (c);
}
};
}
}
}
#endif /* OT_LAYOUT_GPOS_MARKRECORD_HH */

View File

@ -0,0 +1,46 @@
#ifndef OT_LAYOUT_GPOS_PAIRPOS_HH
#define OT_LAYOUT_GPOS_PAIRPOS_HH
#include "PairPosFormat1.hh"
#include "PairPosFormat2.hh"
namespace OT {
namespace Layout {
namespace GPOS_impl {
struct PairPos
{
protected:
union {
HBUINT16 format; /* Format identifier */
PairPosFormat1_3<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
{
TRACE_DISPATCH (this, u.format);
if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
switch (u.format) {
case 1: return_trace (c->dispatch (u.format1, std::forward<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

View File

@ -0,0 +1,219 @@
#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);
unsigned int len1 = valueFormat[0].get_len ();
unsigned int len2 = valueFormat[1].get_len ();
typename PairSet::sanitize_closure_t closure =
{
valueFormat,
len1,
1 + len1 + len2
};
return_trace (coverage.sanitize (c, this) && pairSet.sanitize (c, this, &closure));
}
bool intersects (const hb_set_t *glyphs) const
{
auto &cov = this+coverage;
if (pairSet.len > glyphs->get_population () * hb_bit_storage ((unsigned) pairSet.len) / 4)
{
for (hb_codepoint_t g = HB_SET_VALUE_INVALID; glyphs->next (&g);)
{
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; }
bool apply (hb_ot_apply_context_t *c) const
{
TRACE_APPLY (this);
hb_buffer_t *buffer = c->buffer;
unsigned int index = (this+coverage).get_coverage (buffer->cur().codepoint);
if (likely (index == NOT_COVERED)) return_trace (false);
hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
skippy_iter.reset (buffer->idx, 1);
unsigned unsafe_to;
if (!skippy_iter.next (&unsafe_to))
{
buffer->unsafe_to_concat (buffer->idx, unsafe_to);
return_trace (false);
}
return_trace ((this+pairSet[index]).apply (c, valueFormat, skippy_iter.idx));
}
bool subset (hb_subset_context_t *c) const
{
TRACE_SUBSET (this);
const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
const hb_map_t &glyph_map = *c->plan->glyph_map;
auto *out = c->serializer->start_embed (*this);
if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
out->format = format;
out->valueFormat[0] = valueFormat[0];
out->valueFormat[1] = valueFormat[1];
if (c->plan->flags & HB_SUBSET_FLAGS_NO_HINTING)
{
hb_pair_t<unsigned, unsigned> newFormats = compute_effective_value_formats (glyphset);
out->valueFormat[0] = newFormats.first;
out->valueFormat[1] = newFormats.second;
}
if (c->plan->all_axes_pinned)
{
out->valueFormat[0] = out->valueFormat[0].drop_device_table_flags ();
out->valueFormat[1] = out->valueFormat[1].drop_device_table_flags ();
}
hb_sorted_vector_t<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) const
{
unsigned len1 = valueFormat[0].get_len ();
unsigned len2 = valueFormat[1].get_len ();
unsigned record_size = HBUINT16::static_size + Value::static_size * (len1 + len2);
unsigned format1 = 0;
unsigned format2 = 0;
for (const auto & _ :
+ hb_zip (this+coverage, pairSet)
| hb_filter (glyphset, hb_first)
| hb_map (hb_second)
)
{
const PairSet& set = (this + _);
const PairValueRecord *record = &set.firstPairValueRecord;
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 ());
format2 = format2 | valueFormat[1].get_effective_format (record->get_values_2 (valueFormat[0]));
}
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

View File

@ -0,0 +1,351 @@
#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
{
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 = len1 + len2;
unsigned int record_size = valueFormat1.get_size () + valueFormat2.get_size ();
unsigned int count = (unsigned int) class1Count * (unsigned int) class2Count;
return_trace (c->check_range ((const void *) values,
count,
record_size) &&
valueFormat1.sanitize_values_stride_unsafe (c, this, &values[0], count, stride) &&
valueFormat2.sanitize_values_stride_unsafe (c, this, &values[len1], count, stride));
}
bool intersects (const hb_set_t *glyphs) const
{
return (this+coverage).intersects (glyphs) &&
(this+classDef2).intersects (glyphs);
}
void closure_lookups (hb_closure_lookups_context_t *c) const {}
void collect_variation_indices (hb_collect_variation_indices_context_t *c) const
{
if (!intersects (c->glyph_set)) return;
if ((!valueFormat1.has_device ()) && (!valueFormat2.has_device ())) return;
hb_set_t klass1_glyphs, klass2_glyphs;
if (!(this+classDef1).collect_coverage (&klass1_glyphs)) return;
if (!(this+classDef2).collect_coverage (&klass2_glyphs)) return;
hb_set_t class1_set, class2_set;
for (const unsigned cp : + c->glyph_set->iter () | hb_filter (this + coverage))
{
if (!klass1_glyphs.has (cp)) class1_set.add (0);
else
{
unsigned klass1 = (this+classDef1).get (cp);
class1_set.add (klass1);
}
}
class2_set.add (0);
for (const unsigned cp : + c->glyph_set->iter () | hb_filter (klass2_glyphs))
{
unsigned klass2 = (this+classDef2).get (cp);
class2_set.add (klass2);
}
if (class1_set.is_empty ()
|| class2_set.is_empty ()
|| (class2_set.get_population() == 1 && class2_set.has(0)))
return;
unsigned len1 = valueFormat1.get_len ();
unsigned len2 = valueFormat2.get_len ();
const hb_array_t<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; }
bool apply (hb_ot_apply_context_t *c) const
{
TRACE_APPLY (this);
hb_buffer_t *buffer = c->buffer;
unsigned int index = (this+coverage).get_coverage (buffer->cur().codepoint);
if (likely (index == NOT_COVERED)) return_trace (false);
hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
skippy_iter.reset (buffer->idx, 1);
unsigned unsafe_to;
if (!skippy_iter.next (&unsafe_to))
{
buffer->unsafe_to_concat (buffer->idx, unsafe_to);
return_trace (false);
}
unsigned int len1 = valueFormat1.get_len ();
unsigned int len2 = valueFormat2.get_len ();
unsigned int record_len = len1 + len2;
unsigned int klass1 = (this+classDef1).get_class (buffer->cur().codepoint);
unsigned int klass2 = (this+classDef2).get_class (buffer->info[skippy_iter.idx].codepoint);
if (unlikely (klass1 >= class1Count || klass2 >= class2Count))
{
buffer->unsafe_to_concat (buffer->idx, skippy_iter.idx + 1);
return_trace (false);
}
const Value *v = &values[record_len * (klass1 * class2Count + klass2)];
bool applied_first = false, applied_second = false;
/* Isolate simple kerning and apply it half to each side.
* Results in better cursor positinoing / underline drawing.
*
* Disabled, because causes issues... :-(
* https://github.com/harfbuzz/harfbuzz/issues/3408
* https://github.com/harfbuzz/harfbuzz/pull/3235#issuecomment-1029814978
*/
#ifndef HB_SPLIT_KERN
if (0)
#endif
{
if (!len2)
{
const hb_direction_t dir = buffer->props.direction;
const bool horizontal = HB_DIRECTION_IS_HORIZONTAL (dir);
const bool backward = HB_DIRECTION_IS_BACKWARD (dir);
unsigned mask = horizontal ? ValueFormat::xAdvance : ValueFormat::yAdvance;
if (backward)
mask |= mask >> 2; /* Add eg. xPlacement in RTL. */
/* Add Devices. */
mask |= mask << 4;
if (valueFormat1 & ~mask)
goto bail;
/* Is simple kern. Apply value on an empty position slot,
* then split it between sides. */
hb_glyph_position_t pos{};
if (valueFormat1.apply_value (c, this, v, pos))
{
hb_position_t *src = &pos.x_advance;
hb_position_t *dst1 = &buffer->cur_pos().x_advance;
hb_position_t *dst2 = &buffer->pos[skippy_iter.idx].x_advance;
unsigned i = horizontal ? 0 : 1;
hb_position_t kern = src[i];
hb_position_t kern1 = kern >> 1;
hb_position_t kern2 = kern - kern1;
if (!backward)
{
dst1[i] += kern1;
dst2[i] += kern2;
dst2[i + 2] += kern2;
}
else
{
dst1[i] += kern1;
dst1[i + 2] += src[i + 2] - kern2;
dst2[i] += kern2;
}
applied_first = applied_second = kern != 0;
goto success;
}
goto boring;
}
}
bail:
if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
{
c->buffer->message (c->font,
"try kerning glyphs at %d,%d",
c->buffer->idx, skippy_iter.idx);
}
applied_first = valueFormat1.apply_value (c, this, v, buffer->cur_pos());
applied_second = valueFormat2.apply_value (c, this, v + len1, buffer->pos[skippy_iter.idx]);
if (applied_first || applied_second)
if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
{
c->buffer->message (c->font,
"kerned glyphs at %d,%d",
c->buffer->idx, skippy_iter.idx);
}
if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
{
c->buffer->message (c->font,
"tried kerning glyphs at %d,%d",
c->buffer->idx, skippy_iter.idx);
}
success:
if (applied_first || applied_second)
buffer->unsafe_to_break (buffer->idx, skippy_iter.idx + 1);
else
boring:
buffer->unsafe_to_concat (buffer->idx, skippy_iter.idx + 1);
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->flags & HB_SUBSET_FLAGS_NO_HINTING)
newFormats = compute_effective_value_formats (klass1_map, klass2_map);
out->valueFormat1 = newFormats.first;
out->valueFormat2 = newFormats.second;
if (c->plan->all_axes_pinned)
{
out->valueFormat1 = out->valueFormat1.drop_device_table_flags ();
out->valueFormat2 = out->valueFormat2.drop_device_table_flags ();
}
for (unsigned class1_idx : + hb_range ((unsigned) class1Count) | hb_filter (klass1_map))
{
for (unsigned class2_idx : + hb_range ((unsigned) class2Count) | hb_filter (klass2_map))
{
unsigned idx = (class1_idx * (unsigned) class2Count + class2_idx) * (len1 + len2);
valueFormat1.copy_values (c->serializer, out->valueFormat1, this, &values[idx], c->plan->layout_variation_idx_delta_map);
valueFormat2.copy_values (c->serializer, out->valueFormat2, this, &values[idx + len1], c->plan->layout_variation_idx_delta_map);
}
}
const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
const hb_map_t &glyph_map = *c->plan->glyph_map;
auto it =
+ hb_iter (this+coverage)
| hb_filter (glyphset)
| hb_map_retains_sorting (glyph_map)
;
out->coverage.serialize_serialize (c->serializer, it);
return_trace (out->class1Count && out->class2Count && bool (it));
}
hb_pair_t<unsigned, unsigned> compute_effective_value_formats (const hb_map_t& klass1_map,
const hb_map_t& klass2_map) const
{
unsigned len1 = valueFormat1.get_len ();
unsigned len2 = valueFormat2.get_len ();
unsigned 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]);
format2 = format2 | valueFormat2.get_effective_format (&values[idx + len1]);
}
if (format1 == valueFormat1 && format2 == valueFormat2)
break;
}
return hb_pair (format1, format2);
}
};
}
}
}
#endif // OT_LAYOUT_GPOS_PAIRPOSFORMAT2_HH

View File

@ -0,0 +1,203 @@
#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
{
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);
struct sanitize_closure_t
{
const ValueFormat *valueFormats;
unsigned int len1; /* valueFormats[0].get_len() */
unsigned int stride; /* 1 + len1 + len2 */
};
bool sanitize (hb_sanitize_context_t *c, const sanitize_closure_t *closure) const
{
TRACE_SANITIZE (this);
if (!(c->check_struct (this)
&& c->check_range (&firstPairValueRecord,
len,
HBUINT16::static_size,
closure->stride))) return_trace (false);
unsigned int count = len;
const PairValueRecord *record = &firstPairValueRecord;
return_trace (closure->valueFormats[0].sanitize_values_stride_unsafe (c, this, &record->values[0], count, closure->stride) &&
closure->valueFormats[1].sanitize_values_stride_unsafe (c, this, &record->values[closure->len1], count, closure->stride));
}
bool intersects (const hb_set_t *glyphs,
const ValueFormat *valueFormats) const
{
unsigned int len1 = valueFormats[0].get_len ();
unsigned int len2 = valueFormats[1].get_len ();
unsigned int record_size = HBUINT16::static_size * (1 + len1 + len2);
const PairValueRecord *record = &firstPairValueRecord;
unsigned int count = len;
for (unsigned int i = 0; i < count; i++)
{
if (glyphs->has (record->secondGlyph))
return true;
record = &StructAtOffset<const PairValueRecord> (record, record_size);
}
return false;
}
void collect_glyphs (hb_collect_glyphs_context_t *c,
const ValueFormat *valueFormats) const
{
unsigned int len1 = valueFormats[0].get_len ();
unsigned int len2 = valueFormats[1].get_len ();
unsigned int record_size = HBUINT16::static_size * (1 + len1 + len2);
const PairValueRecord *record = &firstPairValueRecord;
c->input->add_array (&record->secondGlyph, len, record_size);
}
void collect_variation_indices (hb_collect_variation_indices_context_t *c,
const ValueFormat *valueFormats) const
{
unsigned len1 = valueFormats[0].get_len ();
unsigned len2 = valueFormats[1].get_len ();
unsigned record_size = HBUINT16::static_size * (1 + len1 + len2);
const PairValueRecord *record = &firstPairValueRecord;
unsigned count = len;
for (unsigned i = 0; i < count; i++)
{
if (c->glyph_set->has (record->secondGlyph))
{ record->collect_variation_indices (c, valueFormats, this); }
record = &StructAtOffset<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 int record_size = HBUINT16::static_size * (1 + len1 + len2);
const PairValueRecord *record = hb_bsearch (buffer->info[pos].codepoint,
&firstPairValueRecord,
len,
record_size);
if (record)
{
if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
{
c->buffer->message (c->font,
"try kerning glyphs at %d,%d",
c->buffer->idx, pos);
}
bool applied_first = valueFormats[0].apply_value (c, this, &record->values[0], buffer->cur_pos());
bool applied_second = valueFormats[1].apply_value (c, this, &record->values[len1], buffer->pos[pos]);
if (applied_first || applied_second)
if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
{
c->buffer->message (c->font,
"kerned glyphs at %d,%d",
c->buffer->idx, pos);
}
if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
{
c->buffer->message (c->font,
"tried kerning glyphs at %d,%d",
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 = HBUINT16::static_size + Value::static_size * (len1 + len2);
typename PairValueRecord::context_t context =
{
this,
valueFormats,
newFormats,
len1,
&glyph_map,
c->plan->layout_variation_idx_delta_map
};
const PairValueRecord *record = &firstPairValueRecord;
unsigned count = len, num = 0;
for (unsigned i = 0; i < count; i++)
{
if (glyphset.has (record->secondGlyph)
&& record->subset (c, &context)) num++;
record = &StructAtOffset<const PairValueRecord> (record, record_size);
}
out->len = num;
if (!num) c->serializer->revert (snap);
return_trace (num);
}
};
}
}
}
#endif // OT_LAYOUT_GPOS_PAIRSET_HH

View File

@ -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::size, values);
int cmp (hb_codepoint_t k) const
{ return secondGlyph.cmp (k); }
struct context_t
{
const void *base;
const ValueFormat *valueFormats;
const ValueFormat *newFormats;
unsigned len1; /* valueFormats[0].get_len() */
const hb_map_t *glyph_map;
const hb_hashmap_t<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 void *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

View File

@ -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 */

View File

@ -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 */

View File

@ -0,0 +1,100 @@
#ifndef OT_LAYOUT_GPOS_SINGLEPOS_HH
#define OT_LAYOUT_GPOS_SINGLEPOS_HH
#include "SinglePosFormat1.hh"
#include "SinglePosFormat2.hh"
namespace OT {
namespace Layout {
namespace GPOS_impl {
struct SinglePos
{
protected:
union {
HBUINT16 format; /* Format identifier */
SinglePosFormat1 format1;
SinglePosFormat2 format2;
} u;
public:
template<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,
bool all_axes_pinned)
{
if (unlikely (!c->extend_min (u.format))) return;
unsigned format = 2;
ValueFormat new_format = src->get_value_format ();
if (all_axes_pinned)
new_format = new_format.drop_device_table_flags ();
if (glyph_val_iter_pairs)
format = get_format (glyph_val_iter_pairs);
u.format = format;
switch (u.format) {
case 1: u.format1.serialize (c,
src,
glyph_val_iter_pairs,
new_format,
layout_variation_idx_delta_map);
return;
case 2: u.format2.serialize (c,
src,
glyph_val_iter_pairs,
new_format,
layout_variation_idx_delta_map);
return;
default:return;
}
}
template <typename context_t, typename ...Ts>
typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
{
TRACE_DISPATCH (this, u.format);
if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
switch (u.format) {
case 1: return_trace (c->dispatch (u.format1, std::forward<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,
bool all_axes_pinned)
{ c->start_embed<SinglePos> ()->serialize (c, src, it, layout_variation_idx_delta_map, all_axes_pinned); }
}
}
}
#endif /* OT_LAYOUT_GPOS_SINGLEPOS_HH */

View File

@ -0,0 +1,156 @@
#ifndef OT_LAYOUT_GPOS_SINGLEPOSFORMAT1_HH
#define OT_LAYOUT_GPOS_SINGLEPOSFORMAT1_HH
#include "Common.hh"
#include "ValueFormat.hh"
namespace OT {
namespace Layout {
namespace GPOS_impl {
struct SinglePosFormat1
{
protected:
HBUINT16 format; /* Format identifier--format = 1 */
Offset16To<Coverage>
coverage; /* Offset to Coverage table--from
* beginning of subtable */
ValueFormat valueFormat; /* Defines the types of data in the
* ValueRecord */
ValueRecord values; /* Defines positioning
* value(s)--applied to all glyphs in
* the Coverage table */
public:
DEFINE_SIZE_ARRAY (6, values);
bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
return_trace (c->check_struct (this) &&
coverage.sanitize (c, this) &&
valueFormat.sanitize_value (c, this, values));
}
bool intersects (const hb_set_t *glyphs) const
{ return (this+coverage).intersects (glyphs); }
void closure_lookups (hb_closure_lookups_context_t *c) const {}
void collect_variation_indices (hb_collect_variation_indices_context_t *c) const
{
if (!valueFormat.has_device ()) return;
hb_set_t intersection;
(this+coverage).intersect_set (*c->glyph_set, intersection);
if (!intersection) return;
valueFormat.collect_variation_indices (c, this, values.as_array (valueFormat.get_len ()));
}
void collect_glyphs (hb_collect_glyphs_context_t *c) const
{ if (unlikely (!(this+coverage).collect_coverage (c->input))) return; }
const Coverage &get_coverage () const { return this+coverage; }
ValueFormat get_value_format () const { return valueFormat; }
bool apply (hb_ot_apply_context_t *c) const
{
TRACE_APPLY (this);
hb_buffer_t *buffer = c->buffer;
unsigned int index = (this+coverage).get_coverage (buffer->cur().codepoint);
if (likely (index == NOT_COVERED)) return_trace (false);
if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
{
c->buffer->message (c->font,
"positioning glyph at %d",
c->buffer->idx);
}
valueFormat.apply_value (c, this, values, buffer->cur_pos());
if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
{
c->buffer->message (c->font,
"positioned glyph at %d",
c->buffer->idx);
}
buffer->idx++;
return_trace (true);
}
bool
position_single (hb_font_t *font,
hb_direction_t direction,
hb_codepoint_t gid,
hb_glyph_position_t &pos) const
{
unsigned int index = (this+coverage).get_coverage (gid);
if (likely (index == NOT_COVERED)) return false;
/* This is ugly... */
hb_buffer_t buffer;
buffer.props.direction = direction;
OT::hb_ot_apply_context_t c (1, font, &buffer);
valueFormat.apply_value (&c, this, values, pos);
return true;
}
template<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);
auto it =
+ hb_iter (intersection)
| hb_map_retains_sorting (glyph_map)
| hb_zip (hb_repeat (values.as_array (valueFormat.get_len ())))
;
bool ret = bool (it);
SinglePos_serialize (c->serializer, this, it, c->plan->layout_variation_idx_delta_map, c->plan->all_axes_pinned);
return_trace (ret);
}
};
}
}
}
#endif /* OT_LAYOUT_GPOS_SINGLEPOSFORMAT1_HH */

View File

@ -0,0 +1,176 @@
#ifndef OT_LAYOUT_GPOS_SINGLEPOSFORMAT2_HH
#define OT_LAYOUT_GPOS_SINGLEPOSFORMAT2_HH
#include "Common.hh"
namespace OT {
namespace Layout {
namespace GPOS_impl {
struct SinglePosFormat2
{
protected:
HBUINT16 format; /* Format identifier--format = 2 */
Offset16To<Coverage>
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 (likely (index == NOT_COVERED)) return_trace (false);
if (unlikely (index >= valueCount)) return_trace (false);
if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
{
c->buffer->message (c->font,
"positioning glyph at %d",
c->buffer->idx);
}
valueFormat.apply_value (c, this,
&values[index * valueFormat.get_len ()],
buffer->cur_pos());
if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
{
c->buffer->message (c->font,
"positioned glyph at %d",
c->buffer->idx);
}
buffer->idx++;
return_trace (true);
}
bool
position_single (hb_font_t *font,
hb_direction_t direction,
hb_codepoint_t gid,
hb_glyph_position_t &pos) const
{
unsigned int index = (this+coverage).get_coverage (gid);
if (likely (index == NOT_COVERED)) return false;
if (unlikely (index >= valueCount)) return false;
/* This is ugly... */
hb_buffer_t buffer;
buffer.props.direction = direction;
OT::hb_ot_apply_context_t c (1, font, &buffer);
valueFormat.apply_value (&c, this,
&values[index * valueFormat.get_len ()],
pos);
return true;
}
template<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);
}
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));
})
;
bool ret = bool (it);
SinglePos_serialize (c->serializer, this, it, c->plan->layout_variation_idx_delta_map, c->plan->all_axes_pinned);
return_trace (ret);
}
};
}
}
}
#endif /* OT_LAYOUT_GPOS_SINGLEPOSFORMAT2_HH */

View File

@ -0,0 +1,394 @@
#ifndef OT_LAYOUT_GPOS_VALUEFORMAT_HH
#define OT_LAYOUT_GPOS_VALUEFORMAT_HH
#include "../../../hb-ot-layout-gsubgpos.hh"
namespace OT {
namespace Layout {
namespace GPOS_impl {
typedef HBUINT16 Value;
typedef UnsizedArrayOf<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
IntType& operator = (uint16_t i) { v = i; return *this; }
unsigned int get_len () const { return hb_popcount ((unsigned int) *this); }
unsigned int get_size () const { return get_len () * Value::static_size; }
hb_vector_t<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 void *base,
const Value *values,
hb_glyph_position_t &glyph_pos) const
{
bool ret = false;
unsigned int format = *this;
if (!format) return ret;
hb_font_t *font = c->font;
bool horizontal =
#ifndef HB_NO_VERTICAL
HB_DIRECTION_IS_HORIZONTAL (c->direction)
#else
true
#endif
;
if (format & xPlacement) glyph_pos.x_offset += font->em_scale_x (get_short (values++, &ret));
if (format & yPlacement) glyph_pos.y_offset += font->em_scale_y (get_short (values++, &ret));
if (format & xAdvance) {
if (likely (horizontal)) glyph_pos.x_advance += font->em_scale_x (get_short (values, &ret));
values++;
}
/* y_advance values grow downward but font-space grows upward, hence negation */
if (format & yAdvance) {
if (unlikely (!horizontal)) glyph_pos.y_advance -= font->em_scale_y (get_short (values, &ret));
values++;
}
if (!has_device ()) return ret;
bool use_x_device = font->x_ppem || font->num_coords;
bool use_y_device = font->y_ppem || font->num_coords;
if (!use_x_device && !use_y_device) return ret;
const VariationStore &store = c->var_store;
auto *cache = c->var_store_cache;
/* pixel -> fractional pixel */
if (format & xPlaDevice) {
if (use_x_device) glyph_pos.x_offset += (base + get_device (values, &ret)).get_x_delta (font, store, cache);
values++;
}
if (format & yPlaDevice) {
if (use_y_device) glyph_pos.y_offset += (base + get_device (values, &ret)).get_y_delta (font, store, cache);
values++;
}
if (format & xAdvDevice) {
if (horizontal && use_x_device) glyph_pos.x_advance += (base + get_device (values, &ret)).get_x_delta (font, store, cache);
values++;
}
if (format & yAdvDevice) {
/* y_advance values grow downward but font-space grows upward, hence negation */
if (!horizontal && use_y_device) glyph_pos.y_advance -= (base + get_device (values, &ret)).get_y_delta (font, store, cache);
values++;
}
return ret;
}
unsigned int get_effective_format (const Value *values) const
{
unsigned int format = *this;
for (unsigned flag = xPlacement; flag <= yAdvDevice; flag = flag << 1) {
if (format & flag) should_drop (*values++, (Flags) flag, &format);
}
return format;
}
template<typename Iterator,
hb_requires (hb_is_iterator (Iterator))>
unsigned int get_effective_format (Iterator it) const {
unsigned int new_format = 0;
for (const hb_array_t<const Value>& values : it)
new_format = new_format | get_effective_format (&values);
return new_format;
}
void copy_values (hb_serialize_context_t *c,
unsigned int new_format,
const void *base,
const Value *values,
const hb_hashmap_t<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 (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 void *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++;
}
}
unsigned drop_device_table_flags () const
{
unsigned format = *this;
for (unsigned flag = xPlaDevice; flag <= yAdvDevice; flag = flag << 1)
format = format & ~flag;
return format;
}
private:
bool sanitize_value_devices (hb_sanitize_context_t *c, const void *base, const Value *values) const
{
unsigned int format = *this;
if (format & xPlacement) values++;
if (format & yPlacement) values++;
if (format & xAdvance) values++;
if (format & yAdvance) values++;
if ((format & xPlaDevice) && !get_device (values++).sanitize (c, base)) return false;
if ((format & yPlaDevice) && !get_device (values++).sanitize (c, base)) return false;
if ((format & xAdvDevice) && !get_device (values++).sanitize (c, base)) return false;
if ((format & yAdvDevice) && !get_device (values++).sanitize (c, base)) return false;
return true;
}
static inline Offset16To<Device>& get_device (Value* value)
{
return *static_cast<Offset16To<Device> *> (value);
}
static inline const Offset16To<Device>& get_device (const Value* value, bool *worked=nullptr)
{
if (worked) *worked |= bool (*value);
return *static_cast<const Offset16To<Device> *> (value);
}
void add_delta_to_value (HBINT16 *value,
const void *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 void *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 void *base, const Value *values) const
{
TRACE_SANITIZE (this);
return_trace (c->check_range (values, get_size ()) && (!has_device () || sanitize_value_devices (c, base, values)));
}
bool sanitize_values (hb_sanitize_context_t *c, const void *base, const Value *values, unsigned int count) const
{
TRACE_SANITIZE (this);
unsigned int len = get_len ();
if (!c->check_range (values, count, get_size ())) return_trace (false);
if (!has_device ()) return_trace (true);
for (unsigned int i = 0; i < count; i++) {
if (!sanitize_value_devices (c, base, values))
return_trace (false);
values += len;
}
return_trace (true);
}
/* Just sanitize referenced Device tables. Doesn't check the values themselves. */
bool sanitize_values_stride_unsafe (hb_sanitize_context_t *c, const void *base, const Value *values, unsigned int count, unsigned int stride) const
{
TRACE_SANITIZE (this);
if (!has_device ()) return_trace (true);
for (unsigned int i = 0; i < count; i++) {
if (!sanitize_value_devices (c, base, values))
return_trace (false);
values += stride;
}
return_trace (true);
}
private:
void should_drop (Value value, Flags flag, unsigned int* format) const
{
if (value) return;
*format = *format & ~flag;
}
};
}
}
}
#endif // #ifndef OT_LAYOUT_GPOS_VALUEFORMAT_HH

View File

@ -5,12 +5,13 @@
namespace OT {
namespace Layout {
namespace GSUB {
namespace GSUB_impl {
template <typename Types>
struct AlternateSet
{
protected:
Array16Of<HBGlyphID16>
Array16Of<typename Types::HBGlyphID>
alternates; /* Array of alternate GlyphIDs--in
* arbitrary order */
public:
@ -56,8 +57,23 @@ struct AlternateSet
if (unlikely (alt_index > count || alt_index == 0)) return_trace (false);
if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
{
c->buffer->sync_so_far ();
c->buffer->message (c->font,
"replacing glyph at %d (alternate substitution)",
c->buffer->idx);
}
c->replace_glyph (alternates[alt_index - 1]);
if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
{
c->buffer->message (c->font,
"replaced glyph at %d (alternate substitution)",
c->buffer->idx - 1);
}
return_trace (true);
}
@ -68,7 +84,7 @@ struct AlternateSet
{
if (alternates.len && alternate_count)
{
+ alternates.sub_array (start_offset, alternate_count)
+ alternates.as_array ().sub_array (start_offset, alternate_count)
| hb_sink (hb_array (alternate_glyphs, *alternate_count))
;
}

View File

@ -6,14 +6,17 @@
namespace OT {
namespace Layout {
namespace GSUB {
namespace GSUB_impl {
struct AlternateSubst
{
protected:
union {
HBUINT16 format; /* Format identifier */
AlternateSubstFormat1 format1;
HBUINT16 format; /* Format identifier */
AlternateSubstFormat1_2<SmallTypes> format1;
#ifndef HB_NO_BEYOND_64K
AlternateSubstFormat1_2<MediumTypes> format2;
#endif
} u;
public:
@ -24,10 +27,15 @@ struct AlternateSubst
if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
switch (u.format) {
case 1: return_trace (c->dispatch (u.format1, std::forward<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,
@ -42,6 +50,9 @@ struct AlternateSubst
default:return_trace (false);
}
}
/* TODO subset() should choose format. */
};
}

View File

@ -6,20 +6,21 @@
namespace OT {
namespace Layout {
namespace GSUB {
namespace GSUB_impl {
struct AlternateSubstFormat1
template <typename Types>
struct AlternateSubstFormat1_2
{
protected:
HBUINT16 format; /* Format identifier--format = 1 */
Offset16To<Coverage>
typename Types::template OffsetTo<Coverage>
coverage; /* Offset to Coverage table--from
* beginning of Substitution table */
Array16OfOffset16To<AlternateSet>
Array16Of<typename Types::template OffsetTo<AlternateSet<Types>>>
alternateSet; /* Array of AlternateSet tables
* ordered by Coverage Index */
public:
DEFINE_SIZE_ARRAY (6, alternateSet);
DEFINE_SIZE_ARRAY (2 + 2 * Types::size, alternateSet);
bool sanitize (hb_sanitize_context_t *c) const
{
@ -39,9 +40,8 @@ struct AlternateSubstFormat1
| hb_filter (c->parent_active_glyphs (), hb_first)
| hb_map (hb_second)
| hb_map (hb_add (this))
| hb_apply ([c] (const AlternateSet &_) { _.closure (c); })
| hb_apply ([c] (const AlternateSet<Types> &_) { _.closure (c); })
;
}
void closure_lookups (hb_closure_lookups_context_t *c) const {}
@ -52,7 +52,7 @@ struct AlternateSubstFormat1
+ hb_zip (this+coverage, alternateSet)
| hb_map (hb_second)
| hb_map (hb_add (this))
| hb_apply ([c] (const AlternateSet &_) { _.collect_glyphs (c); })
| hb_apply ([c] (const AlternateSet<Types> &_) { _.collect_glyphs (c); })
;
}

View File

@ -7,7 +7,7 @@
namespace OT {
namespace Layout {
namespace GSUB {
namespace GSUB_impl {
struct ChainContextSubst : ChainContext {};

View File

@ -6,7 +6,7 @@
namespace OT {
namespace Layout {
namespace GSUB {
namespace GSUB_impl {
typedef hb_pair_t<hb_codepoint_t, hb_codepoint_t> hb_codepoint_pair_t;

View File

@ -7,7 +7,7 @@
namespace OT {
namespace Layout {
namespace GSUB {
namespace GSUB_impl {
struct ContextSubst : Context {};

View File

@ -7,7 +7,7 @@
namespace OT {
namespace Layout {
namespace GSUB {
namespace GSUB_impl {
struct ExtensionSubst : Extension<ExtensionSubst>
{

View File

@ -1,16 +1,15 @@
#ifndef OT_LAYOUT_GSUB_GSUB_HH
#define OT_LAYOUT_GSUB_GSUB_HH
// TODO(garretrieger): move to new layout.
#include "../../../hb-ot-layout-gsubgpos.hh"
#include "Common.hh"
#include "SubstLookup.hh"
using OT::Layout::GSUB::SubstLookup;
namespace OT {
using Layout::GSUB_impl::SubstLookup;
namespace Layout {
namespace GSUB {
/*
* GSUB -- Glyph Substitution
@ -19,6 +18,8 @@ namespace 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
@ -26,12 +27,15 @@ struct GSUB : GSUBGPOS
bool subset (hb_subset_context_t *c) const
{
hb_subset_layout_context_t l (c, tableTag, c->plan->gsub_lookups, c->plan->gsub_langsys, c->plan->gsub_features);
hb_subset_layout_context_t l (c, tableTag);
return GSUBGPOS::subset<SubstLookup> (&l);
}
bool sanitize (hb_sanitize_context_t *c) const
{ return GSUBGPOS::sanitize<SubstLookup> (c); }
{
TRACE_SANITIZE (this);
return_trace (GSUBGPOS::sanitize<SubstLookup> (c));
}
HB_INTERNAL bool is_blocklisted (hb_blob_t *blob,
hb_face_t *face) const;
@ -45,11 +49,10 @@ struct GSUB : GSUBGPOS
};
}
}
struct GSUB_accelerator_t : Layout::GSUB::GSUB::accelerator_t {
GSUB_accelerator_t (hb_face_t *face) : Layout::GSUB::GSUB::accelerator_t (face) {}
struct GSUB_accelerator_t : Layout::GSUB::accelerator_t {
GSUB_accelerator_t (hb_face_t *face) : Layout::GSUB::accelerator_t (face) {}
};

View File

@ -5,18 +5,20 @@
namespace OT {
namespace Layout {
namespace GSUB {
namespace GSUB_impl {
template <typename Types>
struct Ligature
{
protected:
HBGlyphID16 ligGlyph; /* GlyphID of ligature to substitute */
HeadlessArrayOf<HBGlyphID16>
component; /* Array of component GlyphIDs--start
typename Types::HBGlyphID
ligGlyph; /* GlyphID of ligature to substitute */
HeadlessArrayOf<typename Types::HBGlyphID>
component; /* Array of component GlyphIDs--start
* with the second component--ordered
* in writing direction */
public:
DEFINE_SIZE_ARRAY (4, component);
DEFINE_SIZE_ARRAY (Types::size + 2, component);
bool sanitize (hb_sanitize_context_t *c) const
{
@ -62,7 +64,24 @@ struct Ligature
* as a "ligated" substitution. */
if (unlikely (count == 1))
{
if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
{
c->buffer->sync_so_far ();
c->buffer->message (c->font,
"replacing glyph at %d (ligature substitution)",
c->buffer->idx);
}
c->replace_glyph (ligGlyph);
if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
{
c->buffer->message (c->font,
"replaced glyph at %d (ligature substitution)",
c->buffer->idx - 1);
}
return_trace (true);
}
@ -83,6 +102,31 @@ struct Ligature
return_trace (false);
}
unsigned pos = 0;
if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
{
unsigned delta = c->buffer->sync_so_far ();
pos = c->buffer->idx;
char buf[HB_MAX_CONTEXT_LENGTH * 16] = {0};
char *p = buf;
match_end += delta;
for (unsigned i = 0; i < count; i++)
{
match_positions[i] += delta;
if (i)
*p++ = ',';
snprintf (p, sizeof(buf) - (p - buf), "%u", match_positions[i]);
p += strlen(p);
}
c->buffer->message (c->font,
"ligating glyphs at %s",
buf);
}
ligate_input (c,
count,
match_positions,
@ -90,6 +134,14 @@ struct Ligature
ligGlyph,
total_component_count);
if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
{
c->buffer->sync_so_far ();
c->buffer->message (c->font,
"ligated glyph at %d",
pos);
}
return_trace (true);
}

View File

@ -6,12 +6,13 @@
namespace OT {
namespace Layout {
namespace GSUB {
namespace GSUB_impl {
template <typename Types>
struct LigatureSet
{
protected:
Array16OfOffset16To<Ligature>
Array16OfOffset16To<Ligature<Types>>
ligature; /* Array LigatureSet tables
* ordered by preference */
public:
@ -28,7 +29,7 @@ struct LigatureSet
return
+ hb_iter (ligature)
| hb_map (hb_add (this))
| hb_map ([glyphs] (const Ligature &_) { return _.intersects (glyphs); })
| hb_map ([glyphs] (const Ligature<Types> &_) { return _.intersects (glyphs); })
| hb_any
;
}
@ -37,7 +38,7 @@ struct LigatureSet
{
+ hb_iter (ligature)
| hb_map (hb_add (this))
| hb_apply ([c] (const Ligature &_) { _.closure (c); })
| hb_apply ([c] (const Ligature<Types> &_) { _.closure (c); })
;
}
@ -45,7 +46,7 @@ struct LigatureSet
{
+ hb_iter (ligature)
| hb_map (hb_add (this))
| hb_apply ([c] (const Ligature &_) { _.collect_glyphs (c); })
| hb_apply ([c] (const Ligature<Types> &_) { _.collect_glyphs (c); })
;
}
@ -54,7 +55,7 @@ struct LigatureSet
return
+ hb_iter (ligature)
| hb_map (hb_add (this))
| hb_map ([c] (const Ligature &_) { return _.would_apply (c); })
| hb_map ([c] (const Ligature<Types> &_) { return _.would_apply (c); })
| hb_any
;
}
@ -65,7 +66,7 @@ struct LigatureSet
unsigned int num_ligs = ligature.len;
for (unsigned int i = 0; i < num_ligs; i++)
{
const Ligature &lig = this+ligature[i];
const auto &lig = this+ligature[i];
if (lig.apply (c)) return_trace (true);
}

View File

@ -6,14 +6,17 @@
namespace OT {
namespace Layout {
namespace GSUB {
namespace GSUB_impl {
struct LigatureSubst
{
protected:
union {
HBUINT16 format; /* Format identifier */
LigatureSubstFormat1 format1;
HBUINT16 format; /* Format identifier */
LigatureSubstFormat1_2<SmallTypes> format1;
#ifndef HB_NO_BEYOND_64K
LigatureSubstFormat1_2<MediumTypes> format2;
#endif
} u;
public:
@ -24,10 +27,16 @@ struct LigatureSubst
if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
switch (u.format) {
case 1: return_trace (c->dispatch (u.format1, std::forward<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,
@ -49,6 +58,9 @@ struct LigatureSubst
default:return_trace (false);
}
}
/* TODO subset() should choose format. */
};

View File

@ -6,20 +6,21 @@
namespace OT {
namespace Layout {
namespace GSUB {
namespace GSUB_impl {
struct LigatureSubstFormat1
template <typename Types>
struct LigatureSubstFormat1_2
{
protected:
HBUINT16 format; /* Format identifier--format = 1 */
Offset16To<Coverage>
typename Types::template OffsetTo<Coverage>
coverage; /* Offset to Coverage table--from
* beginning of Substitution table */
Array16OfOffset16To<LigatureSet>
Array16Of<typename Types::template OffsetTo<LigatureSet<Types>>>
ligatureSet; /* Array LigatureSet tables
* ordered by Coverage Index */
public:
DEFINE_SIZE_ARRAY (6, ligatureSet);
DEFINE_SIZE_ARRAY (4 + Types::size, ligatureSet);
bool sanitize (hb_sanitize_context_t *c) const
{
@ -33,7 +34,7 @@ struct LigatureSubstFormat1
+ hb_zip (this+coverage, ligatureSet)
| hb_filter (*glyphs, hb_first)
| hb_map (hb_second)
| hb_map ([this, glyphs] (const Offset16To<LigatureSet> &_)
| hb_map ([this, glyphs] (const typename Types::template OffsetTo<LigatureSet<Types>> &_)
{ return (this+_).intersects (glyphs); })
| hb_any
;
@ -48,7 +49,7 @@ struct LigatureSubstFormat1
| hb_filter (c->parent_active_glyphs (), hb_first)
| hb_map (hb_second)
| hb_map (hb_add (this))
| hb_apply ([c] (const LigatureSet &_) { _.closure (c); })
| hb_apply ([c] (const LigatureSet<Types> &_) { _.closure (c); })
;
}
@ -62,7 +63,7 @@ struct LigatureSubstFormat1
+ hb_zip (this+coverage, ligatureSet)
| hb_map (hb_second)
| hb_map (hb_add (this))
| hb_apply ([c] (const LigatureSet &_) { _.collect_glyphs (c); })
| hb_apply ([c] (const LigatureSet<Types> &_) { _.collect_glyphs (c); })
;
}
@ -73,7 +74,7 @@ struct LigatureSubstFormat1
unsigned int index = (this+coverage).get_coverage (c->glyphs[0]);
if (likely (index == NOT_COVERED)) return false;
const LigatureSet &lig_set = this+ligatureSet[index];
const auto &lig_set = this+ligatureSet[index];
return lig_set.would_apply (c);
}
@ -84,7 +85,7 @@ struct LigatureSubstFormat1
unsigned int index = (this+coverage).get_coverage (c->buffer->cur ().codepoint);
if (likely (index == NOT_COVERED)) return_trace (false);
const LigatureSet &lig_set = this+ligatureSet[index];
const auto &lig_set = this+ligatureSet[index];
return_trace (lig_set.apply (c));
}
@ -128,7 +129,7 @@ struct LigatureSubstFormat1
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& _) {
| hb_filter ([&] (const LigatureSet<Types>& _) {
return _.intersects (&glyphset);
}, hb_second)
| hb_map (hb_first)

View File

@ -6,14 +6,17 @@
namespace OT {
namespace Layout {
namespace GSUB {
namespace GSUB_impl {
struct MultipleSubst
{
protected:
union {
HBUINT16 format; /* Format identifier */
MultipleSubstFormat1 format1;
HBUINT16 format; /* Format identifier */
MultipleSubstFormat1_2<SmallTypes> format1;
#ifndef HB_NO_BEYOND_64K
MultipleSubstFormat1_2<MediumTypes> format2;
#endif
} u;
public:
@ -25,24 +28,30 @@ struct MultipleSubst
if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
switch (u.format) {
case 1: return_trace (c->dispatch (u.format1, std::forward<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,
hb_sorted_array_t<const HBGlyphID16> glyphs,
hb_array_t<const unsigned int> substitute_len_list,
hb_array_t<const HBGlyphID16> substitute_glyphs_list)
Iterator it)
{
TRACE_SERIALIZE (this);
if (unlikely (!c->extend_min (u.format))) return_trace (false);
unsigned int format = 1;
u.format = format;
switch (u.format) {
case 1: return_trace (u.format1.serialize (c, glyphs, substitute_len_list, substitute_glyphs_list));
case 1: return_trace (u.format1.serialize (c, it));
default:return_trace (false);
}
}
/* TODO subset() should choose format. */
};

View File

@ -6,20 +6,21 @@
namespace OT {
namespace Layout {
namespace GSUB {
namespace GSUB_impl {
struct MultipleSubstFormat1
template <typename Types>
struct MultipleSubstFormat1_2
{
protected:
HBUINT16 format; /* Format identifier--format = 1 */
Offset16To<Coverage>
typename Types::template OffsetTo<Coverage>
coverage; /* Offset to Coverage table--from
* beginning of Substitution table */
Array16OfOffset16To<Sequence>
Array16Of<typename Types::template OffsetTo<Sequence<Types>>>
sequence; /* Array of Sequence tables
* ordered by Coverage Index */
public:
DEFINE_SIZE_ARRAY (6, sequence);
DEFINE_SIZE_ARRAY (4 + Types::size, sequence);
bool sanitize (hb_sanitize_context_t *c) const
{
@ -39,7 +40,7 @@ struct MultipleSubstFormat1
| hb_filter (c->parent_active_glyphs (), hb_first)
| hb_map (hb_second)
| hb_map (hb_add (this))
| hb_apply ([c] (const Sequence &_) { _.closure (c); })
| hb_apply ([c] (const Sequence<Types> &_) { _.closure (c); })
;
}
@ -51,7 +52,7 @@ struct MultipleSubstFormat1
+ hb_zip (this+coverage, sequence)
| hb_map (hb_second)
| hb_map (hb_add (this))
| hb_apply ([c] (const Sequence &_) { _.collect_glyphs (c); })
| hb_apply ([c] (const Sequence<Types> &_) { _.collect_glyphs (c); })
;
}
@ -70,22 +71,31 @@ struct MultipleSubstFormat1
return_trace ((this+sequence[index]).apply (c));
}
template<typename Iterator,
hb_requires (hb_is_sorted_iterator (Iterator))>
bool serialize (hb_serialize_context_t *c,
hb_sorted_array_t<const HBGlyphID16> glyphs,
hb_array_t<const unsigned int> substitute_len_list,
hb_array_t<const HBGlyphID16> substitute_glyphs_list)
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, glyphs.length))) return_trace (false);
for (unsigned int i = 0; i < glyphs.length; i++)
if (unlikely (!sequence.serialize (c, sequences.length))) return_trace (false);
for (auto& pair : hb_zip (sequences, sequence))
{
unsigned int substitute_len = substitute_len_list[i];
if (unlikely (!sequence[i]
.serialize_serialize (c, substitute_glyphs_list.sub_array (0, substitute_len))))
if (unlikely (!pair.second
.serialize_serialize (c, pair.first)))
return_trace (false);
substitute_glyphs_list += substitute_len;
}
return_trace (coverage.serialize_serialize (c, glyphs));
}

View File

@ -6,7 +6,7 @@
namespace OT {
namespace Layout {
namespace GSUB {
namespace GSUB_impl {
struct ReverseChainSingleSubst
{

View File

@ -5,7 +5,7 @@
namespace OT {
namespace Layout {
namespace GSUB {
namespace GSUB_impl {
struct ReverseChainSingleSubstFormat1
{
@ -33,10 +33,10 @@ struct ReverseChainSingleSubstFormat1
TRACE_SANITIZE (this);
if (!(coverage.sanitize (c, this) && backtrack.sanitize (c, this)))
return_trace (false);
const Array16OfOffset16To<Coverage> &lookahead = StructAfter<Array16OfOffset16To<Coverage>> (backtrack);
const auto &lookahead = StructAfter<decltype (lookaheadX)> (backtrack);
if (!lookahead.sanitize (c, this))
return_trace (false);
const Array16Of<HBGlyphID16> &substitute = StructAfter<Array16Of<HBGlyphID16>> (lookahead);
const auto &substitute = StructAfter<decltype (substituteX)> (lookahead);
return_trace (substitute.sanitize (c));
}
@ -45,7 +45,7 @@ struct ReverseChainSingleSubstFormat1
if (!(this+coverage).intersects (glyphs))
return false;
const Array16OfOffset16To<Coverage> &lookahead = StructAfter<Array16OfOffset16To<Coverage>> (backtrack);
const auto &lookahead = StructAfter<decltype (lookaheadX)> (backtrack);
unsigned int count;
@ -69,8 +69,8 @@ struct ReverseChainSingleSubstFormat1
{
if (!intersects (c->glyphs)) return;
const Array16OfOffset16To<Coverage> &lookahead = StructAfter<Array16OfOffset16To<Coverage>> (backtrack);
const Array16Of<HBGlyphID16> &substitute = StructAfter<Array16Of<HBGlyphID16>> (lookahead);
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)
@ -91,12 +91,12 @@ struct ReverseChainSingleSubstFormat1
for (unsigned int i = 0; i < count; i++)
if (unlikely (!(this+backtrack[i]).collect_coverage (c->before))) return;
const Array16OfOffset16To<Coverage> &lookahead = StructAfter<Array16OfOffset16To<Coverage>> (backtrack);
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 Array16Of<HBGlyphID16> &substitute = StructAfter<Array16Of<HBGlyphID16>> (lookahead);
const auto &substitute = StructAfter<decltype (substituteX)> (lookahead);
count = substitute.len;
c->output->add_array (substitute.arrayZ, substitute.len);
}
@ -115,8 +115,8 @@ struct ReverseChainSingleSubstFormat1
unsigned int index = (this+coverage).get_coverage (c->buffer->cur ().codepoint);
if (likely (index == NOT_COVERED)) return_trace (false);
const Array16OfOffset16To<Coverage> &lookahead = StructAfter<Array16OfOffset16To<Coverage>> (backtrack);
const Array16Of<HBGlyphID16> &substitute = StructAfter<Array16Of<HBGlyphID16>> (lookahead);
const auto &lookahead = StructAfter<decltype (lookaheadX)> (backtrack);
const auto &substitute = StructAfter<decltype (substituteX)> (lookahead);
if (unlikely (index >= substitute.len)) return_trace (false);
@ -131,7 +131,23 @@ struct ReverseChainSingleSubstFormat1
c->buffer->idx + 1, &end_index))
{
c->buffer->unsafe_to_break_from_outbuffer (start_index, end_index);
if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
{
c->buffer->message (c->font,
"replacing glyph at %d (reverse chaining substitution)",
c->buffer->idx);
}
c->replace_glyph_inplace (substitute[index]);
if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
{
c->buffer->message (c->font,
"replaced glyph at %d (reverse chaining substitution)",
c->buffer->idx);
}
/* Note: We DON'T decrease buffer->idx. The main loop does it
* for us. This is useful for preventing surprises if someone
* calls us through a Context lookup. */
@ -206,8 +222,8 @@ struct ReverseChainSingleSubstFormat1
const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
const hb_map_t &glyph_map = *c->plan->glyph_map;
const Array16OfOffset16To<Coverage> &lookahead = StructAfter<Array16OfOffset16To<Coverage>> (backtrack);
const Array16Of<HBGlyphID16> &substitute = StructAfter<Array16Of<HBGlyphID16>> (lookahead);
const auto &lookahead = StructAfter<decltype (lookaheadX)> (backtrack);
const auto &substitute = StructAfter<decltype (substituteX)> (lookahead);
auto it =
+ hb_zip (this+coverage, substitute)

View File

@ -5,12 +5,13 @@
namespace OT {
namespace Layout {
namespace GSUB {
namespace GSUB_impl {
template <typename Types>
struct Sequence
{
protected:
Array16Of<HBGlyphID16>
Array16Of<typename Types::HBGlyphID>
substitute; /* String of GlyphIDs to substitute */
public:
DEFINE_SIZE_ARRAY (2, substitute);
@ -39,17 +40,58 @@ struct Sequence
* as a "multiplied" substitution. */
if (unlikely (count == 1))
{
if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
{
c->buffer->sync_so_far ();
c->buffer->message (c->font,
"replacing glyph at %d (multiple substitution)",
c->buffer->idx);
}
c->replace_glyph (substitute.arrayZ[0]);
if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
{
c->buffer->message (c->font,
"replaced glyph at %d (multiple subtitution)",
c->buffer->idx - 1);
}
return_trace (true);
}
/* Spec disallows this, but Uniscribe allows it.
* https://github.com/harfbuzz/harfbuzz/issues/253 */
else if (unlikely (count == 0))
{
if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
{
c->buffer->sync_so_far ();
c->buffer->message (c->font,
"deleting glyph at %d (multiple substitution)",
c->buffer->idx);
}
c->buffer->delete_glyph ();
if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
{
c->buffer->sync_so_far ();
c->buffer->message (c->font,
"deleted glyph at %d (multiple substitution)",
c->buffer->idx);
}
return_trace (true);
}
if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
{
c->buffer->sync_so_far ();
c->buffer->message (c->font,
"multiplying glyph at %d",
c->buffer->idx);
}
unsigned int klass = _hb_glyph_info_is_ligature (&c->buffer->cur()) ?
HB_OT_LAYOUT_GLYPH_PROPS_BASE_GLYPH : 0;
unsigned lig_id = _hb_glyph_info_get_lig_id (&c->buffer->cur());
@ -64,6 +106,26 @@ struct Sequence
}
c->buffer->skip_glyph ();
if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
{
c->buffer->sync_so_far ();
char buf[HB_MAX_CONTEXT_LENGTH * 16] = {0};
char *p = buf;
for (unsigned i = c->buffer->idx - count; i < c->buffer->idx; i++)
{
if (buf < p)
*p++ = ',';
snprintf (p, sizeof(buf) - (p - buf), "%u", i);
p += strlen(p);
}
c->buffer->message (c->font,
"multiplied glyphs at %s",
buf);
}
return_trace (true);
}

View File

@ -7,15 +7,19 @@
namespace OT {
namespace Layout {
namespace GSUB {
namespace GSUB_impl {
struct SingleSubst
{
protected:
union {
HBUINT16 format; /* Format identifier */
SingleSubstFormat1 format1;
SingleSubstFormat2 format2;
HBUINT16 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:
@ -28,6 +32,10 @@ struct SingleSubst
switch (u.format) {
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 ());
}
}
@ -45,11 +53,24 @@ struct SingleSubst
if (glyphs)
{
format = 1;
hb_codepoint_t mask = 0xFFFFu;
#ifndef HB_NO_BEYOND_64K
if (+ glyphs
| hb_map_retains_sorting (hb_first)
| hb_filter ([] (hb_codepoint_t gid) { return gid > 0xFFFFu; }))
{
format += 2;
mask = 0xFFFFFFu;
}
#endif
auto get_delta = [=] (hb_codepoint_pair_t _)
{ return (unsigned) (_.second - _.first) & 0xFFFF; };
{ return (unsigned) (_.second - _.first) & mask; };
delta = get_delta (*glyphs);
if (!hb_all (++(+glyphs), delta, get_delta)) format = 2;
if (!hb_all (++(+glyphs), delta, get_delta)) format += 1;
}
u.format = format;
switch (u.format) {
case 1: return_trace (u.format1.serialize (c,
@ -57,6 +78,13 @@ struct SingleSubst
| 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);
}
}

View File

@ -5,20 +5,22 @@
namespace OT {
namespace Layout {
namespace GSUB {
namespace GSUB_impl {
struct SingleSubstFormat1
template <typename Types>
struct SingleSubstFormat1_3
{
protected:
HBUINT16 format; /* Format identifier--format = 1 */
Offset16To<Coverage>
typename Types::template OffsetTo<Coverage>
coverage; /* Offset to Coverage table--from
* beginning of Substitution table */
HBUINT16 deltaGlyphID; /* Add to original GlyphID to get
typename Types::HBUINT
deltaGlyphID; /* Add to original GlyphID to get
* substitute GlyphID, modulo 0x10000 */
public:
DEFINE_SIZE_STATIC (6);
DEFINE_SIZE_STATIC (2 + 2 * Types::size);
bool sanitize (hb_sanitize_context_t *c) const
{
@ -26,6 +28,9 @@ struct SingleSubstFormat1
return_trace (coverage.sanitize (c, this) && deltaGlyphID.sanitize (c));
}
hb_codepoint_t get_mask () const
{ return (1 << (8 * Types::size)) - 1; }
bool intersects (const hb_set_t *glyphs) const
{ return (this+coverage).intersects (glyphs); }
@ -34,14 +39,33 @@ struct SingleSubstFormat1
void closure (hb_closure_context_t *c) const
{
unsigned d = deltaGlyphID;
hb_codepoint_t d = deltaGlyphID;
hb_codepoint_t mask = get_mask ();
+ hb_iter (this+coverage)
| hb_filter (c->parent_active_glyphs ())
| hb_map ([d] (hb_codepoint_t g) { return (g + d) & 0xFFFFu; })
/* 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 {}
@ -49,9 +73,11 @@ struct SingleSubstFormat1
void collect_glyphs (hb_collect_glyphs_context_t *c) const
{
if (unlikely (!(this+coverage).collect_coverage (c->input))) return;
unsigned d = deltaGlyphID;
hb_codepoint_t d = deltaGlyphID;
hb_codepoint_t mask = get_mask ();
+ hb_iter (this+coverage)
| hb_map ([d] (hb_codepoint_t g) { return (g + d) & 0xFFFFu; })
| hb_map ([d, mask] (hb_codepoint_t g) { return (g + d) & mask; })
| hb_sink (c->output)
;
}
@ -68,11 +94,28 @@ struct SingleSubstFormat1
unsigned int index = (this+coverage).get_coverage (glyph_id);
if (likely (index == NOT_COVERED)) return_trace (false);
/* According to the Adobe Annotated OpenType Suite, result is always
* limited to 16bit. */
glyph_id = (glyph_id + deltaGlyphID) & 0xFFFFu;
hb_codepoint_t d = deltaGlyphID;
hb_codepoint_t mask = get_mask ();
glyph_id = (glyph_id + d) & mask;
if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
{
c->buffer->sync_so_far ();
c->buffer->message (c->font,
"replacing glyph at %d (single substitution)",
c->buffer->idx);
}
c->replace_glyph (glyph_id);
if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
{
c->buffer->message (c->font,
"replaced glyph at %d (single substitution)",
c->buffer->idx - 1);
}
return_trace (true);
}
@ -95,14 +138,17 @@ struct SingleSubstFormat1
const hb_set_t &glyphset = *c->plan->glyphset_gsub ();
const hb_map_t &glyph_map = *c->plan->glyph_map;
hb_codepoint_t delta = deltaGlyphID;
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 (this+coverage)
| hb_filter (glyphset)
| hb_map_retains_sorting ([&] (hb_codepoint_t g) {
+ hb_iter (intersection)
| hb_map_retains_sorting ([d, mask] (hb_codepoint_t g) {
return hb_codepoint_pair_t (g,
(g + delta) & 0xFFFF); })
(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]); })

View File

@ -5,21 +5,22 @@
namespace OT {
namespace Layout {
namespace GSUB {
namespace GSUB_impl {
struct SingleSubstFormat2
template <typename Types>
struct SingleSubstFormat2_4
{
protected:
HBUINT16 format; /* Format identifier--format = 2 */
Offset16To<Coverage>
typename Types::template OffsetTo<Coverage>
coverage; /* Offset to Coverage table--from
* beginning of Substitution table */
Array16Of<HBGlyphID16>
Array16Of<typename Types::HBGlyphID>
substitute; /* Array of substitute
* GlyphIDs--ordered by Coverage Index */
public:
DEFINE_SIZE_ARRAY (6, substitute);
DEFINE_SIZE_ARRAY (4 + Types::size, substitute);
bool sanitize (hb_sanitize_context_t *c) const
{
@ -35,12 +36,27 @@ struct SingleSubstFormat2
void closure (hb_closure_context_t *c) const
{
+ hb_zip (this+coverage, substitute)
| hb_filter (c->parent_active_glyphs (), hb_first)
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 {}
@ -67,8 +83,23 @@ struct SingleSubstFormat2
if (unlikely (index >= substitute.len)) return_trace (false);
if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
{
c->buffer->sync_so_far ();
c->buffer->message (c->font,
"replacing glyph at %d (single substitution)",
c->buffer->idx);
}
c->replace_glyph (substitute[index]);
if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
{
c->buffer->message (c->font,
"replaced glyph at %d (single substitution)",
c->buffer->idx - 1);
}
return_trace (true);
}
@ -103,7 +134,7 @@ struct SingleSubstFormat2
+ 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
| 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]); })
;

View File

@ -6,11 +6,11 @@
namespace OT {
namespace Layout {
namespace GSUB {
namespace GSUB_impl {
struct SubstLookup : Lookup
{
typedef SubstLookupSubTable SubTable;
using SubTable = SubstLookupSubTable;
bool sanitize (hb_sanitize_context_t *c) const
{ return Lookup::sanitize<SubTable> (c); }
@ -25,7 +25,7 @@ struct SubstLookup : Lookup
{
unsigned int type = get_type ();
if (unlikely (type == SubTable::Extension))
return reinterpret_cast<const ExtensionSubst &> (get_subtable (0)).is_reverse ();
return get_subtable (0).u.extension.is_reverse ();
return lookup_type_is_reverse (type);
}
@ -73,8 +73,6 @@ struct SubstLookup : Lookup
return hb_closure_lookups_context_t::default_return_value ();
}
c->set_recurse_func (dispatch_closure_lookups_recurse_func);
hb_closure_lookups_context_t::return_t ret = dispatch (c);
return ret;
}
@ -100,12 +98,15 @@ struct SubstLookup : Lookup
return dispatch (c);
}
static inline bool apply_recurse_func (hb_ot_apply_context_t *c, unsigned int lookup_index);
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,
hb_sorted_array_t<const HBGlyphID16> glyphs,
hb_array_t<const HBGlyphID16> substitutes)
Glyphs glyphs,
Substitutes substitutes)
{
TRACE_SERIALIZE (this);
if (unlikely (!Lookup::serialize (c, SubTable::Single, lookup_props, 1))) return_trace (false);
@ -118,19 +119,16 @@ struct SubstLookup : Lookup
return_trace (false);
}
bool serialize_multiple (hb_serialize_context_t *c,
uint32_t lookup_props,
hb_sorted_array_t<const HBGlyphID16> glyphs,
hb_array_t<const unsigned int> substitute_len_list,
hb_array_t<const HBGlyphID16> substitute_glyphs_list)
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,
glyphs,
substitute_len_list,
substitute_glyphs_list))
serialize (c, it))
{
c->add_link (get_subtables<SubTable> ()[0], c->pop_pack ());
return_trace (true);
@ -206,8 +204,6 @@ struct SubstLookup : Lookup
return ret;
}
HB_INTERNAL static hb_closure_lookups_context_t::return_t dispatch_closure_lookups_recurse_func (hb_closure_lookups_context_t *c, unsigned 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)...); }

View File

@ -13,7 +13,7 @@
namespace OT {
namespace Layout {
namespace GSUB {
namespace GSUB_impl {
struct SubstLookupSubTable
{

View File

@ -0,0 +1,66 @@
/*
* Copyright © 2007,2008,2009 Red Hat, Inc.
* Copyright © 2010,2012 Google, Inc.
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Red Hat Author(s): Behdad Esfahbod
* Google Author(s): Behdad Esfahbod, Garret Rieger
*/
#ifndef OT_LAYOUT_TYPES_HH
#define OT_LAYOUT_TYPES_HH
namespace OT {
namespace Layout {
struct SmallTypes {
static constexpr unsigned size = 2;
using large_int = uint32_t;
using HBUINT = HBUINT16;
using HBGlyphID = HBGlyphID16;
using Offset = Offset16;
template <typename Type, bool has_null=true>
using OffsetTo = OT::Offset16To<Type, 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, bool has_null=true>
using OffsetTo = OT::Offset24To<Type, has_null>;
template <typename Type>
using ArrayOf = OT::Array24Of<Type>;
template <typename Type>
using SortedArrayOf = OT::SortedArray24Of<Type>;
};
}
}
#endif /* OT_LAYOUT_TYPES_HH */

View File

@ -0,0 +1,369 @@
#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:
unsigned int get_size () const
{
unsigned int size = min_size;
/* glyphIndex is 24bit instead of 16bit */
#ifndef HB_NO_BEYOND_64K
if (flags & GID_IS_24BIT) size += HBGlyphID24::static_size - HBGlyphID16::static_size;
#endif
/* arg1 and 2 are int16 */
if (flags & ARG_1_AND_2_ARE_WORDS) size += 4;
/* arg1 and 2 are int8 */
else size += 2;
/* One x 16 bit (scale) */
if (flags & WE_HAVE_A_SCALE) size += 2;
/* Two x 16 bit (xscale, yscale) */
else if (flags & WE_HAVE_AN_X_AND_Y_SCALE) size += 4;
/* Four x 16 bit (xscale, scale01, scale10, yscale) */
else if (flags & WE_HAVE_A_TWO_BY_TWO) size += 8;
return size;
}
void drop_instructions_flag () { flags = (uint16_t) flags & ~WE_HAVE_INSTRUCTIONS; }
void set_overlaps_flag ()
{
flags = (uint16_t) flags | OVERLAP_COMPOUND;
}
bool has_instructions () const { return flags & WE_HAVE_INSTRUCTIONS; }
bool has_more () const { return flags & MORE_COMPONENTS; }
bool is_use_my_metrics () const { return flags & USE_MY_METRICS; }
bool is_anchored () const { return !(flags & ARGS_ARE_XY_VALUES); }
void get_anchor_points (unsigned int &point1, unsigned int &point2) const
{
const auto *p = &StructAfter<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];
}
}
void transform_points (contour_point_vector_t &points) const
{
float matrix[4];
contour_point_t trans;
if (get_transformation (matrix, trans))
{
if (scaled_offsets ())
{
points.translate (trans);
points.transform (matrix);
}
else
{
points.transform (matrix);
points.translate (trans);
}
}
}
unsigned compile_with_deltas (const contour_point_t &p_delta,
char *out) const
{
const HBINT8 *p = &StructAfter<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 and update value with deltas
hb_memcpy (out, this, len);
const HBINT16 *px = reinterpret_cast<const HBINT16 *> (p);
HBINT16 *o = reinterpret_cast<HBINT16 *> (out + len_before_val);
o[0] = px[0] + roundf (p_delta.x);
o[1] = px[1] + roundf (p_delta.y);
}
else
{
int new_x = p[0] + roundf (p_delta.x);
int new_y = p[1] + roundf (p_delta.y);
if (new_x <= 127 && new_x >= -128 &&
new_y <= 127 && new_y >= -128)
{
hb_memcpy (out, this, len);
HBINT8 *o = reinterpret_cast<HBINT8 *> (out + len_before_val);
o[0] = new_x;
o[1] = new_y;
}
else
{
// int8 overflows after deltas applied
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; }
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;
trans.init ((float) tx, (float) ty);
{
const F2DOT14 *points = (const F2DOT14 *) p;
if (flags & WE_HAVE_A_SCALE)
{
matrix[0] = matrix[3] = points[0].to_float ();
return true;
}
else if (flags & WE_HAVE_AN_X_AND_Y_SCALE)
{
matrix[0] = points[0].to_float ();
matrix[3] = points[1].to_float ();
return true;
}
else if (flags & WE_HAVE_A_TWO_BY_TWO)
{
matrix[0] = points[0].to_float ();
matrix[1] = points[1].to_float ();
matrix[2] = points[2].to_float ();
matrix[3] = points[3].to_float ();
return true;
}
}
return tx || ty;
}
public:
hb_codepoint_t get_gid () const
{
#ifndef HB_NO_BEYOND_64K
if (flags & GID_IS_24BIT)
return StructAfter<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;
}
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 &deltas,
hb_bytes_t &dest_bytes /* OUT */)
{
if (source_bytes.length <= GlyphHeader::static_size ||
header.numberOfContours != -1)
{
dest_bytes = hb_bytes_t ();
return true;
}
unsigned source_len = source_bytes.length - GlyphHeader::static_size;
/* try to allocate more memories than source glyph bytes
* in case that there might be an overflow for int8 value
* and we would need to use int16 instead */
char *o = (char *) hb_calloc (source_len + source_len/2, sizeof (char));
if (unlikely (!o)) return false;
const CompositeGlyphRecord *c = reinterpret_cast<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 deltas are phantom points and should not be included */
if (i >= deltas.length - 4) return false;
unsigned comp_len = component.get_size ();
if (component.is_anchored ())
{
hb_memcpy (p, &component, comp_len);
p += comp_len;
}
else
{
unsigned new_len = component.compile_with_deltas (deltas[i], p);
p += new_len;
}
i++;
source_comp_len += comp_len;
}
//copy instructions if any
if (source_len > source_comp_len)
{
unsigned instr_len = source_len - source_comp_len;
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 */

View File

@ -0,0 +1,463 @@
#ifndef OT_GLYF_GLYPH_HH
#define OT_GLYF_GLYPH_HH
#include "../../hb-open-type.hh"
#include "GlyphHeader.hh"
#include "SimpleGlyph.hh"
#include "CompositeGlyph.hh"
#include "VarCompositeGlyph.hh"
#include "coord-setter.hh"
namespace OT {
struct glyf_accelerator_t;
namespace glyf_impl {
#ifndef HB_GLYF_MAX_POINTS
#define HB_GLYF_MAX_POINTS 10000
#endif
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, VAR_COMPOSITE };
public:
composite_iter_t get_composite_iterator () const
{
if (type != COMPOSITE) return composite_iter_t ();
return CompositeGlyph (*header, bytes).iter ();
}
var_composite_iter_t get_var_composite_iterator () const
{
if (type != VAR_COMPOSITE) return var_composite_iter_t ();
return VarCompositeGlyph (*header, bytes).iter ();
}
const hb_bytes_t trim_padding () const
{
switch (type) {
case COMPOSITE: return CompositeGlyph (*header, bytes).trim_padding ();
case SIMPLE: return SimpleGlyph (*header, bytes).trim_padding ();
default: return bytes;
}
}
void drop_hints ()
{
switch (type) {
case COMPOSITE: CompositeGlyph (*header, bytes).drop_hints (); return;
case SIMPLE: SimpleGlyph (*header, bytes).drop_hints (); return;
default: return;
}
}
void set_overlaps_flag ()
{
switch (type) {
case COMPOSITE: CompositeGlyph (*header, bytes).set_overlaps_flag (); return;
case SIMPLE: SimpleGlyph (*header, bytes).set_overlaps_flag (); return;
default: return;
}
}
void drop_hints_bytes (hb_bytes_t &dest_start, hb_bytes_t &dest_end) const
{
switch (type) {
case COMPOSITE: CompositeGlyph (*header, bytes).drop_hints_bytes (dest_start); return;
case SIMPLE: SimpleGlyph (*header, bytes).drop_hints_bytes (dest_start, dest_end); return;
default: return;
}
}
void update_mtx (const hb_subset_plan_t *plan,
int xMin, int yMax,
const contour_point_vector_t &all_points) const
{
hb_codepoint_t new_gid = 0;
if (!plan->new_gid_for_old_gid (gid, &new_gid))
return;
unsigned len = all_points.length;
float leftSideX = all_points[len - 4].x;
float rightSideX = all_points[len - 3].x;
float topSideY = all_points[len - 2].y;
float bottomSideY = all_points[len - 1].y;
int hori_aw = roundf (rightSideX - leftSideX);
if (hori_aw < 0) hori_aw = 0;
int lsb = roundf (xMin - leftSideX);
plan->hmtx_map->set (new_gid, hb_pair (hori_aw, lsb));
int vert_aw = roundf (topSideY - bottomSideY);
if (vert_aw < 0) vert_aw = 0;
int tsb = roundf (topSideY - yMax);
plan->vmtx_map->set (new_gid, hb_pair (vert_aw, tsb));
}
bool compile_header_bytes (const hb_subset_plan_t *plan,
const contour_point_vector_t &all_points,
hb_bytes_t &dest_bytes /* OUT */) const
{
GlyphHeader *glyph_header = nullptr;
if (type != EMPTY && all_points.length > 4)
{
glyph_header = (GlyphHeader *) hb_calloc (1, GlyphHeader::static_size);
if (unlikely (!glyph_header)) return false;
}
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;
}
for (unsigned i = 1; i < all_points.length - 4; 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);
}
update_mtx (plan, roundf (xMin), roundf (yMax), all_points);
/*for empty glyphs: all_points only include phantom points.
*just update metrics and then return */
if (!glyph_header)
return true;
glyph_header->numberOfContours = header->numberOfContours;
glyph_header->xMin = roundf (xMin);
glyph_header->yMin = roundf (yMin);
glyph_header->xMax = roundf (xMax);
glyph_header->yMax = roundf (yMax);
dest_bytes = hb_bytes_t ((const char *)glyph_header, GlyphHeader::static_size);
return true;
}
bool compile_bytes_with_deltas (const hb_subset_plan_t *plan,
hb_font_t *font,
const glyf_accelerator_t &glyf,
hb_bytes_t &dest_start, /* IN/OUT */
hb_bytes_t &dest_end /* OUT */)
{
contour_point_vector_t all_points, deltas;
if (!get_points (font, glyf, all_points, &deltas, false, false))
return false;
// .notdef, set type to empty so we only update metrics and don't compile bytes for
// it
if (gid == 0 &&
!(plan->flags & HB_SUBSET_FLAGS_NOTDEF_OUTLINE))
type = EMPTY;
switch (type) {
case COMPOSITE:
if (!CompositeGlyph (*header, bytes).compile_bytes_with_deltas (dest_start,
deltas,
dest_end))
return false;
break;
case SIMPLE:
if (!SimpleGlyph (*header, bytes).compile_bytes_with_deltas (all_points,
plan->flags & HB_SUBSET_FLAGS_NO_HINTING,
dest_end))
return false;
break;
default:
/* set empty bytes for empty glyph
* do not use source glyph's pointers */
dest_start = hb_bytes_t ();
dest_end = hb_bytes_t ();
break;
}
if (!compile_header_bytes (plan, all_points, dest_start))
{
dest_end.fini ();
return false;
}
return true;
}
/* Note: Recursively calls itself.
* all_points includes phantom points
*/
template <typename accelerator_t>
bool get_points (hb_font_t *font, const accelerator_t &glyf_accelerator,
contour_point_vector_t &all_points /* OUT */,
contour_point_vector_t *deltas = nullptr, /* OUT */
bool shift_points_hori = true,
bool use_my_metrics = true,
bool phantom_only = false,
hb_array_t<int> coords = hb_array_t<int> (),
unsigned int depth = 0) const
{
if (unlikely (depth > HB_MAX_NESTING_LEVEL)) return false;
if (!coords)
coords = hb_array (font->coords, font->num_coords);
contour_point_vector_t stack_points;
bool inplace = type == SIMPLE && all_points.length == 0;
/* Load into all_points if it's empty, as an optimization. */
contour_point_vector_t &points = inplace ? all_points : stack_points;
switch (type) {
case SIMPLE:
if (unlikely (!SimpleGlyph (*header, bytes).get_contour_points (points, phantom_only)))
return false;
break;
case COMPOSITE:
{
/* pseudo component points for each component in composite glyph */
unsigned num_points = hb_len (CompositeGlyph (*header, bytes).iter ());
if (unlikely (!points.resize (num_points))) return false;
break;
}
#ifndef HB_NO_VAR_COMPOSITES
case VAR_COMPOSITE:
{
for (auto &item : get_var_composite_iterator ())
if (unlikely (!item.get_points (points))) return false;
}
#endif
default:
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);
{
int lsb = 0;
int h_delta = glyf_accelerator.hmtx->get_leading_bearing_without_var_unscaled (gid, &lsb) ?
(int) header->xMin - lsb : 0;
HB_UNUSED int tsb = 0;
int v_orig = (int) header->yMax +
#ifndef HB_NO_VERTICAL
((void) glyf_accelerator.vmtx->get_leading_bearing_without_var_unscaled (gid, &tsb), tsb)
#else
0
#endif
;
unsigned h_adv = glyf_accelerator.hmtx->get_advance_without_var_unscaled (gid);
unsigned v_adv =
#ifndef HB_NO_VERTICAL
glyf_accelerator.vmtx->get_advance_without_var_unscaled (gid)
#else
- font->face->get_upem ()
#endif
;
phantoms[PHANTOM_LEFT].x = h_delta;
phantoms[PHANTOM_RIGHT].x = h_adv + h_delta;
phantoms[PHANTOM_TOP].y = v_orig;
phantoms[PHANTOM_BOTTOM].y = v_orig - (int) v_adv;
}
if (deltas != nullptr && depth == 0 && type == COMPOSITE)
{
if (unlikely (!deltas->resize (points.length))) return false;
deltas->copy_vector (points);
}
#ifndef HB_NO_VAR
glyf_accelerator.gvar->apply_deltas_to_points (gid,
coords,
points.as_array ());
#endif
// mainly used by CompositeGlyph calculating new X/Y offset value so no need to extend it
// with child glyphs' points
if (deltas != nullptr && depth == 0 && type == COMPOSITE)
{
for (unsigned i = 0 ; i < points.length; i++)
{
deltas->arrayZ[i].x = points.arrayZ[i].x - deltas->arrayZ[i].x;
deltas->arrayZ[i].y = points.arrayZ[i].y - deltas->arrayZ[i].y;
}
}
switch (type) {
case SIMPLE:
if (!inplace)
all_points.extend (points.as_array ());
break;
case COMPOSITE:
{
contour_point_vector_t comp_points;
unsigned int comp_index = 0;
for (auto &item : get_composite_iterator ())
{
comp_points.reset ();
if (unlikely (!glyf_accelerator.glyph_for_gid (item.get_gid ())
.get_points (font,
glyf_accelerator,
comp_points,
deltas,
shift_points_hori,
use_my_metrics,
phantom_only,
coords,
depth + 1)))
return false;
/* 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];
/* Apply component transformation & translation */
item.transform_points (comp_points);
/* Apply translation from gvar */
comp_points.translate (points[comp_index]);
if (item.is_anchored ())
{
unsigned int p1, p2;
item.get_anchor_points (p1, p2);
if (likely (p1 < all_points.length && p2 < comp_points.length))
{
contour_point_t delta;
delta.init (all_points[p1].x - comp_points[p2].x,
all_points[p1].y - comp_points[p2].y);
comp_points.translate (delta);
}
}
all_points.extend (comp_points.as_array ().sub_array (0, comp_points.length - PHANTOM_COUNT));
if (all_points.length > HB_GLYF_MAX_POINTS)
return false;
comp_index++;
}
all_points.extend (phantoms);
} break;
#ifndef HB_NO_VAR_COMPOSITES
case VAR_COMPOSITE:
{
contour_point_vector_t comp_points;
hb_array_t<contour_point_t> points_left = points.as_array ();
for (auto &item : get_var_composite_iterator ())
{
hb_array_t<contour_point_t> record_points = points_left.sub_array (0, item.get_num_points ());
comp_points.reset ();
coord_setter_t coord_setter (coords);
item.set_variations (coord_setter, record_points);
if (unlikely (!glyf_accelerator.glyph_for_gid (item.get_gid ())
.get_points (font,
glyf_accelerator,
comp_points,
deltas,
shift_points_hori,
use_my_metrics,
phantom_only,
coord_setter.get_coords (),
depth + 1)))
return false;
/* Apply component transformation */
item.transform_points (record_points, comp_points);
/* Copy phantom points from component if USE_MY_METRICS flag set */
if (use_my_metrics && item.is_use_my_metrics ())
for (unsigned int i = 0; i < PHANTOM_COUNT; i++)
phantoms[i] = comp_points[comp_points.length - PHANTOM_COUNT + i];
all_points.extend (comp_points.as_array ().sub_array (0, comp_points.length - PHANTOM_COUNT));
if (all_points.length > HB_GLYF_MAX_POINTS)
return false;
points_left += item.get_num_points ();
}
all_points.extend (phantoms);
} break;
#endif
default:
all_points.extend (phantoms);
break;
}
if (depth == 0 && shift_points_hori) /* Apply at top level */
{
/* Undocumented rasterizer behavior:
* Shift points horizontally by the updated left side bearing
*/
contour_point_t delta;
delta.init (-phantoms[PHANTOM_LEFT].x, 0.f);
if (delta.x) all_points.translate (delta);
}
return !all_points.in_error ();
}
bool get_extents_without_var_scaled (hb_font_t *font, const glyf_accelerator_t &glyf_accelerator,
hb_glyph_extents_t *extents) const
{
if (type == EMPTY) return true; /* Empty glyph; zero extents. */
return header->get_extents_without_var_scaled (font, glyf_accelerator, gid, extents);
}
hb_bytes_t get_bytes () const { return bytes; }
Glyph () : bytes (),
header (bytes.as<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 == -2) type = VAR_COMPOSITE;
else type = COMPOSITE; /* negative numbers */
}
protected:
hb_bytes_t bytes;
const GlyphHeader *header;
hb_codepoint_t gid;
unsigned type;
};
} /* namespace glyf_impl */
} /* namespace OT */
#endif /* OT_GLYF_GLYPH_HH */

View File

@ -0,0 +1,50 @@
#ifndef OT_GLYF_GLYPHHEADER_HH
#define OT_GLYF_GLYPHHEADER_HH
#include "../../hb-open-type.hh"
namespace OT {
namespace glyf_impl {
struct GlyphHeader
{
bool has_data () const { return numberOfContours; }
template <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 = font->em_scale_x (lsb);
extents->y_bearing = font->em_scale_y (hb_max (yMin, yMax));
extents->width = font->em_scale_x (hb_max (xMin, xMax) - hb_min (xMin, xMax));
extents->height = font->em_scale_y (hb_min (yMin, yMax) - hb_max (yMin, yMax));
return true;
}
HBINT16 numberOfContours;
/* If the number of contours is
* greater than or equal to zero,
* this is a simple glyph; if negative,
* this is a composite glyph. */
FWORD xMin; /* Minimum x for coordinate data. */
FWORD yMin; /* Minimum y for coordinate data. */
FWORD xMax; /* Maximum x for coordinate data. */
FWORD yMax; /* Maximum y for coordinate data. */
public:
DEFINE_SIZE_STATIC (10);
};
} /* namespace glyf_impl */
} /* namespace OT */
#endif /* OT_GLYF_GLYPHHEADER_HH */

View File

@ -0,0 +1,339 @@
#ifndef OT_GLYF_SIMPLEGLYPH_HH
#define OT_GLYF_SIMPLEGLYPH_HH
#include "../../hb-open-type.hh"
namespace OT {
namespace glyf_impl {
struct SimpleGlyph
{
enum simple_glyph_flag_t
{
FLAG_ON_CURVE = 0x01,
FLAG_X_SHORT = 0x02,
FLAG_Y_SHORT = 0x04,
FLAG_REPEAT = 0x08,
FLAG_X_SAME = 0x10,
FLAG_Y_SAME = 0x20,
FLAG_OVERLAP_SIMPLE = 0x40,
FLAG_RESERVED2 = 0x80
};
const GlyphHeader &header;
hb_bytes_t bytes;
SimpleGlyph (const GlyphHeader &header_, hb_bytes_t bytes_) :
header (header_), bytes (bytes_) {}
unsigned int instruction_len_offset () const
{ return GlyphHeader::static_size + 2 * header.numberOfContours; }
unsigned int length (unsigned int instruction_len) const
{ return instruction_len_offset () + 2 + instruction_len; }
unsigned int instructions_length () const
{
unsigned int instruction_length_offset = instruction_len_offset ();
if (unlikely (instruction_length_offset + 2 > bytes.length)) return 0;
const HBUINT16 &instructionLength = StructAtOffset<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 ()
{
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 */,
contour_point_vector_t &points_ /* IN/OUT */,
const HBUINT8 *end)
{
unsigned count = points_.length;
for (unsigned int i = 0; i < count;)
{
if (unlikely (p + 1 > end)) return false;
uint8_t flag = *p++;
points_.arrayZ[i++].flag = flag;
if (flag & FLAG_REPEAT)
{
if (unlikely (p + 1 > end)) return false;
unsigned int repeat_count = *p++;
unsigned stop = hb_min (i + repeat_count, count);
for (; i < stop; i++)
points_.arrayZ[i].flag = flag;
}
}
return true;
}
static bool read_points (const HBUINT8 *&p /* IN/OUT */,
contour_point_vector_t &points_ /* IN/OUT */,
const HBUINT8 *end,
float contour_point_t::*m,
const simple_glyph_flag_t short_flag,
const simple_glyph_flag_t same_flag)
{
int v = 0;
unsigned count = points_.length;
for (unsigned i = 0; i < count; i++)
{
unsigned flag = points_[i].flag;
if (flag & short_flag)
{
if (unlikely (p + 1 > end)) return false;
if (flag & same_flag)
v += *p++;
else
v -= *p++;
}
else
{
if (!(flag & same_flag))
{
if (unlikely (p + HBINT16::static_size > end)) return false;
v += *(const HBINT16 *) p;
p += HBINT16::static_size;
}
}
points_.arrayZ[i].*m = v;
}
return true;
}
bool get_contour_points (contour_point_vector_t &points_ /* OUT */,
bool phantom_only = false) const
{
const HBUINT16 *endPtsOfContours = &StructAfter<HBUINT16> (header);
int num_contours = header.numberOfContours;
assert (num_contours);
/* One extra item at the end, for the instruction-count below. */
if (unlikely (!bytes.check_range (&endPtsOfContours[num_contours]))) return false;
unsigned int num_points = endPtsOfContours[num_contours - 1] + 1;
points_.alloc (num_points + 4); // Allocate for phantom points, to avoid a possible copy
if (!points_.resize (num_points)) return false;
if (phantom_only) return true;
for (int i = 0; i < num_contours; i++)
points_[endPtsOfContours[i]].is_end_point = true;
/* Skip instructions */
const HBUINT8 *p = &StructAtOffset<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,
uint8_t &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 (uint8_t &flag,
uint8_t &repeat,
uint8_t 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.push (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 (num_points))) return false;
if (unlikely (!x_coords.alloc (2*num_points))) return false;
if (unlikely (!y_coords.alloc (2*num_points))) return false;
uint8_t lastflag = 255, repeat = 0;
int prev_x = 0, prev_y = 0;
for (unsigned i = 0; i < num_points; i++)
{
uint8_t flag = all_points.arrayZ[i].flag;
flag &= FLAG_ON_CURVE + FLAG_OVERLAP_SIMPLE;
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 */

View File

@ -0,0 +1,93 @@
#ifndef OT_GLYF_SUBSETGLYPH_HH
#define OT_GLYF_SUBSETGLYPH_HH
#include "../../hb-open-type.hh"
namespace OT {
struct glyf_accelerator_t;
namespace glyf_impl {
struct SubsetGlyph
{
hb_codepoint_t old_gid;
Glyph source_glyph;
hb_bytes_t dest_start; /* region of source_glyph to copy first */
hb_bytes_t dest_end; /* region of source_glyph to copy second */
bool serialize (hb_serialize_context_t *c,
bool use_short_loca,
const hb_subset_plan_t *plan,
hb_font_t *font)
{
TRACE_SERIALIZE (this);
if (font)
{
const OT::glyf_accelerator_t &glyf = *font->face->table.glyf;
if (!this->compile_bytes_with_deltas (plan, font, glyf))
return_trace (false);
}
hb_bytes_t dest_glyph = dest_start.copy (c);
dest_glyph = hb_bytes_t (&dest_glyph, dest_glyph.length + dest_end.copy (c).length);
unsigned int pad_length = use_short_loca ? padding () : 0;
DEBUG_MSG (SUBSET, nullptr, "serialize %d byte glyph, width %d pad %d", dest_glyph.length, dest_glyph.length + pad_length, pad_length);
HBUINT8 pad;
pad = 0;
while (pad_length > 0)
{
c->embed (pad);
pad_length--;
}
if (unlikely (!dest_glyph.length)) return_trace (true);
/* update components gids */
for (auto &_ : Glyph (dest_glyph).get_composite_iterator ())
{
hb_codepoint_t new_gid;
if (plan->new_gid_for_old_gid (_.get_gid(), &new_gid))
const_cast<CompositeGlyphRecord &> (_).set_gid (new_gid);
}
if (plan->flags & HB_SUBSET_FLAGS_NO_HINTING)
Glyph (dest_glyph).drop_hints ();
if (plan->flags & HB_SUBSET_FLAGS_SET_OVERLAPS_FLAG)
Glyph (dest_glyph).set_overlaps_flag ();
return_trace (true);
}
bool compile_bytes_with_deltas (const hb_subset_plan_t *plan,
hb_font_t *font,
const glyf_accelerator_t &glyf)
{ return source_glyph.compile_bytes_with_deltas (plan, font, glyf, dest_start, dest_end); }
void free_compiled_bytes ()
{
dest_start.fini ();
dest_end.fini ();
}
void drop_hints_bytes ()
{ source_glyph.drop_hints_bytes (dest_start, dest_end); }
unsigned int length () const { return dest_start.length + dest_end.length; }
/* pad to 2 to ensure 2-byte loca will be ok */
unsigned int padding () const { return length () % 2; }
unsigned int padded_size () const { return length () + padding (); }
};
} /* namespace glyf_impl */
} /* namespace OT */
#endif /* OT_GLYF_SUBSETGLYPH_HH */

View File

@ -0,0 +1,353 @@
#ifndef OT_GLYF_VARCOMPOSITEGLYPH_HH
#define OT_GLYF_VARCOMPOSITEGLYPH_HH
#include "../../hb-open-type.hh"
#include "coord-setter.hh"
namespace OT {
namespace glyf_impl {
struct VarCompositeGlyphRecord
{
protected:
enum var_composite_glyph_flag_t
{
USE_MY_METRICS = 0x0001,
AXIS_INDICES_ARE_SHORT = 0x0002,
UNIFORM_SCALE = 0x0004,
HAVE_TRANSLATE_X = 0x0008,
HAVE_TRANSLATE_Y = 0x0010,
HAVE_ROTATION = 0x0020,
HAVE_SCALE_X = 0x0040,
HAVE_SCALE_Y = 0x0080,
HAVE_SKEW_X = 0x0100,
HAVE_SKEW_Y = 0x0200,
HAVE_TCENTER_X = 0x0400,
HAVE_TCENTER_Y = 0x0800,
GID_IS_24 = 0x1000,
AXES_HAVE_VARIATION = 0x2000,
};
public:
unsigned int get_size () const
{
unsigned int size = min_size;
unsigned axis_width = (flags & AXIS_INDICES_ARE_SHORT) ? 4 : 3;
size += numAxes * axis_width;
// gid
size += 2;
if (flags & GID_IS_24) size += 1;
if (flags & HAVE_TRANSLATE_X) size += 2;
if (flags & HAVE_TRANSLATE_Y) size += 2;
if (flags & HAVE_ROTATION) size += 2;
if (flags & HAVE_SCALE_X) size += 2;
if (flags & HAVE_SCALE_Y) size += 2;
if (flags & HAVE_SKEW_X) size += 2;
if (flags & HAVE_SKEW_Y) size += 2;
if (flags & HAVE_TCENTER_X) size += 2;
if (flags & HAVE_TCENTER_Y) size += 2;
return size;
}
bool has_more () const { return true; }
bool is_use_my_metrics () const { return flags & USE_MY_METRICS; }
hb_codepoint_t get_gid () const
{
if (flags & GID_IS_24)
return StructAfter<const HBGlyphID24> (numAxes);
else
return StructAfter<const HBGlyphID16> (numAxes);
}
unsigned get_numAxes () const
{
return numAxes;
}
unsigned get_num_points () const
{
unsigned num = 0;
if (flags & AXES_HAVE_VARIATION) num += numAxes;
if (flags & (HAVE_TRANSLATE_X | HAVE_TRANSLATE_Y)) num++;
if (flags & HAVE_ROTATION) num++;
if (flags & (HAVE_SCALE_X | HAVE_SCALE_Y)) num++;
if (flags & (HAVE_SKEW_X | HAVE_SKEW_Y)) num++;
if (flags & (HAVE_TCENTER_X | HAVE_TCENTER_Y)) num++;
return num;
}
void transform_points (hb_array_t<contour_point_t> record_points,
contour_point_vector_t &points) const
{
float matrix[4];
contour_point_t trans;
get_transformation_from_points (record_points, matrix, trans);
points.transform (matrix);
points.translate (trans);
}
static inline void transform (float (&matrix)[4], contour_point_t &trans,
float (other)[6])
{
// https://github.com/fonttools/fonttools/blob/f66ee05f71c8b57b5f519ee975e95edcd1466e14/Lib/fontTools/misc/transform.py#L268
float xx1 = other[0];
float xy1 = other[1];
float yx1 = other[2];
float yy1 = other[3];
float dx1 = other[4];
float dy1 = other[5];
float xx2 = matrix[0];
float xy2 = matrix[1];
float yx2 = matrix[2];
float yy2 = matrix[3];
float dx2 = trans.x;
float dy2 = trans.y;
matrix[0] = xx1*xx2 + xy1*yx2;
matrix[1] = xx1*xy2 + xy1*yy2;
matrix[2] = yx1*xx2 + yy1*yx2;
matrix[3] = yx1*xy2 + yy1*yy2;
trans.x = xx2*dx1 + yx2*dy1 + dx2;
trans.y = xy2*dx1 + yy2*dy1 + dy2;
}
static void translate (float (&matrix)[4], contour_point_t &trans,
float translateX, float translateY)
{
// https://github.com/fonttools/fonttools/blob/f66ee05f71c8b57b5f519ee975e95edcd1466e14/Lib/fontTools/misc/transform.py#L213
float other[6] = {1.f, 0.f, 0.f, 1.f, translateX, translateY};
transform (matrix, trans, other);
}
static void scale (float (&matrix)[4], contour_point_t &trans,
float scaleX, float scaleY)
{
// https://github.com/fonttools/fonttools/blob/f66ee05f71c8b57b5f519ee975e95edcd1466e14/Lib/fontTools/misc/transform.py#L224
float other[6] = {scaleX, 0.f, 0.f, scaleY, 0.f, 0.f};
transform (matrix, trans, other);
}
static void rotate (float (&matrix)[4], contour_point_t &trans,
float rotation)
{
// https://github.com/fonttools/fonttools/blob/f66ee05f71c8b57b5f519ee975e95edcd1466e14/Lib/fontTools/misc/transform.py#L240
rotation = rotation * float (M_PI);
float c = cosf (rotation);
float s = sinf (rotation);
float other[6] = {c, s, -s, c, 0.f, 0.f};
transform (matrix, trans, other);
}
static void skew (float (&matrix)[4], contour_point_t &trans,
float skewX, float skewY)
{
// https://github.com/fonttools/fonttools/blob/f66ee05f71c8b57b5f519ee975e95edcd1466e14/Lib/fontTools/misc/transform.py#L255
skewX = skewX * float (M_PI);
skewY = skewY * float (M_PI);
float other[6] = {1.f, tanf (skewY), tanf (skewX), 1.f, 0.f, 0.f};
transform (matrix, trans, other);
}
bool get_points (contour_point_vector_t &points) const
{
float translateX = 0.f;
float translateY = 0.f;
float rotation = 0.f;
float scaleX = 1.f * (1 << 12);
float scaleY = 1.f * (1 << 12);
float skewX = 0.f;
float skewY = 0.f;
float tCenterX = 0.f;
float tCenterY = 0.f;
if (unlikely (!points.resize (points.length + get_num_points ()))) return false;
unsigned axis_width = (flags & AXIS_INDICES_ARE_SHORT) ? 2 : 1;
unsigned axes_size = numAxes * axis_width;
const F2DOT14 *q = (const F2DOT14 *) (axes_size +
(flags & GID_IS_24 ? 3 : 2) +
&StructAfter<const HBUINT8> (numAxes));
hb_array_t<contour_point_t> rec_points = points.as_array ().sub_array (points.length - get_num_points ());
unsigned count = numAxes;
if (flags & AXES_HAVE_VARIATION)
{
for (unsigned i = 0; i < count; i++)
rec_points[i].x = *q++;
rec_points += count;
}
else
q += count;
const HBUINT16 *p = (const HBUINT16 *) q;
if (flags & HAVE_TRANSLATE_X) translateX = * (const FWORD *) p++;
if (flags & HAVE_TRANSLATE_Y) translateY = * (const FWORD *) p++;
if (flags & HAVE_ROTATION) rotation = * (const F2DOT14 *) p++;
if (flags & HAVE_SCALE_X) scaleX = * (const F4DOT12 *) p++;
if (flags & HAVE_SCALE_Y) scaleY = * (const F4DOT12 *) p++;
if (flags & HAVE_SKEW_X) skewX = * (const F2DOT14 *) p++;
if (flags & HAVE_SKEW_Y) skewY = * (const F2DOT14 *) p++;
if (flags & HAVE_TCENTER_X) tCenterX = * (const FWORD *) p++;
if (flags & HAVE_TCENTER_Y) tCenterY = * (const FWORD *) p++;
if ((flags & UNIFORM_SCALE) && !(flags & HAVE_SCALE_Y))
scaleY = scaleX;
if (flags & (HAVE_TRANSLATE_X | HAVE_TRANSLATE_Y))
{
rec_points[0].x = translateX;
rec_points[0].y = translateY;
rec_points++;
}
if (flags & HAVE_ROTATION)
{
rec_points[0].x = rotation;
rec_points++;
}
if (flags & (HAVE_SCALE_X | HAVE_SCALE_Y))
{
rec_points[0].x = scaleX;
rec_points[0].y = scaleY;
rec_points++;
}
if (flags & (HAVE_SKEW_X | HAVE_SKEW_Y))
{
rec_points[0].x = skewX;
rec_points[0].y = skewY;
rec_points++;
}
if (flags & (HAVE_TCENTER_X | HAVE_TCENTER_Y))
{
rec_points[0].x = tCenterX;
rec_points[0].y = tCenterY;
rec_points++;
}
assert (!rec_points);
return true;
}
void get_transformation_from_points (hb_array_t<contour_point_t> rec_points,
float (&matrix)[4], contour_point_t &trans) const
{
if (flags & AXES_HAVE_VARIATION)
rec_points += numAxes;
matrix[0] = matrix[3] = 1.f;
matrix[1] = matrix[2] = 0.f;
trans.init (0.f, 0.f);
float translateX = 0.f;
float translateY = 0.f;
float rotation = 0.f;
float scaleX = 1.f;
float scaleY = 1.f;
float skewX = 0.f;
float skewY = 0.f;
float tCenterX = 0.f;
float tCenterY = 0.f;
if (flags & (HAVE_TRANSLATE_X | HAVE_TRANSLATE_Y))
{
translateX = rec_points[0].x;
translateY = rec_points[0].y;
rec_points++;
}
if (flags & HAVE_ROTATION)
{
rotation = rec_points[0].x / (1 << 14);
rec_points++;
}
if (flags & (HAVE_SCALE_X | HAVE_SCALE_Y))
{
scaleX = rec_points[0].x / (1 << 12);
scaleY = rec_points[0].y / (1 << 12);
rec_points++;
}
if (flags & (HAVE_SKEW_X | HAVE_SKEW_Y))
{
skewX = rec_points[0].x / (1 << 14);
skewY = rec_points[0].y / (1 << 14);
rec_points++;
}
if (flags & (HAVE_TCENTER_X | HAVE_TCENTER_Y))
{
tCenterX = rec_points[0].x;
tCenterY = rec_points[0].y;
rec_points++;
}
assert (!rec_points);
translate (matrix, trans, translateX + tCenterX, translateY + tCenterY);
rotate (matrix, trans, rotation);
scale (matrix, trans, scaleX, scaleY);
skew (matrix, trans, -skewX, skewY);
translate (matrix, trans, -tCenterX, -tCenterY);
}
void set_variations (coord_setter_t &setter,
hb_array_t<contour_point_t> rec_points) const
{
bool have_variations = flags & AXES_HAVE_VARIATION;
unsigned axis_width = (flags & AXIS_INDICES_ARE_SHORT) ? 2 : 1;
const HBUINT8 *p = (const HBUINT8 *) (((HBUINT8 *) &numAxes) + numAxes.static_size + (flags & GID_IS_24 ? 3 : 2));
const HBUINT16 *q = (const HBUINT16 *) (((HBUINT8 *) &numAxes) + numAxes.static_size + (flags & GID_IS_24 ? 3 : 2));
const F2DOT14 *a = (const F2DOT14 *) ((HBUINT8 *) (axis_width == 1 ? (p + numAxes) : (HBUINT8 *) (q + numAxes)));
unsigned count = numAxes;
for (unsigned i = 0; i < count; i++)
{
unsigned axis_index = axis_width == 1 ? (unsigned) *p++ : (unsigned) *q++;
signed v = have_variations ? rec_points[i].x : *a++;
v += setter[axis_index];
v = hb_clamp (v, -(1<<14), (1<<14));
setter[axis_index] = v;
}
}
protected:
HBUINT16 flags;
HBUINT8 numAxes;
public:
DEFINE_SIZE_MIN (3);
};
using var_composite_iter_t = composite_iter_tmpl<VarCompositeGlyphRecord>;
struct VarCompositeGlyph
{
const GlyphHeader &header;
hb_bytes_t bytes;
VarCompositeGlyph (const GlyphHeader &header_, hb_bytes_t bytes_) :
header (header_), bytes (bytes_) {}
var_composite_iter_t iter () const
{ return var_composite_iter_t (bytes, &StructAfter<VarCompositeGlyphRecord, GlyphHeader> (header)); }
};
} /* namespace glyf_impl */
} /* namespace OT */
#endif /* OT_GLYF_VARCOMPOSITEGLYPH_HH */

View File

@ -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 */

View File

@ -0,0 +1,34 @@
#ifndef OT_GLYF_COORD_SETTER_HH
#define OT_GLYF_COORD_SETTER_HH
#include "../../hb.hh"
namespace OT {
namespace glyf_impl {
struct coord_setter_t
{
coord_setter_t (hb_array_t<int> coords) :
coords (coords) {}
int& operator [] (unsigned idx)
{
if (coords.length < idx + 1)
coords.resize (idx + 1);
return coords[idx];
}
hb_array_t<int> get_coords ()
{ return coords.as_array (); }
hb_vector_t<int> coords;
};
} /* namespace glyf_impl */
} /* namespace OT */
#endif /* OT_GLYF_COORD_SETTER_HH */

View File

@ -0,0 +1,90 @@
#ifndef OT_GLYF_GLYF_HELPERS_HH
#define OT_GLYF_GLYF_HELPERS_HH
#include "../../hb-open-type.hh"
#include "../../hb-subset-plan.hh"
#include "loca.hh"
namespace OT {
namespace glyf_impl {
template<typename IteratorIn, typename IteratorOut,
hb_requires (hb_is_source_of (IteratorIn, unsigned int)),
hb_requires (hb_is_sink_of (IteratorOut, unsigned))>
static void
_write_loca (IteratorIn&& it, bool short_offsets, IteratorOut&& dest)
{
unsigned right_shift = short_offsets ? 1 : 0;
unsigned int offset = 0;
dest << 0;
+ it
| hb_map ([=, &offset] (unsigned int padded_size)
{
offset += padded_size;
DEBUG_MSG (SUBSET, nullptr, "loca entry offset %d", offset);
return offset >> right_shift;
})
| hb_sink (dest)
;
}
static bool
_add_head_and_set_loca_version (hb_subset_plan_t *plan, bool use_short_loca)
{
hb_blob_t *head_blob = hb_sanitize_context_t ().reference_table<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;
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_plan_t * plan, Iterator padded_offsets, bool use_short_loca)
{
unsigned num_offsets = padded_offsets.len () + 1;
unsigned entry_size = use_short_loca ? 2 : 4;
char *loca_prime_data = (char *) hb_calloc (entry_size, num_offsets);
if (unlikely (!loca_prime_data)) return false;
DEBUG_MSG (SUBSET, nullptr, "loca entry_size %d num_offsets %d size %d",
entry_size, num_offsets, entry_size * num_offsets);
if (use_short_loca)
_write_loca (padded_offsets, true, hb_array ((HBUINT16 *) loca_prime_data, num_offsets));
else
_write_loca (padded_offsets, false, hb_array ((HBUINT32 *) loca_prime_data, num_offsets));
hb_blob_t *loca_blob = hb_blob_create (loca_prime_data,
entry_size * num_offsets,
HB_MEMORY_MODE_WRITABLE,
loca_prime_data,
hb_free);
bool result = plan->add_table (HB_OT_TAG_loca, loca_blob)
&& _add_head_and_set_loca_version (plan, use_short_loca);
hb_blob_destroy (loca_blob);
return result;
}
} /* namespace glyf_impl */
} /* namespace OT */
#endif /* OT_GLYF_GLYF_HELPERS_HH */

View File

@ -0,0 +1,448 @@
#ifndef OT_GLYF_GLYF_HH
#define OT_GLYF_GLYF_HH
#include "../../hb-open-type.hh"
#include "../../hb-ot-head-table.hh"
#include "../../hb-ot-hmtx-table.hh"
#include "../../hb-ot-var-gvar-table.hh"
#include "../../hb-draw.hh"
#include "glyf-helpers.hh"
#include "Glyph.hh"
#include "SubsetGlyph.hh"
#include "loca.hh"
#include "path-builder.hh"
namespace OT {
/*
* glyf -- TrueType Glyph Data
* https://docs.microsoft.com/en-us/typography/opentype/spec/glyf
*/
#define HB_OT_TAG_glyf HB_TAG('g','l','y','f')
struct glyf
{
friend struct glyf_accelerator_t;
static constexpr hb_tag_t tableTag = HB_OT_TAG_glyf;
bool sanitize (hb_sanitize_context_t *c HB_UNUSED) const
{
TRACE_SANITIZE (this);
/* Runtime checks as eager sanitizing each glyph is costy */
return_trace (true);
}
/* requires source of SubsetGlyph complains the identifier isn't declared */
template <typename Iterator>
bool serialize (hb_serialize_context_t *c,
Iterator it,
bool use_short_loca,
const hb_subset_plan_t *plan,
hb_font_t *font)
{
TRACE_SERIALIZE (this);
unsigned init_len = c->length ();
for (auto &_ : it)
if (unlikely (!_.serialize (c, use_short_loca, plan, font)))
return false;
/* As a special case when all glyph in the font are empty, add a zero byte
* to the table, so that OTS doesnt reject it, and to make the table work
* on Windows as well.
* See https://github.com/khaledhosny/ots/issues/52 */
if (init_len == c->length ())
{
HBUINT8 empty_byte;
empty_byte = 0;
c->copy (empty_byte);
}
return_trace (true);
}
/* Byte region(s) per glyph to output
unpadded, hints removed if so requested
If we fail to process a glyph we produce an empty (0-length) glyph */
bool subset (hb_subset_context_t *c) const
{
TRACE_SUBSET (this);
glyf *glyf_prime = c->serializer->start_embed <glyf> ();
if (unlikely (!c->serializer->check_success (glyf_prime))) return_trace (false);
hb_vector_t<glyf_impl::SubsetGlyph> glyphs;
_populate_subset_glyphs (c->plan, glyphs);
hb_font_t *font = nullptr;
if (!c->plan->pinned_at_default)
{
font = _create_font_for_instancing (c->plan);
if (unlikely (!font)) return false;
}
auto padded_offsets =
+ hb_iter (glyphs)
| hb_map (&glyf_impl::SubsetGlyph::padded_size)
;
bool use_short_loca = false;
if (likely (!c->plan->force_long_loca))
{
unsigned max_offset = + padded_offsets | hb_reduce (hb_add, 0);
use_short_loca = max_offset < 0x1FFFF;
}
glyf_prime->serialize (c->serializer, glyphs.writer (), use_short_loca, c->plan, font);
if (!use_short_loca) {
padded_offsets =
+ hb_iter (glyphs)
| hb_map (&glyf_impl::SubsetGlyph::length)
;
}
if (font)
{
_free_compiled_subset_glyphs (&glyphs);
hb_font_destroy (font);
}
if (unlikely (c->serializer->in_error ())) return_trace (false);
return_trace (c->serializer->check_success (glyf_impl::_add_loca_and_head (c->plan,
padded_offsets,
use_short_loca)));
}
void
_populate_subset_glyphs (const hb_subset_plan_t *plan,
hb_vector_t<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 _ : *glyphs)
_.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;
#endif
hmtx = nullptr;
#ifndef HB_NO_VERTICAL
vmtx = nullptr;
#endif
const OT::head &head = *face->table.head;
if (head.indexToLocFormat > 1 || head.glyphDataFormat > 0)
/* Unknown format. Leave num_glyphs=0, that takes care of disabling us. */
return;
short_offset = 0 == head.indexToLocFormat;
loca_table = face->table.loca.get_blob (); // Needs no destruct!
glyf_table = hb_sanitize_context_t ().reference_table<glyf> (face);
#ifndef HB_NO_VAR
gvar = face->table.gvar;
#endif
hmtx = face->table.hmtx;
#ifndef HB_NO_VERTICAL
vmtx = face->table.vmtx;
#endif
num_glyphs = hb_max (1u, loca_table.get_length () / (short_offset ? 2 : 4)) - 1;
num_glyphs = hb_min (num_glyphs, face->get_num_glyphs ());
}
~glyf_accelerator_t ()
{
glyf_table.destroy ();
}
bool has_data () const { return num_glyphs; }
protected:
template<typename T>
bool get_points (hb_font_t *font, hb_codepoint_t gid, T consumer) const
{
if (gid >= num_glyphs) return false;
/* Making this allocfree is not that easy
https://github.com/harfbuzz/harfbuzz/issues/2095
mostly because of gvar handling in VF fonts,
perhaps a separate path for non-VF fonts can be considered */
contour_point_vector_t all_points;
bool phantom_only = !consumer.is_consuming_contour_points ();
if (unlikely (!glyph_for_gid (gid).get_points (font, *this, all_points, nullptr, true, true, phantom_only)))
return false;
if (consumer.is_consuming_contour_points ())
{
unsigned count = all_points.length;
assert (count >= glyf_impl::PHANTOM_COUNT);
count -= glyf_impl::PHANTOM_COUNT;
for (unsigned point_index = 0; point_index < count; point_index++)
consumer.consume_point (all_points[point_index]);
consumer.points_end ();
}
/* Where to write phantoms, nullptr if not requested */
contour_point_t *phantoms = consumer.get_phantoms_sink ();
if (phantoms)
for (unsigned i = 0; i < glyf_impl::PHANTOM_COUNT; ++i)
phantoms[i] = all_points[all_points.length - glyf_impl::PHANTOM_COUNT + i];
return true;
}
#ifndef HB_NO_VAR
struct points_aggregator_t
{
hb_font_t *font;
hb_glyph_extents_t *extents;
contour_point_t *phantoms;
bool scaled;
struct contour_bounds_t
{
contour_bounds_t () { min_x = min_y = FLT_MAX; max_x = max_y = -FLT_MAX; }
void add (const contour_point_t &p)
{
min_x = hb_min (min_x, p.x);
min_y = hb_min (min_y, p.y);
max_x = hb_max (max_x, p.x);
max_y = hb_max (max_y, p.y);
}
bool empty () const { return (min_x >= max_x) || (min_y >= max_y); }
void get_extents (hb_font_t *font, hb_glyph_extents_t *extents, bool scaled)
{
if (unlikely (empty ()))
{
extents->width = 0;
extents->x_bearing = 0;
extents->height = 0;
extents->y_bearing = 0;
return;
}
if (scaled)
{
extents->x_bearing = font->em_scalef_x (min_x);
extents->width = font->em_scalef_x (max_x) - extents->x_bearing;
extents->y_bearing = font->em_scalef_y (max_y);
extents->height = font->em_scalef_y (min_y) - extents->y_bearing;
}
else
{
extents->x_bearing = roundf (min_x);
extents->width = roundf (max_x - extents->x_bearing);
extents->y_bearing = roundf (max_y);
extents->height = roundf (min_y - extents->y_bearing);
}
}
protected:
float min_x, min_y, max_x, max_y;
} bounds;
points_aggregator_t (hb_font_t *font_, hb_glyph_extents_t *extents_, contour_point_t *phantoms_, bool scaled_)
{
font = font_;
extents = extents_;
phantoms = phantoms_;
scaled = scaled_;
if (extents) bounds = contour_bounds_t ();
}
void consume_point (const contour_point_t &point) { bounds.add (point); }
void points_end () { bounds.get_extents (font, extents, scaled); }
bool is_consuming_contour_points () { return extents; }
contour_point_t *get_phantoms_sink () { return phantoms; }
};
public:
unsigned
get_advance_with_var_unscaled (hb_font_t *font, hb_codepoint_t gid, bool is_vertical) const
{
if (unlikely (gid >= num_glyphs)) return 0;
bool success = false;
contour_point_t phantoms[glyf_impl::PHANTOM_COUNT];
if (font->num_coords)
success = get_points (font, gid, points_aggregator_t (font, nullptr, phantoms, false));
if (unlikely (!success))
return
#ifndef HB_NO_VERTICAL
is_vertical ? vmtx->get_advance_without_var_unscaled (gid) :
#endif
hmtx->get_advance_without_var_unscaled (gid);
float result = is_vertical
? phantoms[glyf_impl::PHANTOM_TOP].y - phantoms[glyf_impl::PHANTOM_BOTTOM].y
: phantoms[glyf_impl::PHANTOM_RIGHT].x - phantoms[glyf_impl::PHANTOM_LEFT].x;
return hb_clamp (roundf (result), 0.f, (float) UINT_MAX / 2);
}
bool get_leading_bearing_with_var_unscaled (hb_font_t *font, hb_codepoint_t gid, bool is_vertical, int *lsb) const
{
if (unlikely (gid >= num_glyphs)) return false;
hb_glyph_extents_t extents;
contour_point_t phantoms[glyf_impl::PHANTOM_COUNT];
if (unlikely (!get_points (font, gid, points_aggregator_t (font, &extents, phantoms, false))))
return false;
*lsb = is_vertical
? roundf (phantoms[glyf_impl::PHANTOM_TOP].y) - extents.y_bearing
: roundf (phantoms[glyf_impl::PHANTOM_LEFT].x);
return true;
}
#endif
public:
bool get_extents (hb_font_t *font, hb_codepoint_t gid, hb_glyph_extents_t *extents) const
{
if (unlikely (gid >= num_glyphs)) return false;
#ifndef HB_NO_VAR
if (font->num_coords)
return get_points (font, gid, points_aggregator_t (font, extents, nullptr, true));
#endif
return glyph_for_gid (gid).get_extents_without_var_scaled (font, *this, extents);
}
const glyf_impl::Glyph
glyph_for_gid (hb_codepoint_t gid, bool needs_padding_removal = false) const
{
if (unlikely (gid >= num_glyphs)) return glyf_impl::Glyph ();
unsigned int start_offset, end_offset;
if (short_offset)
{
const HBUINT16 *offsets = (const HBUINT16 *) loca_table->dataZ.arrayZ;
start_offset = 2 * offsets[gid];
end_offset = 2 * offsets[gid + 1];
}
else
{
const HBUINT32 *offsets = (const HBUINT32 *) loca_table->dataZ.arrayZ;
start_offset = offsets[gid];
end_offset = offsets[gid + 1];
}
if (unlikely (start_offset > end_offset || end_offset > glyf_table.get_length ()))
return glyf_impl::Glyph ();
glyf_impl::Glyph glyph (hb_bytes_t ((const char *) this->glyf_table + start_offset,
end_offset - start_offset), gid);
return needs_padding_removal ? glyf_impl::Glyph (glyph.trim_padding (), gid) : glyph;
}
bool
get_path (hb_font_t *font, hb_codepoint_t gid, hb_draw_session_t &draw_session) const
{ return get_points (font, gid, glyf_impl::path_builder_t (font, draw_session)); }
#ifndef HB_NO_VAR
const gvar_accelerator_t *gvar;
#endif
const hmtx_accelerator_t *hmtx;
#ifndef HB_NO_VERTICAL
const vmtx_accelerator_t *vmtx;
#endif
private:
bool short_offset;
unsigned int num_glyphs;
hb_blob_ptr_t<loca> loca_table;
hb_blob_ptr_t<glyf> glyf_table;
};
inline void
glyf::_populate_subset_glyphs (const hb_subset_plan_t *plan,
hb_vector_t<glyf_impl::SubsetGlyph>& glyphs /* OUT */) const
{
OT::glyf_accelerator_t glyf (plan->source);
unsigned num_glyphs = plan->num_output_glyphs ();
if (!glyphs.resize (num_glyphs)) return;
for (auto p : plan->glyph_map->iter ())
{
unsigned new_gid = p.second;
glyf_impl::SubsetGlyph& subset_glyph = glyphs.arrayZ[new_gid];
subset_glyph.old_gid = p.first;
if (unlikely (new_gid == 0 &&
!(plan->flags & HB_SUBSET_FLAGS_NOTDEF_OUTLINE)) &&
plan->pinned_at_default)
subset_glyph.source_glyph = glyf_impl::Glyph ();
else
{
/* 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 ();
}
}
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 ())))
return nullptr;
for (auto _ : *plan->user_axes_location)
{
hb_variation_t var;
var.tag = _.first;
var.value = _.second;
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 */

View File

@ -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 */

View File

@ -0,0 +1,135 @@
#ifndef OT_GLYF_PATH_BUILDER_HH
#define OT_GLYF_PATH_BUILDER_HH
#include "../../hb.hh"
namespace OT {
namespace glyf_impl {
struct path_builder_t
{
hb_font_t *font;
hb_draw_session_t *draw_session;
struct optional_point_t
{
optional_point_t () {}
optional_point_t (float x_, float y_) : has_data (true), x (x_), y (y_) {}
operator bool () const { return has_data; }
bool has_data = false;
float x = 0.;
float y = 0.;
optional_point_t lerp (optional_point_t p, float t)
{ return optional_point_t (x + t * (p.x - x), y + t * (p.y - y)); }
} first_oncurve, first_offcurve, last_offcurve;
path_builder_t (hb_font_t *font_, hb_draw_session_t &draw_session_)
{
font = font_;
draw_session = &draw_session_;
first_oncurve = first_offcurve = last_offcurve = optional_point_t ();
}
/* based on https://github.com/RazrFalcon/ttf-parser/blob/4f32821/src/glyf.rs#L287
See also:
* https://developer.apple.com/fonts/TrueType-Reference-Manual/RM01/Chap1.html
* https://stackoverflow.com/a/20772557 */
void consume_point (const contour_point_t &point)
{
bool is_on_curve = point.flag & glyf_impl::SimpleGlyph::FLAG_ON_CURVE;
optional_point_t p (font->em_fscalef_x (point.x), font->em_fscalef_y (point.y));
if (!first_oncurve)
{
if (is_on_curve)
{
first_oncurve = p;
draw_session->move_to (p.x, p.y);
}
else
{
if (first_offcurve)
{
optional_point_t mid = first_offcurve.lerp (p, .5f);
first_oncurve = mid;
last_offcurve = p;
draw_session->move_to (mid.x, mid.y);
}
else
first_offcurve = p;
}
}
else
{
if (last_offcurve)
{
if (is_on_curve)
{
draw_session->quadratic_to (last_offcurve.x, last_offcurve.y,
p.x, p.y);
last_offcurve = optional_point_t ();
}
else
{
optional_point_t mid = last_offcurve.lerp (p, .5f);
draw_session->quadratic_to (last_offcurve.x, last_offcurve.y,
mid.x, mid.y);
last_offcurve = p;
}
}
else
{
if (is_on_curve)
draw_session->line_to (p.x, p.y);
else
last_offcurve = p;
}
}
if (point.is_end_point)
{
if (first_offcurve && last_offcurve)
{
optional_point_t mid = last_offcurve.lerp (first_offcurve, .5f);
draw_session->quadratic_to (last_offcurve.x, last_offcurve.y,
mid.x, mid.y);
last_offcurve = optional_point_t ();
/* now check the rest */
}
if (first_offcurve && first_oncurve)
draw_session->quadratic_to (first_offcurve.x, first_offcurve.y,
first_oncurve.x, first_oncurve.y);
else if (last_offcurve && first_oncurve)
draw_session->quadratic_to (last_offcurve.x, last_offcurve.y,
first_oncurve.x, first_oncurve.y);
else if (first_oncurve)
draw_session->line_to (first_oncurve.x, first_oncurve.y);
else if (first_offcurve)
{
float x = first_offcurve.x, y = first_offcurve.y;
draw_session->move_to (x, y);
draw_session->quadratic_to (x, y, x, y);
}
/* Getting ready for the next contour */
first_oncurve = first_offcurve = last_offcurve = optional_point_t ();
draw_session->close_path ();
}
}
void points_end () {}
bool is_consuming_contour_points () { return true; }
contour_point_t *get_phantoms_sink () { return nullptr; }
};
} /* namespace glyf_impl */
} /* namespace OT */
#endif /* OT_GLYF_PATH_BUILDER_HH */

View File

@ -0,0 +1,216 @@
/*
* Copyright © 2022 Google, Inc.
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Google Author(s): Garret Rieger
*/
#include "graph.hh"
#include "../hb-ot-layout-common.hh"
#ifndef GRAPH_CLASSDEF_GRAPH_HH
#define GRAPH_CLASSDEF_GRAPH_HH
namespace graph {
struct ClassDefFormat1 : public OT::ClassDefFormat1_3<SmallTypes>
{
bool sanitize (graph_t::vertex_t& vertex) const
{
int64_t vertex_len = vertex.obj.tail - vertex.obj.head;
constexpr unsigned min_size = OT::ClassDefFormat1_3<SmallTypes>::min_size;
if (vertex_len < min_size) return false;
return vertex_len >= min_size + classValue.get_size () - classValue.len.get_size ();
}
};
struct ClassDefFormat2 : public OT::ClassDefFormat2_4<SmallTypes>
{
bool sanitize (graph_t::vertex_t& vertex) const
{
int64_t vertex_len = vertex.obj.tail - vertex.obj.head;
constexpr unsigned min_size = OT::ClassDefFormat2_4<SmallTypes>::min_size;
if (vertex_len < min_size) return false;
return vertex_len >= min_size + rangeRecord.get_size () - rangeRecord.len.get_size ();
}
};
struct ClassDef : public OT::ClassDef
{
template<typename It>
static bool add_class_def (gsubgpos_graph_context_t& c,
unsigned parent_id,
unsigned link_position,
It glyph_and_class,
unsigned max_size)
{
unsigned class_def_prime_id = c.graph.new_node (nullptr, nullptr);
auto& class_def_prime_vertex = c.graph.vertices_[class_def_prime_id];
if (!make_class_def (c, glyph_and_class, class_def_prime_id, max_size))
return false;
auto* class_def_link = c.graph.vertices_[parent_id].obj.real_links.push ();
class_def_link->width = SmallTypes::size;
class_def_link->objidx = class_def_prime_id;
class_def_link->position = link_position;
class_def_prime_vertex.parents.push (parent_id);
return true;
}
template<typename It>
static bool make_class_def (gsubgpos_graph_context_t& c,
It glyph_and_class,
unsigned dest_obj,
unsigned max_size)
{
char* buffer = (char*) hb_calloc (1, max_size);
hb_serialize_context_t serializer (buffer, max_size);
OT::ClassDef_serialize (&serializer, glyph_and_class);
serializer.end_serialize ();
if (serializer.in_error ())
{
hb_free (buffer);
return false;
}
hb_bytes_t class_def_copy = serializer.copy_bytes ();
c.add_buffer ((char *) class_def_copy.arrayZ); // Give ownership to the context, it will cleanup the buffer.
auto& obj = c.graph.vertices_[dest_obj].obj;
obj.head = (char *) class_def_copy.arrayZ;
obj.tail = obj.head + class_def_copy.length;
hb_free (buffer);
return true;
}
bool sanitize (graph_t::vertex_t& vertex) const
{
int64_t vertex_len = vertex.obj.tail - vertex.obj.head;
if (vertex_len < OT::ClassDef::min_size) return false;
switch (u.format)
{
case 1: return ((ClassDefFormat1*)this)->sanitize (vertex);
case 2: return ((ClassDefFormat2*)this)->sanitize (vertex);
#ifndef HB_NO_BEYOND_64K
// Not currently supported
case 3:
case 4:
#endif
default: return false;
}
}
};
struct class_def_size_estimator_t
{
template<typename It>
class_def_size_estimator_t (It glyph_and_class)
: gids_consecutive (true), num_ranges_per_class (), glyphs_per_class ()
{
unsigned last_gid = (unsigned) -1;
for (auto p : + glyph_and_class)
{
unsigned gid = p.first;
unsigned klass = p.second;
if (last_gid != (unsigned) -1 && gid != last_gid + 1)
gids_consecutive = false;
last_gid = gid;
hb_set_t* glyphs;
if (glyphs_per_class.has (klass, &glyphs) && glyphs) {
glyphs->add (gid);
continue;
}
hb_set_t new_glyphs;
new_glyphs.add (gid);
glyphs_per_class.set (klass, std::move (new_glyphs));
}
if (in_error ()) return;
for (unsigned klass : glyphs_per_class.keys ())
{
if (!klass) continue; // class 0 doesn't get encoded.
const hb_set_t& glyphs = glyphs_per_class.get (klass);
hb_codepoint_t start = HB_SET_VALUE_INVALID;
hb_codepoint_t end = HB_SET_VALUE_INVALID;
unsigned count = 0;
while (glyphs.next_range (&start, &end))
count++;
num_ranges_per_class.set (klass, count);
}
}
// Incremental increase in the Coverage and ClassDef table size
// (worst case) if all glyphs associated with 'klass' were added.
unsigned incremental_coverage_size (unsigned klass) const
{
// Coverage takes 2 bytes per glyph worst case,
return 2 * glyphs_per_class.get (klass).get_population ();
}
// Incremental increase in the Coverage and ClassDef table size
// (worst case) if all glyphs associated with 'klass' were added.
unsigned incremental_class_def_size (unsigned klass) const
{
// ClassDef takes 6 bytes per range
unsigned class_def_2_size = 6 * num_ranges_per_class.get (klass);
if (gids_consecutive)
{
// ClassDef1 takes 2 bytes per glyph, but only can be used
// when gids are consecutive.
return hb_min (2 * glyphs_per_class.get (klass).get_population (), class_def_2_size);
}
return class_def_2_size;
}
bool in_error ()
{
if (num_ranges_per_class.in_error ()) return true;
if (glyphs_per_class.in_error ()) return true;
for (const hb_set_t& s : glyphs_per_class.values ())
{
if (s.in_error ()) return true;
}
return false;
}
private:
bool gids_consecutive;
hb_hashmap_t<unsigned, unsigned> num_ranges_per_class;
hb_hashmap_t<unsigned, hb_set_t> glyphs_per_class;
};
}
#endif // GRAPH_CLASSDEF_GRAPH_HH

View File

@ -0,0 +1,152 @@
/*
* Copyright © 2022 Google, Inc.
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Google Author(s): Garret Rieger
*/
#include "graph.hh"
#include "../OT/Layout/Common/Coverage.hh"
#ifndef GRAPH_COVERAGE_GRAPH_HH
#define GRAPH_COVERAGE_GRAPH_HH
namespace graph {
struct CoverageFormat1 : public OT::Layout::Common::CoverageFormat1_3<SmallTypes>
{
bool sanitize (graph_t::vertex_t& vertex) const
{
int64_t vertex_len = vertex.obj.tail - vertex.obj.head;
constexpr unsigned min_size = OT::Layout::Common::CoverageFormat1_3<SmallTypes>::min_size;
if (vertex_len < min_size) return false;
return vertex_len >= min_size + glyphArray.get_size () - glyphArray.len.get_size ();
}
};
struct CoverageFormat2 : public OT::Layout::Common::CoverageFormat2_4<SmallTypes>
{
bool sanitize (graph_t::vertex_t& vertex) const
{
int64_t vertex_len = vertex.obj.tail - vertex.obj.head;
constexpr unsigned min_size = OT::Layout::Common::CoverageFormat2_4<SmallTypes>::min_size;
if (vertex_len < min_size) return false;
return vertex_len >= min_size + rangeRecord.get_size () - rangeRecord.len.get_size ();
}
};
struct Coverage : public OT::Layout::Common::Coverage
{
static Coverage* clone_coverage (gsubgpos_graph_context_t& c,
unsigned coverage_id,
unsigned new_parent_id,
unsigned link_position,
unsigned start, unsigned end)
{
unsigned coverage_size = c.graph.vertices_[coverage_id].table_size ();
auto& coverage_v = c.graph.vertices_[coverage_id];
Coverage* coverage_table = (Coverage*) coverage_v.obj.head;
if (!coverage_table || !coverage_table->sanitize (coverage_v))
return nullptr;
auto new_coverage =
+ hb_zip (coverage_table->iter (), hb_range ())
| hb_filter ([&] (hb_pair_t<unsigned, unsigned> p) {
return p.second >= start && p.second < end;
})
| hb_map_retains_sorting (hb_first)
;
return add_coverage (c, new_parent_id, link_position, new_coverage, coverage_size);
}
template<typename It>
static Coverage* add_coverage (gsubgpos_graph_context_t& c,
unsigned parent_id,
unsigned link_position,
It glyphs,
unsigned max_size)
{
unsigned coverage_prime_id = c.graph.new_node (nullptr, nullptr);
auto& coverage_prime_vertex = c.graph.vertices_[coverage_prime_id];
if (!make_coverage (c, glyphs, coverage_prime_id, max_size))
return nullptr;
auto* coverage_link = c.graph.vertices_[parent_id].obj.real_links.push ();
coverage_link->width = SmallTypes::size;
coverage_link->objidx = coverage_prime_id;
coverage_link->position = link_position;
coverage_prime_vertex.parents.push (parent_id);
return (Coverage*) coverage_prime_vertex.obj.head;
}
template<typename It>
static bool make_coverage (gsubgpos_graph_context_t& c,
It glyphs,
unsigned dest_obj,
unsigned max_size)
{
char* buffer = (char*) hb_calloc (1, max_size);
hb_serialize_context_t serializer (buffer, max_size);
OT::Layout::Common::Coverage_serialize (&serializer, glyphs);
serializer.end_serialize ();
if (serializer.in_error ())
{
hb_free (buffer);
return false;
}
hb_bytes_t coverage_copy = serializer.copy_bytes ();
c.add_buffer ((char *) coverage_copy.arrayZ); // Give ownership to the context, it will cleanup the buffer.
auto& obj = c.graph.vertices_[dest_obj].obj;
obj.head = (char *) coverage_copy.arrayZ;
obj.tail = obj.head + coverage_copy.length;
hb_free (buffer);
return true;
}
bool sanitize (graph_t::vertex_t& vertex) const
{
int64_t vertex_len = vertex.obj.tail - vertex.obj.head;
if (vertex_len < OT::Layout::Common::Coverage::min_size) return false;
switch (u.format)
{
case 1: return ((CoverageFormat1*)this)->sanitize (vertex);
case 2: return ((CoverageFormat2*)this)->sanitize (vertex);
#ifndef HB_NO_BEYOND_64K
// Not currently supported
case 3:
case 4:
#endif
default: return false;
}
}
};
}
#endif // GRAPH_COVERAGE_GRAPH_HH

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,70 @@
/*
* Copyright © 2022 Google, Inc.
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Google Author(s): Garret Rieger
*/
#include "gsubgpos-graph.hh"
namespace graph {
gsubgpos_graph_context_t::gsubgpos_graph_context_t (hb_tag_t table_tag_,
graph_t& graph_)
: table_tag (table_tag_),
graph (graph_),
lookup_list_index (0),
lookups ()
{
if (table_tag_ != HB_OT_TAG_GPOS
&& table_tag_ != HB_OT_TAG_GSUB)
return;
GSTAR* gstar = graph::GSTAR::graph_to_gstar (graph_);
if (gstar) {
gstar->find_lookups (graph, lookups);
lookup_list_index = gstar->get_lookup_list_index (graph_);
}
}
unsigned gsubgpos_graph_context_t::create_node (unsigned size)
{
char* buffer = (char*) hb_calloc (1, size);
if (!buffer)
return -1;
add_buffer (buffer);
return graph.new_node (buffer, buffer + size);
}
unsigned gsubgpos_graph_context_t::num_non_ext_subtables () {
unsigned count = 0;
for (auto l : lookups.values ())
{
if (l->is_extension (table_tag)) continue;
count += l->number_of_subtables ();
}
return count;
}
}

View File

@ -0,0 +1,61 @@
/*
* Copyright © 2022 Google, Inc.
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Google Author(s): Garret Rieger
*/
#include "graph.hh"
#include "../hb-ot-layout-gsubgpos.hh"
#ifndef GRAPH_GSUBGPOS_CONTEXT_HH
#define GRAPH_GSUBGPOS_CONTEXT_HH
namespace graph {
struct Lookup;
struct gsubgpos_graph_context_t
{
hb_tag_t table_tag;
graph_t& graph;
unsigned lookup_list_index;
hb_hashmap_t<unsigned, graph::Lookup*> lookups;
HB_INTERNAL gsubgpos_graph_context_t (hb_tag_t table_tag_,
graph_t& graph_);
HB_INTERNAL unsigned create_node (unsigned size);
void add_buffer (char* buffer)
{
graph.add_buffer (buffer);
}
private:
HB_INTERNAL unsigned num_non_ext_subtables ();
};
}
#endif // GRAPH_GSUBGPOS_CONTEXT

View File

@ -0,0 +1,414 @@
/*
* Copyright © 2022 Google, Inc.
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Google Author(s): Garret Rieger
*/
#include "graph.hh"
#include "../hb-ot-layout-gsubgpos.hh"
#include "../OT/Layout/GSUB/ExtensionSubst.hh"
#include "gsubgpos-context.hh"
#include "pairpos-graph.hh"
#include "markbasepos-graph.hh"
#ifndef GRAPH_GSUBGPOS_GRAPH_HH
#define GRAPH_GSUBGPOS_GRAPH_HH
namespace graph {
struct Lookup;
template<typename T>
struct ExtensionFormat1 : public OT::ExtensionFormat1<T>
{
void reset(unsigned type)
{
this->format = 1;
this->extensionLookupType = type;
this->extensionOffset = 0;
}
bool sanitize (graph_t::vertex_t& vertex) const
{
int64_t vertex_len = vertex.obj.tail - vertex.obj.head;
return vertex_len >= OT::ExtensionFormat1<T>::static_size;
}
unsigned get_lookup_type () const
{
return this->extensionLookupType;
}
unsigned get_subtable_index (graph_t& graph, unsigned this_index) const
{
return graph.index_for_offset (this_index, &this->extensionOffset);
}
};
struct Lookup : public OT::Lookup
{
unsigned number_of_subtables () const
{
return subTable.len;
}
bool sanitize (graph_t::vertex_t& vertex) const
{
int64_t vertex_len = vertex.obj.tail - vertex.obj.head;
if (vertex_len < OT::Lookup::min_size) return false;
return vertex_len >= this->get_size ();
}
bool is_extension (hb_tag_t table_tag) const
{
return lookupType == extension_type (table_tag);
}
bool make_extension (gsubgpos_graph_context_t& c,
unsigned this_index)
{
unsigned type = lookupType;
unsigned ext_type = extension_type (c.table_tag);
if (!ext_type || is_extension (c.table_tag))
{
// NOOP
return true;
}
DEBUG_MSG (SUBSET_REPACK, nullptr,
"Promoting lookup type %u (obj %u) to extension.",
type,
this_index);
for (unsigned i = 0; i < subTable.len; i++)
{
unsigned subtable_index = c.graph.index_for_offset (this_index, &subTable[i]);
if (!make_subtable_extension (c,
this_index,
subtable_index))
return false;
}
lookupType = ext_type;
return true;
}
bool split_subtables_if_needed (gsubgpos_graph_context_t& c,
unsigned this_index)
{
unsigned type = lookupType;
bool is_ext = is_extension (c.table_tag);
if (c.table_tag != HB_OT_TAG_GPOS)
return true;
if (!is_ext &&
type != OT::Layout::GPOS_impl::PosLookupSubTable::Type::Pair &&
type != OT::Layout::GPOS_impl::PosLookupSubTable::Type::MarkBase)
return true;
hb_vector_t<hb_pair_t<unsigned, hb_vector_t<unsigned>>> all_new_subtables;
for (unsigned i = 0; i < subTable.len; i++)
{
unsigned subtable_index = c.graph.index_for_offset (this_index, &subTable[i]);
unsigned parent_index = this_index;
if (is_ext) {
unsigned ext_subtable_index = subtable_index;
parent_index = ext_subtable_index;
ExtensionFormat1<OT::Layout::GSUB_impl::ExtensionSubst>* extension =
(ExtensionFormat1<OT::Layout::GSUB_impl::ExtensionSubst>*)
c.graph.object (ext_subtable_index).head;
if (!extension || !extension->sanitize (c.graph.vertices_[ext_subtable_index]))
continue;
subtable_index = extension->get_subtable_index (c.graph, ext_subtable_index);
type = extension->get_lookup_type ();
if (type != OT::Layout::GPOS_impl::PosLookupSubTable::Type::Pair
&& type != OT::Layout::GPOS_impl::PosLookupSubTable::Type::MarkBase)
continue;
}
hb_vector_t<unsigned> new_sub_tables;
switch (type)
{
case 2:
new_sub_tables = split_subtable<PairPos> (c, parent_index, subtable_index); break;
case 4:
new_sub_tables = split_subtable<MarkBasePos> (c, parent_index, subtable_index); break;
default:
break;
}
if (new_sub_tables.in_error ()) return false;
if (!new_sub_tables) continue;
hb_pair_t<unsigned, hb_vector_t<unsigned>>* entry = all_new_subtables.push ();
entry->first = i;
entry->second = std::move (new_sub_tables);
}
if (all_new_subtables) {
add_sub_tables (c, this_index, type, all_new_subtables);
}
return true;
}
template<typename T>
hb_vector_t<unsigned> split_subtable (gsubgpos_graph_context_t& c,
unsigned parent_idx,
unsigned objidx)
{
T* sub_table = (T*) c.graph.object (objidx).head;
if (!sub_table || !sub_table->sanitize (c.graph.vertices_[objidx]))
return hb_vector_t<unsigned> ();
return sub_table->split_subtables (c, parent_idx, objidx);
}
void add_sub_tables (gsubgpos_graph_context_t& c,
unsigned this_index,
unsigned type,
hb_vector_t<hb_pair_t<unsigned, hb_vector_t<unsigned>>>& subtable_ids)
{
bool is_ext = is_extension (c.table_tag);
auto& v = c.graph.vertices_[this_index];
fix_existing_subtable_links (c, this_index, subtable_ids);
unsigned new_subtable_count = 0;
for (const auto& p : subtable_ids)
new_subtable_count += p.second.length;
size_t new_size = v.table_size ()
+ new_subtable_count * OT::Offset16::static_size;
char* buffer = (char*) hb_calloc (1, new_size);
c.add_buffer (buffer);
hb_memcpy (buffer, v.obj.head, v.table_size());
v.obj.head = buffer;
v.obj.tail = buffer + new_size;
Lookup* new_lookup = (Lookup*) buffer;
unsigned shift = 0;
new_lookup->subTable.len = subTable.len + new_subtable_count;
for (const auto& p : subtable_ids)
{
unsigned offset_index = p.first + shift + 1;
shift += p.second.length;
for (unsigned subtable_id : p.second)
{
if (is_ext)
{
unsigned ext_id = create_extension_subtable (c, subtable_id, type);
c.graph.vertices_[subtable_id].parents.push (ext_id);
subtable_id = ext_id;
}
auto* link = v.obj.real_links.push ();
link->width = 2;
link->objidx = subtable_id;
link->position = (char*) &new_lookup->subTable[offset_index++] -
(char*) new_lookup;
c.graph.vertices_[subtable_id].parents.push (this_index);
}
}
// Repacker sort order depends on link order, which we've messed up so resort it.
v.obj.real_links.qsort ();
// The head location of the lookup has changed, invalidating the lookups map entry
// in the context. Update the map.
c.lookups.set (this_index, new_lookup);
}
void fix_existing_subtable_links (gsubgpos_graph_context_t& c,
unsigned this_index,
hb_vector_t<hb_pair_t<unsigned, hb_vector_t<unsigned>>>& subtable_ids)
{
auto& v = c.graph.vertices_[this_index];
Lookup* lookup = (Lookup*) v.obj.head;
unsigned shift = 0;
for (const auto& p : subtable_ids)
{
unsigned insert_index = p.first + shift;
unsigned pos_offset = p.second.length * OT::Offset16::static_size;
unsigned insert_offset = (char*) &lookup->subTable[insert_index] - (char*) lookup;
shift += p.second.length;
for (auto& l : v.obj.all_links_writer ())
{
if (l.position > insert_offset) l.position += pos_offset;
}
}
}
unsigned create_extension_subtable (gsubgpos_graph_context_t& c,
unsigned subtable_index,
unsigned type)
{
unsigned extension_size = OT::ExtensionFormat1<OT::Layout::GSUB_impl::ExtensionSubst>::static_size;
unsigned ext_index = c.create_node (extension_size);
if (ext_index == (unsigned) -1)
return -1;
auto& ext_vertex = c.graph.vertices_[ext_index];
ExtensionFormat1<OT::Layout::GSUB_impl::ExtensionSubst>* extension =
(ExtensionFormat1<OT::Layout::GSUB_impl::ExtensionSubst>*) ext_vertex.obj.head;
extension->reset (type);
// Make extension point at the subtable.
auto* l = ext_vertex.obj.real_links.push ();
l->width = 4;
l->objidx = subtable_index;
l->position = 4;
return ext_index;
}
bool make_subtable_extension (gsubgpos_graph_context_t& c,
unsigned lookup_index,
unsigned subtable_index)
{
unsigned type = lookupType;
unsigned ext_index = create_extension_subtable(c, subtable_index, type);
if (ext_index == (unsigned) -1)
return false;
auto& lookup_vertex = c.graph.vertices_[lookup_index];
for (auto& l : lookup_vertex.obj.real_links.writer ())
{
if (l.objidx == subtable_index)
// Change lookup to point at the extension.
l.objidx = ext_index;
}
// Make extension point at the subtable.
auto& ext_vertex = c.graph.vertices_[ext_index];
auto& subtable_vertex = c.graph.vertices_[subtable_index];
ext_vertex.parents.push (lookup_index);
subtable_vertex.remap_parent (lookup_index, ext_index);
return true;
}
private:
unsigned extension_type (hb_tag_t table_tag) const
{
switch (table_tag)
{
case HB_OT_TAG_GPOS: return 9;
case HB_OT_TAG_GSUB: return 7;
default: return 0;
}
}
};
template <typename T>
struct LookupList : public OT::LookupList<T>
{
bool sanitize (const graph_t::vertex_t& vertex) const
{
int64_t vertex_len = vertex.obj.tail - vertex.obj.head;
if (vertex_len < OT::LookupList<T>::min_size) return false;
return vertex_len >= OT::LookupList<T>::item_size * this->len;
}
};
struct GSTAR : public OT::GSUBGPOS
{
static GSTAR* graph_to_gstar (graph_t& graph)
{
const auto& r = graph.root ();
GSTAR* gstar = (GSTAR*) r.obj.head;
if (!gstar || !gstar->sanitize (r))
return nullptr;
return gstar;
}
const void* get_lookup_list_field_offset () const
{
switch (u.version.major) {
case 1: return u.version1.get_lookup_list_offset ();
#ifndef HB_NO_BEYOND_64K
case 2: return u.version2.get_lookup_list_offset ();
#endif
default: return 0;
}
}
bool sanitize (const graph_t::vertex_t& vertex)
{
int64_t len = vertex.obj.tail - vertex.obj.head;
if (len < OT::GSUBGPOS::min_size) return false;
return len >= get_size ();
}
void find_lookups (graph_t& graph,
hb_hashmap_t<unsigned, Lookup*>& lookups /* OUT */)
{
switch (u.version.major) {
case 1: find_lookups<SmallTypes> (graph, lookups); break;
#ifndef HB_NO_BEYOND_64K
case 2: find_lookups<MediumTypes> (graph, lookups); break;
#endif
}
}
unsigned get_lookup_list_index (graph_t& graph)
{
return graph.index_for_offset (graph.root_idx (),
get_lookup_list_field_offset());
}
template<typename Types>
void find_lookups (graph_t& graph,
hb_hashmap_t<unsigned, Lookup*>& lookups /* OUT */)
{
unsigned lookup_list_idx = get_lookup_list_index (graph);
const LookupList<Types>* lookupList =
(const LookupList<Types>*) graph.object (lookup_list_idx).head;
if (!lookupList || !lookupList->sanitize (graph.vertices_[lookup_list_idx]))
return;
for (unsigned i = 0; i < lookupList->len; i++)
{
unsigned lookup_idx = graph.index_for_offset (lookup_list_idx, &(lookupList->arrayZ[i]));
Lookup* lookup = (Lookup*) graph.object (lookup_idx).head;
if (!lookup || !lookup->sanitize (graph.vertices_[lookup_idx])) continue;
lookups.set (lookup_idx, lookup);
}
}
};
}
#endif /* GRAPH_GSUBGPOS_GRAPH_HH */

View File

@ -0,0 +1,510 @@
/*
* Copyright © 2022 Google, Inc.
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Google Author(s): Garret Rieger
*/
#ifndef GRAPH_MARKBASEPOS_GRAPH_HH
#define GRAPH_MARKBASEPOS_GRAPH_HH
#include "split-helpers.hh"
#include "coverage-graph.hh"
#include "../OT/Layout/GPOS/MarkBasePos.hh"
#include "../OT/Layout/GPOS/PosLookupSubTable.hh"
namespace graph {
struct AnchorMatrix : public OT::Layout::GPOS_impl::AnchorMatrix
{
bool sanitize (graph_t::vertex_t& vertex, unsigned class_count) const
{
int64_t vertex_len = vertex.obj.tail - vertex.obj.head;
if (vertex_len < AnchorMatrix::min_size) return false;
return vertex_len >= AnchorMatrix::min_size +
OT::Offset16::static_size * class_count * this->rows;
}
bool shrink (gsubgpos_graph_context_t& c,
unsigned this_index,
unsigned old_class_count,
unsigned new_class_count)
{
if (new_class_count >= old_class_count) return false;
auto& o = c.graph.vertices_[this_index].obj;
unsigned base_count = rows;
o.tail = o.head +
AnchorMatrix::min_size +
OT::Offset16::static_size * base_count * new_class_count;
// Reposition links into the new indexing scheme.
for (auto& link : o.real_links.writer ())
{
unsigned index = (link.position - 2) / 2;
unsigned base = index / old_class_count;
unsigned klass = index % old_class_count;
if (klass >= new_class_count)
// should have already been removed
return false;
unsigned new_index = base * new_class_count + klass;
link.position = (char*) &(this->matrixZ[new_index]) - (char*) this;
}
return true;
}
unsigned clone (gsubgpos_graph_context_t& c,
unsigned this_index,
unsigned start,
unsigned end,
unsigned class_count)
{
unsigned base_count = rows;
unsigned new_class_count = end - start;
unsigned size = AnchorMatrix::min_size +
OT::Offset16::static_size * new_class_count * rows;
unsigned prime_id = c.create_node (size);
if (prime_id == (unsigned) -1) return -1;
AnchorMatrix* prime = (AnchorMatrix*) c.graph.object (prime_id).head;
prime->rows = base_count;
auto& o = c.graph.vertices_[this_index].obj;
int num_links = o.real_links.length;
for (int i = 0; i < num_links; i++)
{
const auto& link = o.real_links[i];
unsigned old_index = (link.position - 2) / OT::Offset16::static_size;
unsigned klass = old_index % class_count;
if (klass < start || klass >= end) continue;
unsigned base = old_index / class_count;
unsigned new_klass = klass - start;
unsigned new_index = base * new_class_count + new_klass;
unsigned child_idx = link.objidx;
c.graph.add_link (&(prime->matrixZ[new_index]),
prime_id,
child_idx);
auto& child = c.graph.vertices_[child_idx];
child.remove_parent (this_index);
o.real_links.remove_unordered (i);
num_links--;
i--;
}
return prime_id;
}
};
struct MarkArray : public OT::Layout::GPOS_impl::MarkArray
{
bool sanitize (graph_t::vertex_t& vertex) const
{
int64_t vertex_len = vertex.obj.tail - vertex.obj.head;
unsigned min_size = MarkArray::min_size;
if (vertex_len < min_size) return false;
return vertex_len >= get_size ();
}
bool shrink (gsubgpos_graph_context_t& c,
const hb_hashmap_t<unsigned, unsigned>& mark_array_links,
unsigned this_index,
unsigned new_class_count)
{
auto& o = c.graph.vertices_[this_index].obj;
for (const auto& link : o.real_links)
c.graph.vertices_[link.objidx].remove_parent (this_index);
o.real_links.reset ();
unsigned new_index = 0;
for (const auto& record : this->iter ())
{
unsigned klass = record.klass;
if (klass >= new_class_count) continue;
(*this)[new_index].klass = klass;
unsigned position = (char*) &record.markAnchor - (char*) this;
unsigned* objidx;
if (!mark_array_links.has (position, &objidx))
{
new_index++;
continue;
}
c.graph.add_link (&(*this)[new_index].markAnchor, this_index, *objidx);
new_index++;
}
this->len = new_index;
o.tail = o.head + MarkArray::min_size +
OT::Layout::GPOS_impl::MarkRecord::static_size * new_index;
return true;
}
unsigned clone (gsubgpos_graph_context_t& c,
unsigned this_index,
const hb_hashmap_t<unsigned, unsigned>& pos_to_index,
hb_set_t& marks,
unsigned start_class)
{
unsigned size = MarkArray::min_size +
OT::Layout::GPOS_impl::MarkRecord::static_size *
marks.get_population ();
unsigned prime_id = c.create_node (size);
if (prime_id == (unsigned) -1) return -1;
MarkArray* prime = (MarkArray*) c.graph.object (prime_id).head;
prime->len = marks.get_population ();
unsigned i = 0;
for (hb_codepoint_t mark : marks)
{
(*prime)[i].klass = (*this)[mark].klass - start_class;
unsigned offset_pos = (char*) &((*this)[mark].markAnchor) - (char*) this;
unsigned* anchor_index;
if (pos_to_index.has (offset_pos, &anchor_index))
c.graph.move_child (this_index,
&((*this)[mark].markAnchor),
prime_id,
&((*prime)[i].markAnchor));
i++;
}
return prime_id;
}
};
struct MarkBasePosFormat1 : public OT::Layout::GPOS_impl::MarkBasePosFormat1_2<SmallTypes>
{
bool sanitize (graph_t::vertex_t& vertex) const
{
int64_t vertex_len = vertex.obj.tail - vertex.obj.head;
return vertex_len >= MarkBasePosFormat1::static_size;
}
hb_vector_t<unsigned> split_subtables (gsubgpos_graph_context_t& c,
unsigned parent_index,
unsigned this_index)
{
hb_set_t visited;
const unsigned base_coverage_id = c.graph.index_for_offset (this_index, &baseCoverage);
const unsigned base_size =
OT::Layout::GPOS_impl::PairPosFormat1_3<SmallTypes>::min_size +
MarkArray::min_size +
AnchorMatrix::min_size +
c.graph.vertices_[base_coverage_id].table_size ();
hb_vector_t<class_info_t> class_to_info = get_class_info (c, this_index);
unsigned class_count = classCount;
auto base_array = c.graph.as_table<AnchorMatrix> (this_index,
&baseArray,
class_count);
if (!base_array) return hb_vector_t<unsigned> ();
unsigned base_count = base_array.table->rows;
unsigned partial_coverage_size = 4;
unsigned accumulated = base_size;
hb_vector_t<unsigned> split_points;
for (unsigned klass = 0; klass < class_count; klass++)
{
class_info_t& info = class_to_info[klass];
partial_coverage_size += OT::HBUINT16::static_size * info.marks.get_population ();
unsigned accumulated_delta =
OT::Layout::GPOS_impl::MarkRecord::static_size * info.marks.get_population () +
OT::Offset16::static_size * base_count;
for (unsigned objidx : info.child_indices)
accumulated_delta += c.graph.find_subgraph_size (objidx, visited);
accumulated += accumulated_delta;
unsigned total = accumulated + partial_coverage_size;
if (total >= (1 << 16))
{
split_points.push (klass);
accumulated = base_size + accumulated_delta;
partial_coverage_size = 4 + OT::HBUINT16::static_size * info.marks.get_population ();
visited.clear (); // node sharing isn't allowed between splits.
}
}
const unsigned mark_array_id = c.graph.index_for_offset (this_index, &markArray);
split_context_t split_context {
c,
this,
c.graph.duplicate_if_shared (parent_index, this_index),
std::move (class_to_info),
c.graph.vertices_[mark_array_id].position_to_index_map (),
};
return actuate_subtable_split<split_context_t> (split_context, split_points);
}
private:
struct class_info_t {
hb_set_t marks;
hb_vector_t<unsigned> child_indices;
};
struct split_context_t {
gsubgpos_graph_context_t& c;
MarkBasePosFormat1* thiz;
unsigned this_index;
hb_vector_t<class_info_t> class_to_info;
hb_hashmap_t<unsigned, unsigned> mark_array_links;
hb_set_t marks_for (unsigned start, unsigned end)
{
hb_set_t marks;
for (unsigned klass = start; klass < end; klass++)
{
+ class_to_info[klass].marks.iter ()
| hb_sink (marks)
;
}
return marks;
}
unsigned original_count ()
{
return thiz->classCount;
}
unsigned clone_range (unsigned start, unsigned end)
{
return thiz->clone_range (*this, this->this_index, start, end);
}
bool shrink (unsigned count)
{
return thiz->shrink (*this, this->this_index, count);
}
};
hb_vector_t<class_info_t> get_class_info (gsubgpos_graph_context_t& c,
unsigned this_index)
{
hb_vector_t<class_info_t> class_to_info;
unsigned class_count= classCount;
class_to_info.resize (class_count);
auto mark_array = c.graph.as_table<MarkArray> (this_index, &markArray);
if (!mark_array) return hb_vector_t<class_info_t> ();
unsigned mark_count = mark_array.table->len;
for (unsigned mark = 0; mark < mark_count; mark++)
{
unsigned klass = (*mark_array.table)[mark].get_class ();
class_to_info[klass].marks.add (mark);
}
for (const auto& link : mark_array.vertex->obj.real_links)
{
unsigned mark = (link.position - 2) /
OT::Layout::GPOS_impl::MarkRecord::static_size;
unsigned klass = (*mark_array.table)[mark].get_class ();
class_to_info[klass].child_indices.push (link.objidx);
}
unsigned base_array_id =
c.graph.index_for_offset (this_index, &baseArray);
auto& base_array_v = c.graph.vertices_[base_array_id];
for (const auto& link : base_array_v.obj.real_links)
{
unsigned index = (link.position - 2) / OT::Offset16::static_size;
unsigned klass = index % class_count;
class_to_info[klass].child_indices.push (link.objidx);
}
return class_to_info;
}
bool shrink (split_context_t& sc,
unsigned this_index,
unsigned count)
{
DEBUG_MSG (SUBSET_REPACK, nullptr,
" Shrinking MarkBasePosFormat1 (%u) to [0, %u).",
this_index,
count);
unsigned old_count = classCount;
if (count >= old_count)
return true;
classCount = count;
auto mark_coverage = sc.c.graph.as_mutable_table<Coverage> (this_index,
&markCoverage);
if (!mark_coverage) return false;
hb_set_t marks = sc.marks_for (0, count);
auto new_coverage =
+ hb_enumerate (mark_coverage.table->iter ())
| hb_filter (marks, hb_first)
| hb_map_retains_sorting (hb_second)
;
if (!Coverage::make_coverage (sc.c, + new_coverage,
mark_coverage.index,
4 + 2 * marks.get_population ()))
return false;
auto base_array = sc.c.graph.as_mutable_table<AnchorMatrix> (this_index,
&baseArray,
old_count);
if (!base_array || !base_array.table->shrink (sc.c,
base_array.index,
old_count,
count))
return false;
auto mark_array = sc.c.graph.as_mutable_table<MarkArray> (this_index,
&markArray);
if (!mark_array || !mark_array.table->shrink (sc.c,
sc.mark_array_links,
mark_array.index,
count))
return false;
return true;
}
// Create a new MarkBasePos that has all of the data for classes from [start, end).
unsigned clone_range (split_context_t& sc,
unsigned this_index,
unsigned start, unsigned end) const
{
DEBUG_MSG (SUBSET_REPACK, nullptr,
" Cloning MarkBasePosFormat1 (%u) range [%u, %u).", this_index, start, end);
graph_t& graph = sc.c.graph;
unsigned prime_size = OT::Layout::GPOS_impl::MarkBasePosFormat1_2<SmallTypes>::static_size;
unsigned prime_id = sc.c.create_node (prime_size);
if (prime_id == (unsigned) -1) return -1;
MarkBasePosFormat1* prime = (MarkBasePosFormat1*) graph.object (prime_id).head;
prime->format = this->format;
unsigned new_class_count = end - start;
prime->classCount = new_class_count;
unsigned base_coverage_id =
graph.index_for_offset (sc.this_index, &baseCoverage);
graph.add_link (&(prime->baseCoverage), prime_id, base_coverage_id);
graph.duplicate (prime_id, base_coverage_id);
auto mark_coverage = sc.c.graph.as_table<Coverage> (this_index,
&markCoverage);
if (!mark_coverage) return false;
hb_set_t marks = sc.marks_for (start, end);
auto new_coverage =
+ hb_enumerate (mark_coverage.table->iter ())
| hb_filter (marks, hb_first)
| hb_map_retains_sorting (hb_second)
;
if (!Coverage::add_coverage (sc.c,
prime_id,
2,
+ new_coverage,
marks.get_population () * 2 + 4))
return -1;
auto mark_array =
graph.as_table <MarkArray> (sc.this_index, &markArray);
if (!mark_array) return -1;
unsigned new_mark_array =
mark_array.table->clone (sc.c,
mark_array.index,
sc.mark_array_links,
marks,
start);
graph.add_link (&(prime->markArray), prime_id, new_mark_array);
unsigned class_count = classCount;
auto base_array =
graph.as_table<AnchorMatrix> (sc.this_index, &baseArray, class_count);
if (!base_array) return -1;
unsigned new_base_array =
base_array.table->clone (sc.c,
base_array.index,
start, end, this->classCount);
graph.add_link (&(prime->baseArray), prime_id, new_base_array);
return prime_id;
}
};
struct MarkBasePos : public OT::Layout::GPOS_impl::MarkBasePos
{
hb_vector_t<unsigned> split_subtables (gsubgpos_graph_context_t& c,
unsigned parent_index,
unsigned this_index)
{
switch (u.format) {
case 1:
return ((MarkBasePosFormat1*)(&u.format1))->split_subtables (c, parent_index, this_index);
#ifndef HB_NO_BEYOND_64K
case 2: HB_FALLTHROUGH;
// Don't split 24bit PairPos's.
#endif
default:
return hb_vector_t<unsigned> ();
}
}
bool sanitize (graph_t::vertex_t& vertex) const
{
int64_t vertex_len = vertex.obj.tail - vertex.obj.head;
if (vertex_len < u.format.get_size ()) return false;
switch (u.format) {
case 1:
return ((MarkBasePosFormat1*)(&u.format1))->sanitize (vertex);
#ifndef HB_NO_BEYOND_64K
case 2: HB_FALLTHROUGH;
#endif
default:
// We don't handle format 3 and 4 here.
return false;
}
}
};
}
#endif // GRAPH_MARKBASEPOS_GRAPH_HH

View File

@ -0,0 +1,647 @@
/*
* Copyright © 2022 Google, Inc.
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Google Author(s): Garret Rieger
*/
#ifndef GRAPH_PAIRPOS_GRAPH_HH
#define GRAPH_PAIRPOS_GRAPH_HH
#include "split-helpers.hh"
#include "coverage-graph.hh"
#include "classdef-graph.hh"
#include "../OT/Layout/GPOS/PairPos.hh"
#include "../OT/Layout/GPOS/PosLookupSubTable.hh"
namespace graph {
struct PairPosFormat1 : public OT::Layout::GPOS_impl::PairPosFormat1_3<SmallTypes>
{
bool sanitize (graph_t::vertex_t& vertex) const
{
int64_t vertex_len = vertex.obj.tail - vertex.obj.head;
unsigned min_size = OT::Layout::GPOS_impl::PairPosFormat1_3<SmallTypes>::min_size;
if (vertex_len < min_size) return false;
return vertex_len >=
min_size + pairSet.get_size () - pairSet.len.get_size();
}
hb_vector_t<unsigned> split_subtables (gsubgpos_graph_context_t& c,
unsigned parent_index,
unsigned this_index)
{
hb_set_t visited;
const unsigned coverage_id = c.graph.index_for_offset (this_index, &coverage);
const unsigned coverage_size = c.graph.vertices_[coverage_id].table_size ();
const unsigned base_size = OT::Layout::GPOS_impl::PairPosFormat1_3<SmallTypes>::min_size;
unsigned partial_coverage_size = 4;
unsigned accumulated = base_size;
hb_vector_t<unsigned> split_points;
for (unsigned i = 0; i < pairSet.len; i++)
{
unsigned pair_set_index = pair_set_graph_index (c, this_index, i);
unsigned accumulated_delta =
c.graph.find_subgraph_size (pair_set_index, visited) +
SmallTypes::size; // for PairSet offset.
partial_coverage_size += OT::HBUINT16::static_size;
accumulated += accumulated_delta;
unsigned total = accumulated + hb_min (partial_coverage_size, coverage_size);
if (total >= (1 << 16))
{
split_points.push (i);
accumulated = base_size + accumulated_delta;
partial_coverage_size = 6;
visited.clear (); // node sharing isn't allowed between splits.
}
}
split_context_t split_context {
c,
this,
c.graph.duplicate_if_shared (parent_index, this_index),
};
return actuate_subtable_split<split_context_t> (split_context, split_points);
}
private:
struct split_context_t {
gsubgpos_graph_context_t& c;
PairPosFormat1* thiz;
unsigned this_index;
unsigned original_count ()
{
return thiz->pairSet.len;
}
unsigned clone_range (unsigned start, unsigned end)
{
return thiz->clone_range (this->c, this->this_index, start, end);
}
bool shrink (unsigned count)
{
return thiz->shrink (this->c, this->this_index, count);
}
};
bool shrink (gsubgpos_graph_context_t& c,
unsigned this_index,
unsigned count)
{
DEBUG_MSG (SUBSET_REPACK, nullptr,
" Shrinking PairPosFormat1 (%u) to [0, %u).",
this_index,
count);
unsigned old_count = pairSet.len;
if (count >= old_count)
return true;
pairSet.len = count;
c.graph.vertices_[this_index].obj.tail -= (old_count - count) * SmallTypes::size;
auto coverage = c.graph.as_mutable_table<Coverage> (this_index, &this->coverage);
if (!coverage) return false;
unsigned coverage_size = coverage.vertex->table_size ();
auto new_coverage =
+ hb_zip (coverage.table->iter (), hb_range ())
| hb_filter ([&] (hb_pair_t<unsigned, unsigned> p) {
return p.second < count;
})
| hb_map_retains_sorting (hb_first)
;
return Coverage::make_coverage (c, new_coverage, coverage.index, coverage_size);
}
// Create a new PairPos including PairSet's from start (inclusive) to end (exclusive).
// Returns object id of the new object.
unsigned clone_range (gsubgpos_graph_context_t& c,
unsigned this_index,
unsigned start, unsigned end) const
{
DEBUG_MSG (SUBSET_REPACK, nullptr,
" Cloning PairPosFormat1 (%u) range [%u, %u).", this_index, start, end);
unsigned num_pair_sets = end - start;
unsigned prime_size = OT::Layout::GPOS_impl::PairPosFormat1_3<SmallTypes>::min_size
+ num_pair_sets * SmallTypes::size;
unsigned pair_pos_prime_id = c.create_node (prime_size);
if (pair_pos_prime_id == (unsigned) -1) return -1;
PairPosFormat1* pair_pos_prime = (PairPosFormat1*) c.graph.object (pair_pos_prime_id).head;
pair_pos_prime->format = this->format;
pair_pos_prime->valueFormat[0] = this->valueFormat[0];
pair_pos_prime->valueFormat[1] = this->valueFormat[1];
pair_pos_prime->pairSet.len = num_pair_sets;
for (unsigned i = start; i < end; i++)
{
c.graph.move_child<> (this_index,
&pairSet[i],
pair_pos_prime_id,
&pair_pos_prime->pairSet[i - start]);
}
unsigned coverage_id = c.graph.index_for_offset (this_index, &coverage);
if (!Coverage::clone_coverage (c,
coverage_id,
pair_pos_prime_id,
2,
start, end))
return -1;
return pair_pos_prime_id;
}
unsigned pair_set_graph_index (gsubgpos_graph_context_t& c, unsigned this_index, unsigned i) const
{
return c.graph.index_for_offset (this_index, &pairSet[i]);
}
};
struct PairPosFormat2 : public OT::Layout::GPOS_impl::PairPosFormat2_4<SmallTypes>
{
bool sanitize (graph_t::vertex_t& vertex) const
{
size_t vertex_len = vertex.table_size ();
unsigned min_size = OT::Layout::GPOS_impl::PairPosFormat2_4<SmallTypes>::min_size;
if (vertex_len < min_size) return false;
const unsigned class1_count = class1Count;
return vertex_len >=
min_size + class1_count * get_class1_record_size ();
}
hb_vector_t<unsigned> split_subtables (gsubgpos_graph_context_t& c,
unsigned parent_index,
unsigned this_index)
{
const unsigned base_size = OT::Layout::GPOS_impl::PairPosFormat2_4<SmallTypes>::min_size;
const unsigned class_def_2_size = size_of (c, this_index, &classDef2);
const Coverage* coverage = get_coverage (c, this_index);
const ClassDef* class_def_1 = get_class_def_1 (c, this_index);
auto gid_and_class =
+ coverage->iter ()
| hb_map_retains_sorting ([&] (hb_codepoint_t gid) {
return hb_pair_t<hb_codepoint_t, hb_codepoint_t> (gid, class_def_1->get_class (gid));
})
;
class_def_size_estimator_t estimator (gid_and_class);
const unsigned class1_count = class1Count;
const unsigned class2_count = class2Count;
const unsigned class1_record_size = get_class1_record_size ();
const unsigned value_1_len = valueFormat1.get_len ();
const unsigned value_2_len = valueFormat2.get_len ();
const unsigned total_value_len = value_1_len + value_2_len;
unsigned accumulated = base_size;
unsigned coverage_size = 4;
unsigned class_def_1_size = 4;
unsigned max_coverage_size = coverage_size;
unsigned max_class_def_1_size = class_def_1_size;
hb_vector_t<unsigned> split_points;
hb_hashmap_t<unsigned, unsigned> device_tables = get_all_device_tables (c, this_index);
hb_vector_t<unsigned> format1_device_table_indices = valueFormat1.get_device_table_indices ();
hb_vector_t<unsigned> format2_device_table_indices = valueFormat2.get_device_table_indices ();
bool has_device_tables = bool(format1_device_table_indices) || bool(format2_device_table_indices);
hb_set_t visited;
for (unsigned i = 0; i < class1_count; i++)
{
unsigned accumulated_delta = class1_record_size;
coverage_size += estimator.incremental_coverage_size (i);
class_def_1_size += estimator.incremental_class_def_size (i);
max_coverage_size = hb_max (max_coverage_size, coverage_size);
max_class_def_1_size = hb_max (max_class_def_1_size, class_def_1_size);
if (has_device_tables) {
for (unsigned j = 0; j < class2_count; j++)
{
unsigned value1_index = total_value_len * (class2_count * i + j);
unsigned value2_index = value1_index + value_1_len;
accumulated_delta += size_of_value_record_children (c,
device_tables,
format1_device_table_indices,
value1_index,
visited);
accumulated_delta += size_of_value_record_children (c,
device_tables,
format2_device_table_indices,
value2_index,
visited);
}
}
accumulated += accumulated_delta;
unsigned total = accumulated
+ coverage_size + class_def_1_size + class_def_2_size
// The largest object will pack last and can exceed the size limit.
- hb_max (hb_max (coverage_size, class_def_1_size), class_def_2_size);
if (total >= (1 << 16))
{
split_points.push (i);
// split does not include i, so add the size for i when we reset the size counters.
accumulated = base_size + accumulated_delta;
coverage_size = 4 + estimator.incremental_coverage_size (i);
class_def_1_size = 4 + estimator.incremental_class_def_size (i);
visited.clear (); // node sharing isn't allowed between splits.
}
}
split_context_t split_context {
c,
this,
c.graph.duplicate_if_shared (parent_index, this_index),
class1_record_size,
total_value_len,
value_1_len,
value_2_len,
max_coverage_size,
max_class_def_1_size,
device_tables,
format1_device_table_indices,
format2_device_table_indices
};
return actuate_subtable_split<split_context_t> (split_context, split_points);
}
private:
struct split_context_t
{
gsubgpos_graph_context_t& c;
PairPosFormat2* thiz;
unsigned this_index;
unsigned class1_record_size;
unsigned value_record_len;
unsigned value1_record_len;
unsigned value2_record_len;
unsigned max_coverage_size;
unsigned max_class_def_size;
const hb_hashmap_t<unsigned, unsigned>& device_tables;
const hb_vector_t<unsigned>& format1_device_table_indices;
const hb_vector_t<unsigned>& format2_device_table_indices;
unsigned original_count ()
{
return thiz->class1Count;
}
unsigned clone_range (unsigned start, unsigned end)
{
return thiz->clone_range (*this, start, end);
}
bool shrink (unsigned count)
{
return thiz->shrink (*this, count);
}
};
size_t get_class1_record_size () const
{
const size_t class2_count = class2Count;
return
class2_count * (valueFormat1.get_size () + valueFormat2.get_size ());
}
unsigned clone_range (split_context_t& split_context,
unsigned start, unsigned end) const
{
DEBUG_MSG (SUBSET_REPACK, nullptr,
" Cloning PairPosFormat2 (%u) range [%u, %u).", split_context.this_index, start, end);
graph_t& graph = split_context.c.graph;
unsigned num_records = end - start;
unsigned prime_size = OT::Layout::GPOS_impl::PairPosFormat2_4<SmallTypes>::min_size
+ num_records * split_context.class1_record_size;
unsigned pair_pos_prime_id = split_context.c.create_node (prime_size);
if (pair_pos_prime_id == (unsigned) -1) return -1;
PairPosFormat2* pair_pos_prime =
(PairPosFormat2*) graph.object (pair_pos_prime_id).head;
pair_pos_prime->format = this->format;
pair_pos_prime->valueFormat1 = this->valueFormat1;
pair_pos_prime->valueFormat2 = this->valueFormat2;
pair_pos_prime->class1Count = num_records;
pair_pos_prime->class2Count = this->class2Count;
clone_class1_records (split_context,
pair_pos_prime_id,
start,
end);
unsigned coverage_id =
graph.index_for_offset (split_context.this_index, &coverage);
unsigned class_def_1_id =
graph.index_for_offset (split_context.this_index, &classDef1);
auto& coverage_v = graph.vertices_[coverage_id];
auto& class_def_1_v = graph.vertices_[class_def_1_id];
Coverage* coverage_table = (Coverage*) coverage_v.obj.head;
ClassDef* class_def_1_table = (ClassDef*) class_def_1_v.obj.head;
if (!coverage_table
|| !coverage_table->sanitize (coverage_v)
|| !class_def_1_table
|| !class_def_1_table->sanitize (class_def_1_v))
return -1;
auto klass_map =
+ coverage_table->iter ()
| hb_map_retains_sorting ([&] (hb_codepoint_t gid) {
return hb_pair_t<hb_codepoint_t, hb_codepoint_t> (gid, class_def_1_table->get_class (gid));
})
| hb_filter ([&] (hb_codepoint_t klass) {
return klass >= start && klass < end;
}, hb_second)
| hb_map_retains_sorting ([&] (hb_pair_t<hb_codepoint_t, hb_codepoint_t> gid_and_class) {
// Classes must be from 0...N so subtract start
return hb_pair_t<hb_codepoint_t, hb_codepoint_t> (gid_and_class.first, gid_and_class.second - start);
})
;
if (!Coverage::add_coverage (split_context.c,
pair_pos_prime_id,
2,
+ klass_map | hb_map_retains_sorting (hb_first),
split_context.max_coverage_size))
return -1;
// classDef1
if (!ClassDef::add_class_def (split_context.c,
pair_pos_prime_id,
8,
+ klass_map,
split_context.max_class_def_size))
return -1;
// classDef2
unsigned class_def_2_id =
graph.index_for_offset (split_context.this_index, &classDef2);
auto* class_def_link = graph.vertices_[pair_pos_prime_id].obj.real_links.push ();
class_def_link->width = SmallTypes::size;
class_def_link->objidx = class_def_2_id;
class_def_link->position = 10;
graph.vertices_[class_def_2_id].parents.push (pair_pos_prime_id);
graph.duplicate (pair_pos_prime_id, class_def_2_id);
return pair_pos_prime_id;
}
void clone_class1_records (split_context_t& split_context,
unsigned pair_pos_prime_id,
unsigned start, unsigned end) const
{
PairPosFormat2* pair_pos_prime =
(PairPosFormat2*) split_context.c.graph.object (pair_pos_prime_id).head;
char* start_addr = ((char*)&values[0]) + start * split_context.class1_record_size;
unsigned num_records = end - start;
hb_memcpy (&pair_pos_prime->values[0],
start_addr,
num_records * split_context.class1_record_size);
if (!split_context.format1_device_table_indices
&& !split_context.format2_device_table_indices)
// No device tables to move over.
return;
unsigned class2_count = class2Count;
for (unsigned i = start; i < end; i++)
{
for (unsigned j = 0; j < class2_count; j++)
{
unsigned value1_index = split_context.value_record_len * (class2_count * i + j);
unsigned value2_index = value1_index + split_context.value1_record_len;
unsigned new_value1_index = split_context.value_record_len * (class2_count * (i - start) + j);
unsigned new_value2_index = new_value1_index + split_context.value1_record_len;
transfer_device_tables (split_context,
pair_pos_prime_id,
split_context.format1_device_table_indices,
value1_index,
new_value1_index);
transfer_device_tables (split_context,
pair_pos_prime_id,
split_context.format2_device_table_indices,
value2_index,
new_value2_index);
}
}
}
void transfer_device_tables (split_context_t& split_context,
unsigned pair_pos_prime_id,
const hb_vector_t<unsigned>& device_table_indices,
unsigned old_value_record_index,
unsigned new_value_record_index) const
{
PairPosFormat2* pair_pos_prime =
(PairPosFormat2*) split_context.c.graph.object (pair_pos_prime_id).head;
for (unsigned i : device_table_indices)
{
OT::Offset16* record = (OT::Offset16*) &values[old_value_record_index + i];
unsigned record_position = ((char*) record) - ((char*) this);
if (!split_context.device_tables.has (record_position)) continue;
split_context.c.graph.move_child (
split_context.this_index,
record,
pair_pos_prime_id,
(OT::Offset16*) &pair_pos_prime->values[new_value_record_index + i]);
}
}
bool shrink (split_context_t& split_context,
unsigned count)
{
DEBUG_MSG (SUBSET_REPACK, nullptr,
" Shrinking PairPosFormat2 (%u) to [0, %u).",
split_context.this_index,
count);
unsigned old_count = class1Count;
if (count >= old_count)
return true;
graph_t& graph = split_context.c.graph;
class1Count = count;
graph.vertices_[split_context.this_index].obj.tail -=
(old_count - count) * split_context.class1_record_size;
auto coverage =
graph.as_mutable_table<Coverage> (split_context.this_index, &this->coverage);
if (!coverage) return false;
auto class_def_1 =
graph.as_mutable_table<ClassDef> (split_context.this_index, &classDef1);
if (!class_def_1) return false;
auto klass_map =
+ coverage.table->iter ()
| hb_map_retains_sorting ([&] (hb_codepoint_t gid) {
return hb_pair_t<hb_codepoint_t, hb_codepoint_t> (gid, class_def_1.table->get_class (gid));
})
| hb_filter ([&] (hb_codepoint_t klass) {
return klass < count;
}, hb_second)
;
auto new_coverage = + klass_map | hb_map_retains_sorting (hb_first);
if (!Coverage::make_coverage (split_context.c,
+ new_coverage,
coverage.index,
// existing ranges my not be kept, worst case size is a format 1
// coverage table.
4 + new_coverage.len() * 2))
return false;
return ClassDef::make_class_def (split_context.c,
+ klass_map,
class_def_1.index,
class_def_1.vertex->table_size ());
}
hb_hashmap_t<unsigned, unsigned>
get_all_device_tables (gsubgpos_graph_context_t& c,
unsigned this_index) const
{
const auto& v = c.graph.vertices_[this_index];
return v.position_to_index_map ();
}
const Coverage* get_coverage (gsubgpos_graph_context_t& c,
unsigned this_index) const
{
unsigned coverage_id = c.graph.index_for_offset (this_index, &coverage);
auto& coverage_v = c.graph.vertices_[coverage_id];
Coverage* coverage_table = (Coverage*) coverage_v.obj.head;
if (!coverage_table || !coverage_table->sanitize (coverage_v))
return &Null(Coverage);
return coverage_table;
}
const ClassDef* get_class_def_1 (gsubgpos_graph_context_t& c,
unsigned this_index) const
{
unsigned class_def_1_id = c.graph.index_for_offset (this_index, &classDef1);
auto& class_def_1_v = c.graph.vertices_[class_def_1_id];
ClassDef* class_def_1_table = (ClassDef*) class_def_1_v.obj.head;
if (!class_def_1_table || !class_def_1_table->sanitize (class_def_1_v))
return &Null(ClassDef);
return class_def_1_table;
}
unsigned size_of_value_record_children (gsubgpos_graph_context_t& c,
const hb_hashmap_t<unsigned, unsigned>& device_tables,
const hb_vector_t<unsigned> device_table_indices,
unsigned value_record_index,
hb_set_t& visited)
{
unsigned size = 0;
for (unsigned i : device_table_indices)
{
OT::Layout::GPOS_impl::Value* record = &values[value_record_index + i];
unsigned record_position = ((char*) record) - ((char*) this);
unsigned* obj_idx;
if (!device_tables.has (record_position, &obj_idx)) continue;
size += c.graph.find_subgraph_size (*obj_idx, visited);
}
return size;
}
unsigned size_of (gsubgpos_graph_context_t& c,
unsigned this_index,
const void* offset) const
{
const unsigned id = c.graph.index_for_offset (this_index, offset);
return c.graph.vertices_[id].table_size ();
}
};
struct PairPos : public OT::Layout::GPOS_impl::PairPos
{
hb_vector_t<unsigned> split_subtables (gsubgpos_graph_context_t& c,
unsigned parent_index,
unsigned this_index)
{
switch (u.format) {
case 1:
return ((PairPosFormat1*)(&u.format1))->split_subtables (c, parent_index, this_index);
case 2:
return ((PairPosFormat2*)(&u.format2))->split_subtables (c, parent_index, this_index);
#ifndef HB_NO_BEYOND_64K
case 3: HB_FALLTHROUGH;
case 4: HB_FALLTHROUGH;
// Don't split 24bit PairPos's.
#endif
default:
return hb_vector_t<unsigned> ();
}
}
bool sanitize (graph_t::vertex_t& vertex) const
{
int64_t vertex_len = vertex.obj.tail - vertex.obj.head;
if (vertex_len < u.format.get_size ()) return false;
switch (u.format) {
case 1:
return ((PairPosFormat1*)(&u.format1))->sanitize (vertex);
case 2:
return ((PairPosFormat2*)(&u.format2))->sanitize (vertex);
#ifndef HB_NO_BEYOND_64K
case 3: HB_FALLTHROUGH;
case 4: HB_FALLTHROUGH;
#endif
default:
// We don't handle format 3 and 4 here.
return false;
}
}
};
}
#endif // GRAPH_PAIRPOS_GRAPH_HH

View File

@ -0,0 +1,270 @@
/*
* Copyright © 2022 Google, Inc.
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Google Author(s): Garret Rieger
*/
#ifndef GRAPH_SERIALIZE_HH
#define GRAPH_SERIALIZE_HH
namespace graph {
struct overflow_record_t
{
unsigned parent;
unsigned child;
bool operator != (const overflow_record_t o) const
{ return !(*this == o); }
inline bool operator == (const overflow_record_t& o) const
{
return parent == o.parent &&
child == o.child;
}
inline uint32_t hash () const
{
uint32_t current = 0;
current = current * 31 + hb_hash (parent);
current = current * 31 + hb_hash (child);
return current;
}
};
inline
int64_t compute_offset (
const graph_t& graph,
unsigned parent_idx,
const hb_serialize_context_t::object_t::link_t& link)
{
const auto& parent = graph.vertices_[parent_idx];
const auto& child = graph.vertices_[link.objidx];
int64_t offset = 0;
switch ((hb_serialize_context_t::whence_t) link.whence) {
case hb_serialize_context_t::whence_t::Head:
offset = child.start - parent.start; break;
case hb_serialize_context_t::whence_t::Tail:
offset = child.start - parent.end; break;
case hb_serialize_context_t::whence_t::Absolute:
offset = child.start; break;
}
assert (offset >= link.bias);
offset -= link.bias;
return offset;
}
inline
bool is_valid_offset (int64_t offset,
const hb_serialize_context_t::object_t::link_t& link)
{
if (unlikely (!link.width))
// Virtual links can't overflow.
return link.is_signed || offset >= 0;
if (link.is_signed)
{
if (link.width == 4)
return offset >= -((int64_t) 1 << 31) && offset < ((int64_t) 1 << 31);
else
return offset >= -(1 << 15) && offset < (1 << 15);
}
else
{
if (link.width == 4)
return offset >= 0 && offset < ((int64_t) 1 << 32);
else if (link.width == 3)
return offset >= 0 && offset < ((int32_t) 1 << 24);
else
return offset >= 0 && offset < (1 << 16);
}
}
/*
* Will any offsets overflow on graph when it's serialized?
*/
inline bool
will_overflow (graph_t& graph,
hb_vector_t<overflow_record_t>* overflows = nullptr)
{
if (overflows) overflows->resize (0);
graph.update_positions ();
hb_hashmap_t<overflow_record_t*, bool> record_set;
const auto& vertices = graph.vertices_;
for (int parent_idx = vertices.length - 1; parent_idx >= 0; parent_idx--)
{
// Don't need to check virtual links for overflow
for (const auto& link : vertices[parent_idx].obj.real_links)
{
int64_t offset = compute_offset (graph, parent_idx, link);
if (is_valid_offset (offset, link))
continue;
if (!overflows) return true;
overflow_record_t r;
r.parent = parent_idx;
r.child = link.objidx;
if (record_set.has(&r)) continue; // don't keep duplicate overflows.
overflows->push (r);
record_set.set(&r, true);
}
}
if (!overflows) return false;
return overflows->length;
}
inline
void print_overflows (graph_t& graph,
const hb_vector_t<overflow_record_t>& overflows)
{
if (!DEBUG_ENABLED(SUBSET_REPACK)) return;
graph.update_parents ();
int limit = 10;
for (const auto& o : overflows)
{
if (!limit--) break;
const auto& parent = graph.vertices_[o.parent];
const auto& child = graph.vertices_[o.child];
DEBUG_MSG (SUBSET_REPACK, nullptr,
" overflow from "
"%4d (%4d in, %4d out, space %2d) => "
"%4d (%4d in, %4d out, space %2d)",
o.parent,
parent.incoming_edges (),
parent.obj.real_links.length + parent.obj.virtual_links.length,
graph.space_for (o.parent),
o.child,
child.incoming_edges (),
child.obj.real_links.length + child.obj.virtual_links.length,
graph.space_for (o.child));
}
if (overflows.length > 10) {
DEBUG_MSG (SUBSET_REPACK, nullptr, " ... plus %d more overflows.", overflows.length - 10);
}
}
template <typename O> inline void
serialize_link_of_type (const hb_serialize_context_t::object_t::link_t& link,
char* head,
hb_serialize_context_t* c)
{
OT::Offset<O>* offset = reinterpret_cast<OT::Offset<O>*> (head + link.position);
*offset = 0;
c->add_link (*offset,
// serializer has an extra nil object at the start of the
// object array. So all id's are +1 of what our id's are.
link.objidx + 1,
(hb_serialize_context_t::whence_t) link.whence,
link.bias);
}
inline
void serialize_link (const hb_serialize_context_t::object_t::link_t& link,
char* head,
hb_serialize_context_t* c)
{
switch (link.width)
{
case 0:
// Virtual links aren't serialized.
return;
case 4:
if (link.is_signed)
{
serialize_link_of_type<OT::HBINT32> (link, head, c);
} else {
serialize_link_of_type<OT::HBUINT32> (link, head, c);
}
return;
case 2:
if (link.is_signed)
{
serialize_link_of_type<OT::HBINT16> (link, head, c);
} else {
serialize_link_of_type<OT::HBUINT16> (link, head, c);
}
return;
case 3:
serialize_link_of_type<OT::HBUINT24> (link, head, c);
return;
default:
// Unexpected link width.
assert (0);
}
}
/*
* serialize graph into the provided serialization buffer.
*/
inline hb_blob_t* serialize (const graph_t& graph)
{
hb_vector_t<char> buffer;
size_t size = graph.total_size_in_bytes ();
if (!buffer.alloc (size)) {
DEBUG_MSG (SUBSET_REPACK, nullptr, "Unable to allocate output buffer.");
return nullptr;
}
hb_serialize_context_t c((void *) buffer, size);
c.start_serialize<void> ();
const auto& vertices = graph.vertices_;
for (unsigned i = 0; i < vertices.length; i++) {
c.push ();
size_t size = vertices[i].obj.tail - vertices[i].obj.head;
char* start = c.allocate_size <char> (size);
if (!start) {
DEBUG_MSG (SUBSET_REPACK, nullptr, "Buffer out of space.");
return nullptr;
}
hb_memcpy (start, vertices[i].obj.head, size);
// Only real links needs to be serialized.
for (const auto& link : vertices[i].obj.real_links)
serialize_link (link, start, &c);
// All duplications are already encoded in the graph, so don't
// enable sharing during packing.
c.pop_pack (false);
}
c.end_serialize ();
if (c.in_error ()) {
DEBUG_MSG (SUBSET_REPACK, nullptr, "Error during serialization. Err flag: %d",
c.errors);
return nullptr;
}
return c.copy_blob ();
}
} // namespace graph
#endif // GRAPH_SERIALIZE_HH

View File

@ -0,0 +1,69 @@
/*
* Copyright © 2022 Google, Inc.
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Google Author(s): Garret Rieger
*/
#ifndef GRAPH_SPLIT_HELPERS_HH
#define GRAPH_SPLIT_HELPERS_HH
namespace graph {
template<typename Context>
HB_INTERNAL
hb_vector_t<unsigned> actuate_subtable_split (Context& split_context,
const hb_vector_t<unsigned>& split_points)
{
hb_vector_t<unsigned> new_objects;
if (!split_points)
return new_objects;
for (unsigned i = 0; i < split_points.length; i++)
{
unsigned start = split_points[i];
unsigned end = (i < split_points.length - 1)
? split_points[i + 1]
: split_context.original_count ();
unsigned id = split_context.clone_range (start, end);
if (id == (unsigned) -1)
{
new_objects.reset ();
new_objects.allocated = -1; // mark error
return new_objects;
}
new_objects.push (id);
}
if (!split_context.shrink (split_points[0]))
{
new_objects.reset ();
new_objects.allocated = -1; // mark error
}
return new_objects;
}
}
#endif // GRAPH_SPLIT_HELPERS_HH

View File

@ -0,0 +1,119 @@
/*
* Copyright © 2022 Google, Inc.
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Google Author(s): Garret Rieger
*/
#include "gsubgpos-context.hh"
#include "classdef-graph.hh"
typedef hb_pair_t<hb_codepoint_t, hb_codepoint_t> gid_and_class_t;
typedef hb_vector_t<gid_and_class_t> gid_and_class_list_t;
static bool incremental_size_is (const gid_and_class_list_t& list, unsigned klass,
unsigned cov_expected, unsigned class_def_expected)
{
graph::class_def_size_estimator_t estimator (list.iter ());
unsigned result = estimator.incremental_coverage_size (klass);
if (result != cov_expected)
{
printf ("FAIL: coverage expected size %u but was %u\n", cov_expected, result);
return false;
}
result = estimator.incremental_class_def_size (klass);
if (result != class_def_expected)
{
printf ("FAIL: class def expected size %u but was %u\n", class_def_expected, result);
return false;
}
return true;
}
static void test_class_and_coverage_size_estimates ()
{
gid_and_class_list_t empty = {
};
assert (incremental_size_is (empty, 0, 0, 0));
assert (incremental_size_is (empty, 1, 0, 0));
gid_and_class_list_t class_zero = {
{5, 0},
};
assert (incremental_size_is (class_zero, 0, 2, 0));
gid_and_class_list_t consecutive = {
{4, 0},
{5, 0},
{6, 1},
{7, 1},
{8, 2},
{9, 2},
{10, 2},
{11, 2},
};
assert (incremental_size_is (consecutive, 0, 4, 0));
assert (incremental_size_is (consecutive, 1, 4, 4));
assert (incremental_size_is (consecutive, 2, 8, 6));
gid_and_class_list_t non_consecutive = {
{4, 0},
{5, 0},
{6, 1},
{7, 1},
{9, 2},
{10, 2},
{11, 2},
{12, 2},
};
assert (incremental_size_is (non_consecutive, 0, 4, 0));
assert (incremental_size_is (non_consecutive, 1, 4, 6));
assert (incremental_size_is (non_consecutive, 2, 8, 6));
gid_and_class_list_t multiple_ranges = {
{4, 0},
{5, 0},
{6, 1},
{7, 1},
{9, 1},
{11, 1},
{12, 1},
{13, 1},
};
assert (incremental_size_is (multiple_ranges, 0, 4, 0));
assert (incremental_size_is (multiple_ranges, 1, 2 * 6, 3 * 6));
}
int
main (int argc, char **argv)
{
test_class_and_coverage_size_estimates ();
}

View File

@ -0,0 +1,57 @@
#include "graph/gsubgpos-context.cc"
#include "hb-aat-layout.cc"
#include "hb-aat-map.cc"
#include "hb-blob.cc"
#include "hb-buffer-serialize.cc"
#include "hb-buffer-verify.cc"
#include "hb-buffer.cc"
#include "hb-common.cc"
#include "hb-draw.cc"
#include "hb-face.cc"
#include "hb-fallback-shape.cc"
#include "hb-font.cc"
#include "hb-map.cc"
#include "hb-number.cc"
#include "hb-ot-cff1-table.cc"
#include "hb-ot-cff2-table.cc"
#include "hb-ot-color.cc"
#include "hb-ot-face.cc"
#include "hb-ot-font.cc"
#include "hb-ot-layout.cc"
#include "hb-ot-map.cc"
#include "hb-ot-math.cc"
#include "hb-ot-meta.cc"
#include "hb-ot-metrics.cc"
#include "hb-ot-name.cc"
#include "hb-ot-shape-fallback.cc"
#include "hb-ot-shape-normalize.cc"
#include "hb-ot-shape.cc"
#include "hb-ot-shaper-arabic.cc"
#include "hb-ot-shaper-default.cc"
#include "hb-ot-shaper-hangul.cc"
#include "hb-ot-shaper-hebrew.cc"
#include "hb-ot-shaper-indic-table.cc"
#include "hb-ot-shaper-indic.cc"
#include "hb-ot-shaper-khmer.cc"
#include "hb-ot-shaper-myanmar.cc"
#include "hb-ot-shaper-syllabic.cc"
#include "hb-ot-shaper-thai.cc"
#include "hb-ot-shaper-use.cc"
#include "hb-ot-shaper-vowel-constraints.cc"
#include "hb-ot-tag.cc"
#include "hb-ot-var.cc"
#include "hb-set.cc"
#include "hb-shape-plan.cc"
#include "hb-shape.cc"
#include "hb-shaper.cc"
#include "hb-static.cc"
#include "hb-style.cc"
#include "hb-subset-cff-common.cc"
#include "hb-subset-cff1.cc"
#include "hb-subset-cff2.cc"
#include "hb-subset-input.cc"
#include "hb-subset-plan.cc"
#include "hb-subset-repacker.cc"
#include "hb-subset.cc"
#include "hb-ucd.cc"
#include "hb-unicode.cc"

View File

@ -5,10 +5,16 @@
#include "hb-buffer-verify.cc"
#include "hb-buffer.cc"
#include "hb-common.cc"
#include "hb-coretext.cc"
#include "hb-directwrite.cc"
#include "hb-draw.cc"
#include "hb-face.cc"
#include "hb-fallback-shape.cc"
#include "hb-font.cc"
#include "hb-ft.cc"
#include "hb-gdi.cc"
#include "hb-glib.cc"
#include "hb-graphite2.cc"
#include "hb-map.cc"
#include "hb-number.cc"
#include "hb-ot-cff1-table.cc"
@ -22,21 +28,21 @@
#include "hb-ot-meta.cc"
#include "hb-ot-metrics.cc"
#include "hb-ot-name.cc"
#include "hb-ot-shape-complex-arabic.cc"
#include "hb-ot-shape-complex-default.cc"
#include "hb-ot-shape-complex-hangul.cc"
#include "hb-ot-shape-complex-hebrew.cc"
#include "hb-ot-shape-complex-indic-table.cc"
#include "hb-ot-shape-complex-indic.cc"
#include "hb-ot-shape-complex-khmer.cc"
#include "hb-ot-shape-complex-myanmar.cc"
#include "hb-ot-shape-complex-syllabic.cc"
#include "hb-ot-shape-complex-thai.cc"
#include "hb-ot-shape-complex-use.cc"
#include "hb-ot-shape-complex-vowel-constraints.cc"
#include "hb-ot-shape-fallback.cc"
#include "hb-ot-shape-normalize.cc"
#include "hb-ot-shape.cc"
#include "hb-ot-shaper-arabic.cc"
#include "hb-ot-shaper-default.cc"
#include "hb-ot-shaper-hangul.cc"
#include "hb-ot-shaper-hebrew.cc"
#include "hb-ot-shaper-indic-table.cc"
#include "hb-ot-shaper-indic.cc"
#include "hb-ot-shaper-khmer.cc"
#include "hb-ot-shaper-myanmar.cc"
#include "hb-ot-shaper-syllabic.cc"
#include "hb-ot-shaper-thai.cc"
#include "hb-ot-shaper-use.cc"
#include "hb-ot-shaper-vowel-constraints.cc"
#include "hb-ot-tag.cc"
#include "hb-ot-var.cc"
#include "hb-set.cc"
@ -47,10 +53,4 @@
#include "hb-style.cc"
#include "hb-ucd.cc"
#include "hb-unicode.cc"
#include "hb-glib.cc"
#include "hb-ft.cc"
#include "hb-graphite2.cc"
#include "hb-uniscribe.cc"
#include "hb-gdi.cc"
#include "hb-directwrite.cc"
#include "hb-coretext.cc"

View File

@ -42,7 +42,7 @@ struct BaselineTableFormat0Part
bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
return_trace (likely (c->check_struct (this)));
return_trace (c->check_struct (this));
}
protected:
@ -78,7 +78,7 @@ struct BaselineTableFormat2Part
bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
return_trace (likely (c->check_struct (this)));
return_trace (c->check_struct (this));
}
protected:

View File

@ -415,18 +415,7 @@ struct Lookup
public:
DEFINE_SIZE_UNION (2, format);
};
/* Lookup 0 has unbounded size (dependant on num_glyphs). So we need to defined
* special NULL objects for Lookup<> objects, but since it's template our macros
* don't work. So we have to hand-code them here. UGLY. */
} /* Close namespace. */
/* Ugly hand-coded null objects for template Lookup<> :(. */
extern HB_INTERNAL const unsigned char _hb_Null_AAT_Lookup[2];
template <typename T>
struct Null<AAT::Lookup<T>> {
static AAT::Lookup<T> const & get_null ()
{ return *reinterpret_cast<const AAT::Lookup<T> *> (_hb_Null_AAT_Lookup); }
};
namespace AAT {
DECLARE_NULL_NAMESPACE_BYTES_TEMPLATE1 (AAT, Lookup, 2);
enum { DELETED_GLYPH = 0xFFFF };
@ -681,6 +670,13 @@ struct ObsoleteTypes
const void *base,
const T *array)
{
/* https://github.com/harfbuzz/harfbuzz/issues/3483 */
/* If offset is less than base, return an offset that would
* result in an address half a 32bit address-space away,
* to make sure sanitize fails even on 32bit builds. */
if (unlikely (offset < unsigned ((const char *) array - (const char *) base)))
return INT_MAX / T::static_size;
/* https://github.com/harfbuzz/harfbuzz/issues/2816 */
return (offset - unsigned ((const char *) array - (const char *) base)) / T::static_size;
}

View File

@ -62,7 +62,7 @@ struct SettingName
bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
return_trace (likely (c->check_struct (this)));
return_trace (c->check_struct (this));
}
protected:

View File

@ -48,7 +48,7 @@ struct ActionSubrecordHeader
bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
return_trace (likely (c->check_struct (this)));
return_trace (c->check_struct (this));
}
HBUINT16 actionClass; /* The JustClass value associated with this
@ -65,14 +65,14 @@ struct DecompositionAction
bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
return_trace (likely (c->check_struct (this)));
return_trace (c->check_struct (this));
}
ActionSubrecordHeader
header;
HBFixed lowerLimit; /* If the distance factor is less than this value,
F16DOT16 lowerLimit; /* If the distance factor is less than this value,
* then the ligature is decomposed. */
HBFixed upperLimit; /* If the distance factor is greater than this value,
F16DOT16 upperLimit; /* If the distance factor is greater than this value,
* then the ligature is decomposed. */
HBUINT16 order; /* Numerical order in which this ligature will
* be decomposed; you may want infrequent ligatures
@ -112,13 +112,13 @@ struct ConditionalAddGlyphAction
bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
return_trace (likely (c->check_struct (this)));
return_trace (c->check_struct (this));
}
protected:
ActionSubrecordHeader
header;
HBFixed substThreshold; /* Distance growth factor (in ems) at which
F16DOT16 substThreshold; /* Distance growth factor (in ems) at which
* this glyph is replaced and the growth factor
* recalculated. */
HBGlyphID16 addGlyph; /* Glyph to be added as kashida. If this value is
@ -137,7 +137,7 @@ struct DuctileGlyphAction
bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
return_trace (likely (c->check_struct (this)));
return_trace (c->check_struct (this));
}
protected:
@ -146,13 +146,13 @@ struct DuctileGlyphAction
HBUINT32 variationAxis; /* The 4-byte tag identifying the ductile axis.
* This would normally be 0x64756374 ('duct'),
* but you may use any axis the font contains. */
HBFixed minimumLimit; /* The lowest value for the ductility axis that
F16DOT16 minimumLimit; /* The lowest value for the ductility axis that
* still yields an acceptable appearance. Normally
* this will be 1.0. */
HBFixed noStretchValue; /* This is the default value that corresponds to
F16DOT16 noStretchValue; /* This is the default value that corresponds to
* no change in appearance. Normally, this will
* be 1.0. */
HBFixed maximumLimit; /* The highest value for the ductility axis that
F16DOT16 maximumLimit; /* The highest value for the ductility axis that
* still yields an acceptable appearance. */
public:
DEFINE_SIZE_STATIC (22);
@ -163,7 +163,7 @@ struct RepeatedAddGlyphAction
bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
return_trace (likely (c->check_struct (this)));
return_trace (c->check_struct (this));
}
protected:
@ -271,14 +271,14 @@ struct JustWidthDeltaEntry
};
protected:
HBFixed beforeGrowLimit;/* The ratio by which the advance width of the
F16DOT16 beforeGrowLimit;/* The ratio by which the advance width of the
* glyph is permitted to grow on the left or top side. */
HBFixed beforeShrinkLimit;
F16DOT16 beforeShrinkLimit;
/* The ratio by which the advance width of the
* glyph is permitted to shrink on the left or top side. */
HBFixed afterGrowLimit; /* The ratio by which the advance width of the glyph
F16DOT16 afterGrowLimit; /* The ratio by which the advance width of the glyph
* is permitted to shrink on the left or top side. */
HBFixed afterShrinkLimit;
F16DOT16 afterShrinkLimit;
/* The ratio by which the advance width of the glyph
* is at most permitted to shrink on the right or
* bottom side. */
@ -294,7 +294,7 @@ struct WidthDeltaPair
bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
return_trace (likely (c->check_struct (this)));
return_trace (c->check_struct (this));
}
protected:

View File

@ -287,7 +287,7 @@ struct KerxSubTableFormat1
* in the 'kern' table example. */
if (v == -0x8000)
{
o.attach_type() = ATTACH_TYPE_NONE;
o.attach_type() = OT::Layout::GPOS_impl::ATTACH_TYPE_NONE;
o.attach_chain() = 0;
o.y_offset = 0;
}
@ -310,7 +310,7 @@ struct KerxSubTableFormat1
/* CoreText doesn't do crossStream kerning in vertical. We do. */
if (v == -0x8000)
{
o.attach_type() = ATTACH_TYPE_NONE;
o.attach_type() = OT::Layout::GPOS_impl::ATTACH_TYPE_NONE;
o.attach_chain() = 0;
o.x_offset = 0;
}
@ -567,7 +567,7 @@ struct KerxSubTableFormat4
}
break;
}
o.attach_type() = ATTACH_TYPE_MARK;
o.attach_type() = OT::Layout::GPOS_impl::ATTACH_TYPE_MARK;
o.attach_chain() = (int) mark - (int) buffer->idx;
buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT;
}
@ -751,7 +751,7 @@ struct KerxSubTableHeader
bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
return_trace (likely (c->check_struct (this)));
return_trace (c->check_struct (this));
}
public:
@ -901,7 +901,7 @@ struct KerxTable
unsigned int count = c->buffer->len;
for (unsigned int i = 0; i < count; i++)
{
pos[i].attach_type() = ATTACH_TYPE_CURSIVE;
pos[i].attach_type() = OT::Layout::GPOS_impl::ATTACH_TYPE_CURSIVE;
pos[i].attach_chain() = HB_DIRECTION_IS_FORWARD (c->buffer->props.direction) ? -1 : +1;
/* We intentionally don't set HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT,
* since there needs to be a non-zero attachment for post-positioning to

View File

@ -123,7 +123,7 @@ struct RearrangementSubtable
bool reverse_l = 3 == (m >> 4);
bool reverse_r = 3 == (m & 0x0F);
if (end - start >= l + r)
if (end - start >= l + r && end-start <= HB_MAX_CONTEXT_LENGTH)
{
buffer->merge_clusters (start, hb_min (buffer->idx + 1, buffer->len));
buffer->merge_clusters (start, end);
@ -131,14 +131,14 @@ struct RearrangementSubtable
hb_glyph_info_t *info = buffer->info;
hb_glyph_info_t buf[4];
memcpy (buf, info + start, l * sizeof (buf[0]));
memcpy (buf + 2, info + end - r, r * sizeof (buf[0]));
hb_memcpy (buf, info + start, l * sizeof (buf[0]));
hb_memcpy (buf + 2, info + end - r, r * sizeof (buf[0]));
if (l != r)
memmove (info + start + r, info + start + l, (end - start - l - r) * sizeof (buf[0]));
memcpy (info + start, buf + 2, r * sizeof (buf[0]));
memcpy (info + end - l, buf, l * sizeof (buf[0]));
hb_memcpy (info + start, buf + 2, r * sizeof (buf[0]));
hb_memcpy (info + end - l, buf, l * sizeof (buf[0]));
if (reverse_l)
{
buf[0] = info[end - 1];
@ -980,6 +980,15 @@ struct Chain
setting = HB_AAT_LAYOUT_FEATURE_SELECTOR_LOWER_CASE_SMALL_CAPS;
goto retry;
}
#ifndef HB_NO_AAT
else if (type == HB_AAT_LAYOUT_FEATURE_TYPE_LANGUAGE_TAG_TYPE && setting &&
/* TODO: Rudimentary language matching. */
hb_language_matches (map->face->table.ltag->get_language (setting - 1), map->props.language))
{
flags &= feature.disableFlags;
flags |= feature.enableFlags;
}
#endif
}
}
return flags;

View File

@ -42,7 +42,7 @@ struct OpticalBounds
bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
return_trace (likely (c->check_struct (this)));
return_trace (c->check_struct (this));
}
FWORD leftSide;

View File

@ -62,7 +62,7 @@ struct TrackTableEntry
}
protected:
HBFixed track; /* Track value for this record. */
F16DOT16 track; /* Track value for this record. */
NameID trackNameID; /* The 'name' table index for this track.
* (a short word or phrase like "loose"
* or "very tight") */
@ -82,7 +82,7 @@ struct TrackData
const void *base) const
{
unsigned int sizes = nSizes;
hb_array_t<const HBFixed> size_table ((base+sizeTable).arrayZ, sizes);
hb_array_t<const F16DOT16> size_table ((base+sizeTable).arrayZ, sizes);
float s0 = size_table[idx].to_float ();
float s1 = size_table[idx + 1].to_float ();
@ -120,7 +120,7 @@ struct TrackData
if (!sizes) return 0.;
if (sizes == 1) return trackTableEntry->get_value (base, 0, sizes);
hb_array_t<const HBFixed> size_table ((base+sizeTable).arrayZ, sizes);
hb_array_t<const F16DOT16> size_table ((base+sizeTable).arrayZ, sizes);
unsigned int size_index;
for (size_index = 0; size_index < sizes - 1; size_index++)
if (size_table[size_index].to_float () >= ptem)
@ -141,7 +141,7 @@ struct TrackData
protected:
HBUINT16 nTracks; /* Number of separate tracks included in this table. */
HBUINT16 nSizes; /* Number of point sizes included in this table. */
NNOffset32To<UnsizedArrayOf<HBFixed>>
NNOffset32To<UnsizedArrayOf<F16DOT16>>
sizeTable; /* Offset from start of the tracking table to
* Array[nSizes] of size values.. */
UnsizedArrayOf<TrackTableEntry>

View File

@ -131,6 +131,7 @@ static const hb_aat_feature_mapping_t feature_mappings[] =
{HB_TAG ('p','n','u','m'), HB_AAT_LAYOUT_FEATURE_TYPE_NUMBER_SPACING, HB_AAT_LAYOUT_FEATURE_SELECTOR_PROPORTIONAL_NUMBERS, (hb_aat_layout_feature_selector_t) 4},
{HB_TAG ('p','w','i','d'), HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING, HB_AAT_LAYOUT_FEATURE_SELECTOR_PROPORTIONAL_TEXT, (hb_aat_layout_feature_selector_t) 7},
{HB_TAG ('q','w','i','d'), HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING, HB_AAT_LAYOUT_FEATURE_SELECTOR_QUARTER_WIDTH_TEXT, (hb_aat_layout_feature_selector_t) 7},
{HB_TAG ('r','l','i','g'), HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES, HB_AAT_LAYOUT_FEATURE_SELECTOR_REQUIRED_LIGATURES_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_REQUIRED_LIGATURES_OFF},
{HB_TAG ('r','u','b','y'), HB_AAT_LAYOUT_FEATURE_TYPE_RUBY_KANA, HB_AAT_LAYOUT_FEATURE_SELECTOR_RUBY_KANA_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_RUBY_KANA_OFF},
{HB_TAG ('s','i','n','f'), HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_POSITION, HB_AAT_LAYOUT_FEATURE_SELECTOR_SCIENTIFIC_INFERIORS, HB_AAT_LAYOUT_FEATURE_SELECTOR_NORMAL_POSITION},
{HB_TAG ('s','m','c','p'), HB_AAT_LAYOUT_FEATURE_TYPE_LOWER_CASE, HB_AAT_LAYOUT_FEATURE_SELECTOR_LOWER_CASE_SMALL_CAPS, HB_AAT_LAYOUT_FEATURE_SELECTOR_DEFAULT_LOWER_CASE},
@ -229,7 +230,7 @@ hb_aat_layout_compile_map (const hb_aat_map_builder_t *mapper,
*
* <note>Note: does not examine the `GSUB` table.</note>
*
* Return value: %true if data found, %false otherwise
* Return value: `true` if data found, `false` otherwise
*
* Since: 2.3.0
*/
@ -288,7 +289,7 @@ is_deleted_glyph (const hb_glyph_info_t *info)
void
hb_aat_layout_remove_deleted_glyphs (hb_buffer_t *buffer)
{
hb_ot_layout_delete_glyphs_inplace (buffer, is_deleted_glyph);
buffer->delete_glyphs_inplace (is_deleted_glyph);
}
/**
@ -300,7 +301,7 @@ hb_aat_layout_remove_deleted_glyphs (hb_buffer_t *buffer)
*
* <note>Note: does not examine the `GPOS` table.</note>
*
* Return value: %true if data found, %false otherwise
* Return value: `true` if data found, `false` otherwise
*
* Since: 2.3.0
*/
@ -333,7 +334,7 @@ hb_aat_layout_position (const hb_ot_shape_plan_t *plan,
* Tests whether the specified face includes any tracking information
* in the `trak` table.
*
* Return value: %true if data found, %false otherwise
* Return value: `true` if data found, `false` otherwise
*
* Since: 2.3.0
*/

View File

@ -38,7 +38,7 @@ struct hb_aat_map_t
void init ()
{
memset (this, 0, sizeof (*this));
hb_memset (this, 0, sizeof (*this));
chain_flags.init ();
}
void fini () { chain_flags.fini (); }
@ -52,8 +52,9 @@ struct hb_aat_map_builder_t
public:
HB_INTERNAL hb_aat_map_builder_t (hb_face_t *face_,
const hb_segment_properties_t *props_ HB_UNUSED) :
face (face_) {}
const hb_segment_properties_t props_) :
face (face_),
props (props_) {}
HB_INTERNAL void add_feature (hb_tag_t tag, unsigned int value=1);
@ -87,6 +88,7 @@ struct hb_aat_map_builder_t
public:
hb_face_t *face;
hb_segment_properties_t props;
public:
hb_sorted_vector_t<feature_info_t> features;

View File

@ -59,7 +59,7 @@
static inline constexpr T operator | (T l, T r) { return T ((unsigned) l | (unsigned) r); } \
static inline constexpr T operator & (T l, T r) { return T ((unsigned) l & (unsigned) r); } \
static inline constexpr T operator ^ (T l, T r) { return T ((unsigned) l ^ (unsigned) r); } \
static inline constexpr T operator ~ (T r) { return T (~(unsigned int) r); } \
static inline constexpr unsigned operator ~ (T r) { return (~(unsigned) r); } \
static inline T& operator |= (T &l, T r) { l = l | r; return l; } \
static inline T& operator &= (T& l, T r) { l = l & r; return l; } \
static inline T& operator ^= (T& l, T r) { l = l ^ r; return l; } \
@ -109,15 +109,16 @@ struct BEInt<Type, 2>
struct __attribute__((packed)) packed_uint16_t { uint16_t v; };
constexpr operator Type () const
{
#if ((defined(__GNUC__) && __GNUC__ >= 5) || defined(__clang__)) && \
#if defined(__OPTIMIZE__) && !defined(HB_NO_PACKED) && \
((defined(__GNUC__) && __GNUC__ >= 5) || defined(__clang__)) && \
defined(__BYTE_ORDER) && \
(__BYTE_ORDER == __LITTLE_ENDIAN || __BYTE_ORDER == __BIG_ENDIAN)
/* Spoon-feed the compiler a big-endian integer with alignment 1.
* https://github.com/harfbuzz/harfbuzz/pull/1398 */
#if __BYTE_ORDER == __LITTLE_ENDIAN
return __builtin_bswap16 (((packed_uint16_t *) this)->v);
return __builtin_bswap16 (((packed_uint16_t *) v)->v);
#else /* __BYTE_ORDER == __BIG_ENDIAN */
return ((packed_uint16_t *) this)->v;
return ((packed_uint16_t *) v)->v;
#endif
#else
return (v[0] << 8)
@ -150,10 +151,27 @@ struct BEInt<Type, 4>
uint8_t ((V >> 16) & 0xFF),
uint8_t ((V >> 8) & 0xFF),
uint8_t ((V ) & 0xFF)} {}
constexpr operator Type () const { return (v[0] << 24)
+ (v[1] << 16)
+ (v[2] << 8)
+ (v[3] ); }
struct __attribute__((packed)) packed_uint32_t { uint32_t v; };
constexpr operator Type () const {
#if defined(__OPTIMIZE__) && !defined(HB_NO_PACKED) && \
((defined(__GNUC__) && __GNUC__ >= 5) || defined(__clang__)) && \
defined(__BYTE_ORDER) && \
(__BYTE_ORDER == __LITTLE_ENDIAN || __BYTE_ORDER == __BIG_ENDIAN)
/* Spoon-feed the compiler a big-endian integer with alignment 1.
* https://github.com/harfbuzz/harfbuzz/pull/1398 */
#if __BYTE_ORDER == __LITTLE_ENDIAN
return __builtin_bswap32 (((packed_uint32_t *) v)->v);
#else /* __BYTE_ORDER == __BIG_ENDIAN */
return ((packed_uint32_t *) v)->v;
#endif
#else
return (v[0] << 24)
+ (v[1] << 16)
+ (v[2] << 8)
+ (v[3] );
#endif
}
private: uint8_t v[4];
};
@ -211,31 +229,15 @@ struct
}
HB_FUNCOBJ (hb_bool);
template <typename T>
static inline
T hb_coerce (const T v) { return v; }
template <typename T, typename V,
hb_enable_if (!hb_is_same (hb_decay<T>, hb_decay<V>) && std::is_pointer<V>::value)>
static inline
T hb_coerce (const V v) { return *v; }
struct
{
private:
template <typename T> constexpr auto
impl (const T& v, hb_priority<2>) const HB_RETURN (uint32_t, hb_deref (v).hash ())
impl (const T& v, hb_priority<1>) const HB_RETURN (uint32_t, hb_deref (v).hash ())
template <typename T> constexpr auto
impl (const T& v, hb_priority<1>) const HB_RETURN (uint32_t, std::hash<hb_decay<decltype (hb_deref (v))>>{} (hb_deref (v)))
template <typename T,
hb_enable_if (std::is_integral<T>::value)> constexpr auto
impl (const T& v, hb_priority<0>) const HB_AUTO_RETURN
(
/* Knuth's multiplicative method: */
(uint32_t) v * 2654435761u
)
impl (const T& v, hb_priority<0>) const HB_RETURN (uint32_t, std::hash<hb_decay<decltype (hb_deref (v))>>{} (hb_deref (v)))
public:
@ -482,6 +484,17 @@ struct
}
HB_FUNCOBJ (hb_equal);
struct
{
template <typename T> void
operator () (T& a, T& b) const
{
using std::swap; // allow ADL
swap (a, b);
}
}
HB_FUNCOBJ (hb_swap);
template <typename T1, typename T2>
struct hb_pair_t
@ -494,7 +507,7 @@ struct hb_pair_t
hb_enable_if (std::is_default_constructible<U1>::value &&
std::is_default_constructible<U2>::value)>
hb_pair_t () : first (), second () {}
hb_pair_t (T1 a, T2 b) : first (a), second (b) {}
hb_pair_t (T1 a, T2 b) : first (std::forward<T1> (a)), second (std::forward<T2> (b)) {}
template <typename Q1, typename Q2,
hb_enable_if (hb_is_convertible (T1, Q1) &&
@ -511,10 +524,28 @@ struct hb_pair_t
bool operator > (const pair_t& o) const { return first > o.first || (first == o.first && second > o.second); }
bool operator <= (const pair_t& o) const { return !(*this > o); }
static int cmp (const void *pa, const void *pb)
{
pair_t *a = (pair_t *) pa;
pair_t *b = (pair_t *) pb;
if (a->first < b->first) return -1;
if (a->first > b->first) return +1;
if (a->second < b->second) return -1;
if (a->second > b->second) return +1;
return 0;
}
friend void swap (hb_pair_t& a, hb_pair_t& b)
{
hb_swap (a.first, b.first);
hb_swap (a.second, b.second);
}
T1 first;
T2 second;
};
#define hb_pair_t(T1,T2) hb_pair_t<T1, T2>
template <typename T1, typename T2> static inline hb_pair_t<T1, T2>
hb_pair (T1&& a, T2&& b) { return hb_pair_t<T1, T2> (a, b); }
@ -540,14 +571,14 @@ struct
{
template <typename T, typename T2> constexpr auto
operator () (T&& a, T2&& b) const HB_AUTO_RETURN
(a <= b ? std::forward<T> (a) : std::forward<T2> (b))
(a <= b ? a : b)
}
HB_FUNCOBJ (hb_min);
struct
{
template <typename T, typename T2> constexpr auto
operator () (T&& a, T2&& b) const HB_AUTO_RETURN
(a >= b ? std::forward<T> (a) : std::forward<T2> (b))
(a >= b ? a : b)
}
HB_FUNCOBJ (hb_max);
struct
@ -558,17 +589,6 @@ struct
}
HB_FUNCOBJ (hb_clamp);
struct
{
template <typename T> void
operator () (T& a, T& b) const
{
using std::swap; // allow ADL
swap (a, b);
}
}
HB_FUNCOBJ (hb_swap);
/*
* Bithacks.
*/
@ -837,14 +857,14 @@ hb_in_range (T u, T lo, T hi)
return (T)(u - lo) <= (T)(hi - lo);
}
template <typename T> static inline bool
hb_in_ranges (T u, T lo1, T hi1, T lo2, T hi2)
hb_in_ranges (T u, T lo1, T hi1)
{
return hb_in_range (u, lo1, hi1) || hb_in_range (u, lo2, hi2);
return hb_in_range (u, lo1, hi1);
}
template <typename T> static inline bool
hb_in_ranges (T u, T lo1, T hi1, T lo2, T hi2, T lo3, T hi3)
template <typename T, typename ...Ts> static inline bool
hb_in_ranges (T u, T lo1, T hi1, Ts... ds)
{
return hb_in_range (u, lo1, hi1) || hb_in_range (u, lo2, hi2) || hb_in_range (u, lo3, hi3);
return hb_in_range<T> (u, lo1, hi1) || hb_in_ranges<T> (u, ds...);
}
@ -852,10 +872,18 @@ hb_in_ranges (T u, T lo1, T hi1, T lo2, T hi2, T lo3, T hi3)
* Overflow checking.
*/
/* Consider __builtin_mul_overflow use here also */
static inline bool
hb_unsigned_mul_overflows (unsigned int count, unsigned int size)
hb_unsigned_mul_overflows (unsigned int count, unsigned int size, unsigned *result = nullptr)
{
#if (defined(__GNUC__) && (__GNUC__ >= 4)) || defined(__clang__)
unsigned stack_result;
if (!result)
result = &stack_result;
return __builtin_mul_overflow (count, size, result);
#endif
if (result)
*result = count * size;
return (size > 0) && (count >= ((unsigned int) -1) / size);
}
@ -956,7 +984,7 @@ void hb_qsort(void *base, size_t nel, size_t width,
[void *arg]);
*/
#define SORT_R_SWAP(a,b,tmp) ((tmp) = (a), (a) = (b), (b) = (tmp))
#define SORT_R_SWAP(a,b,tmp) ((void) ((tmp) = (a)), (void) ((a) = (b)), (b) = (tmp))
/* swap a and b */
/* a and b must not be equal! */
@ -1147,9 +1175,12 @@ hb_qsort (void *base, size_t nel, size_t width,
}
template <typename T, typename T2, typename T3> static inline void
hb_stable_sort (T *array, unsigned int len, int(*compar)(const T2 *, const T2 *), T3 *array2)
template <typename T, typename T2, typename T3 = int> static inline void
hb_stable_sort (T *array, unsigned int len, int(*compar)(const T2 *, const T2 *), T3 *array2 = nullptr)
{
static_assert (hb_is_trivially_copy_assignable (T), "");
static_assert (hb_is_trivially_copy_assignable (T3), "");
for (unsigned int i = 1; i < len; i++)
{
unsigned int j = i;
@ -1172,12 +1203,6 @@ hb_stable_sort (T *array, unsigned int len, int(*compar)(const T2 *, const T2 *)
}
}
template <typename T> static inline void
hb_stable_sort (T *array, unsigned int len, int(*compar)(const T *, const T *))
{
hb_stable_sort (array, len, compar, (int *) nullptr);
}
static inline hb_bool_t
hb_codepoint_parse (const char *s, unsigned int len, int base, hb_codepoint_t *out)
{
@ -1305,47 +1330,4 @@ struct
HB_FUNCOBJ (hb_dec);
/* Compiler-assisted vectorization. */
/* Type behaving similar to vectorized vars defined using __attribute__((vector_size(...))),
* basically a fixed-size bitset. */
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 clear (unsigned char v = 0) { memset (this, v, sizeof (*this)); }
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); }
private:
static_assert (0 == byte_size % sizeof (elt_t), "");
elt_t v[byte_size / sizeof (elt_t)];
};
#endif /* HB_ALGS_HH */

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