Merge pull request #1 from harfbuzz/main

Update my copy from upstream
This commit is contained in:
Tim Eves 2022-10-27 11:56:12 +07:00 committed by GitHub
commit e215e4b51d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
89 changed files with 1839 additions and 715 deletions

25
.github/workflows/arm-ci.yml vendored Normal file
View File

@ -0,0 +1,25 @@
name: arm
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
permissions:
contents: read
jobs:
arm-none-eabi:
runs-on: ubuntu-22.04
container:
image: devkitpro/devkitarm:latest
steps:
- uses: actions/checkout@v3
- name: Configure CMake
run: |
cmake -S . -B build \
-DCMAKE_TOOLCHAIN_FILE=${DEVKITPRO}/cmake/3DS.cmake
- name: Build
run: make CXX_FLAGS="-w -DHB_NO_MT"
working-directory: build

47
NEWS
View File

@ -1,3 +1,50 @@
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
====================================

View File

@ -1,6 +1,6 @@
AC_PREREQ([2.64])
AC_INIT([HarfBuzz],
[5.1.0],
[5.3.1],
[https://github.com/harfbuzz/harfbuzz/issues/new],
[harfbuzz],
[http://harfbuzz.org/])

View File

@ -117,6 +117,7 @@
<index id="api-index-full"><title>API Index</title><xi:include href="xml/api-index-full.xml"><xi:fallback /></xi:include></index>
<index id="deprecated-api-index" role="deprecated"><title>Index of deprecated API</title><xi:include href="xml/api-index-deprecated.xml"><xi:fallback /></xi:include></index>
<index id="api-index-5-3-0" role="5.3.0"><title>Index of new symbols in 5.3.0</title><xi:include href="xml/api-index-5.3.0.xml"><xi:fallback /></xi:include></index>
<index id="api-index-5-0-0" role="5.0.0"><title>Index of new symbols in 5.0.0</title><xi:include href="xml/api-index-5.0.0.xml"><xi:fallback /></xi:include></index>
<index id="api-index-4-4-0" role="4.4.0"><title>Index of new symbols in 4.4.0</title><xi:include href="xml/api-index-4.4.0.xml"><xi:fallback /></xi:include></index>
<index id="api-index-4-3-0" role="4.3.0"><title>Index of new symbols in 4.3.0</title><xi:include href="xml/api-index-4.3.0.xml"><xi:fallback /></xi:include></index>

View File

@ -285,6 +285,7 @@ hb_face_collect_variation_selectors
hb_face_collect_variation_unicodes
hb_face_builder_create
hb_face_builder_add_table
hb_face_builder_sort_tables
</SECTION>
<SECTION>
@ -554,6 +555,7 @@ hb_ot_layout_language_get_feature_tags
hb_ot_layout_language_get_required_feature
hb_ot_layout_lookup_collect_glyphs
hb_ot_layout_lookup_get_glyph_alternates
hb_ot_layout_lookup_get_optical_bound
hb_ot_layout_lookup_substitute_closure
hb_ot_layout_lookups_substitute_closure
hb_ot_layout_lookup_would_substitute
@ -786,4 +788,7 @@ hb_subset_plan_old_to_new_glyph_mapping
hb_link_t
hb_object_t
hb_subset_repack_or_fail
hb_subset_preprocess
hb_subset_input_pin_axis_location
hb_subset_input_pin_axis_to_default
</SECTION>

View File

@ -54,6 +54,12 @@ There's four key pieces to the harfbuzz approach:
to calculate the final position of each subtable and then check if any offsets to it will
overflow.
* Content Aware Preprocessing: if the overflow resolver is aware of the format of the underlying
tables (eg. GSUB, GPOS) then in some cases preprocessing can be done to increase the chance of
successfully packing the graph. For example for GSUB and GPOS we can preprocess the graph and
promote lookups to extension lookups (upgrades a 16 bit offset to 32 bits) or split large lookup
subtables into two or more pieces.
* Offset resolution strategies: given a particular occurrence of an overflow these strategies
modify the graph to attempt to resolve the overflow.
@ -64,6 +70,7 @@ def repack(graph):
graph.topological_sort()
if (graph.will_overflow())
preprocess(graph)
assign_spaces(graph)
graph.topological_sort()
@ -185,6 +192,37 @@ The assign_spaces() step in the high level algorithm is responsible for identify
subgraphs and assigning unique spaces to each one. More information on the space assignment can be
found in the next section.
# Graph Preprocessing
For certain table types we can preprocess and modify the graph structure to reduce the occurences
of overflows. Currently the repacker implements preprocessing only for GPOS and GSUB tables.
## GSUB/GPOS Table Splitting
The GSUB/GPOS preprocessor scans each lookup subtable and determines if the subtable's children are
so large that no overflow resolution is possible (for example a single subtable that exceeds 65kb
cannot be pointed over). When such cases are detected table splitting is invoked:
* The subtable is first analyzed to determine the smallest number of split points that will allow
for successful offset overflow resolution.
* Then the subtable in the graph representation is modified to actually perform the split at the
previously computed split points. At a high level splits are done by inserting new subtables
which contain a subset of the data of the original subtable and then shrinking the original subtable.
Table splitting must be aware of the underlying format of each subtable type and thus needs custom
code for each subtable type. Currently subtable splitting is only supported for GPOS subtable types.
## GSUB/GPOS Extension Lookup Promotion
In GSUB/GPOS tables lookups can be regular lookups which use 16 bit offsets to the children subtables
or extension lookups which use 32 bit offsets to the children subtables. If the sub graph of all
regular lookups is too large then it can be difficult to find an overflow free configuration. This
can be remedied by promoting one or more regular lookups to extension lookups.
During preprocessing the graph is scanned to determine the size of the subgraph of regular lookups.
If the graph is found to be too big then the analysis finds a set of lookups to promote to reduce
the subgraph size. Lastly the graph is modified to convert those lookups to extension lookups.
# Offset Resolution Strategies
@ -237,29 +275,20 @@ The harfbuzz repacker has tests defined using generic graphs: https://github.com
# Future Improvements
The above resolution strategies are not sufficient to resolve all overflows. For example consider
the case where a single subtable is 65k and the graph structure requires an offset to point over it.
Currently for GPOS tables the repacker implementation is sufficient to handle both subsetting and the
general case of font compilation repacking. However for GSUB the repacker is only sufficient for
subsetting related overflows. To enable general case repacking of GSUB, support for splitting of
GSUB subtables will need to be added. Other table types such as COLRv1 shouldn't require table
splitting due to the wide use of 24 bit offsets throughout the table.
The current harfbuzz implementation is suitable for the vast majority of subsetting related overflows.
Subsetting related overflows are typically easy to solve since all subsets are derived from a font
that was originally overflow free. A more general purpose version of the algorithm suitable for font
creation purposes will likely need some additional offset resolution strategies:
Beyond subtable splitting there are a couple of "nice to have" improvements, but these are not required
to support the general case:
* Extension demotion: currently extension promotion is supported but in some cases if the non-extension
subgraph is underfilled then packed size can be reduced by demoting extension lookups back to regular
lookups.
* Currently only children nodes are moved to resolve offsets. However, in many cases moving a parent
node closer to it's children will have less impact on the size of other offsets. Thus the algorithm
should use a heuristic (based on parent and child subtable sizes) to decide if the children's
priority should be increased or the parent's priority decreased.
* Many subtables can be split into two smaller subtables without impacting the overall functionality.
This should be done when an overflow is the result of a very large table which can't be moved
to avoid offsets pointing over it.
* Lookup subtables in GSUB/GPOS can be upgraded to extension lookups which uses a 32 bit offset.
Overflows from a Lookup subtable to it's child should be resolved by converting to an extension
lookup.
Once additional resolution strategies are added to the algorithm it's likely that we'll need to
switch to using a [backtracking algorithm](https://en.wikipedia.org/wiki/Backtracking) to explore
the various combinations of resolution strategies until a non-overflowing combination is found. This
will require the ability to restore the graph to an earlier state. It's likely that using a stack
of undoable resolution commands could be used to accomplish this.

View File

@ -1,6 +1,6 @@
project('harfbuzz', 'c', 'cpp',
meson_version: '>= 0.55.0',
version: '5.1.0',
version: '5.3.1',
default_options: [
'cpp_rtti=false', # Just to support msvc, we are passing -fno-exceptions also anyway
'cpp_std=c++11',
@ -83,25 +83,39 @@ check_funcs = [
m_dep = cpp.find_library('m', required: false)
# https://github.com/harfbuzz/harfbuzz/pull/2498
freetype_dep = dependency(cpp.get_argument_syntax() == 'msvc' ? 'freetype' : 'freetype2',
# Try pkgconfig name
freetype_dep = dependency('freetype2', required: false)
if not freetype_dep.found()
# Try cmake name
freetype_dep = dependency('freetype', required: false)
endif
if not freetype_dep.found()
# Subproject fallback, `allow_fallback: true` means the fallback will be
# tried even if the freetype option is set to `auto`.
freetype_dep = dependency('freetype2',
required: get_option('freetype'),
default_options: ['harfbuzz=disabled'])
default_options: ['harfbuzz=disabled'],
allow_fallback: true)
endif
glib_dep = dependency('glib-2.0', required: get_option('glib'))
gobject_dep = dependency('gobject-2.0', required: get_option('gobject'))
graphite2_dep = dependency('graphite2', required: get_option('graphite2'))
graphite_dep = dependency('graphite2', required: get_option('graphite'))
if cpp.get_argument_syntax() == 'msvc'
# Try pkgconfig name
icu_dep = dependency('icu-uc', required: false)
if not icu_dep.found()
# Try cmake name
icu_dep = dependency('ICU',
required: get_option('icu'),
required: false,
components: 'uc',
method: 'cmake')
else
icu_dep = dependency('icu-uc',
required: get_option('icu'),
method: 'pkg-config')
endif
if not icu_dep.found()
# Subproject fallback if icu option is enabled
icu_dep = dependency('icu-uc', required: get_option('icu'))
endif
if icu_dep.found() and icu_dep.type_name() == 'pkgconfig'

View File

@ -97,6 +97,19 @@ void AddGlyphs(unsigned num_glyphs_in_font,
}
}
// Preprocess face and populate the subset accelerator on it to speed up
// the subsetting operations.
static hb_face_t* preprocess_face(hb_face_t* face)
{
#ifdef HB_EXPERIMENTAL_API
hb_face_t* new_face = hb_subset_preprocess(face);
hb_face_destroy(face);
return new_face;
#else
return face;
#endif
}
/* benchmark for subsetting a font */
static void BM_subset (benchmark::State &state,
operation_t operation,
@ -110,6 +123,8 @@ static void BM_subset (benchmark::State &state,
assert (blob);
face = hb_face_create (blob, 0);
hb_blob_destroy (blob);
face = preprocess_face (face);
}
hb_subset_input_t* input = hb_subset_input_create_or_fail ();

View File

@ -341,6 +341,7 @@ HB_SUBSET_sources = \
hb-subset-cff2.hh \
hb-subset-input.cc \
hb-subset-input.hh \
hb-subset-accelerator.hh \
hb-subset-plan.cc \
hb-subset-plan.hh \
hb-subset-repacker.cc \

View File

@ -39,7 +39,7 @@ struct GPOS : GSUBGPOS
bool subset (hb_subset_context_t *c) const
{
hb_subset_layout_context_t l (c, tableTag, c->plan->gpos_lookups, c->plan->gpos_langsys, c->plan->gpos_features);
hb_subset_layout_context_t l (c, tableTag);
return GSUBGPOS::subset<PosLookup> (&l);
}

View File

@ -80,6 +80,24 @@ struct SinglePosFormat1
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))>

View File

@ -68,7 +68,7 @@ struct SinglePosFormat2
unsigned int index = (this+coverage).get_coverage (buffer->cur().codepoint);
if (likely (index == NOT_COVERED)) return_trace (false);
if (likely (index >= valueCount)) return_trace (false);
if (unlikely (index >= valueCount)) return_trace (false);
if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
{
@ -92,6 +92,28 @@ struct SinglePosFormat2
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))>

View File

@ -27,7 +27,7 @@ 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);
}

View File

@ -118,7 +118,7 @@ struct Ligature
match_positions[i] += delta;
if (i)
*p++ = ',';
sprintf (p, "%u", match_positions[i]);
snprintf (p, sizeof(buf), "%u", match_positions[i]);
p += strlen(p);
}

View File

@ -117,7 +117,7 @@ struct Sequence
{
if (buf < p)
*p++ = ',';
sprintf (p, "%u", i);
snprintf (p, sizeof(buf), "%u", i);
p += strlen(p);
}

View File

@ -57,7 +57,7 @@ struct SingleSubstFormat1_3
hb_codepoint_t max_before = intersection.get_max ();
hb_codepoint_t min_after = (min_before + d) & mask;
hb_codepoint_t max_after = (max_before + d) & mask;
if (pop >= max_before - min_before &&
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;

View File

@ -16,6 +16,11 @@ 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,
@ -102,17 +107,19 @@ struct Glyph
hb_bytes_t &dest_bytes /* OUT */) const
{
GlyphHeader *glyph_header = nullptr;
if (all_points.length > 4)
if (type != EMPTY && all_points.length > 4)
{
glyph_header = (GlyphHeader *) hb_calloc (1, GlyphHeader::static_size);
if (unlikely (!glyph_header)) return false;
}
int xMin, xMax;
int xMin = 0, xMax = 0;
int yMin = 0, yMax = 0;
if (all_points.length > 4)
{
xMin = xMax = roundf (all_points[0].x);
int yMin, yMax;
yMin = yMax = roundf (all_points[0].y);
}
for (unsigned i = 1; i < all_points.length - 4; i++)
{
@ -128,7 +135,7 @@ struct Glyph
/*for empty glyphs: all_points only include phantom points.
*just update metrics and then return */
if (all_points.length == 4)
if (!glyph_header)
return true;
glyph_header->numberOfContours = header->numberOfContours;
@ -145,10 +152,17 @@ struct Glyph
hb_font_t *font,
const glyf_accelerator_t &glyf,
hb_bytes_t &dest_start, /* IN/OUT */
hb_bytes_t &dest_end /* OUT */) const
hb_bytes_t &dest_end /* OUT */)
{
contour_point_vector_t all_points, deltas;
get_points (font, glyf, all_points, &deltas, false);
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:
@ -171,7 +185,12 @@ struct Glyph
break;
}
return compile_header_bytes (plan, all_points, dest_start);
if (!compile_header_bytes (plan, all_points, dest_start))
{
dest_end.fini ();
return false;
}
return true;
}
@ -182,6 +201,7 @@ struct Glyph
bool get_points (hb_font_t *font, const accelerator_t &glyf_accelerator,
contour_point_vector_t &all_points /* OUT */,
contour_point_vector_t *deltas = nullptr, /* OUT */
bool shift_points_hori = true,
bool use_my_metrics = true,
bool phantom_only = false,
unsigned int depth = 0) const
@ -238,8 +258,7 @@ struct Glyph
if (deltas != nullptr && depth == 0 && type == COMPOSITE)
{
if (unlikely (!deltas->resize (points.length))) return false;
for (unsigned i = 0 ; i < points.length; i++)
deltas->arrayZ[i] = points.arrayZ[i];
deltas->copy_vector (points);
}
#ifndef HB_NO_VAR
@ -271,14 +290,9 @@ struct Glyph
comp_points.reset ();
if (unlikely (!glyf_accelerator.glyph_for_gid (item.get_gid ())
.get_points (font, glyf_accelerator, comp_points,
deltas, use_my_metrics, phantom_only, depth + 1)))
deltas, shift_points_hori, use_my_metrics, phantom_only, 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);
@ -299,6 +313,14 @@ struct Glyph
}
}
/* Copy phantom points from component if USE_MY_METRICS flag set */
if (use_my_metrics && item.is_use_my_metrics ())
for (unsigned int i = 0; i < PHANTOM_COUNT; i++)
phantoms[i] = comp_points[comp_points.length - PHANTOM_COUNT + i];
if (all_points.length > HB_GLYF_MAX_POINTS)
return false;
all_points.extend (comp_points.sub_array (0, comp_points.length - PHANTOM_COUNT));
comp_index++;
@ -310,7 +332,7 @@ struct Glyph
all_points.extend (phantoms);
}
if (depth == 0) /* Apply at top level */
if (depth == 0 && shift_points_hori) /* Apply at top level */
{
/* Undocumented rasterizer behavior:
* Shift points horizontally by the updated left side bearing
@ -332,8 +354,14 @@ struct Glyph
hb_bytes_t get_bytes () const { return bytes; }
Glyph (hb_bytes_t bytes_ = hb_bytes_t (),
hb_codepoint_t gid_ = (hb_codepoint_t) -1) : bytes (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_)
{

View File

@ -271,31 +271,29 @@ struct SimpleGlyph
}
//convert absolute values to relative values
unsigned num_points = all_points.length - 4;
hb_vector_t<hb_pair_t<int, int>> deltas;
deltas.resize (num_points);
for (unsigned i = 0; i < num_points; i++)
{
deltas[i].first = i == 0 ? roundf (all_points[i].x) : roundf (all_points[i].x) - roundf (all_points[i-1].x);
deltas[i].second = i == 0 ? roundf (all_points[i].y) : roundf (all_points[i].y) - roundf (all_points[i-1].y);
}
hb_vector_t<uint8_t> flags, x_coords, y_coords;
flags.alloc (num_points);
x_coords.alloc (2*num_points);
y_coords.alloc (2*num_points);
if (unlikely (!flags.alloc (num_points))) return false;
if (unlikely (!x_coords.alloc (2*num_points))) return false;
if (unlikely (!y_coords.alloc (2*num_points))) return false;
uint8_t lastflag = 0, repeat = 0;
int prev_x = 0.f, prev_y = 0.f;
for (unsigned i = 0; i < num_points; i++)
{
uint8_t flag = all_points[i].flag;
flag &= FLAG_ON_CURVE + FLAG_OVERLAP_SIMPLE;
encode_coord (deltas[i].first, flag, FLAG_X_SHORT, FLAG_X_SAME, x_coords);
encode_coord (deltas[i].second, flag, FLAG_Y_SHORT, FLAG_Y_SAME, y_coords);
float cur_x = roundf (all_points[i].x);
float cur_y = roundf (all_points[i].y);
encode_coord (cur_x - prev_x, flag, FLAG_X_SHORT, FLAG_X_SAME, x_coords);
encode_coord (cur_y - prev_y, flag, FLAG_Y_SHORT, FLAG_Y_SAME, y_coords);
if (i == 0) lastflag = flag + 1; //make lastflag != flag for the first point
encode_flag (flag, repeat, lastflag, flags);
prev_x = cur_x;
prev_y = cur_y;
}
unsigned len_before_instrs = 2 * header.numberOfContours + 2;

View File

@ -14,7 +14,6 @@ namespace glyf_impl {
struct SubsetGlyph
{
hb_codepoint_t new_gid;
hb_codepoint_t old_gid;
Glyph source_glyph;
hb_bytes_t dest_start; /* region of source_glyph to copy first */

View File

@ -72,10 +72,13 @@ struct 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);
_populate_subset_glyphs (c->plan, glyphs);
if (!c->plan->pinned_at_default)
_compile_subset_glyphs_with_deltas (c->plan, &glyphs);
{
if (!_compile_subset_glyphs_with_deltas (c->plan, &glyphs))
return_trace (false);
}
auto padded_offsets =
+ hb_iter (glyphs)
@ -105,9 +108,9 @@ struct glyf
void
_populate_subset_glyphs (const hb_subset_plan_t *plan,
hb_vector_t<glyf_impl::SubsetGlyph> *glyphs /* OUT */) const;
hb_vector_t<glyf_impl::SubsetGlyph> &glyphs /* OUT */) const;
void
bool
_compile_subset_glyphs_with_deltas (const hb_subset_plan_t *plan,
hb_vector_t<glyf_impl::SubsetGlyph> *glyphs /* OUT */) const;
@ -180,7 +183,7 @@ struct glyf_accelerator_t
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, phantom_only)))
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 ())
@ -374,44 +377,43 @@ struct glyf_accelerator_t
inline void
glyf::_populate_subset_glyphs (const hb_subset_plan_t *plan,
hb_vector_t<glyf_impl::SubsetGlyph> *glyphs /* OUT */) const
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;
+ hb_range (plan->num_output_glyphs ())
| hb_map ([&] (hb_codepoint_t new_gid)
for (auto p : plan->glyph_map->iter ())
{
glyf_impl::SubsetGlyph subset_glyph = {0};
subset_glyph.new_gid = new_gid;
unsigned new_gid = p.second;
glyf_impl::SubsetGlyph& subset_glyph = glyphs.arrayZ[new_gid];
subset_glyph.old_gid = p.first;
/* should never fail: all old gids should be mapped */
if (!plan->old_gid_for_new_gid (new_gid, &subset_glyph.old_gid))
return subset_glyph;
if (new_gid == 0 &&
!(plan->flags & HB_SUBSET_FLAGS_NOTDEF_OUTLINE))
if (unlikely (new_gid == 0 &&
!(plan->flags & HB_SUBSET_FLAGS_NOTDEF_OUTLINE)) &&
plan->pinned_at_default)
subset_glyph.source_glyph = glyf_impl::Glyph ();
else
subset_glyph.source_glyph = glyf.glyph_for_gid (subset_glyph.old_gid, true);
if (plan->flags & HB_SUBSET_FLAGS_NO_HINTING)
subset_glyph.drop_hints_bytes ();
else
subset_glyph.dest_start = subset_glyph.source_glyph.get_bytes ();
return subset_glyph;
})
| hb_sink (glyphs)
;
}
}
inline void
inline bool
glyf::_compile_subset_glyphs_with_deltas (const hb_subset_plan_t *plan,
hb_vector_t<glyf_impl::SubsetGlyph> *glyphs /* OUT */) const
{
OT::glyf_accelerator_t glyf (plan->source);
hb_font_t *font = hb_font_create (plan->source);
if (unlikely (!font)) return false;
hb_vector_t<hb_variation_t> vars;
vars.alloc (plan->user_axes_location->get_population ());
if (unlikely (!vars.alloc (plan->user_axes_location->get_population ())))
return false;
for (auto _ : *plan->user_axes_location)
{
@ -423,9 +425,16 @@ glyf::_compile_subset_glyphs_with_deltas (const hb_subset_plan_t *plan,
hb_font_set_variations (font, vars.arrayZ, plan->user_axes_location->get_population ());
for (auto& subset_glyph : *glyphs)
const_cast<glyf_impl::SubsetGlyph &> (subset_glyph).compile_bytes_with_deltas (plan, font, glyf);
{
if (!const_cast<glyf_impl::SubsetGlyph &> (subset_glyph).compile_bytes_with_deltas (plan, font, glyf))
{
hb_font_destroy (font);
return false;
}
}
hb_font_destroy (font);
return true;
}

View File

@ -31,7 +31,7 @@ for soname in ['harfbuzz', 'harfbuzz-subset', 'harfbuzz-icu', 'harfbuzz-gobject'
symprefix = '_' if suffix == 'dylib' else ''
EXPORTED_SYMBOLS = [s.split ()[2]
for s in re.findall (r'^.+ [BCDGIRST] .+$', subprocess.check_output (nm.split() + [so]).decode ('utf-8'), re.MULTILINE)
for s in re.findall (r'^.+ [BCDGIRSTu] .+$', subprocess.check_output (nm.split() + [so]).decode ('utf-8'), re.MULTILINE)
if not re.match (r'.* %s(%s)\b' % (symprefix, IGNORED_SYMBOLS), s)]
# run again c++filt also if is available

View File

@ -21,7 +21,9 @@ if '--experimental-api' not in sys.argv:
experimental_symbols = \
"""hb_subset_repack_or_fail
hb_subset_input_pin_axis_location
hb_subset_input_pin_axis_to_default""".splitlines ()
hb_subset_input_pin_axis_to_default
hb_subset_preprocess
""".splitlines ()
symbols = [x for x in symbols if x not in experimental_symbols]
symbols = "\n".join (symbols)

View File

@ -134,6 +134,7 @@ property_names = [
'Number_Joiner',
'Number',
'Brahmi_Joining_Number',
'Symbol_Modifier',
'Hieroglyph',
'Hieroglyph_Joiner',
'Hieroglyph_Segment_Begin',
@ -214,8 +215,7 @@ def is_CONS_MED(U, UISC, UDI, UGC, AJT):
return (UISC == Consonant_Medial and UGC != Lo or
UISC == Consonant_Initial_Postfixed)
def is_CONS_MOD(U, UISC, UDI, UGC, AJT):
return (UISC in [Nukta, Gemination_Mark, Consonant_Killer] and
not is_SYM_MOD(U, UISC, UDI, UGC, AJT))
return UISC in [Nukta, Gemination_Mark, Consonant_Killer]
def is_CONS_SUB(U, UISC, UDI, UGC, AJT):
return UISC == Consonant_Subjoined and UGC != Lo
def is_CONS_WITH_STACKER(U, UISC, UDI, UGC, AJT):
@ -257,7 +257,7 @@ def is_SAKOT(U, UISC, UDI, UGC, AJT):
# Split off of HALANT
return U == 0x1A60
def is_SYM_MOD(U, UISC, UDI, UGC, AJT):
return U in [0x1B6B, 0x1B6C, 0x1B6D, 0x1B6E, 0x1B6F, 0x1B70, 0x1B71, 0x1B72, 0x1B73]
return UISC == Symbol_Modifier
def is_VOWEL(U, UISC, UDI, UGC, AJT):
return (UISC == Pure_Killer or
UGC != Lo and UISC in [Vowel, Vowel_Dependent])
@ -359,9 +359,6 @@ def map_to_use(data):
# TODO: These don't have UISC assigned in Unicode 13.0.0, but have UIPC
if 0x0F18 <= U <= 0x0F19 or 0x0F3E <= U <= 0x0F3F: UISC = Vowel_Dependent
# TODO: https://github.com/harfbuzz/harfbuzz/pull/627
if 0x1BF2 <= U <= 0x1BF3: UISC = Nukta; UIPC = Bottom
# TODO: U+1CED should only be allowed after some of
# the nasalization marks, maybe only for U+1CE9..U+1CF1.
if U == 0x1CED: UISC = Tone_Mark
@ -372,23 +369,9 @@ def map_to_use(data):
# Resolve Indic_Positional_Category
# TODO: These should die, but have UIPC in Unicode 13.0.0
if U in [0x953, 0x954]: UIPC = Not_Applicable
# TODO: These are not in USE's override list that we have, nor are they in Unicode 13.0.0
if 0xA926 <= U <= 0xA92A: UIPC = Top
# TODO: https://github.com/harfbuzz/harfbuzz/pull/1037
# and https://github.com/harfbuzz/harfbuzz/issues/1631
if U in [0x11302, 0x11303, 0x114C1]: UIPC = Top
if 0x1CF8 <= U <= 0x1CF9: UIPC = Top
# TODO: https://github.com/harfbuzz/harfbuzz/issues/3550
if U == 0x10A38: UIPC = Bottom
# TODO: https://github.com/harfbuzz/harfbuzz/pull/982
# also https://github.com/harfbuzz/harfbuzz/issues/1012
if 0x1112A <= U <= 0x1112B: UIPC = Top
if 0x11131 <= U <= 0x11132: UIPC = Top
assert (UIPC in [Not_Applicable, Visual_Order_Left] or U == 0x0F7F or
USE in use_positions), "%s %s %s %s %s %s %s" % (hex(U), UIPC, USE, UISC, UDI, UGC, AJT)

View File

@ -70,8 +70,8 @@ struct graph_t
{
DEBUG_MSG (SUBSET_REPACK, nullptr,
"vertex [%lu] bytes != [%lu] bytes, depth = %u",
table_size (),
other.table_size (),
(unsigned long) table_size (),
(unsigned long) other.table_size (),
depth);
auto a = as_bytes ();
@ -495,6 +495,12 @@ struct graph_t
return as_table_from_index<T> (index_for_offset (parent, offset), std::forward<Ts>(ds)...);
}
template <typename T, typename ...Ts>
vertex_and_table_t<T> as_mutable_table (unsigned parent, const void* offset, Ts... ds)
{
return as_table_from_index<T> (mutable_index_for_offset (parent, offset), std::forward<Ts>(ds)...);
}
template <typename T, typename ...Ts>
vertex_and_table_t<T> as_table_from_index (unsigned index, Ts... ds)
{
@ -574,7 +580,7 @@ struct graph_t
while (roots)
{
unsigned next = HB_SET_VALUE_INVALID;
uint32_t next = HB_SET_VALUE_INVALID;
if (unlikely (!check_success (!roots.in_error ()))) break;
if (!roots.next (&next)) break;
@ -655,8 +661,8 @@ struct graph_t
auto new_subgraph =
+ subgraph.keys ()
| hb_map([&] (unsigned node_idx) {
const unsigned *v;
| hb_map([&] (uint32_t node_idx) {
const uint32_t *v;
if (index_map.has (node_idx, &v)) return *v;
return node_idx;
})
@ -666,10 +672,10 @@ struct graph_t
remap_obj_indices (index_map, parents.iter (), true);
// Update roots set with new indices as needed.
unsigned next = HB_SET_VALUE_INVALID;
uint32_t next = HB_SET_VALUE_INVALID;
while (roots.next (&next))
{
const unsigned *v;
const uint32_t *v;
if (index_map.has (next, &v))
{
roots.del (next);
@ -684,7 +690,7 @@ struct graph_t
{
for (const auto& link : vertices_[node_idx].obj.all_links ())
{
const unsigned *v;
const uint32_t *v;
if (subgraph.has (link.objidx, &v))
{
subgraph.set (link.objidx, *v + 1);
@ -833,7 +839,20 @@ struct graph_t
* parent to the clone. The copy is a shallow copy, objects
* linked from child are not duplicated.
*/
bool duplicate (unsigned parent_idx, unsigned child_idx)
unsigned duplicate_if_shared (unsigned parent_idx, unsigned child_idx)
{
unsigned new_idx = duplicate (parent_idx, child_idx);
if (new_idx == (unsigned) -1) return child_idx;
return new_idx;
}
/*
* Creates a copy of child and re-assigns the link from
* parent to the clone. The copy is a shallow copy, objects
* linked from child are not duplicated.
*/
unsigned duplicate (unsigned parent_idx, unsigned child_idx)
{
update_parents ();
@ -849,7 +868,7 @@ struct graph_t
// to child are from parent.
DEBUG_MSG (SUBSET_REPACK, nullptr, " Not duplicating %d => %d",
parent_idx, child_idx);
return false;
return -1;
}
DEBUG_MSG (SUBSET_REPACK, nullptr, " Duplicating %d => %d",
@ -869,7 +888,7 @@ struct graph_t
reassign_link (l, parent_idx, clone_idx);
}
return true;
return clone_idx;
}
@ -1164,7 +1183,7 @@ struct graph_t
{
for (auto& link : vertices_[i].obj.all_links_writer ())
{
const unsigned *v;
const uint32_t *v;
if (!id_map.has (link.objidx, &v)) continue;
if (only_wide && !(link.width == 4 && !link.is_signed)) continue;

View File

@ -131,8 +131,10 @@ struct Lookup : public OT::Lookup
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;
@ -150,9 +152,9 @@ struct Lookup : public OT::Lookup
switch (type)
{
case 2:
new_sub_tables = split_subtable<PairPos> (c, subtable_index); break;
new_sub_tables = split_subtable<PairPos> (c, parent_index, subtable_index); break;
case 4:
new_sub_tables = split_subtable<MarkBasePos> (c, subtable_index); break;
new_sub_tables = split_subtable<MarkBasePos> (c, parent_index, subtable_index); break;
default:
break;
}
@ -172,13 +174,14 @@ struct Lookup : public OT::Lookup
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, objidx);
return sub_table->split_subtables (c, parent_idx, objidx);
}
void add_sub_tables (gsubgpos_graph_context_t& c,

View File

@ -209,7 +209,9 @@ struct MarkBasePosFormat1 : public OT::Layout::GPOS_impl::MarkBasePosFormat1_2<S
return vertex_len >= MarkBasePosFormat1::static_size;
}
hb_vector_t<unsigned> split_subtables (gsubgpos_graph_context_t& c, unsigned this_index)
hb_vector_t<unsigned> split_subtables (gsubgpos_graph_context_t& c,
unsigned parent_index,
unsigned this_index)
{
hb_set_t visited;
@ -261,7 +263,7 @@ struct MarkBasePosFormat1 : public OT::Layout::GPOS_impl::MarkBasePosFormat1_2<S
split_context_t split_context {
c,
this,
this_index,
c.graph.duplicate_if_shared (parent_index, this_index),
std::move (class_to_info),
c.graph.vertices_[mark_array_id].position_to_index_map (),
};
@ -365,7 +367,7 @@ struct MarkBasePosFormat1 : public OT::Layout::GPOS_impl::MarkBasePosFormat1_2<S
classCount = count;
auto mark_coverage = sc.c.graph.as_table<Coverage> (this_index,
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);
@ -380,7 +382,7 @@ struct MarkBasePosFormat1 : public OT::Layout::GPOS_impl::MarkBasePosFormat1_2<S
return false;
auto base_array = sc.c.graph.as_table<AnchorMatrix> (this_index,
auto base_array = sc.c.graph.as_mutable_table<AnchorMatrix> (this_index,
&baseArray,
old_count);
if (!base_array || !base_array.table->shrink (sc.c,
@ -389,7 +391,7 @@ struct MarkBasePosFormat1 : public OT::Layout::GPOS_impl::MarkBasePosFormat1_2<S
count))
return false;
auto mark_array = sc.c.graph.as_table<MarkArray> (this_index,
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,
@ -469,11 +471,12 @@ struct MarkBasePosFormat1 : public OT::Layout::GPOS_impl::MarkBasePosFormat1_2<S
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, this_index);
return ((MarkBasePosFormat1*)(&u.format1))->split_subtables (c, parent_index, this_index);
#ifndef HB_NO_BORING_EXPANSION
case 2: HB_FALLTHROUGH;
// Don't split 24bit PairPos's.

View File

@ -47,7 +47,9 @@ struct PairPosFormat1 : public OT::Layout::GPOS_impl::PairPosFormat1_3<SmallType
min_size + pairSet.get_size () - pairSet.len.get_size();
}
hb_vector_t<unsigned> split_subtables (gsubgpos_graph_context_t& c, unsigned this_index)
hb_vector_t<unsigned> split_subtables (gsubgpos_graph_context_t& c,
unsigned parent_index,
unsigned this_index)
{
hb_set_t visited;
@ -81,7 +83,7 @@ struct PairPosFormat1 : public OT::Layout::GPOS_impl::PairPosFormat1_3<SmallType
split_context_t split_context {
c,
this,
this_index,
c.graph.duplicate_if_shared (parent_index, this_index),
};
return actuate_subtable_split<split_context_t> (split_context, split_points);
@ -125,23 +127,19 @@ struct PairPosFormat1 : public OT::Layout::GPOS_impl::PairPosFormat1_3<SmallType
pairSet.len = count;
c.graph.vertices_[this_index].obj.tail -= (old_count - count) * SmallTypes::size;
unsigned coverage_id = c.graph.mutable_index_for_offset (this_index, &coverage);
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 false;
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_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_id, coverage_size);
return Coverage::make_coverage (c, new_coverage, coverage.index, coverage_size);
}
// Create a new PairPos including PairSet's from start (inclusive) to end (exclusive).
@ -206,7 +204,9 @@ struct PairPosFormat2 : public OT::Layout::GPOS_impl::PairPosFormat2_4<SmallType
min_size + class1_count * get_class1_record_size ();
}
hb_vector_t<unsigned> split_subtables (gsubgpos_graph_context_t& c, unsigned this_index)
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);
@ -287,7 +287,7 @@ struct PairPosFormat2 : public OT::Layout::GPOS_impl::PairPosFormat2_4<SmallType
split_context_t split_context {
c,
this,
this_index,
c.graph.duplicate_if_shared (parent_index, this_index),
class1_record_size,
total_value_len,
value_1_len,
@ -508,40 +508,37 @@ struct PairPosFormat2 : public OT::Layout::GPOS_impl::PairPosFormat2_4<SmallType
graph.vertices_[split_context.this_index].obj.tail -=
(old_count - count) * split_context.class1_record_size;
unsigned coverage_id =
graph.mutable_index_for_offset (split_context.this_index, &coverage);
unsigned class_def_1_id =
graph.mutable_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 false;
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 ()
+ 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));
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,
+ klass_map | hb_map_retains_sorting (hb_first),
coverage_id,
coverage_v.table_size ()))
+ 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_id,
class_def_1_v.table_size ());
class_def_1.index,
class_def_1.vertex->table_size ());
}
hb_hashmap_t<unsigned, unsigned>
@ -605,13 +602,15 @@ struct PairPosFormat2 : public OT::Layout::GPOS_impl::PairPosFormat2_4<SmallType
struct PairPos : public OT::Layout::GPOS_impl::PairPos
{
hb_vector_t<unsigned> split_subtables (gsubgpos_graph_context_t& c, unsigned this_index)
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, this_index);
return ((PairPosFormat1*)(&u.format1))->split_subtables (c, parent_index, this_index);
case 2:
return ((PairPosFormat2*)(&u.format2))->split_subtables (c, this_index);
return ((PairPosFormat2*)(&u.format2))->split_subtables (c, parent_index, this_index);
#ifndef HB_NO_BORING_EXPANSION
case 3: HB_FALLTHROUGH;
case 4: HB_FALLTHROUGH;

View File

@ -1,29 +1,5 @@
# Set these variables so that the `${prefix}/lib` expands to something we can
# remove.
set(_harfbuzz_remove_string "REMOVE_ME")
set(exec_prefix "${_harfbuzz_remove_string}")
set(prefix "${_harfbuzz_remove_string}")
# Compute the installation prefix by stripping components from our current
# location.
get_filename_component(_harfbuzz_prefix "${CMAKE_CURRENT_LIST_DIR}" DIRECTORY)
get_filename_component(_harfbuzz_prefix "${_harfbuzz_prefix}" DIRECTORY)
set(_harfbuzz_libdir "@libdir@")
string(REPLACE "${_harfbuzz_remove_string}/" "" _harfbuzz_libdir "${_harfbuzz_libdir}")
set(_harfbuzz_libdir_iter "${_harfbuzz_libdir}")
while (_harfbuzz_libdir_iter)
set(_harfbuzz_libdir_prev_iter "${_harfbuzz_libdir_iter}")
get_filename_component(_harfbuzz_libdir_iter "${_harfbuzz_libdir_iter}" DIRECTORY)
if (_harfbuzz_libdir_prev_iter STREQUAL _harfbuzz_libdir_iter)
break()
endif ()
get_filename_component(_harfbuzz_prefix "${_harfbuzz_prefix}" DIRECTORY)
endwhile ()
unset(_harfbuzz_libdir_iter)
# Get the include subdir.
set(_harfbuzz_includedir "@includedir@")
string(REPLACE "${_harfbuzz_remove_string}/" "" _harfbuzz_includedir "${_harfbuzz_includedir}")
# Extract version information from libtool.
set(_harfbuzz_version_info "@HB_LIBTOOL_VERSION_INFO@")
@ -48,29 +24,29 @@ endif ()
# Add the libraries.
add_library(harfbuzz::harfbuzz SHARED IMPORTED)
set_target_properties(harfbuzz::harfbuzz PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${_harfbuzz_prefix}/${_harfbuzz_includedir}/harfbuzz"
IMPORTED_LOCATION "${_harfbuzz_prefix}/${_harfbuzz_libdir}/libharfbuzz${_harfbuzz_lib_suffix}")
INTERFACE_INCLUDE_DIRECTORIES "${_harfbuzz_includedir}/harfbuzz"
IMPORTED_LOCATION "${_harfbuzz_libdir}/libharfbuzz${_harfbuzz_lib_suffix}")
add_library(harfbuzz::icu SHARED IMPORTED)
set_target_properties(harfbuzz::icu PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${_harfbuzz_prefix}/${_harfbuzz_includedir}/harfbuzz"
INTERFACE_INCLUDE_DIRECTORIES "${_harfbuzz_includedir}/harfbuzz"
INTERFACE_LINK_LIBRARIES "harfbuzz::harfbuzz"
IMPORTED_LOCATION "${_harfbuzz_prefix}/${_harfbuzz_libdir}/libharfbuzz-icu${_harfbuzz_lib_suffix}")
IMPORTED_LOCATION "${_harfbuzz_libdir}/libharfbuzz-icu${_harfbuzz_lib_suffix}")
add_library(harfbuzz::subset SHARED IMPORTED)
set_target_properties(harfbuzz::subset PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${_harfbuzz_prefix}/${_harfbuzz_includedir}/harfbuzz"
INTERFACE_INCLUDE_DIRECTORIES "${_harfbuzz_includedir}/harfbuzz"
INTERFACE_LINK_LIBRARIES "harfbuzz::harfbuzz"
IMPORTED_LOCATION "${_harfbuzz_prefix}/${_harfbuzz_libdir}/libharfbuzz-subset${_harfbuzz_lib_suffix}")
IMPORTED_LOCATION "${_harfbuzz_libdir}/libharfbuzz-subset${_harfbuzz_lib_suffix}")
# Only add the gobject library if it was built.
set(_harfbuzz_have_gobject "@have_gobject@")
if (_harfbuzz_have_gobject)
add_library(harfbuzz::gobject SHARED IMPORTED)
set_target_properties(harfbuzz::gobject PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${_harfbuzz_prefix}/${_harfbuzz_includedir}/harfbuzz"
INTERFACE_INCLUDE_DIRECTORIES "${_harfbuzz_includedir}/harfbuzz"
INTERFACE_LINK_LIBRARIES "harfbuzz::harfbuzz"
IMPORTED_LOCATION "${_harfbuzz_prefix}/${_harfbuzz_libdir}/libharfbuzz-gobject${_harfbuzz_lib_suffix}")
IMPORTED_LOCATION "${_harfbuzz_libdir}/libharfbuzz-gobject${_harfbuzz_lib_suffix}")
endif ()
# Clean out variables we used in our scope.
@ -80,7 +56,3 @@ unset(_harfbuzz_revision)
unset(_harfbuzz_age)
unset(_harfbuzz_includedir)
unset(_harfbuzz_libdir)
unset(_harfbuzz_prefix)
unset(exec_prefix)
unset(prefix)
unset(_harfbuzz_remove_string)

View File

@ -70,9 +70,9 @@ struct DecompositionAction
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
@ -118,7 +118,7 @@ struct ConditionalAddGlyphAction
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
@ -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);
@ -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. */

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},

View File

@ -495,8 +495,8 @@ hb_language_matches (hb_language_t language,
* @HB_SCRIPT_TOTO: `Toto`, Since: 3.0.0
* @HB_SCRIPT_VITHKUQI: `Vith`, Since: 3.0.0
* @HB_SCRIPT_MATH: `Zmth`, Since: 3.4.0
* @HB_SCRIPT_KAWI: `Kawi`, Since: REPLACEME
* @HB_SCRIPT_NAG_MUNDARI: `Nagm`, Since: REPLACEME
* @HB_SCRIPT_KAWI: `Kawi`, Since: 5.2.0
* @HB_SCRIPT_NAG_MUNDARI: `Nagm`, Since: 5.2.0
* @HB_SCRIPT_INVALID: No script set
*
* Data type for scripts. Each #hb_script_t's value is an #hb_tag_t corresponding
@ -719,7 +719,7 @@ typedef enum
HB_SCRIPT_MATH = HB_TAG ('Z','m','t','h'),
/*
* Since REPLACEME
* Since 5.2.0
*/
HB_SCRIPT_KAWI = HB_TAG ('K','a','w','i'), /*15.0*/
HB_SCRIPT_NAG_MUNDARI = HB_TAG ('N','a','g','m'), /*15.0*/

View File

@ -71,6 +71,7 @@
#define HB_NO_LANGUAGE_PRIVATE_SUBTAG
#define HB_NO_LAYOUT_FEATURE_PARAMS
#define HB_NO_LAYOUT_COLLECT_GLYPHS
#define HB_NO_LAYOUT_RARELY_USED
#define HB_NO_LAYOUT_UNUSED
#define HB_NO_MATH
#define HB_NO_META

View File

@ -69,9 +69,9 @@ struct shared_ptr
operator T * () const { return p; }
T& operator * () const { return *get (); }
T* operator -> () const { return get (); }
operator bool () { return p; }
bool operator == (const shared_ptr &o) { return p == o.p; }
bool operator != (const shared_ptr &o) { return p != o.p; }
operator bool () const { return p; }
bool operator == (const shared_ptr &o) const { return p == o.p; }
bool operator != (const shared_ptr &o) const { return p != o.p; }
static T* get_empty() { return v::get_empty (); }
T* reference() { return v::reference (p); }

View File

@ -633,20 +633,29 @@ hb_face_collect_variation_unicodes (hb_face_t *face,
* face-builder: A face that has add_table().
*/
struct face_table_info_t
{
hb_blob_t* data;
unsigned order;
};
struct hb_face_builder_data_t
{
hb_hashmap_t<hb_tag_t, hb_blob_t *> tables;
hb_hashmap_t<hb_tag_t, face_table_info_t> tables;
};
static int compare_entries (const void* pa, const void* pb)
{
const auto& a = * (const hb_pair_t<hb_tag_t, hb_blob_t*> *) pa;
const auto& b = * (const hb_pair_t<hb_tag_t, hb_blob_t*> *) pb;
const auto& a = * (const hb_pair_t<hb_tag_t, face_table_info_t> *) pa;
const auto& b = * (const hb_pair_t<hb_tag_t, face_table_info_t> *) pb;
/* Order by blob size first (smallest to largest) and then table tag */
if (a.second->length != b.second->length)
return a.second->length < b.second->length ? -1 : +1;
if (a.second.order != b.second.order)
return a.second.order < b.second.order ? -1 : +1;
if (a.second.data->length != b.second.data->length)
return a.second.data->length < b.second.data->length ? -1 : +1;
return a.first < b.first ? -1 : a.first == b.first ? 0 : +1;
}
@ -668,8 +677,8 @@ _hb_face_builder_data_destroy (void *user_data)
{
hb_face_builder_data_t *data = (hb_face_builder_data_t *) user_data;
for (hb_blob_t* b : data->tables.values())
hb_blob_destroy (b);
for (auto info : data->tables.values())
hb_blob_destroy (info.data);
data->tables.fini ();
@ -683,8 +692,8 @@ _hb_face_builder_data_reference_blob (hb_face_builder_data_t *data)
unsigned int table_count = data->tables.get_population ();
unsigned int face_length = table_count * 16 + 12;
for (hb_blob_t* b : data->tables.values())
face_length += hb_ceil_to_4 (hb_blob_get_length (b));
for (auto info : data->tables.values())
face_length += hb_ceil_to_4 (hb_blob_get_length (info.data));
char *buf = (char *) hb_malloc (face_length);
if (unlikely (!buf))
@ -699,7 +708,7 @@ _hb_face_builder_data_reference_blob (hb_face_builder_data_t *data)
hb_tag_t sfnt_tag = is_cff ? OT::OpenTypeFontFile::CFFTag : OT::OpenTypeFontFile::TrueTypeTag;
// Sort the tags so that produced face is deterministic.
hb_vector_t<hb_pair_t <hb_tag_t, hb_blob_t*>> sorted_entries;
hb_vector_t<hb_pair_t <hb_tag_t, face_table_info_t>> sorted_entries;
data->tables.iter () | hb_sink (sorted_entries);
if (unlikely (sorted_entries.in_error ()))
{
@ -708,7 +717,13 @@ _hb_face_builder_data_reference_blob (hb_face_builder_data_t *data)
}
sorted_entries.qsort (compare_entries);
bool ret = f->serialize_single (&c, sfnt_tag, + sorted_entries.iter());
bool ret = f->serialize_single (&c,
sfnt_tag,
+ sorted_entries.iter()
| hb_map ([&] (hb_pair_t<hb_tag_t, face_table_info_t> _) {
return hb_pair_t<hb_tag_t, hb_blob_t*> (_.first, _.second.data);
}));
c.end_serialize ();
@ -729,7 +744,7 @@ _hb_face_builder_reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void
if (!tag)
return _hb_face_builder_data_reference_blob (data);
return hb_blob_reference (data->tables[tag]);
return hb_blob_reference (data->tables[tag].data);
}
@ -777,8 +792,8 @@ hb_face_builder_add_table (hb_face_t *face, hb_tag_t tag, hb_blob_t *blob)
hb_face_builder_data_t *data = (hb_face_builder_data_t *) face->user_data;
hb_blob_t* previous = data->tables.get (tag);
if (!data->tables.set (tag, hb_blob_reference (blob)))
hb_blob_t* previous = data->tables.get (tag).data;
if (!data->tables.set (tag, face_table_info_t {hb_blob_reference (blob), 0}))
{
hb_blob_destroy (blob);
return false;
@ -787,3 +802,36 @@ hb_face_builder_add_table (hb_face_t *face, hb_tag_t tag, hb_blob_t *blob)
hb_blob_destroy (previous);
return true;
}
/**
* hb_face_builder_sort_tables:
* @face: A face object created with hb_face_builder_create()
* @tags: (array zero-terminated=1): ordered list of table tags terminated by
* %HB_TAG_NONE
*
* Set the ordering of tables for serialization. Any tables not
* specified in the tags list will be ordered after the tables in
* tags, ordered by the default sort ordering.
*
* Since: 5.3.0
**/
void
hb_face_builder_sort_tables (hb_face_t *face,
const hb_tag_t *tags)
{
hb_face_builder_data_t *data = (hb_face_builder_data_t *) face->user_data;
// Sort all unspecified tables after any specified tables.
for (auto& info : data->tables.values_ref())
info.order = -1;
unsigned order = 0;
for (const hb_tag_t* tag = tags;
*tag;
tag++)
{
face_table_info_t* info;
if (!data->tables.has (*tag, &info)) continue;
info->order = order++;
}
}

View File

@ -171,6 +171,10 @@ hb_face_builder_add_table (hb_face_t *face,
hb_tag_t tag,
hb_blob_t *blob);
HB_EXTERN void
hb_face_builder_sort_tables (hb_face_t *face,
const hb_tag_t *tags);
HB_END_DECLS

View File

@ -133,6 +133,18 @@ struct
template <typename T> constexpr auto
operator () (T *v) const HB_AUTO_RETURN (*v)
template <typename T> constexpr auto
operator () (const hb::shared_ptr<T>& v) const HB_AUTO_RETURN (*v)
template <typename T> constexpr auto
operator () (hb::shared_ptr<T>& v) const HB_AUTO_RETURN (*v)
template <typename T> constexpr auto
operator () (const hb::unique_ptr<T>& v) const HB_AUTO_RETURN (*v)
template <typename T> constexpr auto
operator () (hb::unique_ptr<T>& v) const HB_AUTO_RETURN (*v)
}
HB_FUNCOBJ (hb_deref);

View File

@ -141,27 +141,24 @@ typedef HBINT32 FWORD32;
/* 16-bit unsigned integer (HBUINT16) that describes a quantity in FUnits. */
typedef HBUINT16 UFWORD;
/* 16-bit signed fixed number with the low 14 bits of fraction (2.14). */
struct F2DOT14 : HBINT16
template <typename Type, unsigned fraction_bits>
struct HBFixed : Type
{
F2DOT14& operator = (uint16_t i ) { HBINT16::operator= (i); return *this; }
// 16384 means 1<<14
float to_float () const { return ((int32_t) v) / 16384.f; }
void set_float (float f) { v = roundf (f * 16384.f); }
static constexpr float shift = (float) (1 << fraction_bits);
static_assert (Type::static_size * 8 > fraction_bits, "");
HBFixed& operator = (typename Type::type i ) { Type::operator= (i); return *this; }
float to_float () const { return ((int32_t) Type::v) / shift; }
void set_float (float f) { Type::v = roundf (f * shift); }
public:
DEFINE_SIZE_STATIC (2);
DEFINE_SIZE_STATIC (Type::static_size);
};
/* 16-bit signed fixed number with the low 14 bits of fraction (2.14). */
using F2DOT14 = HBFixed<HBINT16, 14>;
/* 32-bit signed fixed-point number (16.16). */
struct HBFixed : HBINT32
{
HBFixed& operator = (uint32_t i) { HBINT32::operator= (i); return *this; }
// 65536 means 1<<16
float to_float () const { return ((int32_t) v) / 65536.f; }
void set_float (float f) { v = roundf (f * 65536.f); }
public:
DEFINE_SIZE_STATIC (4);
};
using F16DOT16 = HBFixed<HBINT32, 16>;
/* Date represented in number of seconds since 12:00 midnight, January 1,
* 1904. The value is represented as a signed 64-bit integer. */

View File

@ -358,14 +358,14 @@ struct Affine2x3
return_trace (c->check_struct (this));
}
HBFixed xx;
HBFixed yx;
HBFixed xy;
HBFixed yy;
HBFixed dx;
HBFixed dy;
F16DOT16 xx;
F16DOT16 yx;
F16DOT16 xy;
F16DOT16 yy;
F16DOT16 dx;
F16DOT16 dy;
public:
DEFINE_SIZE_STATIC (6 * HBFixed::static_size);
DEFINE_SIZE_STATIC (6 * F16DOT16::static_size);
};
struct PaintColrLayers

View File

@ -295,8 +295,8 @@ hb_ot_color_has_png (hb_face_t *face)
* @glyph: a glyph index
*
* Fetches the PNG image for a glyph. This function takes a font object, not a face object,
* as input. To get an optimally sized PNG blob, the UPEM value must be set on the @font
* object. If UPEM is unset, the blob returned will be the largest PNG available.
* as input. To get an optimally sized PNG blob, the PPEM values must be set on the @font
* object. If PPEM is unset, the blob returned will be the largest PNG available.
*
* If the glyph has no PNG image, the singleton empty blob is returned.
*

View File

@ -40,7 +40,6 @@
#include "hb-ot-cff1-table.hh"
#include "hb-ot-cff2-table.hh"
#include "hb-ot-hmtx-table.hh"
#include "hb-ot-os2-table.hh"
#include "hb-ot-post-table.hh"
#include "hb-ot-stat-table.hh" // Just so we compile it; unused otherwise.
#include "hb-ot-vorg-table.hh"
@ -349,15 +348,13 @@ hb_ot_get_glyph_extents (hb_font_t *font,
#if !defined(HB_NO_OT_FONT_BITMAP) && !defined(HB_NO_COLOR)
if (ot_face->sbix->get_extents (font, glyph, extents)) return true;
if (ot_face->CBDT->get_extents (font, glyph, extents)) return true;
#endif
if (ot_face->glyf->get_extents (font, glyph, extents)) return true;
#ifndef HB_NO_OT_FONT_CFF
if (ot_face->cff1->get_extents (font, glyph, extents)) return true;
if (ot_face->cff2->get_extents (font, glyph, extents)) return true;
#endif
#if !defined(HB_NO_OT_FONT_BITMAP) && !defined(HB_NO_COLOR)
if (ot_face->CBDT->get_extents (font, glyph, extents)) return true;
#endif
// TODO Hook up side-bearings variations.
return false;

View File

@ -94,6 +94,19 @@ static bool ClassDef_remap_and_serialize (
hb_sorted_vector_t<hb_pair_t<hb_codepoint_t, hb_codepoint_t>> &glyph_and_klass, /* IN/OUT */
hb_map_t *klass_map /*IN/OUT*/);
struct hb_collect_feature_substitutes_with_var_context_t
{
const hb_map_t *axes_index_tag_map;
const hb_hashmap_t<hb_tag_t, int> *axes_location;
hb_hashmap_t<unsigned, hb::shared_ptr<hb_set_t>> *record_cond_idx_map;
hb_hashmap_t<unsigned, const Feature*> *feature_substitutes_map;
// not stored in subset_plan
hb_set_t *feature_indices;
bool apply;
unsigned cur_record_idx;
hb_hashmap_t<hb::shared_ptr<hb_map_t>, unsigned> *conditionset_map;
};
struct hb_prune_langsys_context_t
{
@ -160,24 +173,40 @@ struct hb_subset_layout_context_t :
const hb_map_t *lookup_index_map;
const hb_hashmap_t<unsigned, hb::unique_ptr<hb_set_t>> *script_langsys_map;
const hb_map_t *feature_index_map;
const hb_hashmap_t<unsigned, const Feature*> *feature_substitutes_map;
hb_hashmap_t<unsigned, hb::shared_ptr<hb_set_t>> *feature_record_cond_idx_map;
unsigned cur_script_index;
unsigned cur_feature_var_record_idx;
hb_subset_layout_context_t (hb_subset_context_t *c_,
hb_tag_t tag_,
hb_map_t *lookup_map_,
hb_hashmap_t<unsigned, hb::unique_ptr<hb_set_t>> *script_langsys_map_,
hb_map_t *feature_index_map_) :
hb_tag_t tag_) :
subset_context (c_),
table_tag (tag_),
lookup_index_map (lookup_map_),
script_langsys_map (script_langsys_map_),
feature_index_map (feature_index_map_),
cur_script_index (0xFFFFu),
cur_feature_var_record_idx (0u),
script_count (0),
langsys_count (0),
feature_index_count (0),
lookup_index_count (0)
{}
{
if (tag_ == HB_OT_TAG_GSUB)
{
lookup_index_map = c_->plan->gsub_lookups;
script_langsys_map = c_->plan->gsub_langsys;
feature_index_map = c_->plan->gsub_features;
feature_substitutes_map = c_->plan->gsub_feature_substitutes_map;
feature_record_cond_idx_map = c_->plan->user_axes_location->is_empty () ? nullptr : c_->plan->gsub_feature_record_cond_idx_map;
}
else
{
lookup_index_map = c_->plan->gpos_lookups;
script_langsys_map = c_->plan->gpos_langsys;
feature_index_map = c_->plan->gpos_features;
feature_substitutes_map = c_->plan->gpos_feature_substitutes_map;
feature_record_cond_idx_map = c_->plan->user_axes_location->is_empty () ? nullptr : c_->plan->gpos_feature_record_cond_idx_map;
}
}
private:
unsigned script_count;
@ -324,6 +353,31 @@ struct subset_record_array_t
const void *base;
};
template<typename OutputArray, typename Arg>
struct subset_record_array_arg_t
{
subset_record_array_arg_t (hb_subset_layout_context_t *c_, OutputArray* out_,
const void *base_,
Arg &&arg_) : subset_layout_context (c_),
out (out_), base (base_), arg (arg_) {}
template <typename T>
void
operator () (T&& record)
{
auto snap = subset_layout_context->subset_context->serializer->snapshot ();
bool ret = record.subset (subset_layout_context, base, arg);
if (!ret) subset_layout_context->subset_context->serializer->revert (snap);
else out->len++;
}
private:
hb_subset_layout_context_t *subset_layout_context;
OutputArray *out;
const void *base;
Arg &&arg;
};
/*
* Helper to subset a RecordList/record array. Subsets each Record in the array and
* discards the record if the subset operation returns false.
@ -335,6 +389,13 @@ struct
operator () (hb_subset_layout_context_t *c, OutputArray* out,
const void *base) const
{ return subset_record_array_t<OutputArray> (c, out, base); }
/* Variant with one extra argument passed to subset */
template<typename OutputArray, typename Arg>
subset_record_array_arg_t<OutputArray, Arg>
operator () (hb_subset_layout_context_t *c, OutputArray* out,
const void *base, Arg &&arg) const
{ return subset_record_array_arg_t<OutputArray, Arg> (c, out, base, arg); }
}
HB_FUNCOBJ (subset_record_array);
@ -431,94 +492,6 @@ struct IndexArray : Array16Of<Index>
};
struct Record_sanitize_closure_t {
hb_tag_t tag;
const void *list_base;
};
template <typename Type>
struct Record
{
int cmp (hb_tag_t a) const { return tag.cmp (a); }
bool subset (hb_subset_layout_context_t *c, const void *base) const
{
TRACE_SUBSET (this);
auto *out = c->subset_context->serializer->embed (this);
if (unlikely (!out)) return_trace (false);
bool ret = out->offset.serialize_subset (c->subset_context, offset, base, c, &tag);
return_trace (ret);
}
bool sanitize (hb_sanitize_context_t *c, const void *base) const
{
TRACE_SANITIZE (this);
const Record_sanitize_closure_t closure = {tag, base};
return_trace (c->check_struct (this) && offset.sanitize (c, base, &closure));
}
Tag tag; /* 4-byte Tag identifier */
Offset16To<Type>
offset; /* Offset from beginning of object holding
* the Record */
public:
DEFINE_SIZE_STATIC (6);
};
template <typename Type>
struct RecordArrayOf : SortedArray16Of<Record<Type>>
{
const Offset16To<Type>& get_offset (unsigned int i) const
{ return (*this)[i].offset; }
Offset16To<Type>& get_offset (unsigned int i)
{ return (*this)[i].offset; }
const Tag& get_tag (unsigned int i) const
{ return (*this)[i].tag; }
unsigned int get_tags (unsigned int start_offset,
unsigned int *record_count /* IN/OUT */,
hb_tag_t *record_tags /* OUT */) const
{
if (record_count)
{
+ this->sub_array (start_offset, record_count)
| hb_map (&Record<Type>::tag)
| hb_sink (hb_array (record_tags, *record_count))
;
}
return this->len;
}
bool find_index (hb_tag_t tag, unsigned int *index) const
{
return this->bfind (tag, index, HB_NOT_FOUND_STORE, Index::NOT_FOUND_INDEX);
}
};
template <typename Type>
struct RecordListOf : RecordArrayOf<Type>
{
const Type& operator [] (unsigned int i) const
{ return this+this->get_offset (i); }
bool subset (hb_subset_context_t *c,
hb_subset_layout_context_t *l) const
{
TRACE_SUBSET (this);
auto *out = c->serializer->start_embed (*this);
if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
+ this->iter ()
| hb_apply (subset_record_array (l, out, this))
;
return_trace (true);
}
bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
return_trace (RecordArrayOf<Type>::sanitize (c, this));
}
};
/* https://docs.microsoft.com/en-us/typography/opentype/spec/features_pt#size */
struct FeatureParamsSize
{
@ -801,6 +774,10 @@ struct FeatureParams
DEFINE_SIZE_MIN (0);
};
struct Record_sanitize_closure_t {
hb_tag_t tag;
const void *list_base;
};
struct Feature
{
@ -897,6 +874,103 @@ struct Feature
DEFINE_SIZE_ARRAY_SIZED (4, lookupIndex);
};
template <typename Type>
struct Record
{
int cmp (hb_tag_t a) const { return tag.cmp (a); }
bool subset (hb_subset_layout_context_t *c, const void *base, const void *f_sub = nullptr) const
{
TRACE_SUBSET (this);
auto *out = c->subset_context->serializer->embed (this);
if (unlikely (!out)) return_trace (false);
if (!f_sub)
return_trace (out->offset.serialize_subset (c->subset_context, offset, base, c, &tag));
const Feature& f = *reinterpret_cast<const Feature *> (f_sub);
auto *s = c->subset_context->serializer;
s->push ();
out->offset = 0;
bool ret = f.subset (c->subset_context, c, &tag);
if (ret)
s->add_link (out->offset, s->pop_pack ());
else
s->pop_discard ();
return_trace (ret);
}
bool sanitize (hb_sanitize_context_t *c, const void *base) const
{
TRACE_SANITIZE (this);
const Record_sanitize_closure_t closure = {tag, base};
return_trace (c->check_struct (this) && offset.sanitize (c, base, &closure));
}
Tag tag; /* 4-byte Tag identifier */
Offset16To<Type>
offset; /* Offset from beginning of object holding
* the Record */
public:
DEFINE_SIZE_STATIC (6);
};
template <typename Type>
struct RecordArrayOf : SortedArray16Of<Record<Type>>
{
const Offset16To<Type>& get_offset (unsigned int i) const
{ return (*this)[i].offset; }
Offset16To<Type>& get_offset (unsigned int i)
{ return (*this)[i].offset; }
const Tag& get_tag (unsigned int i) const
{ return (*this)[i].tag; }
unsigned int get_tags (unsigned int start_offset,
unsigned int *record_count /* IN/OUT */,
hb_tag_t *record_tags /* OUT */) const
{
if (record_count)
{
+ this->sub_array (start_offset, record_count)
| hb_map (&Record<Type>::tag)
| hb_sink (hb_array (record_tags, *record_count))
;
}
return this->len;
}
bool find_index (hb_tag_t tag, unsigned int *index) const
{
return this->bfind (tag, index, HB_NOT_FOUND_STORE, Index::NOT_FOUND_INDEX);
}
};
template <typename Type>
struct RecordListOf : RecordArrayOf<Type>
{
const Type& operator [] (unsigned int i) const
{ return this+this->get_offset (i); }
bool subset (hb_subset_context_t *c,
hb_subset_layout_context_t *l) const
{
TRACE_SUBSET (this);
auto *out = c->serializer->start_embed (*this);
if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
+ this->iter ()
| hb_apply (subset_record_array (l, out, this))
;
return_trace (true);
}
bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
return_trace (RecordArrayOf<Type>::sanitize (c, this));
}
};
struct RecordListOfFeature : RecordListOf<Feature>
{
bool subset (hb_subset_context_t *c,
@ -907,11 +981,20 @@ struct RecordListOfFeature : RecordListOf<Feature>
if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false);
unsigned count = this->len;
+ hb_zip (*this, hb_range (count))
| hb_filter (l->feature_index_map, hb_second)
| hb_map (hb_first)
| hb_apply (subset_record_array (l, out, this))
| hb_apply ([l, out, this] (const hb_pair_t<const Record<Feature>&, unsigned>& _)
{
const Feature *f_sub = nullptr;
const Feature **f = nullptr;
if (l->feature_substitutes_map->has (_.second, &f))
f_sub = *f;
subset_record_array (l, out, this, f_sub) (_.first);
})
;
return_trace (true);
}
};
@ -996,7 +1079,7 @@ struct LangSys
auto *out = c->serializer->start_embed (*this);
if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false);
const unsigned *v;
const uint32_t *v;
out->reqFeatureIndex = l->feature_index_map->has (reqFeatureIndex, &v) ? *v : 0xFFFFu;
if (!l->visitFeatureIndex (featureIndex.len))
@ -1305,7 +1388,13 @@ struct Lookup
outMarkFilteringSet = markFilteringSet;
}
return_trace (out->subTable.len);
// Always keep the lookup even if it's empty. The rest of layout subsetting depends on lookup
// indices being consistent with those computed during planning. So if an empty lookup is
// discarded during the subset phase it will invalidate all subsequent lookup indices.
// Generally we shouldn't end up with an empty lookup as we pre-prune them during the planning
// phase, but it can happen in rare cases such as when during closure subtable is considered
// degenerate (see: https://github.com/harfbuzz/harfbuzz/issues/3853)
return true;
}
template <typename TSubTable>
@ -1465,7 +1554,7 @@ struct ClassDefFormat1_3
startGlyph = glyph_min;
if (unlikely (!classValue.serialize (c, glyph_count))) return_trace (false);
for (const hb_pair_t<hb_codepoint_t, unsigned> gid_klass_pair : + it)
for (const hb_pair_t<hb_codepoint_t, uint32_t> gid_klass_pair : + it)
{
unsigned idx = gid_klass_pair.first - glyph_min;
classValue[idx] = gid_klass_pair.second;
@ -1592,11 +1681,11 @@ struct ClassDefFormat1_3
if (klass == 0)
{
unsigned start_glyph = startGlyph;
for (unsigned g = HB_SET_VALUE_INVALID;
for (uint32_t g = HB_SET_VALUE_INVALID;
hb_set_next (glyphs, &g) && g < start_glyph;)
intersect_glyphs->add (g);
for (unsigned g = startGlyph + count - 1;
for (uint32_t g = startGlyph + count - 1;
hb_set_next (glyphs, &g);)
intersect_glyphs->add (g);
@ -2692,6 +2781,13 @@ struct VariationStore
/*
* Feature Variations
*/
enum Cond_with_Var_flag_t
{
KEEP_COND_WITH_VAR = 0,
DROP_COND_WITH_VAR = 1,
DROP_RECORD_WITH_VAR = 2,
MEM_ERR_WITH_VAR = 3,
};
struct ConditionFormat1
{
@ -2702,10 +2798,52 @@ struct ConditionFormat1
TRACE_SUBSET (this);
auto *out = c->serializer->embed (this);
if (unlikely (!out)) return_trace (false);
return_trace (true);
const hb_map_t *index_map = c->plan->axes_index_map;
if (index_map->is_empty ()) return_trace (true);
if (!index_map->has (axisIndex))
return_trace (false);
return_trace (c->serializer->check_assign (out->axisIndex, index_map->get (axisIndex),
HB_SERIALIZE_ERROR_INT_OVERFLOW));
}
private:
Cond_with_Var_flag_t keep_with_variations (hb_collect_feature_substitutes_with_var_context_t *c,
hb_map_t *condition_map /* OUT */) const
{
//invalid axis index, drop the entire record
if (!c->axes_index_tag_map->has (axisIndex))
return DROP_RECORD_WITH_VAR;
hb_tag_t axis_tag = c->axes_index_tag_map->get (axisIndex);
//axis not pinned, keep the condition
if (!c->axes_location->has (axis_tag))
{
// add axisIndex->value into the hashmap so we can check if the record is
// unique with variations
int16_t min_val = filterRangeMinValue;
int16_t max_val = filterRangeMaxValue;
hb_codepoint_t val = (max_val << 16) + min_val;
condition_map->set (axisIndex, val);
return KEEP_COND_WITH_VAR;
}
//axis pinned, check if condition is met
//TODO: add check for axis Ranges
int v = c->axes_location->get (axis_tag);
//condition not met, drop the entire record
if (v < filterRangeMinValue || v > filterRangeMaxValue)
return DROP_RECORD_WITH_VAR;
//axis pinned and condition met, drop the condition
return DROP_COND_WITH_VAR;
}
bool evaluate (const int *coords, unsigned int coord_len) const
{
int coord = axisIndex < coord_len ? coords[axisIndex] : 0;
@ -2737,6 +2875,15 @@ struct Condition
}
}
Cond_with_Var_flag_t keep_with_variations (hb_collect_feature_substitutes_with_var_context_t *c,
hb_map_t *condition_map /* OUT */) const
{
switch (u.format) {
case 1: return u.format1.keep_with_variations (c, condition_map);
default:return KEEP_COND_WITH_VAR;
}
}
template <typename context_t, typename ...Ts>
typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
{
@ -2778,15 +2925,65 @@ struct ConditionSet
return true;
}
bool subset (hb_subset_context_t *c) const
Cond_with_Var_flag_t keep_with_variations (hb_collect_feature_substitutes_with_var_context_t *c) const
{
hb_map_t *condition_map = hb_map_create ();
if (unlikely (!condition_map)) return MEM_ERR_WITH_VAR;
hb::shared_ptr<hb_map_t> p {condition_map};
hb_set_t *cond_set = hb_set_create ();
if (unlikely (!cond_set)) return MEM_ERR_WITH_VAR;
hb::shared_ptr<hb_set_t> s {cond_set};
unsigned num_kept_cond = 0, cond_idx = 0;
for (const auto& offset : conditions)
{
Cond_with_Var_flag_t ret = (this+offset).keep_with_variations (c, condition_map);
// one condition is not met, drop the entire record
if (ret == DROP_RECORD_WITH_VAR)
return DROP_RECORD_WITH_VAR;
// axis not pinned, keep this condition
if (ret == KEEP_COND_WITH_VAR)
{
cond_set->add (cond_idx);
num_kept_cond++;
}
cond_idx++;
}
// all conditions met
if (num_kept_cond == 0) return DROP_COND_WITH_VAR;
//check if condition_set is unique with variations
if (c->conditionset_map->has (p))
//duplicate found, drop the entire record
return DROP_RECORD_WITH_VAR;
c->conditionset_map->set (p, 1);
c->record_cond_idx_map->set (c->cur_record_idx, s);
return KEEP_COND_WITH_VAR;
}
bool subset (hb_subset_context_t *c,
hb_subset_layout_context_t *l) const
{
TRACE_SUBSET (this);
auto *out = c->serializer->start_embed (this);
if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false);
+ conditions.iter ()
| hb_apply (subset_offset_array (c, out->conditions, this))
;
hb_set_t *retained_cond_set = nullptr;
if (l->feature_record_cond_idx_map != nullptr)
retained_cond_set = l->feature_record_cond_idx_map->get (l->cur_feature_var_record_idx);
unsigned int count = conditions.len;
for (unsigned int i = 0; i < count; i++)
{
if (retained_cond_set != nullptr && !retained_cond_set->has (i))
continue;
subset_offset_array (c, out->conditions, this) (conditions[i]);
}
return_trace (bool (out->conditions));
}
@ -2820,10 +3017,19 @@ struct FeatureTableSubstitutionRecord
feature_indexes->add (featureIndex);
}
void collect_feature_substitutes_with_variations (hb_hashmap_t<unsigned, const Feature*> *feature_substitutes_map,
const hb_set_t *feature_indices,
const void *base) const
{
if (feature_indices->has (featureIndex))
feature_substitutes_map->set (featureIndex, &(base+feature));
}
bool subset (hb_subset_layout_context_t *c, const void *base) const
{
TRACE_SUBSET (this);
if (!c->feature_index_map->has (featureIndex)) {
if (!c->feature_index_map->has (featureIndex) ||
c->feature_substitutes_map->has (featureIndex)) {
// Feature that is being substituted is not being retained, so we don't
// need this.
return_trace (false);
@ -2865,10 +3071,16 @@ struct FeatureTableSubstitution
}
void collect_lookups (const hb_set_t *feature_indexes,
const hb_hashmap_t<unsigned, const Feature*> *feature_substitutes_map,
hb_set_t *lookup_indexes /* OUT */) const
{
+ hb_iter (substitutions)
| hb_filter (feature_indexes, &FeatureTableSubstitutionRecord::featureIndex)
| hb_filter ([feature_substitutes_map] (const FeatureTableSubstitutionRecord& record)
{
if (feature_substitutes_map == nullptr) return true;
return !feature_substitutes_map->has (record.featureIndex);
})
| hb_apply ([this, lookup_indexes] (const FeatureTableSubstitutionRecord& r)
{ r.collect_lookups (this, lookup_indexes); })
;
@ -2890,6 +3102,12 @@ struct FeatureTableSubstitution
return false;
}
void collect_feature_substitutes_with_variations (hb_collect_feature_substitutes_with_var_context_t *c) const
{
for (const FeatureTableSubstitutionRecord& record : substitutions)
record.collect_feature_substitutes_with_variations (c->feature_substitutes_map, c->feature_indices, this);
}
bool subset (hb_subset_context_t *c,
hb_subset_layout_context_t *l) const
{
@ -2929,9 +3147,10 @@ struct FeatureVariationRecord
void collect_lookups (const void *base,
const hb_set_t *feature_indexes,
const hb_hashmap_t<unsigned, const Feature*> *feature_substitutes_map,
hb_set_t *lookup_indexes /* OUT */) const
{
return (base+substitutions).collect_lookups (feature_indexes, lookup_indexes);
return (base+substitutions).collect_lookups (feature_indexes, feature_substitutes_map, lookup_indexes);
}
void closure_features (const void *base,
@ -2946,13 +3165,25 @@ struct FeatureVariationRecord
return (base+substitutions).intersects_features (feature_index_map);
}
void collect_feature_substitutes_with_variations (hb_collect_feature_substitutes_with_var_context_t *c,
const void *base) const
{
// ret == 1, all conditions met
if ((base+conditions).keep_with_variations (c) == DROP_COND_WITH_VAR &&
c->apply)
{
(base+substitutions).collect_feature_substitutes_with_variations (c);
c->apply = false; // set variations only once
}
}
bool subset (hb_subset_layout_context_t *c, const void *base) const
{
TRACE_SUBSET (this);
auto *out = c->subset_context->serializer->embed (this);
if (unlikely (!out)) return_trace (false);
out->conditions.serialize_subset (c->subset_context, conditions, base);
out->conditions.serialize_subset (c->subset_context, conditions, base, c);
out->substitutions.serialize_subset (c->subset_context, substitutions, base, c);
return_trace (true);
@ -3002,6 +3233,16 @@ struct FeatureVariations
return (this+record.substitutions).find_substitute (feature_index);
}
void collect_feature_substitutes_with_variations (hb_collect_feature_substitutes_with_var_context_t *c) const
{
unsigned int count = varRecords.len;
for (unsigned int i = 0; i < count; i++)
{
c->cur_record_idx = i;
varRecords[i].collect_feature_substitutes_with_variations (c, this);
}
}
FeatureVariations* copy (hb_serialize_context_t *c) const
{
TRACE_SERIALIZE (this);
@ -3009,17 +3250,25 @@ struct FeatureVariations
}
void collect_lookups (const hb_set_t *feature_indexes,
const hb_hashmap_t<unsigned, const Feature*> *feature_substitutes_map,
hb_set_t *lookup_indexes /* OUT */) const
{
for (const FeatureVariationRecord& r : varRecords)
r.collect_lookups (this, feature_indexes, lookup_indexes);
r.collect_lookups (this, feature_indexes, feature_substitutes_map, lookup_indexes);
}
void closure_features (const hb_map_t *lookup_indexes,
const hb_hashmap_t<unsigned, hb::shared_ptr<hb_set_t>> *feature_record_cond_idx_map,
hb_set_t *feature_indexes /* OUT */) const
{
for (const FeatureVariationRecord& record : varRecords)
record.closure_features (this, lookup_indexes, feature_indexes);
unsigned int count = varRecords.len;
for (unsigned int i = 0; i < count; i++)
{
if (feature_record_cond_idx_map != nullptr &&
!feature_record_cond_idx_map->has (i))
continue;
varRecords[i].closure_features (this, lookup_indexes, feature_indexes);
}
}
bool subset (hb_subset_context_t *c,
@ -3041,7 +3290,13 @@ struct FeatureVariations
}
unsigned count = (unsigned) (keep_up_to + 1);
for (unsigned i = 0; i < count; i++) {
for (unsigned i = 0; i < count; i++)
{
if (l->feature_record_cond_idx_map != nullptr &&
!l->feature_record_cond_idx_map->has (i))
continue;
l->cur_feature_var_record_idx = i;
subset_record_array (l, &(out->varRecords), this) (varRecords[i]);
}
return_trace (bool (out->varRecords));

View File

@ -4236,13 +4236,19 @@ struct GSUBGPOS
}
void feature_variation_collect_lookups (const hb_set_t *feature_indexes,
const hb_hashmap_t<unsigned, const Feature*> *feature_substitutes_map,
hb_set_t *lookup_indexes /* OUT */) const
{
#ifndef HB_NO_VAR
get_feature_variations ().collect_lookups (feature_indexes, lookup_indexes);
get_feature_variations ().collect_lookups (feature_indexes, feature_substitutes_map, lookup_indexes);
#endif
}
#ifndef HB_NO_VAR
void collect_feature_substitutes_with_variations (hb_collect_feature_substitutes_with_var_context_t *c) const
{ get_feature_variations ().collect_feature_substitutes_with_variations (c); }
#endif
template <typename TLookup>
void closure_lookups (hb_face_t *face,
const hb_set_t *glyphs,
@ -4278,6 +4284,8 @@ struct GSUBGPOS
}
void prune_features (const hb_map_t *lookup_indices, /* IN */
const hb_hashmap_t<unsigned, hb::shared_ptr<hb_set_t>> *feature_record_cond_idx_map, /* IN */
const hb_hashmap_t<unsigned, const Feature*> *feature_substitutes_map, /* IN */
hb_set_t *feature_indices /* IN/OUT */) const
{
#ifndef HB_NO_VAR
@ -4285,7 +4293,7 @@ struct GSUBGPOS
// if the FeatureVariation's table and the alternate version(s) intersect the
// set of lookup indices.
hb_set_t alternate_feature_indices;
get_feature_variations ().closure_features (lookup_indices, &alternate_feature_indices);
get_feature_variations ().closure_features (lookup_indices, feature_record_cond_idx_map, &alternate_feature_indices);
if (unlikely (alternate_feature_indices.in_error()))
{
feature_indices->err ();
@ -4295,7 +4303,6 @@ struct GSUBGPOS
for (unsigned i : feature_indices->iter())
{
const Feature& f = get_feature (i);
hb_tag_t tag = get_feature_tag (i);
if (tag == HB_TAG ('p', 'r', 'e', 'f'))
// Note: Never ever drop feature 'pref', even if it's empty.
@ -4305,11 +4312,16 @@ struct GSUBGPOS
continue;
if (!f.featureParams.is_null () &&
const Feature *f = &(get_feature (i));
const Feature** p = nullptr;
if (feature_substitutes_map->has (i, &p))
f = *p;
if (!f->featureParams.is_null () &&
tag == HB_TAG ('s', 'i', 'z', 'e'))
continue;
if (!f.intersects_lookup_indexes (lookup_indices)
if (!f->intersects_lookup_indexes (lookup_indices)
#ifndef HB_NO_VAR
&& !alternate_feature_indices.has (i)
#endif

View File

@ -1271,7 +1271,7 @@ hb_ot_layout_collect_lookups (hb_face_t *face,
hb_set_next (&feature_indexes, &feature_index);)
g.get_feature (feature_index).add_lookup_indexes_to (lookup_indexes);
g.feature_variation_collect_lookups (&feature_indexes, lookup_indexes);
g.feature_variation_collect_lookups (&feature_indexes, nullptr, lookup_indexes);
}
@ -1709,6 +1709,8 @@ hb_ot_layout_get_size_params (hb_face_t *face,
return false;
}
/**
* hb_ot_layout_feature_get_name_ids:
* @face: #hb_face_t to work upon
@ -2341,6 +2343,7 @@ struct hb_get_glyph_alternates_dispatch_t :
( _dispatch (obj, hb_prioritize, std::forward<Ts> (ds)...) )
};
#ifndef HB_NO_LAYOUT_RARELY_USED
/**
* hb_ot_layout_lookup_get_glyph_alternates:
* @face: a face.
@ -2373,4 +2376,72 @@ hb_ot_layout_lookup_get_glyph_alternates (hb_face_t *face,
return ret;
}
struct hb_position_single_dispatch_t :
hb_dispatch_context_t<hb_position_single_dispatch_t, bool>
{
static return_t default_return_value () { return false; }
bool stop_sublookup_iteration (return_t r) const { return r; }
private:
template <typename T, typename ...Ts> auto
_dispatch (const T &obj, hb_priority<1>, Ts&&... ds) HB_AUTO_RETURN
( obj.position_single (std::forward<Ts> (ds)...) )
template <typename T, typename ...Ts> auto
_dispatch (const T &obj, hb_priority<0>, Ts&&... ds) HB_AUTO_RETURN
( default_return_value () )
public:
template <typename T, typename ...Ts> auto
dispatch (const T &obj, Ts&&... ds) HB_AUTO_RETURN
( _dispatch (obj, hb_prioritize, std::forward<Ts> (ds)...) )
};
/**
* hb_ot_layout_lookup_get_optical_bound:
* @font: a font.
* @lookup_index: index of the feature lookup to query.
* @direction: edge of the glyph to query.
* @glyph: a glyph id.
*
* Fetches the optical bound of a glyph positioned at the margin of text.
* The direction identifies which edge of the glyph to query.
*
* Return value: Adjustment value. Negative values mean the glyph will stick out of the margin.
*
* Since: 5.3.0
**/
hb_position_t
hb_ot_layout_lookup_get_optical_bound (hb_font_t *font,
unsigned lookup_index,
hb_direction_t direction,
hb_codepoint_t glyph)
{
const OT::PosLookup &lookup = font->face->table.GPOS->table->get_lookup (lookup_index);
hb_glyph_position_t pos = {0};
hb_position_single_dispatch_t c;
lookup.dispatch (&c, font, direction, glyph, pos);
hb_position_t ret = 0;
switch (direction)
{
case HB_DIRECTION_LTR:
ret = pos.x_offset;
break;
case HB_DIRECTION_RTL:
ret = pos.x_advance - pos.x_offset;
break;
case HB_DIRECTION_TTB:
ret = pos.y_offset;
break;
case HB_DIRECTION_BTT:
ret = pos.y_advance - pos.y_offset;
break;
case HB_DIRECTION_INVALID:
default:
break;
}
return ret;
}
#endif
#endif

View File

@ -403,6 +403,16 @@ hb_ot_layout_get_size_params (hb_face_t *face,
unsigned int *range_start, /* OUT. May be NULL */
unsigned int *range_end /* OUT. May be NULL */);
HB_EXTERN hb_position_t
hb_ot_layout_lookup_get_optical_bound (hb_font_t *font,
unsigned lookup_index,
hb_direction_t direction,
hb_codepoint_t glyph);
/*
* GSUB/GPOS
*/
HB_EXTERN hb_bool_t
hb_ot_layout_feature_get_name_ids (hb_face_t *face,
@ -423,6 +433,7 @@ hb_ot_layout_feature_get_characters (hb_face_t *face,
unsigned int *char_count /* IN/OUT. May be NULL */,
hb_codepoint_t *characters /* OUT. May be NULL */);
/*
* BASE
*/

View File

@ -78,14 +78,14 @@ HB_INTERNAL bool postV2Tail::subset (hb_subset_context_t *c) const
post::accelerator_t _post (c->plan->source);
hb_hashmap_t<hb_bytes_t, unsigned, true> glyph_name_to_new_index;
hb_hashmap_t<hb_bytes_t, uint32_t, true> glyph_name_to_new_index;
for (hb_codepoint_t new_gid = 0; new_gid < num_glyphs; new_gid++)
{
hb_codepoint_t old_gid = reverse_glyph_map.get (new_gid);
unsigned old_index = glyphNameIndex[old_gid];
unsigned new_index;
const unsigned *new_index2;
const uint32_t *new_index2;
if (old_index <= 257) new_index = old_index;
else if (old_new_index_map.has (old_index, &new_index2))
{

View File

@ -282,7 +282,7 @@ struct post
* 0x00020000 for version 2.0
* 0x00025000 for version 2.5 (deprecated)
* 0x00030000 for version 3.0 */
HBFixed italicAngle; /* Italic angle in counter-clockwise degrees
F16DOT16 italicAngle; /* Italic angle in counter-clockwise degrees
* from the vertical. Zero for upright text,
* negative for text that leans to the right
* (forward). */

View File

@ -527,18 +527,20 @@ hb_set_unicode_props (hb_buffer_t *buffer)
}
#endif
/* Or part of the Other_Grapheme_Extend that is not marks.
* As of Unicode 11 that is just:
* As of Unicode 15 that is just:
*
* 200C ; Other_Grapheme_Extend # Cf ZERO WIDTH NON-JOINER
* FF9E..FF9F ; Other_Grapheme_Extend # Lm [2] HALFWIDTH KATAKANA VOICED SOUND MARK..HALFWIDTH KATAKANA SEMI-VOICED SOUND MARK
* E0020..E007F ; Other_Grapheme_Extend # Cf [96] TAG SPACE..CANCEL TAG
*
* ZWNJ is special, we don't want to merge it as there's no need, and keeping
* it separate results in more granular clusters. Ignore Katakana for now.
* it separate results in more granular clusters.
* Tags are used for Emoji sub-region flag sequences:
* https://github.com/harfbuzz/harfbuzz/issues/1556
* Katakana ones were requested:
* https://github.com/harfbuzz/harfbuzz/issues/3844
*/
else if (unlikely (hb_in_range<hb_codepoint_t> (info[i].codepoint, 0xE0020u, 0xE007Fu)))
else if (unlikely (hb_in_ranges<hb_codepoint_t> (info[i].codepoint, 0xFF9Eu, 0xFF9Fu, 0xE0020u, 0xE007Fu)))
_hb_glyph_info_set_continuation (&info[i]);
}
}

View File

@ -25,6 +25,7 @@
* # Updated for Unicode 12.1 by Andrew Glass 2019-05-24
* # Updated for Unicode 13.0 by Andrew Glass 2020-07-28
* # Updated for Unicode 14.0 by Andrew Glass 2021-09-25
* # Updated for Unicode 15.0 by Andrew Glass 2022-09-16
* # Override values For Indic_Positional_Category
* # Not derivable
* # Initial version based on Unicode 7.0 by Andrew Glass 2014-03-17
@ -34,6 +35,7 @@
* # Updated for Unicode 12.1 by Andrew Glass 2019-05-30
* # Updated for Unicode 13.0 by Andrew Glass 2020-07-28
* # Updated for Unicode 14.0 by Andrew Glass 2021-09-28
* # Updated for Unicode 15.0 by Andrew Glass 2022-09-16
* UnicodeData.txt does not have a header.
*/
@ -90,7 +92,7 @@
#pragma GCC diagnostic pop
static const uint8_t
hb_use_u8[3115] =
hb_use_u8[3141] =
{
16, 50, 51, 51, 51, 52, 51, 83, 118, 131, 51, 57, 58, 179, 195, 61,
51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
@ -125,11 +127,11 @@ hb_use_u8[3115] =
2, 2, 2, 2, 2, 2, 2, 2, 2, 88, 89, 2, 2, 2, 2, 2,
2, 2, 2, 90, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 91, 2, 2, 92, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 93, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 94, 94, 95, 96, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94,
94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94,
94, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
2, 2, 2, 91, 2, 2, 92, 2, 2, 2, 93, 2, 2, 2, 2, 2,
2, 2, 2, 94, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 95, 95, 96, 97, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95,
95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95,
95, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
0, 2, 2, 2, 2, 2, 0, 0, 0, 3, 0, 0, 0, 0, 0, 4,
0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 6, 7, 0, 0, 0, 0, 0, 0,
@ -226,70 +228,72 @@ hb_use_u8[3115] =
0, 9, 47, 2, 2, 2, 2, 2, 2, 2, 2, 2, 125, 18, 20, 151,
20, 19, 152, 153, 2, 2, 2, 2, 2, 0, 0, 63, 154, 0, 0, 0,
0, 2, 11, 0, 0, 0, 0, 0, 0, 2, 63, 23, 18, 18, 18, 20,
20, 106, 155, 0, 0, 156, 157, 29, 158, 28, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 21, 17, 20, 20, 159, 42, 0, 0, 0,
20, 106, 155, 0, 0, 54, 156, 29, 157, 28, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 21, 17, 20, 20, 158, 42, 0, 0, 0,
47, 125, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 7, 7, 2, 2,
28, 2, 2, 2, 2, 2, 2, 2, 28, 2, 2, 2, 2, 2, 2, 2,
8, 16, 17, 19, 20, 160, 29, 0, 0, 9, 9, 28, 2, 2, 2, 7,
8, 16, 17, 19, 20, 159, 29, 0, 0, 9, 9, 28, 2, 2, 2, 7,
28, 7, 2, 28, 2, 2, 56, 15, 21, 14, 21, 45, 30, 31, 30, 32,
0, 0, 0, 0, 33, 0, 0, 0, 2, 2, 21, 0, 9, 9, 9, 44,
0, 9, 9, 44, 0, 0, 0, 0, 0, 2, 2, 63, 23, 18, 18, 18,
20, 21, 123, 13, 15, 0, 0, 0, 0, 2, 2, 2, 2, 2, 0, 0,
161, 162, 0, 0, 0, 0, 0, 0, 0, 16, 17, 18, 18, 64, 97, 23,
158, 9, 163, 7, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2,
63, 23, 18, 18, 0, 46, 46, 9, 164, 35, 0, 0, 0, 0, 0, 0,
160, 161, 0, 0, 0, 0, 0, 0, 0, 16, 17, 18, 18, 64, 97, 23,
157, 9, 162, 7, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2,
63, 23, 18, 18, 0, 46, 46, 9, 163, 35, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 2, 2, 18, 0, 21, 17, 18, 18, 19, 14, 80,
164, 36, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 8, 165,
23, 18, 20, 20, 163, 7, 0, 0, 0, 2, 2, 2, 2, 2, 7, 41,
163, 36, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 8, 164,
23, 18, 20, 20, 162, 7, 0, 0, 0, 2, 2, 2, 2, 2, 7, 41,
133, 21, 20, 18, 74, 19, 20, 0, 0, 2, 2, 2, 7, 0, 0, 0,
0, 2, 2, 2, 2, 2, 2, 16, 17, 18, 19, 20, 103, 164, 35, 0,
0, 2, 2, 2, 2, 2, 2, 16, 17, 18, 19, 20, 103, 163, 35, 0,
0, 2, 2, 2, 7, 28, 0, 2, 2, 2, 2, 28, 7, 2, 2, 2,
2, 21, 21, 16, 30, 31, 10, 166, 167, 168, 169, 0, 0, 0, 0, 0,
2, 21, 21, 16, 30, 31, 10, 165, 166, 167, 168, 0, 0, 0, 0, 0,
0, 2, 2, 2, 2, 0, 2, 2, 2, 63, 23, 18, 18, 0, 20, 21,
27, 106, 0, 31, 0, 0, 0, 0, 0, 50, 18, 20, 20, 20, 137, 2,
2, 2, 170, 171, 9, 13, 172, 70, 173, 0, 0, 1, 144, 0, 0, 0,
0, 50, 18, 20, 14, 17, 18, 2, 2, 2, 2, 155, 155, 155, 174, 174,
174, 174, 174, 174, 13, 175, 0, 28, 0, 20, 18, 18, 29, 20, 20, 9,
164, 0, 59, 59, 59, 59, 59, 59, 59, 64, 19, 80, 44, 0, 0, 0,
2, 2, 169, 170, 9, 13, 171, 70, 172, 0, 0, 1, 144, 0, 0, 0,
0, 50, 18, 20, 14, 17, 18, 2, 2, 2, 2, 155, 155, 155, 173, 173,
173, 173, 173, 173, 13, 174, 0, 28, 0, 20, 18, 18, 29, 20, 20, 9,
163, 0, 59, 59, 59, 59, 59, 59, 59, 64, 19, 80, 44, 0, 0, 0,
0, 2, 2, 2, 7, 2, 28, 2, 2, 50, 20, 20, 29, 0, 36, 20,
25, 9, 157, 176, 172, 0, 0, 0, 0, 2, 2, 2, 28, 7, 2, 2,
25, 9, 156, 175, 171, 0, 0, 0, 0, 2, 2, 2, 28, 7, 2, 2,
2, 2, 2, 2, 2, 2, 21, 21, 45, 20, 33, 80, 66, 0, 0, 0,
0, 2, 177, 64, 45, 0, 0, 0, 0, 9, 178, 2, 2, 2, 2, 2,
0, 2, 176, 64, 45, 0, 0, 0, 0, 9, 177, 2, 2, 2, 2, 2,
2, 2, 2, 21, 20, 18, 29, 0, 46, 14, 140, 0, 0, 0, 0, 0,
0, 179, 179, 179, 106, 7, 0, 0, 0, 9, 9, 9, 44, 0, 0, 0,
0, 2, 2, 2, 2, 2, 7, 0, 56, 180, 18, 18, 18, 18, 18, 18,
0, 178, 178, 178, 106, 179, 178, 0, 0, 145, 2, 2, 180, 114, 114, 114,
114, 114, 114, 114, 0, 0, 0, 0, 0, 9, 9, 9, 44, 0, 0, 0,
0, 2, 2, 2, 2, 2, 7, 0, 56, 181, 18, 18, 18, 18, 18, 18,
18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 0, 0, 0,
38, 114, 24, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0,
0, 2, 2, 2, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 0, 56,
35, 0, 4, 118, 118, 118, 119, 0, 0, 9, 9, 9, 47, 2, 2, 2,
0, 2, 2, 2, 2, 2, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2,
44, 2, 2, 2, 2, 2, 2, 9, 9, 2, 2, 42, 42, 42, 90, 0,
0, O, O, O, GB, B, B, GB, O, O, WJ,FMPst,FMPst, O, CGJ, B,
O, B,VMAbv,VMAbv,VMAbv, O,VMAbv, B,CMBlw,CMBlw,CMBlw,VMAbv,VMPst, VAbv, VPst,CMBlw,
B, VPst, VPre, VPst, VBlw, VBlw, VBlw, VBlw, VAbv, VAbv, VAbv, VPst, VPst, VPst, H, VPre,
VPst,VMBlw, O, O, VAbv, GB,VMAbv,VMPst,VMPst, O, B, VBlw, O, O, VPre, VPre,
O, VPre, H, O, VPst,FMAbv, O,CMBlw, O, VAbv, O, VAbv, H, O,VMBlw,VMAbv,
CMAbv, GB, GB, O, MBlw,CMAbv,CMAbv, VPst, VAbv,VMAbv, O, VPst, O, VPre, VPre,VMAbv,
B, O, CS, CS,VMPst, B, VAbv, VAbv, B, R, O, HVM, O, O, FBlw, O,
CMAbv, O,CMBlw, VAbv, VBlw, B, SUB, SUB, SUB, O, SUB, SUB, O, FBlw, O, B,
VPst, VBlw, VPre,VMAbv,VMBlw,VMPst, IS, VAbv, MPst, MPre, MBlw, MBlw, B, MBlw, MBlw, VPst,
VMPst,VMPst, B, MBlw, VPst, VPre, VAbv, VAbv,VMPst,VMPst,VMBlw, B,VMPst, VBlw, VPst, CGJ,
CGJ, VPst,VMAbv,VMAbv,FMAbv, FAbv,CMAbv,FMAbv,VMAbv,FMAbv, VAbv, IS,FMAbv, B,FMAbv, B,
CGJ, WJ, CGJ, GB,CMAbv,CMAbv, B, GB, B, VAbv, SUB, FPst, FPst,VMBlw, FPst, FPst,
FBlw,VMAbv,FMBlw, VAbv, VPre, B, MPre, MBlw, SUB, FAbv, FAbv, MAbv, SUB, Sk, VPst, VAbv,
VMAbv,VMAbv, FAbv,CMAbv, VPst, H, B, O,SMAbv,SMBlw,SMAbv,SMAbv,SMAbv, VPst, IS, VBlw,
FAbv,VMPre,VMPre,FMAbv,CMBlw,VMBlw,VMBlw,VMAbv, CS, O,FMAbv, ZWNJ, CGJ, WJ, WJ, WJ,
O,FMPst, O, O, H, MPst, VPst, H,VMAbv, VAbv,VMBlw, B, VBlw, FPst, VPst, FAbv,
VMPst, B,CMAbv, VAbv, MBlw, MPst, MBlw, H, O, VBlw, MPst, MPre, MAbv, MBlw, O, B,
FAbv, FAbv, FPst, VBlw, B, B, VPre, O,VMPst, IS, O,VMPst, VBlw, VPst,VMBlw,VMBlw,
VMAbv, O, IS,VMBlw, B,VMPst,VMAbv,VMPst, CS, CS, B, N, N, O, HN, VPre,
VBlw, VAbv, IS,CMAbv, O, VPst, B, R, R, O,FMBlw,CMBlw, VAbv, VPre,VMAbv,VMAbv,
H, VAbv,CMBlw,FMAbv, B, CS, CS, H,CMBlw,VMPst, H,VMPst, VAbv,VMAbv, VPst, IS,
R, MPst, R, MPst,CMBlw, B,FMBlw, VBlw,VMAbv, R, MBlw, MBlw, GB, FBlw, FBlw,CMAbv,
IS, VBlw, IS, GB, VAbv, R,VMPst, H, H, O, VBlw,
44, 2, 2, 2, 2, 2, 2, 9, 9, 2, 2, 2, 2, 2, 2, 20,
20, 2, 2, 42, 42, 42, 90, 0, 0, O, O, O, GB, B, B, GB,
O, O, WJ,FMPst,FMPst, O, CGJ, B, O, B,VMAbv,VMAbv,VMAbv, O,VMAbv, B,
CMBlw,CMBlw,CMBlw,VMAbv,VMPst, VAbv, VPst,CMBlw, B, VPst, VPre, VPst, VBlw, VBlw, VBlw, VBlw,
VAbv, VAbv, VAbv, VPst, VPst, VPst, H, VPre, VPst,VMBlw, O, O, VAbv, GB,VMAbv,VMPst,
VMPst, O, B, VBlw, O, O, VPre, VPre, O, VPre, H, O, VPst,FMAbv, O,CMBlw,
O, VAbv, O, VAbv, H, O,VMBlw,VMAbv,CMAbv, GB, GB, O, MBlw,CMAbv,CMAbv, VPst,
VAbv,VMAbv, O, VPst, O, VPre, VPre,VMAbv, B, O, CS, CS,VMPst, B, VAbv, VAbv,
B, R, O, HVM, O, O,FMBlw, O,CMAbv, O,CMBlw, VAbv, VBlw, B, SUB, SUB,
SUB, O, SUB, SUB, O,FMBlw, O, B, VPst, VBlw, VPre,VMAbv,VMBlw,VMPst, IS, VAbv,
MPst, MPre, MBlw, MBlw, B, MBlw, MBlw, VPst,VMPst,VMPst, B, MBlw, VPst, VPre, VAbv, VAbv,
VMPst,VMPst,VMBlw, B,VMPst, VBlw, VPst, CGJ, CGJ, VPst,VMAbv,VMAbv,FMAbv, FAbv,CMAbv,FMAbv,
VMAbv,FMAbv, VAbv, IS,FMAbv, B,FMAbv, B, CGJ, WJ, CGJ, GB,CMAbv,CMAbv, B, GB,
B, VAbv, SUB, FPst, FPst,VMBlw, FPst, FPst, FBlw,VMAbv,FMBlw, VAbv, VPre, B, MPre, MBlw,
SUB, FAbv, FAbv, MAbv, SUB, Sk, VPst, VAbv,VMAbv,VMAbv, FAbv,CMAbv, VPst, H, B, O,
SMAbv,SMBlw,SMAbv,SMAbv,SMAbv, VPst, IS, VBlw, FAbv,VMPre,VMPre,FMAbv,CMBlw,VMBlw,VMBlw,VMAbv,
CS, O,FMAbv, ZWNJ, CGJ, WJ, WJ, WJ, O,FMPst, O, O, H, MPst, VPst, H,
VMAbv, VAbv,VMBlw, B, VBlw, FPst, VPst, FAbv,VMPst, B,CMAbv, VAbv, MBlw, MPst, MBlw, H,
O, VBlw, MPst, MPre, MAbv, MBlw, O, B, FAbv, FAbv, FPst, VBlw, B, B, VPre, O,
VMPst, IS, O,VMPst, VBlw, VPst,VMBlw,VMBlw,VMAbv, O, IS,VMBlw, B,VMPst,VMAbv,VMPst,
CS, CS, B, N, N, O, HN, VPre, VBlw, VAbv, IS,CMAbv, O, VPst, B, R,
R,CMBlw, VAbv, VPre,VMAbv,VMAbv, H, VAbv,CMBlw,FMAbv, B, CS, CS, H,CMBlw,VMPst,
H,VMPst, VAbv,VMAbv, VPst, IS, R, MPst, R, MPst,CMBlw, B,FMBlw, VBlw,VMAbv, R,
MBlw, MBlw, GB, FBlw, FBlw,CMAbv, IS, VBlw, IS, GB, VAbv, R,VMPst, H, H, B,
H, B,VMBlw, O, VBlw,
};
static const uint16_t
hb_use_u16[776] =
hb_use_u16[784] =
{
0, 0, 1, 2, 0, 0, 0, 0, 0, 0, 3, 4, 0, 5, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0,
@ -332,14 +336,14 @@ hb_use_u16[776] =
9,242, 73,243, 0, 0, 0, 0,244, 9, 9,245,246, 2,247, 9,
248,249, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9,250,
251, 48, 9,252,253, 2, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 98,254, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0,
9, 9, 9,255, 0, 0, 0, 0, 9, 9, 9, 9,256,257,258,258,
259,260, 0, 0, 0, 0,261, 0, 9, 9, 9, 9, 9,262, 0, 0,
9, 9, 9, 9, 9, 9,105, 70, 94,263, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0,264, 9, 9, 70,265,266, 0, 0, 0,
0, 9,267, 0, 9, 9,268, 2, 9, 9, 9, 9,269, 2, 0, 0,
129,129,129,129,129,129,129,129,160,160,160,160,160,160,160,160,
160,160,160,160,160,160,160,129,
9, 9, 9,254,255,256, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0,
9, 9, 9,257, 0, 0, 0, 0, 9, 9, 9, 9,258,259,260,260,
261,262, 0, 0, 0, 0,263, 0, 9, 9, 9, 9, 9,264, 0, 0,
9, 9, 9, 9, 9, 9,105, 70, 94,265, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0,266, 9, 9, 70,267,268, 0, 0, 0,
0, 9,269, 0, 9, 9,270, 2, 0, 0, 0, 0, 0, 9,271, 2,
9, 9, 9, 9,272, 2, 0, 0,129,129,129,129,129,129,129,129,
160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,129,
};
static inline unsigned
@ -350,7 +354,7 @@ hb_use_b4 (const uint8_t* a, unsigned i)
static inline uint_fast8_t
hb_use_get_category (unsigned u)
{
return u<921600u?hb_use_u8[2753+(((hb_use_u8[593+(((hb_use_u16[((hb_use_u8[113+(((hb_use_b4(hb_use_u8,u>>1>>3>>3>>5))<<5)+((u>>1>>3>>3)&31u))])<<3)+((u>>1>>3)&7u)])<<3)+((u>>1)&7u))])<<1)+((u)&1u))]:O;
return u<921600u?hb_use_u8[2777+(((hb_use_u8[593+(((hb_use_u16[((hb_use_u8[113+(((hb_use_b4(hb_use_u8,u>>1>>3>>3>>5))<<5)+((u>>1>>3>>3)&31u))])<<3)+((u>>1>>3)&7u)])<<3)+((u>>1)&7u))])<<1)+((u)&1u))]:O;
}
#undef B

View File

@ -342,6 +342,40 @@ _hb_preprocess_text_vowel_constraints (const hb_ot_shape_plan_t *plan HB_UNUSED,
}
break;
case HB_SCRIPT_KHOJKI:
for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;)
{
bool matched = false;
switch (buffer->cur ().codepoint)
{
case 0x11200u:
switch (buffer->cur (1).codepoint)
{
case 0x1122Cu: case 0x11231u: case 0x11233u:
matched = true;
break;
}
break;
case 0x11206u:
matched = 0x1122Cu == buffer->cur (1).codepoint;
break;
case 0x1122Cu:
switch (buffer->cur (1).codepoint)
{
case 0x11230u: case 0x11231u:
matched = true;
break;
}
break;
case 0x11240u:
matched = 0x1122Eu == buffer->cur (1).codepoint;
break;
}
(void) buffer->next_glyph ();
if (matched) _output_with_dotted_circle (buffer);
}
break;
case HB_SCRIPT_KHUDAWADI:
for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;)
{

View File

@ -136,7 +136,7 @@ struct AxisValueFormat1
NameID valueNameID; /* The name ID for entries in the 'name' table
* that provide a display string for this
* attribute value. */
HBFixed value; /* A numeric value for this attribute value. */
F16DOT16 value; /* A numeric value for this attribute value. */
public:
DEFINE_SIZE_STATIC (12);
};
@ -195,10 +195,10 @@ struct AxisValueFormat2
NameID valueNameID; /* The name ID for entries in the 'name' table
* that provide a display string for this
* attribute value. */
HBFixed nominalValue; /* A numeric value for this attribute value. */
HBFixed rangeMinValue; /* The minimum value for a range associated
F16DOT16 nominalValue; /* A numeric value for this attribute value. */
F16DOT16 rangeMinValue; /* The minimum value for a range associated
* with the specified name ID. */
HBFixed rangeMaxValue; /* The maximum value for a range associated
F16DOT16 rangeMaxValue; /* The maximum value for a range associated
* with the specified name ID. */
public:
DEFINE_SIZE_STATIC (20);
@ -258,8 +258,8 @@ struct AxisValueFormat3
NameID valueNameID; /* The name ID for entries in the 'name' table
* that provide a display string for this
* attribute value. */
HBFixed value; /* A numeric value for this attribute value. */
HBFixed linkedValue; /* The numeric value for a style-linked mapping
F16DOT16 value; /* A numeric value for this attribute value. */
F16DOT16 linkedValue; /* The numeric value for a style-linked mapping
* from this value. */
public:
DEFINE_SIZE_STATIC (16);
@ -280,7 +280,7 @@ struct AxisValueRecord
HBUINT16 axisIndex; /* Zero-base index into the axis record array
* identifying the axis to which this value
* applies. Must be less than designAxisCount. */
HBFixed value; /* A numeric value for this attribute value. */
F16DOT16 value; /* A numeric value for this attribute value. */
public:
DEFINE_SIZE_STATIC (6);
};

View File

@ -44,9 +44,47 @@ struct InstanceRecord
{
friend struct fvar;
hb_array_t<const HBFixed> get_coordinates (unsigned int axis_count) const
hb_array_t<const F16DOT16> get_coordinates (unsigned int axis_count) const
{ return coordinatesZ.as_array (axis_count); }
bool subset (hb_subset_context_t *c,
unsigned axis_count,
bool has_postscript_nameid) const
{
TRACE_SUBSET (this);
if (unlikely (!c->serializer->embed (subfamilyNameID))) return_trace (false);
if (unlikely (!c->serializer->embed (flags))) return_trace (false);
const hb_array_t<const F16DOT16> coords = get_coordinates (axis_count);
const hb_hashmap_t<hb_tag_t, float> *axes_location = c->plan->user_axes_location;
for (unsigned i = 0 ; i < axis_count; i++)
{
uint32_t *axis_tag;
// only keep instances whose coordinates == pinned axis location
if (!c->plan->axes_old_index_tag_map->has (i, &axis_tag)) continue;
if (axes_location->has (*axis_tag) &&
fabsf (axes_location->get (*axis_tag) - coords[i].to_float ()) > 0.001f)
return_trace (false);
if (!c->plan->axes_index_map->has (i))
continue;
if (!c->serializer->embed (coords[i]))
return_trace (false);
}
if (has_postscript_nameid)
{
NameID name_id;
name_id = StructAfter<NameID> (coords);
if (!c->serializer->embed (name_id))
return_trace (false);
}
return_trace (true);
}
bool sanitize (hb_sanitize_context_t *c, unsigned int axis_count) const
{
TRACE_SANITIZE (this);
@ -58,7 +96,7 @@ struct InstanceRecord
NameID subfamilyNameID;/* The name ID for entries in the 'name' table
* that provide subfamily names for this instance. */
HBUINT16 flags; /* Reserved for future use — set to 0. */
UnsizedArrayOf<HBFixed>
UnsizedArrayOf<F16DOT16>
coordinatesZ; /* The coordinates array for this instance. */
//NameID postScriptNameIDX;/*Optional. The name ID for entries in the 'name'
// * table that provide PostScript names for this
@ -151,9 +189,9 @@ struct AxisRecord
public:
Tag axisTag; /* Tag identifying the design variation for the axis. */
protected:
HBFixed minValue; /* The minimum coordinate value for the axis. */
HBFixed defaultValue; /* The default coordinate value for the axis. */
HBFixed maxValue; /* The maximum coordinate value for the axis. */
F16DOT16 minValue; /* The minimum coordinate value for the axis. */
F16DOT16 defaultValue; /* The default coordinate value for the axis. */
F16DOT16 maxValue; /* The maximum coordinate value for the axis. */
public:
HBUINT16 flags; /* Axis flags. */
NameID axisNameID; /* The name ID for entries in the 'name' table that
@ -268,7 +306,7 @@ struct fvar
if (coords_length && *coords_length)
{
hb_array_t<const HBFixed> instanceCoords = instance->get_coordinates (axisCount)
hb_array_t<const F16DOT16> instanceCoords = instance->get_coordinates (axisCount)
.sub_array (0, coords_length);
for (unsigned int i = 0; i < instanceCoords.length; i++)
coords[i] = instanceCoords.arrayZ[i].to_float ();
@ -301,7 +339,7 @@ struct fvar
if (hb_any (+ hb_zip (instance->get_coordinates (axisCount), hb_range ((unsigned)axisCount))
| hb_filter (pinned_axes, hb_second)
| hb_map ([&] (const hb_pair_t<const HBFixed&, unsigned>& _)
| hb_map ([&] (const hb_pair_t<const F16DOT16&, unsigned>& _)
{
hb_tag_t axis_tag = pinned_axes.get (_.second);
float location = user_axes_location->get (axis_tag);
@ -321,6 +359,48 @@ struct fvar
}
}
bool subset (hb_subset_context_t *c) const
{
TRACE_SUBSET (this);
unsigned retained_axis_count = c->plan->axes_index_map->get_population ();
if (!retained_axis_count) //all axes are pinned
return_trace (false);
fvar *out = c->serializer->embed (this);
if (unlikely (!out)) return_trace (false);
if (!c->serializer->check_assign (out->axisCount, retained_axis_count, HB_SERIALIZE_ERROR_INT_OVERFLOW))
return_trace (false);
bool has_postscript_nameid = false;
if (instanceSize >= axisCount * 4 + 6)
has_postscript_nameid = true;
if (!c->serializer->check_assign (out->instanceSize, retained_axis_count * 4 + (has_postscript_nameid ? 6 : 4),
HB_SERIALIZE_ERROR_INT_OVERFLOW))
return_trace (false);
auto axes_records = get_axes ();
for (unsigned i = 0 ; i < (unsigned)axisCount; i++)
{
if (!c->plan->axes_index_map->has (i)) continue;
if (unlikely (!c->serializer->embed (axes_records[i])))
return_trace (false);
}
if (!c->serializer->check_assign (out->firstAxis, get_size (), HB_SERIALIZE_ERROR_INT_OVERFLOW))
return_trace (false);
for (unsigned i = 0 ; i < (unsigned)instanceCount; i++)
{
const InstanceRecord *instance = get_instance (i);
auto snap = c->serializer->snapshot ();
if (!instance->subset (c, axisCount, has_postscript_nameid))
c->serializer->revert (snap);
}
return_trace (true);
}
public:
hb_array_t<const AxisRecord> get_axes () const
{ return hb_array (&(this+firstAxis), axisCount); }
@ -346,8 +426,8 @@ struct fvar
HBUINT16 instanceCount; /* The number of named instances defined in the font
* (the number of records in the instances array). */
HBUINT16 instanceSize; /* The size in bytes of each InstanceRecord — set
* to either axisCount * sizeof(HBFixed) + 4, or to
* axisCount * sizeof(HBFixed) + 6. */
* to either axisCount * sizeof(F16DOT16) + 4, or to
* axisCount * sizeof(F16DOT16) + 6. */
public:
DEFINE_SIZE_STATIC (16);

View File

@ -209,7 +209,7 @@ bool _try_isolating_subgraphs (const hb_vector_t<graph::overflow_record_t>& over
// Only move at most half of the roots in a space at a time.
unsigned extra = roots_to_isolate.get_population () - maximum_to_move;
while (extra--) {
unsigned root = HB_SET_VALUE_INVALID;
uint32_t root = HB_SET_VALUE_INVALID;
roots_to_isolate.previous (&root);
roots_to_isolate.del (root);
}
@ -244,7 +244,7 @@ bool _process_overflows (const hb_vector_t<graph::overflow_record_t>& overflows,
{
// The child object is shared, we may be able to eliminate the overflow
// by duplicating it.
if (!sorted_graph.duplicate (r.parent, r.child)) continue;
if (sorted_graph.duplicate (r.parent, r.child) == (unsigned) -1) continue;
return true;
}

View File

@ -0,0 +1,77 @@
/*
* Copyright © 2022 Google, Inc.
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Google Author(s): Garret Rieger
*/
#ifndef HB_SUBSET_ACCELERATOR_HH
#define HB_SUBSET_ACCELERATOR_HH
#include "hb.hh"
#include "hb-map.hh"
#include "hb-set.hh"
extern HB_INTERNAL hb_user_data_key_t _hb_subset_accelerator_user_data_key;
struct hb_subset_accelerator_t
{
static hb_user_data_key_t* user_data_key()
{
return &_hb_subset_accelerator_user_data_key;
}
static hb_subset_accelerator_t* create(const hb_map_t& unicode_to_gid_,
const hb_set_t& unicodes_) {
hb_subset_accelerator_t* accel =
(hb_subset_accelerator_t*) hb_malloc (sizeof(hb_subset_accelerator_t));
new (accel) hb_subset_accelerator_t (unicode_to_gid_, unicodes_);
return accel;
}
static void destroy(void* value) {
if (!value) return;
hb_subset_accelerator_t* accel = (hb_subset_accelerator_t*) value;
accel->~hb_subset_accelerator_t ();
hb_free (accel);
}
hb_subset_accelerator_t(const hb_map_t& unicode_to_gid_,
const hb_set_t& unicodes_)
: unicode_to_gid(unicode_to_gid_), unicodes(unicodes_) {}
const hb_map_t unicode_to_gid;
const hb_set_t unicodes;
// TODO(garretrieger): cumulative glyf checksum map
// TODO(garretrieger): sanitized table cache.
bool in_error () const
{
return unicode_to_gid.in_error() || unicodes.in_error ();
}
};
#endif /* HB_SUBSET_ACCELERATOR_HH */

View File

@ -89,7 +89,6 @@ hb_subset_input_create_or_fail (void)
hb_tag_t default_no_subset_tables[] = {
HB_TAG ('a', 'v', 'a', 'r'),
HB_TAG ('f', 'v', 'a', 'r'),
HB_TAG ('g', 'a', 's', 'p'),
HB_TAG ('c', 'v', 't', ' '),
HB_TAG ('f', 'p', 'g', 'm'),
@ -391,9 +390,9 @@ hb_subset_input_get_user_data (const hb_subset_input_t *input,
*
* Return value: `true` if success, `false` otherwise
*
* Since: REPLACEME
* Since: EXPERIMENTAL
**/
hb_bool_t
HB_EXTERN hb_bool_t
hb_subset_input_pin_axis_to_default (hb_subset_input_t *input,
hb_face_t *face,
hb_tag_t axis_tag)
@ -415,9 +414,9 @@ hb_subset_input_pin_axis_to_default (hb_subset_input_t *input,
*
* Return value: `true` if success, `false` otherwise
*
* Since: REPLACEME
* Since: EXPERIMENTAL
**/
hb_bool_t
HB_EXTERN hb_bool_t
hb_subset_input_pin_axis_location (hb_subset_input_t *input,
hb_face_t *face,
hb_tag_t axis_tag,
@ -432,3 +431,51 @@ hb_subset_input_pin_axis_location (hb_subset_input_t *input,
}
#endif
#endif
#ifdef HB_EXPERIMENTAL_API
/**
* hb_subset_preprocess
* @input: a #hb_face_t object.
*
* Preprocesses the face and attaches data that will be needed by the
* subsetter. Future subsetting operations can then use the precomputed data
* to speed up the subsetting operation.
*
* Since: EXPERIMENTAL
**/
HB_EXTERN hb_face_t *
hb_subset_preprocess (hb_face_t *source)
{
hb_subset_input_t* input = hb_subset_input_create_or_fail ();
hb_set_clear (hb_subset_input_set(input, HB_SUBSET_SETS_UNICODE));
hb_set_invert (hb_subset_input_set(input, HB_SUBSET_SETS_UNICODE));
hb_set_clear (hb_subset_input_set(input,
HB_SUBSET_SETS_LAYOUT_FEATURE_TAG));
hb_set_invert (hb_subset_input_set(input,
HB_SUBSET_SETS_LAYOUT_FEATURE_TAG));
hb_set_clear (hb_subset_input_set(input,
HB_SUBSET_SETS_LAYOUT_SCRIPT_TAG));
hb_set_invert (hb_subset_input_set(input,
HB_SUBSET_SETS_LAYOUT_SCRIPT_TAG));
hb_set_clear (hb_subset_input_set(input,
HB_SUBSET_SETS_NAME_ID));
hb_set_invert (hb_subset_input_set(input,
HB_SUBSET_SETS_NAME_ID));
hb_subset_input_set_flags(input,
HB_SUBSET_FLAGS_NOTDEF_OUTLINE |
HB_SUBSET_FLAGS_GLYPH_NAMES |
HB_SUBSET_FLAGS_RETAIN_GIDS);
input->attach_accelerator_data = true;
hb_face_t* new_source = hb_subset_or_fail (source, input);
hb_subset_input_destroy (input);
return new_source;
}
#endif

View File

@ -59,6 +59,7 @@ struct hb_subset_input_t
};
unsigned flags;
bool attach_accelerator_data = false;
hb_hashmap_t<hb_tag_t, float> *axes_location;
inline unsigned num_sets () const

View File

@ -25,6 +25,7 @@
*/
#include "hb-subset-plan.hh"
#include "hb-subset-accelerator.hh"
#include "hb-map.hh"
#include "hb-set.hh"
@ -129,7 +130,9 @@ template <typename T>
static void _collect_layout_indices (hb_subset_plan_t *plan,
const T& table,
hb_set_t *lookup_indices, /* OUT */
hb_set_t *feature_indices /* OUT */)
hb_set_t *feature_indices, /* OUT */
hb_hashmap_t<unsigned, hb::shared_ptr<hb_set_t>> *feature_record_cond_idx_map, /* OUT */
hb_hashmap_t<unsigned, const OT::Feature*> *feature_substitutes_map /* OUT */)
{
unsigned num_features = table.get_feature_count ();
hb_vector_t<hb_tag_t> features;
@ -154,16 +157,37 @@ static void _collect_layout_indices (hb_subset_plan_t *plan,
retain_all_features ? nullptr : features.arrayZ,
feature_indices);
#ifndef HB_NO_VAR
// collect feature substitutes with variations
if (!plan->user_axes_location->is_empty ())
{
hb_hashmap_t<hb::shared_ptr<hb_map_t>, unsigned> conditionset_map;
OT::hb_collect_feature_substitutes_with_var_context_t c =
{
plan->axes_old_index_tag_map,
plan->axes_location,
feature_record_cond_idx_map,
feature_substitutes_map,
feature_indices,
true,
0,
&conditionset_map
};
table.collect_feature_substitutes_with_variations (&c);
}
#endif
for (unsigned feature_index : *feature_indices)
{
//TODO: replace HB_OT_LAYOUT_NO_VARIATIONS_INDEX with variation_index for
//instancing
const OT::Feature &f = table.get_feature_variation (feature_index, HB_OT_LAYOUT_NO_VARIATIONS_INDEX);
f.add_lookup_indexes_to (lookup_indices);
const OT::Feature* f = &(table.get_feature (feature_index));
const OT::Feature **p = nullptr;
if (feature_substitutes_map->has (feature_index, &p))
f = *p;
f->add_lookup_indexes_to (lookup_indices);
}
//TODO: update for instancing: only collect lookups from feature_indexes that have no variations
table.feature_variation_collect_lookups (feature_indices, lookup_indices);
table.feature_variation_collect_lookups (feature_indices, feature_substitutes_map, lookup_indices);
}
@ -171,6 +195,7 @@ static inline void
_GSUBGPOS_find_duplicate_features (const OT::GSUBGPOS &g,
const hb_map_t *lookup_indices,
const hb_set_t *feature_indices,
const hb_hashmap_t<unsigned, const OT::Feature*> *feature_substitutes_map,
hb_map_t *duplicate_feature_map /* OUT */)
{
if (feature_indices->is_empty ()) return;
@ -195,16 +220,22 @@ _GSUBGPOS_find_duplicate_features (const OT::GSUBGPOS &g,
hb_set_t* same_tag_features = unique_features.get (t);
for (unsigned other_f_index : same_tag_features->iter ())
{
const OT::Feature& f = g.get_feature (i);
const OT::Feature& other_f = g.get_feature (other_f_index);
const OT::Feature* f = &(g.get_feature (i));
const OT::Feature **p = nullptr;
if (feature_substitutes_map->has (i, &p))
f = *p;
const OT::Feature* other_f = &(g.get_feature (other_f_index));
if (feature_substitutes_map->has (other_f_index, &p))
f = *p;
auto f_iter =
+ hb_iter (f.lookupIndex)
+ hb_iter (f->lookupIndex)
| hb_filter (lookup_indices)
;
auto other_f_iter =
+ hb_iter (other_f.lookupIndex)
+ hb_iter (other_f->lookupIndex)
| hb_filter (lookup_indices)
;
@ -237,7 +268,9 @@ _closure_glyphs_lookups_features (hb_subset_plan_t *plan,
hb_set_t *gids_to_retain,
hb_map_t *lookups,
hb_map_t *features,
script_langsys_map *langsys_map)
script_langsys_map *langsys_map,
hb_hashmap_t<unsigned, hb::shared_ptr<hb_set_t>> *feature_record_cond_idx_map,
hb_hashmap_t<unsigned, const OT::Feature*> *feature_substitutes_map)
{
hb_blob_ptr_t<T> table = plan->source_table<T> ();
hb_tag_t table_tag = table->tableTag;
@ -245,7 +278,9 @@ _closure_glyphs_lookups_features (hb_subset_plan_t *plan,
_collect_layout_indices<T> (plan,
*table,
&lookup_indices,
&feature_indices);
&feature_indices,
feature_record_cond_idx_map,
feature_substitutes_map);
if (table_tag == HB_OT_TAG_GSUB)
hb_ot_layout_lookups_substitute_closure (plan->source,
@ -257,9 +292,12 @@ _closure_glyphs_lookups_features (hb_subset_plan_t *plan,
_remap_indexes (&lookup_indices, lookups);
// prune features
table->prune_features (lookups, &feature_indices);
table->prune_features (lookups,
plan->user_axes_location->is_empty () ? nullptr : feature_record_cond_idx_map,
feature_substitutes_map,
&feature_indices);
hb_map_t duplicate_feature_map;
_GSUBGPOS_find_duplicate_features (*table, lookups, &feature_indices, &duplicate_feature_map);
_GSUBGPOS_find_duplicate_features (*table, lookups, &feature_indices, feature_substitutes_map, &duplicate_feature_map);
feature_indices.clear ();
table->prune_langsys (&duplicate_feature_map, plan->layout_scripts, langsys_map, &feature_indices);
@ -419,14 +457,19 @@ _populate_unicodes_to_retain (const hb_set_t *unicodes,
hb_subset_plan_t *plan)
{
OT::cmap::accelerator_t cmap (plan->source);
unsigned size_threshold = plan->source->get_num_glyphs ();
if (glyphs->is_empty () && unicodes->get_population () < size_threshold)
{
const hb_map_t* unicode_to_gid = nullptr;
if (plan->accelerator)
unicode_to_gid = &plan->accelerator->unicode_to_gid;
// This is approach to collection is faster, but can only be used if glyphs
// are not being explicitly added to the subset and the input unicodes set is
// not excessively large (eg. an inverted set).
plan->unicode_to_new_gid_list.alloc (unicodes->get_population ());
if (!unicode_to_gid) {
for (hb_codepoint_t cp : *unicodes)
{
hb_codepoint_t gid;
@ -439,21 +482,48 @@ _populate_unicodes_to_retain (const hb_set_t *unicodes,
plan->codepoint_to_glyph->set (cp, gid);
plan->unicode_to_new_gid_list.push (hb_pair (cp, gid));
}
} else {
// Use in memory unicode to gid map it's faster then looking up from
// the map. This code is mostly duplicated from above to avoid doing
// conditionals on the presence of the unicode_to_gid map each
// iteration.
for (hb_codepoint_t cp : *unicodes)
{
hb_codepoint_t gid = unicode_to_gid->get (cp);
if (gid == HB_MAP_VALUE_INVALID)
{
DEBUG_MSG(SUBSET, nullptr, "Drop U+%04X; no gid", cp);
continue;
}
plan->codepoint_to_glyph->set (cp, gid);
plan->unicode_to_new_gid_list.push (hb_pair (cp, gid));
}
}
}
else
{
// This approach is slower, but can handle adding in glyphs to the subset and will match
// them with cmap entries.
hb_map_t unicode_glyphid_map;
hb_set_t cmap_unicodes;
cmap.collect_mapping (&cmap_unicodes, &unicode_glyphid_map);
hb_map_t unicode_glyphid_map_storage;
hb_set_t cmap_unicodes_storage;
const hb_map_t* unicode_glyphid_map = &unicode_glyphid_map_storage;
const hb_set_t* cmap_unicodes = &cmap_unicodes_storage;
if (!plan->accelerator) {
cmap.collect_mapping (&cmap_unicodes_storage, &unicode_glyphid_map_storage);
plan->unicode_to_new_gid_list.alloc (hb_min(unicodes->get_population ()
+ glyphs->get_population (),
cmap_unicodes.get_population ()));
cmap_unicodes->get_population ()));
} else {
unicode_glyphid_map = &plan->accelerator->unicode_to_gid;
cmap_unicodes = &plan->accelerator->unicodes;
}
for (hb_codepoint_t cp : cmap_unicodes)
for (hb_codepoint_t cp : *cmap_unicodes)
{
hb_codepoint_t gid = unicode_glyphid_map[cp];
hb_codepoint_t gid = (*unicode_glyphid_map)[cp];
if (!unicodes->has (cp) && !glyphs->has (gid))
continue;
@ -509,9 +579,7 @@ _glyf_add_gid_and_children (const OT::glyf_accelerator_t &glyf,
static void
_populate_gids_to_retain (hb_subset_plan_t* plan,
bool close_over_gsub,
bool close_over_gpos,
bool close_over_gdef)
hb_set_t* drop_tables)
{
OT::glyf_accelerator_t glyf (plan->source);
#ifndef HB_NO_SUBSET_CFF
@ -523,32 +591,42 @@ _populate_gids_to_retain (hb_subset_plan_t* plan,
_cmap_closure (plan->source, plan->unicodes, plan->_glyphset_gsub);
#ifndef HB_NO_SUBSET_LAYOUT
if (close_over_gsub)
if (!drop_tables->has (HB_OT_TAG_GSUB))
// closure all glyphs/lookups/features needed for GSUB substitutions.
_closure_glyphs_lookups_features<GSUB> (
plan,
plan->_glyphset_gsub,
plan->gsub_lookups,
plan->gsub_features,
plan->gsub_langsys);
plan->gsub_langsys,
plan->gsub_feature_record_cond_idx_map,
plan->gsub_feature_substitutes_map);
if (close_over_gpos)
if (!drop_tables->has (HB_OT_TAG_GPOS))
_closure_glyphs_lookups_features<GPOS> (
plan,
plan->_glyphset_gsub,
plan->gpos_lookups,
plan->gpos_features,
plan->gpos_langsys);
plan->gpos_langsys,
plan->gpos_feature_record_cond_idx_map,
plan->gpos_feature_substitutes_map);
#endif
_remove_invalid_gids (plan->_glyphset_gsub, plan->source->get_num_glyphs ());
hb_set_set (plan->_glyphset_mathed, plan->_glyphset_gsub);
if (!drop_tables->has (HB_OT_TAG_MATH))
{
_math_closure (plan, plan->_glyphset_mathed);
_remove_invalid_gids (plan->_glyphset_mathed, plan->source->get_num_glyphs ());
}
hb_set_t cur_glyphset = *plan->_glyphset_mathed;
if (!drop_tables->has (HB_OT_TAG_COLR))
{
_colr_closure (plan->source, plan->colrv1_layers, plan->colr_palettes, &cur_glyphset);
_remove_invalid_gids (&cur_glyphset, plan->source->get_num_glyphs ());
}
hb_set_set (plan->_glyphset_colred, &cur_glyphset);
@ -570,7 +648,7 @@ _populate_gids_to_retain (hb_subset_plan_t* plan,
#ifndef HB_NO_VAR
if (close_over_gdef)
if (!drop_tables->has (HB_OT_TAG_GDEF))
_collect_layout_variation_indices (plan);
#endif
}
@ -659,18 +737,22 @@ _normalize_axes_location (hb_face_t *face, hb_subset_plan_t *plan)
seg_maps = face->table.avar->get_segment_maps ();
bool axis_not_pinned = false;
unsigned axis_count = 0;
unsigned old_axis_idx = 0, new_axis_idx = 0;
for (const auto& axis : axes)
{
hb_tag_t axis_tag = axis.get_axis_tag ();
plan->axes_old_index_tag_map->set (old_axis_idx, axis_tag);
if (!plan->user_axes_location->has (axis_tag))
{
axis_not_pinned = true;
plan->axes_index_map->set (old_axis_idx, new_axis_idx);
new_axis_idx++;
}
else
{
int normalized_v = axis.normalize_axis_value (plan->user_axes_location->get (axis_tag));
if (has_avar && axis_count < face->table.avar->get_axis_count ())
if (has_avar && old_axis_idx < face->table.avar->get_axis_count ())
{
normalized_v = seg_maps->map (normalized_v);
}
@ -681,7 +763,7 @@ _normalize_axes_location (hb_face_t *face, hb_subset_plan_t *plan)
if (has_avar)
seg_maps = &StructAfter<OT::SegmentMaps> (*seg_maps);
axis_count++;
old_axis_idx++;
}
plan->all_axes_pinned = !axis_not_pinned;
}
@ -741,6 +823,13 @@ hb_subset_plan_create_or_fail (hb_face_t *face,
plan->gsub_features = hb_map_create ();
plan->gpos_features = hb_map_create ();
plan->check_success (plan->gsub_feature_record_cond_idx_map = hb_hashmap_create<unsigned, hb::shared_ptr<hb_set_t>> ());
plan->check_success (plan->gpos_feature_record_cond_idx_map = hb_hashmap_create<unsigned, hb::shared_ptr<hb_set_t>> ());
plan->check_success (plan->gsub_feature_substitutes_map = hb_hashmap_create<unsigned, const OT::Feature*> ());
plan->check_success (plan->gpos_feature_substitutes_map = hb_hashmap_create<unsigned, const OT::Feature*> ());
plan->colrv1_layers = hb_map_create ();
plan->colr_palettes = hb_map_create ();
plan->check_success (plan->layout_variation_idx_delta_map = hb_hashmap_create<unsigned, hb_pair_t<unsigned, int>> ());
@ -751,12 +840,21 @@ hb_subset_plan_create_or_fail (hb_face_t *face,
plan->check_success (plan->user_axes_location = hb_hashmap_create<hb_tag_t, float> ());
if (plan->user_axes_location && input->axes_location)
*plan->user_axes_location = *input->axes_location;
plan->check_success (plan->axes_index_map = hb_map_create ());
plan->check_success (plan->axes_old_index_tag_map = hb_map_create ());
plan->all_axes_pinned = false;
plan->pinned_at_default = true;
plan->check_success (plan->vmtx_map = hb_hashmap_create<unsigned, hb_pair_t<unsigned, int>> ());
plan->check_success (plan->hmtx_map = hb_hashmap_create<unsigned, hb_pair_t<unsigned, int>> ());
void* accel = hb_face_get_user_data(face, hb_subset_accelerator_t::user_data_key());
plan->attach_accelerator_data = input->attach_accelerator_data;
if (accel)
plan->accelerator = (hb_subset_accelerator_t*) accel;
if (unlikely (plan->in_error ())) {
hb_subset_plan_destroy (plan);
return nullptr;
@ -768,10 +866,7 @@ hb_subset_plan_create_or_fail (hb_face_t *face,
_populate_unicodes_to_retain (input->sets.unicodes, input->sets.glyphs, plan);
_populate_gids_to_retain (plan,
!input->sets.drop_tables->has (HB_OT_TAG_GSUB),
!input->sets.drop_tables->has (HB_OT_TAG_GPOS),
!input->sets.drop_tables->has (HB_OT_TAG_GDEF));
_populate_gids_to_retain (plan, input->sets.drop_tables);
_create_old_gid_to_new_gid_map (face,
input->flags & HB_SUBSET_FLAGS_RETAIN_GIDS,

View File

@ -31,11 +31,16 @@
#include "hb-subset.h"
#include "hb-subset-input.hh"
#include "hb-subset-accelerator.hh"
#include "hb-map.hh"
#include "hb-bimap.hh"
#include "hb-set.hh"
namespace OT {
struct Feature;
}
struct hb_subset_plan_t
{
hb_subset_plan_t ()
@ -67,9 +72,15 @@ struct hb_subset_plan_t
hb_map_destroy (gpos_features);
hb_map_destroy (colrv1_layers);
hb_map_destroy (colr_palettes);
hb_map_destroy (axes_index_map);
hb_map_destroy (axes_old_index_tag_map);
hb_hashmap_destroy (gsub_langsys);
hb_hashmap_destroy (gpos_langsys);
hb_hashmap_destroy (gsub_feature_record_cond_idx_map);
hb_hashmap_destroy (gpos_feature_record_cond_idx_map);
hb_hashmap_destroy (gsub_feature_substitutes_map);
hb_hashmap_destroy (gpos_feature_substitutes_map);
hb_hashmap_destroy (axes_location);
hb_hashmap_destroy (sanitized_table_cache);
hb_hashmap_destroy (hmtx_map);
@ -87,6 +98,7 @@ struct hb_subset_plan_t
bool successful;
unsigned flags;
bool attach_accelerator_data = false;
// For each cp that we'd like to retain maps to the corresponding gid.
hb_set_t *unicodes;
@ -143,6 +155,15 @@ struct hb_subset_plan_t
hb_map_t *gsub_features;
hb_map_t *gpos_features;
//active feature variation records/condition index with variations
hb_hashmap_t<unsigned, hb::shared_ptr<hb_set_t>> *gsub_feature_record_cond_idx_map;
hb_hashmap_t<unsigned, hb::shared_ptr<hb_set_t>> *gpos_feature_record_cond_idx_map;
//feature index-> address of substituation feature table mapping with
//variations
hb_hashmap_t<unsigned, const OT::Feature*> *gsub_feature_substitutes_map;
hb_hashmap_t<unsigned, const OT::Feature*> *gpos_feature_substitutes_map;
//active layers/palettes we'd like to retain
hb_map_t *colrv1_layers;
hb_map_t *colr_palettes;
@ -158,6 +179,10 @@ struct hb_subset_plan_t
hb_hashmap_t<hb_tag_t, int> *axes_location;
//user specified axes location map
hb_hashmap_t<hb_tag_t, float> *user_axes_location;
//retained old axis index -> new axis index mapping in fvar axis array
hb_map_t *axes_index_map;
//axis_index->axis_tag mapping in fvar axis array
hb_map_t *axes_old_index_tag_map;
bool all_axes_pinned;
bool pinned_at_default;
@ -166,6 +191,8 @@ struct hb_subset_plan_t
//vmtx metrics map: new gid->(advance, lsb)
hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *vmtx_map;
const hb_subset_accelerator_t* accelerator;
public:
template<typename T>

View File

@ -50,11 +50,13 @@
#include "hb-ot-color-cbdt-table.hh"
#include "hb-ot-layout-gsub-table.hh"
#include "hb-ot-layout-gpos-table.hh"
#include "hb-ot-var-fvar-table.hh"
#include "hb-ot-var-gvar-table.hh"
#include "hb-ot-var-hvar-table.hh"
#include "hb-ot-math-table.hh"
#include "hb-ot-stat-table.hh"
#include "hb-repacker.hh"
#include "hb-subset-accelerator.hh"
using OT::Layout::GSUB;
using OT::Layout::GPOS;
@ -80,6 +82,10 @@ using OT::Layout::GPOS;
* retain glyph ids option and configure the subset to pass through the layout tables untouched.
*/
hb_user_data_key_t _hb_subset_accelerator_user_data_key = {};
/*
* The list of tables in the open type spec. Used to check for tables that may need handling
* if we are unable to list the tables in a face.
@ -475,6 +481,9 @@ _subset_table (hb_subset_plan_t *plan,
case HB_OT_TAG_HVAR: return _subset<const OT::HVAR> (plan, buf);
case HB_OT_TAG_VVAR: return _subset<const OT::VVAR> (plan, buf);
#endif
case HB_OT_TAG_fvar:
if (plan->user_axes_location->is_empty ()) return _passthrough (plan, tag);
return _subset<const OT::fvar> (plan, buf);
case HB_OT_TAG_STAT:
/*TODO(qxliu): change the condition as we support more complex
* instancing operation*/
@ -490,6 +499,27 @@ _subset_table (hb_subset_plan_t *plan,
}
}
static void _attach_accelerator_data (const hb_subset_plan_t* plan,
hb_face_t* face /* IN/OUT */)
{
hb_subset_accelerator_t* accel =
hb_subset_accelerator_t::create (*plan->codepoint_to_glyph,
*plan->unicodes);
if (accel->in_error ())
{
hb_subset_accelerator_t::destroy (accel);
return;
}
if (!hb_face_set_user_data(face,
hb_subset_accelerator_t::user_data_key(),
accel,
hb_subset_accelerator_t::destroy,
true))
hb_subset_accelerator_t::destroy (accel);
}
/**
* hb_subset_or_fail:
* @source: font face data to be subset.
@ -572,6 +602,10 @@ hb_subset_plan_execute_or_fail (hb_subset_plan_t *plan)
offset += num_tables;
}
if (success && plan->attach_accelerator_data) {
_attach_accelerator_data (plan, plan->dest);
}
end:
return success ? hb_face_reference (plan->dest) : nullptr;
}

View File

@ -70,6 +70,14 @@ typedef struct hb_subset_plan_t hb_subset_plan_t;
* in the final subset.
* @HB_SUBSET_FLAGS_NO_PRUNE_UNICODE_RANGES: If set then the unicode ranges in
* OS/2 will not be recalculated.
* @HB_SUBSET_FLAGS_PATCH_MODE: If set the subsetter behaviour will be modified
* to produce a subset that is better suited to patching. For example cmap
* subtable format will be kept stable.
* @HB_SUBSET_FLAGS_OMIT_GLYF: If set the subsetter won't actually produce the final
* glyf table bytes. The table directory will include and entry as if the table was
* there but the actual final font blob will be truncated prior to the glyf data. This
* is a useful performance optimization when a font aware binary patching algorithm
* is being used to diff two subsets.
*
* List of boolean properties that can be configured on the subset input.
*
@ -86,6 +94,8 @@ typedef enum { /*< flags >*/
HB_SUBSET_FLAGS_NOTDEF_OUTLINE = 0x00000040u,
HB_SUBSET_FLAGS_GLYPH_NAMES = 0x00000080u,
HB_SUBSET_FLAGS_NO_PRUNE_UNICODE_RANGES = 0x00000100u,
// Not supported yet: HB_SUBSET_FLAGS_PATCH_MODE = 0x00000200u,
// Not supported yet: HB_SUBSET_FLAGS_OMIT_GLYF = 0x00000400u,
} hb_subset_flags_t;
/**
@ -169,6 +179,13 @@ hb_subset_input_pin_axis_location (hb_subset_input_t *input,
#endif
#endif
#ifdef HB_EXPERIMENTAL_API
HB_EXTERN hb_face_t *
hb_subset_preprocess (hb_face_t *source);
#endif
HB_EXTERN hb_face_t *
hb_subset_or_fail (hb_face_t *source, const hb_subset_input_t *input);

View File

@ -47,20 +47,20 @@ HB_BEGIN_DECLS
*
* The minor component of the library version available at compile-time.
*/
#define HB_VERSION_MINOR 1
#define HB_VERSION_MINOR 3
/**
* HB_VERSION_MICRO:
*
* The micro component of the library version available at compile-time.
*/
#define HB_VERSION_MICRO 0
#define HB_VERSION_MICRO 1
/**
* HB_VERSION_STRING:
*
* A string literal containing the library version available at compile-time.
*/
#define HB_VERSION_STRING "5.1.0"
#define HB_VERSION_STRING "5.3.1"
/**
* HB_VERSION_ATLEAST:

View File

@ -334,6 +334,7 @@ hb_subset_sources = files(
'hb-ot-cff1-table.cc',
'hb-ot-cff2-table.cc',
'hb-static.cc',
'hb-subset-accelerator.hh',
'hb-subset-cff-common.cc',
'hb-subset-cff-common.hh',
'hb-subset-cff1.cc',
@ -656,8 +657,8 @@ endif
have_gobject = conf.get('HAVE_GOBJECT', 0) == 1
cmake_config = configuration_data()
cmake_config.set('libdir', '${prefix}/@0@'.format(get_option('libdir')))
cmake_config.set('includedir', '${prefix}/@0@'.format(get_option('includedir')))
cmake_config.set('libdir', get_option('prefix') / get_option('libdir'))
cmake_config.set('includedir', get_option('prefix') / get_option('includedir'))
cmake_config.set('HB_LIBTOOL_VERSION_INFO', hb_libtool_version_info)
cmake_config.set('have_gobject', '@0@'.format(have_gobject))
configure_file(input: 'harfbuzz-config.cmake.in',

View File

@ -7,6 +7,7 @@
# Updated for Unicode 12.1 by Andrew Glass 2019-05-30
# Updated for Unicode 13.0 by Andrew Glass 2020-07-28
# Updated for Unicode 14.0 by Andrew Glass 2021-09-28
# Updated for Unicode 15.0 by Andrew Glass 2022-09-16
# ================================================
# ================================================
@ -19,9 +20,12 @@
0F7A..0F7D ; Bottom # Mn [4] TIBETAN VOWEL SIGN E..TIBETAN VOWEL SIGN OO # Not really below, but need to override to fit into Universal model
0F80 ; Bottom # Mn TIBETAN VOWEL SIGN REVERSED I # Not really below, but need to override to fit into Universal model
A9BF ; Bottom # Mc JAVANESE CONSONANT SIGN CAKRA
10A38 ; Bottom # Mn KHAROSHTHI SIGN BAR ABOVE # Overriden, ccc controls order USE issue #26
11127..11129 ; Bottom # Mn [3] CHAKMA VOWEL SIGN A..CHAKMA VOWEL SIGN II
1112D ; Bottom # Mn CHAKMA VOWEL SIGN AI
11130 ; Bottom # Mn CHAKMA VOWEL SIGN OI
1BF2..1BF3 ; Bottom # Mc [2] BATAK PANGOLAT..BATAK PANONGONAN # see USE issue #20
# ================================================
@ -42,12 +46,15 @@ A9BE ; Right # Mc JAVANESE CONSONANT SIGN PENGKAL # Reduced from
0F74 ; Top # Mn TIBETAN VOWEL SIGN U # Not really above, but need to override to fit into Universal model
1A18 ; Top # Mn BUGINESE VOWEL SIGN U # Workaround to allow below to occur before above by treating all below marks as above
AA35   ; Top # Mn       CHAM CONSONANT SIGN
1112A..1112B ; Top # Mn [2] CHAKMA VOWEL SIGN U..CHAKMA VOWEL SIGN UU # see USE issue #25
11131..11132 ; Top # Mn [2] CHAKMA O MARK..CHAKMA AU MARK # see USE issue #25
1E4EC..1E4EF ; Top # Mn [4] NAG MUNDARI SIGN MUHOR..NAG MUNDARI SIGN SUTUH # 1E4EE is below, but made to for ccc
# ================================================
# Indic_Positional_Category=Top_And_Right
0E33 ; Top_And_Right # Lo THAI CHARACTER SARA AM # IMC has Right, which seems to be a mistake.
0EB3 ; Top_And_Right # Lo LAO VOWEL SIGN AM # IMC has Right, which seems to be a mistake.
0E33 ; Top_And_Right # Lo THAI CHARACTER SARA AM # IPC has Right, which seems to be a mistake.
0EB3 ; Top_And_Right # Lo LAO VOWEL SIGN AM # IPC has Right, which seems to be a mistake.
# ================================================
# ================================================
@ -72,6 +79,9 @@ AA35   ; Top # Mn       CHAM CONSONANT SIGN
16F4F ; Bottom # Mn MIAO SIGN CONSONANT MODIFIER BAR
16F51..16F87 ; Bottom # Mc [55] MIAO SIGN ASPIRATION..MIAO VOWEL SIGN UI
16F8F..16F92 ; Bottom # Mn [4] MIAO TONE RIGHT..MIAO TONE BELOW
#HIEROGLYPHS defined here while ISC is being used as a proxy for dedicated Hieroglyph cluster
13440 ; Bottom # Mn EGYPTIAN HIEROGLYPH MIRROR HORIZONTALLY
13447..13455 ; Bottom # Mn [15] EGYPTIAN HIEROGLYPH MODIFIER DAMAGED AT TOP START..EGYPTIAN HIEROGLYPH MODIFIER DAMAGED
# ================================================
@ -84,6 +94,7 @@ AA35   ; Top # Mn       CHAM CONSONANT SIGN
07EB..07F3 ; Top # Mn [9] NKO COMBINING SHORT HIGH TONE..NKO COMBINING DOUBLE DOT ABOVE
07FD ; Top # Mn NKO DANTAYALAN # Not really top, but assigned here to allow ccc to control mark order
1885..1886 ; Top # Mn [2] MONGOLIAN LETTER ALI GALI BALUDA..MONGOLIAN LETTER ALI GALI THREE BALUDA
1CF8..1CF9 ; Top # Mn [2] VEDIC TONE RING ABOVE..VEDIC TONE DOUBLE RING ABOVE
10D24..10D27 ; Top # Mn [4] HANIFI ROHINGYA SIGN HARBAHAY..HANIFI ROHINGYA SIGN TASSI
10EAB..10EAC ; Top # Mn [2] YEZIDI COMBINING HAMZA MARK..YEZIDI COMBINING MADDA MARK
16B30..16B36 ; Top # Mn [7] PAHAWH HMONG MARK CIM TUB..PAHAWH HMONG MARK CIM TAUM

View File

@ -89,6 +89,14 @@
11686 116B2 ; # TAKRI LETTER E, TAKRI VOWEL SIGN E
11680 116B4 ; # TAKRI LETTER A, TAKRI VOWEL SIGN O
11680 116B5 ; # TAKRI LETTER A, TAKRI VOWEL SIGN AU
11200 1122C ; # KHOJKI LETTER A, KHOJKI VOWEL SIGN AA
11240 1122E ; # KHOJKI LETTER SHORT I, KHOJKI VOWEL SIGN II
11206 1122C ; # KHOJKI LETTER O, KHOJKI VOWEL SIGN AA
11200 11231 ; # KHOJKI LETTER A, KHOJKI VOWEL SIGN AI
11200 11233 ; # KHOJKI LETTER A, KHOJKI VOWEL SIGN AU
11200 1122C 11231 ; # KHOJKI LETTER A, KHOJKI VOWEL SIGN AA, KHOJKI VOWEL SIGN AI
1122C 11230 ; # KHOJKI VOWEL SIGN AA, KHOJKI VOWEL SIGN E
1122C 11231 ; # KHOJKI VOWEL SIGN AA, KHOJKI VOWEL SIGN AI
112B0 112E0 ; # KHUDAWADI LETTER A, KHUDAWADI VOWEL SIGN AA
112B0 112E5 ; # KHUDAWADI LETTER A, KHUDAWADI VOWEL SIGN E
112B0 112E6 ; # KHUDAWADI LETTER A, KHUDAWADI VOWEL SIGN AI

View File

@ -5,6 +5,7 @@
# Updated for Unicode 12.1 by Andrew Glass 2019-05-24
# Updated for Unicode 13.0 by Andrew Glass 2020-07-28
# Updated for Unicode 14.0 by Andrew Glass 2021-09-25
# Updated for Unicode 15.0 by Andrew Glass 2022-09-16
# ================================================
# OVERRIDES TO ASSIGNED VALUES
@ -18,23 +19,13 @@ AA29 ; Bindu # Mn  CHAM VOWEL SIGN AA
# ================================================
# Indic_Syllabic_Category=Consonant
0840..0858 ; Consonant # Lo [25] MANDAIC LETTER HALQA..MANDAIC LETTER AIN
0F00..0F01 ; Consonant # Lo [2] TIBETAN SYLLABLE OM..TIBETAN MARK GTER YIG MGO TRUNCATED
0F04..0F06 ; Consonant # Po TIBETAN MARK INITIAL YIG MGO MDUN MA..TIBETAN MARK CARET YIG MGO PHUR SHAD MA
19C1..19C7 ; Consonant # Lo [7] NEW TAI LUE LETTER FINAL V..NEW TAI LUE LETTER FINAL B # Reassigned to avoid clustering with a base consonant
25CC ; Consonant # So DOTTED CIRCLE
25CC ; Consonant # So DOTTED CIRCLE #Reassigned to allow it to cluster as a generic base
# ================================================
# Indic_Syllabic_Category=Consonant_Dead
0F7F ; Consonant_Dead # Mc TIBETAN SIGN RNAM BCAD # reassigned so that visarga will form an independent cluster
# ================================================
# Indic_Syllabic_Category=Consonant_Final
0F35 ; Consonant_Final # Mn TIBETAN MARK NGAS BZUNG NYI ZLA
0F37 ; Consonant_Final # Mn TIBETAN MARK NGAS BZUNG SGOR RTAGS
0FC6 ; Consonant_Final # Mn TIBETAN SYMBOL PADMA GDAN
0F7F ; Consonant_Dead # Mc TIBETAN SIGN RNAM BCAD # reassigned so that visarga can form an independent cluster, but see #19
# ================================================
@ -49,8 +40,8 @@ AA29 ; Bindu # Mn  CHAM VOWEL SIGN AA
# ================================================
# Indic_Syllabic_Category=Nukta
0F71 ; Nukta # Mn TIBETAN VOWEL SIGN AA # Reassigned to get this before an above vowel
10A38..10A3A ; Nukta # Mn [3] KHAROSHTHI SIGN BAR ABOVE..KHAROSHTHI SIGN DOT BELOW
0F71 ; Nukta # Mn TIBETAN VOWEL SIGN AA # Reassigned to get this before an above vowel, but see #22
1BF2..1BF3 ; Nukta # Mc [2] BATAK PANGOLAT..BATAK PANONGONAN # see USE issue #20
# ================================================
@ -73,6 +64,9 @@ AABD ; Vowel_Independent # Lo TAI VIET VOWEL AN
# Indic_Syllabic_Category=Consonant
0800..0815 ; Consonant # Lo [22] SAMARITAN LETTER ALAF..SAMARITAN LETTER TAAF
0840..0858 ; Consonant # Lo [25] MANDAIC LETTER HALQA..MANDAIC LETTER AIN
0F00..0F01 ; Consonant # Lo [2] TIBETAN SYLLABLE OM..TIBETAN MARK GTER YIG MGO TRUNCATED
0F04..0F06 ; Consonant # Po TIBETAN MARK INITIAL YIG MGO MDUN MA..TIBETAN MARK CARET YIG MGO PHUR SHAD MA
1800 ; Consonant # Po MONGOLIAN BIRGA # Reassigned so that legacy Birga + MFVS sequences still work
1807 ; Consonant # Po MONGOLIAN SIBE SYLLABLE BOUNDARY MARKER
180A ; Consonant # Po MONGOLIAN NIRUGU
@ -94,11 +88,13 @@ AABD ; Vowel_Independent # Lo TAI VIET VOWEL AN
10E80..10EA9 ; Consonant # Lo [42] YEZIDI LETTER ELIF..YEZIDI LETTER ET
10EB0..10EB1 ; Consonant # Lo [2] YEZIDI LETTER LAM WITH DOT ABOVE..YEZIDI LETTER YOT WITH CIRCUMFLEX ABOVE
10F30..10F45 ; Consonant # Lo [22] SOGDIAN LETTER ALEPH..SOGDIAN INDEPENDENT SHIN
10F70..10F81 ; Consonant # Lo [18] OLD UYGHUR LETTER ALEPH..OLD UYGHUR LETTER LESH
111DA ; Consonant # Lo SHARADA EKAM
#HIEROGLYPHS to be moved to new category
13000..1342E ; Consonant # Lo [1071] EGYPTIAN HIEROGLYPH A001..EGYPTIAN HIEROGLYPH AA032
13000..1342F ; Consonant # Lo [1071] EGYPTIAN HIEROGLYPH A001..EGYPTIAN HIEROGLYPH V011D
#For the Begin and End segment to be handled fully correctly, the cluster model needs to be modified.
13437..13438 ; Consonant # Lo [2] EGYPTIAN HIEROGLYPH BEGIN SEGMENT..EGYPTIAN HIEROGLYPH END SEGMENT
13441..13446 ; Consonant # Lo [6] EGYPTIAN HIEROGLYPH FULL BLANK..HIEROGLYPH WIDE LOST SIGN
16B00..16B2F ; Consonant # Lo [48] PAHAWH HMONG VOWEL KEEB..PAHAWH HMONG CONSONANT CAU
16F00..16F4A ; Consonant # Lo [75] MIAO LETTER PA..MIAO LETTER RTE
16FE4 ; Consonant # Mn KHITAN SMALL SCRIPT FILLER # Avoids Mn pushing this into VOWEL class
@ -113,6 +109,8 @@ AABD ; Vowel_Independent # Lo TAI VIET VOWEL AN
1E14F ; Consonant # So NYIAKENG PUACHUE HMONG CIRCLED CA
1E290..1E2AD ; Consonant # Lo [30] TOTO LETTER PA..TOTO LETTER A
1E2C0..1E2EB ; Consonant # Lo [44] WANCHO LETTER AA..WANCHO LETTER YIH
1E4D0..1E4EA ; Consonant # Lo [27] NAG MUNDARI LETTER O..NAG MUNDARI LETTER ELL
1E4EB ; Consonant # Lm NAG MUNDARI SIGN OJOD
1E900..1E921 ; Consonant # Lu [34] ADLAM CAPITAL LETTER ALIF..ADLAM CAPITAL LETTER SHA
1E922..1E943 ; Consonant # Ll [34] ADLAM SMALL LETTER ALIF..ADLAM SMALL LETTER SHA
1E94B ; Consonant # Lm ADLAM NASALIZATION MARK
@ -140,7 +138,6 @@ FE00..FE0F ; Modifying_Letter # Mn [16] VARIATION SELECTOR-1..VARIATION SEL
0F39 ; Nukta # Mn TIBETAN MARK TSA -PHRU # NOW IN UNICODE 10.0
1885..1886 ; Nukta # Mn [2] MONGOLIAN LETTER ALI GALI BALUDA..MONGOLIAN LETTER ALI GALI THREE BALUDA
18A9 ; Nukta # Mn MONGOLIAN LETTER ALI GALI DAGALGA
1B6B..1B73 ; Nukta # Mn [9] BALINESE MUSICAL SYMBOL COMBINING TEGEH..BALINESE MUSICAL SYMBOL COMBINING GONG
10AE5..10AE6 ; Nukta # Mn [2] MANICHAEAN ABBREVIATION MARK ABOVE..MANICHAEAN ABBREVIATION MARK BELOW
16F4F ; Nukta # Mn MIAO SIGN CONSONANT MODIFIER BAR
1BC9D..1BC9E ; Nukta # Mn [2] DUPLOYAN THICK LETTER SELECTOR..DUPLOYAN DOUBLE MARK
@ -155,6 +152,7 @@ FE00..FE0F ; Modifying_Letter # Mn [16] VARIATION SELECTOR-1..VARIATION SEL
16AC0..16AC9 ; Number # Nd [10] TANGSA DIGIT ZERO..TANGSA DIGIT NINE
1E140..1E149 ; Number # Nd [10] NYIAKENG PUACHUE HMONG DIGIT ZERO..NYIAKENG PUACHUE HMONG DIGIT NINE
1E2F0..1E2F9 ; Number # Nd [10] WANCHO DIGIT ZERO..WANCHO DIGIT NINE
1E4F0..1E4F9 ; Number # Nd [10] NAG MUNDARI DIGIT ZERO..NAG MUNDARI DIGIT NINE
1E950..1E959 ; Number # Nd [10] ADLAM DIGIT ZERO..ADLAM DIGIT NINE
# ================================================
@ -176,7 +174,9 @@ FE00..FE0F ; Modifying_Letter # Mn [16] VARIATION SELECTOR-1..VARIATION SEL
# Indic_Syllabic_Category=Virama
2D7F ; Virama # Mn TIFINAGH CONSONANT JOINER
#HIEROGLYPHS to be moved to new category
13430..13436 ; Virama # Cf [7] EGYPTIAN HIEROGLYPH VERTICAL JOINER..EGYPTIAN HIEROGLYPH OVERLAY MIDDLE
13439..1343B ; Virama # Cf [3] EGYPTIAN HIEROGLYPH INSERT AT MIDDLE..EGYPTIAN HIEROGLYPH INSERT AT BOTTOM
# ================================================
@ -191,6 +191,21 @@ AABD ; Vowel_Independent # Lo TAI VIET VOWEL AN
0B55 ; Vowel_Dependent # Mn ORIYA SIGN OVERLINE
10EAB..10EAC ; Vowel_Dependent # Mn [2] YEZIDI COMBINING HAMZA MARK..YEZIDI COMBINING MADDA MARK
16F51..16F87 ; Vowel_Dependent # Mc [55] MIAO SIGN ASPIRATION..MIAO VOWEL SIGN UI
1E4EC..1E4EF ; Vowel_Dependent # Mn [4] NAG MUNDARI SIGN MUHOR..NAG MUNDARI SIGN SUTUH
# ================================================
# Indic_Syllabic_Category=Cantillation_Mark
1CF8..1CF9 ; Cantillation_Mark # Mn [2] VEDIC TONE RING ABOVE..VEDIC TONE DOUBLE RING ABOVE
#HIEROGLYPHS to be moved to new category
13440 ; Cantillation_Mark # Mn EGYPTIAN HIEROGLYPH MIRROR HORIZONTALLY
13447..13455 ; Cantillation_Mark # Mn [15] EGYPTIAN HIEROGLYPH MODIFIER DAMAGED AT TOP START..EGYPTIAN HIEROGLYPH MODIFIER DAMAGED
# ================================================
# Indic_Syllabic_Category=Symbol_Modifier
1B6B..1B73 ; Symbol_Modifier # Mn [9] BALINESE MUSICAL SYMBOL COMBINING TEGEH..BALINESE MUSICAL SYMBOL COMBINING GONG
# ================================================
# ================================================
@ -198,24 +213,56 @@ AABD ; Vowel_Independent # Lo TAI VIET VOWEL AN
# ================================================
# ================================================
# USE_Syllabic_Category=Hieroglyph
# 13000..1342E ; Hieroglyph # Lo [1071] EGYPTIAN HIEROGLYPH A001..EGYPTIAN HIEROGLYPH AA032
# USE, Extended_Syllabic_Category=Hieroglyph
# 13000..1342F ; Hieroglyph # Lo [1072] EGYPTIAN HIEROGLYPH A001..EGYPTIAN HIEROGLYPH V011D
# 13441..13446 ; Hieroglyph # Lo [6] EGYPTIAN HIEROGLYPH FULL BLANK..HIEROGLYPH WIDE LOST SIGN
# ================================================
# USE_Syllabic_Category=Hieroglyph_Joiner
# 13430..13436 ; Hieroglyph_Joiner # Cf EGYPTIAN HIEROGLYPH VERTICAL JOINER..EGYPTIAN HIEROGLYPH OVERLAY MIDDLE
# USE, Extended_Syllabic_Category=Hieroglyph_Joiner
# 13430..13436 ; Hieroglyph_Joiner # Cf [7] EGYPTIAN HIEROGLYPH VERTICAL JOINER..EGYPTIAN HIEROGLYPH OVERLAY MIDDLE
# 13439..1343B ; Hieroglyph_Joiner # Cf [3] EGYPTIAN HIEROGLYPH INSERT AT MIDDLE..EGYPTIAN HIEROGLYPH INSERT AT BOTTOM
# ================================================
# USE_Syllabic_Category= Hieroglyph_Segment_Begin
# USE, Extended_Syllabic_Category=Hieroglyph_Mark_Begin
# 005B ; Hieroglyph_Mark_Begin # Ps LEFT SQUARE BRACKET
# 007B ; Hieroglyph_Mark_Begin # Ps LEFT CURLY BRACKET
# 27E6 ; Hieroglyph_Mark_Begin # Ps MATHEMATICAL LEFT WHITE SQUARE BRACKET
# 27E8 ; Hieroglyph_Mark_Begin # Ps MATHEMATICAL LEFT ANGLE BRACKET
# 2E22 ; Hieroglyph_Mark_Begin # Ps TOP LEFT HALF BRACKET
# 2E24 ; Hieroglyph_Mark_Begin # Ps BOTTOM LEFT HALF BRACKET
# ================================================
# USE, Extended_Syllabic_Category=Hieroglyph_Mark_End
# 005D ; Hieroglyph_Mark_Begin # Pe RIGHT SQUARE BRACKET
# 007D ; Hieroglyph_Mark_Begin # Pe RIGHT CURLY BRACKET
# 27E7 ; Hieroglyph_Mark_Begin # Pe MATHEMATICAL RIGHT WHITE SQUARE BRACKET
# 27E9 ; Hieroglyph_Mark_Begin # Pe MATHEMATICAL RIGHT ANGLE BRACKET
# 2E23 ; Hieroglyph_Mark_Begin # Pe TOP RIGHT HALF BRACKET
# 2E25 ; Hieroglyph_Mark_Begin # Pe BOTTOM RIGHT HALF BRACKET
# ================================================
# USE, Extended_Syllabic_Category=Hieroglyph_Segment_Begin
# 13437 ; Hieroglyph_Segment_Begin # Cf EGYPTIAN HIEROGLYPH BEGIN SEGMENT
# ================================================
# USE_Syllabic_Category= Hieroglyph_Segment_End
# USE, Extended_Syllabic_Category=Hieroglyph_Segment_End
# 13438 ; Hieroglyph_Segment_End # Cf EGYPTIAN HIEROGLYPH END SEGMENT
# ================================================
# USE, Extended_Syllabic_Category=Hieroglyph_Mirror
# 13440 ; Hieroglyph_Mirror # Mn EGYPTIAN HIEROGLYPH MIRROR HORIZONTALLY
# ================================================
# USE, Extended_Syllabic_Category=Hieroglyph_Modifier
# 13447..13455 ; Hieroglyph_Modifier # Mn [15] EGYPTIAN HIEROGLYPH MODIFIER DAMAGED AT TOP START..EGYPTIAN HIEROGLYPH MODIFIER DAMAGED
# ================================================
# eof

View File

@ -222,6 +222,23 @@ main (int argc, char **argv)
hb::unique_ptr<hb_hashmap_t<int, int>> *v2;
m.has (0, &v2);
}
/* Test hashmap with complex shared_ptrs as keys. */
{
hb_hashmap_t<hb::shared_ptr<hb_map_t>, unsigned> m;
hb_map_t *m1 = hb_map_create ();
hb_map_t *m2 = hb_map_create ();
m1->set (1,3);
m2->set (1,3);
hb::shared_ptr<hb_map_t> p1 {m1};
hb::shared_ptr<hb_map_t> p2 {m2};
m.set (p1,1);
assert (m.has (p2));
m1->set (2,4);
assert (!m.has (p2));
}
return 0;
}

View File

@ -36,7 +36,7 @@ emoji-data.txt:
emoji-test.txt:
curl -O https://www.unicode.org/Public/emoji/latest/emoji-test.txt
languagetags:
curl -O https://docs.microsoft.com/en-us/typography/opentype/spec/languagetags
curl -O https://learn.microsoft.com/en-us/typography/opentype/spec/languagetags
language-subtag-registry:
curl -O https://www.iana.org/assignments/language-subtag-registry/language-subtag-registry
ucd.nounihan.grouped.zip:

View File

@ -129,6 +129,8 @@
/System/Library/Fonts/GeezaPro.ttc@ab26ea45dcaa5e1c5a958e42af10e10d330e7334;--font-funcs ot;U+0631,U+0628;[u0628.beh=1+1415|u0631.reh=0@-202,0+700]
/System/Library/Fonts/GeezaPro.ttc@ab26ea45dcaa5e1c5a958e42af10e10d330e7334;--font-funcs ot;U+0628,U+064F;[u064f.damma=0@250,-250+250|u0628.beh=0@-250,0+1165]
/System/Library/Fonts/GeezaPro.ttc@ab26ea45dcaa5e1c5a958e42af10e10d330e7334;--font-funcs ot;U+0644,U+064E,U+0645,U+064E,U+0651,U+0627;[u0627.final.alef=5+647|u064e.fatha=0@-80,160+-80|u064e_u0651.shaddaFatha=0@490,250+490|u0644_u0645.initial.lamMeem=0@-410,0+415]
/System/Library/Fonts/GeezaPro.ttc@ab26ea45dcaa5e1c5a958e42af10e10d330e7334;--font-funcs ot;U+0644,U+0627;[u0644_u0627.isolated.lamAlef=0+1162]
/System/Library/Fonts/GeezaPro.ttc@ab26ea45dcaa5e1c5a958e42af10e10d330e7334;--font-funcs ot --features rlig=0;U+0644,U+0627;[u0627.final.alef=1+647|u0644.initial.lam=0+515]
# SFNS uses opsz variation axis which isn't invoked here, see https;//crbug.com/1005969#c37
/System/Library/Fonts/SFNS.ttf@253b4b28662acc1de4a86350fd2b26d620ea213c;--font-funcs ot;U+0054,U+0065,U+0020,U+0041,U+0056,U+0020,U+0054,U+0072,U+0020,U+0056,U+0061,U+0020,U+0072,U+0054,U+0020,U+0065,U+0054,U+0020,U+0054,U+0064;[T=0+920|e=1+1049|space=2+420|A=3+1162|V=4+1292|space=5+420|T=6+960|r=7+631|space=8+420|V=9+1142|a=10+1028|space=11+420|r=12+461|T=13+1190|space=14+420|e=15+779|T=16+1190|space=17+420|T=18+920|d=19+1134]
/System/Library/Fonts/SFNS.ttf@253b4b28662acc1de4a86350fd2b26d620ea213c;--font-ptem 9 --font-funcs ot;U+0054,U+0065,U+0020,U+0041,U+0056,U+0020,U+0054,U+0072,U+0020,U+0056,U+0061,U+0020,U+0072,U+0054,U+0020,U+0065,U+0054,U+0020,U+0054,U+0064;[T=0@19,0+958|e=1@19,0+1087|space=2@19,0+458|A=3@19,0+1200|V=4@19,0+1330|space=5@19,0+458|T=6@19,0+998|r=7@19,0+669|space=8@19,0+458|V=9@19,0+1180|a=10@19,0+1066|space=11@19,0+458|r=12@19,0+499|T=13@19,0+1228|space=14@19,0+458|e=15@19,0+817|T=16@19,0+1228|space=17@19,0+458|T=18@19,0+958|d=19@19,0+1172]

View File

@ -39,7 +39,6 @@ EXTRA_DIST += \
expected/layout.gdef-attachlist \
expected/layout.notonastaliqurdu \
expected/layout.tinos \
expected/layout.default_features \
expected/layout.duplicate_features \
expected/layout.unsorted_featurelist \
expected/layout.drop_feature \

View File

@ -38,7 +38,6 @@ TESTS = \
tests/layout.notonastaliqurdu.tests \
tests/layout.tests \
tests/layout.tinos.tests \
tests/layout.default_features.tests \
tests/layout.duplicate_features.tests \
tests/layout.unsorted_featurelist.tests \
tests/layout.drop_feature.tests \

Binary file not shown.

View File

@ -0,0 +1,12 @@
FONTS:
MPLUS1-Variable.ttf
PROFILES:
default.txt
notdef-outline.txt
SUBSETS:
*
INSTANCES:
wght=400

View File

@ -1,11 +0,0 @@
FONTS:
FranklinGothic-Regular.ttf
PROFILES:
layout-test.txt
default.txt
retain-gids.txt
SUBSETS:
achi
*

View File

@ -28,15 +28,7 @@ def strip_check_sum (ttx_string):
def generate_expected_output(input_file, unicodes, profile_flags, instance_flags, output_directory, font_name):
fonttools_path = os.path.join(tempfile.mkdtemp (), font_name)
args = ["fonttools", "subset", input_file]
args.extend(["--drop-tables+=DSIG",
"--drop-tables-=sbix",
"--unicodes=%s" % unicodes,
"--output-file=%s" % fonttools_path])
args.extend(profile_flags)
check_call(args)
input_path = input_file
if instance_flags:
instance_path = os.path.join(tempfile.mkdtemp (), font_name)
args = ["fonttools", "varLib.instancer",
@ -44,10 +36,19 @@ def generate_expected_output(input_file, unicodes, profile_flags, instance_flags
"--no-recalc-bounds",
"--no-recalc-timestamp",
"--output=%s" % instance_path,
fonttools_path]
input_file]
args.extend(instance_flags)
check_call(args)
fonttools_path = instance_path
input_path = instance_path
fonttools_path = os.path.join(tempfile.mkdtemp (), font_name)
args = ["fonttools", "subset", input_path]
args.extend(["--drop-tables+=DSIG",
"--drop-tables-=sbix",
"--unicodes=%s" % unicodes,
"--output-file=%s" % fonttools_path])
args.extend(profile_flags)
check_call(args)
with io.StringIO () as fp:
with TTFont (fonttools_path) as font:

View File

@ -33,7 +33,6 @@ tests = [
'layout.duplicate_features',
'layout.unsorted_featurelist',
'layout.drop_feature',
'layout.default_features',
'cmap',
'cmap14',
'sbix',
@ -54,6 +53,7 @@ tests = [
# 'pin_all_at_default',
# 'instantiate_glyf',
# 'full_instance',
# 'instance_feature_variations',
]
repack_tests = [

View File

@ -52,6 +52,7 @@ def run_test (test, should_check_ots):
cli_args = ["--font-file=" + test.font_path,
"--output-file=" + out_file,
"--unicodes=%s" % test.unicodes (),
"--preprocess-face",
"--drop-tables+=DSIG",
"--drop-tables-=sbix"]
cli_args.extend (test.get_profile_flags ())

View File

@ -32,6 +32,15 @@
#include <hb-subset.h>
static hb_face_t* preprocess_face(hb_face_t* face)
{
#ifdef HB_EXPERIMENTAL_API
return hb_subset_preprocess (face);
#else
return hb_face_reference(face);
#endif
}
/*
* Command line interface to the harfbuzz font subsetter.
*/
@ -103,6 +112,10 @@ struct subset_main_t : option_parser_t, face_options_t, output_options_t<false>
{
parse (argc, argv);
hb_face_t* orig_face = face;
if (preprocess)
orig_face = preprocess_face (face);
hb_face_t *new_face = nullptr;
for (unsigned i = 0; i < num_iterations; i++)
{
@ -119,6 +132,8 @@ struct subset_main_t : option_parser_t, face_options_t, output_options_t<false>
}
hb_face_destroy (new_face);
if (preprocess)
hb_face_destroy (orig_face);
return success ? 0 : 1;
}
@ -160,6 +175,7 @@ struct subset_main_t : option_parser_t, face_options_t, output_options_t<false>
public:
unsigned num_iterations = 1;
gboolean preprocess;
hb_subset_input_t *input = nullptr;
};
@ -915,6 +931,8 @@ subset_main_t::add_options ()
{"no-prune-unicode-ranges", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, (gpointer) &set_flag<HB_SUBSET_FLAGS_NO_PRUNE_UNICODE_RANGES>, "Don't change the 'OS/2 ulUnicodeRange*' bits.", nullptr},
{"glyph-names", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, (gpointer) &set_flag<HB_SUBSET_FLAGS_GLYPH_NAMES>, "Keep PS glyph names in TT-flavored fonts. ", nullptr},
{"passthrough-tables", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, (gpointer) &set_flag<HB_SUBSET_FLAGS_PASSTHROUGH_UNRECOGNIZED>, "Do not drop tables that the tool does not know how to subset.", nullptr},
{"preprocess-face", 0, 0, G_OPTION_ARG_NONE, &this->preprocess,
"If set preprocesses the face with the add accelerator option before actually subsetting.", nullptr},
{nullptr}
};
add_group (flag_entries,