Merge branch 'master' into cff-subset

This commit is contained in:
Michiharu Ariza 2018-11-28 16:27:45 -08:00
commit 32d291ae89
422 changed files with 3430 additions and 988 deletions

View File

@ -2,38 +2,55 @@ version: 2
jobs: jobs:
macos-llvm-gcc-4.2: macos-10.12.6-aat-fonts:
macos: macos:
xcode: "8.3.3" xcode: "9.2.0"
steps: steps:
- checkout - checkout
- run: brew update-reset - run: HOMEBREW_NO_AUTO_UPDATE=1 brew install wget pkg-config libtool ragel freetype glib cairo
- run: brew install wget pkg-config libtool ragel freetype glib cairo - run: ./autogen.sh --with-freetype --with-glib --with-gobject --with-cairo
- run: wget https://packages.macports.org/llvm-gcc42/llvm-gcc42-2336.11_3+universal.darwin_15.i386-x86_64.tbz2 && tar zxvf llvm-gcc42-2336.11_3+universal.darwin_15.i386-x86_64.tbz2 - run: make -j4
- run: CC=$PWD/opt/local/bin/llvm-gcc-4.2 CXX=$PWD/opt/local/bin/llvm-g++-4.2 ./autogen.sh --with-freetype --with-glib --with-gobject --with-cairo
# Ignoring assembler complains, https://stackoverflow.com/a/39867021
- run: make 2>&1 | grep -v -e '^/var/folders/*' -e '^[[:space:]]*\.section' -e '^[[:space:]]*\^[[:space:]]*~*'
- run: make check || .ci/fail.sh - run: make check || .ci/fail.sh
macos-notest-apple-gcc-i686-4.2: macos-10.13.6-aat-fonts:
macos: macos:
xcode: "8.3.3" xcode: "10.1.0"
steps: steps:
- checkout - checkout
- run: brew update-reset - run: HOMEBREW_NO_AUTO_UPDATE=1 brew install wget pkg-config libtool ragel freetype glib cairo
- run: brew install wget pkg-config libtool ragel - run: ./autogen.sh --with-freetype --with-glib --with-gobject --with-cairo
- run: wget https://packages.macports.org/apple-gcc42/apple-gcc42-5666.3_15+universal.darwin_15.i386-x86_64.tbz2 && tar zxvf apple-gcc42-5666.3_15+universal.darwin_15.i386-x86_64.tbz2 - run: make -j4
- run: CPP=$PWD/opt/local/bin/i686-apple-darwin15-cpp-apple-4.2.1 CC=$PWD/opt/local/bin/i686-apple-darwin15-gcc-apple-4.2.1 CXX=$PWD/opt/local/bin/i686-apple-darwin15-g++-apple-4.2.1 ./autogen.sh - run: make check || .ci/fail.sh
# Ignoring assembler complains, https://stackoverflow.com/a/39867021
- run: make 2>&1 | grep -v -e '^/var/folders/*' -e '^[[:space:]]*\.section' -e '^[[:space:]]*\^[[:space:]]*~*' # macos-llvm-gcc-4.2:
# macos:
# xcode: "8.3.3"
# steps:
# - checkout
# - run: HOMEBREW_NO_AUTO_UPDATE=1 brew install wget pkg-config libtool ragel freetype glib cairo
# - run: wget https://packages.macports.org/llvm-gcc42/llvm-gcc42-2336.11_3+universal.darwin_15.i386-x86_64.tbz2 && tar zxvf llvm-gcc42-2336.11_3+universal.darwin_15.i386-x86_64.tbz2
# - run: CC=$PWD/opt/local/bin/llvm-gcc-4.2 CXX=$PWD/opt/local/bin/llvm-g++-4.2 ./autogen.sh --with-freetype --with-glib --with-gobject --with-cairo
# # Ignoring assembler complains, https://stackoverflow.com/a/39867021
# - run: make 2>&1 | grep -v -e '^/var/folders/*' -e '^[[:space:]]*\.section' -e '^[[:space:]]*\^[[:space:]]*~*'
# - run: make check || .ci/fail.sh
# macos-notest-apple-gcc-i686-4.2:
# macos:
# xcode: "8.3.3"
# steps:
# - checkout
# - run: HOMEBREW_NO_AUTO_UPDATE=1 brew install wget pkg-config libtool ragel
# - run: wget https://packages.macports.org/apple-gcc42/apple-gcc42-5666.3_15+universal.darwin_15.i386-x86_64.tbz2 && tar zxvf apple-gcc42-5666.3_15+universal.darwin_15.i386-x86_64.tbz2
# - run: CPP=$PWD/opt/local/bin/i686-apple-darwin15-cpp-apple-4.2.1 CC=$PWD/opt/local/bin/i686-apple-darwin15-gcc-apple-4.2.1 CXX=$PWD/opt/local/bin/i686-apple-darwin15-g++-apple-4.2.1 ./autogen.sh
# # Ignoring assembler complains, https://stackoverflow.com/a/39867021
# - run: make 2>&1 | grep -v -e '^/var/folders/*' -e '^[[:space:]]*\.section' -e '^[[:space:]]*\^[[:space:]]*~*'
macos-notest-ios: macos-notest-ios:
macos: macos:
xcode: "10.0.0" xcode: "10.0.0"
steps: steps:
- checkout - checkout
- run: brew update-reset - run: HOMEBREW_NO_AUTO_UPDATE=1 brew install cmake
- run: brew install cmake
# not needed to be a framework but we like to test that also # not needed to be a framework but we like to test that also
# TODO: wrong way of targeting iOS as it doesn't point to iOS headers thus building # TODO: wrong way of targeting iOS as it doesn't point to iOS headers thus building
# CoreText support is not possible, after the fix feel free HB_IOS from CMake altogether # CoreText support is not possible, after the fix feel free HB_IOS from CMake altogether
@ -302,6 +319,8 @@ workflows:
build: build:
jobs: jobs:
# macOS # macOS
- macos-10.12.6-aat-fonts
- macos-10.13.6-aat-fonts
#- macos-llvm-gcc-4.2 #- macos-llvm-gcc-4.2
#- macos-notest-apple-gcc-i686-4.2 #- macos-notest-apple-gcc-i686-4.2
- macos-notest-ios - macos-notest-ios

7
.codecov.yml Normal file
View File

@ -0,0 +1,7 @@
comment: off
coverage:
status:
project:
default:
threshold: 1%

View File

@ -508,6 +508,7 @@ test/api/Makefile
test/fuzzing/Makefile test/fuzzing/Makefile
test/shaping/Makefile test/shaping/Makefile
test/shaping/data/Makefile test/shaping/data/Makefile
test/shaping/data/aots/Makefile
test/shaping/data/in-house/Makefile test/shaping/data/in-house/Makefile
test/shaping/data/text-rendering-tests/Makefile test/shaping/data/text-rendering-tests/Makefile
test/subset/Makefile test/subset/Makefile

View File

@ -112,6 +112,11 @@
<xi:include href="xml/hb-ot-var.xml"/> <xi:include href="xml/hb-ot-var.xml"/>
</chapter> </chapter>
<chapter>
<title>Apple Advanced Typography API</title>
<xi:include href="xml/hb-aat-layout.xml"/>
</chapter>
<chapter> <chapter>
<title>Integration API</title> <title>Integration API</title>
<xi:include href="xml/hb-coretext.xml"/> <xi:include href="xml/hb-coretext.xml"/>

View File

@ -3,6 +3,17 @@ HB_H_IN
HB_OT_H_IN HB_OT_H_IN
</SECTION> </SECTION>
<SECTION>
<FILE>hb-aat-layout</FILE>
HB_AAT_LAYOUT_NO_SELECTOR_INDEX
hb_aat_layout_feature_type_t
hb_aat_layout_get_feature_types
hb_aat_layout_feature_type_get_name_id
hb_aat_layout_feature_selector_t
hb_aat_layout_feature_selector_info_t
hb_aat_layout_feature_type_get_selector_infos
</SECTION>
<SECTION> <SECTION>
<FILE>hb-blob</FILE> <FILE>hb-blob</FILE>
hb_blob_create hb_blob_create
@ -161,6 +172,10 @@ hb_ot_layout_table_choose_script
hb_ot_layout_table_find_script hb_ot_layout_table_find_script
hb_ot_tag_from_language hb_ot_tag_from_language
hb_ot_tags_from_script hb_ot_tags_from_script
HB_OT_VAR_NO_AXIS_INDEX
hb_ot_var_axis_t
hb_ot_var_find_axis
hb_ot_var_get_axes
hb_set_invert hb_set_invert
hb_unicode_eastasian_width_func_t hb_unicode_eastasian_width_func_t
hb_unicode_eastasian_width hb_unicode_eastasian_width
@ -581,14 +596,12 @@ HB_OT_TAG_VAR_AXIS_OPTICAL_SIZE
HB_OT_TAG_VAR_AXIS_SLANT HB_OT_TAG_VAR_AXIS_SLANT
HB_OT_TAG_VAR_AXIS_WEIGHT HB_OT_TAG_VAR_AXIS_WEIGHT
HB_OT_TAG_VAR_AXIS_WIDTH HB_OT_TAG_VAR_AXIS_WIDTH
HB_OT_VAR_NO_AXIS_INDEX
hb_ot_var_axis_t
hb_ot_var_has_data hb_ot_var_has_data
hb_ot_var_find_axis
hb_ot_var_get_axis_count
hb_ot_var_get_axes
hb_ot_var_axis_flags_t hb_ot_var_axis_flags_t
hb_ot_var_axis_get_flags hb_ot_var_axis_info_t
hb_ot_var_find_axis_info
hb_ot_var_get_axis_count
hb_ot_var_get_axis_infos
hb_ot_var_get_named_instance_count hb_ot_var_get_named_instance_count
hb_ot_var_named_instance_get_subfamily_name_id hb_ot_var_named_instance_get_subfamily_name_id
hb_ot_var_named_instance_get_postscript_name_id hb_ot_var_named_instance_get_postscript_name_id

View File

@ -5,306 +5,538 @@
<!ENTITY version SYSTEM "version.xml"> <!ENTITY version SYSTEM "version.xml">
]> ]>
<chapter id="clusters"> <chapter id="clusters">
<sect1 id="clusters">
<title>Clusters</title> <title>Clusters</title>
<para> <section id="clusters">
In shaping text, a <emphasis>cluster</emphasis> is a sequence of <title>Clusters</title>
code points that needs to be treated as a single, indivisible unit.
</para>
<para>
When you add text to a HB buffer, each character is associated with
a <emphasis>cluster value</emphasis>. This is an arbitrary number as
far as HB is concerned.
</para>
<para>
Most clients will use UTF-8, UTF-16, or UTF-32 indices, but the
actual number does not matter. Moreover, it is not required for the
cluster values to be monotonically increasing, but pretty much all
of HB's tests are performed on monotonically increasing cluster
numbers. Nevertheless, there is no such assumption in the code
itself. With that in mind, let's examine what happens with cluster
values during shaping under each cluster-level.
</para>
<para>
HarfBuzz provides three <emphasis>levels</emphasis> of clustering
support. Level 0 is the default behavior and reproduces the behavior
of the old HarfBuzz library. Level 1 tweaks this behavior slightly
to produce better results, so level 1 clustering is recommended for
code that is not required to implement backward compatibility with
the old HarfBuzz.
</para>
<para>
Level 2 differs significantly in how it treats cluster values.
Levels 0 and 1 both process ligatures and glyph decomposition by
merging clusters; level 2 does not.
</para>
<para>
The conceptual model for what the cluster values mean, in levels 0
and 1, is this:
</para>
<itemizedlist spacing="compact">
<listitem>
<para>
the sequence of cluster values will always remain monotone
</para>
</listitem>
<listitem>
<para>
each value represents a single cluster
</para>
</listitem>
<listitem>
<para>
each cluster contains one or more glyphs and one or more
characters
</para>
</listitem>
</itemizedlist>
<para>
Assuming that initial cluster numbers were monotonically increasing
and distinct, then all adjacent glyphs having the same cluster
number belong to the same cluster, and all characters belong to the
cluster that has the highest number not larger than their initial
cluster number. This will become clearer with an example.
</para>
</sect1>
<sect1 id="a-clustering-example-for-levels-0-and-1">
<title>A clustering example for levels 0 and 1</title>
<para>
Let's say we start with the following character sequence and cluster
values:
</para>
<programlisting>
A,B,C,D,E
0,1,2,3,4
</programlisting>
<para>
We then map the characters to glyphs. For simplicity, let's assume
that each character maps to the corresponding, identical-looking
glyph:
</para>
<programlisting>
A,B,C,D,E
0,1,2,3,4
</programlisting>
<para>
Now if, for example, <literal>B</literal> and <literal>C</literal>
ligate, then the clusters to which they belong &quot;merge&quot;.
This merged cluster takes for its cluster number the minimum of all
the cluster numbers of the clusters that went in. In this case, we
get:
</para>
<programlisting>
A,BC,D,E
0,1 ,3,4
</programlisting>
<para>
Now let's assume that the <literal>BC</literal> glyph decomposes
into three components, and <literal>D</literal> also decomposes into
two. The components each inherit the cluster value of their parent:
</para>
<programlisting>
A,BC0,BC1,BC2,D0,D1,E
0,1 ,1 ,1 ,3 ,3 ,4
</programlisting>
<para>
Now if <literal>BC2</literal> and <literal>D0</literal> ligate, then
their clusters (numbers 1 and 3) merge into
<literal>min(1,3) = 1</literal>:
</para>
<programlisting>
A,BC0,BC1,BC2D0,D1,E
0,1 ,1 ,1 ,1 ,4
</programlisting>
<para>
At this point, cluster 1 means: the character sequence
<literal>BCD</literal> is represented by glyphs
<literal>BC0,BC1,BC2D0,D1</literal> and cannot be broken down any
further.
</para>
</sect1>
<sect1 id="reordering-in-levels-0-and-1">
<title>Reordering in levels 0 and 1</title>
<para>
Another common operation in the more complex shapers is when things
reorder. In those cases, to maintain monotone clusters, HB merges
the clusters of everything in the reordering sequence. For example,
let's again start with the character sequence:
</para>
<programlisting>
A,B,C,D,E
0,1,2,3,4
</programlisting>
<para>
If <literal>D</literal> is reordered before <literal>B</literal>,
then the <literal>B</literal>, <literal>C</literal>, and
<literal>D</literal> clusters merge, and we get:
</para>
<programlisting>
A,D,B,C,E
0,1,1,1,4
</programlisting>
<para>
This is clearly not ideal, but it is the only sensible way to
maintain monotone indices and retain the true relationship between
glyphs and characters.
</para>
</sect1>
<sect1 id="the-distinction-between-levels-0-and-1">
<title>The distinction between levels 0 and 1</title>
<para>
So, the above is pretty much what cluster levels 0 and 1 do. The
only difference between the two is this: in level 0, at the very
beginning of the shaping process, we also merge clusters between
base characters and all Unicode marks (combining or not) following
them. E.g.:
</para>
<programlisting>
A,acute,B
0,1 ,2
</programlisting>
<para>
will become:
</para>
<programlisting>
A,acute,B
0,0 ,2
</programlisting>
<para>
This is the default behavior. We do it because Windows did it and
old HarfBuzz did it, so this remained the default. But this behavior
makes it impossible to color diacritic marks differently from their
base characters. That's why in level 1 we do not perform this
initial merging step.
</para>
<para>
For clients, level 0 is more convenient if they rely on HarfBuzz
clusters for cursor positioning. But that's wrong anyway: cursor
positions should be determined based on Unicode grapheme boundaries,
NOT shaping clusters. As such, level 1 clusters are preferred.
</para>
<para>
One last note about levels 0 and 1. We currently don't allow a
<literal>MultipleSubst</literal> lookup to replace a glyph with zero
glyphs (i.e., to delete a glyph). But in some other situations,
glyphs can be deleted. In those cases, if the glyph being deleted is
the last glyph of its cluster, we make sure to merge the cluster
with a neighboring cluster.
</para>
<para>
This is, primarily, to make sure that the starting cluster of the
text always has the cluster index pointing to the start of the text
for the run; more than one client currently relies on this
guarantee.
</para>
<para>
Incidentally, Apple's CoreText does something else to maintain the
same promise: it inserts a glyph with id 65535 at the beginning of
the glyph string if the glyph corresponding to the first character
in the run was deleted. HarfBuzz might do something similar in the
future.
</para>
</sect1>
<sect1 id="level-2">
<title>Level 2</title>
<para>
Level 2 is a different beast from levels 0 and 1. It is simple to
describe, but hard to make sense of. It simply doesn't do any
cluster merging whatsoever. When things ligate or otherwise multiple
glyphs turn into one, the cluster value of the first glyph is
retained.
</para>
<para>
Here are a few examples of why processing cluster values produced at
this level might be tricky:
</para>
<sect2 id="ligatures-with-combining-marks">
<title>Ligatures with combining marks</title>
<para> <para>
Imagine capital letters are bases and lower case letters are In text shaping, a <emphasis>cluster</emphasis> is a sequence of
combining marks. With an input sequence like this: characters that needs to be treated as a single, indivisible
unit.
</para>
<para>
A cluster is distinct from a <emphasis>grapheme</emphasis>,
which is the smallest unit of a writing system or script,
because clusters are only relevant for script shaping and the
layout of glyphs.
</para>
<para>
For example, a grapheme may be a letter, a number, a logogram,
or a symbol. When two letters form a ligature, however, they
combine into a single glyph. They are therefore part of the same
cluster and are treated as a unit &mdash; even though the two
original, underlying letters are separate graphemes.
</para>
<para>
During the shaping process, there are several shaping operations
that may merge adjacent characters (for example, when two code
points form a ligature or a conjunct form and are replaced by a
single glyph) or split one character into several (for example,
when decomposing a code point through the
<literal>ccmp</literal> feature).
</para>
<para>
HarfBuzz tracks clusters independently from how these
shaping operations affect the individual glyphs that comprise the
output HarfBuzz returns in a buffer. Consequently,
a client program using HarfBuzz can utilize the cluster
information to implement features such as:
</para>
<itemizedlist>
<listitem>
<para>
Correctly positioning the cursor within a shaped text run,
even when characters have formed ligatures, composed or
decomposed, reordered, or undergone other shaping operations.
</para>
</listitem>
<listitem>
<para>
Correctly highlighting a text selection that includes some,
but not all, of the characters in a word.
</para>
</listitem>
<listitem>
<para>
Applying text attributes (such as color or underlining) to
part, but not all, of a word.
</para>
</listitem>
<listitem>
<para>
Generating output document formats (such as PDF) with
embedded text that can be fully extracted.
</para>
</listitem>
<listitem>
<para>
Determining the mapping between input characters and output
glyphs, such as which glyphs are ligatures.
</para>
</listitem>
<listitem>
<para>
Performing line-breaking, justification, and other
line-level or paragraph-level operations that must be done
after shaping is complete, but which require character-level
properties.
</para>
</listitem>
</itemizedlist>
<para>
When you add text to a HarfBuzz buffer, each code point must be
assigned a <emphasis>cluster value</emphasis>.
</para>
<para>
This cluster value is an arbitrary number; HarfBuzz uses it only
to distinguish between clusters. Many client programs will use
the index of each code point in the input text stream as the
cluster value. This is for the sake of convenience; the actual
value does not matter.
</para>
<para>
Client programs can choose how HarfBuzz handles clusters during
shaping by setting the
<literal>cluster_level</literal> of the
buffer. HarfBuzz offers three <emphasis>levels</emphasis> of
clustering support for this property:
</para>
<itemizedlist>
<listitem>
<para><emphasis>Level 0</emphasis> is the default and
reproduces the behavior of the old HarfBuzz library.
</para>
<para>
The distinguishing feature of level 0 behavior is that, at
the beginning of processing the buffer, all code points that
are categorized as <emphasis>marks</emphasis>,
<emphasis>modifier symbols</emphasis>, or
<emphasis>Emoji extended pictographic</emphasis> modifiers,
as well as the <emphasis>Zero Width Joiner</emphasis> and
<emphasis>Zero Width Non-Joiner</emphasis> code points, are
assigned the cluster value of the closest preceding code
point from <emphasis>different</emphasis> category.
</para>
<para>
In essence, whenever a base character is followed by a mark
character or a sequence of mark characters, those marks are
reassigned to the same initial cluster value as the base
character. This reassignment is referred to as
"merging" the affected clusters. This behavior is based on
the Grapheme Cluster Boundary specification in <ulink
url="https://www.unicode.org/reports/tr29/#Regex_Definitions">Unicode
Technical Report 29</ulink>.
</para>
<para>
Client programs can specify level 0 behavior for a buffer by
setting its <literal>cluster_level</literal> to
<literal>HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES</literal>.
</para>
</listitem>
<listitem>
<para>
<emphasis>Level 1</emphasis> tweaks the old behavior
slightly to produce better results. Therefore, level 1
clustering is recommended for code that is not required to
implement backward compatibility with the old HarfBuzz.
</para>
<para>
Level 1 differs from level 0 by not merging the
clusters of marks and other modifier code points with the
preceding "base" code point's cluster. By preserving the
separate cluster values of these marks and modifier code
points, script shapers can perform additional operations
that might lead to improved results (for example, reordering
a sequence of marks).
</para>
<para>
Client programs can specify level 1 behavior for a buffer by
setting its <literal>cluster_level</literal> to
<literal>HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS</literal>.
</para>
</listitem>
<listitem>
<para>
<emphasis>Level 2</emphasis> differs significantly in how it
treats cluster values. In level 2, HarfBuzz never merges
clusters.
</para>
<para>
This difference can be seen most clearly when HarfBuzz processes
ligature substitutions and glyph decompositions. In level 0
and level 1, ligatures and glyph decomposition both involve
merging clusters; in level 2, neither of these operations
triggers a merge.
</para>
<para>
Client programs can specify level 2 behavior for a buffer by
setting its <literal>cluster_level</literal> to
<literal>HB_BUFFER_CLUSTER_LEVEL_CHARACTERS</literal>.
</para>
</listitem>
</itemizedlist>
<para>
As mentioned earlier, client programs using HarfBuzz often
assign initial cluster values in a buffer by reusing the indices
of the code points in the input text. This gives a sequence of
cluster values that is monotonically increasing (for example,
0,1,2,3,4,5).
</para>
<para>
It is not <emphasis>required</emphasis> that the cluster values
in a buffer be monotonically increasing. However, if the initial
cluster values in a buffer are monotonic and the buffer is
configured to use cluster level 0 or 1, then HarfBuzz
guarantees that the final cluster values in the shaped buffer
will also be monotonic. No such guarantee is made for cluster
level 2.
</para>
<para>
In levels 0 and 1, HarfBuzz implements the following conceptual
model for cluster values:
</para>
<itemizedlist spacing="compact">
<listitem>
<para>
If the sequence of input cluster values is monotonic, the
sequence of cluster values will remain monotonic.
</para>
</listitem>
<listitem>
<para>
Each cluster value represents a single cluster.
</para>
</listitem>
<listitem>
<para>
Each cluster contains one or more glyphs and one or more
characters.
</para>
</listitem>
</itemizedlist>
<para>
In practice, this model offers several benefits. Assuming that
the initial cluster values were monotonically increasing
and distinct before shaping began, then, in the final output:
</para>
<itemizedlist spacing="compact">
<listitem>
<para>
All adjacent glyphs having the same final cluster
value belong to the same cluster.
</para>
</listitem>
<listitem>
<para>
Each character belongs to the cluster that has the highest
cluster value <emphasis>not larger than</emphasis> its
initial cluster value.
</para>
</listitem>
</itemizedlist>
</section>
<section id="a-clustering-example-for-levels-0-and-1">
<title>A clustering example for levels 0 and 1</title>
<para>
The guarantees and benefits of level 0 and level 1 can be seen
with some examples. First, let us examine what happens with cluster
values when shaping involves cluster merging with ligatures and
decomposition.
</para>
<para>
Let's say we start with the following character sequence (top row) and
initial cluster values (bottom row):
</para> </para>
<programlisting> <programlisting>
A,a,B,b,C,c A,B,C,D,E
0,1,2,3,4,5 0,1,2,3,4
</programlisting> </programlisting>
<para> <para>
if <literal>A,B,C</literal> ligate, then here are the cluster During shaping, HarfBuzz maps these characters to glyphs from
values one would get under the various levels: the font. For simplicity, let us assume that each character maps
</para> to the corresponding, identical-looking glyph:
<para>
level 0:
</para> </para>
<programlisting> <programlisting>
ABC,a,b,c A,B,C,D,E
0 ,0,0,0 0,1,2,3,4
</programlisting> </programlisting>
<para> <para>
level 1: Now if, for example, <literal>B</literal> and <literal>C</literal>
form a ligature, then the clusters to which they belong
&quot;merge&quot;. This merged cluster takes for its cluster
value the minimum of all the cluster values of the clusters that
went in to the ligature. In this case, we get:
</para> </para>
<programlisting> <programlisting>
ABC,a,b,c A,BC,D,E
0 ,0,0,5 0,1 ,3,4
</programlisting> </programlisting>
<para> <para>
level 2: because 1 is the minimum of the set {1,2}, which were the
cluster values of <literal>B</literal> and
<literal>C</literal>.
</para>
<para>
Next, let us say that the <literal>BC</literal> ligature glyph
decomposes into three components, and <literal>D</literal> also
decomposes into two components. These components each inherit the
cluster value of their parent:
</para> </para>
<programlisting> <programlisting>
ABC,a,b,c A,BC0,BC1,BC2,D0,D1,E
0 ,1,3,5 0,1 ,1 ,1 ,3 ,3 ,4
</programlisting> </programlisting>
<para> <para>
Making sense of the last example is the hardest for a client, Next, if <literal>BC2</literal> and <literal>D0</literal> form a
because there is nothing in the cluster values to suggest that ligature, then their clusters (cluster values 1 and 3) merge into
<literal>B</literal> and <literal>C</literal> ligated with <literal>min(1,3) = 1</literal>:
<literal>A</literal>.
</para>
</sect2>
<sect2 id="reordering">
<title>Reordering</title>
<para>
Another tricky case is when things reorder. Under level 2:
</para> </para>
<programlisting> <programlisting>
A,B,C,D,E A,BC0,BC1,BC2D0,D1,E
0,1,2,3,4 0,1 ,1 ,1 ,1 ,4
</programlisting> </programlisting>
<para> <para>
Now imagine <literal>D</literal> moves before At this point, cluster 1 means: the character sequence
<literal>B</literal>: <literal>BCD</literal> is represented by glyphs
<literal>BC0,BC1,BC2D0,D1</literal> and cannot be broken down any
further.
</para>
</section>
<section id="reordering-in-levels-0-and-1">
<title>Reordering in levels 0 and 1</title>
<para>
Another common operation in the more complex shapers is glyph
reordering. In order to maintain a monotonic cluster sequence
when glyph reordering takes place, HarfBuzz merges the clusters
of everything in the reordering sequence.
</para>
<para>
For example, let us again start with the character sequence (top
row) and initial cluster values (bottom row):
</para> </para>
<programlisting> <programlisting>
A,D,B,C,E A,B,C,D,E
0,3,1,2,4 0,1,2,3,4
</programlisting> </programlisting>
<para> <para>
Now, if <literal>D</literal> ligates with <literal>B</literal>, we If <literal>D</literal> is reordered to before <literal>B</literal>,
then HarfBuzz merges the <literal>B</literal>,
<literal>C</literal>, and <literal>D</literal> clusters, and we
get: get:
</para> </para>
<programlisting> <programlisting>
A,DB,C,E A,D,B,C,E
0,3 ,2,4 0,1,1,1,4
</programlisting> </programlisting>
<para> <para>
In a different scenario, <literal>A</literal> and This is clearly not ideal, but it is the only sensible way to
<literal>B</literal> could have ligated maintain a monotonic sequence of cluster values and retain the
<emphasis>before</emphasis> <literal>D</literal> reordered; that true relationship between glyphs and characters.
would have resulted in: </para>
</section>
<section id="the-distinction-between-levels-0-and-1">
<title>The distinction between levels 0 and 1</title>
<para>
The preceding examples demonstrate the main effects of using
cluster levels 0 and 1. The only difference between the two
levels is this: in level 0, at the very beginning of the shaping
process, HarfBuzz also merges clusters between any base character
and all Unicode marks (combining or not) that follow it.
</para>
<para>
For example, let us start with the following character sequence
(top row) and accompanying initial cluster values (bottom row):
</para> </para>
<programlisting> <programlisting>
AB,D,C,E A,acute,B
0 ,3,2,4 0,1 ,2
</programlisting> </programlisting>
<para> <para>
There's no way to differentiate between these two scenarios based The <literal>acute</literal> is a Unicode mark. If HarfBuzz is
on the cluster numbers alone. using cluster level 0 on this sequence, then the
<literal>A</literal> and <literal>acute</literal> clusters will
merge, and the result will become:
</para>
<programlisting>
A,acute,B
0,0 ,2
</programlisting>
<para>
This initial cluster merging is the default behavior of the
Windows shaping engine, and the old HarfBuzz codebase copied
that behavior to maintain compatibility. Consequently, it has
remained the default behavior in the new HarfBuzz codebase.
</para> </para>
<para> <para>
Another problem happens with ligatures under level 2 if the But this initial cluster-merging behavior makes it impossible to
direction of the text is forced to opposite of its natural color diacritic marks differently from their base
direction (e.g. left-to-right Arabic). But that's too much of a characters. That is why, in level 1, HarfBuzz does not perform
corner case to worry about. the initial merging step.
</para> </para>
</sect2> <para>
</sect1> For client programs that rely on HarfBuzz cluster values to
perform cursor positioning, level 0 is more convenient. But
relying on cluster boundaries for cursor positioning is wrong: cursor
positions should be determined based on Unicode grapheme
boundaries, not on shaping-cluster boundaries. As such, level 1
clusters are preferred.
</para>
<para>
One last note about levels 0 and 1. HarfBuzz currently does not allow a
<literal>MultipleSubst</literal> lookup to replace a glyph with zero
glyphs (in other words, to delete a glyph). But, in some other situations,
glyphs can be deleted. In those cases, if the glyph being deleted is
the last glyph of its cluster, HarfBuzz makes sure to merge the cluster
with a neighboring cluster.
</para>
<para>
This is done primarily to make sure that the starting cluster of the
text always has the cluster index pointing to the start of the text
for the run; more than one client currently relies on this
guarantee.
</para>
<para>
Incidentally, Apple's CoreText does something else to maintain the
same promise: it inserts a glyph with id 65535 at the beginning of
the glyph string if the glyph corresponding to the first character
in the run was deleted. HarfBuzz might do something similar in the
future.
</para>
</section>
<section id="level-2">
<title>Level 2</title>
<para>
HarfBuzz's level 2 cluster behavior uses a significantly
different model than that of level 0 and level 1.
</para>
<para>
The level 2 behavior is easy to describe, but it may be
difficult to understand in practical terms. In brief, level 2
performs no merging of clusters whatsoever.
</para>
<para>
When glyphs form a ligature (or when some other feature
substitutes multiple glyphs with one glyph), the cluster value
of the first glyph is retained as the cluster value for the
ligature. However, no subsequent clusters &mdash; including
marks and modifiers &mdash; are affected.
</para>
<para>
Level 2 cluster behavior is less complex than level 0 or level
1, but there are a few cases in which processing cluster values
produced at level 2 may be tricky.
</para>
<section id="ligatures-with-combining-marks-in-level-2">
<title>Ligatures with combining marks in level 2</title>
<para>
The first example of how HarfBuzz's level 2 cluster behavior
can be tricky is when the text to be shaped includes combining
marks attached to ligatures.
</para>
<para>
Let us start with an input sequence with the following
characters (top row) and initial cluster values (bottom row):
</para>
<programlisting>
A,acute,B,breve,C,circumflex
0,1 ,2,3 ,4,5
</programlisting>
<para>
If the sequence <literal>A,B,C</literal> forms a ligature,
then these are the cluster values HarfBuzz will return under
the various cluster levels:
</para>
<para>
Level 0:
</para>
<programlisting>
ABC,acute,breve,circumflex
0 ,0 ,0 ,0
</programlisting>
<para>
Level 1:
</para>
<programlisting>
ABC,acute,breve,circumflex
0 ,0 ,0 ,5
</programlisting>
<para>
Level 2:
</para>
<programlisting>
ABC,acute,breve,circumflex
0 ,1 ,3 ,5
</programlisting>
<para>
Making sense of the level 2 result is the hardest for a client
program, because there is nothing in the cluster values that
indicates that <literal>B</literal> and <literal>C</literal>
formed a ligature with <literal>A</literal>.
</para>
<para>
In contrast, the "merged" cluster values of the mark glyphs
that are seen in the level 0 and level 1 output are evidence
that a ligature substitution took place.
</para>
</section>
<section id="reordering-in-level-2">
<title>Reordering in level 2</title>
<para>
Another example of how HarfBuzz's level 2 cluster behavior
can be tricky is when glyphs reorder. Consider an input sequence
with the following characters (top row) and initial cluster
values (bottom row):
</para>
<programlisting>
A,B,C,D,E
0,1,2,3,4
</programlisting>
<para>
Now imagine <literal>D</literal> moves before
<literal>B</literal> in a reordering operation. The cluster
values will then be:
</para>
<programlisting>
A,D,B,C,E
0,3,1,2,4
</programlisting>
<para>
Next, if <literal>D</literal> forms a ligature with
<literal>B</literal>, the output is:
</para>
<programlisting>
A,DB,C,E
0,3 ,2,4
</programlisting>
<para>
However, in a different scenario, in which the shaping rules
of the script instead caused <literal>A</literal> and
<literal>B</literal> to form a ligature
<emphasis>before</emphasis> the <literal>D</literal> reordered, the
result would be:
</para>
<programlisting>
AB,D,C,E
0 ,3,2,4
</programlisting>
<para>
There is no way for a client program to differentiate between
these two scenarios based on the cluster values
alone. Consequently, client programs that use level 2 might
need to undertake additional work in order to manage cursor
positioning, text attributes, or other desired features.
</para>
</section>
<section id="other-considerations-in-level-2">
<title>Other considerations in level 2</title>
<para>
There may be other problems encountered with ligatures under
level 2, such as if the direction of the text is forced to
opposite of its natural direction (for example, left-to-right
Arabic). But, generally speaking, these other scenarios are
minor corner cases that are too obscure for most client
programs to need to worry about.
</para>
</section>
</section>
</chapter> </chapter>

View File

@ -157,7 +157,8 @@
</orderedlist> </orderedlist>
<programlisting language="C"> <programlisting language="C">
#include &lt;hb-ft.h&gt; #include &lt;hb-ft.h&gt;
FT_New_Face(ft_library, font_path, index, &amp;face) FT_New_Face(ft_library, font_path, index, &amp;face);
FT_Set_Char_Size(face, 0, 1000, 0, 0);
hb_font_t *font = hb_ft_font_create(face); hb_font_t *font = hb_ft_font_create(face);
</programlisting> </programlisting>
<orderedlist numeration="arabic"> <orderedlist numeration="arabic">

View File

@ -424,6 +424,8 @@ HarfBuzz_0_0_gir_CFLAGS = \
-DHB_H_IN \ -DHB_H_IN \
-DHB_OT_H \ -DHB_OT_H \
-DHB_OT_H_IN \ -DHB_OT_H_IN \
-DHB_AAT_H \
-DHB_AAT_H_IN \
-DHB_GOBJECT_H \ -DHB_GOBJECT_H \
-DHB_GOBJECT_H_IN \ -DHB_GOBJECT_H_IN \
-DHB_EXTERN= \ -DHB_EXTERN= \

View File

@ -192,6 +192,8 @@ HB_OT_RAGEL_sources = \
$(NULL) $(NULL)
HB_OT_headers = \ HB_OT_headers = \
hb-aat.h \
hb-aat-layout.h \
hb-ot.h \ hb-ot.h \
hb-ot-color.h \ hb-ot-color.h \
hb-ot-font.h \ hb-ot-font.h \

View File

@ -50,6 +50,8 @@ data[0][0x20F0] = defaults[0]
# TODO https://github.com/roozbehp/unicode-data/issues/9 # TODO https://github.com/roozbehp/unicode-data/issues/9
data[0][0x11C44] = 'Consonant_Placeholder' data[0][0x11C44] = 'Consonant_Placeholder'
data[0][0x11C45] = 'Consonant_Placeholder' data[0][0x11C45] = 'Consonant_Placeholder'
# TODO https://github.com/harfbuzz/harfbuzz/pull/1399
data[0][0x111C8] = 'Consonant_Placeholder'
for u in range (0xFE00, 0xFE0F + 1): for u in range (0xFE00, 0xFE0F + 1):
data[0][u] = defaults[0] data[0][u] = defaults[0]
@ -168,7 +170,7 @@ def is_BASE(U, UISC, UGC):
def is_BASE_IND(U, UISC, UGC): def is_BASE_IND(U, UISC, UGC):
#SPEC-DRAFT return (UISC in [Consonant_Dead, Modifying_Letter] or UGC == Po) #SPEC-DRAFT return (UISC in [Consonant_Dead, Modifying_Letter] or UGC == Po)
return (UISC in [Consonant_Dead, Modifying_Letter] or return (UISC in [Consonant_Dead, Modifying_Letter] or
(UGC == Po and not U in [0x104B, 0x104E, 0x2022, 0x11A3F, 0x11A45, 0x11C44, 0x11C45]) or (UGC == Po and not U in [0x104B, 0x104E, 0x2022, 0x111C8, 0x11A3F, 0x11A45, 0x11C44, 0x11C45]) or
False # SPEC-DRAFT-OUTDATED! U == 0x002D False # SPEC-DRAFT-OUTDATED! U == 0x002D
) )
def is_BASE_NUM(U, UISC, UGC): def is_BASE_NUM(U, UISC, UGC):
@ -354,6 +356,9 @@ def map_to_use(data):
# TODO: https://github.com/harfbuzz/harfbuzz/issues/1105 # TODO: https://github.com/harfbuzz/harfbuzz/issues/1105
if U == 0x11134: UISC = Gemination_Mark if U == 0x11134: UISC = Gemination_Mark
# TODO: https://github.com/harfbuzz/harfbuzz/pull/1399
if U == 0x111C9: UISC = Consonant_Final
values = [k for k,v in items if v(U,UISC,UGC)] values = [k for k,v in items if v(U,UISC,UGC)]
assert len(values) == 1, "%s %s %s %s" % (hex(U), UISC, UGC, values) assert len(values) == 1, "%s %s %s %s" % (hex(U), UISC, UGC, values)
USE = values[0] USE = values[0]

View File

@ -69,13 +69,15 @@ struct LookupFormat0
UnsizedArrayOf<T> UnsizedArrayOf<T>
arrayZ; /* Array of lookup values, indexed by glyph index. */ arrayZ; /* Array of lookup values, indexed by glyph index. */
public: public:
DEFINE_SIZE_ARRAY (2, arrayZ); DEFINE_SIZE_UNBOUNDED (2);
}; };
template <typename T> template <typename T>
struct LookupSegmentSingle struct LookupSegmentSingle
{ {
enum { TerminationWordCount = 2 };
inline int cmp (hb_codepoint_t g) const { inline int cmp (hb_codepoint_t g) const {
return g < first ? -1 : g <= last ? 0 : +1 ; return g < first ? -1 : g <= last ? 0 : +1 ;
} }
@ -134,6 +136,8 @@ struct LookupFormat2
template <typename T> template <typename T>
struct LookupSegmentArray struct LookupSegmentArray
{ {
enum { TerminationWordCount = 2 };
inline const T* get_value (hb_codepoint_t glyph_id, const void *base) const inline const T* get_value (hb_codepoint_t glyph_id, const void *base) const
{ {
return first <= glyph_id && glyph_id <= last ? &(base+valuesZ)[glyph_id - first] : nullptr; return first <= glyph_id && glyph_id <= last ? &(base+valuesZ)[glyph_id - first] : nullptr;
@ -204,6 +208,8 @@ struct LookupFormat4
template <typename T> template <typename T>
struct LookupSingle struct LookupSingle
{ {
enum { TerminationWordCount = 1 };
inline int cmp (hb_codepoint_t g) const { return glyph.cmp (g); } inline int cmp (hb_codepoint_t g) const { return glyph.cmp (g); }
inline bool sanitize (hb_sanitize_context_t *c) const inline bool sanitize (hb_sanitize_context_t *c) const
@ -357,6 +363,14 @@ struct Lookup
} }
} }
inline typename T::type get_class (hb_codepoint_t glyph_id,
unsigned int num_glyphs,
unsigned int outOfRange) const
{
const T *v = get_value (glyph_id, num_glyphs);
return v ? *v : outOfRange;
}
inline bool sanitize (hb_sanitize_context_t *c) const inline bool sanitize (hb_sanitize_context_t *c) const
{ {
TRACE_SANITIZE (this); TRACE_SANITIZE (this);
@ -436,8 +450,10 @@ struct Entry
* which ensures that data has a simple sanitize(). To be determined * which ensures that data has a simple sanitize(). To be determined
* if I need to remove that as well. * if I need to remove that as well.
* *
* XXX Because we are a template, our DEFINE_SIZE_STATIC assertion * HOWEVER! Because we are a template, our DEFINE_SIZE_STATIC
* wouldn't be checked. */ * assertion wouldn't be checked, hence the line below. */
static_assert (T::static_size, "");
return_trace (c->check_struct (this)); return_trace (c->check_struct (this));
} }
@ -472,7 +488,7 @@ struct StateTable
{ {
typedef typename Types::HBUINT HBUINT; typedef typename Types::HBUINT HBUINT;
typedef typename Types::HBUSHORT HBUSHORT; typedef typename Types::HBUSHORT HBUSHORT;
typedef typename Types::ClassType ClassType; typedef typename Types::ClassTypeNarrow ClassType;
enum State enum State
{ {
@ -630,6 +646,7 @@ struct StateTable
DEFINE_SIZE_STATIC (4 * sizeof (HBUINT)); DEFINE_SIZE_STATIC (4 * sizeof (HBUINT));
}; };
template <typename HBUCHAR>
struct ClassTable struct ClassTable
{ {
inline unsigned int get_class (hb_codepoint_t glyph_id, unsigned int outOfRange) const inline unsigned int get_class (hb_codepoint_t glyph_id, unsigned int outOfRange) const
@ -637,6 +654,12 @@ struct ClassTable
unsigned int i = glyph_id - firstGlyph; unsigned int i = glyph_id - firstGlyph;
return i >= classArray.len ? outOfRange : classArray.arrayZ[i]; return i >= classArray.len ? outOfRange : classArray.arrayZ[i];
} }
inline unsigned int get_class (hb_codepoint_t glyph_id,
unsigned int num_glyphs HB_UNUSED,
unsigned int outOfRange) const
{
return get_class (glyph_id, outOfRange);
}
inline bool sanitize (hb_sanitize_context_t *c) const inline bool sanitize (hb_sanitize_context_t *c) const
{ {
TRACE_SANITIZE (this); TRACE_SANITIZE (this);
@ -644,7 +667,7 @@ struct ClassTable
} }
protected: protected:
GlyphID firstGlyph; /* First glyph index included in the trimmed array. */ GlyphID firstGlyph; /* First glyph index included in the trimmed array. */
ArrayOf<HBUINT8> classArray; /* The class codes (indexed by glyph index minus ArrayOf<HBUCHAR> classArray; /* The class codes (indexed by glyph index minus
* firstGlyph). */ * firstGlyph). */
public: public:
DEFINE_SIZE_ARRAY (4, classArray); DEFINE_SIZE_ARRAY (4, classArray);
@ -655,15 +678,9 @@ struct ObsoleteTypes
static const bool extended = false; static const bool extended = false;
typedef HBUINT16 HBUINT; typedef HBUINT16 HBUINT;
typedef HBUINT8 HBUSHORT; typedef HBUINT8 HBUSHORT;
struct ClassType : ClassTable typedef ClassTable<HBUINT8> ClassTypeNarrow;
{ typedef ClassTable<HBUINT16> ClassTypeWide;
inline unsigned int get_class (hb_codepoint_t glyph_id,
unsigned int num_glyphs HB_UNUSED,
unsigned int outOfRange) const
{
return ClassTable::get_class (glyph_id, outOfRange);
}
};
template <typename T> template <typename T>
static inline unsigned int offsetToIndex (unsigned int offset, static inline unsigned int offsetToIndex (unsigned int offset,
const void *base, const void *base,
@ -672,6 +689,13 @@ struct ObsoleteTypes
return (offset - ((const char *) array - (const char *) base)) / sizeof (T); return (offset - ((const char *) array - (const char *) base)) / sizeof (T);
} }
template <typename T> template <typename T>
static inline unsigned int byteOffsetToIndex (unsigned int offset,
const void *base,
const T *array)
{
return offsetToIndex (offset, base, array);
}
template <typename T>
static inline unsigned int wordOffsetToIndex (unsigned int offset, static inline unsigned int wordOffsetToIndex (unsigned int offset,
const void *base, const void *base,
const T *array) const T *array)
@ -684,16 +708,9 @@ struct ExtendedTypes
static const bool extended = true; static const bool extended = true;
typedef HBUINT32 HBUINT; typedef HBUINT32 HBUINT;
typedef HBUINT16 HBUSHORT; typedef HBUINT16 HBUSHORT;
struct ClassType : Lookup<HBUINT16> typedef Lookup<HBUINT16> ClassTypeNarrow;
{ typedef Lookup<HBUINT16> ClassTypeWide;
inline unsigned int get_class (hb_codepoint_t glyph_id,
unsigned int num_glyphs,
unsigned int outOfRange) const
{
const HBUINT16 *v = get_value (glyph_id, num_glyphs);
return v ? *v : outOfRange;
}
};
template <typename T> template <typename T>
static inline unsigned int offsetToIndex (unsigned int offset, static inline unsigned int offsetToIndex (unsigned int offset,
const void *base, const void *base,
@ -702,6 +719,13 @@ struct ExtendedTypes
return offset; return offset;
} }
template <typename T> template <typename T>
static inline unsigned int byteOffsetToIndex (unsigned int offset,
const void *base,
const T *array)
{
return offset / 2;
}
template <typename T>
static inline unsigned int wordOffsetToIndex (unsigned int offset, static inline unsigned int wordOffsetToIndex (unsigned int offset,
const void *base, const void *base,
const T *array) const T *array)

View File

@ -39,6 +39,27 @@ namespace AAT {
struct SettingName struct SettingName
{ {
friend struct FeatureName;
int cmp (hb_aat_layout_feature_selector_t key) const
{ return (int) key - (int) setting; }
inline hb_aat_layout_feature_selector_t get_selector (void) const
{ return (hb_aat_layout_feature_selector_t) (unsigned) setting; }
inline void get_info (hb_aat_layout_feature_selector_info_t *s,
hb_aat_layout_feature_selector_t default_selector) const
{
s->name_id = nameIndex;
s->enable = (hb_aat_layout_feature_selector_t) (unsigned int) setting;
s->disable = default_selector == HB_AAT_LAYOUT_FEATURE_SELECTOR_INVALID ?
(hb_aat_layout_feature_selector_t) (s->enable + 1) :
default_selector;
s->reserved = 0;
}
inline bool sanitize (hb_sanitize_context_t *c) const inline bool sanitize (hb_sanitize_context_t *c) const
{ {
TRACE_SANITIZE (this); TRACE_SANITIZE (this);
@ -51,35 +72,76 @@ struct SettingName
public: public:
DEFINE_SIZE_STATIC (4); DEFINE_SIZE_STATIC (4);
}; };
DECLARE_NULL_NAMESPACE_BYTES (AAT, SettingName);
struct feat;
struct FeatureName struct FeatureName
{ {
int cmp (hb_aat_layout_feature_type_t key) const
{ return (int) key - (int) feature; }
enum { enum {
Exclusive = 0x8000, /* If set, the feature settings are mutually exclusive. */ Exclusive = 0x8000, /* If set, the feature settings are mutually exclusive. */
NotDefault = 0x4000, /* If clear, then the setting with an index of 0 in NotDefault = 0x4000, /* If clear, then the setting with an index of 0 in
* the setting name array for this feature should * the setting name array for this feature should
* be taken as the default for the feature * be taken as the default for the feature
* (if one is required). If set, then bits 0-15 of this * (if one is required). If set, then bits 0-15 of this
* featureFlags field contain the index of the setting * featureFlags field contain the index of the setting
* which is to be taken as the default. */ * which is to be taken as the default. */
IndexMask = 0x00FF /* If bits 30 and 31 are set, then these sixteen bits IndexMask = 0x00FF /* If bits 30 and 31 are set, then these sixteen bits
* indicate the index of the setting in the setting name * indicate the index of the setting in the setting name
* array for this feature which should be taken * array for this feature which should be taken
* as the default. */ * as the default. */
}; };
inline unsigned int get_selector_infos (unsigned int start_offset,
unsigned int *selectors_count, /* IN/OUT. May be NULL. */
hb_aat_layout_feature_selector_info_t *selectors, /* OUT. May be NULL. */
unsigned int *pdefault_index, /* OUT. May be NULL. */
const void *base) const
{
hb_array_t< const SettingName> settings_table = (base+settingTableZ).as_array (nSettings);
static_assert (Index::NOT_FOUND_INDEX == HB_AAT_LAYOUT_NO_SELECTOR_INDEX, "");
hb_aat_layout_feature_selector_t default_selector = HB_AAT_LAYOUT_FEATURE_SELECTOR_INVALID;
unsigned int default_index = Index::NOT_FOUND_INDEX;
if (featureFlags & Exclusive)
{
default_index = (featureFlags & NotDefault) ? featureFlags & IndexMask : 0;
default_selector = settings_table[default_index].get_selector ();
}
if (pdefault_index)
*pdefault_index = default_index;
if (selectors_count)
{
hb_array_t<const SettingName> arr = settings_table.sub_array (start_offset, selectors_count);
unsigned int count = arr.len;
for (unsigned int i = 0; i < count; i++)
settings_table[start_offset + i].get_info (&selectors[i], default_selector);
}
return settings_table.len;
}
inline hb_aat_layout_feature_type_t get_feature_type () const
{ return (hb_aat_layout_feature_type_t) (unsigned int) feature; }
inline hb_ot_name_id_t get_feature_name_id () const { return nameIndex; }
inline bool sanitize (hb_sanitize_context_t *c, const void *base) const inline bool sanitize (hb_sanitize_context_t *c, const void *base) const
{ {
TRACE_SANITIZE (this); TRACE_SANITIZE (this);
return_trace (likely (c->check_struct (this) && return_trace (likely (c->check_struct (this) &&
(base+settingTable).sanitize (c, nSettings))); (base+settingTableZ).sanitize (c, nSettings)));
} }
protected: protected:
HBUINT16 feature; /* Feature type. */ HBUINT16 feature; /* Feature type. */
HBUINT16 nSettings; /* The number of records in the setting name array. */ HBUINT16 nSettings; /* The number of records in the setting name array. */
LOffsetTo<UnsizedArrayOf<SettingName>, false> LOffsetTo<UnsizedArrayOf<SettingName>, false>
settingTable; /* Offset in bytes from the beginning of this table to settingTableZ; /* Offset in bytes from the beginning of this table to
* this feature's setting name array. The actual type of * this feature's setting name array. The actual type of
* record this offset refers to will depend on the * record this offset refers to will depend on the
* exclusivity value, as described below. */ * exclusivity value, as described below. */
@ -95,11 +157,47 @@ struct feat
{ {
static const hb_tag_t tableTag = HB_AAT_TAG_feat; static const hb_tag_t tableTag = HB_AAT_TAG_feat;
inline bool has_data (void) const { return version.to_int (); }
inline unsigned int get_feature_types (unsigned int start_offset,
unsigned int *count,
hb_aat_layout_feature_type_t *features) const
{
unsigned int feature_count = featureNameCount;
if (count && *count)
{
unsigned int len = MIN (feature_count - start_offset, *count);
for (unsigned int i = 0; i < len; i++)
features[i] = namesZ[i + start_offset].get_feature_type ();
*count = len;
}
return featureNameCount;
}
inline const FeatureName& get_feature (hb_aat_layout_feature_type_t feature_type) const
{
return namesZ.bsearch (featureNameCount, feature_type);
}
inline hb_ot_name_id_t get_feature_name_id (hb_aat_layout_feature_type_t feature) const
{ return get_feature (feature).get_feature_name_id (); }
inline unsigned int get_selector_infos (hb_aat_layout_feature_type_t feature_type,
unsigned int start_offset,
unsigned int *selectors_count, /* IN/OUT. May be NULL. */
hb_aat_layout_feature_selector_info_t *selectors, /* OUT. May be NULL. */
unsigned int *default_index /* OUT. May be NULL. */) const
{
return get_feature (feature_type).get_selector_infos (start_offset, selectors_count, selectors,
default_index, this);
}
inline bool sanitize (hb_sanitize_context_t *c) const inline bool sanitize (hb_sanitize_context_t *c) const
{ {
TRACE_SANITIZE (this); TRACE_SANITIZE (this);
return_trace (likely (c->check_struct (this) && return_trace (likely (c->check_struct (this) &&
names.sanitize (c, featureNameCount, this))); version.major == 1 &&
namesZ.sanitize (c, featureNameCount, this)));
} }
protected: protected:
@ -109,8 +207,8 @@ struct feat
/* The number of entries in the feature name array. */ /* The number of entries in the feature name array. */
HBUINT16 reserved1; /* Reserved (set to zero). */ HBUINT16 reserved1; /* Reserved (set to zero). */
HBUINT32 reserved2; /* Reserved (set to zero). */ HBUINT32 reserved2; /* Reserved (set to zero). */
UnsizedArrayOf<FeatureName> SortedUnsizedArrayOf<FeatureName>
names; /* The feature name array. */ namesZ; /* The feature name array. */
public: public:
DEFINE_SIZE_STATIC (24); DEFINE_SIZE_STATIC (24);
}; };

View File

@ -49,7 +49,7 @@ kerxTupleKern (int value,
const void *base, const void *base,
hb_aat_apply_context_t *c) hb_aat_apply_context_t *c)
{ {
if (likely (!tupleCount)) return value; if (likely (!tupleCount || !c)) return value;
unsigned int offset = value; unsigned int offset = value;
const FWORD *pv = &StructAtOffset<FWORD> (base, offset); const FWORD *pv = &StructAtOffset<FWORD> (base, offset);
@ -93,21 +93,11 @@ struct KernPair
template <typename KernSubTableHeader> template <typename KernSubTableHeader>
struct KerxSubTableFormat0 struct KerxSubTableFormat0
{ {
inline int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const
{
hb_glyph_pair_t pair = {left, right};
int i = pairs.bsearch (pair);
if (i == -1) return 0;
return pairs[i].get_kerning ();
}
inline int get_kerning (hb_codepoint_t left, hb_codepoint_t right, inline int get_kerning (hb_codepoint_t left, hb_codepoint_t right,
hb_aat_apply_context_t *c) const hb_aat_apply_context_t *c = nullptr) const
{ {
hb_glyph_pair_t pair = {left, right}; hb_glyph_pair_t pair = {left, right};
int i = pairs.bsearch (pair); int v = pairs.bsearch (pair).get_kerning ();
if (i == -1) return 0;
int v = pairs[i].get_kerning ();
return kerxTupleKern (v, header.tuple_count (), this, c); return kerxTupleKern (v, header.tuple_count (), this, c);
} }
@ -265,7 +255,7 @@ struct KerxSubTableFormat1
unsigned int tuple_count = MAX (1u, table->header.tuple_count ()); unsigned int tuple_count = MAX (1u, table->header.tuple_count ());
unsigned int kern_idx = Format1EntryT::kernActionIndex (entry); unsigned int kern_idx = Format1EntryT::kernActionIndex (entry);
kern_idx = Types::offsetToIndex (kern_idx, &table->machine, kernAction.arrayZ); kern_idx = Types::byteOffsetToIndex (kern_idx, &table->machine, kernAction.arrayZ);
const FWORD *actions = &kernAction[kern_idx]; const FWORD *actions = &kernAction[kern_idx];
if (!c->sanitizer.check_array (actions, depth, tuple_count)) if (!c->sanitizer.check_array (actions, depth, tuple_count))
{ {
@ -402,9 +392,13 @@ struct KerxSubTableFormat2
unsigned int num_glyphs = c->sanitizer.get_num_glyphs (); unsigned int num_glyphs = c->sanitizer.get_num_glyphs ();
unsigned int l = (this+leftClassTable).get_class (left, num_glyphs, 0); unsigned int l = (this+leftClassTable).get_class (left, num_glyphs, 0);
unsigned int r = (this+rightClassTable).get_class (right, num_glyphs, 0); unsigned int r = (this+rightClassTable).get_class (right, num_glyphs, 0);
unsigned int offset = l + r;
const FWORD *v = &StructAtOffset<FWORD> (&(this+array), offset); const UnsizedArrayOf<FWORD> &arrayZ = this+array;
unsigned int kern_idx = l + r;
kern_idx = Types::offsetToIndex (kern_idx, this, &arrayZ);
const FWORD *v = &arrayZ[kern_idx];
if (unlikely (!v->sanitize (&c->sanitizer))) return 0; if (unlikely (!v->sanitize (&c->sanitizer))) return 0;
return kerxTupleKern (*v, header.tuple_count (), this, c); return kerxTupleKern (*v, header.tuple_count (), this, c);
} }
@ -447,19 +441,13 @@ struct KerxSubTableFormat2
c->check_range (this, array))); c->check_range (this, array)));
} }
/* Note:
* OT kern table specifies ClassTable as having 16-bit entries, whereas
* AAT kern table specifies them as having 8bit entries.
* I've not seen any fonts with this format in kern table.
* We follow AAT. */
protected: protected:
KernSubTableHeader header; KernSubTableHeader header;
HBUINT rowWidth; /* The width, in bytes, of a row in the table. */ HBUINT rowWidth; /* The width, in bytes, of a row in the table. */
OffsetTo<typename Types::ClassType, HBUINT, false> OffsetTo<typename Types::ClassTypeWide, HBUINT, false>
leftClassTable; /* Offset from beginning of this subtable to leftClassTable; /* Offset from beginning of this subtable to
* left-hand class table. */ * left-hand class table. */
OffsetTo<typename Types::ClassType, HBUINT, false> OffsetTo<typename Types::ClassTypeWide, HBUINT, false>
rightClassTable;/* Offset from beginning of this subtable to rightClassTable;/* Offset from beginning of this subtable to
* right-hand class table. */ * right-hand class table. */
OffsetTo<UnsizedArrayOf<FWORD>, HBUINT, false> OffsetTo<UnsizedArrayOf<FWORD>, HBUINT, false>
@ -812,6 +800,7 @@ struct KerxSubTable
{ {
TRACE_SANITIZE (this); TRACE_SANITIZE (this);
if (!u.header.sanitize (c) || if (!u.header.sanitize (c) ||
u.header.length <= u.header.static_size ||
!c->check_range (this, u.header.length)) !c->check_range (this, u.header.length))
return_trace (false); return_trace (false);
@ -842,6 +831,21 @@ struct KerxTable
/* https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern */ /* https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern */
inline const T* thiz (void) const { return static_cast<const T *> (this); } inline const T* thiz (void) const { return static_cast<const T *> (this); }
inline bool has_state_machine (void) const
{
typedef typename T::SubTable SubTable;
const SubTable *st = &thiz()->firstSubTable;
unsigned int count = thiz()->tableCount;
for (unsigned int i = 0; i < count; i++)
{
if (st->get_type () == 1)
return true;
st = &StructAfter<SubTable> (*st);
}
return false;
}
inline bool has_cross_stream (void) const inline bool has_cross_stream (void) const
{ {
typedef typename T::SubTable SubTable; typedef typename T::SubTable SubTable;
@ -920,9 +924,11 @@ struct KerxTable
if (reverse) if (reverse)
c->buffer->reverse (); c->buffer->reverse ();
c->sanitizer.set_object (*st); {
/* See comment in sanitize() for conditional here. */
ret |= st->dispatch (c); hb_sanitize_with_object_t with (&c->sanitizer, i < count - 1 ? st : (const SubTable *) nullptr);
ret |= st->dispatch (c);
}
if (reverse) if (reverse)
c->buffer->reverse (); c->buffer->reverse ();
@ -951,8 +957,20 @@ struct KerxTable
unsigned int count = thiz()->tableCount; unsigned int count = thiz()->tableCount;
for (unsigned int i = 0; i < count; i++) for (unsigned int i = 0; i < count; i++)
{ {
if (unlikely (!st->u.header.sanitize (c)))
return_trace (false);
/* OpenType kern table has 2-byte subtable lengths. That's limiting.
* MS implementation also only supports one subtable, of format 0,
* anyway. Certain versions of some fonts, like Calibry, contain
* kern subtable that exceeds 64kb. Looks like, the subtable length
* is simply ignored. Which makes sense. It's only needed if you
* have multiple subtables. To handle such fonts, we just ignore
* the length for the last subtable. */
hb_sanitize_with_object_t with (c, i < count - 1 ? st : (const SubTable *) nullptr);
if (unlikely (!st->sanitize (c))) if (unlikely (!st->sanitize (c)))
return_trace (false); return_trace (false);
st = &StructAfter<SubTable> (*st); st = &StructAfter<SubTable> (*st);
} }

View File

@ -52,10 +52,10 @@ struct lcar
const OffsetTo<LigCaretClassEntry>* entry_offset = lookup.get_value (glyph, const OffsetTo<LigCaretClassEntry>* entry_offset = lookup.get_value (glyph,
font->face->get_num_glyphs ()); font->face->get_num_glyphs ());
const LigCaretClassEntry& array = entry_offset ? this+*entry_offset : Null (LigCaretClassEntry); const LigCaretClassEntry& array = entry_offset ? this+*entry_offset : Null (LigCaretClassEntry);
if (caret_count && *caret_count) if (caret_count)
{ {
const HBINT16 *arr = array.sub_array (start_offset, caret_count); hb_array_t<const HBINT16> arr = array.sub_array (start_offset, caret_count);
unsigned int count = *caret_count; unsigned int count = arr.len;
for (unsigned int i = 0; i < count; ++i) for (unsigned int i = 0; i < count; ++i)
switch (format) switch (format)
{ {

View File

@ -911,14 +911,22 @@ struct ChainSubtable
} }
} }
inline bool apply (hb_aat_apply_context_t *c) const
{
TRACE_APPLY (this);
hb_sanitize_with_object_t with (&c->sanitizer, this);
return_trace (dispatch (c));
}
inline bool sanitize (hb_sanitize_context_t *c) const inline bool sanitize (hb_sanitize_context_t *c) const
{ {
TRACE_SANITIZE (this); TRACE_SANITIZE (this);
if (!length.sanitize (c) || if (!length.sanitize (c) ||
length < min_size || length <= min_size ||
!c->check_range (this, length)) !c->check_range (this, length))
return_trace (false); return_trace (false);
hb_sanitize_with_object_t with (c, this);
return_trace (dispatch (c)); return_trace (dispatch (c));
} }
@ -949,21 +957,21 @@ struct Chain
unsigned int count = featureCount; unsigned int count = featureCount;
for (unsigned i = 0; i < count; i++) for (unsigned i = 0; i < count; i++)
{ {
const Feature &feature = featureZ[i]; const Feature &feature = featureZ[i];
uint16_t type = feature.featureType; hb_aat_layout_feature_type_t type = (hb_aat_layout_feature_type_t) (unsigned int) feature.featureType;
uint16_t setting = feature.featureSetting; hb_aat_layout_feature_selector_t setting = (hb_aat_layout_feature_selector_t) (unsigned int) feature.featureSetting;
retry: retry:
const hb_aat_map_builder_t::feature_info_t *info = map->features.bsearch (type); const hb_aat_map_builder_t::feature_info_t *info = map->features.bsearch ((uint16_t) type);
if (info && info->setting == setting) if (info && info->setting == setting)
{ {
flags &= feature.disableFlags; flags &= feature.disableFlags;
flags |= feature.enableFlags; flags |= feature.enableFlags;
} }
else if (type == 3/*kLetterCaseType*/ && setting == 3/*kSmallCapsSelector*/) else if (type == HB_AAT_LAYOUT_FEATURE_TYPE_LETTER_CASE && setting == HB_AAT_LAYOUT_FEATURE_SELECTOR_SMALL_CAPS)
{ {
/* Deprecated. https://github.com/harfbuzz/harfbuzz/issues/1342 */ /* Deprecated. https://github.com/harfbuzz/harfbuzz/issues/1342 */
type = 37/*kLowerCaseType*/; type = HB_AAT_LAYOUT_FEATURE_TYPE_LOWER_CASE;
setting = 1/*kLowerCaseSmallCapsSelector*/; setting = HB_AAT_LAYOUT_FEATURE_SELECTOR_LOWER_CASE_SMALL_CAPS;
goto retry; goto retry;
} }
} }
@ -1026,9 +1034,7 @@ struct Chain
if (reverse) if (reverse)
c->buffer->reverse (); c->buffer->reverse ();
c->sanitizer.set_object (*subtable); subtable->apply (c);
subtable->dispatch (c);
if (reverse) if (reverse)
c->buffer->reverse (); c->buffer->reverse ();

View File

@ -133,7 +133,6 @@ struct TrackData
if (!sizes) return 0.; if (!sizes) return 0.;
if (sizes == 1) return trackTableEntry->get_value (base, 0, sizes); if (sizes == 1) return trackTableEntry->get_value (base, 0, sizes);
/* TODO bfind() */
hb_array_t<const Fixed> size_table ((base+sizeTable).arrayZ, sizes); hb_array_t<const Fixed> size_table ((base+sizeTable).arrayZ, sizes);
unsigned int size_index; unsigned int size_index;
for (size_index = 0; size_index < sizes - 1; size_index++) for (size_index = 0; size_index < sizes - 1; size_index++)

View File

@ -1,5 +1,6 @@
/* /*
* Copyright © 2017 Google, Inc. * Copyright © 2017 Google, Inc.
* Copyright © 2018 Ebrahim Byagowi
* *
* This is part of HarfBuzz, a text shaping library. * This is part of HarfBuzz, a text shaping library.
* *
@ -30,7 +31,7 @@
#include "hb-aat-layout.hh" #include "hb-aat-layout.hh"
#include "hb-aat-layout-ankr-table.hh" #include "hb-aat-layout-ankr-table.hh"
#include "hb-aat-layout-bsln-table.hh" // Just so we compile it; unused otherwise. #include "hb-aat-layout-bsln-table.hh" // Just so we compile it; unused otherwise.
#include "hb-aat-layout-feat-table.hh" // Just so we compile it; unused otherwise. #include "hb-aat-layout-feat-table.hh"
#include "hb-aat-layout-just-table.hh" // Just so we compile it; unused otherwise. #include "hb-aat-layout-just-table.hh" // Just so we compile it; unused otherwise.
#include "hb-aat-layout-kerx-table.hh" #include "hb-aat-layout-kerx-table.hh"
#include "hb-aat-layout-morx-table.hh" #include "hb-aat-layout-morx-table.hh"
@ -38,86 +39,96 @@
#include "hb-aat-ltag-table.hh" #include "hb-aat-ltag-table.hh"
/**
* SECTION:hb-aat-layout
* @title: hb-aat-layout
* @short_description: Apple Advanced Typography Layout
* @include: hb-aat.h
*
* Functions for querying OpenType Layout features in the font face.
**/
/* Table data courtesy of Apple. Converted from mnemonics to integers /* Table data courtesy of Apple. Converted from mnemonics to integers
* when moving to this file. */ * when moving to this file. */
static const hb_aat_feature_mapping_t feature_mappings[] = static const hb_aat_feature_mapping_t feature_mappings[] =
{ {
{HB_TAG ('a','f','r','c'), 11/*kFractionsType*/, 1/*kVerticalFractionsSelector*/, 0/*kNoFractionsSelector*/}, {HB_TAG ('a','f','r','c'), HB_AAT_LAYOUT_FEATURE_TYPE_FRACTIONS, HB_AAT_LAYOUT_FEATURE_SELECTOR_VERTICAL_FRACTIONS, HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_FRACTIONS},
{HB_TAG ('c','2','p','c'), 38/*kUpperCaseType*/, 2/*kUpperCasePetiteCapsSelector*/, 0/*kDefaultUpperCaseSelector*/}, {HB_TAG ('c','2','p','c'), HB_AAT_LAYOUT_FEATURE_TYPE_UPPER_CASE, HB_AAT_LAYOUT_FEATURE_SELECTOR_UPPER_CASE_PETITE_CAPS, HB_AAT_LAYOUT_FEATURE_SELECTOR_DEFAULT_UPPER_CASE},
{HB_TAG ('c','2','s','c'), 38/*kUpperCaseType*/, 1/*kUpperCaseSmallCapsSelector*/, 0/*kDefaultUpperCaseSelector*/}, {HB_TAG ('c','2','s','c'), HB_AAT_LAYOUT_FEATURE_TYPE_UPPER_CASE, HB_AAT_LAYOUT_FEATURE_SELECTOR_UPPER_CASE_SMALL_CAPS, HB_AAT_LAYOUT_FEATURE_SELECTOR_DEFAULT_UPPER_CASE},
{HB_TAG ('c','a','l','t'), 36/*kContextualAlternatesType*/, 0/*kContextualAlternatesOnSelector*/, 1/*kContextualAlternatesOffSelector*/}, {HB_TAG ('c','a','l','t'), HB_AAT_LAYOUT_FEATURE_TYPE_CONTEXTUAL_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_ALTERNATES_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_ALTERNATES_OFF},
{HB_TAG ('c','a','s','e'), 33/*kCaseSensitiveLayoutType*/, 0/*kCaseSensitiveLayoutOnSelector*/, 1/*kCaseSensitiveLayoutOffSelector*/}, {HB_TAG ('c','a','s','e'), HB_AAT_LAYOUT_FEATURE_TYPE_CASE_SENSITIVE_LAYOUT, HB_AAT_LAYOUT_FEATURE_SELECTOR_CASE_SENSITIVE_LAYOUT_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_CASE_SENSITIVE_LAYOUT_OFF},
{HB_TAG ('c','l','i','g'), 1/*kLigaturesType*/, 18/*kContextualLigaturesOnSelector*/, 19/*kContextualLigaturesOffSelector*/}, {HB_TAG ('c','l','i','g'), HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES, HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_LIGATURES_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_LIGATURES_OFF},
{HB_TAG ('c','p','s','p'), 33/*kCaseSensitiveLayoutType*/, 2/*kCaseSensitiveSpacingOnSelector*/, 3/*kCaseSensitiveSpacingOffSelector*/}, {HB_TAG ('c','p','s','p'), HB_AAT_LAYOUT_FEATURE_TYPE_CASE_SENSITIVE_LAYOUT, HB_AAT_LAYOUT_FEATURE_SELECTOR_CASE_SENSITIVE_SPACING_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_CASE_SENSITIVE_SPACING_OFF},
{HB_TAG ('c','s','w','h'), 36/*kContextualAlternatesType*/, 4/*kContextualSwashAlternatesOnSelector*/, 5/*kContextualSwashAlternatesOffSelector*/}, {HB_TAG ('c','s','w','h'), HB_AAT_LAYOUT_FEATURE_TYPE_CONTEXTUAL_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_SWASH_ALTERNATES_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_SWASH_ALTERNATES_OFF},
{HB_TAG ('d','l','i','g'), 1/*kLigaturesType*/, 4/*kRareLigaturesOnSelector*/, 5/*kRareLigaturesOffSelector*/}, {HB_TAG ('d','l','i','g'), HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES, HB_AAT_LAYOUT_FEATURE_SELECTOR_RARE_LIGATURES_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_RARE_LIGATURES_OFF},
{HB_TAG ('e','x','p','t'), 20/*kCharacterShapeType*/, 10/*kExpertCharactersSelector*/, 16}, {HB_TAG ('e','x','p','t'), HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE, HB_AAT_LAYOUT_FEATURE_SELECTOR_EXPERT_CHARACTERS, (hb_aat_layout_feature_selector_t) 16},
{HB_TAG ('f','r','a','c'), 11/*kFractionsType*/, 2/*kDiagonalFractionsSelector*/, 0/*kNoFractionsSelector*/}, {HB_TAG ('f','r','a','c'), HB_AAT_LAYOUT_FEATURE_TYPE_FRACTIONS, HB_AAT_LAYOUT_FEATURE_SELECTOR_DIAGONAL_FRACTIONS, HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_FRACTIONS},
{HB_TAG ('f','w','i','d'), 22/*kTextSpacingType*/, 1/*kMonospacedTextSelector*/, 7}, {HB_TAG ('f','w','i','d'), HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING, HB_AAT_LAYOUT_FEATURE_SELECTOR_MONOSPACED_TEXT, (hb_aat_layout_feature_selector_t) 7},
{HB_TAG ('h','a','l','t'), 22/*kTextSpacingType*/, 6/*kAltHalfWidthTextSelector*/, 7}, {HB_TAG ('h','a','l','t'), HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING, HB_AAT_LAYOUT_FEATURE_SELECTOR_ALT_HALF_WIDTH_TEXT, (hb_aat_layout_feature_selector_t) 7},
{HB_TAG ('h','i','s','t'), 1/*kLigaturesType*/, 20/*kHistoricalLigaturesOnSelector*/, 21/*kHistoricalLigaturesOffSelector*/}, {HB_TAG ('h','i','s','t'), HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES, HB_AAT_LAYOUT_FEATURE_SELECTOR_HISTORICAL_LIGATURES_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_HISTORICAL_LIGATURES_OFF},
{HB_TAG ('h','k','n','a'), 34/*kAlternateKanaType*/, 0/*kAlternateHorizKanaOnSelector*/, 1/*kAlternateHorizKanaOffSelector*/, }, {HB_TAG ('h','k','n','a'), HB_AAT_LAYOUT_FEATURE_TYPE_ALTERNATE_KANA, HB_AAT_LAYOUT_FEATURE_SELECTOR_ALTERNATE_HORIZ_KANA_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_ALTERNATE_HORIZ_KANA_OFF},
{HB_TAG ('h','l','i','g'), 1/*kLigaturesType*/, 20/*kHistoricalLigaturesOnSelector*/, 21/*kHistoricalLigaturesOffSelector*/}, {HB_TAG ('h','l','i','g'), HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES, HB_AAT_LAYOUT_FEATURE_SELECTOR_HISTORICAL_LIGATURES_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_HISTORICAL_LIGATURES_OFF},
{HB_TAG ('h','n','g','l'), 23/*kTransliterationType*/, 1/*kHanjaToHangulSelector*/, 0/*kNoTransliterationSelector*/}, {HB_TAG ('h','n','g','l'), HB_AAT_LAYOUT_FEATURE_TYPE_TRANSLITERATION, HB_AAT_LAYOUT_FEATURE_SELECTOR_HANJA_TO_HANGUL, HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_TRANSLITERATION},
{HB_TAG ('h','o','j','o'), 20/*kCharacterShapeType*/, 12/*kHojoCharactersSelector*/, 16}, {HB_TAG ('h','o','j','o'), HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE, HB_AAT_LAYOUT_FEATURE_SELECTOR_HOJO_CHARACTERS, (hb_aat_layout_feature_selector_t) 16},
{HB_TAG ('h','w','i','d'), 22/*kTextSpacingType*/, 2/*kHalfWidthTextSelector*/, 7}, {HB_TAG ('h','w','i','d'), HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING, HB_AAT_LAYOUT_FEATURE_SELECTOR_HALF_WIDTH_TEXT, (hb_aat_layout_feature_selector_t) 7},
{HB_TAG ('i','t','a','l'), 32/*kItalicCJKRomanType*/, 2/*kCJKItalicRomanOnSelector*/, 3/*kCJKItalicRomanOffSelector*/}, {HB_TAG ('i','t','a','l'), HB_AAT_LAYOUT_FEATURE_TYPE_ITALIC_CJK_ROMAN, HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_ITALIC_ROMAN_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_ITALIC_ROMAN_OFF},
{HB_TAG ('j','p','0','4'), 20/*kCharacterShapeType*/, 11/*kJIS2004CharactersSelector*/, 16}, {HB_TAG ('j','p','0','4'), HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE, HB_AAT_LAYOUT_FEATURE_SELECTOR_JIS2004_CHARACTERS, (hb_aat_layout_feature_selector_t) 16},
{HB_TAG ('j','p','7','8'), 20/*kCharacterShapeType*/, 2/*kJIS1978CharactersSelector*/, 16}, {HB_TAG ('j','p','7','8'), HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE, HB_AAT_LAYOUT_FEATURE_SELECTOR_JIS1978_CHARACTERS, (hb_aat_layout_feature_selector_t) 16},
{HB_TAG ('j','p','8','3'), 20/*kCharacterShapeType*/, 3/*kJIS1983CharactersSelector*/, 16}, {HB_TAG ('j','p','8','3'), HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE, HB_AAT_LAYOUT_FEATURE_SELECTOR_JIS1983_CHARACTERS, (hb_aat_layout_feature_selector_t) 16},
{HB_TAG ('j','p','9','0'), 20/*kCharacterShapeType*/, 4/*kJIS1990CharactersSelector*/, 16}, {HB_TAG ('j','p','9','0'), HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE, HB_AAT_LAYOUT_FEATURE_SELECTOR_JIS1990_CHARACTERS, (hb_aat_layout_feature_selector_t) 16},
{HB_TAG ('l','i','g','a'), 1/*kLigaturesType*/, 2/*kCommonLigaturesOnSelector*/, 3/*kCommonLigaturesOffSelector*/}, {HB_TAG ('l','i','g','a'), HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES, HB_AAT_LAYOUT_FEATURE_SELECTOR_COMMON_LIGATURES_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_COMMON_LIGATURES_OFF},
{HB_TAG ('l','n','u','m'), 21/*kNumberCaseType*/, 1/*kUpperCaseNumbersSelector*/, 2}, {HB_TAG ('l','n','u','m'), HB_AAT_LAYOUT_FEATURE_TYPE_NUMBER_CASE, HB_AAT_LAYOUT_FEATURE_SELECTOR_UPPER_CASE_NUMBERS, (hb_aat_layout_feature_selector_t) 2},
{HB_TAG ('m','g','r','k'), 15/*kMathematicalExtrasType*/, 10/*kMathematicalGreekOnSelector*/, 11/*kMathematicalGreekOffSelector*/}, {HB_TAG ('m','g','r','k'), HB_AAT_LAYOUT_FEATURE_TYPE_MATHEMATICAL_EXTRAS, HB_AAT_LAYOUT_FEATURE_SELECTOR_MATHEMATICAL_GREEK_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_MATHEMATICAL_GREEK_OFF},
{HB_TAG ('n','l','c','k'), 20/*kCharacterShapeType*/, 13/*kNLCCharactersSelector*/, 16}, {HB_TAG ('n','l','c','k'), HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE, HB_AAT_LAYOUT_FEATURE_SELECTOR_NLCCHARACTERS, (hb_aat_layout_feature_selector_t) 16},
{HB_TAG ('o','n','u','m'), 21/*kNumberCaseType*/, 0/*kLowerCaseNumbersSelector*/, 2}, {HB_TAG ('o','n','u','m'), HB_AAT_LAYOUT_FEATURE_TYPE_NUMBER_CASE, HB_AAT_LAYOUT_FEATURE_SELECTOR_LOWER_CASE_NUMBERS, (hb_aat_layout_feature_selector_t) 2},
{HB_TAG ('o','r','d','n'), 10/*kVerticalPositionType*/, 3/*kOrdinalsSelector*/, 0/*kNormalPositionSelector*/}, {HB_TAG ('o','r','d','n'), HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_POSITION, HB_AAT_LAYOUT_FEATURE_SELECTOR_ORDINALS, HB_AAT_LAYOUT_FEATURE_SELECTOR_NORMAL_POSITION},
{HB_TAG ('p','a','l','t'), 22/*kTextSpacingType*/, 5/*kAltProportionalTextSelector*/, 7}, {HB_TAG ('p','a','l','t'), HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING, HB_AAT_LAYOUT_FEATURE_SELECTOR_ALT_PROPORTIONAL_TEXT, (hb_aat_layout_feature_selector_t) 7},
{HB_TAG ('p','c','a','p'), 37/*kLowerCaseType*/, 2/*kLowerCasePetiteCapsSelector*/, 0/*kDefaultLowerCaseSelector*/}, {HB_TAG ('p','c','a','p'), HB_AAT_LAYOUT_FEATURE_TYPE_LOWER_CASE, HB_AAT_LAYOUT_FEATURE_SELECTOR_LOWER_CASE_PETITE_CAPS, HB_AAT_LAYOUT_FEATURE_SELECTOR_DEFAULT_LOWER_CASE},
{HB_TAG ('p','k','n','a'), 22/*kTextSpacingType*/, 0/*kProportionalTextSelector*/, 7}, {HB_TAG ('p','k','n','a'), HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING, HB_AAT_LAYOUT_FEATURE_SELECTOR_PROPORTIONAL_TEXT, (hb_aat_layout_feature_selector_t) 7},
{HB_TAG ('p','n','u','m'), 6/*kNumberSpacingType*/, 1/*kProportionalNumbersSelector*/, 4}, {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'), 22/*kTextSpacingType*/, 0/*kProportionalTextSelector*/, 7}, {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'), 22/*kTextSpacingType*/, 4/*kQuarterWidthTextSelector*/, 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','u','b','y'), 28/*kRubyKanaType*/, 2/*kRubyKanaOnSelector*/, 3/*kRubyKanaOffSelector*/}, {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'), 10/*kVerticalPositionType*/, 4/*kScientificInferiorsSelector*/, 0/*kNormalPositionSelector*/}, {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'), 37/*kLowerCaseType*/, 1/*kLowerCaseSmallCapsSelector*/, 0/*kDefaultLowerCaseSelector*/}, {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},
{HB_TAG ('s','m','p','l'), 20/*kCharacterShapeType*/, 1/*kSimplifiedCharactersSelector*/, 16}, {HB_TAG ('s','m','p','l'), HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE, HB_AAT_LAYOUT_FEATURE_SELECTOR_SIMPLIFIED_CHARACTERS, (hb_aat_layout_feature_selector_t) 16},
{HB_TAG ('s','s','0','1'), 35/*kStylisticAlternativesType*/, 2/*kStylisticAltOneOnSelector*/, 3/*kStylisticAltOneOffSelector*/}, {HB_TAG ('s','s','0','1'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_ONE_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_ONE_OFF},
{HB_TAG ('s','s','0','2'), 35/*kStylisticAlternativesType*/, 4/*kStylisticAltTwoOnSelector*/, 5/*kStylisticAltTwoOffSelector*/}, {HB_TAG ('s','s','0','2'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWO_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWO_OFF},
{HB_TAG ('s','s','0','3'), 35/*kStylisticAlternativesType*/, 6/*kStylisticAltThreeOnSelector*/, 7/*kStylisticAltThreeOffSelector*/}, {HB_TAG ('s','s','0','3'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_THREE_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_THREE_OFF},
{HB_TAG ('s','s','0','4'), 35/*kStylisticAlternativesType*/, 8/*kStylisticAltFourOnSelector*/, 9/*kStylisticAltFourOffSelector*/}, {HB_TAG ('s','s','0','4'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FOUR_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FOUR_OFF},
{HB_TAG ('s','s','0','5'), 35/*kStylisticAlternativesType*/, 10/*kStylisticAltFiveOnSelector*/, 11/*kStylisticAltFiveOffSelector*/}, {HB_TAG ('s','s','0','5'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FIVE_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FIVE_OFF},
{HB_TAG ('s','s','0','6'), 35/*kStylisticAlternativesType*/, 12/*kStylisticAltSixOnSelector*/, 13/*kStylisticAltSixOffSelector*/}, {HB_TAG ('s','s','0','6'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SIX_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SIX_OFF},
{HB_TAG ('s','s','0','7'), 35/*kStylisticAlternativesType*/, 14/*kStylisticAltSevenOnSelector*/, 15/*kStylisticAltSevenOffSelector*/}, {HB_TAG ('s','s','0','7'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SEVEN_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SEVEN_OFF},
{HB_TAG ('s','s','0','8'), 35/*kStylisticAlternativesType*/, 16/*kStylisticAltEightOnSelector*/, 17/*kStylisticAltEightOffSelector*/}, {HB_TAG ('s','s','0','8'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_EIGHT_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_EIGHT_OFF},
{HB_TAG ('s','s','0','9'), 35/*kStylisticAlternativesType*/, 18/*kStylisticAltNineOnSelector*/, 19/*kStylisticAltNineOffSelector*/}, {HB_TAG ('s','s','0','9'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_NINE_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_NINE_OFF},
{HB_TAG ('s','s','1','0'), 35/*kStylisticAlternativesType*/, 20/*kStylisticAltTenOnSelector*/, 21/*kStylisticAltTenOffSelector*/}, {HB_TAG ('s','s','1','0'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TEN_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TEN_OFF},
{HB_TAG ('s','s','1','1'), 35/*kStylisticAlternativesType*/, 22/*kStylisticAltElevenOnSelector*/, 23/*kStylisticAltElevenOffSelector*/}, {HB_TAG ('s','s','1','1'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_ELEVEN_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_ELEVEN_OFF},
{HB_TAG ('s','s','1','2'), 35/*kStylisticAlternativesType*/, 24/*kStylisticAltTwelveOnSelector*/, 25/*kStylisticAltTwelveOffSelector*/}, {HB_TAG ('s','s','1','2'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWELVE_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWELVE_OFF},
{HB_TAG ('s','s','1','3'), 35/*kStylisticAlternativesType*/, 26/*kStylisticAltThirteenOnSelector*/, 27/*kStylisticAltThirteenOffSelector*/}, {HB_TAG ('s','s','1','3'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_THIRTEEN_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_THIRTEEN_OFF},
{HB_TAG ('s','s','1','4'), 35/*kStylisticAlternativesType*/, 28/*kStylisticAltFourteenOnSelector*/, 29/*kStylisticAltFourteenOffSelector*/}, {HB_TAG ('s','s','1','4'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FOURTEEN_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FOURTEEN_OFF},
{HB_TAG ('s','s','1','5'), 35/*kStylisticAlternativesType*/, 30/*kStylisticAltFifteenOnSelector*/, 31/*kStylisticAltFifteenOffSelector*/}, {HB_TAG ('s','s','1','5'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FIFTEEN_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FIFTEEN_OFF},
{HB_TAG ('s','s','1','6'), 35/*kStylisticAlternativesType*/, 32/*kStylisticAltSixteenOnSelector*/, 33/*kStylisticAltSixteenOffSelector*/}, {HB_TAG ('s','s','1','6'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SIXTEEN_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SIXTEEN_OFF},
{HB_TAG ('s','s','1','7'), 35/*kStylisticAlternativesType*/, 34/*kStylisticAltSeventeenOnSelector*/, 35/*kStylisticAltSeventeenOffSelector*/}, {HB_TAG ('s','s','1','7'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SEVENTEEN_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SEVENTEEN_OFF},
{HB_TAG ('s','s','1','8'), 35/*kStylisticAlternativesType*/, 36/*kStylisticAltEighteenOnSelector*/, 37/*kStylisticAltEighteenOffSelector*/}, {HB_TAG ('s','s','1','8'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_EIGHTEEN_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_EIGHTEEN_OFF},
{HB_TAG ('s','s','1','9'), 35/*kStylisticAlternativesType*/, 38/*kStylisticAltNineteenOnSelector*/, 39/*kStylisticAltNineteenOffSelector*/}, {HB_TAG ('s','s','1','9'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_NINETEEN_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_NINETEEN_OFF},
{HB_TAG ('s','s','2','0'), 35/*kStylisticAlternativesType*/, 40/*kStylisticAltTwentyOnSelector*/, 41/*kStylisticAltTwentyOffSelector*/}, {HB_TAG ('s','s','2','0'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWENTY_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWENTY_OFF},
{HB_TAG ('s','u','b','s'), 10/*kVerticalPositionType*/, 2/*kInferiorsSelector*/, 0/*kNormalPositionSelector*/}, {HB_TAG ('s','u','b','s'), HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_POSITION, HB_AAT_LAYOUT_FEATURE_SELECTOR_INFERIORS, HB_AAT_LAYOUT_FEATURE_SELECTOR_NORMAL_POSITION},
{HB_TAG ('s','u','p','s'), 10/*kVerticalPositionType*/, 1/*kSuperiorsSelector*/, 0/*kNormalPositionSelector*/}, {HB_TAG ('s','u','p','s'), HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_POSITION, HB_AAT_LAYOUT_FEATURE_SELECTOR_SUPERIORS, HB_AAT_LAYOUT_FEATURE_SELECTOR_NORMAL_POSITION},
{HB_TAG ('s','w','s','h'), 36/*kContextualAlternatesType*/, 2/*kSwashAlternatesOnSelector*/, 3/*kSwashAlternatesOffSelector*/}, {HB_TAG ('s','w','s','h'), HB_AAT_LAYOUT_FEATURE_TYPE_CONTEXTUAL_ALTERNATIVES, HB_AAT_LAYOUT_FEATURE_SELECTOR_SWASH_ALTERNATES_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_SWASH_ALTERNATES_OFF},
{HB_TAG ('t','i','t','l'), 19/*kStyleOptionsType*/, 4/*kTitlingCapsSelector*/, 0/*kNoStyleOptionsSelector*/}, {HB_TAG ('t','i','t','l'), HB_AAT_LAYOUT_FEATURE_TYPE_STYLE_OPTIONS, HB_AAT_LAYOUT_FEATURE_SELECTOR_TITLING_CAPS, HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_STYLE_OPTIONS},
{HB_TAG ('t','n','a','m'), 20/*kCharacterShapeType*/, 14/*kTraditionalNamesCharactersSelector*/, 16}, {HB_TAG ('t','n','a','m'), HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE, HB_AAT_LAYOUT_FEATURE_SELECTOR_TRADITIONAL_NAMES_CHARACTERS, (hb_aat_layout_feature_selector_t) 16},
{HB_TAG ('t','n','u','m'), 6/*kNumberSpacingType*/, 0/*kMonospacedNumbersSelector*/, 4}, {HB_TAG ('t','n','u','m'), HB_AAT_LAYOUT_FEATURE_TYPE_NUMBER_SPACING, HB_AAT_LAYOUT_FEATURE_SELECTOR_MONOSPACED_NUMBERS, (hb_aat_layout_feature_selector_t) 4},
{HB_TAG ('t','r','a','d'), 20/*kCharacterShapeType*/, 0/*kTraditionalCharactersSelector*/, 16}, {HB_TAG ('t','r','a','d'), HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE, HB_AAT_LAYOUT_FEATURE_SELECTOR_TRADITIONAL_CHARACTERS, (hb_aat_layout_feature_selector_t) 16},
{HB_TAG ('t','w','i','d'), 22/*kTextSpacingType*/, 3/*kThirdWidthTextSelector*/, 7}, {HB_TAG ('t','w','i','d'), HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING, HB_AAT_LAYOUT_FEATURE_SELECTOR_THIRD_WIDTH_TEXT, (hb_aat_layout_feature_selector_t) 7},
{HB_TAG ('u','n','i','c'), 3/*kLetterCaseType*/, 14, 15}, {HB_TAG ('u','n','i','c'), HB_AAT_LAYOUT_FEATURE_TYPE_LETTER_CASE, (hb_aat_layout_feature_selector_t) 14, (hb_aat_layout_feature_selector_t) 15},
{HB_TAG ('v','a','l','t'), 22/*kTextSpacingType*/, 5/*kAltProportionalTextSelector*/, 7}, {HB_TAG ('v','a','l','t'), HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING, HB_AAT_LAYOUT_FEATURE_SELECTOR_ALT_PROPORTIONAL_TEXT, (hb_aat_layout_feature_selector_t) 7},
{HB_TAG ('v','e','r','t'), 4/*kVerticalSubstitutionType*/, 0/*kSubstituteVerticalFormsOnSelector*/, 1/*kSubstituteVerticalFormsOffSelector*/}, {HB_TAG ('v','e','r','t'), HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_SUBSTITUTION, HB_AAT_LAYOUT_FEATURE_SELECTOR_SUBSTITUTE_VERTICAL_FORMS_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_SUBSTITUTE_VERTICAL_FORMS_OFF},
{HB_TAG ('v','h','a','l'), 22/*kTextSpacingType*/, 6/*kAltHalfWidthTextSelector*/, 7}, {HB_TAG ('v','h','a','l'), HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING, HB_AAT_LAYOUT_FEATURE_SELECTOR_ALT_HALF_WIDTH_TEXT, (hb_aat_layout_feature_selector_t) 7},
{HB_TAG ('v','k','n','a'), 34/*kAlternateKanaType*/, 2/*kAlternateVertKanaOnSelector*/, 3/*kAlternateVertKanaOffSelector*/}, {HB_TAG ('v','k','n','a'), HB_AAT_LAYOUT_FEATURE_TYPE_ALTERNATE_KANA, HB_AAT_LAYOUT_FEATURE_SELECTOR_ALTERNATE_VERT_KANA_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_ALTERNATE_VERT_KANA_OFF},
{HB_TAG ('v','p','a','l'), 22/*kTextSpacingType*/, 5/*kAltProportionalTextSelector*/, 7}, {HB_TAG ('v','p','a','l'), HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING, HB_AAT_LAYOUT_FEATURE_SELECTOR_ALT_PROPORTIONAL_TEXT, (hb_aat_layout_feature_selector_t) 7},
{HB_TAG ('v','r','t','2'), 4/*kVerticalSubstitutionType*/, 0/*kSubstituteVerticalFormsOnSelector*/, 1/*kSubstituteVerticalFormsOffSelector*/}, {HB_TAG ('v','r','t','2'), HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_SUBSTITUTION, HB_AAT_LAYOUT_FEATURE_SELECTOR_SUBSTITUTE_VERTICAL_FORMS_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_SUBSTITUTE_VERTICAL_FORMS_OFF},
{HB_TAG ('z','e','r','o'), 14/*kTypographicExtrasType*/, 4/*kSlashedZeroOnSelector*/, 5/*kSlashedZeroOffSelector*/}, {HB_TAG ('z','e','r','o'), HB_AAT_LAYOUT_FEATURE_TYPE_TYPOGRAPHIC_EXTRAS, HB_AAT_LAYOUT_FEATURE_SELECTOR_SLASHED_ZERO_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_SLASHED_ZERO_OFF},
}; };
const hb_aat_feature_mapping_t * const hb_aat_feature_mapping_t *
@ -297,3 +308,65 @@ _hb_aat_language_get (hb_face_t *face,
{ {
return face->table.ltag->get_language (i); return face->table.ltag->get_language (i);
} }
/**
* hb_aat_layout_get_feature_types:
* @face: a face object
* @start_offset: iteration's start offset
* @feature_count:(inout) (allow-none): buffer size as input, filled size as output
* @features: (out caller-allocates) (array length=feature_count): features buffer
*
* Return value: Number of all available feature types.
*
* Since: REPLACEME
*/
unsigned int
hb_aat_layout_get_feature_types (hb_face_t *face,
unsigned int start_offset,
unsigned int *feature_count, /* IN/OUT. May be NULL. */
hb_aat_layout_feature_type_t *features /* OUT. May be NULL. */)
{
return face->table.feat->get_feature_types (start_offset, feature_count, features);
}
/**
* hb_aat_layout_feature_type_get_name_id:
* @face: a face object
* @feature_type: feature id
*
* Return value: Name ID index
*
* Since: REPLACEME
*/
hb_ot_name_id_t
hb_aat_layout_feature_type_get_name_id (hb_face_t *face,
hb_aat_layout_feature_type_t feature_type)
{ return face->table.feat->get_feature_name_id (feature_type); }
/**
* hb_aat_layout_feature_type_get_selectors:
* @face: a face object
* @feature_type: feature id
* @start_offset: iteration's start offset
* @selector_count: (inout) (allow-none): buffer size as input, filled size as output
* @selectors: (out caller-allocates) (array length=selector_count): settings buffer
* @default_index: (out) (allow-none): index of default selector if any
*
* If upon return, @default_index is set to #HB_AAT_LAYOUT_NO_SELECTOR_INDEX, then
* the feature type is non-exclusive. Otherwise, @default_index is the index of
* the selector that is selected by default.
*
* Return value: Number of all available feature selectors.
*
* Since: REPLACEME
*/
unsigned int
hb_aat_layout_feature_type_get_selector_infos (hb_face_t *face,
hb_aat_layout_feature_type_t feature_type,
unsigned int start_offset,
unsigned int *selector_count, /* IN/OUT. May be NULL. */
hb_aat_layout_feature_selector_info_t *selectors, /* OUT. May be NULL. */
unsigned int *default_index /* OUT. May be NULL. */)
{
return face->table.feat->get_selector_infos (feature_type, start_offset, selector_count, selectors, default_index);
}

462
src/hb-aat-layout.h Normal file
View File

@ -0,0 +1,462 @@
/*
* Copyright © 2018 Ebrahim Byagowi
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*/
#ifndef HB_AAT_H_IN
#error "Include <hb-aat.h> instead."
#endif
#ifndef HB_AAT_LAYOUT_H
#define HB_AAT_LAYOUT_H
#include "hb.h"
#include "hb-ot.h"
HB_BEGIN_DECLS
/**
* hb_aat_layout_feature_type_t:
*
*
* Since: REPLACEME
*/
typedef enum
{
HB_AAT_LAYOUT_FEATURE_TYPE_INVALID = 0xFFFF,
HB_AAT_LAYOUT_FEATURE_TYPE_ALL_TYPOGRAPHIC = 0,
HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES = 1,
HB_AAT_LAYOUT_FEATURE_TYPE_CURISVE_CONNECTION = 2,
HB_AAT_LAYOUT_FEATURE_TYPE_LETTER_CASE = 3,
HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_SUBSTITUTION = 4,
HB_AAT_LAYOUT_FEATURE_TYPE_LINGUISTIC_REARRANGEMENT = 5,
HB_AAT_LAYOUT_FEATURE_TYPE_NUMBER_SPACING = 6,
HB_AAT_LAYOUT_FEATURE_TYPE_SMART_SWASH_TYPE = 8,
HB_AAT_LAYOUT_FEATURE_TYPE_DIACRITICS_TYPE = 9,
HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_POSITION = 10,
HB_AAT_LAYOUT_FEATURE_TYPE_FRACTIONS = 11,
HB_AAT_LAYOUT_FEATURE_TYPE_OVERLAPPING_CHARACTERS_TYPE = 13,
HB_AAT_LAYOUT_FEATURE_TYPE_TYPOGRAPHIC_EXTRAS = 14,
HB_AAT_LAYOUT_FEATURE_TYPE_MATHEMATICAL_EXTRAS = 15,
HB_AAT_LAYOUT_FEATURE_TYPE_ORNAMENT_SETS_TYPE = 16,
HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_ALTERNATIVES = 17,
HB_AAT_LAYOUT_FEATURE_TYPE_DESIGN_COMPLEXITY_TYPE = 18,
HB_AAT_LAYOUT_FEATURE_TYPE_STYLE_OPTIONS = 19,
HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE = 20,
HB_AAT_LAYOUT_FEATURE_TYPE_NUMBER_CASE = 21,
HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING = 22,
HB_AAT_LAYOUT_FEATURE_TYPE_TRANSLITERATION = 23,
HB_AAT_LAYOUT_FEATURE_TYPE_ANNOTATION_TYPE = 24,
HB_AAT_LAYOUT_FEATURE_TYPE_KANA_SPACING_TYPE = 25,
HB_AAT_LAYOUT_FEATURE_TYPE_IDEOGRAPHIC_SPACING_TYPE = 26,
HB_AAT_LAYOUT_FEATURE_TYPE_UNICODE_DECOMPOSITION_TYPE = 27,
HB_AAT_LAYOUT_FEATURE_TYPE_RUBY_KANA = 28,
HB_AAT_LAYOUT_FEATURE_TYPE_CJK_SYMBOL_ALTERNATIVES_TYPE = 29,
HB_AAT_LAYOUT_FEATURE_TYPE_IDEOGRAPHIC_ALTERNATIVES_TYPE = 30,
HB_AAT_LAYOUT_FEATURE_TYPE_CJK_VERTICAL_ROMAN_PLACEMENT_TYPE = 31,
HB_AAT_LAYOUT_FEATURE_TYPE_ITALIC_CJK_ROMAN = 32,
HB_AAT_LAYOUT_FEATURE_TYPE_CASE_SENSITIVE_LAYOUT = 33,
HB_AAT_LAYOUT_FEATURE_TYPE_ALTERNATE_KANA = 34,
HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES = 35,
HB_AAT_LAYOUT_FEATURE_TYPE_CONTEXTUAL_ALTERNATIVES = 36,
HB_AAT_LAYOUT_FEATURE_TYPE_LOWER_CASE = 37,
HB_AAT_LAYOUT_FEATURE_TYPE_UPPER_CASE = 38,
HB_AAT_LAYOUT_FEATURE_TYPE_LANGUAGE_TAG_TYPE = 39,
HB_AAT_LAYOUT_FEATURE_TYPE_CJK_ROMAN_SPACING_TYPE = 103,
_HB_AAT_LAYOUT_FEATURE_TYPE_MAX_VALUE= 0x7FFFFFFFu, /*< skip >*/
} hb_aat_layout_feature_type_t;
/**
* hb_aat_layout_feature_selector_t:
*
*
* Since: REPLACEME
*/
typedef enum
{
HB_AAT_LAYOUT_FEATURE_SELECTOR_INVALID = 0xFFFF,
/* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_ALL_TYPOGRAPHIC */
HB_AAT_LAYOUT_FEATURE_SELECTOR_ALL_TYPE_FEATURES_ON = 0,
HB_AAT_LAYOUT_FEATURE_SELECTOR_ALL_TYPE_FEATURES_OFF = 1,
/* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES */
HB_AAT_LAYOUT_FEATURE_SELECTOR_REQUIRED_LIGATURES_ON = 0,
HB_AAT_LAYOUT_FEATURE_SELECTOR_REQUIRED_LIGATURES_OFF = 1,
HB_AAT_LAYOUT_FEATURE_SELECTOR_COMMON_LIGATURES_ON = 2,
HB_AAT_LAYOUT_FEATURE_SELECTOR_COMMON_LIGATURES_OFF = 3,
HB_AAT_LAYOUT_FEATURE_SELECTOR_RARE_LIGATURES_ON = 4,
HB_AAT_LAYOUT_FEATURE_SELECTOR_RARE_LIGATURES_OFF = 5,
HB_AAT_LAYOUT_FEATURE_SELECTOR_LOGOS_ON = 6,
HB_AAT_LAYOUT_FEATURE_SELECTOR_LOGOS_OFF = 7,
HB_AAT_LAYOUT_FEATURE_SELECTOR_REBUS_PICTURES_ON = 8,
HB_AAT_LAYOUT_FEATURE_SELECTOR_REBUS_PICTURES_OFF = 9,
HB_AAT_LAYOUT_FEATURE_SELECTOR_DIPHTHONG_LIGATURES_ON = 10,
HB_AAT_LAYOUT_FEATURE_SELECTOR_DIPHTHONG_LIGATURES_OFF = 11,
HB_AAT_LAYOUT_FEATURE_SELECTOR_SQUARED_LIGATURES_ON = 12,
HB_AAT_LAYOUT_FEATURE_SELECTOR_SQUARED_LIGATURES_OFF = 13,
HB_AAT_LAYOUT_FEATURE_SELECTOR_ABBREV_SQUARED_LIGATURES_ON = 14,
HB_AAT_LAYOUT_FEATURE_SELECTOR_ABBREV_SQUARED_LIGATURES_OFF = 15,
HB_AAT_LAYOUT_FEATURE_SELECTOR_SYMBOL_LIGATURES_ON = 16,
HB_AAT_LAYOUT_FEATURE_SELECTOR_SYMBOL_LIGATURES_OFF = 17,
HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_LIGATURES_ON = 18,
HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_LIGATURES_OFF = 19,
HB_AAT_LAYOUT_FEATURE_SELECTOR_HISTORICAL_LIGATURES_ON = 20,
HB_AAT_LAYOUT_FEATURE_SELECTOR_HISTORICAL_LIGATURES_OFF = 21,
/* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES */
HB_AAT_LAYOUT_FEATURE_SELECTOR_UNCONNECTED = 0,
HB_AAT_LAYOUT_FEATURE_SELECTOR_PARTIALLY_CONNECTED = 1,
HB_AAT_LAYOUT_FEATURE_SELECTOR_CURSIVE = 2,
/* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_LETTER_CASE */
HB_AAT_LAYOUT_FEATURE_SELECTOR_UPPER_AND_LOWER_CASE = 0, /* deprecated */
HB_AAT_LAYOUT_FEATURE_SELECTOR_ALL_CAPS = 1, /* deprecated */
HB_AAT_LAYOUT_FEATURE_SELECTOR_ALL_LOWER_CASE = 2, /* deprecated */
HB_AAT_LAYOUT_FEATURE_SELECTOR_SMALL_CAPS = 3, /* deprecated */
HB_AAT_LAYOUT_FEATURE_SELECTOR_INITIAL_CAPS = 4, /* deprecated */
HB_AAT_LAYOUT_FEATURE_SELECTOR_INITIAL_CAPS_AND_SMALL_CAPS = 5, /* deprecated */
/* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_SUBSTITUTION */
HB_AAT_LAYOUT_FEATURE_SELECTOR_SUBSTITUTE_VERTICAL_FORMS_ON = 0,
HB_AAT_LAYOUT_FEATURE_SELECTOR_SUBSTITUTE_VERTICAL_FORMS_OFF = 1,
/* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_LINGUISTIC_REARRANGEMENT */
HB_AAT_LAYOUT_FEATURE_SELECTOR_LINGUISTIC_REARRANGEMENT_ON = 0,
HB_AAT_LAYOUT_FEATURE_SELECTOR_LINGUISTIC_REARRANGEMENT_OFF = 1,
/* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_NUMBER_SPACING */
HB_AAT_LAYOUT_FEATURE_SELECTOR_MONOSPACED_NUMBERS = 0,
HB_AAT_LAYOUT_FEATURE_SELECTOR_PROPORTIONAL_NUMBERS = 1,
HB_AAT_LAYOUT_FEATURE_SELECTOR_THIRD_WIDTH_NUMBERS = 2,
HB_AAT_LAYOUT_FEATURE_SELECTOR_QUARTER_WIDTH_NUMBERS = 3,
/* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_SMART_SWASH_TYPE */
HB_AAT_LAYOUT_FEATURE_SELECTOR_WORD_INITIAL_SWASHES_ON = 0,
HB_AAT_LAYOUT_FEATURE_SELECTOR_WORD_INITIAL_SWASHES_OFF = 1,
HB_AAT_LAYOUT_FEATURE_SELECTOR_WORD_FINAL_SWASHES_ON = 2,
HB_AAT_LAYOUT_FEATURE_SELECTOR_WORD_FINAL_SWASHES_OFF = 3,
HB_AAT_LAYOUT_FEATURE_SELECTOR_LINE_INITIAL_SWASHES_ON = 4,
HB_AAT_LAYOUT_FEATURE_SELECTOR_LINE_INITIAL_SWASHES_OFF = 5,
HB_AAT_LAYOUT_FEATURE_SELECTOR_LINE_FINAL_SWASHES_ON = 6,
HB_AAT_LAYOUT_FEATURE_SELECTOR_LINE_FINAL_SWASHES_OFF = 7,
HB_AAT_LAYOUT_FEATURE_SELECTOR_NON_FINAL_SWASHES_ON = 8,
HB_AAT_LAYOUT_FEATURE_SELECTOR_NON_FINAL_SWASHES_OFF = 9,
/* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_DIACRITICS_TYPE */
HB_AAT_LAYOUT_FEATURE_SELECTOR_SHOW_DIACRITICS = 0,
HB_AAT_LAYOUT_FEATURE_SELECTOR_HIDE_DIACRITICS = 1,
HB_AAT_LAYOUT_FEATURE_SELECTOR_DECOMPOSE_DIACRITICS = 2,
/* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_POSITION */
HB_AAT_LAYOUT_FEATURE_SELECTOR_NORMAL_POSITION = 0,
HB_AAT_LAYOUT_FEATURE_SELECTOR_SUPERIORS = 1,
HB_AAT_LAYOUT_FEATURE_SELECTOR_INFERIORS = 2,
HB_AAT_LAYOUT_FEATURE_SELECTOR_ORDINALS = 3,
HB_AAT_LAYOUT_FEATURE_SELECTOR_SCIENTIFIC_INFERIORS = 4,
/* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_FRACTIONS */
HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_FRACTIONS = 0,
HB_AAT_LAYOUT_FEATURE_SELECTOR_VERTICAL_FRACTIONS = 1,
HB_AAT_LAYOUT_FEATURE_SELECTOR_DIAGONAL_FRACTIONS = 2,
/* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_OVERLAPPING_CHARACTERS_TYPE */
HB_AAT_LAYOUT_FEATURE_SELECTOR_PREVENT_OVERLAP_ON = 0,
HB_AAT_LAYOUT_FEATURE_SELECTOR_PREVENT_OVERLAP_OFF = 1,
/* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_TYPOGRAPHIC_EXTRAS */
HB_AAT_LAYOUT_FEATURE_SELECTOR_HYPHENS_TO_EM_DASH_ON = 0,
HB_AAT_LAYOUT_FEATURE_SELECTOR_HYPHENS_TO_EM_DASH_OFF = 1,
HB_AAT_LAYOUT_FEATURE_SELECTOR_HYPHEN_TO_EN_DASH_ON = 2,
HB_AAT_LAYOUT_FEATURE_SELECTOR_HYPHEN_TO_EN_DASH_OFF = 3,
HB_AAT_LAYOUT_FEATURE_SELECTOR_SLASHED_ZERO_ON = 4,
HB_AAT_LAYOUT_FEATURE_SELECTOR_SLASHED_ZERO_OFF = 5,
HB_AAT_LAYOUT_FEATURE_SELECTOR_FORM_INTERROBANG_ON = 6,
HB_AAT_LAYOUT_FEATURE_SELECTOR_FORM_INTERROBANG_OFF = 7,
HB_AAT_LAYOUT_FEATURE_SELECTOR_SMART_QUOTES_ON = 8,
HB_AAT_LAYOUT_FEATURE_SELECTOR_SMART_QUOTES_OFF = 9,
HB_AAT_LAYOUT_FEATURE_SELECTOR_PERIODS_TO_ELLIPSIS_ON = 10,
HB_AAT_LAYOUT_FEATURE_SELECTOR_PERIODS_TO_ELLIPSIS_OFF = 11,
/* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_MATHEMATICAL_EXTRAS */
HB_AAT_LAYOUT_FEATURE_SELECTOR_HYPHEN_TO_MINUS_ON = 0,
HB_AAT_LAYOUT_FEATURE_SELECTOR_HYPHEN_TO_MINUS_OFF = 1,
HB_AAT_LAYOUT_FEATURE_SELECTOR_ASTERISK_TO_MULTIPLY_ON = 2,
HB_AAT_LAYOUT_FEATURE_SELECTOR_ASTERISK_TO_MULTIPLY_OFF = 3,
HB_AAT_LAYOUT_FEATURE_SELECTOR_SLASH_TO_DIVIDE_ON = 4,
HB_AAT_LAYOUT_FEATURE_SELECTOR_SLASH_TO_DIVIDE_OFF = 5,
HB_AAT_LAYOUT_FEATURE_SELECTOR_INEQUALITY_LIGATURES_ON = 6,
HB_AAT_LAYOUT_FEATURE_SELECTOR_INEQUALITY_LIGATURES_OFF = 7,
HB_AAT_LAYOUT_FEATURE_SELECTOR_EXPONENTS_ON = 8,
HB_AAT_LAYOUT_FEATURE_SELECTOR_EXPONENTS_OFF = 9,
HB_AAT_LAYOUT_FEATURE_SELECTOR_MATHEMATICAL_GREEK_ON = 10,
HB_AAT_LAYOUT_FEATURE_SELECTOR_MATHEMATICAL_GREEK_OFF = 11,
/* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_ORNAMENT_SETS_TYPE */
HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_ORNAMENTS = 0,
HB_AAT_LAYOUT_FEATURE_SELECTOR_DINGBATS = 1,
HB_AAT_LAYOUT_FEATURE_SELECTOR_PI_CHARACTERS = 2,
HB_AAT_LAYOUT_FEATURE_SELECTOR_FLEURONS = 3,
HB_AAT_LAYOUT_FEATURE_SELECTOR_DECORATIVE_BORDERS = 4,
HB_AAT_LAYOUT_FEATURE_SELECTOR_INTERNATIONAL_SYMBOLS = 5,
HB_AAT_LAYOUT_FEATURE_SELECTOR_MATH_SYMBOLS = 6,
/* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_ALTERNATIVES */
HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_ALTERNATES = 0,
/* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_DESIGN_COMPLEXITY_TYPE */
HB_AAT_LAYOUT_FEATURE_SELECTOR_DESIGN_LEVEL1 = 0,
HB_AAT_LAYOUT_FEATURE_SELECTOR_DESIGN_LEVEL2 = 1,
HB_AAT_LAYOUT_FEATURE_SELECTOR_DESIGN_LEVEL3 = 2,
HB_AAT_LAYOUT_FEATURE_SELECTOR_DESIGN_LEVEL4 = 3,
HB_AAT_LAYOUT_FEATURE_SELECTOR_DESIGN_LEVEL5 = 4,
/* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLE_OPTIONS */
HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_STYLE_OPTIONS = 0,
HB_AAT_LAYOUT_FEATURE_SELECTOR_DISPLAY_TEXT = 1,
HB_AAT_LAYOUT_FEATURE_SELECTOR_ENGRAVED_TEXT = 2,
HB_AAT_LAYOUT_FEATURE_SELECTOR_ILLUMINATED_CAPS = 3,
HB_AAT_LAYOUT_FEATURE_SELECTOR_TITLING_CAPS = 4,
HB_AAT_LAYOUT_FEATURE_SELECTOR_TALL_CAPS = 5,
/* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE */
HB_AAT_LAYOUT_FEATURE_SELECTOR_TRADITIONAL_CHARACTERS = 0,
HB_AAT_LAYOUT_FEATURE_SELECTOR_SIMPLIFIED_CHARACTERS = 1,
HB_AAT_LAYOUT_FEATURE_SELECTOR_JIS1978_CHARACTERS = 2,
HB_AAT_LAYOUT_FEATURE_SELECTOR_JIS1983_CHARACTERS = 3,
HB_AAT_LAYOUT_FEATURE_SELECTOR_JIS1990_CHARACTERS = 4,
HB_AAT_LAYOUT_FEATURE_SELECTOR_TRADITIONAL_ALT_ONE = 5,
HB_AAT_LAYOUT_FEATURE_SELECTOR_TRADITIONAL_ALT_TWO = 6,
HB_AAT_LAYOUT_FEATURE_SELECTOR_TRADITIONAL_ALT_THREE = 7,
HB_AAT_LAYOUT_FEATURE_SELECTOR_TRADITIONAL_ALT_FOUR = 8,
HB_AAT_LAYOUT_FEATURE_SELECTOR_TRADITIONAL_ALT_FIVE = 9,
HB_AAT_LAYOUT_FEATURE_SELECTOR_EXPERT_CHARACTERS = 10,
HB_AAT_LAYOUT_FEATURE_SELECTOR_JIS2004_CHARACTERS = 11,
HB_AAT_LAYOUT_FEATURE_SELECTOR_HOJO_CHARACTERS = 12,
HB_AAT_LAYOUT_FEATURE_SELECTOR_NLCCHARACTERS = 13,
HB_AAT_LAYOUT_FEATURE_SELECTOR_TRADITIONAL_NAMES_CHARACTERS = 14,
/* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_NUMBER_CASE */
HB_AAT_LAYOUT_FEATURE_SELECTOR_LOWER_CASE_NUMBERS = 0,
HB_AAT_LAYOUT_FEATURE_SELECTOR_UPPER_CASE_NUMBERS = 1,
/* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING */
HB_AAT_LAYOUT_FEATURE_SELECTOR_PROPORTIONAL_TEXT = 0,
HB_AAT_LAYOUT_FEATURE_SELECTOR_MONOSPACED_TEXT = 1,
HB_AAT_LAYOUT_FEATURE_SELECTOR_HALF_WIDTH_TEXT = 2,
HB_AAT_LAYOUT_FEATURE_SELECTOR_THIRD_WIDTH_TEXT = 3,
HB_AAT_LAYOUT_FEATURE_SELECTOR_QUARTER_WIDTH_TEXT = 4,
HB_AAT_LAYOUT_FEATURE_SELECTOR_ALT_PROPORTIONAL_TEXT = 5,
HB_AAT_LAYOUT_FEATURE_SELECTOR_ALT_HALF_WIDTH_TEXT = 6,
/* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_TRANSLITERATION */
HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_TRANSLITERATION = 0,
HB_AAT_LAYOUT_FEATURE_SELECTOR_HANJA_TO_HANGUL = 1,
HB_AAT_LAYOUT_FEATURE_SELECTOR_HIRAGANA_TO_KATAKANA = 2,
HB_AAT_LAYOUT_FEATURE_SELECTOR_KATAKANA_TO_HIRAGANA = 3,
HB_AAT_LAYOUT_FEATURE_SELECTOR_KANA_TO_ROMANIZATION = 4,
HB_AAT_LAYOUT_FEATURE_SELECTOR_ROMANIZATION_TO_HIRAGANA = 5,
HB_AAT_LAYOUT_FEATURE_SELECTOR_ROMANIZATION_TO_KATAKANA = 6,
HB_AAT_LAYOUT_FEATURE_SELECTOR_HANJA_TO_HANGUL_ALT_ONE = 7,
HB_AAT_LAYOUT_FEATURE_SELECTOR_HANJA_TO_HANGUL_ALT_TWO = 8,
HB_AAT_LAYOUT_FEATURE_SELECTOR_HANJA_TO_HANGUL_ALT_THREE = 9,
/* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_ANNOTATION_TYPE */
HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_ANNOTATION = 0,
HB_AAT_LAYOUT_FEATURE_SELECTOR_BOX_ANNOTATION = 1,
HB_AAT_LAYOUT_FEATURE_SELECTOR_ROUNDED_BOX_ANNOTATION = 2,
HB_AAT_LAYOUT_FEATURE_SELECTOR_CIRCLE_ANNOTATION = 3,
HB_AAT_LAYOUT_FEATURE_SELECTOR_INVERTED_CIRCLE_ANNOTATION = 4,
HB_AAT_LAYOUT_FEATURE_SELECTOR_PARENTHESIS_ANNOTATION = 5,
HB_AAT_LAYOUT_FEATURE_SELECTOR_PERIOD_ANNOTATION = 6,
HB_AAT_LAYOUT_FEATURE_SELECTOR_ROMAN_NUMERAL_ANNOTATION = 7,
HB_AAT_LAYOUT_FEATURE_SELECTOR_DIAMOND_ANNOTATION = 8,
HB_AAT_LAYOUT_FEATURE_SELECTOR_INVERTED_BOX_ANNOTATION = 9,
HB_AAT_LAYOUT_FEATURE_SELECTOR_INVERTED_ROUNDED_BOX_ANNOTATION= 10,
/* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_KANA_SPACING_TYPE */
HB_AAT_LAYOUT_FEATURE_SELECTOR_FULL_WIDTH_KANA = 0,
HB_AAT_LAYOUT_FEATURE_SELECTOR_PROPORTIONAL_KANA = 1,
/* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_IDEOGRAPHIC_SPACING_TYPE */
HB_AAT_LAYOUT_FEATURE_SELECTOR_FULL_WIDTH_IDEOGRAPHS = 0,
HB_AAT_LAYOUT_FEATURE_SELECTOR_PROPORTIONAL_IDEOGRAPHS = 1,
HB_AAT_LAYOUT_FEATURE_SELECTOR_HALF_WIDTH_IDEOGRAPHS = 2,
/* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_UNICODE_DECOMPOSITION_TYPE */
HB_AAT_LAYOUT_FEATURE_SELECTOR_CANONICAL_COMPOSITION_ON = 0,
HB_AAT_LAYOUT_FEATURE_SELECTOR_CANONICAL_COMPOSITION_OFF = 1,
HB_AAT_LAYOUT_FEATURE_SELECTOR_COMPATIBILITY_COMPOSITION_ON = 2,
HB_AAT_LAYOUT_FEATURE_SELECTOR_COMPATIBILITY_COMPOSITION_OFF = 3,
HB_AAT_LAYOUT_FEATURE_SELECTOR_TRANSCODING_COMPOSITION_ON = 4,
HB_AAT_LAYOUT_FEATURE_SELECTOR_TRANSCODING_COMPOSITION_OFF = 5,
/* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_RUBY_KANA */
HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_RUBY_KANA = 0, /* deprecated - use HB_AAT_LAYOUT_FEATURE_SELECTOR_RUBY_KANA_OFF instead */
HB_AAT_LAYOUT_FEATURE_SELECTOR_RUBY_KANA = 1, /* deprecated - use HB_AAT_LAYOUT_FEATURE_SELECTOR_RUBY_KANA_ON instead */
HB_AAT_LAYOUT_FEATURE_SELECTOR_RUBY_KANA_ON = 2,
HB_AAT_LAYOUT_FEATURE_SELECTOR_RUBY_KANA_OFF = 3,
/* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_CJK_SYMBOL_ALTERNATIVES_TYPE */
HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_CJK_SYMBOL_ALTERNATIVES = 0,
HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_SYMBOL_ALT_ONE = 1,
HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_SYMBOL_ALT_TWO = 2,
HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_SYMBOL_ALT_THREE = 3,
HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_SYMBOL_ALT_FOUR = 4,
HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_SYMBOL_ALT_FIVE = 5,
/* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_IDEOGRAPHIC_ALTERNATIVES_TYPE */
HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_IDEOGRAPHIC_ALTERNATIVES = 0,
HB_AAT_LAYOUT_FEATURE_SELECTOR_IDEOGRAPHIC_ALT_ONE = 1,
HB_AAT_LAYOUT_FEATURE_SELECTOR_IDEOGRAPHIC_ALT_TWO = 2,
HB_AAT_LAYOUT_FEATURE_SELECTOR_IDEOGRAPHIC_ALT_THREE = 3,
HB_AAT_LAYOUT_FEATURE_SELECTOR_IDEOGRAPHIC_ALT_FOUR = 4,
HB_AAT_LAYOUT_FEATURE_SELECTOR_IDEOGRAPHIC_ALT_FIVE = 5,
/* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_CJK_VERTICAL_ROMAN_PLACEMENT_TYPE */
HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_VERTICAL_ROMAN_CENTERED = 0,
HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_VERTICAL_ROMAN_HBASELINE = 1,
/* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_ITALIC_CJK_ROMAN */
HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_CJK_ITALIC_ROMAN = 0, /* deprecated - use HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_ITALIC_ROMAN_OFF instead */
HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_ITALIC_ROMAN = 1, /* deprecated - use HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_ITALIC_ROMAN_ON instead */
HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_ITALIC_ROMAN_ON = 2,
HB_AAT_LAYOUT_FEATURE_SELECTOR_CJK_ITALIC_ROMAN_OFF = 3,
/* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_CASE_SENSITIVE_LAYOUT */
HB_AAT_LAYOUT_FEATURE_SELECTOR_CASE_SENSITIVE_LAYOUT_ON = 0,
HB_AAT_LAYOUT_FEATURE_SELECTOR_CASE_SENSITIVE_LAYOUT_OFF = 1,
HB_AAT_LAYOUT_FEATURE_SELECTOR_CASE_SENSITIVE_SPACING_ON = 2,
HB_AAT_LAYOUT_FEATURE_SELECTOR_CASE_SENSITIVE_SPACING_OFF = 3,
/* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_ALTERNATE_KANA */
HB_AAT_LAYOUT_FEATURE_SELECTOR_ALTERNATE_HORIZ_KANA_ON = 0,
HB_AAT_LAYOUT_FEATURE_SELECTOR_ALTERNATE_HORIZ_KANA_OFF = 1,
HB_AAT_LAYOUT_FEATURE_SELECTOR_ALTERNATE_VERT_KANA_ON = 2,
HB_AAT_LAYOUT_FEATURE_SELECTOR_ALTERNATE_VERT_KANA_OFF = 3,
/* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES */
HB_AAT_LAYOUT_FEATURE_SELECTOR_NO_STYLISTIC_ALTERNATES = 0,
HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_ONE_ON = 2,
HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_ONE_OFF = 3,
HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWO_ON = 4,
HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWO_OFF = 5,
HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_THREE_ON = 6,
HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_THREE_OFF = 7,
HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FOUR_ON = 8,
HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FOUR_OFF = 9,
HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FIVE_ON = 10,
HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FIVE_OFF = 11,
HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SIX_ON = 12,
HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SIX_OFF = 13,
HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SEVEN_ON = 14,
HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SEVEN_OFF = 15,
HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_EIGHT_ON = 16,
HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_EIGHT_OFF = 17,
HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_NINE_ON = 18,
HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_NINE_OFF = 19,
HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TEN_ON = 20,
HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TEN_OFF = 21,
HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_ELEVEN_ON = 22,
HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_ELEVEN_OFF = 23,
HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWELVE_ON = 24,
HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWELVE_OFF = 25,
HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_THIRTEEN_ON = 26,
HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_THIRTEEN_OFF = 27,
HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FOURTEEN_ON = 28,
HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FOURTEEN_OFF = 29,
HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FIFTEEN_ON = 30,
HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_FIFTEEN_OFF = 31,
HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SIXTEEN_ON = 32,
HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SIXTEEN_OFF = 33,
HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SEVENTEEN_ON = 34,
HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_SEVENTEEN_OFF = 35,
HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_EIGHTEEN_ON = 36,
HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_EIGHTEEN_OFF = 37,
HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_NINETEEN_ON = 38,
HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_NINETEEN_OFF = 39,
HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWENTY_ON = 40,
HB_AAT_LAYOUT_FEATURE_SELECTOR_STYLISTIC_ALT_TWENTY_OFF = 41,
/* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_CONTEXTUAL_ALTERNATIVES */
HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_ALTERNATES_ON = 0,
HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_ALTERNATES_OFF = 1,
HB_AAT_LAYOUT_FEATURE_SELECTOR_SWASH_ALTERNATES_ON = 2,
HB_AAT_LAYOUT_FEATURE_SELECTOR_SWASH_ALTERNATES_OFF = 3,
HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_SWASH_ALTERNATES_ON = 4,
HB_AAT_LAYOUT_FEATURE_SELECTOR_CONTEXTUAL_SWASH_ALTERNATES_OFF= 5,
/* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_LOWER_CASE */
HB_AAT_LAYOUT_FEATURE_SELECTOR_DEFAULT_LOWER_CASE = 0,
HB_AAT_LAYOUT_FEATURE_SELECTOR_LOWER_CASE_SMALL_CAPS = 1,
HB_AAT_LAYOUT_FEATURE_SELECTOR_LOWER_CASE_PETITE_CAPS = 2,
/* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_UPPER_CASE */
HB_AAT_LAYOUT_FEATURE_SELECTOR_DEFAULT_UPPER_CASE = 0,
HB_AAT_LAYOUT_FEATURE_SELECTOR_UPPER_CASE_SMALL_CAPS = 1,
HB_AAT_LAYOUT_FEATURE_SELECTOR_UPPER_CASE_PETITE_CAPS = 2,
/* Selectors for #HB_AAT_LAYOUT_FEATURE_TYPE_CJK_ROMAN_SPACING_TYPE */
HB_AAT_LAYOUT_FEATURE_SELECTOR_HALF_WIDTH_CJK_ROMAN = 0,
HB_AAT_LAYOUT_FEATURE_SELECTOR_PROPORTIONAL_CJK_ROMAN = 1,
HB_AAT_LAYOUT_FEATURE_SELECTOR_DEFAULT_CJK_ROMAN = 2,
HB_AAT_LAYOUT_FEATURE_SELECTOR_FULL_WIDTH_CJK_ROMAN = 3,
_HB_AAT_LAYOUT_FEATURE_SELECTOR_MAX_VALUE= 0x7FFFFFFFu, /*< skip >*/
} hb_aat_layout_feature_selector_t;
HB_EXTERN unsigned int
hb_aat_layout_get_feature_types (hb_face_t *face,
unsigned int start_offset,
unsigned int *feature_count, /* IN/OUT. May be NULL. */
hb_aat_layout_feature_type_t *features /* OUT. May be NULL. */);
HB_EXTERN hb_ot_name_id_t
hb_aat_layout_feature_type_get_name_id (hb_face_t *face,
hb_aat_layout_feature_type_t feature_type);
typedef struct hb_aat_layout_feature_selector_info_t
{
hb_ot_name_id_t name_id;
hb_aat_layout_feature_selector_t enable;
hb_aat_layout_feature_selector_t disable;
/*< private >*/
unsigned int reserved;
} hb_aat_layout_feature_selector_info_t;
#define HB_AAT_LAYOUT_NO_SELECTOR_INDEX 0xFFFFu
HB_EXTERN unsigned int
hb_aat_layout_feature_type_get_selector_infos (hb_face_t *face,
hb_aat_layout_feature_type_t feature_type,
unsigned int start_offset,
unsigned int *selector_count, /* IN/OUT. May be NULL. */
hb_aat_layout_feature_selector_info_t *selectors, /* OUT. May be NULL. */
unsigned int *default_index /* OUT. May be NULL. */);
HB_END_DECLS
#endif /* HB_AAT_LAYOUT_H */

View File

@ -35,9 +35,9 @@
struct hb_aat_feature_mapping_t struct hb_aat_feature_mapping_t
{ {
hb_tag_t otFeatureTag; hb_tag_t otFeatureTag;
uint16_t aatFeatureType; hb_aat_layout_feature_type_t aatFeatureType;
uint16_t selectorToEnable; hb_aat_layout_feature_selector_t selectorToEnable;
uint16_t selectorToDisable; hb_aat_layout_feature_selector_t selectorToDisable;
static inline int cmp (const void *key_, const void *entry_) static inline int cmp (const void *key_, const void *entry_)
{ {

View File

@ -37,8 +37,8 @@ void hb_aat_map_builder_t::add_feature (hb_tag_t tag,
if (tag == HB_TAG ('a','a','l','t')) if (tag == HB_TAG ('a','a','l','t'))
{ {
feature_info_t *info = features.push(); feature_info_t *info = features.push();
info->type = 17/*kCharacterAlternativesType*/; info->type = HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_ALTERNATIVES;
info->setting = value; info->setting = (hb_aat_layout_feature_selector_t) value;
return; return;
} }

View File

@ -65,8 +65,8 @@ struct hb_aat_map_builder_t
public: public:
struct feature_info_t struct feature_info_t
{ {
uint16_t type; hb_aat_layout_feature_type_t type;
uint16_t setting; hb_aat_layout_feature_selector_t setting;
unsigned seq; /* For stable sorting only. */ unsigned seq; /* For stable sorting only. */
static int cmp (const void *pa, const void *pb) static int cmp (const void *pa, const void *pb)
@ -77,9 +77,9 @@ struct hb_aat_map_builder_t
(a->seq < b->seq ? -1 : a->seq > b->seq ? 1 : 0); (a->seq < b->seq ? -1 : a->seq > b->seq ? 1 : 0);
} }
int cmp (const short unsigned int *ty) const int cmp (unsigned int ty) const
{ {
return (type != *ty) ? (type < *ty ? -1 : 1) : 0; return (type != ty) ? (type < ty ? -1 : 1) : 0;
} }
}; };

38
src/hb-aat.h Normal file
View File

@ -0,0 +1,38 @@
/*
* Copyright © 2018 Ebrahim Byagowi
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*/
#ifndef HB_AAT_H
#define HB_AAT_H
#define HB_AAT_H_IN
#include "hb.h"
#include "hb-aat-layout.h"
HB_BEGIN_DECLS
HB_END_DECLS
#undef HB_AAT_H_IN
#endif /* HB_AAT_H */

View File

@ -282,7 +282,7 @@ struct hb_atomic_int_t
template <typename P> template <typename P>
struct hb_atomic_ptr_t struct hb_atomic_ptr_t
{ {
typedef typename hb_remove_pointer<P>::value T; typedef typename hb_remove_pointer (P) T;
inline void init (T* v_ = nullptr) { set_relaxed (v_); } inline void init (T* v_ = nullptr) { set_relaxed (v_); }
inline void set_relaxed (T* v_) { hb_atomic_ptr_impl_set_relaxed (&v, v_); } inline void set_relaxed (T* v_) { hb_atomic_ptr_impl_set_relaxed (&v, v_); }

View File

@ -60,7 +60,7 @@ struct hb_blob_t
template <typename Type> template <typename Type>
inline const Type* as (void) const inline const Type* as (void) const
{ {
return length < Type::min_size ? &Null(Type) : reinterpret_cast<const Type *> (data); return length < hb_null_size (Type) ? &Null(Type) : reinterpret_cast<const Type *> (data);
} }
inline hb_bytes_t as_bytes (void) const inline hb_bytes_t as_bytes (void) const
{ {
@ -86,7 +86,7 @@ struct hb_blob_t
template <typename P> template <typename P>
struct hb_blob_ptr_t struct hb_blob_ptr_t
{ {
typedef typename hb_remove_pointer<P>::value T; typedef typename hb_remove_pointer (P) T;
inline hb_blob_ptr_t (hb_blob_t *b_ = nullptr) : b (b_) {} inline hb_blob_ptr_t (hb_blob_t *b_ = nullptr) : b (b_) {}
inline hb_blob_t * operator = (hb_blob_t *b_) { return b = b_; } inline hb_blob_t * operator = (hb_blob_t *b_) { return b = b_; }

View File

@ -204,7 +204,7 @@ static const char canon_map[256] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '-', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '-', 0, 0,
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 0, 0, 0, 0, 0, 0, '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 0, 0, 0, 0, 0, 0,
'-', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 0, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, 0, 0, 0, '-', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, 0, 0, 0, '-',
0, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 0, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, 0, 0, 0, 0 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, 0, 0, 0, 0
@ -276,7 +276,7 @@ struct hb_language_item_t {
static hb_atomic_ptr_t <hb_language_item_t> langs; static hb_atomic_ptr_t <hb_language_item_t> langs;
#ifdef HB_USE_ATEXIT #if HB_USE_ATEXIT
static void static void
free_langs (void) free_langs (void)
{ {
@ -323,7 +323,7 @@ retry:
goto retry; goto retry;
} }
#ifdef HB_USE_ATEXIT #if HB_USE_ATEXIT
if (!first_lang) if (!first_lang)
atexit (free_langs); /* First person registers atexit() callback. */ atexit (free_langs); /* First person registers atexit() callback. */
#endif #endif
@ -780,18 +780,18 @@ parse_uint32 (const char **pp, const char *end, uint32_t *pv)
#ifdef USE_XLOCALE #ifdef USE_XLOCALE
#ifdef HB_USE_ATEXIT #if HB_USE_ATEXIT
static void free_static_C_locale (void); static void free_static_C_locale (void);
#endif #endif
static struct hb_C_locale_lazy_loader_t : hb_lazy_loader_t<hb_remove_pointer<HB_LOCALE_T>::value, static struct hb_C_locale_lazy_loader_t : hb_lazy_loader_t<hb_remove_pointer (HB_LOCALE_T),
hb_C_locale_lazy_loader_t> hb_C_locale_lazy_loader_t>
{ {
static inline HB_LOCALE_T create (void) static inline HB_LOCALE_T create (void)
{ {
HB_LOCALE_T C_locale = HB_CREATE_LOCALE ("C"); HB_LOCALE_T C_locale = HB_CREATE_LOCALE ("C");
#ifdef HB_USE_ATEXIT #if HB_USE_ATEXIT
atexit (free_static_C_locale); atexit (free_static_C_locale);
#endif #endif
@ -807,7 +807,7 @@ static struct hb_C_locale_lazy_loader_t : hb_lazy_loader_t<hb_remove_pointer<HB_
} }
} static_C_locale; } static_C_locale;
#ifdef HB_USE_ATEXIT #if HB_USE_ATEXIT
static static
void free_static_C_locale (void) void free_static_C_locale (void)
{ {

View File

@ -293,14 +293,16 @@ struct hb_auto_trace_t
if (plevel) --*plevel; if (plevel) --*plevel;
} }
inline ret_t ret (ret_t v, unsigned int line = 0) inline ret_t ret (ret_t v,
const char *func = "",
unsigned int line = 0)
{ {
if (unlikely (returned)) { if (unlikely (returned)) {
fprintf (stderr, "OUCH, double calls to return_trace(). This is a bug, please report.\n"); fprintf (stderr, "OUCH, double calls to return_trace(). This is a bug, please report.\n");
return v; return v;
} }
_hb_debug_msg<max_level> (what, obj, nullptr, true, plevel ? *plevel : 1, -1, _hb_debug_msg<max_level> (what, obj, func, true, plevel ? *plevel : 1, -1,
"return %s (line %d)", "return %s (line %d)",
hb_printer_t<ret_t>().print (v), line); hb_printer_t<ret_t>().print (v), line);
if (plevel) --*plevel; if (plevel) --*plevel;
@ -325,17 +327,21 @@ struct hb_auto_trace_t<0, ret_t>
const char *message, const char *message,
...) HB_PRINTF_FUNC(6, 7) {} ...) HB_PRINTF_FUNC(6, 7) {}
inline ret_t ret (ret_t v, unsigned int line HB_UNUSED = 0) { return v; } inline ret_t ret (ret_t v,
const char *func HB_UNUSED = 0,
unsigned int line HB_UNUSED = 0) { return v; }
}; };
/* For disabled tracing; optimize out everything. /* For disabled tracing; optimize out everything.
* https://github.com/harfbuzz/harfbuzz/pull/605 */ * https://github.com/harfbuzz/harfbuzz/pull/605 */
template <typename ret_t> template <typename ret_t>
struct hb_no_trace_t { struct hb_no_trace_t {
inline ret_t ret (ret_t v, unsigned int line HB_UNUSED = 0) { return v; } inline ret_t ret (ret_t v,
const char *func HB_UNUSED = "",
unsigned int line HB_UNUSED = 0) { return v; }
}; };
#define return_trace(RET) return trace.ret (RET, __LINE__) #define return_trace(RET) return trace.ret (RET, HB_FUNC, __LINE__)
/* /*

View File

@ -241,6 +241,43 @@ HB_EXTERN HB_DEPRECATED_FOR (hb_ot_tags_from_script_and_language) hb_tag_t
hb_ot_tag_from_language (hb_language_t language); hb_ot_tag_from_language (hb_language_t language);
typedef unsigned int hb_ot_name_id_t; /* Since is in hb-ot.h */
/**
* HB_OT_VAR_NO_AXIS_INDEX:
*
* Since: 1.4.2
* Deprecated: REPLACEME
*/
#define HB_OT_VAR_NO_AXIS_INDEX 0xFFFFFFFFu
/**
* hb_ot_var_axis_t:
*
* Since: 1.4.2
* Deprecated: REPLACEME
*/
typedef struct hb_ot_var_axis_t
{
hb_tag_t tag;
hb_ot_name_id_t name_id;
float min_value;
float default_value;
float max_value;
} hb_ot_var_axis_t;
HB_EXTERN HB_DEPRECATED_FOR (hb_ot_var_get_axis_infos) unsigned int
hb_ot_var_get_axes (hb_face_t *face,
unsigned int start_offset,
unsigned int *axes_count /* IN/OUT */,
hb_ot_var_axis_t *axes_array /* OUT */);
HB_EXTERN HB_DEPRECATED_FOR (hb_ot_var_find_axis_info) hb_bool_t
hb_ot_var_find_axis (hb_face_t *face,
hb_tag_t axis_tag,
unsigned int *axis_index,
hb_ot_var_axis_t *axis_info);
#endif #endif
HB_END_DECLS HB_END_DECLS

View File

@ -559,10 +559,16 @@ struct hb_bytes_t
unsigned int len; unsigned int len;
}; };
template <typename Type>
struct hb_sorted_array_t;
template <typename Type> template <typename Type>
struct hb_array_t struct hb_array_t
{ {
static_assert ((bool) (unsigned) hb_static_size (Type), "");
inline hb_array_t (void) : arrayZ (nullptr), len (0) {} inline hb_array_t (void) : arrayZ (nullptr), len (0) {}
inline hb_array_t (const hb_array_t &o) : arrayZ (o.arrayZ), len (o.len) {}
inline hb_array_t (Type *array_, unsigned int len_) : arrayZ (array_), len (len_) {} inline hb_array_t (Type *array_, unsigned int len_) : arrayZ (array_), len (len_) {}
inline Type& operator [] (unsigned int i) const inline Type& operator [] (unsigned int i) const
@ -571,39 +577,160 @@ struct hb_array_t
return arrayZ[i]; return arrayZ[i];
} }
inline unsigned int get_size (void) const { return len * sizeof (Type); }
template <typename hb_sanitize_context_t>
inline bool sanitize (hb_sanitize_context_t *c) const
{ return c->check_array (arrayZ, len); }
template <typename T> inline operator T * (void) const { return arrayZ; } template <typename T> inline operator T * (void) const { return arrayZ; }
inline Type * operator & (void) const { return arrayZ; } inline Type * operator & (void) const { return arrayZ; }
inline hb_array_t<Type> sub_array (unsigned int start_offset, unsigned int seg_count) const inline unsigned int get_size (void) const { return len * sizeof (Type); }
inline hb_array_t<Type> sub_array (unsigned int start_offset, unsigned int *seg_count /* IN/OUT */) const
{ {
if (!seg_count) return hb_array_t<Type> ();
unsigned int count = len; unsigned int count = len;
if (unlikely (start_offset > count)) if (unlikely (start_offset > count))
count = 0; count = 0;
else else
count -= start_offset; count -= start_offset;
count = MIN (count, seg_count); count = *seg_count = MIN (count, *seg_count);
return hb_array_t<Type> (arrayZ + start_offset, count); return hb_array_t<Type> (arrayZ + start_offset, count);
} }
inline hb_array_t<Type> sub_array (unsigned int start_offset, unsigned int seg_count) const
{ return sub_array (start_offset, &seg_count); }
inline hb_bytes_t as_bytes (void) const inline hb_bytes_t as_bytes (void) const
{ return hb_bytes_t (arrayZ, len * sizeof (Type)); }
template <typename T>
inline Type *lsearch (const T &x,
Type *not_found = nullptr)
{ {
return hb_bytes_t (arrayZ, len * sizeof (Type)); unsigned int count = len;
for (unsigned int i = 0; i < count; i++)
if (!this->arrayZ[i].cmp (x))
return &this->arrayZ[i];
return not_found;
}
template <typename T>
inline const Type *lsearch (const T &x,
const Type *not_found = nullptr) const
{
unsigned int count = len;
for (unsigned int i = 0; i < count; i++)
if (!this->arrayZ[i].cmp (x))
return &this->arrayZ[i];
return not_found;
} }
inline void free (void) { ::free ((void *) arrayZ); arrayZ = nullptr; len = 0; } inline hb_sorted_array_t<Type> qsort (int (*cmp)(const void*, const void*))
{
::qsort (arrayZ, len, sizeof (Type), cmp);
return hb_sorted_array_t<Type> (*this);
}
inline hb_sorted_array_t<Type> qsort (void)
{
::qsort (arrayZ, len, sizeof (Type), Type::cmp);
return hb_sorted_array_t<Type> (*this);
}
inline void qsort (unsigned int start, unsigned int end)
{
end = MIN (end, len);
assert (start <= end);
::qsort (arrayZ + start, end - start, sizeof (Type), Type::cmp);
}
inline void free (void)
{ ::free ((void *) arrayZ); arrayZ = nullptr; len = 0; }
template <typename hb_sanitize_context_t>
inline bool sanitize (hb_sanitize_context_t *c) const
{ return c->check_array (arrayZ, len); }
public:
Type *arrayZ; Type *arrayZ;
unsigned int len; unsigned int len;
}; };
template <typename T> template <typename T>
inline hb_array_t<T> hb_array (T *array, unsigned int len) { return hb_array_t<T> (array, len); } inline hb_array_t<T> hb_array (T *array, unsigned int len)
{ return hb_array_t<T> (array, len); }
enum hb_bfind_not_found_t
{
HB_BFIND_NOT_FOUND_DONT_STORE,
HB_BFIND_NOT_FOUND_STORE,
HB_BFIND_NOT_FOUND_STORE_CLOSEST,
};
template <typename Type>
struct hb_sorted_array_t : hb_array_t<Type>
{
inline hb_sorted_array_t (void) : hb_array_t<Type> () {}
inline hb_sorted_array_t (const hb_array_t<Type> &o) : hb_array_t<Type> (o) {}
inline hb_sorted_array_t (Type *array_, unsigned int len_) : hb_array_t<Type> (array_, len_) {}
inline hb_sorted_array_t<Type> sub_array (unsigned int start_offset, unsigned int *seg_count /* IN/OUT */) const
{ return hb_sorted_array_t<Type> (((const hb_array_t<Type> *) (this))->sub_array (start_offset, seg_count)); }
inline hb_sorted_array_t<Type> sub_array (unsigned int start_offset, unsigned int seg_count) const
{ return sub_array (start_offset, &seg_count); }
template <typename T>
inline Type *bsearch (const T &x, Type *not_found = nullptr)
{
unsigned int i;
return bfind (x, &i) ? &this->arrayZ[i] : not_found;
}
template <typename T>
inline const Type *bsearch (const T &x, const Type *not_found = nullptr) const
{
unsigned int i;
return bfind (x, &i) ? &this->arrayZ[i] : not_found;
}
template <typename T>
inline bool bfind (const T &x, unsigned int *i = nullptr,
hb_bfind_not_found_t not_found = HB_BFIND_NOT_FOUND_DONT_STORE,
unsigned int to_store = (unsigned int) -1) const
{
int min = 0, max = (int) this->len - 1;
const Type *array = this->arrayZ;
while (min <= max)
{
int mid = ((unsigned int) min + (unsigned int) max) / 2;
int c = array[mid].cmp (x);
if (c < 0)
max = mid - 1;
else if (c > 0)
min = mid + 1;
else
{
if (i)
*i = mid;
return true;
}
}
if (i)
{
switch (not_found)
{
case HB_BFIND_NOT_FOUND_DONT_STORE:
break;
case HB_BFIND_NOT_FOUND_STORE:
*i = to_store;
break;
case HB_BFIND_NOT_FOUND_STORE_CLOSEST:
if (max < 0 || (max < (int) this->len && array[max].cmp (x) > 0))
max++;
*i = max;
break;
}
}
return false;
}
};
template <typename T>
inline hb_sorted_array_t<T> hb_sorted_array (T *array, unsigned int len)
{ return hb_sorted_array_t<T> (array, len); }
struct HbOpOr struct HbOpOr

View File

@ -588,10 +588,10 @@ struct hb_face_builder_data_t
{ {
struct table_entry_t struct table_entry_t
{ {
inline int cmp (const hb_tag_t *t) const inline int cmp (hb_tag_t t) const
{ {
if (*t < tag) return -1; if (t < tag) return -1;
if (*t > tag) return -1; if (t > tag) return -1;
return 0; return 0;
} }

View File

@ -478,7 +478,7 @@ hb_ft_get_font_h_extents (hb_font_t *font HB_UNUSED,
return true; return true;
} }
#ifdef HB_USE_ATEXIT #if HB_USE_ATEXIT
static void free_static_ft_funcs (void); static void free_static_ft_funcs (void);
#endif #endif
@ -504,7 +504,7 @@ static struct hb_ft_font_funcs_lazy_loader_t : hb_font_funcs_lazy_loader_t<hb_ft
hb_font_funcs_make_immutable (funcs); hb_font_funcs_make_immutable (funcs);
#ifdef HB_USE_ATEXIT #if HB_USE_ATEXIT
atexit (free_static_ft_funcs); atexit (free_static_ft_funcs);
#endif #endif
@ -512,7 +512,7 @@ static struct hb_ft_font_funcs_lazy_loader_t : hb_font_funcs_lazy_loader_t<hb_ft
} }
} static_ft_funcs; } static_ft_funcs;
#ifdef HB_USE_ATEXIT #if HB_USE_ATEXIT
static static
void free_static_ft_funcs (void) void free_static_ft_funcs (void)
{ {
@ -744,11 +744,11 @@ hb_ft_font_create_referenced (FT_Face ft_face)
return hb_ft_font_create (ft_face, _hb_ft_face_destroy); return hb_ft_font_create (ft_face, _hb_ft_face_destroy);
} }
#ifdef HB_USE_ATEXIT #if HB_USE_ATEXIT
static void free_static_ft_library (void); static void free_static_ft_library (void);
#endif #endif
static struct hb_ft_library_lazy_loader_t : hb_lazy_loader_t<hb_remove_pointer<FT_Library>::value, static struct hb_ft_library_lazy_loader_t : hb_lazy_loader_t<hb_remove_pointer (FT_Library),
hb_ft_library_lazy_loader_t> hb_ft_library_lazy_loader_t>
{ {
static inline FT_Library create (void) static inline FT_Library create (void)
@ -757,7 +757,7 @@ static struct hb_ft_library_lazy_loader_t : hb_lazy_loader_t<hb_remove_pointer<F
if (FT_Init_FreeType (&l)) if (FT_Init_FreeType (&l))
return nullptr; return nullptr;
#ifdef HB_USE_ATEXIT #if HB_USE_ATEXIT
atexit (free_static_ft_library); atexit (free_static_ft_library);
#endif #endif
@ -773,7 +773,7 @@ static struct hb_ft_library_lazy_loader_t : hb_lazy_loader_t<hb_remove_pointer<F
} }
} static_ft_library; } static_ft_library;
#ifdef HB_USE_ATEXIT #if HB_USE_ATEXIT
static static
void free_static_ft_library (void) void free_static_ft_library (void)
{ {

View File

@ -336,7 +336,7 @@ hb_glib_unicode_decompose (hb_unicode_funcs_t *ufuncs HB_UNUSED,
} }
#ifdef HB_USE_ATEXIT #if HB_USE_ATEXIT
static void free_static_glib_funcs (void); static void free_static_glib_funcs (void);
#endif #endif
@ -355,7 +355,7 @@ static struct hb_glib_unicode_funcs_lazy_loader_t : hb_unicode_funcs_lazy_loader
hb_unicode_funcs_make_immutable (funcs); hb_unicode_funcs_make_immutable (funcs);
#ifdef HB_USE_ATEXIT #if HB_USE_ATEXIT
atexit (free_static_glib_funcs); atexit (free_static_glib_funcs);
#endif #endif
@ -363,7 +363,7 @@ static struct hb_glib_unicode_funcs_lazy_loader_t : hb_unicode_funcs_lazy_loader
} }
} static_glib_funcs; } static_glib_funcs;
#ifdef HB_USE_ATEXIT #if HB_USE_ATEXIT
static static
void free_static_glib_funcs (void) void free_static_glib_funcs (void)
{ {

View File

@ -300,7 +300,7 @@ hb_icu_unicode_decompose (hb_unicode_funcs_t *ufuncs HB_UNUSED,
} }
#ifdef HB_USE_ATEXIT #if HB_USE_ATEXIT
static void free_static_icu_funcs (void); static void free_static_icu_funcs (void);
#endif #endif
@ -326,7 +326,7 @@ static struct hb_icu_unicode_funcs_lazy_loader_t : hb_unicode_funcs_lazy_loader_
hb_unicode_funcs_make_immutable (funcs); hb_unicode_funcs_make_immutable (funcs);
#ifdef HB_USE_ATEXIT #if HB_USE_ATEXIT
atexit (free_static_icu_funcs); atexit (free_static_icu_funcs);
#endif #endif
@ -334,7 +334,7 @@ static struct hb_icu_unicode_funcs_lazy_loader_t : hb_unicode_funcs_lazy_loader_
} }
} static_icu_funcs; } static_icu_funcs;
#ifdef HB_USE_ATEXIT #if HB_USE_ATEXIT
static static
void free_static_icu_funcs (void) void free_static_icu_funcs (void)
{ {

View File

@ -82,8 +82,7 @@ static inline Type& StructAfter(TObject &X)
/* Check _assertion in a method environment */ /* Check _assertion in a method environment */
#define _DEFINE_INSTANCE_ASSERTION1(_line, _assertion) \ #define _DEFINE_INSTANCE_ASSERTION1(_line, _assertion) \
inline void _instance_assertion_on_line_##_line (void) const \ inline void _instance_assertion_on_line_##_line (void) const \
{ static_assert ((_assertion), ""); } \ { static_assert ((_assertion), ""); }
static_assert (true, "") /* So we require semicolon here. */
# define _DEFINE_INSTANCE_ASSERTION0(_line, _assertion) _DEFINE_INSTANCE_ASSERTION1 (_line, _assertion) # define _DEFINE_INSTANCE_ASSERTION0(_line, _assertion) _DEFINE_INSTANCE_ASSERTION1 (_line, _assertion)
# define DEFINE_INSTANCE_ASSERTION(_assertion) _DEFINE_INSTANCE_ASSERTION0 (__LINE__, _assertion) # define DEFINE_INSTANCE_ASSERTION(_assertion) _DEFINE_INSTANCE_ASSERTION0 (__LINE__, _assertion)
@ -96,32 +95,36 @@ static inline Type& StructAfter(TObject &X)
#define DEFINE_SIZE_STATIC(size) \ #define DEFINE_SIZE_STATIC(size) \
DEFINE_INSTANCE_ASSERTION (sizeof (*this) == (size)); \ DEFINE_INSTANCE_ASSERTION (sizeof (*this) == (size)) \
inline unsigned int get_size (void) const { return (size); } \ inline unsigned int get_size (void) const { return (size); } \
enum { static_size = (size) }; \ enum { null_size = (size) }; \
enum { min_size = (size) } enum { min_size = (size) }; \
enum { static_size = (size) }
#define DEFINE_SIZE_UNION(size, _member) \ #define DEFINE_SIZE_UNION(size, _member) \
DEFINE_INSTANCE_ASSERTION (0*sizeof(this->u._member.static_size) + sizeof(this->u._member) == (size)); \ DEFINE_COMPILES_ASSERTION ((void) this->u._member.static_size) \
static const unsigned int min_size = (size) DEFINE_INSTANCE_ASSERTION (sizeof(this->u._member) == (size)) \
enum { null_size = (size) }; \
enum { min_size = (size) }
#define DEFINE_SIZE_MIN(size) \ #define DEFINE_SIZE_MIN(size) \
DEFINE_INSTANCE_ASSERTION (sizeof (*this) >= (size)); \ DEFINE_INSTANCE_ASSERTION (sizeof (*this) >= (size)) \
static const unsigned int min_size = (size) enum { null_size = (size) }; \
enum { min_size = (size) }
#define DEFINE_SIZE_UNBOUNDED(size) \
DEFINE_INSTANCE_ASSERTION (sizeof (*this) >= (size)) \
enum { min_size = (size) }
#define DEFINE_SIZE_ARRAY(size, array) \ #define DEFINE_SIZE_ARRAY(size, array) \
DEFINE_INSTANCE_ASSERTION (sizeof (*this) == (size) + VAR * sizeof ((array)[0])); \
DEFINE_COMPILES_ASSERTION ((void) (array)[0].static_size) \ DEFINE_COMPILES_ASSERTION ((void) (array)[0].static_size) \
DEFINE_INSTANCE_ASSERTION (sizeof (*this) == (size) + VAR * sizeof ((array)[0])) \
enum { null_size = (size) }; \
enum { min_size = (size) } enum { min_size = (size) }
#define DEFINE_SIZE_ARRAY_SIZED(size, array) \ #define DEFINE_SIZE_ARRAY_SIZED(size, array) \
inline unsigned int get_size (void) const { return (size - (array).min_size + (array).get_size ()); } \ inline unsigned int get_size (void) const { return (size - (array).min_size + (array).get_size ()); } \
DEFINE_SIZE_ARRAY(size, array) DEFINE_SIZE_ARRAY(size, array)
#define DEFINE_SIZE_ARRAY2(size, array1, array2) \
DEFINE_INSTANCE_ASSERTION (sizeof (*this) == (size) + sizeof (this->array1[0]) + sizeof (this->array2[0])); \
DEFINE_COMPILES_ASSERTION ((void) (array1)[0].static_size; (void) (array2)[0].static_size) \
static const unsigned int min_size = (size)
/* /*
@ -256,26 +259,36 @@ struct hb_sanitize_context_t :
inline void set_max_ops (int max_ops_) { max_ops = max_ops_; } inline void set_max_ops (int max_ops_) { max_ops = max_ops_; }
/* TODO
* This set_object() thing is to use sanitize at runtime lookup
* application time. This is very distinct from the regular
* sanitizer operation, so, eventually, separate into another
* type and make hb_aat_apply_context_t use that one instead
* of abusing this one.
*/
template <typename T> template <typename T>
inline void set_object (const T& obj) inline void set_object (const T *obj)
{ {
this->start = (const char *) &obj; reset_object ();
this->end = (const char *) &obj + obj.get_size ();
if (!obj) return;
const char *obj_start = (const char *) obj;
const char *obj_end = (const char *) obj + obj->get_size ();
assert (obj_start <= obj_end); /* Must not overflow. */
if (unlikely (obj_end < this->start || this->end < obj_start))
this->start = this->end = nullptr;
else
{
this->start = MAX (this->start, obj_start);
this->end = MIN (this->end , obj_end );
}
}
inline void reset_object (void)
{
this->start = this->blob->data;
this->end = this->start + this->blob->length;
assert (this->start <= this->end); /* Must not overflow. */ assert (this->start <= this->end); /* Must not overflow. */
} }
inline void start_processing (void) inline void start_processing (void)
{ {
this->start = this->blob->data; reset_object ();
this->end = this->start + this->blob->length;
assert (this->start <= this->end); /* Must not overflow. */
this->max_ops = MAX ((unsigned int) (this->end - this->start) * HB_SANITIZE_MAX_OPS_FACTOR, this->max_ops = MAX ((unsigned int) (this->end - this->start) * HB_SANITIZE_MAX_OPS_FACTOR,
(unsigned) HB_SANITIZE_MAX_OPS_MIN); (unsigned) HB_SANITIZE_MAX_OPS_MIN);
this->edit_count = 0; this->edit_count = 0;
@ -469,6 +482,23 @@ struct hb_sanitize_context_t :
bool num_glyphs_set; bool num_glyphs_set;
}; };
struct hb_sanitize_with_object_t
{
template <typename T>
inline hb_sanitize_with_object_t (hb_sanitize_context_t *c,
const T& obj) : c (c)
{
c->set_object (obj);
}
inline ~hb_sanitize_with_object_t (void)
{
c->reset_object ();
}
private:
hb_sanitize_context_t *c;
};
/* /*
* Serialize * Serialize
@ -691,6 +721,12 @@ struct BEInt<Type, 2>
} }
inline operator Type (void) const inline operator Type (void) const
{ {
#if defined(__GNUC__) || defined(__clang__)
/* Spoon-feed the compiler a big-endian integer with alignment 1.
* https://github.com/harfbuzz/harfbuzz/pull/1398 */
struct __attribute__((packed)) packed_uint16_t { uint16_t v; };
return __builtin_bswap16 (((packed_uint16_t *) this)->v);
#endif
return (v[0] << 8) return (v[0] << 8)
+ (v[1] ); + (v[1] );
} }

View File

@ -38,21 +38,60 @@
#define HB_NULL_POOL_SIZE 9880 #define HB_NULL_POOL_SIZE 9880
/* Use SFINAE to sniff whether T has min_size; in which case return T::null_size,
* otherwise return sizeof(T). */
/* The hard way...
* https://stackoverflow.com/questions/7776448/sfinae-tried-with-bool-gives-compiler-error-template-argument-tvalue-invol
*/
template<bool> struct _hb_bool_type {};
template <typename T, typename B>
struct _hb_null_size
{ enum { value = sizeof (T) }; };
template <typename T>
struct _hb_null_size<T, _hb_bool_type<(bool) (1 + (unsigned int) T::min_size)> >
{ enum { value = T::null_size }; };
template <typename T>
struct hb_null_size
{ enum { value = _hb_null_size<T, _hb_bool_type<true> >::value }; };
#define hb_null_size(T) hb_null_size<T>::value
/* This doesn't belong here, but since is copy/paste from above, put it here. */
template <typename T, typename B>
struct _hb_static_size
{ enum { value = sizeof (T) }; };
template <typename T>
struct _hb_static_size<T, _hb_bool_type<(bool) (1 + (unsigned int) T::min_size)> >
{ enum { value = T::static_size }; };
template <typename T>
struct hb_static_size
{ enum { value = _hb_static_size<T, _hb_bool_type<true> >::value }; };
#define hb_static_size(T) hb_static_size<T>::value
/*
* Null()
*/
extern HB_INTERNAL extern HB_INTERNAL
hb_vector_size_impl_t const _hb_NullPool[(HB_NULL_POOL_SIZE + sizeof (hb_vector_size_impl_t) - 1) / sizeof (hb_vector_size_impl_t)]; hb_vector_size_impl_t const _hb_NullPool[(HB_NULL_POOL_SIZE + sizeof (hb_vector_size_impl_t) - 1) / sizeof (hb_vector_size_impl_t)];
/* Generic nul-content Null objects. */ /* Generic nul-content Null objects. */
template <typename Type> template <typename Type>
static inline Type const & Null (void) { static inline Type const & Null (void) {
static_assert (sizeof (Type) <= HB_NULL_POOL_SIZE, "Increase HB_NULL_POOL_SIZE."); static_assert (hb_null_size (Type) <= HB_NULL_POOL_SIZE, "Increase HB_NULL_POOL_SIZE.");
return *reinterpret_cast<Type const *> (_hb_NullPool); return *reinterpret_cast<Type const *> (_hb_NullPool);
} }
#define Null(Type) Null<typename hb_remove_const<typename hb_remove_reference<Type>::value>::value>() #define Null(Type) Null<typename hb_remove_const (typename hb_remove_reference (Type))> ()
/* Specializations for arbitrary-content Null objects expressed in bytes. */ /* Specializations for arbitrary-content Null objects expressed in bytes. */
#define DECLARE_NULL_NAMESPACE_BYTES(Namespace, Type) \ #define DECLARE_NULL_NAMESPACE_BYTES(Namespace, Type) \
} /* Close namespace. */ \ } /* Close namespace. */ \
extern HB_INTERNAL const unsigned char _hb_Null_##Namespace##_##Type[Namespace::Type::min_size]; \ extern HB_INTERNAL const unsigned char _hb_Null_##Namespace##_##Type[Namespace::Type::null_size]; \
template <> \ template <> \
/*static*/ inline const Namespace::Type& Null<Namespace::Type> (void) { \ /*static*/ inline const Namespace::Type& Null<Namespace::Type> (void) { \
return *reinterpret_cast<const Namespace::Type *> (_hb_Null_##Namespace##_##Type); \ return *reinterpret_cast<const Namespace::Type *> (_hb_Null_##Namespace##_##Type); \
@ -60,7 +99,7 @@ static inline Type const & Null (void) {
namespace Namespace { \ namespace Namespace { \
static_assert (true, "Just so we take semicolon after.") static_assert (true, "Just so we take semicolon after.")
#define DEFINE_NULL_NAMESPACE_BYTES(Namespace, Type) \ #define DEFINE_NULL_NAMESPACE_BYTES(Namespace, Type) \
const unsigned char _hb_Null_##Namespace##_##Type[Namespace::Type::min_size] const unsigned char _hb_Null_##Namespace##_##Type[Namespace::Type::null_size]
/* Specializations for arbitrary-content Null objects expressed as struct initializer. */ /* Specializations for arbitrary-content Null objects expressed as struct initializer. */
#define DECLARE_NULL_INSTANCE(Type) \ #define DECLARE_NULL_INSTANCE(Type) \
@ -85,12 +124,12 @@ extern HB_INTERNAL
/* CRAP pool: Common Region for Access Protection. */ /* CRAP pool: Common Region for Access Protection. */
template <typename Type> template <typename Type>
static inline Type& Crap (void) { static inline Type& Crap (void) {
static_assert (sizeof (Type) <= HB_NULL_POOL_SIZE, "Increase HB_NULL_POOL_SIZE."); static_assert (hb_null_size (Type) <= HB_NULL_POOL_SIZE, "Increase HB_NULL_POOL_SIZE.");
Type *obj = reinterpret_cast<Type *> (_hb_CrapPool); Type *obj = reinterpret_cast<Type *> (_hb_CrapPool);
memcpy (obj, &Null(Type), sizeof (*obj)); memcpy (obj, &Null(Type), sizeof (*obj));
return *obj; return *obj;
} }
#define Crap(Type) Crap<typename hb_remove_const<typename hb_remove_reference<Type>::value>::value>() #define Crap(Type) Crap<typename hb_remove_const (typename hb_remove_reference (Type))> ()
template <typename Type> template <typename Type>
struct CrapOrNull { struct CrapOrNull {
@ -110,7 +149,7 @@ struct CrapOrNull<const Type> {
template <typename P> template <typename P>
struct hb_nonnull_ptr_t struct hb_nonnull_ptr_t
{ {
typedef typename hb_remove_pointer<P>::value T; typedef typename hb_remove_pointer (P) T;
inline hb_nonnull_ptr_t (T *v_ = nullptr) : v (v_) {} inline hb_nonnull_ptr_t (T *v_ = nullptr) : v (v_) {}
inline T * operator = (T *v_) { return v = v_; } inline T * operator = (T *v_) { return v = v_; }

View File

@ -111,12 +111,7 @@ typedef struct OffsetTable
{ {
Tag t; Tag t;
t.set (tag); t.set (tag);
/* Linear-search for small tables to work around fonts with unsorted return tables.bfind (t, table_index, HB_BFIND_NOT_FOUND_STORE, Index::NOT_FOUND_INDEX);
* table list. */
int i = tables.len < 64 ? tables.lsearch (t) : tables.bsearch (t);
if (table_index)
*table_index = i == -1 ? (unsigned) Index::NOT_FOUND_INDEX : (unsigned) i;
return i != -1;
} }
inline const TableRecord& get_table_by_tag (hb_tag_t tag) const inline const TableRecord& get_table_by_tag (hb_tag_t tag) const
{ {

View File

@ -231,22 +231,31 @@ struct FixedVersion
* Use: (base+offset) * Use: (base+offset)
*/ */
template <typename Type, bool has_null_> struct assert_has_min_size { static_assert (Type::min_size > 0, ""); }; template <typename Type, bool has_null>
template <typename Type> struct assert_has_min_size<Type, false> {}; struct _hb_has_null
{
static inline const Type *get_null (void) { return nullptr; }
static inline Type *get_crap (void) { return nullptr; }
};
template <typename Type>
struct _hb_has_null<Type, true>
{
static inline const Type *get_null (void) { return &Null(Type); }
static inline Type *get_crap (void) { return &Crap(Type); }
};
template <typename Type, typename OffsetType=HBUINT16, bool has_null=true> template <typename Type, typename OffsetType=HBUINT16, bool has_null=true>
struct OffsetTo : Offset<OffsetType, has_null> struct OffsetTo : Offset<OffsetType, has_null>
{ {
static_assert (sizeof (assert_has_min_size<Type, has_null>) || true, "");
inline const Type& operator () (const void *base) const inline const Type& operator () (const void *base) const
{ {
if (unlikely (this->is_null ())) return Null (Type); if (unlikely (this->is_null ())) return *_hb_has_null<Type, has_null>::get_null ();
return StructAtOffset<const Type> (base, *this); return StructAtOffset<const Type> (base, *this);
} }
inline Type& operator () (void *base) const inline Type& operator () (void *base) const
{ {
if (unlikely (this->is_null ())) return Crap (Type); if (unlikely (this->is_null ())) return Crap (Type);
if (unlikely (this->is_null ())) return *_hb_has_null<Type, has_null>::get_crap ();
return StructAtOffset<Type> (base, *this); return StructAtOffset<Type> (base, *this);
} }
@ -322,6 +331,7 @@ struct OffsetTo : Offset<OffsetType, has_null>
DEFINE_SIZE_STATIC (sizeof (OffsetType)); DEFINE_SIZE_STATIC (sizeof (OffsetType));
}; };
template <typename Type, bool has_null=true> struct LOffsetTo : OffsetTo<Type, HBUINT32, has_null> {}; template <typename Type, bool has_null=true> struct LOffsetTo : OffsetTo<Type, HBUINT32, has_null> {};
template <typename Base, typename OffsetType, bool has_null, typename Type> template <typename Base, typename OffsetType, bool has_null, typename Type>
static inline const Type& operator + (const Base &base, const OffsetTo<Type, OffsetType, has_null> &offset) { return offset (base); } static inline const Type& operator + (const Base &base, const OffsetTo<Type, OffsetType, has_null> &offset) { return offset (base); }
template <typename Base, typename OffsetType, bool has_null, typename Type> template <typename Base, typename OffsetType, bool has_null, typename Type>
@ -335,6 +345,8 @@ static inline Type& operator + (Base &base, OffsetTo<Type, OffsetType, has_null>
template <typename Type> template <typename Type>
struct UnsizedArrayOf struct UnsizedArrayOf
{ {
static_assert ((bool) (unsigned) hb_static_size (Type), "");
enum { item_size = Type::static_size }; enum { item_size = Type::static_size };
HB_NO_CREATE_COPY_ASSIGN_TEMPLATE (UnsizedArrayOf, Type); HB_NO_CREATE_COPY_ASSIGN_TEMPLATE (UnsizedArrayOf, Type);
@ -358,8 +370,20 @@ struct UnsizedArrayOf
inline unsigned int get_size (unsigned int len) const inline unsigned int get_size (unsigned int len) const
{ return len * Type::static_size; } { return len * Type::static_size; }
inline hb_array_t<Type> as_array (unsigned int len) { return hb_array_t<Type> (arrayZ, len); } inline hb_array_t<Type> as_array (unsigned int len)
inline hb_array_t<const Type> as_array (unsigned int len) const { return hb_array_t<const Type> (arrayZ, len); } { return hb_array (arrayZ, len); }
inline hb_array_t<const Type> as_array (unsigned int len) const
{ return hb_array (arrayZ, len); }
template <typename T>
inline Type &lsearch (unsigned int len, const T &x, Type &not_found = Crap (Type))
{ return *as_array (len).lsearch (x, &not_found); }
template <typename T>
inline const Type &lsearch (unsigned int len, const T &x, const Type &not_found = Null (Type)) const
{ return *as_array (len).lsearch (x, &not_found); }
inline void qsort (unsigned int len, unsigned int start = 0, unsigned int end = (unsigned int) -1)
{ as_array (len).qsort (start, end); }
inline bool sanitize (hb_sanitize_context_t *c, unsigned int count) const inline bool sanitize (hb_sanitize_context_t *c, unsigned int count) const
{ {
@ -406,7 +430,7 @@ struct UnsizedArrayOf
public: public:
Type arrayZ[VAR]; Type arrayZ[VAR];
public: public:
DEFINE_SIZE_ARRAY (0, arrayZ); DEFINE_SIZE_UNBOUNDED (0);
}; };
/* Unsized array of offset's */ /* Unsized array of offset's */
@ -419,8 +443,17 @@ struct UnsizedOffsetListOf : UnsizedOffsetArrayOf<Type, OffsetType, has_null>
{ {
inline const Type& operator [] (unsigned int i) const inline const Type& operator [] (unsigned int i) const
{ {
return this+this->arrayZ[i]; const OffsetTo<Type, OffsetType, has_null> *p = &this->arrayZ[i];
if (unlikely (p < this->arrayZ)) return Null (Type); /* Overflowed. */
return this+*p;
} }
inline Type& operator [] (unsigned int i)
{
const OffsetTo<Type, OffsetType, has_null> *p = &this->arrayZ[i];
if (unlikely (p < this->arrayZ)) return Crap (Type); /* Overflowed. */
return this+*p;
}
inline bool sanitize (hb_sanitize_context_t *c, unsigned int count) const inline bool sanitize (hb_sanitize_context_t *c, unsigned int count) const
{ {
@ -435,26 +468,39 @@ struct UnsizedOffsetListOf : UnsizedOffsetArrayOf<Type, OffsetType, has_null>
} }
}; };
/* An array with sorted elements. Supports binary searching. */
template <typename Type>
struct SortedUnsizedArrayOf : UnsizedArrayOf<Type>
{
inline hb_sorted_array_t<Type> as_array (unsigned int len)
{ return hb_sorted_array (this->arrayZ, len); }
inline hb_sorted_array_t<const Type> as_array (unsigned int len) const
{ return hb_sorted_array (this->arrayZ, len); }
template <typename T>
inline Type &bsearch (unsigned int len, const T &x, Type &not_found = Crap (Type))
{ return *as_array (len).bsearch (x, &not_found); }
template <typename T>
inline const Type &bsearch (unsigned int len, const T &x, const Type &not_found = Null (Type)) const
{ return *as_array (len).bsearch (x, &not_found); }
template <typename T>
inline bool bfind (unsigned int len, const T &x, unsigned int *i = nullptr,
hb_bfind_not_found_t not_found = HB_BFIND_NOT_FOUND_DONT_STORE,
unsigned int to_store = (unsigned int) -1) const
{ return as_array (len).bfind (x, i, not_found, to_store); }
};
/* An array with a number of elements. */ /* An array with a number of elements. */
template <typename Type, typename LenType=HBUINT16> template <typename Type, typename LenType=HBUINT16>
struct ArrayOf struct ArrayOf
{ {
static_assert ((bool) (unsigned) hb_static_size (Type), "");
enum { item_size = Type::static_size }; enum { item_size = Type::static_size };
HB_NO_CREATE_COPY_ASSIGN_TEMPLATE2 (ArrayOf, Type, LenType); HB_NO_CREATE_COPY_ASSIGN_TEMPLATE2 (ArrayOf, Type, LenType);
inline const Type *sub_array (unsigned int start_offset, unsigned int *pcount /* IN/OUT */) const
{
unsigned int count = len;
if (unlikely (start_offset > count))
count = 0;
else
count -= start_offset;
count = MIN (count, *pcount);
*pcount = count;
return arrayZ + start_offset;
}
inline const Type& operator [] (unsigned int i) const inline const Type& operator [] (unsigned int i) const
{ {
if (unlikely (i >= len)) return Null (Type); if (unlikely (i >= len)) return Null (Type);
@ -469,6 +515,20 @@ struct ArrayOf
inline unsigned int get_size (void) const inline unsigned int get_size (void) const
{ return len.static_size + len * Type::static_size; } { return len.static_size + len * Type::static_size; }
inline hb_array_t<Type> as_array (void)
{ return hb_array (arrayZ, len); }
inline hb_array_t<const Type> as_array (void) const
{ return hb_array (arrayZ, len); }
inline hb_array_t<const Type> sub_array (unsigned int start_offset, unsigned int count) const
{ return as_array ().sub_array (start_offset, count);}
inline hb_array_t<const Type> sub_array (unsigned int start_offset, unsigned int *count /* IN/OUT */) const
{ return as_array ().sub_array (start_offset, count);}
inline hb_array_t<Type> sub_array (unsigned int start_offset, unsigned int count)
{ return as_array ().sub_array (start_offset, count);}
inline hb_array_t<Type> sub_array (unsigned int start_offset, unsigned int *count /* IN/OUT */)
{ return as_array ().sub_array (start_offset, count);}
inline bool serialize (hb_serialize_context_t *c, inline bool serialize (hb_serialize_context_t *c,
unsigned int items_len) unsigned int items_len)
{ {
@ -528,20 +588,15 @@ struct ArrayOf
return_trace (true); return_trace (true);
} }
template <typename SearchType> template <typename T>
inline int lsearch (const SearchType &x) const inline Type &lsearch (const T &x, Type &not_found = Crap (Type))
{ { return *as_array ().lsearch (x, &not_found); }
unsigned int count = len; template <typename T>
for (unsigned int i = 0; i < count; i++) inline const Type &lsearch (const T &x, const Type &not_found = Null (Type)) const
if (!this->arrayZ[i].cmp (x)) { return *as_array ().lsearch (x, &not_found); }
return i;
return -1;
}
inline void qsort (void) inline void qsort (unsigned int start = 0, unsigned int end = (unsigned int) -1)
{ { as_array ().qsort (start, end); }
::qsort (arrayZ, len, sizeof (Type), Type::cmp);
}
inline bool sanitize_shallow (hb_sanitize_context_t *c) const inline bool sanitize_shallow (hb_sanitize_context_t *c) const
{ {
@ -723,22 +778,31 @@ struct ArrayOfM1
template <typename Type, typename LenType=HBUINT16> template <typename Type, typename LenType=HBUINT16>
struct SortedArrayOf : ArrayOf<Type, LenType> struct SortedArrayOf : ArrayOf<Type, LenType>
{ {
template <typename SearchType> inline hb_sorted_array_t<Type> as_array (void)
inline int bsearch (const SearchType &x) const { return hb_sorted_array (this->arrayZ, this->len); }
{ inline hb_sorted_array_t<const Type> as_array (void) const
/* Hand-coded bsearch here since this is in the hot inner loop. */ { return hb_sorted_array (this->arrayZ, this->len); }
const Type *arr = this->arrayZ;
int min = 0, max = (int) this->len - 1; inline hb_array_t<const Type> sub_array (unsigned int start_offset, unsigned int count) const
while (min <= max) { return as_array ().sub_array (start_offset, count);}
{ inline hb_array_t<const Type> sub_array (unsigned int start_offset, unsigned int *count /* IN/OUT */) const
int mid = ((unsigned int) min + (unsigned int) max) / 2; { return as_array ().sub_array (start_offset, count);}
int c = arr[mid].cmp (x); inline hb_array_t<Type> sub_array (unsigned int start_offset, unsigned int count)
if (c < 0) max = mid - 1; { return as_array ().sub_array (start_offset, count);}
else if (c > 0) min = mid + 1; inline hb_array_t<Type> sub_array (unsigned int start_offset, unsigned int *count /* IN/OUT */)
else return mid; { return as_array ().sub_array (start_offset, count);}
}
return -1; template <typename T>
} inline Type &bsearch (const T &x, Type &not_found = Crap (Type))
{ return *as_array ().bsearch (x, &not_found); }
template <typename T>
inline const Type &bsearch (const T &x, const Type &not_found = Null (Type)) const
{ return *as_array ().bsearch (x, &not_found); }
template <typename T>
inline bool bfind (const T &x, unsigned int *i = nullptr,
hb_bfind_not_found_t not_found = HB_BFIND_NOT_FOUND_DONT_STORE,
unsigned int to_store = (unsigned int) -1) const
{ return as_array ().bfind (x, i, not_found, to_store); }
}; };
/* /*
@ -810,15 +874,36 @@ struct VarSizedBinSearchArrayOf
HB_NO_CREATE_COPY_ASSIGN_TEMPLATE (VarSizedBinSearchArrayOf, Type); HB_NO_CREATE_COPY_ASSIGN_TEMPLATE (VarSizedBinSearchArrayOf, Type);
inline bool last_is_terminator (void) const
{
if (unlikely (!header.nUnits)) return false;
/* Gah.
*
* "The number of termination values that need to be included is table-specific.
* The value that indicates binary search termination is 0xFFFF." */
const HBUINT16 *words = &StructAtOffset<HBUINT16> (&bytesZ, (header.nUnits - 1) * header.unitSize);
unsigned int count = Type::TerminationWordCount;
for (unsigned int i = 0; i < count; i++)
if (words[i] != 0xFFFFu)
return false;
return true;
}
inline const Type& operator [] (unsigned int i) const inline const Type& operator [] (unsigned int i) const
{ {
if (unlikely (i >= header.nUnits)) return Null (Type); if (unlikely (i >= get_length ())) return Null (Type);
return StructAtOffset<Type> (&bytesZ, i * header.unitSize); return StructAtOffset<Type> (&bytesZ, i * header.unitSize);
} }
inline Type& operator [] (unsigned int i) inline Type& operator [] (unsigned int i)
{ {
if (unlikely (i >= get_length ())) return Crap (Type);
return StructAtOffset<Type> (&bytesZ, i * header.unitSize); return StructAtOffset<Type> (&bytesZ, i * header.unitSize);
} }
inline unsigned int get_length (void) const
{
return header.nUnits - last_is_terminator ();
}
inline unsigned int get_size (void) const inline unsigned int get_size (void) const
{ return header.static_size + header.nUnits * header.unitSize; } { return header.static_size + header.nUnits * header.unitSize; }
@ -842,7 +927,7 @@ struct VarSizedBinSearchArrayOf
{ {
TRACE_SANITIZE (this); TRACE_SANITIZE (this);
if (unlikely (!sanitize_shallow (c))) return_trace (false); if (unlikely (!sanitize_shallow (c))) return_trace (false);
unsigned int count = header.nUnits; unsigned int count = get_length ();
for (unsigned int i = 0; i < count; i++) for (unsigned int i = 0; i < count; i++)
if (unlikely (!(*this)[i].sanitize (c, base))) if (unlikely (!(*this)[i].sanitize (c, base)))
return_trace (false); return_trace (false);
@ -853,7 +938,7 @@ struct VarSizedBinSearchArrayOf
{ {
TRACE_SANITIZE (this); TRACE_SANITIZE (this);
if (unlikely (!sanitize_shallow (c))) return_trace (false); if (unlikely (!sanitize_shallow (c))) return_trace (false);
unsigned int count = header.nUnits; unsigned int count = get_length ();
for (unsigned int i = 0; i < count; i++) for (unsigned int i = 0; i < count; i++)
if (unlikely (!(*this)[i].sanitize (c, base, user_data))) if (unlikely (!(*this)[i].sanitize (c, base, user_data)))
return_trace (false); return_trace (false);
@ -864,7 +949,7 @@ struct VarSizedBinSearchArrayOf
inline const Type *bsearch (const T &key) const inline const Type *bsearch (const T &key) const
{ {
unsigned int size = header.unitSize; unsigned int size = header.unitSize;
int min = 0, max = (int) header.nUnits - 1; int min = 0, max = (int) get_length () - 1;
while (min <= max) while (min <= max)
{ {
int mid = ((unsigned int) min + (unsigned int) max) / 2; int mid = ((unsigned int) min + (unsigned int) max) / 2;

View File

@ -417,6 +417,7 @@ struct CmapSubtableLongGroup
public: public:
DEFINE_SIZE_STATIC (12); DEFINE_SIZE_STATIC (12);
}; };
DECLARE_NULL_NAMESPACE_BYTES (OT, CmapSubtableLongGroup);
template <typename UINT> template <typename UINT>
struct CmapSubtableTrimmed struct CmapSubtableTrimmed
@ -467,10 +468,7 @@ struct CmapSubtableLongSegmented
inline bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const inline bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const
{ {
int i = groups.bsearch (codepoint); hb_codepoint_t gid = T::group_get_glyph (groups.bsearch (codepoint), codepoint);
if (i == -1)
return false;
hb_codepoint_t gid = T::group_get_glyph (groups[i], codepoint);
if (!gid) if (!gid)
return false; return false;
*glyph = gid; *glyph = gid;
@ -517,7 +515,8 @@ struct CmapSubtableFormat12 : CmapSubtableLongSegmented<CmapSubtableFormat12>
{ {
static inline hb_codepoint_t group_get_glyph (const CmapSubtableLongGroup &group, static inline hb_codepoint_t group_get_glyph (const CmapSubtableLongGroup &group,
hb_codepoint_t u) hb_codepoint_t u)
{ return group.glyphID + (u - group.startCharCode); } { return likely (group.startCharCode <= group.endCharCode) ?
group.glyphID + (u - group.startCharCode) : 0; }
bool serialize (hb_serialize_context_t *c, bool serialize (hb_serialize_context_t *c,
@ -673,16 +672,12 @@ struct VariationSelectorRecord
hb_codepoint_t *glyph, hb_codepoint_t *glyph,
const void *base) const const void *base) const
{ {
int i; if ((base+defaultUVS).bfind (codepoint))
const DefaultUVS &defaults = base+defaultUVS;
i = defaults.bsearch (codepoint);
if (i != -1)
return GLYPH_VARIANT_USE_DEFAULT; return GLYPH_VARIANT_USE_DEFAULT;
const NonDefaultUVS &nonDefaults = base+nonDefaultUVS; const UVSMapping &nonDefault = (base+nonDefaultUVS).bsearch (codepoint);
i = nonDefaults.bsearch (codepoint); if (nonDefault.glyphID)
if (i != -1 && nonDefaults[i].glyphID)
{ {
*glyph = nonDefaults[i].glyphID; *glyph = nonDefault.glyphID;
return GLYPH_VARIANT_FOUND; return GLYPH_VARIANT_FOUND;
} }
return GLYPH_VARIANT_NOT_FOUND; return GLYPH_VARIANT_NOT_FOUND;
@ -722,7 +717,7 @@ struct CmapSubtableFormat14
hb_codepoint_t variation_selector, hb_codepoint_t variation_selector,
hb_codepoint_t *glyph) const hb_codepoint_t *glyph) const
{ {
return record[record.bsearch (variation_selector)].get_glyph (codepoint, glyph, this); return record.bsearch (variation_selector).get_glyph (codepoint, glyph, this);
} }
inline void collect_variation_selectors (hb_set_t *out) const inline void collect_variation_selectors (hb_set_t *out) const
@ -734,7 +729,7 @@ struct CmapSubtableFormat14
inline void collect_variation_unicodes (hb_codepoint_t variation_selector, inline void collect_variation_unicodes (hb_codepoint_t variation_selector,
hb_set_t *out) const hb_set_t *out) const
{ {
record[record.bsearch (variation_selector)].collect_unicodes (out, this); record.bsearch (variation_selector).collect_unicodes (out, this);
} }
inline bool sanitize (hb_sanitize_context_t *c) const inline bool sanitize (hb_sanitize_context_t *c) const
@ -863,14 +858,6 @@ struct cmap
hb_vector_t<CmapSubtableLongGroup> format12_groups; hb_vector_t<CmapSubtableLongGroup> format12_groups;
}; };
inline bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
return_trace (c->check_struct (this) &&
likely (version == 0) &&
encodingRecord.sanitize (c, this));
}
inline bool _create_plan (const hb_subset_plan_t *plan, inline bool _create_plan (const hb_subset_plan_t *plan,
subset_plan *cmap_plan) const subset_plan *cmap_plan) const
{ {
@ -1164,11 +1151,21 @@ struct cmap
key.platformID.set (platform_id); key.platformID.set (platform_id);
key.encodingID.set (encoding_id); key.encodingID.set (encoding_id);
int result = encodingRecord.bsearch (key); const EncodingRecord &result = encodingRecord.bsearch (key);
if (result == -1 || !encodingRecord[result].subtable) if (!result.subtable)
return nullptr; return nullptr;
return &(this+encodingRecord[result].subtable); return &(this+result.subtable);
}
public:
inline bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
return_trace (c->check_struct (this) &&
likely (version == 0) &&
encodingRecord.sanitize (c, this));
} }
protected: protected:

View File

@ -66,13 +66,6 @@ struct BaseGlyphRecord
inline int cmp (hb_codepoint_t g) const inline int cmp (hb_codepoint_t g) const
{ return g < glyphId ? -1 : g > glyphId ? 1 : 0; } { return g < glyphId ? -1 : g > glyphId ? 1 : 0; }
static int cmp (const void *pa, const void *pb)
{
const hb_codepoint_t *a = (const hb_codepoint_t *) pa;
const BaseGlyphRecord *b = (const BaseGlyphRecord *) pb;
return b->cmp (*a);
}
inline bool sanitize (hb_sanitize_context_t *c) const inline bool sanitize (hb_sanitize_context_t *c) const
{ {
TRACE_SANITIZE (this); TRACE_SANITIZE (this);
@ -103,12 +96,7 @@ struct COLR
unsigned int *count, /* IN/OUT. May be NULL. */ unsigned int *count, /* IN/OUT. May be NULL. */
hb_ot_color_layer_t *layers /* OUT. May be NULL. */) const hb_ot_color_layer_t *layers /* OUT. May be NULL. */) const
{ {
const BaseGlyphRecord *rec = (BaseGlyphRecord *) bsearch (&glyph, const BaseGlyphRecord &record = (this+baseGlyphsZ).bsearch (numBaseGlyphs, glyph);
&(this+baseGlyphsZ),
numBaseGlyphs,
sizeof (BaseGlyphRecord),
BaseGlyphRecord::cmp);
const BaseGlyphRecord &record = rec ? *rec : Null (BaseGlyphRecord);
hb_array_t<const LayerRecord> all_layers ((this+layersZ).arrayZ, numLayers); hb_array_t<const LayerRecord> all_layers ((this+layersZ).arrayZ, numLayers);
hb_array_t<const LayerRecord> glyph_layers = all_layers.sub_array (record.firstLayerIdx, hb_array_t<const LayerRecord> glyph_layers = all_layers.sub_array (record.firstLayerIdx,
@ -137,7 +125,7 @@ struct COLR
protected: protected:
HBUINT16 version; /* Table version number (starts at 0). */ HBUINT16 version; /* Table version number (starts at 0). */
HBUINT16 numBaseGlyphs; /* Number of Base Glyph Records. */ HBUINT16 numBaseGlyphs; /* Number of Base Glyph Records. */
LOffsetTo<UnsizedArrayOf<BaseGlyphRecord>, false> LOffsetTo<SortedUnsizedArrayOf<BaseGlyphRecord>, false>
baseGlyphsZ; /* Offset to Base Glyph records. */ baseGlyphsZ; /* Offset to Base Glyph records. */
LOffsetTo<UnsizedArrayOf<LayerRecord>, false> LOffsetTo<UnsizedArrayOf<LayerRecord>, false>
layersZ; /* Offset to Layer Records. */ layersZ; /* Offset to Layer Records. */

View File

@ -103,8 +103,7 @@ struct SVG
inline const SVGDocumentIndexEntry &get_glyph_entry (hb_codepoint_t glyph_id) const inline const SVGDocumentIndexEntry &get_glyph_entry (hb_codepoint_t glyph_id) const
{ {
const SortedArrayOf<SVGDocumentIndexEntry> &docs = this+svgDocEntries; return (this+svgDocEntries).bsearch (glyph_id);
return docs[docs.bsearch (glyph_id)];
} }
inline bool sanitize (hb_sanitize_context_t *c) const inline bool sanitize (hb_sanitize_context_t *c) const

View File

@ -67,6 +67,7 @@
HB_OT_TABLE(AAT, trak) \ HB_OT_TABLE(AAT, trak) \
HB_OT_TABLE(AAT, lcar) \ HB_OT_TABLE(AAT, lcar) \
HB_OT_TABLE(AAT, ltag) \ HB_OT_TABLE(AAT, ltag) \
HB_OT_TABLE(AAT, feat) \
/* OpenType variations. */ \ /* OpenType variations. */ \
HB_OT_TABLE(OT, fvar) \ HB_OT_TABLE(OT, fvar) \
HB_OT_TABLE(OT, avar) \ HB_OT_TABLE(OT, avar) \

View File

@ -249,7 +249,7 @@ hb_ot_get_font_v_extents (hb_font_t *font,
return vmtx.has_font_extents; return vmtx.has_font_extents;
} }
#ifdef HB_USE_ATEXIT #if HB_USE_ATEXIT
static void free_static_ot_funcs (void); static void free_static_ot_funcs (void);
#endif #endif
@ -275,7 +275,7 @@ static struct hb_ot_font_funcs_lazy_loader_t : hb_font_funcs_lazy_loader_t<hb_ot
hb_font_funcs_make_immutable (funcs); hb_font_funcs_make_immutable (funcs);
#ifdef HB_USE_ATEXIT #if HB_USE_ATEXIT
atexit (free_static_ot_funcs); atexit (free_static_ot_funcs);
#endif #endif
@ -283,7 +283,7 @@ static struct hb_ot_font_funcs_lazy_loader_t : hb_font_funcs_lazy_loader_t<hb_ot
} }
} static_ot_funcs; } static_ot_funcs;
#ifdef HB_USE_ATEXIT #if HB_USE_ATEXIT
static static
void free_static_ot_funcs (void) void free_static_ot_funcs (void)
{ {

View File

@ -56,7 +56,9 @@ struct loca
protected: protected:
UnsizedArrayOf<HBUINT8> dataZ; /* Location data. */ UnsizedArrayOf<HBUINT8> dataZ; /* Location data. */
public: public:
DEFINE_SIZE_ARRAY (0, dataZ); DEFINE_SIZE_MIN (0); /* In reality, this is UNBOUNDED() type; but since we always
* check the size externally, allow Null() object of it by
* defining it MIN() instead. */
}; };
@ -465,7 +467,9 @@ struct glyf
protected: protected:
UnsizedArrayOf<HBUINT8> dataZ; /* Glyphs data. */ UnsizedArrayOf<HBUINT8> dataZ; /* Glyphs data. */
public: public:
DEFINE_SIZE_ARRAY (0, dataZ); DEFINE_SIZE_MIN (0); /* In reality, this is UNBOUNDED() type; but since we always
* check the size externally, allow Null() object of it by
* defining it MIN() instead. */
}; };
struct glyf_accelerator_t : glyf::accelerator_t {}; struct glyf_accelerator_t : glyf::accelerator_t {};

View File

@ -154,7 +154,7 @@ struct KernSubTable
KernSubTableFormat3<KernSubTableHeader> format3; KernSubTableFormat3<KernSubTableHeader> format3;
} u; } u;
public: public:
DEFINE_SIZE_MIN (0); DEFINE_SIZE_MIN (KernSubTableHeader::static_size);
}; };
@ -274,6 +274,15 @@ struct kern
inline bool has_data (void) const { return u.version32; } inline bool has_data (void) const { return u.version32; }
inline unsigned int get_type (void) const { return u.major; } inline unsigned int get_type (void) const { return u.major; }
inline bool has_state_machine (void) const
{
switch (get_type ()) {
case 0: return u.ot.has_state_machine ();
case 1: return u.aat.has_state_machine ();
default:return false;
}
}
inline bool has_cross_stream (void) const inline bool has_cross_stream (void) const
{ {
switch (get_type ()) { switch (get_type ()) {

View File

@ -128,15 +128,7 @@ struct RecordArrayOf : SortedArrayOf<Record<Type> >
} }
inline bool find_index (hb_tag_t tag, unsigned int *index) const inline bool find_index (hb_tag_t tag, unsigned int *index) const
{ {
/* If we want to allow non-sorted data, we can lsearch(). */ return this->bfind (tag, index, HB_BFIND_NOT_FOUND_STORE, Index::NOT_FOUND_INDEX);
int i = this->/*lsearch*/bsearch (tag);
if (i != -1) {
if (index) *index = i;
return true;
} else {
if (index) *index = Index::NOT_FOUND_INDEX;
return false;
}
} }
}; };
@ -802,11 +794,11 @@ struct Lookup
HBUINT16 lookupFlag; /* Lookup qualifiers */ HBUINT16 lookupFlag; /* Lookup qualifiers */
ArrayOf<Offset16> ArrayOf<Offset16>
subTable; /* Array of SubTables */ subTable; /* Array of SubTables */
HBUINT16 markFilteringSetX[VAR]; /* Index (base 0) into GDEF mark glyph sets /*HBUINT16 markFilteringSetX[VAR];*//* Index (base 0) into GDEF mark glyph sets
* structure. This field is only present if bit * structure. This field is only present if bit
* UseMarkFilteringSet of lookup flags is set. */ * UseMarkFilteringSet of lookup flags is set. */
public: public:
DEFINE_SIZE_ARRAY2 (6, subTable, markFilteringSetX); DEFINE_SIZE_ARRAY (6, subTable);
}; };
typedef OffsetListOf<Lookup> LookupList; typedef OffsetListOf<Lookup> LookupList;
@ -823,8 +815,8 @@ struct CoverageFormat1
private: private:
inline unsigned int get_coverage (hb_codepoint_t glyph_id) const inline unsigned int get_coverage (hb_codepoint_t glyph_id) const
{ {
int i = glyphArray.bsearch (glyph_id); unsigned int i;
static_assert ((((unsigned int) -1) == NOT_COVERED), ""); glyphArray.bfind (glyph_id, &i, HB_BFIND_NOT_FOUND_STORE, NOT_COVERED);
return i; return i;
} }
@ -896,12 +888,10 @@ struct CoverageFormat2
private: private:
inline unsigned int get_coverage (hb_codepoint_t glyph_id) const inline unsigned int get_coverage (hb_codepoint_t glyph_id) const
{ {
int i = rangeRecord.bsearch (glyph_id); const RangeRecord &range = rangeRecord.bsearch (glyph_id);
if (i != -1) { return likely (range.start <= range.end) ?
const RangeRecord &range = rangeRecord[i]; (unsigned int) range.value + (glyph_id - range.start) :
return (unsigned int) range.value + (glyph_id - range.start); NOT_COVERED;
}
return NOT_COVERED;
} }
inline bool serialize (hb_serialize_context_t *c, inline bool serialize (hb_serialize_context_t *c,
@ -1194,10 +1184,7 @@ struct ClassDefFormat1
private: private:
inline unsigned int get_class (hb_codepoint_t glyph_id) const inline unsigned int get_class (hb_codepoint_t glyph_id) const
{ {
unsigned int i = (unsigned int) (glyph_id - startGlyph); return classValue[(unsigned int) (glyph_id - startGlyph)];
if (unlikely (i < classValue.len))
return classValue[i];
return 0;
} }
inline bool sanitize (hb_sanitize_context_t *c) const inline bool sanitize (hb_sanitize_context_t *c) const
@ -1265,10 +1252,10 @@ struct ClassDefFormat1
} }
protected: protected:
HBUINT16 classFormat; /* Format identifier--format = 1 */ HBUINT16 classFormat; /* Format identifier--format = 1 */
GlyphID startGlyph; /* First GlyphID of the classValueArray */ GlyphID startGlyph; /* First GlyphID of the classValueArray */
ArrayOf<HBUINT16> ArrayOf<HBUINT16>
classValue; /* Array of Class Values--one per GlyphID */ classValue; /* Array of Class Values--one per GlyphID */
public: public:
DEFINE_SIZE_ARRAY (6, classValue); DEFINE_SIZE_ARRAY (6, classValue);
}; };
@ -1280,10 +1267,7 @@ struct ClassDefFormat2
private: private:
inline unsigned int get_class (hb_codepoint_t glyph_id) const inline unsigned int get_class (hb_codepoint_t glyph_id) const
{ {
int i = rangeRecord.bsearch (glyph_id); return rangeRecord.bsearch (glyph_id).value;
if (unlikely (i != -1))
return rangeRecord[i].value;
return 0;
} }
inline bool sanitize (hb_sanitize_context_t *c) const inline bool sanitize (hb_sanitize_context_t *c) const
@ -1586,9 +1570,9 @@ struct VarData
HBUINT16 itemCount; HBUINT16 itemCount;
HBUINT16 shortCount; HBUINT16 shortCount;
ArrayOf<HBUINT16> regionIndices; ArrayOf<HBUINT16> regionIndices;
UnsizedArrayOf<HBUINT8>bytesX; /*UnsizedArrayOf<HBUINT8>bytesX;*/
public: public:
DEFINE_SIZE_ARRAY2 (6, regionIndices, bytesX); DEFINE_SIZE_ARRAY (6, regionIndices);
}; };
struct VariationStore struct VariationStore

View File

@ -61,9 +61,10 @@ struct AttachList
const AttachPoint &points = this+attachPoint[index]; const AttachPoint &points = this+attachPoint[index];
if (point_count) { if (point_count)
const HBUINT16 *array = points.sub_array (start_offset, point_count); {
unsigned int count = *point_count; hb_array_t<const HBUINT16> array = points.sub_array (start_offset, point_count);
unsigned int count = array.len;
for (unsigned int i = 0; i < count; i++) for (unsigned int i = 0; i < count; i++)
point_array[i] = array[i]; point_array[i] = array[i];
} }
@ -216,9 +217,10 @@ struct LigGlyph
unsigned int *caret_count /* IN/OUT */, unsigned int *caret_count /* IN/OUT */,
hb_position_t *caret_array /* OUT */) const hb_position_t *caret_array /* OUT */) const
{ {
if (caret_count) { if (caret_count)
const OffsetTo<CaretValue> *array = carets.sub_array (start_offset, caret_count); {
unsigned int count = *caret_count; hb_array_t <const OffsetTo<CaretValue> > array = carets.sub_array (start_offset, caret_count);
unsigned int count = array.len;
for (unsigned int i = 0; i < count; i++) for (unsigned int i = 0; i < count; i++)
caret_array[i] = (this+array[i]).get_caret_value (font, direction, glyph_id, var_store); caret_array[i] = (this+array[i]).get_caret_value (font, direction, glyph_id, var_store);
} }
@ -406,9 +408,20 @@ struct GDEF
} }
} }
HB_INTERNAL bool is_blacklisted (hb_blob_t *blob,
hb_face_t *face) const;
struct accelerator_t struct accelerator_t
{ {
HB_INTERNAL void init (hb_face_t *face); inline void init (hb_face_t *face)
{
this->table = hb_sanitize_context_t().reference_table<GDEF> (face);
if (unlikely (this->table->is_blacklisted (this->table.get_blob (), face)))
{
hb_blob_destroy (this->table.get_blob ());
this->table = hb_blob_get_empty ();
}
}
inline void fini (void) inline void fini (void)
{ {

View File

@ -1643,6 +1643,9 @@ struct GPOS : GSUBGPOS
inline bool sanitize (hb_sanitize_context_t *c) const inline bool sanitize (hb_sanitize_context_t *c) const
{ return GSUBGPOS::sanitize<PosLookup> (c); } { return GSUBGPOS::sanitize<PosLookup> (c); }
HB_INTERNAL bool is_blacklisted (hb_blob_t *blob,
hb_face_t *face) const;
typedef GSUBGPOS::accelerator_t<GPOS> accelerator_t; typedef GSUBGPOS::accelerator_t<GPOS> accelerator_t;
}; };

View File

@ -1449,7 +1449,10 @@ struct SubstLookup : Lookup
hb_closure_context_t::return_t ret = dispatch_recurse_func (c, lookup_index); hb_closure_context_t::return_t ret = dispatch_recurse_func (c, lookup_index);
c->flush (); /* While in theory we should flush here, it will cause timeouts because a recursive
* lookup can keep growing the glyph set. Skip, and outer loop will retry up to
* HB_CLOSURE_MAX_STAGES time, which should be enough for every realistic font. */
//c->flush ();
return ret; return ret;
} }
@ -1483,6 +1486,9 @@ struct GSUB : GSUBGPOS
inline bool sanitize (hb_sanitize_context_t *c) const inline bool sanitize (hb_sanitize_context_t *c) const
{ return GSUBGPOS::sanitize<SubstLookup> (c); } { return GSUBGPOS::sanitize<SubstLookup> (c); }
HB_INTERNAL bool is_blacklisted (hb_blob_t *blob,
hb_face_t *face) const;
typedef GSUBGPOS::accelerator_t<GSUB> accelerator_t; typedef GSUBGPOS::accelerator_t<GSUB> accelerator_t;
}; };

View File

@ -2753,6 +2753,11 @@ struct GSUBGPOS
inline void init (hb_face_t *face) inline void init (hb_face_t *face)
{ {
this->table = hb_sanitize_context_t().reference_table<T> (face); this->table = hb_sanitize_context_t().reference_table<T> (face);
if (unlikely (this->table->is_blacklisted (this->table.get_blob (), face)))
{
hb_blob_destroy (this->table.get_blob ());
this->table = hb_blob_get_empty ();
}
this->lookup_count = table->get_lookup_count (); this->lookup_count = table->get_lookup_count ();

View File

@ -34,15 +34,17 @@
#include "hb-ot-map.hh" #include "hb-ot-map.hh"
#include "hb-map.hh" #include "hb-map.hh"
#include "hb-ot-kern-table.hh"
#include "hb-ot-layout-gdef-table.hh" #include "hb-ot-layout-gdef-table.hh"
#include "hb-ot-layout-gsub-table.hh" #include "hb-ot-layout-gsub-table.hh"
#include "hb-ot-layout-gpos-table.hh" #include "hb-ot-layout-gpos-table.hh"
#include "hb-ot-layout-base-table.hh" // Just so we compile it; unused otherwise #include "hb-ot-layout-base-table.hh" // Just so we compile it; unused otherwise
#include "hb-ot-layout-jstf-table.hh" // Just so we compile it; unused otherwise #include "hb-ot-layout-jstf-table.hh" // Just so we compile it; unused otherwise
#include "hb-ot-kern-table.hh"
#include "hb-ot-name-table.hh" #include "hb-ot-name-table.hh"
#include "hb-ot-os2-table.hh"
#include "hb-aat-layout-lcar-table.hh" #include "hb-aat-layout-lcar-table.hh"
#include "hb-aat-layout-morx-table.hh"
/** /**
@ -65,6 +67,12 @@ hb_ot_layout_has_kerning (hb_face_t *face)
return face->table.kern->has_data (); return face->table.kern->has_data ();
} }
bool
hb_ot_layout_has_machine_kerning (hb_face_t *face)
{
return face->table.kern->has_state_machine ();
}
bool bool
hb_ot_layout_has_cross_kerning (hb_face_t *face) hb_ot_layout_has_cross_kerning (hb_face_t *face)
{ {
@ -89,10 +97,9 @@ hb_ot_layout_kern (const hb_ot_shape_plan_t *plan,
* GDEF * GDEF
*/ */
static bool bool
_hb_ot_blacklist_gdef (unsigned int gdef_len, OT::GDEF::is_blacklisted (hb_blob_t *blob,
unsigned int gsub_len, hb_face_t *face) const
unsigned int gpos_len)
{ {
/* The ugly business of blacklisting individual fonts' tables happen here! /* The ugly business of blacklisting individual fonts' tables happen here!
* See this thread for why we finally had to bend in and do this: * See this thread for why we finally had to bend in and do this:
@ -111,8 +118,10 @@ _hb_ot_blacklist_gdef (unsigned int gdef_len,
* https://bugzilla.mozilla.org/show_bug.cgi?id=1279693 * https://bugzilla.mozilla.org/show_bug.cgi?id=1279693
* https://bugzilla.mozilla.org/show_bug.cgi?id=1279875 * https://bugzilla.mozilla.org/show_bug.cgi?id=1279875
*/ */
#define ENCODE(x,y,z) ((int64_t) (x) << 32 | (int64_t) (y) << 16 | (z)) #define ENCODE(x,y,z) (((uint64_t) (x) << 48) | ((uint64_t) (y) << 24) | (uint64_t) (z))
switch ENCODE(gdef_len, gsub_len, gpos_len) switch ENCODE(blob->length,
face->table.GSUB->table.get_length (),
face->table.GPOS->table.get_length ())
{ {
/* sha1sum:c5ee92f0bca4bfb7d06c4d03e8cf9f9cf75d2e8a Windows 7? timesi.ttf */ /* sha1sum:c5ee92f0bca4bfb7d06c4d03e8cf9f9cf75d2e8a Windows 7? timesi.ttf */
case ENCODE (442, 2874, 42038): case ENCODE (442, 2874, 42038):
@ -191,20 +200,6 @@ _hb_ot_blacklist_gdef (unsigned int gdef_len,
return false; return false;
} }
void
OT::GDEF::accelerator_t::init (hb_face_t *face)
{
this->table = hb_sanitize_context_t().reference_table<GDEF> (face);
if (unlikely (_hb_ot_blacklist_gdef (this->table.get_length (),
face->table.GSUB->table.get_length (),
face->table.GPOS->table.get_length ())))
{
hb_blob_destroy (this->table.get_blob ());
this->table = hb_blob_get_empty ();
}
}
static void static void
_hb_ot_layout_set_glyph_props (hb_font_t *font, _hb_ot_layout_set_glyph_props (hb_font_t *font,
hb_buffer_t *buffer) hb_buffer_t *buffer)
@ -291,6 +286,38 @@ hb_ot_layout_get_ligature_carets (hb_font_t *font,
* GSUB/GPOS * GSUB/GPOS
*/ */
bool
OT::GSUB::is_blacklisted (hb_blob_t *blob HB_UNUSED,
hb_face_t *face) const
{
/* Mac OS X prefers morx over GSUB. It also ships with various Indic fonts,
* all by 'MUTF' foundry (Tamil MN, Tamil Sangam MN, etc.), that have broken
* GSUB/GPOS tables. Some have GSUB with zero scripts, those are ignored by
* our morx/GSUB preference code. But if GSUB has non-zero scripts, we tend
* to prefer it over morx because we want to be consistent with other OpenType
* shapers.
*
* To work around broken Indic Mac system fonts, we ignore GSUB table if
* OS/2 VendorId is 'MUTF' and font has morx table as well.
*
* https://github.com/harfbuzz/harfbuzz/issues/1410
* https://github.com/harfbuzz/harfbuzz/issues/1348
* https://github.com/harfbuzz/harfbuzz/issues/1391
*/
if (unlikely (face->table.OS2->achVendID == HB_TAG ('M','U','T','F') &&
face->table.morx->has_data ()))
return true;
return false;
}
bool
OT::GPOS::is_blacklisted (hb_blob_t *blob HB_UNUSED,
hb_face_t *face HB_UNUSED) const
{
return false;
}
static const OT::GSUBGPOS& static const OT::GSUBGPOS&
get_gsubgpos_table (hb_face_t *face, get_gsubgpos_table (hb_face_t *face,
hb_tag_t table_tag) hb_tag_t table_tag)

View File

@ -48,6 +48,9 @@ struct hb_ot_shape_plan_t;
HB_INTERNAL bool HB_INTERNAL bool
hb_ot_layout_has_kerning (hb_face_t *face); hb_ot_layout_has_kerning (hb_face_t *face);
HB_INTERNAL bool
hb_ot_layout_has_machine_kerning (hb_face_t *face);
HB_INTERNAL bool HB_INTERNAL bool
hb_ot_layout_has_cross_kerning (hb_face_t *face); hb_ot_layout_has_cross_kerning (hb_face_t *face);

View File

@ -57,8 +57,8 @@ struct hb_ot_map_t
unsigned int auto_zwj : 1; unsigned int auto_zwj : 1;
unsigned int random : 1; unsigned int random : 1;
inline int cmp (const hb_tag_t *tag_) const inline int cmp (const hb_tag_t tag_) const
{ return *tag_ < tag ? -1 : *tag_ > tag ? 1 : 0; } { return tag_ < tag ? -1 : tag_ > tag ? 1 : 0; }
}; };
struct lookup_map_t { struct lookup_map_t {

View File

@ -509,9 +509,8 @@ struct MathGlyphAssembly
if (parts_count) if (parts_count)
{ {
int scale = font->dir_scale (direction); int scale = font->dir_scale (direction);
const MathGlyphPartRecord *arr = hb_array_t<const MathGlyphPartRecord> arr = partRecords.sub_array (start_offset, parts_count);
partRecords.sub_array (start_offset, parts_count); unsigned int count = arr.len;
unsigned int count = *parts_count;
for (unsigned int i = 0; i < count; i++) for (unsigned int i = 0; i < count; i++)
arr[i].extract (parts[i], scale, font); arr[i].extract (parts[i], scale, font);
} }
@ -556,9 +555,8 @@ struct MathGlyphConstruction
if (variants_count) if (variants_count)
{ {
int scale = font->dir_scale (direction); int scale = font->dir_scale (direction);
const MathGlyphVariantRecord *arr = hb_array_t<const MathGlyphVariantRecord> arr = mathGlyphVariantRecord.sub_array (start_offset, variants_count);
mathGlyphVariantRecord.sub_array (start_offset, variants_count); unsigned int count = arr.len;
unsigned int count = *variants_count;
for (unsigned int i = 0; i < count; i++) for (unsigned int i = 0; i < count; i++)
{ {
variants[i].glyph = arr[i].variantGlyph; variants[i].glyph = arr[i].variantGlyph;

View File

@ -61,12 +61,12 @@ struct postV2Tail
ArrayOf<HBUINT16> glyphNameIndex; /* This is not an offset, but is the ArrayOf<HBUINT16> glyphNameIndex; /* This is not an offset, but is the
* ordinal number of the glyph in 'post' * ordinal number of the glyph in 'post'
* string tables. */ * string tables. */
UnsizedArrayOf<HBUINT8> /*UnsizedArrayOf<HBUINT8>
namesX; /* Glyph names with length bytes [variable] namesX;*/ /* Glyph names with length bytes [variable]
* (a Pascal string). */ * (a Pascal string). */
public: public:
DEFINE_SIZE_ARRAY2 (2, glyphNameIndex, namesX); DEFINE_SIZE_ARRAY (2, glyphNameIndex);
}; };
struct post struct post

View File

@ -391,27 +391,6 @@ const hb_ot_complex_shaper_t _hb_ot_complex_shaper_myanmar =
}; };
/* Uniscribe seems to have a shaper for 'mymr' that is like the
* generic shaper, except that it zeros mark advances GDEF_LATE. */
const hb_ot_complex_shaper_t _hb_ot_complex_shaper_myanmar_old =
{
nullptr, /* collect_features */
nullptr, /* override_features */
nullptr, /* data_create */
nullptr, /* data_destroy */
nullptr, /* preprocess_text */
nullptr, /* postprocess_glyphs */
HB_OT_SHAPE_NORMALIZATION_MODE_DEFAULT,
nullptr, /* decompose */
nullptr, /* compose */
nullptr, /* setup_masks */
HB_TAG_NONE, /* gpos_tag */
nullptr, /* reorder_marks */
HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE,
true, /* fallback_position */
};
/* Ugly Zawgyi encoding. /* Ugly Zawgyi encoding.
* Disable all auto processing. * Disable all auto processing.
* https://github.com/harfbuzz/harfbuzz/issues/1162 */ * https://github.com/harfbuzz/harfbuzz/issues/1162 */

View File

@ -544,7 +544,7 @@ static const USE_TABLE_ELEMENT_TYPE use_table[] = {
/* 11190 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, /* 11190 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
/* 111A0 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, /* 111A0 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
/* 111B0 */ B, B, B, VPst, VPre, VPst, VBlw, VBlw, VBlw, VBlw, VBlw, VBlw, VAbv, VAbv, VAbv, VAbv, /* 111B0 */ B, B, B, VPst, VPre, VPst, VBlw, VBlw, VBlw, VBlw, VBlw, VBlw, VAbv, VAbv, VAbv, VAbv,
/* 111C0 */ H, B, R, R, O, O, O, O, O, FM, CMBlw, VAbv, VBlw, O, O, O, /* 111C0 */ H, B, R, R, O, O, O, O, GB, FBlw, CMBlw, VAbv, VBlw, O, O, O,
/* 111D0 */ B, B, B, B, B, B, B, B, B, B, O, O, O, O, O, O, /* 111D0 */ B, B, B, B, B, B, B, B, B, B, O, O, O, O, O, O,
/* Sinhala Archaic Numbers */ /* Sinhala Archaic Numbers */

View File

@ -432,7 +432,8 @@ record_pref (const hb_ot_shape_plan_t *plan HB_UNUSED,
static inline bool static inline bool
is_halant (const hb_glyph_info_t &info) is_halant (const hb_glyph_info_t &info)
{ {
return info.use_category() == USE_H && !_hb_glyph_info_ligated (&info); return (info.use_category() == USE_H || info.use_category() == USE_HVM) &&
!_hb_glyph_info_ligated (&info);
} }
static void static void
@ -449,19 +450,38 @@ reorder_syllable (hb_buffer_t *buffer, unsigned int start, unsigned int end)
hb_glyph_info_t *info = buffer->info; hb_glyph_info_t *info = buffer->info;
#define BASE_FLAGS (FLAG (USE_B) | FLAG (USE_GB)) #define POST_BASE_FLAGS64 (FLAG64 (USE_FM) | \
FLAG64 (USE_FAbv) | \
FLAG64 (USE_FBlw) | \
FLAG64 (USE_FPst) | \
FLAG64 (USE_MAbv) | \
FLAG64 (USE_MBlw) | \
FLAG64 (USE_MPst) | \
FLAG64 (USE_MPre) | \
FLAG64 (USE_VAbv) | \
FLAG64 (USE_VBlw) | \
FLAG64 (USE_VPst) | \
FLAG64 (USE_VPre) | \
FLAG64 (USE_VMAbv) | \
FLAG64 (USE_VMBlw) | \
FLAG64 (USE_VMPst) | \
FLAG64 (USE_VMPre))
/* Move things forward. */ /* Move things forward. */
if (info[start].use_category() == USE_R && end - start > 1) if (info[start].use_category() == USE_R && end - start > 1)
{ {
/* Got a repha. Reorder it to after first base, before first halant. */ /* Got a repha. Reorder it towards the end, but before the first post-base
* glyph. */
for (unsigned int i = start + 1; i < end; i++) for (unsigned int i = start + 1; i < end; i++)
if ((FLAG_UNSAFE (info[i].use_category()) & (BASE_FLAGS)) || is_halant (info[i])) {
bool is_post_base_glyph = (FLAG64_UNSAFE (info[i].use_category()) & POST_BASE_FLAGS64) ||
is_halant (info[i]);
if (is_post_base_glyph || i == end - 1)
{ {
/* If we hit a halant, move before it; otherwise it's a base: move to it's /* If we hit a post-base glyph, move before it; otherwise move to the
* place, and shift things in between backward. */ * end. Shift things in between backward. */
if (is_halant (info[i])) if (is_post_base_glyph)
i--; i--;
buffer->merge_clusters (start, i + 1); buffer->merge_clusters (start, i + 1);
@ -471,21 +491,19 @@ reorder_syllable (hb_buffer_t *buffer, unsigned int start, unsigned int end)
break; break;
} }
}
} }
/* Move things back. */ /* Move things back. */
unsigned int j = end; unsigned int j = start;
for (unsigned int i = start; i < end; i++) for (unsigned int i = start; i < end; i++)
{ {
uint32_t flag = FLAG_UNSAFE (info[i].use_category()); uint32_t flag = FLAG_UNSAFE (info[i].use_category());
if ((flag & (BASE_FLAGS)) || is_halant (info[i])) if (is_halant (info[i]))
{ {
/* If we hit a halant, move after it; otherwise it's a base: move to it's /* If we hit a halant, move after it; otherwise move to the beginning, and
* place, and shift things in between backward. */ * shift things in between forward. */
if (is_halant (info[i])) j = i + 1;
j = i + 1;
else
j = i;
} }
else if (((flag) & (FLAG (USE_VPre) | FLAG (USE_VMPre))) && else if (((flag) & (FLAG (USE_VPre) | FLAG (USE_VMPre))) &&
/* Only move the first component of a MultipleSubst. */ /* Only move the first component of a MultipleSubst. */

View File

@ -57,7 +57,6 @@ enum hb_ot_shape_zero_width_marks_type_t {
HB_COMPLEX_SHAPER_IMPLEMENT (indic) \ HB_COMPLEX_SHAPER_IMPLEMENT (indic) \
HB_COMPLEX_SHAPER_IMPLEMENT (khmer) \ HB_COMPLEX_SHAPER_IMPLEMENT (khmer) \
HB_COMPLEX_SHAPER_IMPLEMENT (myanmar) \ HB_COMPLEX_SHAPER_IMPLEMENT (myanmar) \
HB_COMPLEX_SHAPER_IMPLEMENT (myanmar_old) \
HB_COMPLEX_SHAPER_IMPLEMENT (myanmar_zawgyi) \ HB_COMPLEX_SHAPER_IMPLEMENT (myanmar_zawgyi) \
HB_COMPLEX_SHAPER_IMPLEMENT (thai) \ HB_COMPLEX_SHAPER_IMPLEMENT (thai) \
HB_COMPLEX_SHAPER_IMPLEMENT (use) \ HB_COMPLEX_SHAPER_IMPLEMENT (use) \
@ -269,12 +268,25 @@ hb_ot_shape_complex_categorize (const hb_ot_shape_planner_t *planner)
return &_hb_ot_complex_shaper_khmer; return &_hb_ot_complex_shaper_khmer;
case HB_SCRIPT_MYANMAR: case HB_SCRIPT_MYANMAR:
if (planner->map.chosen_script[0] == HB_TAG ('m','y','m','2')) /* If the designer designed the font for the 'DFLT' script,
return &_hb_ot_complex_shaper_myanmar; * (or we ended up arbitrarily pick 'latn'), use the default shaper.
else if (planner->map.chosen_script[0] == HB_TAG ('m','y','m','r')) * Otherwise, use the specific shaper.
return &_hb_ot_complex_shaper_myanmar_old; *
else * If designer designed for 'mymr' tag, also send to default
* shaper. That's tag used from before Myanmar shaping spec
* was developed. The shaping spec uses 'mym2' tag. */
if (planner->map.chosen_script[0] == HB_TAG ('D','F','L','T') ||
planner->map.chosen_script[0] == HB_TAG ('l','a','t','n') ||
planner->map.chosen_script[0] == HB_TAG ('m','y','m','r'))
return &_hb_ot_complex_shaper_default; return &_hb_ot_complex_shaper_default;
else
return &_hb_ot_complex_shaper_myanmar;
/* https://github.com/harfbuzz/harfbuzz/issues/1162 */
case HB_SCRIPT_MYANMAR_ZAWGYI:
return &_hb_ot_complex_shaper_myanmar_zawgyi;
/* Unicode-2.0 additions */ /* Unicode-2.0 additions */
@ -375,10 +387,6 @@ hb_ot_shape_complex_categorize (const hb_ot_shape_planner_t *planner)
return &_hb_ot_complex_shaper_default; return &_hb_ot_complex_shaper_default;
else else
return &_hb_ot_complex_shaper_use; return &_hb_ot_complex_shaper_use;
/* https://github.com/harfbuzz/harfbuzz/issues/1162 */
case HB_SCRIPT_MYANMAR_ZAWGYI:
return &_hb_ot_complex_shaper_myanmar_zawgyi;
} }
} }

View File

@ -76,10 +76,16 @@ hb_ot_shape_planner_t::hb_ot_shape_planner_t (hb_face_t *fac
props (*props), props (*props),
map (face, props), map (face, props),
aat_map (face, props), aat_map (face, props),
apply_morx (_hb_apply_morx (face)), apply_morx (_hb_apply_morx (face))
shaper (apply_morx ? {
&_hb_ot_complex_shaper_default : shaper = hb_ot_shape_complex_categorize (this);
hb_ot_shape_complex_categorize (this)) {}
script_zero_marks = shaper->zero_width_marks != HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE;
script_fallback_mark_positioning = shaper->fallback_position;
if (apply_morx)
shaper = &_hb_ot_complex_shaper_default;
}
void void
hb_ot_shape_planner_t::compile (hb_ot_shape_plan_t &plan, hb_ot_shape_planner_t::compile (hb_ot_shape_plan_t &plan,
@ -140,10 +146,17 @@ hb_ot_shape_planner_t::compile (hb_ot_shape_plan_t &plan,
plan.apply_kern = true; plan.apply_kern = true;
} }
bool has_kern_mark = plan.apply_kern && hb_ot_layout_has_cross_kerning (face); plan.zero_marks = script_zero_marks &&
plan.zero_marks = !plan.apply_morx && !plan.apply_kerx && !has_kern_mark; !plan.apply_kerx &&
(!plan.apply_kern || !hb_ot_layout_has_machine_kerning (face));
plan.has_gpos_mark = !!plan.map.get_1_mask (HB_TAG ('m','a','r','k')); plan.has_gpos_mark = !!plan.map.get_1_mask (HB_TAG ('m','a','r','k'));
plan.fallback_mark_positioning = !plan.apply_gpos && plan.zero_marks;
plan.adjust_mark_positioning_when_zeroing = !plan.apply_gpos &&
!plan.apply_kerx &&
(!plan.apply_kern || !hb_ot_layout_has_cross_kerning (face));
plan.fallback_mark_positioning = plan.adjust_mark_positioning_when_zeroing &&
script_fallback_mark_positioning;
/* Currently we always apply trak. */ /* Currently we always apply trak. */
plan.apply_trak = plan.requested_tracking && hb_aat_layout_has_tracking (face); plan.apply_trak = plan.requested_tracking && hb_aat_layout_has_tracking (face);
@ -158,6 +171,7 @@ hb_ot_shape_plan_t::init0 (hb_face_t *face,
hb_ot_shape_planner_t planner (face, hb_ot_shape_planner_t planner (face,
&key->props); &key->props);
hb_ot_shape_collect_features (&planner, hb_ot_shape_collect_features (&planner,
key->user_features, key->user_features,
key->num_user_features); key->num_user_features);
@ -811,17 +825,16 @@ hb_ot_position_complex (const hb_ot_shape_context_t *c)
hb_glyph_info_t *info = c->buffer->info; hb_glyph_info_t *info = c->buffer->info;
hb_glyph_position_t *pos = c->buffer->pos; hb_glyph_position_t *pos = c->buffer->pos;
/* If the font has no GPOS, AND, no fallback positioning will /* If the font has no GPOS and direction is forward, then when
* happen, AND, direction is forward, then when zeroing mark * zeroing mark widths, we shift the mark with it, such that the
* widths, we shift the mark with it, such that the mark * mark is positioned hanging over the previous glyph. When
* is positioned hanging over the previous glyph. When
* direction is backward we don't shift and it will end up * direction is backward we don't shift and it will end up
* hanging over the next glyph after the final reordering. * hanging over the next glyph after the final reordering.
* If fallback positinoing happens or GPOS is present, we don't *
* care. * Note: If fallback positinoing happens, we don't care about
* this as it will be overriden.
*/ */
bool adjust_offsets_when_zeroing = c->plan->fallback_mark_positioning && bool adjust_offsets_when_zeroing = c->plan->adjust_mark_positioning_when_zeroing &&
!c->plan->shaper->fallback_position &&
HB_DIRECTION_IS_FORWARD (c->buffer->props.direction); HB_DIRECTION_IS_FORWARD (c->buffer->props.direction);
/* We change glyph origin to what GPOS expects (horizontal), apply GPOS, change it back. */ /* We change glyph origin to what GPOS expects (horizontal), apply GPOS, change it back. */
@ -877,7 +890,7 @@ hb_ot_position_complex (const hb_ot_shape_context_t *c)
&pos[i].x_offset, &pos[i].x_offset,
&pos[i].y_offset); &pos[i].y_offset);
if (c->plan->fallback_mark_positioning && c->plan->shaper->fallback_position) if (c->plan->fallback_mark_positioning)
_hb_ot_shape_fallback_mark_position (c->plan, c->font, c->buffer); _hb_ot_shape_fallback_mark_position (c->plan, c->font, c->buffer);
} }

View File

@ -77,6 +77,7 @@ struct hb_ot_shape_plan_t
bool zero_marks : 1; bool zero_marks : 1;
bool fallback_glyph_classes : 1; bool fallback_glyph_classes : 1;
bool fallback_mark_positioning : 1; bool fallback_mark_positioning : 1;
bool adjust_mark_positioning_when_zeroing : 1;
bool apply_gpos : 1; bool apply_gpos : 1;
bool apply_kerx : 1; bool apply_kerx : 1;
@ -113,6 +114,8 @@ struct hb_ot_shape_planner_t
hb_ot_map_builder_t map; hb_ot_map_builder_t map;
hb_aat_map_builder_t aat_map; hb_aat_map_builder_t aat_map;
bool apply_morx : 1; bool apply_morx : 1;
bool script_zero_marks : 1;
bool script_fallback_mark_positioning : 1;
const struct hb_ot_complex_shaper_t *shaper; const struct hb_ot_complex_shaper_t *shaper;
HB_INTERNAL hb_ot_shape_planner_t (hb_face_t *face, HB_INTERNAL hb_ot_shape_planner_t (hb_face_t *face,

View File

@ -65,7 +65,7 @@ struct InstanceRecord
// * instance. */ // * instance. */
public: public:
DEFINE_SIZE_ARRAY (4, coordinatesZ); DEFINE_SIZE_UNBOUNDED (4);
}; };
struct AxisRecord struct AxisRecord
@ -109,40 +109,46 @@ struct fvar
axisSize == 20 && /* Assumed in our code. */ axisSize == 20 && /* Assumed in our code. */
instanceSize >= axisCount * 4 + 4 && instanceSize >= axisCount * 4 + 4 &&
get_axes ().sanitize (c) && get_axes ().sanitize (c) &&
c->check_range (&get_instance (0), instanceCount, instanceSize)); c->check_range (get_instance (0), instanceCount, instanceSize));
} }
inline unsigned int get_axis_count (void) const inline unsigned int get_axis_count (void) const
{ return axisCount; } { return axisCount; }
inline bool get_axis (unsigned int index, hb_ot_var_axis_t *info) const inline void get_axis_deprecated (unsigned int axis_index,
hb_ot_var_axis_t *info) const
{ {
if (info) const AxisRecord &axis = get_axes ()[axis_index];
{ info->tag = axis.axisTag;
const AxisRecord &axis = get_axes ()[index]; info->name_id = axis.axisNameID;
info->tag = axis.axisTag; info->default_value = axis.defaultValue / 65536.;
info->name_id = axis.axisNameID; /* Ensure order, to simplify client math. */
info->default_value = axis.defaultValue / 65536.; info->min_value = MIN<float> (info->default_value, axis.minValue / 65536.);
/* Ensure order, to simplify client math. */ info->max_value = MAX<float> (info->default_value, axis.maxValue / 65536.);
info->min_value = MIN<float> (info->default_value, axis.minValue / 65536.);
info->max_value = MAX<float> (info->default_value, axis.maxValue / 65536.);
}
return true;
} }
inline hb_ot_var_axis_flags_t get_axis_flags (unsigned int index) const inline void get_axis_info (unsigned int axis_index,
hb_ot_var_axis_info_t *info) const
{ {
const AxisRecord &axis = get_axes ()[index]; const AxisRecord &axis = get_axes ()[axis_index];
return (hb_ot_var_axis_flags_t) (unsigned int) axis.flags; info->axis_index = axis_index;
info->tag = axis.axisTag;
info->name_id = axis.axisNameID;
info->flags = (hb_ot_var_axis_flags_t) (unsigned int) axis.flags;
info->default_value = axis.defaultValue / 65536.;
/* Ensure order, to simplify client math. */
info->min_value = MIN<float> (info->default_value, axis.minValue / 65536.);
info->max_value = MAX<float> (info->default_value, axis.maxValue / 65536.);
info->reserved = 0;
} }
inline unsigned int get_axis_infos (unsigned int start_offset, inline unsigned int get_axes_deprecated (unsigned int start_offset,
unsigned int *axes_count /* IN/OUT */, unsigned int *axes_count /* IN/OUT */,
hb_ot_var_axis_t *axes_array /* OUT */) const hb_ot_var_axis_t *axes_array /* OUT */) const
{ {
if (axes_count) if (axes_count)
{ {
/* TODO Rewrite as hb_array_t<>::sub-array() */
unsigned int count = axisCount; unsigned int count = axisCount;
start_offset = MIN (start_offset, count); start_offset = MIN (start_offset, count);
@ -153,32 +159,70 @@ struct fvar
*axes_count = count; *axes_count = count;
for (unsigned int i = 0; i < count; i++) for (unsigned int i = 0; i < count; i++)
get_axis (start_offset + i, axes_array + i); get_axis_deprecated (start_offset + i, axes_array + i);
} }
return axisCount; return axisCount;
} }
inline bool find_axis (hb_tag_t tag, unsigned int *index, hb_ot_var_axis_t *info) const inline unsigned int get_axis_infos (unsigned int start_offset,
unsigned int *axes_count /* IN/OUT */,
hb_ot_var_axis_info_t *axes_array /* OUT */) const
{
if (axes_count)
{
/* TODO Rewrite as hb_array_t<>::sub-array() */
unsigned int count = axisCount;
start_offset = MIN (start_offset, count);
count -= start_offset;
axes_array += start_offset;
count = MIN (count, *axes_count);
*axes_count = count;
for (unsigned int i = 0; i < count; i++)
get_axis_info (start_offset + i, axes_array + i);
}
return axisCount;
}
inline bool find_axis_deprecated (hb_tag_t tag,
unsigned int *axis_index,
hb_ot_var_axis_t *info) const
{ {
const AxisRecord *axes = get_axes (); const AxisRecord *axes = get_axes ();
unsigned int count = get_axis_count (); unsigned int count = get_axis_count ();
for (unsigned int i = 0; i < count; i++) for (unsigned int i = 0; i < count; i++)
if (axes[i].axisTag == tag) if (axes[i].axisTag == tag)
{ {
if (index) if (axis_index)
*index = i; *axis_index = i;
return get_axis (i, info); get_axis_deprecated (i, info);
return true;
}
if (axis_index)
*axis_index = HB_OT_VAR_NO_AXIS_INDEX;
return false;
}
inline bool find_axis_info (hb_tag_t tag,
hb_ot_var_axis_info_t *info) const
{
const AxisRecord *axes = get_axes ();
unsigned int count = get_axis_count ();
for (unsigned int i = 0; i < count; i++)
if (axes[i].axisTag == tag)
{
get_axis_info (i, info);
return true;
} }
if (index)
*index = HB_OT_VAR_NO_AXIS_INDEX;
return false; return false;
} }
inline int normalize_axis_value (unsigned int axis_index, float v) const inline int normalize_axis_value (unsigned int axis_index, float v) const
{ {
hb_ot_var_axis_t axis; hb_ot_var_axis_info_t axis;
if (!get_axis (axis_index, &axis)) get_axis_info (axis_index, &axis);
return 0;
v = MAX (MIN (v, axis.max_value), axis.min_value); /* Clamp. */ v = MAX (MIN (v, axis.max_value), axis.min_value); /* Clamp. */
@ -194,25 +238,28 @@ struct fvar
inline unsigned int get_instance_count (void) const inline unsigned int get_instance_count (void) const
{ return instanceCount; } { return instanceCount; }
inline hb_ot_name_id_t get_instance_subfamily_name_id (unsigned int index) const inline hb_ot_name_id_t get_instance_subfamily_name_id (unsigned int instance_index) const
{ {
const InstanceRecord &instance = get_instance (index); const InstanceRecord *instance = get_instance (instance_index);
return instance.subfamilyNameID; if (unlikely (!instance)) return HB_OT_NAME_ID_INVALID;
return instance->subfamilyNameID;
} }
inline hb_ot_name_id_t get_instance_postscript_name_id (unsigned int index) const inline hb_ot_name_id_t get_instance_postscript_name_id (unsigned int instance_index) const
{ {
const InstanceRecord &instance = get_instance (index); const InstanceRecord *instance = get_instance (instance_index);
if (unlikely (!instance)) return HB_OT_NAME_ID_INVALID;
if (instanceSize >= axisCount * 4 + 6) if (instanceSize >= axisCount * 4 + 6)
return StructAfter<NameID> (instance.get_coordinates (axisCount)); return StructAfter<NameID> (instance->get_coordinates (axisCount));
return HB_OT_NAME_ID_INVALID; return HB_OT_NAME_ID_INVALID;
} }
inline unsigned int get_instance_coords (unsigned int index, inline unsigned int get_instance_coords (unsigned int instance_index,
unsigned int *coords_length, /* IN/OUT */ unsigned int *coords_length, /* IN/OUT */
float *coords /* OUT */) const float *coords /* OUT */) const
{ {
if (unlikely (index >= instanceCount)) const InstanceRecord *instance = get_instance (instance_index);
if (unlikely (!instance))
{ {
if (coords_length) if (coords_length)
*coords_length = 0; *coords_length = 0;
@ -221,9 +268,8 @@ struct fvar
if (coords_length && *coords_length) if (coords_length && *coords_length)
{ {
const InstanceRecord &instance = get_instance (index); hb_array_t<const Fixed> instanceCoords = instance->get_coordinates (axisCount)
hb_array_t<const Fixed> instanceCoords = instance.get_coordinates (axisCount) .sub_array (0, *coords_length);
.sub_array (0, *coords_length);
for (unsigned int i = 0; i < instanceCoords.len; i++) for (unsigned int i = 0; i < instanceCoords.len; i++)
coords[i] = instanceCoords.arrayZ[i].to_float (); coords[i] = instanceCoords.arrayZ[i].to_float ();
} }
@ -234,12 +280,11 @@ struct fvar
inline hb_array_t<const AxisRecord> get_axes (void) const inline hb_array_t<const AxisRecord> get_axes (void) const
{ return hb_array (&(this+firstAxis), axisCount); } { return hb_array (&(this+firstAxis), axisCount); }
inline const InstanceRecord &get_instance (unsigned int i) const inline const InstanceRecord *get_instance (unsigned int i) const
{ {
if (unlikely (i >= instanceCount)) return Null (InstanceRecord); if (unlikely (i >= instanceCount)) return nullptr;
return &StructAtOffset<InstanceRecord> (&StructAfter<InstanceRecord> (get_axes ()),
return StructAtOffset<InstanceRecord> (&StructAfter<InstanceRecord> (get_axes ()), i * instanceSize);
i * instanceSize);
} }
protected: protected:

View File

@ -53,7 +53,6 @@
* @face: #hb_face_t to test * @face: #hb_face_t to test
* *
* This function allows to verify the presence of OpenType variation data on the face. * This function allows to verify the presence of OpenType variation data on the face.
* Alternatively, use hb_ot_var_get_axis_count().
* *
* Return value: true if face has a `fvar' table and false otherwise * Return value: true if face has a `fvar' table and false otherwise
* *
@ -80,6 +79,7 @@ hb_ot_var_get_axis_count (hb_face_t *face)
* hb_ot_var_get_axes: * hb_ot_var_get_axes:
* *
* Since: 1.4.2 * Since: 1.4.2
* Deprecated: REPLACEME
**/ **/
unsigned int unsigned int
hb_ot_var_get_axes (hb_face_t *face, hb_ot_var_get_axes (hb_face_t *face,
@ -87,13 +87,14 @@ hb_ot_var_get_axes (hb_face_t *face,
unsigned int *axes_count /* IN/OUT */, unsigned int *axes_count /* IN/OUT */,
hb_ot_var_axis_t *axes_array /* OUT */) hb_ot_var_axis_t *axes_array /* OUT */)
{ {
return face->table.fvar->get_axis_infos (start_offset, axes_count, axes_array); return face->table.fvar->get_axes_deprecated (start_offset, axes_count, axes_array);
} }
/** /**
* hb_ot_var_find_axis: * hb_ot_var_find_axis:
* *
* Since: 1.4.2 * Since: 1.4.2
* Deprecated: REPLACEME
**/ **/
hb_bool_t hb_bool_t
hb_ot_var_find_axis (hb_face_t *face, hb_ot_var_find_axis (hb_face_t *face,
@ -101,21 +102,37 @@ hb_ot_var_find_axis (hb_face_t *face,
unsigned int *axis_index, unsigned int *axis_index,
hb_ot_var_axis_t *axis_info) hb_ot_var_axis_t *axis_info)
{ {
return face->table.fvar->find_axis (axis_tag, axis_index, axis_info); return face->table.fvar->find_axis_deprecated (axis_tag, axis_index, axis_info);
} }
/** /**
* hb_ot_var_axis_get_flags: * hb_ot_var_get_axis_infos:
* *
* Since: REPLACEME * Since: REPLACEME
**/ **/
hb_ot_var_axis_flags_t HB_EXTERN unsigned int
hb_ot_var_axis_get_flags (hb_face_t *face, hb_ot_var_get_axis_infos (hb_face_t *face,
unsigned int axis_index) unsigned int start_offset,
unsigned int *axes_count /* IN/OUT */,
hb_ot_var_axis_info_t *axes_array /* OUT */)
{ {
return face->table.fvar->get_axis_flags (axis_index); return face->table.fvar->get_axis_infos (start_offset, axes_count, axes_array);
} }
/**
* hb_ot_var_find_axis_info:
*
* Since: REPLACEME
**/
HB_EXTERN hb_bool_t
hb_ot_var_find_axis_info (hb_face_t *face,
hb_tag_t axis_tag,
hb_ot_var_axis_info_t *axis_info)
{
return face->table.fvar->find_axis_info (axis_tag, axis_info);
}
/* /*
* Named instances. * Named instances.
*/ */
@ -168,10 +185,10 @@ hb_ot_var_normalize_variations (hb_face_t *face,
const OT::fvar &fvar = *face->table.fvar; const OT::fvar &fvar = *face->table.fvar;
for (unsigned int i = 0; i < variations_length; i++) for (unsigned int i = 0; i < variations_length; i++)
{ {
unsigned int axis_index; hb_ot_var_axis_info_t info;
if (hb_ot_var_find_axis (face, variations[i].tag, &axis_index, nullptr) && if (hb_ot_var_find_axis_info (face, variations[i].tag, &info) &&
axis_index < coords_length) info.axis_index < coords_length)
coords[axis_index] = fvar.normalize_axis_value (axis_index, variations[i].value); coords[info.axis_index] = fvar.normalize_axis_value (info.axis_index, variations[i].value);
} }
face->table.avar->map_coords (coords, coords_length); face->table.avar->map_coords (coords, coords_length);

View File

@ -47,19 +47,6 @@ HB_BEGIN_DECLS
* fvar / avar * fvar / avar
*/ */
/**
* hb_ot_var_axis_t:
*
* Since: 1.4.2
*/
typedef struct hb_ot_var_axis_t {
hb_tag_t tag;
hb_ot_name_id_t name_id;
float min_value;
float default_value;
float max_value;
} hb_ot_var_axis_t;
HB_EXTERN hb_bool_t HB_EXTERN hb_bool_t
hb_ot_var_has_data (hb_face_t *face); hb_ot_var_has_data (hb_face_t *face);
@ -68,28 +55,10 @@ hb_ot_var_has_data (hb_face_t *face);
* Variation axes. * Variation axes.
*/ */
/**
* HB_OT_VAR_NO_AXIS_INDEX:
*
* Since: 1.4.2
*/
#define HB_OT_VAR_NO_AXIS_INDEX 0xFFFFFFFFu
HB_EXTERN unsigned int HB_EXTERN unsigned int
hb_ot_var_get_axis_count (hb_face_t *face); hb_ot_var_get_axis_count (hb_face_t *face);
HB_EXTERN unsigned int
hb_ot_var_get_axes (hb_face_t *face,
unsigned int start_offset,
unsigned int *axes_count /* IN/OUT */,
hb_ot_var_axis_t *axes_array /* OUT */);
HB_EXTERN hb_bool_t
hb_ot_var_find_axis (hb_face_t *face,
hb_tag_t axis_tag,
unsigned int *axis_index,
hb_ot_var_axis_t *axis_info);
/** /**
* hb_ot_var_axis_flags_t: * hb_ot_var_axis_flags_t:
* @HB_OT_VAR_AXIS_FLAG_HIDDEN: The axis should not be exposed directly in user interfaces. * @HB_OT_VAR_AXIS_FLAG_HIDDEN: The axis should not be exposed directly in user interfaces.
@ -97,12 +66,39 @@ hb_ot_var_find_axis (hb_face_t *face,
* Since: REPLACEME * Since: REPLACEME
*/ */
typedef enum { /*< flags >*/ typedef enum { /*< flags >*/
HB_OT_VAR_AXIS_FLAG_HIDDEN = 0x0001u, HB_OT_VAR_AXIS_FLAG_HIDDEN = 0x00000001u,
_HB_OT_VAR_AXIS_FLAG_MAX_VALUE= 0x7FFFFFFFu, /*< skip >*/
} hb_ot_var_axis_flags_t; } hb_ot_var_axis_flags_t;
HB_EXTERN hb_ot_var_axis_flags_t /**
hb_ot_var_axis_get_flags (hb_face_t *face, * hb_ot_var_axis_info_t:
unsigned int axis_index); *
* Since: REPLACEME
*/
typedef struct hb_ot_var_axis_info_t
{
unsigned int axis_index;
hb_tag_t tag;
hb_ot_name_id_t name_id;
hb_ot_var_axis_flags_t flags;
float min_value;
float default_value;
float max_value;
/*< private >*/
unsigned int reserved;
} hb_ot_var_axis_info_t;
HB_EXTERN unsigned int
hb_ot_var_get_axis_infos (hb_face_t *face,
unsigned int start_offset,
unsigned int *axes_count /* IN/OUT */,
hb_ot_var_axis_info_t *axes_array /* OUT */);
HB_EXTERN hb_bool_t
hb_ot_var_find_axis_info (hb_face_t *face,
hb_tag_t axis_tag,
hb_ot_var_axis_info_t *axis_info);
/* /*

View File

@ -63,11 +63,10 @@ struct VORG
inline int get_y_origin (hb_codepoint_t glyph) const inline int get_y_origin (hb_codepoint_t glyph) const
{ {
int i = vertYOrigins.bsearch (glyph); unsigned int i;
if (i != -1) if (!vertYOrigins.bfind (glyph, &i))
return vertYOrigins[i].vertOriginY; return defaultVertOriginY;
return vertYOrigins[i].vertOriginY;
return defaultVertOriginY;
} }
inline bool _subset (const hb_subset_plan_t *plan HB_UNUSED, inline bool _subset (const hb_subset_plan_t *plan HB_UNUSED,

View File

@ -45,7 +45,7 @@ struct hb_set_t
struct page_map_t struct page_map_t
{ {
inline int cmp (const page_map_t *o) const { return (int) o->major - (int) major; } inline int cmp (const page_map_t &o) const { return (int) o.major - (int) major; }
uint32_t major; uint32_t major;
uint32_t index; uint32_t index;
@ -341,11 +341,11 @@ struct hb_set_t
{ {
/* TODO perform op even if !successful. */ /* TODO perform op even if !successful. */
if (unlikely (!successful)) return; if (unlikely (!successful)) return;
page_t *p = page_for (g); page_t *page = page_for (g);
if (!p) if (!page)
return; return;
dirty (); dirty ();
p->del (g); page->del (g);
} }
inline void del_range (hb_codepoint_t a, hb_codepoint_t b) inline void del_range (hb_codepoint_t a, hb_codepoint_t b)
{ {
@ -357,10 +357,10 @@ struct hb_set_t
} }
inline bool has (hb_codepoint_t g) const inline bool has (hb_codepoint_t g) const
{ {
const page_t *p = page_for (g); const page_t *page = page_for (g);
if (!p) if (!page)
return false; return false;
return p->has (g); return page->has (g);
} }
inline bool intersects (hb_codepoint_t first, inline bool intersects (hb_codepoint_t first,
hb_codepoint_t last) const hb_codepoint_t last) const
@ -544,7 +544,7 @@ struct hb_set_t
page_map_t map = {get_major (*codepoint), 0}; page_map_t map = {get_major (*codepoint), 0};
unsigned int i; unsigned int i;
page_map.bfind (map, &i); page_map.bfind (map, &i, HB_BFIND_NOT_FOUND_STORE_CLOSEST);
if (i < page_map.len && page_map[i].major == map.major) if (i < page_map.len && page_map[i].major == map.major)
{ {
if (pages[page_map[i].index].next (codepoint)) if (pages[page_map[i].index].next (codepoint))
@ -575,7 +575,7 @@ struct hb_set_t
page_map_t map = {get_major (*codepoint), 0}; page_map_t map = {get_major (*codepoint), 0};
unsigned int i; unsigned int i;
page_map.bfind (map, &i); page_map.bfind (map, &i, HB_BFIND_NOT_FOUND_STORE_CLOSEST);
if (i < page_map.len && page_map[i].major == map.major) if (i < page_map.len && page_map[i].major == map.major)
{ {
if (pages[page_map[i].index].previous (codepoint)) if (pages[page_map[i].index].previous (codepoint))
@ -670,7 +670,7 @@ struct hb_set_t
{ {
page_map_t map = {get_major (g), pages.len}; page_map_t map = {get_major (g), pages.len};
unsigned int i; unsigned int i;
if (!page_map.bfind (map, &i)) if (!page_map.bfind (map, &i, HB_BFIND_NOT_FOUND_STORE_CLOSEST))
{ {
if (!resize (pages.len + 1)) if (!resize (pages.len + 1))
return nullptr; return nullptr;

View File

@ -48,7 +48,7 @@
**/ **/
#ifdef HB_USE_ATEXIT #if HB_USE_ATEXIT
static void free_static_shaper_list (void); static void free_static_shaper_list (void);
#endif #endif
@ -69,7 +69,7 @@ static struct hb_shaper_list_lazy_loader_t : hb_lazy_loader_t<const char *,
shaper_list[i] = shapers[i].name; shaper_list[i] = shapers[i].name;
shaper_list[i] = nullptr; shaper_list[i] = nullptr;
#ifdef HB_USE_ATEXIT #if HB_USE_ATEXIT
atexit (free_static_shaper_list); atexit (free_static_shaper_list);
#endif #endif
@ -85,7 +85,7 @@ static struct hb_shaper_list_lazy_loader_t : hb_lazy_loader_t<const char *,
} }
} static_shaper_list; } static_shaper_list;
#ifdef HB_USE_ATEXIT #if HB_USE_ATEXIT
static static
void free_static_shaper_list (void) void free_static_shaper_list (void)
{ {

View File

@ -35,7 +35,7 @@ static const hb_shaper_entry_t all_shapers[] = {
#undef HB_SHAPER_IMPLEMENT #undef HB_SHAPER_IMPLEMENT
}; };
#ifdef HB_USE_ATEXIT #if HB_USE_ATEXIT
static void free_static_shapers (void); static void free_static_shapers (void);
#endif #endif
@ -80,7 +80,7 @@ static struct hb_shapers_lazy_loader_t : hb_lazy_loader_t<const hb_shaper_entry_
p = end + 1; p = end + 1;
} }
#ifdef HB_USE_ATEXIT #if HB_USE_ATEXIT
atexit (free_static_shapers); atexit (free_static_shapers);
#endif #endif
@ -96,7 +96,7 @@ static struct hb_shapers_lazy_loader_t : hb_lazy_loader_t<const hb_shaper_entry_
} }
} static_shapers; } static_shapers;
#ifdef HB_USE_ATEXIT #if HB_USE_ATEXIT
static static
void free_static_shapers (void) void free_static_shapers (void)
{ {

View File

@ -27,10 +27,12 @@
#include "hb.hh" #include "hb.hh"
#include "hb-open-type.hh" #include "hb-open-type.hh"
#include "hb-ot-layout-common.hh"
#include "hb-aat-layout-common.hh"
#include "hb-face.hh" #include "hb-face.hh"
#include "hb-aat-layout-common.hh"
#include "hb-aat-layout-feat-table.hh"
#include "hb-ot-layout-common.hh"
#include "hb-ot-cmap-table.hh"
#include "hb-ot-head-table.hh" #include "hb-ot-head-table.hh"
#include "hb-ot-maxp-table.hh" #include "hb-ot-maxp-table.hh"
@ -42,6 +44,8 @@ hb_vector_size_impl_t const _hb_NullPool[(HB_NULL_POOL_SIZE + sizeof (hb_vector_
DEFINE_NULL_NAMESPACE_BYTES (OT, Index) = {0xFF,0xFF}; DEFINE_NULL_NAMESPACE_BYTES (OT, Index) = {0xFF,0xFF};
DEFINE_NULL_NAMESPACE_BYTES (OT, LangSys) = {0x00,0x00, 0xFF,0xFF, 0x00,0x00}; DEFINE_NULL_NAMESPACE_BYTES (OT, LangSys) = {0x00,0x00, 0xFF,0xFF, 0x00,0x00};
DEFINE_NULL_NAMESPACE_BYTES (OT, RangeRecord) = {0x00,0x01, 0x00,0x00, 0x00, 0x00}; DEFINE_NULL_NAMESPACE_BYTES (OT, RangeRecord) = {0x00,0x01, 0x00,0x00, 0x00, 0x00};
DEFINE_NULL_NAMESPACE_BYTES (OT, CmapSubtableLongGroup) = {0x00,0x00,0x00,0x01, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00};
DEFINE_NULL_NAMESPACE_BYTES (AAT, SettingName) = {0xFF,0xFF, 0xFF,0xFF};
/* Hand-coded because Lookup is a template. Sad. */ /* Hand-coded because Lookup is a template. Sad. */
const unsigned char _hb_Null_AAT_Lookup[2] = {0xFF, 0xFF}; const unsigned char _hb_Null_AAT_Lookup[2] = {0xFF, 0xFF};

View File

@ -222,7 +222,7 @@ hb_ucdn_decompose(hb_unicode_funcs_t *ufuncs HB_UNUSED,
} }
#ifdef HB_USE_ATEXIT #if HB_USE_ATEXIT
static void free_static_ucdn_funcs (void); static void free_static_ucdn_funcs (void);
#endif #endif
@ -241,7 +241,7 @@ static struct hb_ucdn_unicode_funcs_lazy_loader_t : hb_unicode_funcs_lazy_loader
hb_unicode_funcs_make_immutable (funcs); hb_unicode_funcs_make_immutable (funcs);
#ifdef HB_USE_ATEXIT #if HB_USE_ATEXIT
atexit (free_static_ucdn_funcs); atexit (free_static_ucdn_funcs);
#endif #endif
@ -249,7 +249,7 @@ static struct hb_ucdn_unicode_funcs_lazy_loader_t : hb_unicode_funcs_lazy_loader
} }
} static_ucdn_funcs; } static_ucdn_funcs;
#ifdef HB_USE_ATEXIT #if HB_USE_ATEXIT
static static
void free_static_ucdn_funcs (void) void free_static_ucdn_funcs (void)
{ {

View File

@ -245,7 +245,7 @@ static struct hb_uniscribe_shaper_funcs_lazy_loader_t : hb_lazy_loader_t<hb_unis
funcs->init (); funcs->init ();
#ifdef HB_USE_ATEXIT #if HB_USE_ATEXIT
atexit (free_static_uniscribe_shaper_funcs); atexit (free_static_uniscribe_shaper_funcs);
#endif #endif
@ -261,7 +261,7 @@ static struct hb_uniscribe_shaper_funcs_lazy_loader_t : hb_lazy_loader_t<hb_unis
} }
} static_uniscribe_shaper_funcs; } static_uniscribe_shaper_funcs;
#ifdef HB_USE_ATEXIT #if HB_USE_ATEXIT
static static
void free_static_uniscribe_shaper_funcs (void) void free_static_uniscribe_shaper_funcs (void)
{ {

View File

@ -31,13 +31,15 @@
#include "hb.hh" #include "hb.hh"
template <typename Type, unsigned int StaticSize=8> template <typename Type, unsigned int PreallocedCount=8>
struct hb_vector_t struct hb_vector_t
{ {
static_assert ((bool) (unsigned) hb_static_size (Type), "");
typedef Type ItemType; typedef Type ItemType;
enum { item_size = sizeof (Type) }; enum { item_size = sizeof (Type) };
HB_NO_COPY_ASSIGN_TEMPLATE2 (hb_vector_t, Type, StaticSize); HB_NO_COPY_ASSIGN_TEMPLATE2 (hb_vector_t, Type, PreallocedCount);
inline hb_vector_t (void) { init (); } inline hb_vector_t (void) { init (); }
inline ~hb_vector_t (void) { fini (); } inline ~hb_vector_t (void) { fini (); }
@ -45,7 +47,7 @@ struct hb_vector_t
private: private:
unsigned int allocated; /* == 0 means allocation failed. */ unsigned int allocated; /* == 0 means allocation failed. */
Type *arrayZ_; Type *arrayZ_;
Type static_array[StaticSize]; Type static_array[PreallocedCount];
public: public:
void init (void) void init (void)
@ -89,6 +91,16 @@ struct hb_vector_t
return arrayZ()[i]; return arrayZ()[i];
} }
inline hb_array_t<Type> as_array (void)
{ return hb_array (arrayZ(), len); }
inline hb_array_t<const Type> as_array (void) const
{ return hb_array (arrayZ(), len); }
inline hb_sorted_array_t<Type> as_sorted_array (void)
{ return hb_sorted_array (arrayZ(), len); }
inline hb_sorted_array_t<const Type> as_sorted_array (void) const
{ return hb_sorted_array (arrayZ(), len); }
template <typename T> inline operator T * (void) { return arrayZ(); } template <typename T> inline operator T * (void) { return arrayZ(); }
template <typename T> inline operator const T * (void) const { return arrayZ(); } template <typename T> inline operator const T * (void) const { return arrayZ(); }
@ -209,75 +221,28 @@ struct hb_vector_t
} }
inline void qsort (int (*cmp)(const void*, const void*)) inline void qsort (int (*cmp)(const void*, const void*))
{ { as_array ().qsort (cmp); }
::qsort (arrayZ(), len, sizeof (Type), cmp); inline void qsort (unsigned int start = 0, unsigned int end = (unsigned int) -1)
} { as_array ().qsort (start, end); }
inline void qsort (void)
{
::qsort (arrayZ(), len, sizeof (Type), Type::cmp);
}
inline void qsort (unsigned int start, unsigned int end)
{
::qsort (arrayZ() + start, end - start, sizeof (Type), Type::cmp);
}
template <typename T> template <typename T>
inline Type *lsearch (const T &x) inline Type *lsearch (const T &x, Type *not_found = nullptr)
{ { return as_array ().lsearch (x, not_found); }
Type *array = arrayZ();
for (unsigned int i = 0; i < len; i++)
if (0 == array[i].cmp (&x))
return &array[i];
return nullptr;
}
template <typename T> template <typename T>
inline const Type *lsearch (const T &x) const inline const Type *lsearch (const T &x, const Type *not_found = nullptr) const
{ { return as_array ().lsearch (x, not_found); }
const Type *array = arrayZ();
for (unsigned int i = 0; i < len; i++)
if (0 == array[i].cmp (&x))
return &array[i];
return nullptr;
}
template <typename T> template <typename T>
inline Type *bsearch (const T &x) inline Type *bsearch (const T &x, Type *not_found = nullptr)
{ { return as_sorted_array ().bsearch (x, not_found); }
unsigned int i;
return bfind (x, &i) ? &arrayZ()[i] : nullptr;
}
template <typename T> template <typename T>
inline const Type *bsearch (const T &x) const inline const Type *bsearch (const T &x, const Type *not_found = nullptr) const
{ { return as_sorted_array ().bsearch (x, not_found); }
unsigned int i;
return bfind (x, &i) ? &arrayZ()[i] : nullptr;
}
template <typename T> template <typename T>
inline bool bfind (const T &x, unsigned int *i) const inline bool bfind (const T &x, unsigned int *i = nullptr,
{ hb_bfind_not_found_t not_found = HB_BFIND_NOT_FOUND_DONT_STORE,
int min = 0, max = (int) this->len - 1; unsigned int to_store = (unsigned int) -1) const
const Type *array = this->arrayZ(); { return as_sorted_array ().bfind (x, i, not_found, to_store); }
while (min <= max)
{
int mid = ((unsigned int) min + (unsigned int) max) / 2;
int c = array[mid].cmp (&x);
if (c < 0)
max = mid - 1;
else if (c > 0)
min = mid + 1;
else
{
*i = mid;
return true;
}
}
if (max < 0 || (max < (int) this->len && array[max].cmp (&x) > 0))
max++;
*i = max;
return false;
}
}; };

View File

@ -47,6 +47,10 @@
#define HB_H_IN #define HB_H_IN
#include "hb-ot.h" #include "hb-ot.h"
#define HB_OT_H_IN #define HB_OT_H_IN
#include "hb-aat.h"
#define HB_AAT_H_IN
#include "hb-aat.h"
#include <math.h> #include <math.h>
#include <stdlib.h> #include <stdlib.h>
@ -281,7 +285,7 @@ static int errno = 0; /* Use something better? */
# endif # endif
#endif #endif
#if HAVE_ATEXIT #if defined(HAVE_ATEXIT) && !defined(HB_USE_ATEXIT)
/* atexit() is only safe to be called from shared libraries on certain /* atexit() is only safe to be called from shared libraries on certain
* platforms. Whitelist. * platforms. Whitelist.
* https://bugs.freedesktop.org/show_bug.cgi?id=82246 */ * https://bugs.freedesktop.org/show_bug.cgi?id=82246 */
@ -313,6 +317,9 @@ static int errno = 0; /* Use something better? */
#ifdef HB_NO_ATEXIT #ifdef HB_NO_ATEXIT
# undef HB_USE_ATEXIT # undef HB_USE_ATEXIT
#endif #endif
#ifndef HB_USE_ATEXIT
# define HB_USE_ATEXIT 0
#endif
#define HB_STMT_START do #define HB_STMT_START do
#define HB_STMT_END while (0) #define HB_STMT_END while (0)
@ -443,9 +450,11 @@ typedef uint64_t hb_vector_size_impl_t;
* For example, for testing "x ∈ {x1, x2, x3}" use: * For example, for testing "x ∈ {x1, x2, x3}" use:
* (FLAG_UNSAFE(x) & (FLAG(x1) | FLAG(x2) | FLAG(x3))) * (FLAG_UNSAFE(x) & (FLAG(x1) | FLAG(x2) | FLAG(x3)))
*/ */
#define FLAG(x) (ASSERT_STATIC_EXPR_ZERO ((unsigned int)(x) < 32) + (1U << (unsigned int)(x))) #define FLAG(x) (ASSERT_STATIC_EXPR_ZERO ((unsigned)(x) < 32) + (((uint32_t) 1U) << (unsigned)(x)))
#define FLAG_UNSAFE(x) ((unsigned int)(x) < 32 ? (1U << (unsigned int)(x)) : 0) #define FLAG_UNSAFE(x) ((unsigned)(x) < 32 ? (((uint32_t) 1U) << (unsigned)(x)) : 0)
#define FLAG_RANGE(x,y) (ASSERT_STATIC_EXPR_ZERO ((x) < (y)) + FLAG(y+1) - FLAG(x)) #define FLAG_RANGE(x,y) (ASSERT_STATIC_EXPR_ZERO ((x) < (y)) + FLAG(y+1) - FLAG(x))
#define FLAG64(x) (ASSERT_STATIC_EXPR_ZERO ((unsigned)(x) < 64) + (((uint64_t) 1ULL) << (unsigned)(x)))
#define FLAG64_UNSAFE(x) ((unsigned)(x) < 64 ? (((uint64_t) 1ULL) << (unsigned)(x)) : 0)
/* Size signifying variable-sized array */ /* Size signifying variable-sized array */
@ -503,10 +512,13 @@ _hb_memalign(void **memptr, size_t alignment, size_t size)
/* Some really basic things everyone wants. */ /* Some really basic things everyone wants. */
template <typename T> struct hb_remove_const { typedef T value; }; template <typename T> struct hb_remove_const { typedef T value; };
template <typename T> struct hb_remove_const<const T> { typedef T value; }; template <typename T> struct hb_remove_const<const T> { typedef T value; };
#define hb_remove_const(T) hb_remove_const<T>::value
template <typename T> struct hb_remove_reference { typedef T value; }; template <typename T> struct hb_remove_reference { typedef T value; };
template <typename T> struct hb_remove_reference<T &> { typedef T value; }; template <typename T> struct hb_remove_reference<T &> { typedef T value; };
#define hb_remove_reference(T) hb_remove_reference<T>::value
template <typename T> struct hb_remove_pointer { typedef T value; }; template <typename T> struct hb_remove_pointer { typedef T value; };
template <typename T> struct hb_remove_pointer<T *> { typedef T value; }; template <typename T> struct hb_remove_pointer<T *> { typedef T value; };
#define hb_remove_pointer(T) hb_remove_pointer<T>::value
/* Headers we include for everyone. Keep sorted. They express dependency amongst /* Headers we include for everyone. Keep sorted. They express dependency amongst

View File

@ -28,6 +28,7 @@ check_PROGRAMS = $(TEST_PROGS)
noinst_PROGRAMS = $(TEST_PROGS) noinst_PROGRAMS = $(TEST_PROGS)
TEST_PROGS = \ TEST_PROGS = \
test-aat-layout \
test-baseline \ test-baseline \
test-blob \ test-blob \
test-buffer \ test-buffer \

BIN
test/api/fonts/aat-feat.ttf Normal file

Binary file not shown.

118
test/api/test-aat-layout.c Normal file
View File

@ -0,0 +1,118 @@
/*
* Copyright © 2018 Google, Inc.
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*/
#include "hb-test.h"
#include <hb.h>
#include <hb-ot.h>
#include <hb-aat.h>
/* Unit tests for hb-aat.h */
static hb_face_t *face;
static hb_face_t *sbix;
static void
test_aat_get_feature_types (void)
{
hb_aat_layout_feature_type_t features[3];
unsigned int count = 3;
g_assert_cmpuint (11, ==, hb_aat_layout_get_feature_types (face, 0, &count, features));
g_assert_cmpuint (1, ==, features[0]);
g_assert_cmpuint (3, ==, features[1]);
g_assert_cmpuint (6, ==, features[2]);
g_assert_cmpuint (258, ==, hb_aat_layout_feature_type_get_name_id (face, features[0]));
g_assert_cmpuint (261, ==, hb_aat_layout_feature_type_get_name_id (face, features[1]));
g_assert_cmpuint (265, ==, hb_aat_layout_feature_type_get_name_id (face, features[2]));
}
static void
test_aat_get_feature_selectors (void)
{
unsigned int default_index;
hb_aat_layout_feature_selector_info_t settings[3];
unsigned int count = 3;
g_assert_cmpuint (4, ==, hb_aat_layout_feature_type_get_selector_infos (face,
HB_AAT_LAYOUT_FEATURE_TYPE_DESIGN_COMPLEXITY_TYPE,
0, &count, settings,
&default_index));
g_assert_cmpuint (3, ==, count);
g_assert_cmpuint (0, ==, default_index);
g_assert_cmpuint (0, ==, settings[0].enable);
g_assert_cmpuint (294, ==, settings[0].name_id);
g_assert_cmpuint (1, ==, settings[1].enable);
g_assert_cmpuint (295, ==, settings[1].name_id);
g_assert_cmpuint (2, ==, settings[2].enable);
g_assert_cmpuint (296, ==, settings[2].name_id);
count = 3;
g_assert_cmpuint (4, ==, hb_aat_layout_feature_type_get_selector_infos (face,
HB_AAT_LAYOUT_FEATURE_TYPE_DESIGN_COMPLEXITY_TYPE,
3, &count, settings,
&default_index));
g_assert_cmpuint (1, ==, count);
g_assert_cmpuint (0, ==, default_index);
g_assert_cmpuint (3, ==, settings[0].enable);
g_assert_cmpuint (297, ==, settings[0].name_id);
count = 1;
g_assert_cmpuint (1, ==, hb_aat_layout_feature_type_get_selector_infos (face,
HB_AAT_LAYOUT_FEATURE_TYPE_TYPOGRAPHIC_EXTRAS,
0, &count, settings,
&default_index));
g_assert_cmpuint (1, ==, count);
g_assert_cmpuint (HB_AAT_LAYOUT_NO_SELECTOR_INDEX, ==, default_index);
g_assert_cmpuint (8, ==, settings[0].enable);
g_assert_cmpuint (308, ==, settings[0].name_id);
count = 100;
g_assert_cmpuint (0, ==, hb_aat_layout_feature_type_get_selector_infos (face, HB_AAT_LAYOUT_FEATURE_TYPE_INVALID,
0, &count, settings,
NULL));
g_assert_cmpuint (0, ==, count);
}
int
main (int argc, char **argv)
{
hb_test_init (&argc, &argv);
hb_test_add (test_aat_get_feature_types);
hb_test_add (test_aat_get_feature_selectors);
face = hb_test_open_font_file ("fonts/aat-feat.ttf");
sbix = hb_test_open_font_file ("fonts/chromacheck-sbix.ttf");
unsigned int status = hb_test_run ();
hb_face_destroy (sbix);
hb_face_destroy (face);
return status;
}

View File

@ -33,6 +33,7 @@
#include <hb.h> #include <hb.h>
#include <hb-ot.h> #include <hb-ot.h>
#include <hb-aat.h>
#ifdef HAVE_GLIB #ifdef HAVE_GLIB
#include <hb-glib.h> #include <hb-glib.h>

View File

@ -93,7 +93,7 @@ test_face (hb_face_t *face,
hb_ot_name_get_utf32 (face, cp, NULL, NULL, NULL); hb_ot_name_get_utf32 (face, cp, NULL, NULL, NULL);
hb_ot_var_get_axis_count (face); hb_ot_var_get_axis_count (face);
hb_ot_var_get_axes (face, 0, NULL, NULL); hb_ot_var_get_axis_infos (face, 0, NULL, NULL);
hb_ot_var_normalize_variations (face, NULL, 0, NULL, 0); hb_ot_var_normalize_variations (face, NULL, 0, NULL, 0);
hb_ot_var_normalize_coords (face, 0, NULL, NULL); hb_ot_var_normalize_coords (face, 0, NULL, NULL);

View File

@ -449,6 +449,9 @@ test_ot_tag_language (void)
/* A UN M.49 region code, not an extended language subtag */ /* A UN M.49 region code, not an extended language subtag */
test_tag_from_language ("ARA", "ar-001"); test_tag_from_language ("ARA", "ar-001");
/* An invalid tag */
test_tag_from_language ("TRK", "tr@foo=bar");
} }
static void static void

View File

@ -5,6 +5,16 @@ if (HB_BUILD_UTILS)
add_test (NAME ${test} add_test (NAME ${test}
COMMAND "${PYTHON_EXECUTABLE}" run-tests.py $<TARGET_FILE:hb-shape> "data/in-house/${test}" COMMAND "${PYTHON_EXECUTABLE}" run-tests.py $<TARGET_FILE:hb-shape> "data/in-house/${test}"
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
set_property (TEST ${test} PROPERTY SKIP_RETURN_CODE 77)
endforeach ()
file (READ "${CMAKE_CURRENT_SOURCE_DIR}/data/aots/Makefile.sources" INHOUSE)
extract_make_variable (TESTS ${INHOUSE})
foreach (test IN ITEMS ${TESTS})
add_test (NAME ${test}
COMMAND "${PYTHON_EXECUTABLE}" run-tests.py $<TARGET_FILE:hb-shape> "data/aots/${test}"
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
set_property (TEST ${test} PROPERTY SKIP_RETURN_CODE 77)
endforeach () endforeach ()
file (READ "${CMAKE_CURRENT_SOURCE_DIR}/data/text-rendering-tests/Makefile.sources" TEXTRENDERING) file (READ "${CMAKE_CURRENT_SOURCE_DIR}/data/text-rendering-tests/Makefile.sources" TEXTRENDERING)
@ -13,5 +23,6 @@ if (HB_BUILD_UTILS)
add_test (NAME ${test} add_test (NAME ${test}
COMMAND "${PYTHON_EXECUTABLE}" run-tests.py $<TARGET_FILE:hb-shape> "data/text-rendering-tests/${test}" COMMAND "${PYTHON_EXECUTABLE}" run-tests.py $<TARGET_FILE:hb-shape> "data/text-rendering-tests/${test}"
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
set_property (TEST ${test} PROPERTY SKIP_RETURN_CODE 77)
endforeach () endforeach ()
endif () endif ()

View File

@ -4,6 +4,7 @@ NULL =
SUBDIRS = \ SUBDIRS = \
in-house \ in-house \
text-rendering-tests \ text-rendering-tests \
aots \
$(NULL) $(NULL)
# Convenience targets: # Convenience targets:

View File

@ -0,0 +1,13 @@
Copyright 2000-2016 Adobe Systems Incorporated. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use these files except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -0,0 +1,37 @@
# Process this file with automake to produce Makefile.in
NULL =
# Convenience targets:
lib:
@$(MAKE) $(AM_MAKEFLAGS) -C $(top_builddir)/src lib
EXTRA_DIST = \
COPYING \
fonts \
$(TESTS) \
$(NULL)
TEST_EXTENSIONS = .tests
TESTS_LOG_COMPILER = $(srcdir)/../../run-tests.py $(top_builddir)/util/hb-shape$(EXEEXT)
init-aots:
git clone https://github.com/adobe-type-tools/aots $(srcdir)/aots
make -C$(srcdir)/aots
make -C$(srcdir)/aots/harfbuzz
touch $(srcdir)/init-aots
update-tests: init-aots lib
cp $(srcdir)/hb-aots-tester.cpp $(srcdir)/aots/harfbuzz/hb-aots-tester.cpp
$(CXX) -Wno-narrowing $(srcdir)/aots/harfbuzz/hb-aots-tester.cpp \
-I$(top_srcdir)/src/ -o $(srcdir)/aots/harfbuzz/aots \
-L$(top_builddir)/src/.libs -lharfbuzz
rm -rf $(srcdir)/tests/
mkdir $(srcdir)/tests/
export LD_LIBRARY_PATH=$(realpath $(top_builddir)/src/.libs); cd $(srcdir)/aots/harfbuzz; ./aots
.PHONY: update-tests
include Makefile.sources
-include $(top_srcdir)/git.mk

View File

@ -0,0 +1,126 @@
TESTS = \
tests/classdef1_empty.tests \
tests/classdef1_multiple.tests \
tests/classdef1_single.tests \
tests/classdef1.tests \
tests/classdef2_empty.tests \
tests/classdef2_multiple.tests \
tests/classdef2_single.tests \
tests/classdef2.tests \
tests/gpos_chaining1_boundary.tests \
tests/gpos_chaining1_lookupflag.tests \
tests/gpos_chaining1_multiple_subrules.tests \
tests/gpos_chaining1_next_glyph.tests \
tests/gpos_chaining1_simple.tests \
tests/gpos_chaining1_successive.tests \
tests/gpos_chaining2_boundary.tests \
tests/gpos_chaining2_lookupflag.tests \
tests/gpos_chaining2_multiple_subrules.tests \
tests/gpos_chaining2_next_glyph.tests \
tests/gpos_chaining2_simple.tests \
tests/gpos_chaining2_successive.tests \
tests/gpos_chaining3_boundary.tests \
tests/gpos_chaining3_lookupflag.tests \
tests/gpos_chaining3_next_glyph.tests \
tests/gpos_chaining3_simple.tests \
tests/gpos_chaining3_successive.tests \
tests/gpos_context1_boundary.tests \
tests/gpos_context1_expansion.tests \
tests/gpos_context1_lookupflag.tests \
tests/gpos_context1_multiple_subrules.tests \
tests/gpos_context1_next_glyph.tests \
tests/gpos_context1_simple.tests \
tests/gpos_context1_successive.tests \
tests/gpos_context2_boundary.tests \
tests/gpos_context2_classes.tests \
tests/gpos_context2_expansion.tests \
tests/gpos_context2_lookupflag.tests \
tests/gpos_context2_multiple_subrules.tests \
tests/gpos_context2_next_glyph.tests \
tests/gpos_context2_simple.tests \
tests/gpos_context2_successive.tests \
tests/gpos_context3_boundary.tests \
tests/gpos_context3_lookupflag.tests \
tests/gpos_context3_next_glyph.tests \
tests/gpos_context3_simple.tests \
tests/gpos_context3_successive.tests \
tests/gpos1_1_lookupflag.tests \
tests/gpos1_1_simple.tests \
tests/gpos1_2_lookupflag.tests \
tests/gpos1_2.tests \
tests/gpos2_1_lookupflag.tests \
tests/gpos2_1_next_glyph.tests \
tests/gpos2_1_simple.tests \
tests/gpos2_1.tests \
tests/gpos2_2.tests \
tests/gpos3_lookupflag.tests \
tests/gpos3.tests \
tests/gpos4_lookupflag.tests \
tests/gpos4_multiple_anchors.tests \
tests/gpos4_simple.tests \
tests/gpos5.tests \
tests/gpos6.tests \
tests/gpos7_1.tests \
tests/gpos9.tests \
tests/gsub_chaining1_boundary.tests \
tests/gsub_chaining1_lookupflag.tests \
tests/gsub_chaining1_multiple_subrules.tests \
tests/gsub_chaining1_next_glyph.tests \
tests/gsub_chaining1_simple.tests \
tests/gsub_chaining1_successive.tests \
tests/gsub_chaining2_boundary.tests \
tests/gsub_chaining2_lookupflag.tests \
tests/gsub_chaining2_multiple_subrules.tests \
tests/gsub_chaining2_next_glyph.tests \
tests/gsub_chaining2_simple.tests \
tests/gsub_chaining2_successive.tests \
tests/gsub_chaining3_boundary.tests \
tests/gsub_chaining3_lookupflag.tests \
tests/gsub_chaining3_next_glyph.tests \
tests/gsub_chaining3_simple.tests \
tests/gsub_chaining3_successive.tests \
tests/gsub_context1_boundary.tests \
tests/gsub_context1_expansion.tests \
tests/gsub_context1_lookupflag.tests \
tests/gsub_context1_multiple_subrules.tests \
tests/gsub_context1_next_glyph.tests \
tests/gsub_context1_simple.tests \
tests/gsub_context1_successive.tests \
tests/gsub_context2_boundary.tests \
tests/gsub_context2_classes.tests \
tests/gsub_context2_expansion.tests \
tests/gsub_context2_lookupflag.tests \
tests/gsub_context2_multiple_subrules.tests \
tests/gsub_context2_next_glyph.tests \
tests/gsub_context2_simple.tests \
tests/gsub_context2_successive.tests \
tests/gsub_context3_boundary.tests \
tests/gsub_context3_lookupflag.tests \
tests/gsub_context3_next_glyph.tests \
tests/gsub_context3_simple.tests \
tests/gsub_context3_successive.tests \
tests/gsub1_1_lookupflag.tests \
tests/gsub1_1_modulo.tests \
tests/gsub1_1_simple.tests \
tests/gsub1_2_lookupflag.tests \
tests/gsub1_2_simple.tests \
tests/gsub2_1_lookupflag.tests \
tests/gsub2_1_multiple_sequences.tests \
tests/gsub2_1_simple.tests \
tests/gsub3_1_lookupflag.tests \
tests/gsub3_1_multiple.tests \
tests/gsub3_1_simple.tests \
tests/gsub4_1_lookupflag.tests \
tests/gsub4_1_multiple_ligatures.tests \
tests/gsub4_1_multiple_ligsets.tests \
tests/gsub4_1_simple.tests \
tests/gsub7.tests \
tests/lookupflag_ignore_attach.tests \
tests/lookupflag_ignore_base.tests \
tests/lookupflag_ignore_combination.tests \
tests/lookupflag_ignore_ligatures.tests \
tests/lookupflag_ignore_marks.tests \
$(NULL)
DISABLED_TESTS = \
$(NULL)

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

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