Merge harfbuzz-ng
diff --git a/src/COPYING b/src/COPYING
new file mode 100644
index 0000000..820a9e6
--- /dev/null
+++ b/src/COPYING
@@ -0,0 +1,24 @@
+HarfBuzz was previously licensed under different licenses. This was
+changed in January 2008. If you need to relicense your old copies,
+consult the announcement of the license change on the internet.
+Other than that, each copy of HarfBuzz is licensed under the COPYING
+file included with it. The actual license follows:
+
+
+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.
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..40644c1
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,68 @@
+## Process this file with automake to produce Makefile.in
+
+INCLUDES = \
+ -I $(srcdir) \
+ $(FREETYPE_CFLAGS) \
+ $(GLIB_CFLAGS)
+CXX = gcc $(GCCOPTS) -fno-rtti -fno-exceptions -Wabi -Wpadded -Wcast-align
+
+noinst_LTLIBRARIES = libharfbuzz-1.la
+
+MAINSOURCES = \
+ $(INCLUDEDSOURCES) \
+ hb-ot-layout.cc
+# harfbuzz.c
+
+# included from harfbuzz.c
+INCLUDEDSOURCES = \
+ harfbuzz-buffer.c \
+ harfbuzz-gpos.c \
+ harfbuzz-gsub.c \
+ harfbuzz-impl.c \
+ harfbuzz-open.c \
+ harfbuzz-stream.c
+
+PUBLICHEADERS = \
+ harfbuzz.h \
+ harfbuzz-global.h \
+ harfbuzz-buffer.h \
+ harfbuzz-gpos.h \
+ harfbuzz-gsub.h \
+ harfbuzz-open.h
+
+PRIVATEHEADERS = \
+ harfbuzz-impl.h \
+ harfbuzz-buffer-private.h \
+ harfbuzz-gpos-private.h \
+ harfbuzz-gsub-private.h \
+ harfbuzz-open-private.h \
+ harfbuzz-stream-private.h
+
+libharfbuzz_1_la_SOURCES = \
+ $(MAINSOURCES) \
+ $(PUBLICHEADERS) \
+ $(PRIVATEHEADERS)
+
+libharfbuzz_1_la_LIBADD = \
+ $(FREETYPE_LIBS)
+
+noinst_PROGRAMS = harfbuzz-dump main
+
+harfbuzz_dump_SOURCES = \
+ harfbuzz-dump.c \
+ harfbuzz-dump.h \
+ harfbuzz-dump-main.c
+
+harfbuzz_dump_LDADD = \
+ $(libharfbuzz_1_la_LIBADD) \
+ libharfbuzz-1.la
+
+main_LDADD = \
+ $(GLIB_LIBS)
+
+EXTRA_DIST = \
+ README \
+ COPYING \
+ $(INCLUDEDSOURCES)
+
+-include $(top_srcdir)/git.mk
diff --git a/src/README b/src/README
new file mode 100644
index 0000000..f7746ef
--- /dev/null
+++ b/src/README
@@ -0,0 +1,18 @@
+This is HarfBuzz, an OpenType Layout engine.
+
+It was derived originally from the OpenType code in FreeType-1.x, ported to
+FreeType2. (This code has been abandoned for FreeType2, but until something
+better comes along, should serve our purposes.) In addition to porting to
+FreeType-2, it has been modified in various other ways.
+
+It also includes a partial XML dumper for OpenType Layout tables useful for
+figuring out what is going on. Please extend to cover additional parts of the
+tables as you encounter fonts using them. The dumper is written by Owen Taylor.
+
+Bug reports on these files should be sent to the HarfBuzz mailing list as
+listed on http://freedesktop.org/wiki/Software/harfbuzz
+
+For license information, see the file COPYING.
+
+Behdad Esfahbod
+April 1st, 2006
diff --git a/src/harfbuzz-buffer-private.h b/src/harfbuzz-buffer-private.h
new file mode 100644
index 0000000..02ae336
--- /dev/null
+++ b/src/harfbuzz-buffer-private.h
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 1998-2004 David Turner and Werner Lemberg
+ * Copyright (C) 2004,2007 Red Hat, Inc.
+ *
+ * This is part of HarfBuzz, an OpenType Layout engine library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Red Hat Author(s): Owen Taylor, Behdad Esfahbod
+ */
+
+#ifndef HARFBUZZ_BUFFER_PRIVATE_H
+#define HARFBUZZ_BUFFER_PRIVATE_H
+
+#include "harfbuzz-impl.h"
+#include "harfbuzz-buffer.h"
+
+HB_BEGIN_HEADER
+
+#define HB_GLYPH_PROPERTIES_UNKNOWN 0xFFFF
+
+HB_INTERNAL void
+_hb_buffer_swap( HB_Buffer buffer );
+
+HB_INTERNAL void
+_hb_buffer_clear_output( HB_Buffer buffer );
+
+HB_INTERNAL HB_Error
+_hb_buffer_clear_positions( HB_Buffer buffer );
+
+HB_INTERNAL HB_Error
+_hb_buffer_add_output_glyphs( HB_Buffer buffer,
+ HB_UShort num_in,
+ HB_UShort num_out,
+ HB_UShort *glyph_data,
+ HB_UShort component,
+ HB_UShort ligID );
+
+HB_INTERNAL HB_Error
+_hb_buffer_add_output_glyph ( HB_Buffer buffer,
+ HB_UInt glyph_index,
+ HB_UShort component,
+ HB_UShort ligID );
+
+HB_INTERNAL HB_Error
+_hb_buffer_copy_output_glyph ( HB_Buffer buffer );
+
+HB_INTERNAL HB_Error
+_hb_buffer_replace_output_glyph ( HB_Buffer buffer,
+ HB_UInt glyph_index,
+ HB_Bool inplace );
+
+HB_INTERNAL HB_UShort
+_hb_buffer_allocate_ligid( HB_Buffer buffer );
+
+
+/* convenience macros */
+
+#define IN_GLYPH( pos ) (buffer->in_string[(pos)].gindex)
+#define IN_ITEM( pos ) (&buffer->in_string[(pos)])
+#define IN_CURGLYPH() (buffer->in_string[buffer->in_pos].gindex)
+#define IN_CURITEM() (&buffer->in_string[buffer->in_pos])
+#define IN_PROPERTIES( pos ) (buffer->in_string[(pos)].properties)
+#define IN_LIGID( pos ) (buffer->in_string[(pos)].ligID)
+#define IN_COMPONENT( pos ) (buffer->in_string[(pos)].component)
+#define POSITION( pos ) (&buffer->positions[(pos)])
+#define OUT_GLYPH( pos ) (buffer->out_string[(pos)].gindex)
+#define OUT_ITEM( pos ) (&buffer->out_string[(pos)])
+
+#define CHECK_Property( layout, index, flags, property ) \
+ (error = _hb_ot_layout_check_glyph_properties((layout), (index), (flags), (property)) \
+ ? HB_Err_Ok : HB_Err_Not_Covered)
+
+#define ADD_String( buffer, num_in, num_out, glyph_data, component, ligID ) \
+ ( ( error = _hb_buffer_add_output_glyphs( (buffer), \
+ (num_in), (num_out), \
+ (glyph_data), (component), (ligID) \
+ ) ) != HB_Err_Ok )
+#define ADD_Glyph( buffer, glyph_index, component, ligID ) \
+ ( ( error = _hb_buffer_add_output_glyph( (buffer), \
+ (glyph_index), (component), (ligID) \
+ ) ) != HB_Err_Ok )
+#define REPLACE_Glyph( buffer, glyph_index, nesting_level ) \
+ ( ( error = _hb_buffer_replace_output_glyph( (buffer), (glyph_index), \
+ (nesting_level) == 1 ) ) != HB_Err_Ok )
+#define COPY_Glyph( buffer ) \
+ ( (error = _hb_buffer_copy_output_glyph ( buffer ) ) != HB_Err_Ok )
+
+HB_END_HEADER
+
+#endif /* HARFBUZZ_BUFFER_PRIVATE_H */
diff --git a/src/harfbuzz-buffer.c b/src/harfbuzz-buffer.c
new file mode 100644
index 0000000..568fbd8
--- /dev/null
+++ b/src/harfbuzz-buffer.c
@@ -0,0 +1,383 @@
+/*
+ * Copyright (C) 1998-2004 David Turner and Werner Lemberg
+ * Copyright (C) 2004,2007 Red Hat, Inc.
+ *
+ * This is part of HarfBuzz, an OpenType Layout engine library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Red Hat Author(s): Owen Taylor, Behdad Esfahbod
+ */
+
+#include "harfbuzz-impl.h"
+#include "harfbuzz-buffer-private.h"
+#include "harfbuzz-gsub-private.h"
+#include "harfbuzz-gpos-private.h"
+
+/* Here is how the buffer works internally:
+ *
+ * There are two string pointers: in_string and out_string. They
+ * always have same allocated size, but different length and positions.
+ *
+ * As an optimization, both in_string and out_string may point to the
+ * same piece of memory, which is owned by in_string. This remains the
+ * case as long as:
+ *
+ * - copy_glyph() is called
+ * - replace_glyph() is called with inplace=TRUE
+ * - add_output_glyph() and add_output_glyphs() are not called
+ *
+ * In that case swap(), and copy_glyph(), and replace_glyph() are all
+ * mostly no-op.
+ *
+ * As soon an add_output_glyph[s]() or replace_glyph() with inplace=FALSE is
+ * called, out_string is moved over to an alternate buffer (alt_string), and
+ * its current contents (out_length entries) are copied to the alt buffer.
+ * This should all remain transparent to the user. swap() then switches
+ * in_string and alt_string. alt_string is not allocated until its needed,
+ * but after that it's grown with in_string unconditionally.
+ *
+ * The buffer->separate_out boolean keeps status of whether out_string points
+ * to in_string (FALSE) or alt_string (TRUE).
+ */
+
+/* Internal API */
+
+static HB_Error
+hb_buffer_ensure( HB_Buffer buffer,
+ HB_UInt size )
+{
+ HB_UInt new_allocated = buffer->allocated;
+
+ if (size > new_allocated)
+ {
+ HB_Error error;
+
+ while (size > new_allocated)
+ new_allocated += (new_allocated >> 1) + 8;
+
+ if ( buffer->positions )
+ {
+ if ( REALLOC_ARRAY( buffer->positions, new_allocated, HB_PositionRec ) )
+ return error;
+ }
+
+ if ( REALLOC_ARRAY( buffer->in_string, new_allocated, HB_GlyphItemRec ) )
+ return error;
+
+ if ( buffer->separate_out )
+ {
+ if ( REALLOC_ARRAY( buffer->alt_string, new_allocated, HB_GlyphItemRec ) )
+ return error;
+
+ buffer->out_string = buffer->alt_string;
+ }
+ else
+ {
+ buffer->out_string = buffer->in_string;
+
+ if ( buffer->alt_string )
+ {
+ if ( REALLOC_ARRAY( buffer->alt_string, new_allocated, HB_GlyphItemRec ) )
+ return error;
+ }
+ }
+
+ buffer->allocated = new_allocated;
+ }
+
+ return HB_Err_Ok;
+}
+
+static HB_Error
+hb_buffer_duplicate_out_buffer( HB_Buffer buffer )
+{
+ if ( !buffer->alt_string )
+ {
+ HB_Error error;
+
+ if ( ALLOC_ARRAY( buffer->alt_string, buffer->allocated, HB_GlyphItemRec ) )
+ return error;
+ }
+
+ buffer->out_string = buffer->alt_string;
+ memcpy( buffer->out_string, buffer->in_string, buffer->out_length * sizeof (buffer->out_string[0]) );
+ buffer->separate_out = TRUE;
+
+ return HB_Err_Ok;
+}
+
+/* Public API */
+
+HB_Error
+hb_buffer_new( HB_Buffer *pbuffer )
+{
+ HB_Buffer buffer;
+ HB_Error error;
+
+ if ( ALLOC( buffer, sizeof( HB_BufferRec ) ) )
+ return error;
+
+ buffer->allocated = 0;
+ buffer->in_string = NULL;
+ buffer->alt_string = NULL;
+ buffer->positions = NULL;
+
+ hb_buffer_clear( buffer );
+
+ *pbuffer = buffer;
+
+ return HB_Err_Ok;
+}
+
+void
+hb_buffer_free( HB_Buffer buffer )
+{
+ FREE( buffer->in_string );
+ FREE( buffer->alt_string );
+ buffer->out_string = NULL;
+ FREE( buffer->positions );
+ FREE( buffer );
+}
+
+void
+hb_buffer_clear( HB_Buffer buffer )
+{
+ buffer->in_length = 0;
+ buffer->out_length = 0;
+ buffer->in_pos = 0;
+ buffer->out_pos = 0;
+ buffer->out_string = buffer->in_string;
+ buffer->separate_out = FALSE;
+ buffer->max_ligID = 0;
+}
+
+HB_Error
+hb_buffer_add_glyph( HB_Buffer buffer,
+ HB_UInt glyph_index,
+ HB_UInt properties,
+ HB_UInt cluster )
+{
+ HB_Error error;
+ HB_GlyphItem glyph;
+
+ error = hb_buffer_ensure( buffer, buffer->in_length + 1 );
+ if ( error )
+ return error;
+
+ glyph = &buffer->in_string[buffer->in_length];
+ glyph->gindex = glyph_index;
+ glyph->properties = properties;
+ glyph->cluster = cluster;
+ glyph->component = 0;
+ glyph->ligID = 0;
+ glyph->gproperties = HB_GLYPH_PROPERTIES_UNKNOWN;
+
+ buffer->in_length++;
+
+ return HB_Err_Ok;
+}
+
+/* HarfBuzz-Internal API */
+
+HB_INTERNAL void
+_hb_buffer_clear_output( HB_Buffer buffer )
+{
+ buffer->out_length = 0;
+ buffer->out_pos = 0;
+ buffer->out_string = buffer->in_string;
+ buffer->separate_out = FALSE;
+}
+
+HB_INTERNAL HB_Error
+_hb_buffer_clear_positions( HB_Buffer buffer )
+{
+ if ( !buffer->positions )
+ {
+ HB_Error error;
+
+ if ( ALLOC_ARRAY( buffer->positions, buffer->allocated, HB_PositionRec ) )
+ return error;
+ }
+
+ memset (buffer->positions, 0, sizeof (buffer->positions[0]) * buffer->in_length);
+
+ return HB_Err_Ok;
+}
+
+HB_INTERNAL void
+_hb_buffer_swap( HB_Buffer buffer )
+{
+ HB_GlyphItem tmp_string;
+ int tmp_length;
+ int tmp_pos;
+
+ if ( buffer->separate_out )
+ {
+ tmp_string = buffer->in_string;
+ buffer->in_string = buffer->out_string;
+ buffer->out_string = tmp_string;
+ buffer->alt_string = buffer->out_string;
+ }
+
+ tmp_length = buffer->in_length;
+ buffer->in_length = buffer->out_length;
+ buffer->out_length = tmp_length;
+
+ tmp_pos = buffer->in_pos;
+ buffer->in_pos = buffer->out_pos;
+ buffer->out_pos = tmp_pos;
+}
+
+/* The following function copies `num_out' elements from `glyph_data'
+ to `buffer->out_string', advancing the in array pointer in the structure
+ by `num_in' elements, and the out array pointer by `num_out' elements.
+ Finally, it sets the `length' field of `out' equal to
+ `pos' of the `out' structure.
+
+ If `component' is 0xFFFF, the component value from buffer->in_pos
+ will copied `num_out' times, otherwise `component' itself will
+ be used to fill the `component' fields.
+
+ If `ligID' is 0xFFFF, the ligID value from buffer->in_pos
+ will copied `num_out' times, otherwise `ligID' itself will
+ be used to fill the `ligID' fields.
+
+ The properties for all replacement glyphs are taken
+ from the glyph at position `buffer->in_pos'.
+
+ The cluster value for the glyph at position buffer->in_pos is used
+ for all replacement glyphs */
+HB_INTERNAL HB_Error
+_hb_buffer_add_output_glyphs( HB_Buffer buffer,
+ HB_UShort num_in,
+ HB_UShort num_out,
+ HB_UShort *glyph_data,
+ HB_UShort component,
+ HB_UShort ligID )
+{
+ HB_Error error;
+ HB_UShort i;
+ HB_UInt properties;
+ HB_UInt cluster;
+
+ error = hb_buffer_ensure( buffer, buffer->out_pos + num_out );
+ if ( error )
+ return error;
+
+ if ( !buffer->separate_out )
+ {
+ error = hb_buffer_duplicate_out_buffer( buffer );
+ if ( error )
+ return error;
+ }
+
+ properties = buffer->in_string[buffer->in_pos].properties;
+ cluster = buffer->in_string[buffer->in_pos].cluster;
+ if ( component == 0xFFFF )
+ component = buffer->in_string[buffer->in_pos].component;
+ if ( ligID == 0xFFFF )
+ ligID = buffer->in_string[buffer->in_pos].ligID;
+
+ for ( i = 0; i < num_out; i++ )
+ {
+ HB_GlyphItem item = &buffer->out_string[buffer->out_pos + i];
+
+ item->gindex = glyph_data[i];
+ item->properties = properties;
+ item->cluster = cluster;
+ item->component = component;
+ item->ligID = ligID;
+ item->gproperties = HB_GLYPH_PROPERTIES_UNKNOWN;
+ }
+
+ buffer->in_pos += num_in;
+ buffer->out_pos += num_out;
+
+ buffer->out_length = buffer->out_pos;
+
+ return HB_Err_Ok;
+}
+
+HB_INTERNAL HB_Error
+_hb_buffer_add_output_glyph( HB_Buffer buffer,
+ HB_UInt glyph_index,
+ HB_UShort component,
+ HB_UShort ligID )
+{
+ HB_UShort glyph_data = glyph_index;
+
+ return _hb_buffer_add_output_glyphs ( buffer, 1, 1,
+ &glyph_data, component, ligID );
+}
+
+HB_INTERNAL HB_Error
+_hb_buffer_copy_output_glyph ( HB_Buffer buffer )
+{
+ HB_Error error;
+
+ error = hb_buffer_ensure( buffer, buffer->out_pos + 1 );
+ if ( error )
+ return error;
+
+ if ( buffer->separate_out )
+ {
+ buffer->out_string[buffer->out_pos] = buffer->in_string[buffer->in_pos];
+ }
+
+ buffer->in_pos++;
+ buffer->out_pos++;
+ buffer->out_length = buffer->out_pos;
+
+ return HB_Err_Ok;
+}
+
+HB_INTERNAL HB_Error
+_hb_buffer_replace_output_glyph( HB_Buffer buffer,
+ HB_UInt glyph_index,
+ HB_Bool inplace )
+{
+
+ HB_Error error;
+
+ if ( inplace )
+ {
+ error = _hb_buffer_copy_output_glyph ( buffer );
+ if ( error )
+ return error;
+
+ buffer->out_string[buffer->out_pos-1].gindex = glyph_index;
+ }
+ else
+ {
+ return _hb_buffer_add_output_glyph( buffer, glyph_index, 0xFFFF, 0xFFFF );
+ }
+
+ return HB_Err_Ok;
+}
+
+HB_INTERNAL HB_UShort
+_hb_buffer_allocate_ligid( HB_Buffer buffer )
+{
+ buffer->max_ligID++;
+ if (HB_UNLIKELY (buffer->max_ligID == 0))
+ buffer->max_ligID++;
+
+ return buffer->max_ligID;
+}
diff --git a/src/harfbuzz-buffer.h b/src/harfbuzz-buffer.h
new file mode 100644
index 0000000..b134407
--- /dev/null
+++ b/src/harfbuzz-buffer.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 1998-2004 David Turner and Werner Lemberg
+ * Copyright (C) 2004,2007 Red Hat, Inc.
+ *
+ * This is part of HarfBuzz, an OpenType Layout engine library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Red Hat Author(s): Owen Taylor, Behdad Esfahbod
+ */
+
+#ifndef HARFBUZZ_BUFFER_H
+#define HARFBUZZ_BUFFER_H
+
+#include "harfbuzz-global.h"
+
+HB_BEGIN_HEADER
+
+typedef struct HB_GlyphItemRec_ {
+ HB_UInt gindex;
+ HB_UInt properties;
+ HB_UInt cluster;
+ HB_UShort component;
+ HB_UShort ligID;
+ HB_UShort gproperties;
+} HB_GlyphItemRec, *HB_GlyphItem;
+
+typedef struct HB_PositionRec_ {
+ HB_Fixed x_pos;
+ HB_Fixed y_pos;
+ HB_Fixed x_advance;
+ HB_Fixed y_advance;
+ HB_UShort back; /* number of glyphs to go back
+ for drawing current glyph */
+ HB_Bool new_advance; /* if set, the advance width values are
+ absolute, i.e., they won't be
+ added to the original glyph's value
+ but rather replace them. */
+ HB_Short cursive_chain; /* character to which this connects,
+ may be positive or negative; used
+ only internally */
+} HB_PositionRec, *HB_Position;
+
+
+typedef struct HB_BufferRec_{
+ HB_UInt allocated;
+
+ HB_UInt in_length;
+ HB_UInt out_length;
+ HB_UInt in_pos;
+ HB_UInt out_pos;
+
+ HB_Bool separate_out;
+ HB_GlyphItem in_string;
+ HB_GlyphItem out_string;
+ HB_GlyphItem alt_string;
+ HB_Position positions;
+ HB_UShort max_ligID;
+} HB_BufferRec, *HB_Buffer;
+
+HB_Error
+hb_buffer_new( HB_Buffer *buffer );
+
+void
+hb_buffer_free( HB_Buffer buffer );
+
+void
+hb_buffer_clear( HB_Buffer buffer );
+
+HB_Error
+hb_buffer_add_glyph( HB_Buffer buffer,
+ HB_UInt glyph_index,
+ HB_UInt properties,
+ HB_UInt cluster );
+
+HB_END_HEADER
+
+#endif /* HARFBUZZ_BUFFER_H */
diff --git a/src/harfbuzz-dump-main.c b/src/harfbuzz-dump-main.c
new file mode 100644
index 0000000..dfb35fb
--- /dev/null
+++ b/src/harfbuzz-dump-main.c
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2000 Red Hat, Inc.
+ *
+ * This is part of HarfBuzz, an OpenType Layout engine library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Red Hat Author(s): Owen Taylor
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "harfbuzz.h"
+#include "harfbuzz-dump.h"
+
+#define N_ELEMENTS(arr) (sizeof(arr)/ sizeof((arr)[0]))
+
+static int
+croak (const char *situation, HB_Error error)
+{
+ fprintf (stderr, "%s: Error %d\n", situation, error);
+
+ exit (1);
+}
+
+int
+main (int argc, char **argv)
+{
+ HB_Error error;
+ FT_Library library;
+ HB_Font font;
+ HB_GSUB gsub;
+ HB_GPOS gpos;
+
+ if (argc != 2)
+ {
+ fprintf (stderr, "Usage: harfbuzz-dump MYFONT.TTF\n");
+ exit(1);
+ }
+
+ if ((error = FT_Init_FreeType (&library)))
+ croak ("FT_Init_FreeType", error);
+
+ if ((error = FT_New_Face (library, argv[1], 0, &font)))
+ croak ("FT_New_Face", error);
+
+ printf ("<?xml version=\"1.0\"?>\n");
+ printf ("<OpenType>\n");
+
+ if (!(error = HB_Load_GSUB_Table (font, &gsub, NULL)))
+ {
+ HB_Dump_GSUB_Table (gsub, stdout);
+
+ if ((error = HB_Done_GSUB_Table (gsub)))
+ croak ("HB_Done_GSUB_Table", error);
+ }
+ else if (error != HB_Err_Not_Covered)
+ fprintf (stderr, "HB_Load_GSUB_Table: error 0x%x\n", error);
+
+ if (!(error = HB_Load_GPOS_Table (font, &gpos, NULL)))
+ {
+ HB_Dump_GPOS_Table (gpos, stdout);
+
+ if ((error = HB_Done_GPOS_Table (gpos)))
+ croak ("HB_Done_GPOS_Table", error);
+ }
+ else if (error != HB_Err_Not_Covered)
+ fprintf (stderr, "HB_Load_GPOS_Table: error 0x%x\n", error);
+
+ printf ("</OpenType>\n");
+
+ if ((error = FT_Done_Face (font)))
+ croak ("FT_Done_Face", error);
+
+ if ((error = FT_Done_FreeType (library)))
+ croak ("FT_Done_FreeType", error);
+
+ return 0;
+}
+
diff --git a/src/harfbuzz-dump.c b/src/harfbuzz-dump.c
new file mode 100644
index 0000000..8611664
--- /dev/null
+++ b/src/harfbuzz-dump.c
@@ -0,0 +1,768 @@
+/*
+ * Copyright (C) 2000, 2007 Red Hat, Inc.
+ *
+ * This is part of HarfBuzz, an OpenType Layout engine library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Red Hat Author(s): Owen Taylor, Behdad Esfahbod
+ */
+
+#include "harfbuzz-impl.h"
+#include "harfbuzz-dump.h"
+#include "harfbuzz-gdef-private.h"
+#include "harfbuzz-gsub-private.h"
+#include "harfbuzz-gpos-private.h"
+#include "harfbuzz-open-private.h"
+#include <stdarg.h>
+
+#define DUMP(format) dump (stream, indent, format)
+#define DUMP1(format, arg1) dump (stream, indent, format, arg1)
+#define DUMP2(format, arg1, arg2) dump (stream, indent, format, arg1, arg2)
+#define DUMP3(format, arg1, arg2, arg3) dump (stream, indent, format, arg1, arg2, arg3)
+
+#define DUMP_FINT(strct,fld) dump (stream, indent, "<" #fld ">%d</" #fld ">\n", (strct)->fld)
+#define DUMP_FUINT(strct,fld) dump (stream, indent, "<" #fld ">%u</" #fld ">\n", (strct)->fld)
+#define DUMP_FGLYPH(strct,fld) dump (stream, indent, "<" #fld ">%#06x</" #fld ">\n", (strct)->fld)
+#define DUMP_FGLYPH(strct,fld) dump (stream, indent, "<" #fld ">%#06x</" #fld ">\n", (strct)->fld)
+#define DUMP_USHORT_ARRAY(strct,fld,cnt) Dump_UShort_Array ((strct)->fld, cnt, #fld, stream, indent);
+
+#define DEF_DUMP(type) static void Dump_ ## type (HB_ ## type *type, FILE *stream, int indent, HB_Type hb_type)
+#define RECURSE(name, type, val) do { DUMP ("<" #name ">\n"); Dump_ ## type (val, stream, indent + 1, hb_type); DUMP ("</" #name ">\n"); } while (0)
+#define RECURSE_NUM(name, i, type, val) do { DUMP1 ("<" #name "> <!-- %d -->\n", i); Dump_ ## type (val, stream, indent + 1, hb_type); DUMP ("</" #name ">\n"); } while (0)
+#define DUMP_VALUE_RECORD(val, frmt) do { DUMP ("<ValueRecord>\n"); Dump_ValueRecord (val, stream, indent + 1, hb_type, frmt); DUMP ("</ValueRecord>\n"); } while (0)
+
+static void
+do_indent (FILE *stream, int indent)
+{
+ fprintf (stream, "%*s", indent * 3, "");
+}
+
+#if __GNUC__ >= 3
+__attribute__((__format__(__printf__, 3, 4)))
+#endif
+static void
+dump (FILE *stream, int indent, const char *format, ...)
+{
+ va_list list;
+
+ do_indent (stream, indent);
+
+ va_start (list, format);
+ vfprintf (stream, format, list);
+ va_end (list);
+}
+
+static void
+Dump_UShort_Array (HB_UShort *array, int count, const char *name, FILE *stream, int indent)
+{
+ int i;
+
+ do_indent (stream, indent);
+
+ fprintf (stream, "<%s>", name);
+ for (i = 0; i < count; i++)
+ fprintf (stream, "%d%s", array[i], i == 0 ? "" : " ");
+ fprintf (stream, "</%s>\n", name);
+}
+
+static void
+Print_Tag (HB_UInt tag, FILE *stream)
+{
+ fprintf (stream, "%c%c%c%c",
+ (unsigned char)(tag >> 24),
+ (unsigned char)((tag >> 16) & 0xff),
+ (unsigned char)((tag >> 8) & 0xff),
+ (unsigned char)(tag & 0xff));
+}
+
+DEF_DUMP (LangSys)
+{
+ int i;
+
+ HB_UNUSED(hb_type);
+
+ DUMP_FUINT (LangSys, LookupOrderOffset);
+ DUMP_FUINT (LangSys, ReqFeatureIndex);
+ DUMP_FUINT (LangSys, FeatureCount);
+
+ for (i=0; i < LangSys->FeatureCount; i++)
+ DUMP1("<FeatureIndex>%d</FeatureIndex>\n", LangSys->FeatureIndex[i]);
+}
+
+DEF_DUMP (ScriptTable)
+{
+ int i;
+
+ RECURSE (DefaultLangSys, LangSys, &ScriptTable->DefaultLangSys);
+
+ DUMP_FUINT (ScriptTable, LangSysCount);
+
+ for (i=0; i < ScriptTable->LangSysCount; i++)
+ {
+ do_indent (stream, indent);
+ fprintf (stream, "<LangSysTag>");
+ Print_Tag (ScriptTable->LangSysRecord[i].LangSysTag, stream);
+ fprintf (stream, "</LangSysTag>\n");
+ RECURSE_NUM (LangSys, i, LangSys, &ScriptTable->LangSysRecord[i].LangSys);
+ }
+}
+
+DEF_DUMP (ScriptList)
+{
+ int i;
+
+ DUMP_FUINT (ScriptList, ScriptCount);
+
+ for (i=0; i < ScriptList->ScriptCount; i++)
+ {
+ do_indent (stream, indent);
+ fprintf (stream, "<ScriptTag>");
+ Print_Tag (ScriptList->ScriptRecord[i].ScriptTag, stream);
+ fprintf (stream, "</ScriptTag>\n");
+ RECURSE_NUM (Script, i, ScriptTable, &ScriptList->ScriptRecord[i].Script);
+ }
+}
+
+DEF_DUMP (Feature)
+{
+ int i;
+
+ HB_UNUSED(hb_type);
+
+ DUMP_FUINT (Feature, FeatureParams);
+ DUMP_FUINT (Feature, LookupListCount);
+
+ for (i=0; i < Feature->LookupListCount; i++)
+ DUMP1("<LookupIndex>%d</LookupIndex>\n", Feature->LookupListIndex[i]);
+}
+
+DEF_DUMP (MarkRecord)
+{
+ HB_UNUSED(hb_type);
+
+ DUMP_FUINT (MarkRecord, Class);
+ DUMP1("<Anchor>%d</Anchor>\n", MarkRecord->MarkAnchor.PosFormat );
+}
+
+DEF_DUMP (MarkArray)
+{
+ int i;
+
+ DUMP_FUINT (MarkArray, MarkCount);
+
+ for (i=0; i < MarkArray->MarkCount; i++)
+ RECURSE_NUM (MarkRecord, i, MarkRecord, &MarkArray->MarkRecord[i]);
+}
+
+DEF_DUMP (FeatureList)
+{
+ int i;
+
+ DUMP_FUINT (FeatureList, FeatureCount);
+
+ for (i=0; i < FeatureList->FeatureCount; i++)
+ {
+ do_indent (stream, indent);
+ fprintf (stream, "<FeatureTag>");
+ Print_Tag (FeatureList->FeatureRecord[i].FeatureTag, stream);
+ fprintf (stream, "</FeatureTag> <!-- %d -->\n", i);
+ RECURSE_NUM (Feature, i, Feature, &FeatureList->FeatureRecord[i].Feature);
+ }
+}
+
+DEF_DUMP (Coverage)
+{
+ HB_UNUSED(hb_type);
+
+ DUMP_FUINT (Coverage, CoverageFormat);
+
+ if (Coverage->CoverageFormat == 1)
+ {
+ int i;
+ DUMP_FUINT (&Coverage->cf.cf1, GlyphCount);
+
+ for (i = 0; i < Coverage->cf.cf1.GlyphCount; i++)
+ DUMP2("<Glyph>%#06x</Glyph> <!-- %d -->\n",
+ Coverage->cf.cf1.GlyphArray[i], i);
+ }
+ else
+ {
+ int i;
+ DUMP_FUINT (&Coverage->cf.cf2, RangeCount);
+
+ for ( i = 0; i < Coverage->cf.cf2.RangeCount; i++ )
+ DUMP3("<Glyph>%#06x - %#06x</Glyph> <!-- %d -->\n",
+ Coverage->cf.cf2.RangeRecord[i].Start,
+ Coverage->cf.cf2.RangeRecord[i].End, i);
+ }
+}
+
+DEF_DUMP (ClassRangeRecord)
+{
+ HB_UNUSED(hb_type);
+
+ DUMP_FGLYPH (ClassRangeRecord, Start);
+ DUMP_FGLYPH (ClassRangeRecord, End);
+ DUMP_FUINT (ClassRangeRecord, Class);
+}
+
+DEF_DUMP (ClassDefinition)
+{
+ HB_UNUSED(hb_type);
+
+ DUMP_FUINT( ClassDefinition, ClassFormat);
+ DUMP_FUINT( ClassDefinition, loaded);
+
+ if (ClassDefinition->ClassFormat == 1)
+ {
+ int i;
+ HB_ClassDefFormat1 *ClassDefFormat1 = &ClassDefinition->cd.cd1;
+ DUMP("<ClassDefinition>\n");
+ DUMP_FUINT (ClassDefFormat1, StartGlyph );
+ DUMP_FUINT (ClassDefFormat1, GlyphCount );
+ for (i = 0; i < ClassDefFormat1->GlyphCount; i++)
+ DUMP2(" <Class>%d</Class> <!-- %#06x -->", ClassDefFormat1->ClassValueArray[i],
+ ClassDefFormat1->StartGlyph+i );
+ }
+ else if (ClassDefinition->ClassFormat == 2)
+ {
+ int i;
+ HB_ClassDefFormat2 *ClassDefFormat2 = &ClassDefinition->cd.cd2;
+ DUMP_FUINT (ClassDefFormat2, ClassRangeCount);
+
+ for (i = 0; i < ClassDefFormat2->ClassRangeCount; i++)
+ RECURSE_NUM (ClassRangeRecord, i, ClassRangeRecord, &ClassDefFormat2->ClassRangeRecord[i]);
+ }
+ else
+ fprintf(stderr, "invalid class def table!!!\n");
+}
+
+DEF_DUMP (SubstLookupRecord)
+{
+ HB_UNUSED(hb_type);
+
+ DUMP_FUINT (SubstLookupRecord, SequenceIndex);
+ DUMP_FUINT (SubstLookupRecord, LookupListIndex);
+}
+
+DEF_DUMP (ChainSubClassRule)
+{
+ int i;
+
+ DUMP_USHORT_ARRAY (ChainSubClassRule, Backtrack, ChainSubClassRule->BacktrackGlyphCount);
+ DUMP_USHORT_ARRAY (ChainSubClassRule, Input, ChainSubClassRule->InputGlyphCount - 1);
+ DUMP_USHORT_ARRAY (ChainSubClassRule, Lookahead, ChainSubClassRule->LookaheadGlyphCount);
+
+ for (i = 0; i < ChainSubClassRule->SubstCount; i++)
+ RECURSE_NUM (SubstLookupRecord, i, SubstLookupRecord, &ChainSubClassRule->SubstLookupRecord[i]);
+
+ indent--;
+}
+
+DEF_DUMP (ChainSubClassSet)
+{
+ int i;
+
+ DUMP_FUINT( ChainSubClassSet, ChainSubClassRuleCount );
+ for (i = 0; i < ChainSubClassSet->ChainSubClassRuleCount; i++)
+ RECURSE_NUM (ChainSubClassRule, i, ChainSubClassRule, &ChainSubClassSet->ChainSubClassRule[i]);
+}
+
+static void
+Dump_GSUB_Lookup_Single (HB_SubTable *subtable, FILE *stream, int indent, HB_Type hb_type)
+{
+ HB_SingleSubst *SingleSubst = &subtable->st.gsub.single;
+
+ DUMP_FUINT (SingleSubst, SubstFormat);
+ RECURSE (Coverage, Coverage, &SingleSubst->Coverage);
+
+ if (SingleSubst->SubstFormat == 1)
+ {
+ DUMP_FINT (&SingleSubst->ssf.ssf1, DeltaGlyphID);
+ }
+ else
+ {
+ int i;
+
+ DUMP_FINT (&SingleSubst->ssf.ssf2, GlyphCount);
+ for (i=0; i < SingleSubst->ssf.ssf2.GlyphCount; i++)
+ DUMP2("<Substitute>%#06x</Substitute> <!-- %d -->\n", SingleSubst->ssf.ssf2.Substitute[i], i);
+ }
+}
+
+DEF_DUMP (Ligature)
+{
+ int i;
+
+ HB_UNUSED(hb_type);
+
+ DUMP_FGLYPH (Ligature, LigGlyph);
+ DUMP_FUINT (Ligature, ComponentCount);
+
+ for (i=0; i < Ligature->ComponentCount - 1; i++)
+ DUMP1("<Component>%#06x</Component>\n", Ligature->Component[i]);
+}
+
+DEF_DUMP (LigatureSet)
+{
+ int i;
+
+ DUMP_FUINT (LigatureSet, LigatureCount);
+
+ for (i=0; i < LigatureSet->LigatureCount; i++)
+ RECURSE_NUM (Ligature, i, Ligature, &LigatureSet->Ligature[i]);
+}
+
+static void
+Dump_GSUB_Lookup_Ligature (HB_SubTable *subtable, FILE *stream, int indent, HB_Type hb_type)
+{
+ int i;
+ HB_LigatureSubst *LigatureSubst = &subtable->st.gsub.ligature;
+
+ DUMP_FUINT (LigatureSubst, SubstFormat);
+ RECURSE (Coverage, Coverage, &LigatureSubst->Coverage);
+
+ DUMP_FUINT (LigatureSubst, LigatureSetCount);
+
+ for (i=0; i < LigatureSubst->LigatureSetCount; i++)
+ RECURSE_NUM (LigatureSet, i, LigatureSet, &LigatureSubst->LigatureSet[i]);
+}
+
+DEF_DUMP (ContextSubstFormat1)
+{
+ HB_UNUSED(hb_type);
+ HB_UNUSED(ContextSubstFormat1);
+
+
+ DUMP("<!-- Not implemented!!! -->\n");
+}
+
+DEF_DUMP (ContextSubstFormat2)
+{
+ DUMP_FUINT (ContextSubstFormat2, MaxContextLength);
+ RECURSE (Coverage, Coverage, &ContextSubstFormat2->Coverage);
+ RECURSE (ClassDefinition, ClassDefinition, &ContextSubstFormat2->ClassDef);
+}
+
+DEF_DUMP (ContextSubstFormat3)
+{
+ HB_UNUSED(hb_type);
+ HB_UNUSED(ContextSubstFormat3);
+
+ DUMP("<!-- Not implemented!!! -->\n");
+}
+
+static void
+Dump_GSUB_Lookup_Context (HB_SubTable *subtable, FILE *stream, int indent, HB_Type hb_type)
+{
+ HB_ContextSubst *ContextSubst = &subtable->st.gsub.context;
+
+ DUMP_FUINT (ContextSubst, SubstFormat);
+ switch( ContextSubst->SubstFormat )
+ {
+ case 1:
+ Dump_ContextSubstFormat1 (&ContextSubst->csf.csf1, stream, indent+2, hb_type);
+ break;
+ case 2:
+ Dump_ContextSubstFormat2 (&ContextSubst->csf.csf2, stream, indent+2, hb_type);
+ break;
+ case 3:
+ Dump_ContextSubstFormat3 (&ContextSubst->csf.csf3, stream, indent+2, hb_type);
+ break;
+ default:
+ fprintf(stderr, "invalid subformat!!!!!\n");
+ }
+}
+
+DEF_DUMP (ChainContextSubstFormat1)
+{
+ HB_UNUSED(hb_type);
+ HB_UNUSED(ChainContextSubstFormat1);
+
+ DUMP("<!-- Not implemented!!! -->\n");
+}
+
+DEF_DUMP (ChainContextSubstFormat2)
+{
+ int i;
+
+ RECURSE (Coverage, Coverage, &ChainContextSubstFormat2->Coverage);
+ DUMP_FUINT (ChainContextSubstFormat2, MaxBacktrackLength);
+ RECURSE (ClassDefinition, ClassDefinition, &ChainContextSubstFormat2->BacktrackClassDef);
+ DUMP_FUINT (ChainContextSubstFormat2, MaxInputLength);
+ RECURSE (ClassDefinition, ClassDefinition, &ChainContextSubstFormat2->InputClassDef);
+ DUMP_FUINT (ChainContextSubstFormat2, MaxLookaheadLength);
+ RECURSE (ClassDefinition, ClassDefinition, &ChainContextSubstFormat2->LookaheadClassDef);
+
+ DUMP_FUINT (ChainContextSubstFormat2, ChainSubClassSetCount);
+ for (i = 0; i < ChainContextSubstFormat2->ChainSubClassSetCount; i++)
+ RECURSE (ChainSubClassSet, ChainSubClassSet, &ChainContextSubstFormat2->ChainSubClassSet[i]);
+}
+
+DEF_DUMP (ChainContextSubstFormat3)
+{
+ int i;
+
+ DUMP_FUINT (ChainContextSubstFormat3, BacktrackGlyphCount);
+ for (i = 0; i < ChainContextSubstFormat3->BacktrackGlyphCount; i++)
+ RECURSE (BacktrackCoverage, Coverage, &ChainContextSubstFormat3->BacktrackCoverage[i]);
+ DUMP_FUINT (ChainContextSubstFormat3, InputGlyphCount);
+ for (i = 0; i < ChainContextSubstFormat3->InputGlyphCount; i++)
+ RECURSE (InputCoverage, Coverage, &ChainContextSubstFormat3->InputCoverage[i]);
+ DUMP_FUINT (ChainContextSubstFormat3, LookaheadGlyphCount);
+ for (i = 0; i < ChainContextSubstFormat3->LookaheadGlyphCount; i++)
+ RECURSE (LookaheadCoverage, Coverage, &ChainContextSubstFormat3->LookaheadCoverage[i]);
+
+ for (i = 0; i < ChainContextSubstFormat3->SubstCount; i++)
+ RECURSE_NUM (SubstLookupRecord, i, SubstLookupRecord, &ChainContextSubstFormat3->SubstLookupRecord[i]);
+
+}
+
+static void
+Dump_GSUB_Lookup_Chain (HB_SubTable *subtable, FILE *stream, int indent, HB_Type hb_type)
+{
+ HB_ChainContextSubst *chain = &subtable->st.gsub.chain;
+
+ DUMP_FUINT (chain, SubstFormat);
+ switch (chain->SubstFormat)
+ {
+ case 1:
+ Dump_ChainContextSubstFormat1 (&chain->ccsf.ccsf1, stream, indent+2, hb_type);
+ break;
+ case 2:
+ Dump_ChainContextSubstFormat2 (&chain->ccsf.ccsf2, stream, indent+2, hb_type);
+ break;
+ case 3:
+ Dump_ChainContextSubstFormat3 (&chain->ccsf.ccsf3, stream, indent+2, hb_type);
+ break;
+ default:
+ fprintf(stderr, "invalid subformat!!!!!\n");
+ }
+}
+
+static void
+Dump_Device (HB_Device *Device, FILE *stream, int indent, HB_Type hb_type)
+{
+ int i;
+ int bits;
+ int n_per;
+ unsigned int mask;
+
+ HB_UNUSED(hb_type);
+
+ DUMP_FUINT (Device, StartSize);
+ DUMP_FUINT (Device, EndSize);
+ DUMP_FUINT (Device, DeltaFormat);
+ switch (Device->DeltaFormat)
+ {
+ case 1:
+ bits = 2;
+ break;
+ case 2:
+ bits = 4;
+ break;
+ case 3:
+ bits = 8;
+ break;
+ default:
+ bits = 0;
+ break;
+ }
+
+ DUMP ("<DeltaValue>");
+ if (!bits)
+ {
+
+ fprintf(stderr, "invalid DeltaFormat!!!!!\n");
+ }
+ else
+ {
+ n_per = 16 / bits;
+ mask = (1 << bits) - 1;
+ mask = mask << (16 - bits);
+
+ for (i = Device->StartSize; i <= Device->EndSize ; i++)
+ {
+ HB_UShort val = Device->DeltaValue[i / n_per];
+ HB_Short signed_val = ((val << ((i % n_per) * bits)) & mask);
+ dump (stream, indent, "%d", signed_val >> (16 - bits));
+ if (i != Device->EndSize)
+ DUMP (", ");
+ }
+ }
+ DUMP ("</DeltaValue>\n");
+}
+
+static void
+Dump_ValueRecord (HB_ValueRecord *ValueRecord, FILE *stream, int indent, HB_Type hb_type, HB_UShort value_format)
+{
+ if (value_format & HB_GPOS_FORMAT_HAVE_X_PLACEMENT)
+ DUMP_FINT (ValueRecord, XPlacement);
+ if (value_format & HB_GPOS_FORMAT_HAVE_Y_PLACEMENT)
+ DUMP_FINT (ValueRecord, YPlacement);
+ if (value_format & HB_GPOS_FORMAT_HAVE_X_ADVANCE)
+ DUMP_FINT (ValueRecord, XAdvance);
+ if (value_format & HB_GPOS_FORMAT_HAVE_Y_ADVANCE)
+ DUMP_FINT (ValueRecord, XAdvance);
+ if (value_format & HB_GPOS_FORMAT_HAVE_X_PLACEMENT_DEVICE)
+ RECURSE (Device, Device, &ValueRecord->XPlacementDevice);
+ if (value_format & HB_GPOS_FORMAT_HAVE_Y_PLACEMENT_DEVICE)
+ RECURSE (Device, Device, &ValueRecord->YPlacementDevice);
+ if (value_format & HB_GPOS_FORMAT_HAVE_X_ADVANCE_DEVICE)
+ RECURSE (Device, Device, &ValueRecord->XAdvanceDevice);
+ if (value_format & HB_GPOS_FORMAT_HAVE_Y_ADVANCE_DEVICE)
+ RECURSE (Device, Device, &ValueRecord->YAdvanceDevice);
+ if (value_format & HB_GPOS_FORMAT_HAVE_X_ID_PLACEMENT)
+ DUMP_FUINT (ValueRecord, XIdPlacement);
+ if (value_format & HB_GPOS_FORMAT_HAVE_Y_ID_PLACEMENT)
+ DUMP_FUINT (ValueRecord, YIdPlacement);
+ if (value_format & HB_GPOS_FORMAT_HAVE_X_ID_ADVANCE)
+ DUMP_FUINT (ValueRecord, XIdAdvance);
+ if (value_format & HB_GPOS_FORMAT_HAVE_Y_ID_ADVANCE)
+ DUMP_FUINT (ValueRecord, XIdAdvance);
+}
+
+static void
+Dump_GPOS_Lookup_Single (HB_SubTable *subtable, FILE *stream, int indent, HB_Type hb_type)
+{
+ HB_SinglePos *SinglePos = &subtable->st.gpos.single;
+
+ DUMP_FUINT (SinglePos, PosFormat);
+ RECURSE (Coverage, Coverage, &SinglePos->Coverage);
+
+ DUMP_FUINT (SinglePos, ValueFormat);
+
+ if (SinglePos->PosFormat == 1)
+ {
+ DUMP_VALUE_RECORD (&SinglePos->spf.spf1.Value, SinglePos->ValueFormat);
+ }
+ else
+ {
+ int i;
+
+ DUMP_FUINT (&SinglePos->spf.spf2, ValueCount);
+ for (i = 0; i < SinglePos->spf.spf2.ValueCount; i++)
+ DUMP_VALUE_RECORD (&SinglePos->spf.spf2.Value[i], SinglePos->ValueFormat);
+ }
+}
+
+static void
+Dump_PairValueRecord (HB_PairValueRecord *PairValueRecord, FILE *stream, int indent, HB_Type hb_type, HB_UShort ValueFormat1, HB_UShort ValueFormat2)
+{
+ DUMP_FUINT (PairValueRecord, SecondGlyph);
+ DUMP_VALUE_RECORD (&PairValueRecord->Value1, ValueFormat1);
+ DUMP_VALUE_RECORD (&PairValueRecord->Value2, ValueFormat2);
+}
+
+static void
+Dump_PairSet (HB_PairSet *PairSet, FILE *stream, int indent, HB_Type hb_type, HB_UShort ValueFormat1, HB_UShort ValueFormat2)
+{
+ int i;
+ DUMP_FUINT (PairSet, PairValueCount);
+
+ for (i = 0; i < PairSet->PairValueCount; i++)
+ {
+ DUMP ("<PairValueRecord>\n");
+ Dump_PairValueRecord (&PairSet->PairValueRecord[i], stream, indent + 1, hb_type, ValueFormat1, ValueFormat2);
+ DUMP ("</PairValueRecord>\n");
+ }
+}
+
+static void
+Dump_GPOS_Lookup_Pair (HB_SubTable *subtable, FILE *stream, int indent, HB_Type hb_type)
+{
+ HB_PairPos *PairPos = &subtable->st.gpos.pair;
+
+ DUMP_FUINT (PairPos, PosFormat);
+ RECURSE (Coverage, Coverage, &PairPos->Coverage);
+
+ DUMP_FUINT (PairPos, ValueFormat1);
+ DUMP_FUINT (PairPos, ValueFormat2);
+
+ if (PairPos->PosFormat == 1)
+ {
+ int i;
+
+ DUMP_FUINT (&PairPos->ppf.ppf1, PairSetCount);
+ for (i = 0; i < PairPos->ppf.ppf1.PairSetCount; i++)
+ {
+ DUMP ("<PairSet>\n");
+ Dump_PairSet (&PairPos->ppf.ppf1.PairSet[i], stream, indent + 1, hb_type, PairPos->ValueFormat1, PairPos->ValueFormat2);
+ DUMP ("</PairSet>\n");
+ }
+ }
+ else
+ {
+ }
+}
+
+static void
+Dump_GPOS_Lookup_Markbase (HB_SubTable *subtable, FILE *stream, int indent, HB_Type hb_type)
+{
+ int i;
+ HB_MarkBasePos *markbase = &subtable->st.gpos.markbase;
+
+ DUMP_FUINT (markbase, PosFormat);
+ RECURSE (Coverage, Coverage, &markbase->MarkCoverage);
+ RECURSE (Coverage, Coverage, &markbase->BaseCoverage);
+ DUMP_FUINT (markbase, ClassCount);
+ RECURSE (MarkArray, MarkArray, &markbase->MarkArray);
+
+ DUMP ("<BaseArray>\n");
+ indent++;
+
+ DUMP_FUINT (&markbase->BaseArray, BaseCount);
+ for (i = 0; i < markbase->BaseArray.BaseCount; i++)
+ {
+ int j;
+ HB_BaseRecord *r = &markbase->BaseArray.BaseRecord[i];
+ DUMP1 ("<BaseRecord> <!-- %d -->\n", i);
+ for (j = 0; j < markbase->ClassCount; j++)
+ DUMP1 (" <Anchor>%d</Anchor>\n", r->BaseAnchor->PosFormat);
+ DUMP ("<BaseRecord>\n");
+ }
+
+ indent--;
+ DUMP ("</BaseArray>\n");
+}
+
+DEF_DUMP (Lookup)
+{
+ int i;
+ const char *lookup_name;
+ void (*lookup_func) (HB_SubTable *subtable, FILE *stream, int indent, HB_Type hb_type) = NULL;
+
+ if (hb_type == HB_Type_GSUB)
+ {
+ switch (Lookup->LookupType)
+ {
+ case HB_GSUB_LOOKUP_SINGLE:
+ lookup_name = "SINGLE";
+ lookup_func = Dump_GSUB_Lookup_Single;
+ break;
+ case HB_GSUB_LOOKUP_MULTIPLE:
+ lookup_name = "MULTIPLE";
+ break;
+ case HB_GSUB_LOOKUP_ALTERNATE:
+ lookup_name = "ALTERNATE";
+ break;
+ case HB_GSUB_LOOKUP_LIGATURE:
+ lookup_name = "LIGATURE";
+ lookup_func = Dump_GSUB_Lookup_Ligature;
+ break;
+ case HB_GSUB_LOOKUP_CONTEXT:
+ lookup_name = "CONTEXT";
+ lookup_func = Dump_GSUB_Lookup_Context;
+ break;
+ case HB_GSUB_LOOKUP_CHAIN:
+ lookup_name = "CHAIN";
+ lookup_func = Dump_GSUB_Lookup_Chain;
+ break;
+ default:
+ lookup_name = "(unknown)";
+ lookup_func = NULL;
+ break;
+ }
+ }
+ else
+ {
+ switch (Lookup->LookupType)
+ {
+ case HB_GPOS_LOOKUP_SINGLE:
+ lookup_name = "SINGLE";
+ lookup_func = Dump_GPOS_Lookup_Single;
+ break;
+ case HB_GPOS_LOOKUP_PAIR:
+ lookup_name = "PAIR";
+ lookup_func = Dump_GPOS_Lookup_Pair;
+ break;
+ case HB_GPOS_LOOKUP_CURSIVE:
+ lookup_name = "CURSIVE";
+ break;
+ case HB_GPOS_LOOKUP_MARKBASE:
+ lookup_name = "MARKBASE";
+ lookup_func = Dump_GPOS_Lookup_Markbase;
+ break;
+ case HB_GPOS_LOOKUP_MARKLIG:
+ lookup_name = "MARKLIG";
+ break;
+ case HB_GPOS_LOOKUP_MARKMARK:
+ lookup_name = "MARKMARK";
+ break;
+ case HB_GPOS_LOOKUP_CONTEXT:
+ lookup_name = "CONTEXT";
+ break;
+ case HB_GPOS_LOOKUP_CHAIN:
+ lookup_name = "CHAIN";
+ break;
+ default:
+ lookup_name = "(unknown)";
+ lookup_func = NULL;
+ break;
+ }
+ }
+
+ DUMP2("<LookupType>%s</LookupType> <!-- %d -->\n", lookup_name, Lookup->LookupType);
+ DUMP1("<LookupFlag>%#06x</LookupFlag>\n", Lookup->LookupFlag);
+
+ for (i=0; i < Lookup->SubTableCount; i++)
+ {
+ DUMP ("<Subtable>\n");
+ if (lookup_func)
+ (*lookup_func) (&Lookup->SubTable[i], stream, indent + 1, hb_type);
+ DUMP ("</Subtable>\n");
+ }
+}
+
+DEF_DUMP (LookupList)
+{
+ int i;
+
+ DUMP_FUINT (LookupList, LookupCount);
+
+ for (i=0; i < LookupList->LookupCount; i++)
+ RECURSE_NUM (Lookup, i, Lookup, &LookupList->Lookup[i]);
+}
+
+void
+HB_Dump_GSUB_Table (HB_GSUB gsub, FILE *stream)
+{
+ int indent = 1;
+ HB_Type hb_type = HB_Type_GSUB;
+
+ do_indent (stream, indent);
+ fprintf(stream, "<!-- GSUB -->\n");
+ RECURSE (ScriptList, ScriptList, &gsub->ScriptList);
+ RECURSE (FeatureList, FeatureList, &gsub->FeatureList);
+ RECURSE (LookupList, LookupList, &gsub->LookupList);
+}
+
+void
+HB_Dump_GPOS_Table (HB_GPOS gpos, FILE *stream)
+{
+ int indent = 1;
+ HB_Type hb_type = HB_Type_GPOS;
+
+ do_indent (stream, indent);
+ fprintf(stream, "<!-- GPOS -->\n");
+ RECURSE (ScriptList, ScriptList, &gpos->ScriptList);
+ RECURSE (FeatureList, FeatureList, &gpos->FeatureList);
+ RECURSE (LookupList, LookupList, &gpos->LookupList);
+}
diff --git a/src/harfbuzz-dump.h b/src/harfbuzz-dump.h
new file mode 100644
index 0000000..ea4a62b
--- /dev/null
+++ b/src/harfbuzz-dump.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2000, 2007 Red Hat, Inc.
+ *
+ * This is part of HarfBuzz, an OpenType Layout engine library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Red Hat Author(s): Owen Taylor, Behdad Esfahbod
+ */
+
+#ifndef HARFBUZZ_DUMP_H
+#define HARFBUZZ_DUMP_H
+
+#include <stdio.h>
+#include "harfbuzz-gsub.h"
+#include "harfbuzz-gpos.h"
+
+HB_BEGIN_HEADER
+
+void HB_Dump_GSUB_Table (HB_GSUB gsub, FILE *stream);
+void HB_Dump_GPOS_Table (HB_GPOS gpos, FILE *stream);
+
+HB_END_HEADER
+
+#endif /* HARFBUZZ_DUMP_H */
diff --git a/src/harfbuzz-gdef-private.h b/src/harfbuzz-gdef-private.h
new file mode 100644
index 0000000..4a57520
--- /dev/null
+++ b/src/harfbuzz-gdef-private.h
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 1998-2004 David Turner and Werner Lemberg
+ * Copyright (C) 2006 Behdad Esfahbod
+ *
+ * This is part of HarfBuzz, an OpenType Layout engine 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 HARFBUZZ_GDEF_PRIVATE_H
+#define HARFBUZZ_GDEF_PRIVATE_H
+
+#include "harfbuzz-impl.h"
+#include "harfbuzz-stream-private.h"
+#include "harfbuzz-buffer-private.h"
+#include "harfbuzz-gdef.h"
+
+HB_BEGIN_HEADER
+
+
+/* Attachment related structures */
+
+struct HB_AttachPoint_
+{
+ HB_UShort PointCount; /* size of the PointIndex array */
+ HB_UShort* PointIndex; /* array of contour points */
+};
+
+/* Ligature Caret related structures */
+
+struct HB_CaretValueFormat1_
+{
+ HB_Short Coordinate; /* x or y value (in design units) */
+};
+
+typedef struct HB_CaretValueFormat1_ HB_CaretValueFormat1;
+
+
+struct HB_CaretValueFormat2_
+{
+ HB_UShort CaretValuePoint; /* contour point index on glyph */
+};
+
+typedef struct HB_CaretValueFormat2_ HB_CaretValueFormat2;
+
+
+struct HB_CaretValueFormat3_
+{
+ HB_Short Coordinate; /* x or y value (in design units) */
+ HB_Device Device; /* Device table for x or y value */
+};
+
+typedef struct HB_CaretValueFormat3_ HB_CaretValueFormat3;
+
+
+struct HB_CaretValueFormat4_
+{
+ HB_UShort IdCaretValue; /* metric ID */
+};
+
+typedef struct HB_CaretValueFormat4_ HB_CaretValueFormat4;
+
+
+struct HB_CaretValue_
+{
+ HB_UShort CaretValueFormat; /* 1, 2, 3, or 4 */
+
+ union
+ {
+ HB_CaretValueFormat1 cvf1;
+ HB_CaretValueFormat2 cvf2;
+ HB_CaretValueFormat3 cvf3;
+ HB_CaretValueFormat4 cvf4;
+ } cvf;
+};
+
+typedef struct HB_CaretValue_ HB_CaretValue;
+
+
+struct HB_LigGlyph_
+{
+ HB_Bool loaded;
+
+ HB_UShort CaretCount; /* number of caret values */
+ HB_CaretValue* CaretValue; /* array of caret values */
+};
+
+
+HB_INTERNAL HB_Error
+_HB_GDEF_Add_Glyph_Property( HB_GDEFHeader* gdef,
+ HB_UShort glyphID,
+ HB_UShort property );
+
+HB_INTERNAL HB_Error
+_HB_GDEF_Check_Property( HB_GDEFHeader* gdef,
+ HB_GlyphItem item,
+ HB_UShort flags,
+ HB_UShort* property );
+
+HB_INTERNAL HB_Error
+_HB_GDEF_LoadMarkAttachClassDef_From_LookupFlags( HB_GDEFHeader* gdef,
+ HB_Stream input,
+ HB_Lookup* lo,
+ HB_UShort num_lookups );
+
+HB_END_HEADER
+
+#endif /* HARFBUZZ_GDEF_PRIVATE_H */
diff --git a/src/harfbuzz-gdef.c b/src/harfbuzz-gdef.c
new file mode 100644
index 0000000..8fecf4c
--- /dev/null
+++ b/src/harfbuzz-gdef.c
@@ -0,0 +1,1160 @@
+/*
+ * Copyright (C) 1998-2004 David Turner and Werner Lemberg
+ * Copyright (C) 2006 Behdad Esfahbod
+ *
+ * This is part of HarfBuzz, an OpenType Layout engine 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 "harfbuzz-impl.h"
+#include "harfbuzz-gdef-private.h"
+#include "harfbuzz-open-private.h"
+
+static HB_Error Load_AttachList( HB_AttachList* al,
+ HB_Stream stream );
+static HB_Error Load_LigCaretList( HB_LigCaretList* lcl,
+ HB_Stream stream );
+
+static void Free_AttachList( HB_AttachList* al );
+static void Free_LigCaretList( HB_LigCaretList* lcl );
+
+static void Free_NewGlyphClasses( HB_GDEFHeader* gdef );
+
+
+
+/* GDEF glyph classes */
+
+#define UNCLASSIFIED_GLYPH 0
+#define SIMPLE_GLYPH 1
+#define LIGATURE_GLYPH 2
+#define MARK_GLYPH 3
+#define COMPONENT_GLYPH 4
+
+
+
+
+
+
+HB_Error HB_New_GDEF_Table( HB_GDEFHeader** retptr )
+{
+ HB_Error error;
+
+ HB_GDEFHeader* gdef;
+
+ if ( !retptr )
+ return ERR(HB_Err_Invalid_Argument);
+
+ if ( ALLOC( gdef, sizeof( *gdef ) ) )
+ return error;
+
+ gdef->GlyphClassDef.loaded = FALSE;
+ gdef->AttachList.loaded = FALSE;
+ gdef->LigCaretList.loaded = FALSE;
+ gdef->MarkAttachClassDef_offset = 0;
+ gdef->MarkAttachClassDef.loaded = FALSE;
+
+ gdef->LastGlyph = 0;
+ gdef->NewGlyphClasses = NULL;
+
+ *retptr = gdef;
+
+ return HB_Err_Ok;
+}
+
+
+HB_Error HB_Load_GDEF_Table( HB_Font font,
+ HB_GDEFHeader** retptr )
+{
+ HB_Error error;
+ HB_Stream stream = font->stream;
+ HB_UInt cur_offset, new_offset, base_offset;
+
+ HB_GDEFHeader* gdef;
+
+
+ if ( !retptr )
+ return ERR(HB_Err_Invalid_Argument);
+
+ if ( GOTO_Table( TTAG_GDEF ) )
+ return error;
+
+ if (( error = HB_New_GDEF_Table ( &gdef ) ))
+ return error;
+
+ base_offset = FILE_Pos();
+
+ /* skip version */
+
+ if ( FILE_Seek( base_offset + 4L ) ||
+ ACCESS_Frame( 2L ) )
+ goto Fail0;
+
+ new_offset = GET_UShort();
+
+ FORGET_Frame();
+
+ /* all GDEF subtables are optional */
+
+ if ( new_offset )
+ {
+ new_offset += base_offset;
+
+ /* only classes 1-4 are allowed here */
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = _HB_OPEN_Load_ClassDefinition( &gdef->GlyphClassDef, 5,
+ stream ) ) != HB_Err_Ok )
+ goto Fail0;
+ (void)FILE_Seek( cur_offset );
+ }
+
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail1;
+
+ new_offset = GET_UShort();
+
+ FORGET_Frame();
+
+ if ( new_offset )
+ {
+ new_offset += base_offset;
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = Load_AttachList( &gdef->AttachList,
+ stream ) ) != HB_Err_Ok )
+ goto Fail1;
+ (void)FILE_Seek( cur_offset );
+ }
+
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail2;
+
+ new_offset = GET_UShort();
+
+ FORGET_Frame();
+
+ if ( new_offset )
+ {
+ new_offset += base_offset;
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = Load_LigCaretList( &gdef->LigCaretList,
+ stream ) ) != HB_Err_Ok )
+ goto Fail2;
+ (void)FILE_Seek( cur_offset );
+ }
+
+ /* OpenType 1.2 has introduced the `MarkAttachClassDef' field. We
+ first have to scan the LookupFlag values to find out whether we
+ must load it or not. Here we only store the offset of the table. */
+
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail3;
+
+ new_offset = GET_UShort();
+
+ FORGET_Frame();
+
+ if ( new_offset )
+ gdef->MarkAttachClassDef_offset = new_offset + base_offset;
+ else
+ gdef->MarkAttachClassDef_offset = 0;
+
+ *retptr = gdef;
+
+ return HB_Err_Ok;
+
+Fail3:
+ Free_LigCaretList( &gdef->LigCaretList );
+
+Fail2:
+ Free_AttachList( &gdef->AttachList );
+
+Fail1:
+ _HB_OPEN_Free_ClassDefinition( &gdef->GlyphClassDef );
+
+Fail0:
+ FREE( gdef );
+
+ return error;
+}
+
+
+HB_Error HB_Done_GDEF_Table ( HB_GDEFHeader* gdef )
+{
+ Free_LigCaretList( &gdef->LigCaretList );
+ Free_AttachList( &gdef->AttachList );
+ _HB_OPEN_Free_ClassDefinition( &gdef->GlyphClassDef );
+ _HB_OPEN_Free_ClassDefinition( &gdef->MarkAttachClassDef );
+
+ Free_NewGlyphClasses( gdef );
+
+ FREE( gdef );
+
+ return HB_Err_Ok;
+}
+
+
+
+
+/*******************************
+ * AttachList related functions
+ *******************************/
+
+
+/* AttachPoint */
+
+static HB_Error Load_AttachPoint( HB_AttachPoint* ap,
+ HB_Stream stream )
+{
+ HB_Error error;
+
+ HB_UShort n, count;
+ HB_UShort* pi;
+
+
+ if ( ACCESS_Frame( 2L ) )
+ return error;
+
+ count = ap->PointCount = GET_UShort();
+
+ FORGET_Frame();
+
+ ap->PointIndex = NULL;
+
+ if ( count )
+ {
+ if ( ALLOC_ARRAY( ap->PointIndex, count, HB_UShort ) )
+ return error;
+
+ pi = ap->PointIndex;
+
+ if ( ACCESS_Frame( count * 2L ) )
+ {
+ FREE( pi );
+ return error;
+ }
+
+ for ( n = 0; n < count; n++ )
+ pi[n] = GET_UShort();
+
+ FORGET_Frame();
+ }
+
+ return HB_Err_Ok;
+}
+
+
+static void Free_AttachPoint( HB_AttachPoint* ap )
+{
+ FREE( ap->PointIndex );
+}
+
+
+/* AttachList */
+
+static HB_Error Load_AttachList( HB_AttachList* al,
+ HB_Stream stream )
+{
+ HB_Error error;
+
+ HB_UShort n, m, count;
+ HB_UInt cur_offset, new_offset, base_offset;
+
+ HB_AttachPoint* ap;
+
+
+ base_offset = FILE_Pos();
+
+ if ( ACCESS_Frame( 2L ) )
+ return error;
+
+ new_offset = GET_UShort() + base_offset;
+
+ FORGET_Frame();
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = _HB_OPEN_Load_Coverage( &al->Coverage, stream ) ) != HB_Err_Ok )
+ return error;
+ (void)FILE_Seek( cur_offset );
+
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail2;
+
+ count = al->GlyphCount = GET_UShort();
+
+ FORGET_Frame();
+
+ al->AttachPoint = NULL;
+
+ if ( ALLOC_ARRAY( al->AttachPoint, count, HB_AttachPoint ) )
+ goto Fail2;
+
+ ap = al->AttachPoint;
+
+ for ( n = 0; n < count; n++ )
+ {
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail1;
+
+ new_offset = GET_UShort() + base_offset;
+
+ FORGET_Frame();
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = Load_AttachPoint( &ap[n], stream ) ) != HB_Err_Ok )
+ goto Fail1;
+ (void)FILE_Seek( cur_offset );
+ }
+
+ al->loaded = TRUE;
+
+ return HB_Err_Ok;
+
+Fail1:
+ for ( m = 0; m < n; m++ )
+ Free_AttachPoint( &ap[m] );
+
+ FREE( ap );
+
+Fail2:
+ _HB_OPEN_Free_Coverage( &al->Coverage );
+ return error;
+}
+
+
+static void Free_AttachList( HB_AttachList* al )
+{
+ HB_UShort n, count;
+
+ HB_AttachPoint* ap;
+
+
+ if ( !al->loaded )
+ return;
+
+ if ( al->AttachPoint )
+ {
+ count = al->GlyphCount;
+ ap = al->AttachPoint;
+
+ for ( n = 0; n < count; n++ )
+ Free_AttachPoint( &ap[n] );
+
+ FREE( ap );
+ }
+
+ _HB_OPEN_Free_Coverage( &al->Coverage );
+}
+
+
+
+/*********************************
+ * LigCaretList related functions
+ *********************************/
+
+
+/* CaretValueFormat1 */
+/* CaretValueFormat2 */
+/* CaretValueFormat3 */
+/* CaretValueFormat4 */
+
+static HB_Error Load_CaretValue( HB_CaretValue* cv,
+ HB_Stream stream )
+{
+ HB_Error error;
+
+ HB_UInt cur_offset, new_offset, base_offset;
+
+
+ base_offset = FILE_Pos();
+
+ if ( ACCESS_Frame( 2L ) )
+ return error;
+
+ cv->CaretValueFormat = GET_UShort();
+
+ FORGET_Frame();
+
+ switch ( cv->CaretValueFormat )
+ {
+ case 1:
+ if ( ACCESS_Frame( 2L ) )
+ return error;
+
+ cv->cvf.cvf1.Coordinate = GET_Short();
+
+ FORGET_Frame();
+
+ break;
+
+ case 2:
+ if ( ACCESS_Frame( 2L ) )
+ return error;
+
+ cv->cvf.cvf2.CaretValuePoint = GET_UShort();
+
+ FORGET_Frame();
+
+ break;
+
+ case 3:
+ if ( ACCESS_Frame( 4L ) )
+ return error;
+
+ cv->cvf.cvf3.Coordinate = GET_Short();
+
+ new_offset = GET_UShort() + base_offset;
+
+ FORGET_Frame();
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = _HB_OPEN_Load_Device( &cv->cvf.cvf3.Device,
+ stream ) ) != HB_Err_Ok )
+ return error;
+ (void)FILE_Seek( cur_offset );
+
+ break;
+
+ case 4:
+ if ( ACCESS_Frame( 2L ) )
+ return error;
+
+ cv->cvf.cvf4.IdCaretValue = GET_UShort();
+
+ FORGET_Frame();
+ break;
+
+ default:
+ return ERR(HB_Err_Invalid_SubTable_Format);
+ }
+
+ return HB_Err_Ok;
+}
+
+
+static void Free_CaretValue( HB_CaretValue* cv )
+{
+ if ( cv->CaretValueFormat == 3 )
+ _HB_OPEN_Free_Device( &cv->cvf.cvf3.Device );
+}
+
+
+/* LigGlyph */
+
+static HB_Error Load_LigGlyph( HB_LigGlyph* lg,
+ HB_Stream stream )
+{
+ HB_Error error;
+
+ HB_UShort n, m, count;
+ HB_UInt cur_offset, new_offset, base_offset;
+
+ HB_CaretValue* cv;
+
+
+ base_offset = FILE_Pos();
+
+ if ( ACCESS_Frame( 2L ) )
+ return error;
+
+ count = lg->CaretCount = GET_UShort();
+
+ FORGET_Frame();
+
+ lg->CaretValue = NULL;
+
+ if ( ALLOC_ARRAY( lg->CaretValue, count, HB_CaretValue ) )
+ return error;
+
+ cv = lg->CaretValue;
+
+ for ( n = 0; n < count; n++ )
+ {
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail;
+
+ new_offset = GET_UShort() + base_offset;
+
+ FORGET_Frame();
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = Load_CaretValue( &cv[n], stream ) ) != HB_Err_Ok )
+ goto Fail;
+ (void)FILE_Seek( cur_offset );
+ }
+
+ return HB_Err_Ok;
+
+Fail:
+ for ( m = 0; m < n; m++ )
+ Free_CaretValue( &cv[m] );
+
+ FREE( cv );
+ return error;
+}
+
+
+static void Free_LigGlyph( HB_LigGlyph* lg )
+{
+ HB_UShort n, count;
+
+ HB_CaretValue* cv;
+
+
+ if ( lg->CaretValue )
+ {
+ count = lg->CaretCount;
+ cv = lg->CaretValue;
+
+ for ( n = 0; n < count; n++ )
+ Free_CaretValue( &cv[n] );
+
+ FREE( cv );
+ }
+}
+
+
+/* LigCaretList */
+
+static HB_Error Load_LigCaretList( HB_LigCaretList* lcl,
+ HB_Stream stream )
+{
+ HB_Error error;
+
+ HB_UShort m, n, count;
+ HB_UInt cur_offset, new_offset, base_offset;
+
+ HB_LigGlyph* lg;
+
+
+ base_offset = FILE_Pos();
+
+ if ( ACCESS_Frame( 2L ) )
+ return error;
+
+ new_offset = GET_UShort() + base_offset;
+
+ FORGET_Frame();
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = _HB_OPEN_Load_Coverage( &lcl->Coverage, stream ) ) != HB_Err_Ok )
+ return error;
+ (void)FILE_Seek( cur_offset );
+
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail2;
+
+ count = lcl->LigGlyphCount = GET_UShort();
+
+ FORGET_Frame();
+
+ lcl->LigGlyph = NULL;
+
+ if ( ALLOC_ARRAY( lcl->LigGlyph, count, HB_LigGlyph ) )
+ goto Fail2;
+
+ lg = lcl->LigGlyph;
+
+ for ( n = 0; n < count; n++ )
+ {
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail1;
+
+ new_offset = GET_UShort() + base_offset;
+
+ FORGET_Frame();
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = Load_LigGlyph( &lg[n], stream ) ) != HB_Err_Ok )
+ goto Fail1;
+ (void)FILE_Seek( cur_offset );
+ }
+
+ lcl->loaded = TRUE;
+
+ return HB_Err_Ok;
+
+Fail1:
+ for ( m = 0; m < n; m++ )
+ Free_LigGlyph( &lg[m] );
+
+ FREE( lg );
+
+Fail2:
+ _HB_OPEN_Free_Coverage( &lcl->Coverage );
+ return error;
+}
+
+
+static void Free_LigCaretList( HB_LigCaretList* lcl )
+{
+ HB_UShort n, count;
+
+ HB_LigGlyph* lg;
+
+
+ if ( !lcl->loaded )
+ return;
+
+ if ( lcl->LigGlyph )
+ {
+ count = lcl->LigGlyphCount;
+ lg = lcl->LigGlyph;
+
+ for ( n = 0; n < count; n++ )
+ Free_LigGlyph( &lg[n] );
+
+ FREE( lg );
+ }
+
+ _HB_OPEN_Free_Coverage( &lcl->Coverage );
+}
+
+
+
+/***********
+ * GDEF API
+ ***********/
+
+
+static HB_UShort Get_New_Class( HB_GDEFHeader* gdef,
+ HB_UShort glyphID,
+ HB_UShort index )
+{
+ HB_UShort glyph_index, array_index, count;
+ HB_UShort byte, bits;
+
+ HB_ClassRangeRecord* gcrr;
+ HB_UShort** ngc;
+
+
+ if ( glyphID >= gdef->LastGlyph )
+ return 0;
+
+ count = gdef->GlyphClassDef.cd.cd2.ClassRangeCount;
+ gcrr = gdef->GlyphClassDef.cd.cd2.ClassRangeRecord;
+ ngc = gdef->NewGlyphClasses;
+
+ if ( index < count && glyphID < gcrr[index].Start )
+ {
+ array_index = index;
+ if ( index == 0 )
+ glyph_index = glyphID;
+ else
+ glyph_index = glyphID - gcrr[index - 1].End - 1;
+ }
+ else
+ {
+ array_index = index + 1;
+ glyph_index = glyphID - gcrr[index].End - 1;
+ }
+
+ byte = ngc[array_index][glyph_index / 4];
+ bits = byte >> ( 16 - ( glyph_index % 4 + 1 ) * 4 );
+
+ return bits & 0x000F;
+}
+
+
+
+HB_Error HB_GDEF_Get_Glyph_Property( HB_GDEFHeader* gdef,
+ HB_UShort glyphID,
+ HB_UShort* property )
+{
+ HB_UShort class = 0, index = 0; /* shut compiler up */
+
+ HB_Error error;
+
+
+ if ( !gdef || !property )
+ return ERR(HB_Err_Invalid_Argument);
+
+ /* first, we check for mark attach classes */
+
+ if ( gdef->MarkAttachClassDef.loaded )
+ {
+ error = _HB_OPEN_Get_Class( &gdef->MarkAttachClassDef, glyphID, &class, &index );
+ if ( error && error != HB_Err_Not_Covered )
+ return error;
+ if ( !error )
+ {
+ *property = class << 8;
+ return HB_Err_Ok;
+ }
+ }
+
+ error = _HB_OPEN_Get_Class( &gdef->GlyphClassDef, glyphID, &class, &index );
+ if ( error && error != HB_Err_Not_Covered )
+ return error;
+
+ /* if we have a constructed class table, check whether additional
+ values have been assigned */
+
+ if ( error == HB_Err_Not_Covered && gdef->NewGlyphClasses )
+ class = Get_New_Class( gdef, glyphID, index );
+
+ switch ( class )
+ {
+ default:
+ case UNCLASSIFIED_GLYPH:
+ *property = 0;
+ break;
+
+ case SIMPLE_GLYPH:
+ *property = HB_GDEF_BASE_GLYPH;
+ break;
+
+ case LIGATURE_GLYPH:
+ *property = HB_GDEF_LIGATURE;
+ break;
+
+ case MARK_GLYPH:
+ *property = HB_GDEF_MARK;
+ break;
+
+ case COMPONENT_GLYPH:
+ *property = HB_GDEF_COMPONENT;
+ break;
+ }
+
+ return HB_Err_Ok;
+}
+
+
+static HB_Error Make_ClassRange( HB_ClassDefinition* cd,
+ HB_UShort start,
+ HB_UShort end,
+ HB_UShort class )
+{
+ HB_Error error;
+ HB_UShort index;
+
+ HB_ClassDefFormat2* cdf2;
+ HB_ClassRangeRecord* crr;
+
+
+ cdf2 = &cd->cd.cd2;
+
+ if ( REALLOC_ARRAY( cdf2->ClassRangeRecord,
+ cdf2->ClassRangeCount + 1 ,
+ HB_ClassRangeRecord ) )
+ return error;
+
+ cdf2->ClassRangeCount++;
+
+ crr = cdf2->ClassRangeRecord;
+ index = cdf2->ClassRangeCount - 1;
+
+ crr[index].Start = start;
+ crr[index].End = end;
+ crr[index].Class = class;
+
+ return HB_Err_Ok;
+}
+
+
+
+HB_Error HB_GDEF_Build_ClassDefinition( HB_GDEFHeader* gdef,
+ HB_UShort num_glyphs,
+ HB_UShort glyph_count,
+ HB_UShort* glyph_array,
+ HB_UShort* class_array )
+{
+ HB_UShort start, curr_glyph, curr_class;
+ HB_UShort n, m, count;
+ HB_Error error;
+
+ HB_ClassDefinition* gcd;
+ HB_ClassRangeRecord* gcrr;
+ HB_UShort** ngc;
+
+
+ if ( !gdef || !glyph_array || !class_array )
+ return ERR(HB_Err_Invalid_Argument);
+
+ gcd = &gdef->GlyphClassDef;
+
+ /* We build a format 2 table */
+
+ gcd->ClassFormat = 2;
+
+ gcd->cd.cd2.ClassRangeCount = 0;
+ gcd->cd.cd2.ClassRangeRecord = NULL;
+
+ start = glyph_array[0];
+ curr_class = class_array[0];
+ curr_glyph = start;
+
+ if ( curr_class >= 5 )
+ {
+ error = ERR(HB_Err_Invalid_Argument);
+ goto Fail4;
+ }
+
+ glyph_count--;
+
+ for ( n = 0; n < glyph_count + 1; n++ )
+ {
+ if ( curr_glyph == glyph_array[n] && curr_class == class_array[n] )
+ {
+ if ( n == glyph_count )
+ {
+ if ( ( error = Make_ClassRange( gcd, start,
+ curr_glyph,
+ curr_class ) ) != HB_Err_Ok )
+ goto Fail3;
+ }
+ else
+ {
+ if ( curr_glyph == 0xFFFF )
+ {
+ error = ERR(HB_Err_Invalid_Argument);
+ goto Fail3;
+ }
+ else
+ curr_glyph++;
+ }
+ }
+ else
+ {
+ if ( ( error = Make_ClassRange( gcd, start,
+ curr_glyph - 1,
+ curr_class ) ) != HB_Err_Ok )
+ goto Fail3;
+
+ if ( curr_glyph > glyph_array[n] )
+ {
+ error = ERR(HB_Err_Invalid_Argument);
+ goto Fail3;
+ }
+
+ start = glyph_array[n];
+ curr_class = class_array[n];
+ curr_glyph = start;
+
+ if ( curr_class >= 5 )
+ {
+ error = ERR(HB_Err_Invalid_Argument);
+ goto Fail3;
+ }
+
+ if ( n == glyph_count )
+ {
+ if ( ( error = Make_ClassRange( gcd, start,
+ curr_glyph,
+ curr_class ) ) != HB_Err_Ok )
+ goto Fail3;
+ }
+ else
+ {
+ if ( curr_glyph == 0xFFFF )
+ {
+ error = ERR(HB_Err_Invalid_Argument);
+ goto Fail3;
+ }
+ else
+ curr_glyph++;
+ }
+ }
+ }
+
+ /* now prepare the arrays for class values assigned during the lookup
+ process */
+
+ if ( ALLOC_ARRAY( gdef->NewGlyphClasses,
+ gcd->cd.cd2.ClassRangeCount + 1, HB_UShort* ) )
+ goto Fail3;
+
+ count = gcd->cd.cd2.ClassRangeCount;
+ gcrr = gcd->cd.cd2.ClassRangeRecord;
+ ngc = gdef->NewGlyphClasses;
+
+ /* We allocate arrays for all glyphs not covered by the class range
+ records. Each element holds four class values. */
+
+ if ( count > 0 )
+ {
+ if ( gcrr[0].Start )
+ {
+ if ( ALLOC_ARRAY( ngc[0], ( gcrr[0].Start + 3 ) / 4, HB_UShort ) )
+ goto Fail2;
+ }
+
+ for ( n = 1; n < count; n++ )
+ {
+ if ( gcrr[n].Start - gcrr[n - 1].End > 1 )
+ if ( ALLOC_ARRAY( ngc[n],
+ ( gcrr[n].Start - gcrr[n - 1].End + 2 ) / 4,
+ HB_UShort ) )
+ goto Fail1;
+ }
+
+ if ( gcrr[count - 1].End != num_glyphs - 1 )
+ {
+ if ( ALLOC_ARRAY( ngc[count],
+ ( num_glyphs - gcrr[count - 1].End + 2 ) / 4,
+ HB_UShort ) )
+ goto Fail1;
+ }
+ }
+ else if ( num_glyphs > 0 )
+ {
+ if ( ALLOC_ARRAY( ngc[count],
+ ( num_glyphs + 3 ) / 4,
+ HB_UShort ) )
+ goto Fail2;
+ }
+
+ gdef->LastGlyph = num_glyphs - 1;
+
+ gdef->MarkAttachClassDef_offset = 0L;
+ gdef->MarkAttachClassDef.loaded = FALSE;
+
+ gcd->loaded = TRUE;
+
+ return HB_Err_Ok;
+
+Fail1:
+ for ( m = 0; m < n; m++ )
+ FREE( ngc[m] );
+
+Fail2:
+ FREE( gdef->NewGlyphClasses );
+
+Fail3:
+ FREE( gcd->cd.cd2.ClassRangeRecord );
+
+Fail4:
+ return error;
+}
+
+
+static void Free_NewGlyphClasses( HB_GDEFHeader* gdef )
+{
+ HB_UShort** ngc;
+ HB_UShort n, count;
+
+
+ if ( gdef->NewGlyphClasses )
+ {
+ count = gdef->GlyphClassDef.cd.cd2.ClassRangeCount + 1;
+ ngc = gdef->NewGlyphClasses;
+
+ for ( n = 0; n < count; n++ )
+ FREE( ngc[n] );
+
+ FREE( ngc );
+ }
+}
+
+
+HB_INTERNAL HB_Error
+_HB_GDEF_Add_Glyph_Property( HB_GDEFHeader* gdef,
+ HB_UShort glyphID,
+ HB_UShort property )
+{
+ HB_Error error;
+ HB_UShort class, new_class, index = 0; /* shut compiler up */
+ HB_UShort byte, bits, mask;
+ HB_UShort array_index, glyph_index, count;
+
+ HB_ClassRangeRecord* gcrr;
+ HB_UShort** ngc;
+
+
+ error = _HB_OPEN_Get_Class( &gdef->GlyphClassDef, glyphID, &class, &index );
+ if ( error && error != HB_Err_Not_Covered )
+ return error;
+
+ /* we don't accept glyphs covered in `GlyphClassDef' */
+
+ if ( !error )
+ return HB_Err_Not_Covered;
+
+ switch ( property )
+ {
+ case 0:
+ new_class = UNCLASSIFIED_GLYPH;
+ break;
+
+ case HB_GDEF_BASE_GLYPH:
+ new_class = SIMPLE_GLYPH;
+ break;
+
+ case HB_GDEF_LIGATURE:
+ new_class = LIGATURE_GLYPH;
+ break;
+
+ case HB_GDEF_MARK:
+ new_class = MARK_GLYPH;
+ break;
+
+ case HB_GDEF_COMPONENT:
+ new_class = COMPONENT_GLYPH;
+ break;
+
+ default:
+ return ERR(HB_Err_Invalid_Argument);
+ }
+
+ count = gdef->GlyphClassDef.cd.cd2.ClassRangeCount;
+ gcrr = gdef->GlyphClassDef.cd.cd2.ClassRangeRecord;
+ ngc = gdef->NewGlyphClasses;
+
+ if ( index < count && glyphID < gcrr[index].Start )
+ {
+ array_index = index;
+ if ( index == 0 )
+ glyph_index = glyphID;
+ else
+ glyph_index = glyphID - gcrr[index - 1].End - 1;
+ }
+ else
+ {
+ array_index = index + 1;
+ glyph_index = glyphID - gcrr[index].End - 1;
+ }
+
+ byte = ngc[array_index][glyph_index / 4];
+ bits = byte >> ( 16 - ( glyph_index % 4 + 1 ) * 4 );
+ class = bits & 0x000F;
+
+ /* we don't overwrite existing entries */
+
+ if ( !class )
+ {
+ bits = new_class << ( 16 - ( glyph_index % 4 + 1 ) * 4 );
+ mask = ~( 0x000F << ( 16 - ( glyph_index % 4 + 1 ) * 4 ) );
+
+ ngc[array_index][glyph_index / 4] &= mask;
+ ngc[array_index][glyph_index / 4] |= bits;
+ }
+
+ return HB_Err_Ok;
+}
+
+
+HB_INTERNAL HB_Error
+_HB_GDEF_Check_Property( HB_GDEFHeader* gdef,
+ HB_GlyphItem gitem,
+ HB_UShort flags,
+ HB_UShort* property )
+{
+ HB_Error error;
+
+ if ( gdef )
+ {
+ HB_UShort basic_glyph_class;
+ HB_UShort desired_attachment_class;
+
+ if ( gitem->gproperties == HB_GLYPH_PROPERTIES_UNKNOWN )
+ {
+ error = HB_GDEF_Get_Glyph_Property( gdef, gitem->gindex, &gitem->gproperties );
+ if ( error )
+ return error;
+ }
+
+ *property = gitem->gproperties;
+
+ /* If the glyph was found in the MarkAttachmentClass table,
+ * then that class value is the high byte of the result,
+ * otherwise the low byte contains the basic type of the glyph
+ * as defined by the GlyphClassDef table.
+ */
+ if ( *property & HB_LOOKUP_FLAG_IGNORE_SPECIAL_MARKS )
+ basic_glyph_class = HB_GDEF_MARK;
+ else
+ basic_glyph_class = *property;
+
+ /* Return Not_Covered, if, for example, basic_glyph_class
+ * is HB_GDEF_LIGATURE and LookFlags includes HB_LOOKUP_FLAG_IGNORE_LIGATURES
+ */
+ if ( flags & basic_glyph_class )
+ return HB_Err_Not_Covered;
+
+ /* The high byte of LookupFlags has the meaning
+ * "ignore marks of attachment type different than
+ * the attachment type specified."
+ */
+ desired_attachment_class = flags & HB_LOOKUP_FLAG_IGNORE_SPECIAL_MARKS;
+ if ( desired_attachment_class )
+ {
+ if ( basic_glyph_class == HB_GDEF_MARK &&
+ *property != desired_attachment_class )
+ return HB_Err_Not_Covered;
+ }
+ } else {
+ *property = 0;
+ }
+
+ return HB_Err_Ok;
+}
+
+HB_INTERNAL HB_Error
+_HB_GDEF_LoadMarkAttachClassDef_From_LookupFlags( HB_GDEFHeader* gdef,
+ HB_Stream stream,
+ HB_Lookup* lo,
+ HB_UShort num_lookups)
+{
+ HB_Error error = HB_Err_Ok;
+ HB_UShort i;
+
+ /* We now check the LookupFlags for values larger than 0xFF to find
+ out whether we need to load the `MarkAttachClassDef' field of the
+ GDEF table -- this hack is necessary for OpenType 1.2 tables since
+ the version field of the GDEF table hasn't been incremented.
+
+ For constructed GDEF tables, we only load it if
+ `MarkAttachClassDef_offset' is not zero (nevertheless, a build of
+ a constructed mark attach table is not supported currently). */
+
+ if ( gdef &&
+ gdef->MarkAttachClassDef_offset && !gdef->MarkAttachClassDef.loaded )
+ {
+ for ( i = 0; i < num_lookups; i++ )
+ {
+
+ if ( lo[i].LookupFlag & HB_LOOKUP_FLAG_IGNORE_SPECIAL_MARKS )
+ {
+ if ( FILE_Seek( gdef->MarkAttachClassDef_offset ) ||
+ ( error = _HB_OPEN_Load_ClassDefinition( &gdef->MarkAttachClassDef,
+ 256, stream ) ) != HB_Err_Ok )
+ goto Done;
+
+ break;
+ }
+ }
+ }
+
+Done:
+ return error;
+}
+
+/* END */
diff --git a/src/harfbuzz-gdef.h b/src/harfbuzz-gdef.h
new file mode 100644
index 0000000..84afe17
--- /dev/null
+++ b/src/harfbuzz-gdef.h
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 1998-2004 David Turner and Werner Lemberg
+ * Copyright (C) 2006 Behdad Esfahbod
+ *
+ * This is part of HarfBuzz, an OpenType Layout engine 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 HARFBUZZ_GDEF_H
+#define HARFBUZZ_GDEF_H
+
+#include "harfbuzz-open.h"
+
+HB_BEGIN_HEADER
+
+
+/* GDEF glyph properties. Note that HB_GDEF_COMPONENT has no corresponding
+ * flag in the LookupFlag field. */
+#define HB_GDEF_BASE_GLYPH 0x0002
+#define HB_GDEF_LIGATURE 0x0004
+#define HB_GDEF_MARK 0x0008
+#define HB_GDEF_COMPONENT 0x0010
+
+
+typedef struct HB_AttachPoint_ HB_AttachPoint;
+
+
+struct HB_AttachList_
+{
+ HB_Bool loaded;
+
+ HB_Coverage Coverage; /* Coverage table */
+ HB_UShort GlyphCount; /* number of glyphs with
+ attachments */
+ HB_AttachPoint* AttachPoint; /* array of AttachPoint tables */
+};
+
+typedef struct HB_AttachList_ HB_AttachList;
+
+typedef struct HB_LigGlyph_ HB_LigGlyph;
+
+struct HB_LigCaretList_
+{
+ HB_Bool loaded;
+
+ HB_Coverage Coverage; /* Coverage table */
+ HB_UShort LigGlyphCount; /* number of ligature glyphs */
+ HB_LigGlyph* LigGlyph; /* array of LigGlyph tables */
+};
+
+typedef struct HB_LigCaretList_ HB_LigCaretList;
+
+
+
+/* The `NewGlyphClasses' field is not defined in the TTO specification.
+ We use it for fonts with a constructed `GlyphClassDef' structure
+ (i.e., which don't have a GDEF table) to collect glyph classes
+ assigned during the lookup process. The number of arrays in this
+ pointer array is GlyphClassDef->cd.cd2.ClassRangeCount+1; the nth
+ array then contains the glyph class values of the glyphs not covered
+ by the ClassRangeRecords structures with index n-1 and n. We store
+ glyph class values for four glyphs in a single array element.
+
+ `LastGlyph' is identical to the number of glyphs minus one in the
+ font; we need it only if `NewGlyphClasses' is not NULL (to have an
+ upper bound for the last array).
+
+ Note that we first store the file offset to the `MarkAttachClassDef'
+ field (which has been introduced in OpenType 1.2) -- since the
+ `Version' field value hasn't been increased to indicate that we have
+ one more field for some obscure reason, we must parse the GSUB table
+ to find out whether class values refer to this table. Only then we
+ can finally load the MarkAttachClassDef structure if necessary. */
+
+struct HB_GDEFHeader_
+{
+ HB_UInt offset;
+
+ HB_16Dot16 Version;
+
+ HB_ClassDefinition GlyphClassDef;
+ HB_AttachList AttachList;
+ HB_LigCaretList LigCaretList;
+ HB_UInt MarkAttachClassDef_offset;
+ HB_ClassDefinition MarkAttachClassDef; /* new in OT 1.2 */
+
+ HB_UShort LastGlyph;
+ HB_UShort** NewGlyphClasses;
+};
+
+typedef struct HB_GDEFHeader_ HB_GDEFHeader;
+typedef struct HB_GDEFHeader_* HB_GDEF;
+
+
+HB_Error HB_New_GDEF_Table( HB_GDEFHeader** retptr );
+
+
+HB_Error HB_Load_GDEF_Table( HB_Font font,
+ HB_GDEFHeader** gdef );
+
+
+HB_Error HB_Done_GDEF_Table ( HB_GDEFHeader* gdef );
+
+
+HB_Error HB_GDEF_Get_Glyph_Property( HB_GDEFHeader* gdef,
+ HB_UShort glyphID,
+ HB_UShort* property );
+
+HB_Error HB_GDEF_Build_ClassDefinition( HB_GDEFHeader* gdef,
+ HB_UShort num_glyphs,
+ HB_UShort glyph_count,
+ HB_UShort* glyph_array,
+ HB_UShort* class_array );
+
+
+HB_END_HEADER
+
+#endif /* HARFBUZZ_GDEF_H */
diff --git a/src/harfbuzz-global.h b/src/harfbuzz-global.h
new file mode 100644
index 0000000..7b8a0f2
--- /dev/null
+++ b/src/harfbuzz-global.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2007 Trolltech ASA
+ * Copyright (C) 2007 Red Hat, Inc.
+ *
+ * This is part of HarfBuzz, an OpenType Layout engine library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Red Hat Author(s): Behdad Esfahbod
+ */
+
+#ifndef HARFBUZZ_GLOBAL_H
+#define HARFBUZZ_GLOBAL_H
+
+/* XXX */
+#include "hb-ot-layout.h"
+
+#include <ft2build.h>
+#include FT_FREETYPE_H
+
+#ifdef __cplusplus
+#define HB_BEGIN_HEADER extern "C" {
+#define HB_END_HEADER }
+#else
+#define HB_BEGIN_HEADER /* nothing */
+#define HB_END_HEADER /* nothing */
+#endif
+
+HB_BEGIN_HEADER
+
+#define HB_MAKE_TAG(a,b,c,d) FT_MAKE_TAG(a,b,c,d)
+typedef FT_Pos HB_Fixed; /* 26.6 */
+typedef FT_Fixed HB_16Dot16; /* 16.6 */
+typedef FT_Face HB_Font;
+
+typedef unsigned char HB_Byte;
+typedef signed char HB_Char;
+typedef unsigned short HB_UShort;
+typedef signed short HB_Short;
+typedef unsigned int HB_UInt;
+typedef signed int HB_Int;
+typedef int HB_Bool;
+typedef void * HB_Pointer;
+
+
+typedef enum {
+ /* no error */
+ HB_Err_Ok = 0x0000,
+ HB_Err_Not_Covered = 0xFFFF,
+
+ /* _hb_err() is called whenever returning the following errors,
+ * and in a couple places for HB_Err_Not_Covered too. */
+
+ /* programmer error */
+ HB_Err_Invalid_Argument = 0x1A66,
+
+ /* font error */
+ HB_Err_Invalid_SubTable_Format = 0x157F,
+ HB_Err_Invalid_SubTable = 0x1570,
+ HB_Err_Read_Error = 0x6EAD,
+
+ /* system error */
+ HB_Err_Out_Of_Memory = 0xDEAD
+} HB_Error;
+
+HB_END_HEADER
+
+#endif
diff --git a/src/harfbuzz-gpos-private.h b/src/harfbuzz-gpos-private.h
new file mode 100644
index 0000000..3a5c4bc
--- /dev/null
+++ b/src/harfbuzz-gpos-private.h
@@ -0,0 +1,712 @@
+/*
+ * Copyright (C) 1998-2004 David Turner and Werner Lemberg
+ * Copyright (C) 2006 Behdad Esfahbod
+ *
+ * This is part of HarfBuzz, an OpenType Layout engine 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 HARFBUZZ_GPOS_PRIVATE_H
+#define HARFBUZZ_GPOS_PRIVATE_H
+
+#include "harfbuzz-impl.h"
+#include "harfbuzz-stream-private.h"
+#include "harfbuzz-gpos.h"
+
+HB_BEGIN_HEADER
+
+
+/* shared tables */
+
+struct HB_ValueRecord_
+{
+ HB_Short XPlacement; /* horizontal adjustment for
+ placement */
+ HB_Short YPlacement; /* vertical adjustment for
+ placement */
+ HB_Short XAdvance; /* horizontal adjustment for
+ advance */
+ HB_Short YAdvance; /* vertical adjustment for
+ advance */
+ HB_Device XPlacementDevice; /* device table for horizontal
+ placement */
+ HB_Device YPlacementDevice; /* device table for vertical
+ placement */
+ HB_Device XAdvanceDevice; /* device table for horizontal
+ advance */
+ HB_Device YAdvanceDevice; /* device table for vertical
+ advance */
+ HB_UShort XIdPlacement; /* horizontal placement metric ID */
+ HB_UShort YIdPlacement; /* vertical placement metric ID */
+ HB_UShort XIdAdvance; /* horizontal advance metric ID */
+ HB_UShort YIdAdvance; /* vertical advance metric ID */
+};
+
+typedef struct HB_ValueRecord_ HB_ValueRecord;
+
+
+/* Mask values to scan the value format of the ValueRecord structure.
+ We always expand compressed ValueRecords of the font. */
+
+#define HB_GPOS_FORMAT_HAVE_X_PLACEMENT 0x0001
+#define HB_GPOS_FORMAT_HAVE_Y_PLACEMENT 0x0002
+#define HB_GPOS_FORMAT_HAVE_X_ADVANCE 0x0004
+#define HB_GPOS_FORMAT_HAVE_Y_ADVANCE 0x0008
+#define HB_GPOS_FORMAT_HAVE_X_PLACEMENT_DEVICE 0x0010
+#define HB_GPOS_FORMAT_HAVE_Y_PLACEMENT_DEVICE 0x0020
+#define HB_GPOS_FORMAT_HAVE_X_ADVANCE_DEVICE 0x0040
+#define HB_GPOS_FORMAT_HAVE_Y_ADVANCE_DEVICE 0x0080
+#define HB_GPOS_FORMAT_HAVE_X_ID_PLACEMENT 0x0100
+#define HB_GPOS_FORMAT_HAVE_Y_ID_PLACEMENT 0x0200
+#define HB_GPOS_FORMAT_HAVE_X_ID_ADVANCE 0x0400
+#define HB_GPOS_FORMAT_HAVE_Y_ID_ADVANCE 0x0800
+
+
+struct HB_AnchorFormat1_
+{
+ HB_Short XCoordinate; /* horizontal value */
+ HB_Short YCoordinate; /* vertical value */
+};
+
+typedef struct HB_AnchorFormat1_ HB_AnchorFormat1;
+
+
+struct HB_AnchorFormat2_
+{
+ HB_Short XCoordinate; /* horizontal value */
+ HB_Short YCoordinate; /* vertical value */
+ HB_UShort AnchorPoint; /* index to glyph contour point */
+};
+
+typedef struct HB_AnchorFormat2_ HB_AnchorFormat2;
+
+
+struct HB_AnchorFormat3_
+{
+ HB_Short XCoordinate; /* horizontal value */
+ HB_Short YCoordinate; /* vertical value */
+ HB_Device XDeviceTable; /* device table for X coordinate */
+ HB_Device YDeviceTable; /* device table for Y coordinate */
+};
+
+typedef struct HB_AnchorFormat3_ HB_AnchorFormat3;
+
+
+struct HB_AnchorFormat4_
+{
+ HB_UShort XIdAnchor; /* horizontal metric ID */
+ HB_UShort YIdAnchor; /* vertical metric ID */
+};
+
+typedef struct HB_AnchorFormat4_ HB_AnchorFormat4;
+
+
+struct HB_Anchor_
+{
+ HB_UShort PosFormat; /* 1, 2, 3, or 4 -- 0 indicates
+ that there is no Anchor table */
+
+ union
+ {
+ HB_AnchorFormat1 af1;
+ HB_AnchorFormat2 af2;
+ HB_AnchorFormat3 af3;
+ HB_AnchorFormat4 af4;
+ } af;
+};
+
+typedef struct HB_Anchor_ HB_Anchor;
+
+
+struct HB_MarkRecord_
+{
+ HB_UShort Class; /* mark class */
+ HB_Anchor MarkAnchor; /* anchor table */
+};
+
+typedef struct HB_MarkRecord_ HB_MarkRecord;
+
+
+struct HB_MarkArray_
+{
+ HB_UShort MarkCount; /* number of MarkRecord tables */
+ HB_MarkRecord* MarkRecord; /* array of MarkRecord tables */
+};
+
+typedef struct HB_MarkArray_ HB_MarkArray;
+
+
+/* LookupType 1 */
+
+struct HB_SinglePosFormat1_
+{
+ HB_ValueRecord Value; /* ValueRecord for all covered
+ glyphs */
+};
+
+typedef struct HB_SinglePosFormat1_ HB_SinglePosFormat1;
+
+
+struct HB_SinglePosFormat2_
+{
+ HB_UShort ValueCount; /* number of ValueRecord tables */
+ HB_ValueRecord* Value; /* array of ValueRecord tables */
+};
+
+typedef struct HB_SinglePosFormat2_ HB_SinglePosFormat2;
+
+
+struct HB_SinglePos_
+{
+ HB_UShort PosFormat; /* 1 or 2 */
+ HB_Coverage Coverage; /* Coverage table */
+
+ HB_UShort ValueFormat; /* format of ValueRecord table */
+
+ union
+ {
+ HB_SinglePosFormat1 spf1;
+ HB_SinglePosFormat2 spf2;
+ } spf;
+};
+
+typedef struct HB_SinglePos_ HB_SinglePos;
+
+
+/* LookupType 2 */
+
+struct HB_PairValueRecord_
+{
+ HB_UShort SecondGlyph; /* glyph ID for second glyph */
+ HB_ValueRecord Value1; /* pos. data for first glyph */
+ HB_ValueRecord Value2; /* pos. data for second glyph */
+};
+
+typedef struct HB_PairValueRecord_ HB_PairValueRecord;
+
+
+struct HB_PairSet_
+{
+ HB_UShort PairValueCount;
+ /* number of PairValueRecord tables */
+ HB_PairValueRecord* PairValueRecord;
+ /* array of PairValueRecord tables */
+};
+
+typedef struct HB_PairSet_ HB_PairSet;
+
+
+struct HB_PairPosFormat1_
+{
+ HB_UShort PairSetCount; /* number of PairSet tables */
+ HB_PairSet* PairSet; /* array of PairSet tables */
+};
+
+typedef struct HB_PairPosFormat1_ HB_PairPosFormat1;
+
+
+struct HB_Class2Record_
+{
+ HB_ValueRecord Value1; /* pos. data for first glyph */
+ HB_ValueRecord Value2; /* pos. data for second glyph */
+};
+
+typedef struct HB_Class2Record_ HB_Class2Record;
+
+
+struct HB_Class1Record_
+{
+ HB_Class2Record* Class2Record; /* array of Class2Record tables */
+};
+
+typedef struct HB_Class1Record_ HB_Class1Record;
+
+
+struct HB_PairPosFormat2_
+{
+ HB_ClassDefinition ClassDef1; /* class def. for first glyph */
+ HB_ClassDefinition ClassDef2; /* class def. for second glyph */
+ HB_UShort Class1Count; /* number of classes in ClassDef1
+ table */
+ HB_UShort Class2Count; /* number of classes in ClassDef2
+ table */
+ HB_Class1Record* Class1Record; /* array of Class1Record tables */
+};
+
+typedef struct HB_PairPosFormat2_ HB_PairPosFormat2;
+
+
+struct HB_PairPos_
+{
+ HB_UShort PosFormat; /* 1 or 2 */
+ HB_Coverage Coverage; /* Coverage table */
+ HB_UShort ValueFormat1; /* format of ValueRecord table
+ for first glyph */
+ HB_UShort ValueFormat2; /* format of ValueRecord table
+ for second glyph */
+
+ union
+ {
+ HB_PairPosFormat1 ppf1;
+ HB_PairPosFormat2 ppf2;
+ } ppf;
+};
+
+typedef struct HB_PairPos_ HB_PairPos;
+
+
+/* LookupType 3 */
+
+struct HB_EntryExitRecord_
+{
+ HB_Anchor EntryAnchor; /* entry Anchor table */
+ HB_Anchor ExitAnchor; /* exit Anchor table */
+};
+
+
+typedef struct HB_EntryExitRecord_ HB_EntryExitRecord;
+
+struct HB_CursivePos_
+{
+ HB_UShort PosFormat; /* always 1 */
+ HB_Coverage Coverage; /* Coverage table */
+ HB_UShort EntryExitCount;
+ /* number of EntryExitRecord tables */
+ HB_EntryExitRecord* EntryExitRecord;
+ /* array of EntryExitRecord tables */
+};
+
+typedef struct HB_CursivePos_ HB_CursivePos;
+
+
+/* LookupType 4 */
+
+struct HB_BaseRecord_
+{
+ HB_Anchor* BaseAnchor; /* array of base glyph anchor
+ tables */
+};
+
+typedef struct HB_BaseRecord_ HB_BaseRecord;
+
+
+struct HB_BaseArray_
+{
+ HB_UShort BaseCount; /* number of BaseRecord tables */
+ HB_BaseRecord* BaseRecord; /* array of BaseRecord tables */
+};
+
+typedef struct HB_BaseArray_ HB_BaseArray;
+
+
+struct HB_MarkBasePos_
+{
+ HB_UShort PosFormat; /* always 1 */
+ HB_Coverage MarkCoverage; /* mark glyph coverage table */
+ HB_Coverage BaseCoverage; /* base glyph coverage table */
+ HB_UShort ClassCount; /* number of mark classes */
+ HB_MarkArray MarkArray; /* mark array table */
+ HB_BaseArray BaseArray; /* base array table */
+};
+
+typedef struct HB_MarkBasePos_ HB_MarkBasePos;
+
+
+/* LookupType 5 */
+
+struct HB_ComponentRecord_
+{
+ HB_Anchor* LigatureAnchor; /* array of ligature glyph anchor
+ tables */
+};
+
+typedef struct HB_ComponentRecord_ HB_ComponentRecord;
+
+
+struct HB_LigatureAttach_
+{
+ HB_UShort ComponentCount;
+ /* number of ComponentRecord tables */
+ HB_ComponentRecord* ComponentRecord;
+ /* array of ComponentRecord tables */
+};
+
+typedef struct HB_LigatureAttach_ HB_LigatureAttach;
+
+
+struct HB_LigatureArray_
+{
+ HB_UShort LigatureCount; /* number of LigatureAttach tables */
+ HB_LigatureAttach* LigatureAttach;
+ /* array of LigatureAttach tables */
+};
+
+typedef struct HB_LigatureArray_ HB_LigatureArray;
+
+
+struct HB_MarkLigPos_
+{
+ HB_UShort PosFormat; /* always 1 */
+ HB_Coverage MarkCoverage; /* mark glyph coverage table */
+ HB_Coverage LigatureCoverage;
+ /* ligature glyph coverage table */
+ HB_UShort ClassCount; /* number of mark classes */
+ HB_MarkArray MarkArray; /* mark array table */
+ HB_LigatureArray LigatureArray; /* ligature array table */
+};
+
+typedef struct HB_MarkLigPos_ HB_MarkLigPos;
+
+
+/* LookupType 6 */
+
+struct HB_Mark2Record_
+{
+ HB_Anchor* Mark2Anchor; /* array of mark glyph anchor
+ tables */
+};
+
+typedef struct HB_Mark2Record_ HB_Mark2Record;
+
+
+struct HB_Mark2Array_
+{
+ HB_UShort Mark2Count; /* number of Mark2Record tables */
+ HB_Mark2Record* Mark2Record; /* array of Mark2Record tables */
+};
+
+typedef struct HB_Mark2Array_ HB_Mark2Array;
+
+
+struct HB_MarkMarkPos_
+{
+ HB_UShort PosFormat; /* always 1 */
+ HB_Coverage Mark1Coverage; /* first mark glyph coverage table */
+ HB_Coverage Mark2Coverage; /* second mark glyph coverave table */
+ HB_UShort ClassCount; /* number of combining mark classes */
+ HB_MarkArray Mark1Array; /* MarkArray table for first mark */
+ HB_Mark2Array Mark2Array; /* MarkArray table for second mark */
+};
+
+typedef struct HB_MarkMarkPos_ HB_MarkMarkPos;
+
+
+/* needed by both lookup type 7 and 8 */
+
+struct HB_PosLookupRecord_
+{
+ HB_UShort SequenceIndex; /* index into current
+ glyph sequence */
+ HB_UShort LookupListIndex; /* Lookup to apply to that pos. */
+};
+
+typedef struct HB_PosLookupRecord_ HB_PosLookupRecord;
+
+
+/* LookupType 7 */
+
+struct HB_PosRule_
+{
+ HB_UShort GlyphCount; /* total number of input glyphs */
+ HB_UShort PosCount; /* number of PosLookupRecord tables */
+ HB_UShort* Input; /* array of input glyph IDs */
+ HB_PosLookupRecord* PosLookupRecord;
+ /* array of PosLookupRecord tables */
+};
+
+typedef struct HB_PosRule_ HB_PosRule;
+
+
+struct HB_PosRuleSet_
+{
+ HB_UShort PosRuleCount; /* number of PosRule tables */
+ HB_PosRule* PosRule; /* array of PosRule tables */
+};
+
+typedef struct HB_PosRuleSet_ HB_PosRuleSet;
+
+
+struct HB_ContextPosFormat1_
+{
+ HB_Coverage Coverage; /* Coverage table */
+ HB_UShort PosRuleSetCount; /* number of PosRuleSet tables */
+ HB_PosRuleSet* PosRuleSet; /* array of PosRuleSet tables */
+};
+
+typedef struct HB_ContextPosFormat1_ HB_ContextPosFormat1;
+
+
+struct HB_PosClassRule_
+{
+ HB_UShort GlyphCount; /* total number of context classes */
+ HB_UShort PosCount; /* number of PosLookupRecord tables */
+ HB_UShort* Class; /* array of classes */
+ HB_PosLookupRecord* PosLookupRecord;
+ /* array of PosLookupRecord tables */
+};
+
+typedef struct HB_PosClassRule_ HB_PosClassRule;
+
+
+struct HB_PosClassSet_
+{
+ HB_UShort PosClassRuleCount;
+ /* number of PosClassRule tables */
+ HB_PosClassRule* PosClassRule; /* array of PosClassRule tables */
+};
+
+typedef struct HB_PosClassSet_ HB_PosClassSet;
+
+
+/* The `MaxContextLength' field is not defined in the TTO specification
+ but simplifies the implementation of this format. It holds the
+ maximal context length used in the context rules. */
+
+struct HB_ContextPosFormat2_
+{
+ HB_UShort MaxContextLength;
+ /* maximal context length */
+ HB_Coverage Coverage; /* Coverage table */
+ HB_ClassDefinition ClassDef; /* ClassDef table */
+ HB_UShort PosClassSetCount;
+ /* number of PosClassSet tables */
+ HB_PosClassSet* PosClassSet; /* array of PosClassSet tables */
+};
+
+typedef struct HB_ContextPosFormat2_ HB_ContextPosFormat2;
+
+
+struct HB_ContextPosFormat3_
+{
+ HB_UShort GlyphCount; /* number of input glyphs */
+ HB_UShort PosCount; /* number of PosLookupRecord tables */
+ HB_Coverage* Coverage; /* array of Coverage tables */
+ HB_PosLookupRecord* PosLookupRecord;
+ /* array of PosLookupRecord tables */
+};
+
+typedef struct HB_ContextPosFormat3_ HB_ContextPosFormat3;
+
+
+struct HB_ContextPos_
+{
+ HB_UShort PosFormat; /* 1, 2, or 3 */
+
+ union
+ {
+ HB_ContextPosFormat1 cpf1;
+ HB_ContextPosFormat2 cpf2;
+ HB_ContextPosFormat3 cpf3;
+ } cpf;
+};
+
+typedef struct HB_ContextPos_ HB_ContextPos;
+
+
+/* LookupType 8 */
+
+struct HB_ChainPosRule_
+{
+ HB_UShort BacktrackGlyphCount;
+ /* total number of backtrack glyphs */
+ HB_UShort* Backtrack; /* array of backtrack glyph IDs */
+ HB_UShort InputGlyphCount;
+ /* total number of input glyphs */
+ HB_UShort* Input; /* array of input glyph IDs */
+ HB_UShort LookaheadGlyphCount;
+ /* total number of lookahead glyphs */
+ HB_UShort* Lookahead; /* array of lookahead glyph IDs */
+ HB_UShort PosCount; /* number of PosLookupRecords */
+ HB_PosLookupRecord* PosLookupRecord;
+ /* array of PosLookupRecords */
+};
+
+typedef struct HB_ChainPosRule_ HB_ChainPosRule;
+
+
+struct HB_ChainPosRuleSet_
+{
+ HB_UShort ChainPosRuleCount;
+ /* number of ChainPosRule tables */
+ HB_ChainPosRule* ChainPosRule; /* array of ChainPosRule tables */
+};
+
+typedef struct HB_ChainPosRuleSet_ HB_ChainPosRuleSet;
+
+
+struct HB_ChainContextPosFormat1_
+{
+ HB_Coverage Coverage; /* Coverage table */
+ HB_UShort ChainPosRuleSetCount;
+ /* number of ChainPosRuleSet tables */
+ HB_ChainPosRuleSet* ChainPosRuleSet;
+ /* array of ChainPosRuleSet tables */
+};
+
+typedef struct HB_ChainContextPosFormat1_ HB_ChainContextPosFormat1;
+
+
+struct HB_ChainPosClassRule_
+{
+ HB_UShort BacktrackGlyphCount;
+ /* total number of backtrack
+ classes */
+ HB_UShort* Backtrack; /* array of backtrack classes */
+ HB_UShort InputGlyphCount;
+ /* total number of context classes */
+ HB_UShort* Input; /* array of context classes */
+ HB_UShort LookaheadGlyphCount;
+ /* total number of lookahead
+ classes */
+ HB_UShort* Lookahead; /* array of lookahead classes */
+ HB_UShort PosCount; /* number of PosLookupRecords */
+ HB_PosLookupRecord* PosLookupRecord;
+ /* array of substitution lookups */
+};
+
+typedef struct HB_ChainPosClassRule_ HB_ChainPosClassRule;
+
+
+struct HB_ChainPosClassSet_
+{
+ HB_UShort ChainPosClassRuleCount;
+ /* number of ChainPosClassRule
+ tables */
+ HB_ChainPosClassRule* ChainPosClassRule;
+ /* array of ChainPosClassRule
+ tables */
+};
+
+typedef struct HB_ChainPosClassSet_ HB_ChainPosClassSet;
+
+
+/* The `MaxXXXLength' fields are not defined in the TTO specification
+ but simplifies the implementation of this format. It holds the
+ maximal context length used in the specific context rules. */
+
+struct HB_ChainContextPosFormat2_
+{
+ HB_Coverage Coverage; /* Coverage table */
+
+ HB_UShort MaxBacktrackLength;
+ /* maximal backtrack length */
+ HB_ClassDefinition BacktrackClassDef;
+ /* BacktrackClassDef table */
+ HB_UShort MaxInputLength;
+ /* maximal input length */
+ HB_ClassDefinition InputClassDef;
+ /* InputClassDef table */
+ HB_UShort MaxLookaheadLength;
+ /* maximal lookahead length */
+ HB_ClassDefinition LookaheadClassDef;
+ /* LookaheadClassDef table */
+
+ HB_UShort ChainPosClassSetCount;
+ /* number of ChainPosClassSet
+ tables */
+ HB_ChainPosClassSet* ChainPosClassSet;
+ /* array of ChainPosClassSet
+ tables */
+};
+
+typedef struct HB_ChainContextPosFormat2_ HB_ChainContextPosFormat2;
+
+
+struct HB_ChainContextPosFormat3_
+{
+ HB_UShort BacktrackGlyphCount;
+ /* number of backtrack glyphs */
+ HB_Coverage* BacktrackCoverage;
+ /* array of backtrack Coverage
+ tables */
+ HB_UShort InputGlyphCount;
+ /* number of input glyphs */
+ HB_Coverage* InputCoverage;
+ /* array of input coverage
+ tables */
+ HB_UShort LookaheadGlyphCount;
+ /* number of lookahead glyphs */
+ HB_Coverage* LookaheadCoverage;
+ /* array of lookahead coverage
+ tables */
+ HB_UShort PosCount; /* number of PosLookupRecords */
+ HB_PosLookupRecord* PosLookupRecord;
+ /* array of substitution lookups */
+};
+
+typedef struct HB_ChainContextPosFormat3_ HB_ChainContextPosFormat3;
+
+
+struct HB_ChainContextPos_
+{
+ HB_UShort PosFormat; /* 1, 2, or 3 */
+
+ union
+ {
+ HB_ChainContextPosFormat1 ccpf1;
+ HB_ChainContextPosFormat2 ccpf2;
+ HB_ChainContextPosFormat3 ccpf3;
+ } ccpf;
+};
+
+typedef struct HB_ChainContextPos_ HB_ChainContextPos;
+
+
+#if 0
+/* LookupType 10 */
+struct HB_ExtensionPos_
+{
+ HB_UShort PosFormat; /* always 1 */
+ HB_UShort LookuptType; /* lookup-type of referenced subtable */
+ HB_GPOS_SubTable *subtable; /* referenced subtable */
+};
+
+typedef struct HB_ExtensionPos_ HB_ExtensionPos;
+#endif
+
+
+union HB_GPOS_SubTable_
+{
+ HB_SinglePos single;
+ HB_PairPos pair;
+ HB_CursivePos cursive;
+ HB_MarkBasePos markbase;
+ HB_MarkLigPos marklig;
+ HB_MarkMarkPos markmark;
+ HB_ContextPos context;
+ HB_ChainContextPos chain;
+};
+
+typedef union HB_GPOS_SubTable_ HB_GPOS_SubTable;
+
+
+
+HB_INTERNAL HB_Error
+_HB_GPOS_Load_SubTable( HB_GPOS_SubTable* st,
+ HB_Stream stream,
+ HB_UShort lookup_type );
+
+HB_INTERNAL void
+_HB_GPOS_Free_SubTable( HB_GPOS_SubTable* st,
+ HB_UShort lookup_type );
+
+HB_END_HEADER
+
+#endif /* HARFBUZZ_GPOS_PRIVATE_H */
diff --git a/src/harfbuzz-gpos.c b/src/harfbuzz-gpos.c
new file mode 100644
index 0000000..560a291
--- /dev/null
+++ b/src/harfbuzz-gpos.c
@@ -0,0 +1,6071 @@
+/*
+ * Copyright (C) 1998-2004 David Turner and Werner Lemberg
+ * Copyright (C) 2006 Behdad Esfahbod
+ * Copyright (C) 2007 Red Hat, Inc.
+ *
+ * This is part of HarfBuzz, an OpenType Layout engine library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Red Hat Author(s): Behdad Esfahbod
+ */
+
+#include "harfbuzz-impl.h"
+#include "harfbuzz-gpos-private.h"
+#include "harfbuzz-open-private.h"
+#include "harfbuzz-gdef-private.h"
+
+struct GPOS_Instance_
+{
+ HB_GPOSHeader* gpos;
+ HB_Font font;
+ HB_Bool dvi;
+ HB_UShort load_flags; /* how the glyph should be loaded */
+ HB_Bool r2l;
+
+ HB_UShort last; /* the last valid glyph -- used
+ with cursive positioning */
+ HB_Fixed anchor_x; /* the coordinates of the anchor point */
+ HB_Fixed anchor_y; /* of the last valid glyph */
+};
+
+typedef struct GPOS_Instance_ GPOS_Instance;
+
+
+static HB_Error GPOS_Do_Glyph_Lookup( GPOS_Instance* gpi,
+ HB_UShort lookup_index,
+ HB_Buffer buffer,
+ HB_UShort context_length,
+ int nesting_level );
+
+
+
+/* the client application must replace this with something more
+ meaningful if multiple master fonts are to be supported. */
+
+static HB_Error default_mmfunc( HB_Font font,
+ HB_UShort metric_id,
+ HB_Fixed* metric_value,
+ void* data )
+{
+ HB_UNUSED(font);
+ HB_UNUSED(metric_id);
+ HB_UNUSED(metric_value);
+ HB_UNUSED(data);
+ return ERR(HB_Err_Not_Covered); /* ERR() call intended */
+}
+
+
+
+HB_Error HB_Load_GPOS_Table( HB_Font font,
+ HB_GPOSHeader** retptr,
+ hb_ot_layout_t *layout )
+{
+ HB_UInt cur_offset, new_offset, base_offset;
+
+ HB_GPOSHeader* gpos;
+
+ HB_Stream stream = font->stream;
+ HB_Error error;
+
+
+ if ( !retptr || !layout )
+ return ERR(HB_Err_Invalid_Argument);
+
+ if ( GOTO_Table( TTAG_GPOS ) )
+ return error;
+
+ base_offset = FILE_Pos();
+
+ if ( ALLOC ( gpos, sizeof( *gpos ) ) )
+ return error;
+
+ gpos->gfunc = (HB_GlyphFunction) FT_Load_Glyph;
+ gpos->mmfunc = default_mmfunc;
+
+ /* skip version */
+
+ if ( FILE_Seek( base_offset + 4L ) ||
+ ACCESS_Frame( 2L ) )
+ goto Fail4;
+
+ new_offset = GET_UShort() + base_offset;
+
+ FORGET_Frame();
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = _HB_OPEN_Load_ScriptList( &gpos->ScriptList,
+ stream ) ) != HB_Err_Ok )
+ goto Fail4;
+ (void)FILE_Seek( cur_offset );
+
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail3;
+
+ new_offset = GET_UShort() + base_offset;
+
+ FORGET_Frame();
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = _HB_OPEN_Load_FeatureList( &gpos->FeatureList,
+ stream ) ) != HB_Err_Ok )
+ goto Fail3;
+ (void)FILE_Seek( cur_offset );
+
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail2;
+
+ new_offset = GET_UShort() + base_offset;
+
+ FORGET_Frame();
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = _HB_OPEN_Load_LookupList( &gpos->LookupList,
+ stream, HB_Type_GPOS ) ) != HB_Err_Ok )
+ goto Fail2;
+
+ gpos->layout = layout; /* can be NULL */
+
+ *retptr = gpos;
+
+ return HB_Err_Ok;
+
+Fail2:
+ _HB_OPEN_Free_FeatureList( &gpos->FeatureList );
+
+Fail3:
+ _HB_OPEN_Free_ScriptList( &gpos->ScriptList );
+
+Fail4:
+ FREE( gpos );
+
+ return error;
+}
+
+
+HB_Error HB_Done_GPOS_Table( HB_GPOSHeader* gpos )
+{
+ _HB_OPEN_Free_LookupList( &gpos->LookupList, HB_Type_GPOS );
+ _HB_OPEN_Free_FeatureList( &gpos->FeatureList );
+ _HB_OPEN_Free_ScriptList( &gpos->ScriptList );
+
+ FREE( gpos );
+
+ return HB_Err_Ok;
+}
+
+
+/*****************************
+ * SubTable related functions
+ *****************************/
+
+/* shared tables */
+
+/* ValueRecord */
+
+/* There is a subtle difference in the specs between a `table' and a
+ `record' -- offsets for device tables in ValueRecords are taken from
+ the parent table and not the parent record. */
+
+static HB_Error Load_ValueRecord( HB_ValueRecord* vr,
+ HB_UShort format,
+ HB_UInt base_offset,
+ HB_Stream stream )
+{
+ HB_Error error;
+
+ HB_UInt cur_offset, new_offset;
+
+
+ if ( format & HB_GPOS_FORMAT_HAVE_X_PLACEMENT )
+ {
+ if ( ACCESS_Frame( 2L ) )
+ return error;
+
+ vr->XPlacement = GET_Short();
+
+ FORGET_Frame();
+ }
+ else
+ vr->XPlacement = 0;
+
+ if ( format & HB_GPOS_FORMAT_HAVE_Y_PLACEMENT )
+ {
+ if ( ACCESS_Frame( 2L ) )
+ return error;
+
+ vr->YPlacement = GET_Short();
+
+ FORGET_Frame();
+ }
+ else
+ vr->YPlacement = 0;
+
+ if ( format & HB_GPOS_FORMAT_HAVE_X_ADVANCE )
+ {
+ if ( ACCESS_Frame( 2L ) )
+ return error;
+
+ vr->XAdvance = GET_Short();
+
+ FORGET_Frame();
+ }
+ else
+ vr->XAdvance = 0;
+
+ if ( format & HB_GPOS_FORMAT_HAVE_Y_ADVANCE )
+ {
+ if ( ACCESS_Frame( 2L ) )
+ return error;
+
+ vr->YAdvance = GET_Short();
+
+ FORGET_Frame();
+ }
+ else
+ vr->YAdvance = 0;
+
+ if ( format & HB_GPOS_FORMAT_HAVE_X_PLACEMENT_DEVICE )
+ {
+ if ( ACCESS_Frame( 2L ) )
+ return error;
+
+ new_offset = GET_UShort();
+
+ FORGET_Frame();
+
+ if ( new_offset )
+ {
+ new_offset += base_offset;
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = _HB_OPEN_Load_Device( &vr->XPlacementDevice,
+ stream ) ) != HB_Err_Ok )
+ return error;
+ (void)FILE_Seek( cur_offset );
+ }
+ else
+ goto empty1;
+ }
+ else
+ {
+ empty1:
+ vr->XPlacementDevice.StartSize = 0;
+ vr->XPlacementDevice.EndSize = 0;
+ vr->XPlacementDevice.DeltaValue = NULL;
+ }
+
+ if ( format & HB_GPOS_FORMAT_HAVE_Y_PLACEMENT_DEVICE )
+ {
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail3;
+
+ new_offset = GET_UShort();
+
+ FORGET_Frame();
+
+ if ( new_offset )
+ {
+ new_offset += base_offset;
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = _HB_OPEN_Load_Device( &vr->YPlacementDevice,
+ stream ) ) != HB_Err_Ok )
+ goto Fail3;
+ (void)FILE_Seek( cur_offset );
+ }
+ else
+ goto empty2;
+ }
+ else
+ {
+ empty2:
+ vr->YPlacementDevice.StartSize = 0;
+ vr->YPlacementDevice.EndSize = 0;
+ vr->YPlacementDevice.DeltaValue = NULL;
+ }
+
+ if ( format & HB_GPOS_FORMAT_HAVE_X_ADVANCE_DEVICE )
+ {
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail2;
+
+ new_offset = GET_UShort();
+
+ FORGET_Frame();
+
+ if ( new_offset )
+ {
+ new_offset += base_offset;
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = _HB_OPEN_Load_Device( &vr->XAdvanceDevice,
+ stream ) ) != HB_Err_Ok )
+ goto Fail2;
+ (void)FILE_Seek( cur_offset );
+ }
+ else
+ goto empty3;
+ }
+ else
+ {
+ empty3:
+ vr->XAdvanceDevice.StartSize = 0;
+ vr->XAdvanceDevice.EndSize = 0;
+ vr->XAdvanceDevice.DeltaValue = NULL;
+ }
+
+ if ( format & HB_GPOS_FORMAT_HAVE_Y_ADVANCE_DEVICE )
+ {
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail1;
+
+ new_offset = GET_UShort();
+
+ FORGET_Frame();
+
+ if ( new_offset )
+ {
+ new_offset += base_offset;
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = _HB_OPEN_Load_Device( &vr->YAdvanceDevice,
+ stream ) ) != HB_Err_Ok )
+ goto Fail1;
+ (void)FILE_Seek( cur_offset );
+ }
+ else
+ goto empty4;
+ }
+ else
+ {
+ empty4:
+ vr->YAdvanceDevice.StartSize = 0;
+ vr->YAdvanceDevice.EndSize = 0;
+ vr->YAdvanceDevice.DeltaValue = NULL;
+ }
+
+ if ( format & HB_GPOS_FORMAT_HAVE_X_ID_PLACEMENT )
+ {
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail1;
+
+ vr->XIdPlacement = GET_UShort();
+
+ FORGET_Frame();
+ }
+ else
+ vr->XIdPlacement = 0;
+
+ if ( format & HB_GPOS_FORMAT_HAVE_Y_ID_PLACEMENT )
+ {
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail1;
+
+ vr->YIdPlacement = GET_UShort();
+
+ FORGET_Frame();
+ }
+ else
+ vr->YIdPlacement = 0;
+
+ if ( format & HB_GPOS_FORMAT_HAVE_X_ID_ADVANCE )
+ {
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail1;
+
+ vr->XIdAdvance = GET_UShort();
+
+ FORGET_Frame();
+ }
+ else
+ vr->XIdAdvance = 0;
+
+ if ( format & HB_GPOS_FORMAT_HAVE_Y_ID_ADVANCE )
+ {
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail1;
+
+ vr->YIdAdvance = GET_UShort();
+
+ FORGET_Frame();
+ }
+ else
+ vr->YIdAdvance = 0;
+
+ return HB_Err_Ok;
+
+Fail1:
+ _HB_OPEN_Free_Device( &vr->YAdvanceDevice );
+
+Fail2:
+ _HB_OPEN_Free_Device( &vr->XAdvanceDevice );
+
+Fail3:
+ _HB_OPEN_Free_Device( &vr->YPlacementDevice );
+ return error;
+}
+
+
+static void Free_ValueRecord( HB_ValueRecord* vr,
+ HB_UShort format )
+{
+ if ( format & HB_GPOS_FORMAT_HAVE_Y_ADVANCE_DEVICE )
+ _HB_OPEN_Free_Device( &vr->YAdvanceDevice );
+ if ( format & HB_GPOS_FORMAT_HAVE_X_ADVANCE_DEVICE )
+ _HB_OPEN_Free_Device( &vr->XAdvanceDevice );
+ if ( format & HB_GPOS_FORMAT_HAVE_Y_PLACEMENT_DEVICE )
+ _HB_OPEN_Free_Device( &vr->YPlacementDevice );
+ if ( format & HB_GPOS_FORMAT_HAVE_X_PLACEMENT_DEVICE )
+ _HB_OPEN_Free_Device( &vr->XPlacementDevice );
+}
+
+
+static HB_Error Get_ValueRecord( GPOS_Instance* gpi,
+ HB_ValueRecord* vr,
+ HB_UShort format,
+ HB_Position gd )
+{
+ HB_Fixed value;
+ HB_Short pixel_value;
+ HB_Error error = HB_Err_Ok;
+ HB_GPOSHeader* gpos = gpi->gpos;
+
+ HB_UShort x_ppem, y_ppem;
+ HB_16Dot16 x_scale, y_scale;
+
+
+ if ( !format )
+ return HB_Err_Ok;
+
+ x_ppem = gpi->font->size->metrics.x_ppem;
+ y_ppem = gpi->font->size->metrics.y_ppem;
+ x_scale = gpi->font->size->metrics.x_scale;
+ y_scale = gpi->font->size->metrics.y_scale;
+
+ /* design units -> fractional pixel */
+
+ if ( format & HB_GPOS_FORMAT_HAVE_X_PLACEMENT )
+ gd->x_pos += x_scale * vr->XPlacement / 0x10000;
+ if ( format & HB_GPOS_FORMAT_HAVE_Y_PLACEMENT )
+ gd->y_pos += y_scale * vr->YPlacement / 0x10000;
+ if ( format & HB_GPOS_FORMAT_HAVE_X_ADVANCE )
+ gd->x_advance += x_scale * vr->XAdvance / 0x10000;
+ if ( format & HB_GPOS_FORMAT_HAVE_Y_ADVANCE )
+ gd->y_advance += y_scale * vr->YAdvance / 0x10000;
+
+ if ( !gpi->dvi )
+ {
+ /* pixel -> fractional pixel */
+
+ if ( format & HB_GPOS_FORMAT_HAVE_X_PLACEMENT_DEVICE )
+ {
+ _HB_OPEN_Get_Device( &vr->XPlacementDevice, x_ppem, &pixel_value );
+ gd->x_pos += pixel_value << 6;
+ }
+ if ( format & HB_GPOS_FORMAT_HAVE_Y_PLACEMENT_DEVICE )
+ {
+ _HB_OPEN_Get_Device( &vr->YPlacementDevice, y_ppem, &pixel_value );
+ gd->y_pos += pixel_value << 6;
+ }
+ if ( format & HB_GPOS_FORMAT_HAVE_X_ADVANCE_DEVICE )
+ {
+ _HB_OPEN_Get_Device( &vr->XAdvanceDevice, x_ppem, &pixel_value );
+ gd->x_advance += pixel_value << 6;
+ }
+ if ( format & HB_GPOS_FORMAT_HAVE_Y_ADVANCE_DEVICE )
+ {
+ _HB_OPEN_Get_Device( &vr->YAdvanceDevice, y_ppem, &pixel_value );
+ gd->y_advance += pixel_value << 6;
+ }
+ }
+
+ /* values returned from mmfunc() are already in fractional pixels */
+
+ if ( format & HB_GPOS_FORMAT_HAVE_X_ID_PLACEMENT )
+ {
+ error = (gpos->mmfunc)( gpi->font, vr->XIdPlacement,
+ &value, gpos->data );
+ if ( error )
+ return error;
+ gd->x_pos += value;
+ }
+ if ( format & HB_GPOS_FORMAT_HAVE_Y_ID_PLACEMENT )
+ {
+ error = (gpos->mmfunc)( gpi->font, vr->YIdPlacement,
+ &value, gpos->data );
+ if ( error )
+ return error;
+ gd->y_pos += value;
+ }
+ if ( format & HB_GPOS_FORMAT_HAVE_X_ID_ADVANCE )
+ {
+ error = (gpos->mmfunc)( gpi->font, vr->XIdAdvance,
+ &value, gpos->data );
+ if ( error )
+ return error;
+ gd->x_advance += value;
+ }
+ if ( format & HB_GPOS_FORMAT_HAVE_Y_ID_ADVANCE )
+ {
+ error = (gpos->mmfunc)( gpi->font, vr->YIdAdvance,
+ &value, gpos->data );
+ if ( error )
+ return error;
+ gd->y_advance += value;
+ }
+
+ return error;
+}
+
+
+/* AnchorFormat1 */
+/* AnchorFormat2 */
+/* AnchorFormat3 */
+/* AnchorFormat4 */
+
+static HB_Error Load_Anchor( HB_Anchor* an,
+ HB_Stream stream )
+{
+ HB_Error error;
+
+ HB_UInt cur_offset, new_offset, base_offset;
+
+
+ base_offset = FILE_Pos();
+
+ if ( ACCESS_Frame( 2L ) )
+ return error;
+
+ an->PosFormat = GET_UShort();
+
+ FORGET_Frame();
+
+ switch ( an->PosFormat )
+ {
+ case 1:
+ if ( ACCESS_Frame( 4L ) )
+ return error;
+
+ an->af.af1.XCoordinate = GET_Short();
+ an->af.af1.YCoordinate = GET_Short();
+
+ FORGET_Frame();
+ break;
+
+ case 2:
+ if ( ACCESS_Frame( 6L ) )
+ return error;
+
+ an->af.af2.XCoordinate = GET_Short();
+ an->af.af2.YCoordinate = GET_Short();
+ an->af.af2.AnchorPoint = GET_UShort();
+
+ FORGET_Frame();
+ break;
+
+ case 3:
+ if ( ACCESS_Frame( 6L ) )
+ return error;
+
+ an->af.af3.XCoordinate = GET_Short();
+ an->af.af3.YCoordinate = GET_Short();
+
+ new_offset = GET_UShort();
+
+ FORGET_Frame();
+
+ if ( new_offset )
+ {
+ new_offset += base_offset;
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = _HB_OPEN_Load_Device( &an->af.af3.XDeviceTable,
+ stream ) ) != HB_Err_Ok )
+ return error;
+ (void)FILE_Seek( cur_offset );
+ }
+ else
+ {
+ an->af.af3.XDeviceTable.StartSize = 0;
+ an->af.af3.XDeviceTable.EndSize = 0;
+ an->af.af3.XDeviceTable.DeltaValue = NULL;
+ }
+
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail;
+
+ new_offset = GET_UShort();
+
+ FORGET_Frame();
+
+ if ( new_offset )
+ {
+ new_offset += base_offset;
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = _HB_OPEN_Load_Device( &an->af.af3.YDeviceTable,
+ stream ) ) != HB_Err_Ok )
+ goto Fail;
+ (void)FILE_Seek( cur_offset );
+ }
+ else
+ {
+ an->af.af3.YDeviceTable.StartSize = 0;
+ an->af.af3.YDeviceTable.EndSize = 0;
+ an->af.af3.YDeviceTable.DeltaValue = NULL;
+ }
+ break;
+
+ case 4:
+ if ( ACCESS_Frame( 4L ) )
+ return error;
+
+ an->af.af4.XIdAnchor = GET_UShort();
+ an->af.af4.YIdAnchor = GET_UShort();
+
+ FORGET_Frame();
+ break;
+
+ default:
+ return ERR(HB_Err_Invalid_SubTable_Format);
+ }
+
+ return HB_Err_Ok;
+
+Fail:
+ _HB_OPEN_Free_Device( &an->af.af3.XDeviceTable );
+ return error;
+}
+
+
+static void Free_Anchor( HB_Anchor* an )
+{
+ if ( an->PosFormat == 3 )
+ {
+ _HB_OPEN_Free_Device( &an->af.af3.YDeviceTable );
+ _HB_OPEN_Free_Device( &an->af.af3.XDeviceTable );
+ }
+}
+
+
+static HB_Error Get_Anchor( GPOS_Instance* gpi,
+ HB_Anchor* an,
+ HB_UShort glyph_index,
+ HB_Fixed* x_value,
+ HB_Fixed* y_value )
+{
+ HB_Error error = HB_Err_Ok;
+
+ FT_Outline outline;
+ HB_GPOSHeader* gpos = gpi->gpos;
+ HB_UShort ap;
+
+ HB_Short pixel_value;
+ HB_UShort load_flags;
+
+ HB_UShort x_ppem, y_ppem;
+ HB_16Dot16 x_scale, y_scale;
+
+
+ x_ppem = gpi->font->size->metrics.x_ppem;
+ y_ppem = gpi->font->size->metrics.y_ppem;
+ x_scale = gpi->font->size->metrics.x_scale;
+ y_scale = gpi->font->size->metrics.y_scale;
+
+ switch ( an->PosFormat )
+ {
+ case 0:
+ /* The special case of an empty AnchorTable */
+ default:
+
+ return HB_Err_Not_Covered;
+
+ case 1:
+ *x_value = x_scale * an->af.af1.XCoordinate / 0x10000;
+ *y_value = y_scale * an->af.af1.YCoordinate / 0x10000;
+ break;
+
+ case 2:
+ /* glyphs must be scaled */
+
+ load_flags = gpi->load_flags & ~FT_LOAD_NO_SCALE;
+
+ if ( !gpi->dvi )
+ {
+ error = (gpos->gfunc)( gpi->font, glyph_index, load_flags );
+ if ( error )
+ return error;
+
+ if ( gpi->font->glyph->format != ft_glyph_format_outline )
+ return ERR(HB_Err_Invalid_SubTable);
+
+ ap = an->af.af2.AnchorPoint;
+
+ outline = gpi->font->glyph->outline;
+
+ /* if n_points is set to zero, we use the design coordinate value pair.
+ * This can happen e.g. for sbit glyphs. */
+ if ( !outline.n_points )
+ goto no_contour_point;
+
+ if ( ap >= outline.n_points )
+ return ERR(HB_Err_Invalid_SubTable);
+
+ *x_value = outline.points[ap].x;
+ *y_value = outline.points[ap].y;
+ }
+ else
+ {
+ no_contour_point:
+ *x_value = x_scale * an->af.af3.XCoordinate / 0x10000;
+ *y_value = y_scale * an->af.af3.YCoordinate / 0x10000;
+ }
+ break;
+
+ case 3:
+ if ( !gpi->dvi )
+ {
+ _HB_OPEN_Get_Device( &an->af.af3.XDeviceTable, x_ppem, &pixel_value );
+ *x_value = pixel_value << 6;
+ _HB_OPEN_Get_Device( &an->af.af3.YDeviceTable, y_ppem, &pixel_value );
+ *y_value = pixel_value << 6;
+ }
+ else
+ *x_value = *y_value = 0;
+
+ *x_value += x_scale * an->af.af3.XCoordinate / 0x10000;
+ *y_value += y_scale * an->af.af3.YCoordinate / 0x10000;
+ break;
+
+ case 4:
+ error = (gpos->mmfunc)( gpi->font, an->af.af4.XIdAnchor,
+ x_value, gpos->data );
+ if ( error )
+ return error;
+
+ error = (gpos->mmfunc)( gpi->font, an->af.af4.YIdAnchor,
+ y_value, gpos->data );
+ if ( error )
+ return error;
+ break;
+ }
+
+ return error;
+}
+
+
+/* MarkArray */
+
+static HB_Error Load_MarkArray ( HB_MarkArray* ma,
+ HB_Stream stream )
+{
+ HB_Error error;
+
+ HB_UShort n, m, count;
+ HB_UInt cur_offset, new_offset, base_offset;
+
+ HB_MarkRecord* mr;
+
+
+ base_offset = FILE_Pos();
+
+ if ( ACCESS_Frame( 2L ) )
+ return error;
+
+ count = ma->MarkCount = GET_UShort();
+
+ FORGET_Frame();
+
+ ma->MarkRecord = NULL;
+
+ if ( ALLOC_ARRAY( ma->MarkRecord, count, HB_MarkRecord ) )
+ return error;
+
+ mr = ma->MarkRecord;
+
+ for ( n = 0; n < count; n++ )
+ {
+ if ( ACCESS_Frame( 4L ) )
+ goto Fail;
+
+ mr[n].Class = GET_UShort();
+ new_offset = GET_UShort() + base_offset;
+
+ FORGET_Frame();
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = Load_Anchor( &mr[n].MarkAnchor, stream ) ) != HB_Err_Ok )
+ goto Fail;
+ (void)FILE_Seek( cur_offset );
+ }
+
+ return HB_Err_Ok;
+
+Fail:
+ for ( m = 0; m < n; m++ )
+ Free_Anchor( &mr[m].MarkAnchor );
+
+ FREE( mr );
+ return error;
+}
+
+
+static void Free_MarkArray( HB_MarkArray* ma )
+{
+ HB_UShort n, count;
+
+ HB_MarkRecord* mr;
+
+
+ if ( ma->MarkRecord )
+ {
+ count = ma->MarkCount;
+ mr = ma->MarkRecord;
+
+ for ( n = 0; n < count; n++ )
+ Free_Anchor( &mr[n].MarkAnchor );
+
+ FREE( mr );
+ }
+}
+
+
+/* LookupType 1 */
+
+/* SinglePosFormat1 */
+/* SinglePosFormat2 */
+
+static HB_Error Load_SinglePos( HB_GPOS_SubTable* st,
+ HB_Stream stream )
+{
+ HB_Error error;
+ HB_SinglePos* sp = &st->single;
+
+ HB_UShort n, m, count, format;
+ HB_UInt cur_offset, new_offset, base_offset;
+
+ HB_ValueRecord* vr;
+
+
+ base_offset = FILE_Pos();
+
+ if ( ACCESS_Frame( 6L ) )
+ return error;
+
+ sp->PosFormat = GET_UShort();
+ new_offset = GET_UShort() + base_offset;
+
+ format = sp->ValueFormat = GET_UShort();
+
+ FORGET_Frame();
+
+ if ( !format )
+ return ERR(HB_Err_Invalid_SubTable);
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = _HB_OPEN_Load_Coverage( &sp->Coverage, stream ) ) != HB_Err_Ok )
+ return error;
+ (void)FILE_Seek( cur_offset );
+
+ switch ( sp->PosFormat )
+ {
+ case 1:
+ error = Load_ValueRecord( &sp->spf.spf1.Value, format,
+ base_offset, stream );
+ if ( error )
+ goto Fail2;
+ break;
+
+ case 2:
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail2;
+
+ count = sp->spf.spf2.ValueCount = GET_UShort();
+
+ FORGET_Frame();
+
+ sp->spf.spf2.Value = NULL;
+
+ if ( ALLOC_ARRAY( sp->spf.spf2.Value, count, HB_ValueRecord ) )
+ goto Fail2;
+
+ vr = sp->spf.spf2.Value;
+
+ for ( n = 0; n < count; n++ )
+ {
+ error = Load_ValueRecord( &vr[n], format, base_offset, stream );
+ if ( error )
+ goto Fail1;
+ }
+ break;
+
+ default:
+ return ERR(HB_Err_Invalid_SubTable_Format);
+ }
+
+ return HB_Err_Ok;
+
+Fail1:
+ for ( m = 0; m < n; m++ )
+ Free_ValueRecord( &vr[m], format );
+
+ FREE( vr );
+
+Fail2:
+ _HB_OPEN_Free_Coverage( &sp->Coverage );
+ return error;
+}
+
+
+static void Free_SinglePos( HB_GPOS_SubTable* st )
+{
+ HB_UShort n, count, format;
+ HB_SinglePos* sp = &st->single;
+
+ HB_ValueRecord* v;
+
+
+ format = sp->ValueFormat;
+
+ switch ( sp->PosFormat )
+ {
+ case 1:
+ Free_ValueRecord( &sp->spf.spf1.Value, format );
+ break;
+
+ case 2:
+ if ( sp->spf.spf2.Value )
+ {
+ count = sp->spf.spf2.ValueCount;
+ v = sp->spf.spf2.Value;
+
+ for ( n = 0; n < count; n++ )
+ Free_ValueRecord( &v[n], format );
+
+ FREE( v );
+ }
+ break;
+ default:
+ break;
+ }
+
+ _HB_OPEN_Free_Coverage( &sp->Coverage );
+}
+
+static HB_Error Lookup_SinglePos( GPOS_Instance* gpi,
+ HB_GPOS_SubTable* st,
+ HB_Buffer buffer,
+ HB_UShort flags,
+ HB_UShort context_length,
+ int nesting_level )
+{
+ HB_UShort index, property;
+ HB_Error error;
+ HB_GPOSHeader* gpos = gpi->gpos;
+ HB_SinglePos* sp = &st->single;
+
+ HB_UNUSED(nesting_level);
+
+ if ( context_length != 0xFFFF && context_length < 1 )
+ return HB_Err_Not_Covered;
+
+ if ( CHECK_Property( gpos->layout, IN_CURITEM(), flags, &property ) )
+ return error;
+
+ error = _HB_OPEN_Coverage_Index( &sp->Coverage, IN_CURGLYPH(), &index );
+ if ( error )
+ return error;
+
+ switch ( sp->PosFormat )
+ {
+ case 1:
+ error = Get_ValueRecord( gpi, &sp->spf.spf1.Value,
+ sp->ValueFormat, POSITION( buffer->in_pos ) );
+ if ( error )
+ return error;
+ break;
+
+ case 2:
+ if ( index >= sp->spf.spf2.ValueCount )
+ return ERR(HB_Err_Invalid_SubTable);
+ error = Get_ValueRecord( gpi, &sp->spf.spf2.Value[index],
+ sp->ValueFormat, POSITION( buffer->in_pos ) );
+ if ( error )
+ return error;
+ break;
+
+ default:
+ return ERR(HB_Err_Invalid_SubTable);
+ }
+
+ (buffer->in_pos)++;
+
+ return HB_Err_Ok;
+}
+
+
+/* LookupType 2 */
+
+/* PairSet */
+
+static HB_Error Load_PairSet ( HB_PairSet* ps,
+ HB_UShort format1,
+ HB_UShort format2,
+ HB_Stream stream )
+{
+ HB_Error error;
+
+ HB_UShort n, m, count;
+ HB_UInt base_offset;
+
+ HB_PairValueRecord* pvr;
+
+
+ base_offset = FILE_Pos();
+
+ if ( ACCESS_Frame( 2L ) )
+ return error;
+
+ count = ps->PairValueCount = GET_UShort();
+
+ FORGET_Frame();
+
+ ps->PairValueRecord = NULL;
+
+ if ( ALLOC_ARRAY( ps->PairValueRecord, count, HB_PairValueRecord ) )
+ return error;
+
+ pvr = ps->PairValueRecord;
+
+ for ( n = 0; n < count; n++ )
+ {
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail;
+
+ pvr[n].SecondGlyph = GET_UShort();
+
+ FORGET_Frame();
+
+ if ( format1 )
+ {
+ error = Load_ValueRecord( &pvr[n].Value1, format1,
+ base_offset, stream );
+ if ( error )
+ goto Fail;
+ }
+ if ( format2 )
+ {
+ error = Load_ValueRecord( &pvr[n].Value2, format2,
+ base_offset, stream );
+ if ( error )
+ {
+ if ( format1 )
+ Free_ValueRecord( &pvr[n].Value1, format1 );
+ goto Fail;
+ }
+ }
+ }
+
+ return HB_Err_Ok;
+
+Fail:
+ for ( m = 0; m < n; m++ )
+ {
+ if ( format1 )
+ Free_ValueRecord( &pvr[m].Value1, format1 );
+ if ( format2 )
+ Free_ValueRecord( &pvr[m].Value2, format2 );
+ }
+
+ FREE( pvr );
+ return error;
+}
+
+
+static void Free_PairSet( HB_PairSet* ps,
+ HB_UShort format1,
+ HB_UShort format2 )
+{
+ HB_UShort n, count;
+
+ HB_PairValueRecord* pvr;
+
+
+ if ( ps->PairValueRecord )
+ {
+ count = ps->PairValueCount;
+ pvr = ps->PairValueRecord;
+
+ for ( n = 0; n < count; n++ )
+ {
+ if ( format1 )
+ Free_ValueRecord( &pvr[n].Value1, format1 );
+ if ( format2 )
+ Free_ValueRecord( &pvr[n].Value2, format2 );
+ }
+
+ FREE( pvr );
+ }
+}
+
+
+/* PairPosFormat1 */
+
+static HB_Error Load_PairPos1( HB_PairPosFormat1* ppf1,
+ HB_UShort format1,
+ HB_UShort format2,
+ HB_Stream stream )
+{
+ HB_Error error;
+
+ HB_UShort n, m, count;
+ HB_UInt cur_offset, new_offset, base_offset;
+
+ HB_PairSet* ps;
+
+
+ base_offset = FILE_Pos() - 8L;
+
+ if ( ACCESS_Frame( 2L ) )
+ return error;
+
+ count = ppf1->PairSetCount = GET_UShort();
+
+ FORGET_Frame();
+
+ ppf1->PairSet = NULL;
+
+ if ( ALLOC_ARRAY( ppf1->PairSet, count, HB_PairSet ) )
+ return error;
+
+ ps = ppf1->PairSet;
+
+ for ( n = 0; n < count; n++ )
+ {
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail;
+
+ new_offset = GET_UShort() + base_offset;
+
+ FORGET_Frame();
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = Load_PairSet( &ps[n], format1,
+ format2, stream ) ) != HB_Err_Ok )
+ goto Fail;
+ (void)FILE_Seek( cur_offset );
+ }
+
+ return HB_Err_Ok;
+
+Fail:
+ for ( m = 0; m < n; m++ )
+ Free_PairSet( &ps[m], format1, format2 );
+
+ FREE( ps );
+ return error;
+}
+
+
+static void Free_PairPos1( HB_PairPosFormat1* ppf1,
+ HB_UShort format1,
+ HB_UShort format2 )
+{
+ HB_UShort n, count;
+
+ HB_PairSet* ps;
+
+
+ if ( ppf1->PairSet )
+ {
+ count = ppf1->PairSetCount;
+ ps = ppf1->PairSet;
+
+ for ( n = 0; n < count; n++ )
+ Free_PairSet( &ps[n], format1, format2 );
+
+ FREE( ps );
+ }
+}
+
+
+/* PairPosFormat2 */
+
+static HB_Error Load_PairPos2( HB_PairPosFormat2* ppf2,
+ HB_UShort format1,
+ HB_UShort format2,
+ HB_Stream stream )
+{
+ HB_Error error;
+
+ HB_UShort m, n, k, count1, count2;
+ HB_UInt cur_offset, new_offset1, new_offset2, base_offset;
+
+ HB_Class1Record* c1r;
+ HB_Class2Record* c2r;
+
+
+ base_offset = FILE_Pos() - 8L;
+
+ if ( ACCESS_Frame( 8L ) )
+ return error;
+
+ new_offset1 = GET_UShort() + base_offset;
+ new_offset2 = GET_UShort() + base_offset;
+
+ /* `Class1Count' and `Class2Count' are the upper limits for class
+ values, thus we read it now to make additional safety checks. */
+
+ count1 = ppf2->Class1Count = GET_UShort();
+ count2 = ppf2->Class2Count = GET_UShort();
+
+ FORGET_Frame();
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset1 ) ||
+ ( error = _HB_OPEN_Load_ClassDefinition( &ppf2->ClassDef1, count1,
+ stream ) ) != HB_Err_Ok )
+ return error;
+ if ( FILE_Seek( new_offset2 ) ||
+ ( error = _HB_OPEN_Load_ClassDefinition( &ppf2->ClassDef2, count2,
+ stream ) ) != HB_Err_Ok )
+ goto Fail3;
+ (void)FILE_Seek( cur_offset );
+
+ ppf2->Class1Record = NULL;
+
+ if ( ALLOC_ARRAY( ppf2->Class1Record, count1, HB_Class1Record ) )
+ goto Fail2;
+
+ c1r = ppf2->Class1Record;
+
+ for ( m = 0; m < count1; m++ )
+ {
+ c1r[m].Class2Record = NULL;
+
+ if ( ALLOC_ARRAY( c1r[m].Class2Record, count2, HB_Class2Record ) )
+ goto Fail1;
+
+ c2r = c1r[m].Class2Record;
+
+ for ( n = 0; n < count2; n++ )
+ {
+ if ( format1 )
+ {
+ error = Load_ValueRecord( &c2r[n].Value1, format1,
+ base_offset, stream );
+ if ( error )
+ goto Fail0;
+ }
+ if ( format2 )
+ {
+ error = Load_ValueRecord( &c2r[n].Value2, format2,
+ base_offset, stream );
+ if ( error )
+ {
+ if ( format1 )
+ Free_ValueRecord( &c2r[n].Value1, format1 );
+ goto Fail0;
+ }
+ }
+ }
+
+ continue;
+
+ Fail0:
+ for ( k = 0; k < n; k++ )
+ {
+ if ( format1 )
+ Free_ValueRecord( &c2r[k].Value1, format1 );
+ if ( format2 )
+ Free_ValueRecord( &c2r[k].Value2, format2 );
+ }
+ goto Fail1;
+ }
+
+ return HB_Err_Ok;
+
+Fail1:
+ for ( k = 0; k < m; k++ )
+ {
+ c2r = c1r[k].Class2Record;
+
+ for ( n = 0; n < count2; n++ )
+ {
+ if ( format1 )
+ Free_ValueRecord( &c2r[n].Value1, format1 );
+ if ( format2 )
+ Free_ValueRecord( &c2r[n].Value2, format2 );
+ }
+
+ FREE( c2r );
+ }
+
+ FREE( c1r );
+Fail2:
+
+ _HB_OPEN_Free_ClassDefinition( &ppf2->ClassDef2 );
+
+Fail3:
+ _HB_OPEN_Free_ClassDefinition( &ppf2->ClassDef1 );
+ return error;
+}
+
+
+static void Free_PairPos2( HB_PairPosFormat2* ppf2,
+ HB_UShort format1,
+ HB_UShort format2 )
+{
+ HB_UShort m, n, count1, count2;
+
+ HB_Class1Record* c1r;
+ HB_Class2Record* c2r;
+
+
+ if ( ppf2->Class1Record )
+ {
+ c1r = ppf2->Class1Record;
+ count1 = ppf2->Class1Count;
+ count2 = ppf2->Class2Count;
+
+ for ( m = 0; m < count1; m++ )
+ {
+ c2r = c1r[m].Class2Record;
+
+ for ( n = 0; n < count2; n++ )
+ {
+ if ( format1 )
+ Free_ValueRecord( &c2r[n].Value1, format1 );
+ if ( format2 )
+ Free_ValueRecord( &c2r[n].Value2, format2 );
+ }
+
+ FREE( c2r );
+ }
+
+ FREE( c1r );
+
+ _HB_OPEN_Free_ClassDefinition( &ppf2->ClassDef2 );
+ _HB_OPEN_Free_ClassDefinition( &ppf2->ClassDef1 );
+ }
+}
+
+
+static HB_Error Load_PairPos( HB_GPOS_SubTable* st,
+ HB_Stream stream )
+{
+ HB_Error error;
+ HB_PairPos* pp = &st->pair;
+
+ HB_UShort format1, format2;
+ HB_UInt cur_offset, new_offset, base_offset;
+
+
+ base_offset = FILE_Pos();
+
+ if ( ACCESS_Frame( 8L ) )
+ return error;
+
+ pp->PosFormat = GET_UShort();
+ new_offset = GET_UShort() + base_offset;
+
+ format1 = pp->ValueFormat1 = GET_UShort();
+ format2 = pp->ValueFormat2 = GET_UShort();
+
+ FORGET_Frame();
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = _HB_OPEN_Load_Coverage( &pp->Coverage, stream ) ) != HB_Err_Ok )
+ return error;
+ (void)FILE_Seek( cur_offset );
+
+ switch ( pp->PosFormat )
+ {
+ case 1:
+ error = Load_PairPos1( &pp->ppf.ppf1, format1, format2, stream );
+ if ( error )
+ goto Fail;
+ break;
+
+ case 2:
+ error = Load_PairPos2( &pp->ppf.ppf2, format1, format2, stream );
+ if ( error )
+ goto Fail;
+ break;
+
+ default:
+ return ERR(HB_Err_Invalid_SubTable_Format);
+ }
+
+ return HB_Err_Ok;
+
+Fail:
+ _HB_OPEN_Free_Coverage( &pp->Coverage );
+ return error;
+}
+
+
+static void Free_PairPos( HB_GPOS_SubTable* st )
+{
+ HB_UShort format1, format2;
+ HB_PairPos* pp = &st->pair;
+
+
+ format1 = pp->ValueFormat1;
+ format2 = pp->ValueFormat2;
+
+ switch ( pp->PosFormat )
+ {
+ case 1:
+ Free_PairPos1( &pp->ppf.ppf1, format1, format2 );
+ break;
+
+ case 2:
+ Free_PairPos2( &pp->ppf.ppf2, format1, format2 );
+ break;
+
+ default:
+ break;
+ }
+
+ _HB_OPEN_Free_Coverage( &pp->Coverage );
+}
+
+
+static HB_Error Lookup_PairPos1( GPOS_Instance* gpi,
+ HB_PairPosFormat1* ppf1,
+ HB_Buffer buffer,
+ HB_UInt first_pos,
+ HB_UShort index,
+ HB_UShort format1,
+ HB_UShort format2 )
+{
+ HB_Error error;
+ HB_UShort numpvr, glyph2;
+
+ HB_PairValueRecord* pvr;
+
+
+ if ( index >= ppf1->PairSetCount )
+ return ERR(HB_Err_Invalid_SubTable);
+
+ pvr = ppf1->PairSet[index].PairValueRecord;
+ if ( !pvr )
+ return ERR(HB_Err_Invalid_SubTable);
+
+ glyph2 = IN_CURGLYPH();
+
+ for ( numpvr = ppf1->PairSet[index].PairValueCount;
+ numpvr;
+ numpvr--, pvr++ )
+ {
+ if ( glyph2 == pvr->SecondGlyph )
+ {
+ error = Get_ValueRecord( gpi, &pvr->Value1, format1,
+ POSITION( first_pos ) );
+ if ( error )
+ return error;
+ return Get_ValueRecord( gpi, &pvr->Value2, format2,
+ POSITION( buffer->in_pos ) );
+ }
+ }
+
+ return HB_Err_Not_Covered;
+}
+
+
+static HB_Error Lookup_PairPos2( GPOS_Instance* gpi,
+ HB_PairPosFormat2* ppf2,
+ HB_Buffer buffer,
+ HB_UInt first_pos,
+ HB_UShort format1,
+ HB_UShort format2 )
+{
+ HB_Error error;
+ HB_UShort cl1 = 0, cl2 = 0; /* shut compiler up */
+
+ HB_Class1Record* c1r;
+ HB_Class2Record* c2r;
+
+
+ error = _HB_OPEN_Get_Class( &ppf2->ClassDef1, IN_GLYPH( first_pos ),
+ &cl1, NULL );
+ if ( error && error != HB_Err_Not_Covered )
+ return error;
+ error = _HB_OPEN_Get_Class( &ppf2->ClassDef2, IN_CURGLYPH(),
+ &cl2, NULL );
+ if ( error && error != HB_Err_Not_Covered )
+ return error;
+
+ c1r = &ppf2->Class1Record[cl1];
+ if ( !c1r )
+ return ERR(HB_Err_Invalid_SubTable);
+ c2r = &c1r->Class2Record[cl2];
+
+ error = Get_ValueRecord( gpi, &c2r->Value1, format1, POSITION( first_pos ) );
+ if ( error )
+ return error;
+ return Get_ValueRecord( gpi, &c2r->Value2, format2, POSITION( buffer->in_pos ) );
+}
+
+
+static HB_Error Lookup_PairPos( GPOS_Instance* gpi,
+ HB_GPOS_SubTable* st,
+ HB_Buffer buffer,
+ HB_UShort flags,
+ HB_UShort context_length,
+ int nesting_level )
+{
+ HB_Error error;
+ HB_UShort index, property;
+ HB_UInt first_pos;
+ HB_GPOSHeader* gpos = gpi->gpos;
+ HB_PairPos* pp = &st->pair;
+
+ HB_UNUSED(nesting_level);
+
+ if ( buffer->in_pos >= buffer->in_length - 1 )
+ return HB_Err_Not_Covered; /* Not enough glyphs in stream */
+
+ if ( context_length != 0xFFFF && context_length < 2 )
+ return HB_Err_Not_Covered;
+
+ if ( CHECK_Property( gpos->layout, IN_CURITEM(), flags, &property ) )
+ return error;
+
+ error = _HB_OPEN_Coverage_Index( &pp->Coverage, IN_CURGLYPH(), &index );
+ if ( error )
+ return error;
+
+ /* second glyph */
+
+ first_pos = buffer->in_pos;
+ (buffer->in_pos)++;
+
+ while ( CHECK_Property( gpos->layout, IN_CURITEM(),
+ flags, &property ) )
+ {
+ if ( error && error != HB_Err_Not_Covered )
+ return error;
+
+ if ( buffer->in_pos == buffer->in_length )
+ {
+ buffer->in_pos = first_pos;
+ return HB_Err_Not_Covered;
+ }
+ (buffer->in_pos)++;
+
+ }
+
+ switch ( pp->PosFormat )
+ {
+ case 1:
+ error = Lookup_PairPos1( gpi, &pp->ppf.ppf1, buffer,
+ first_pos, index,
+ pp->ValueFormat1, pp->ValueFormat2 );
+ break;
+
+ case 2:
+ error = Lookup_PairPos2( gpi, &pp->ppf.ppf2, buffer, first_pos,
+ pp->ValueFormat1, pp->ValueFormat2 );
+ break;
+
+ default:
+ return ERR(HB_Err_Invalid_SubTable_Format);
+ }
+
+ /* if we don't have coverage for the second glyph don't skip it for
+ further lookups but reset in_pos back to the first_glyph and let
+ the caller in Do_String_Lookup increment in_pos */
+ if ( error == HB_Err_Not_Covered )
+ buffer->in_pos = first_pos;
+
+ /* adjusting the `next' glyph */
+
+ if ( pp->ValueFormat2 )
+ (buffer->in_pos)++;
+
+ return error;
+}
+
+
+/* LookupType 3 */
+
+/* CursivePosFormat1 */
+
+static HB_Error Load_CursivePos( HB_GPOS_SubTable* st,
+ HB_Stream stream )
+{
+ HB_Error error;
+ HB_CursivePos* cp = &st->cursive;
+
+ HB_UShort n, m, count;
+ HB_UInt cur_offset, new_offset, base_offset;
+
+ HB_EntryExitRecord* eer;
+
+
+ base_offset = FILE_Pos();
+
+ if ( ACCESS_Frame( 4L ) )
+ return error;
+
+ cp->PosFormat = GET_UShort();
+ new_offset = GET_UShort() + base_offset;
+
+ FORGET_Frame();
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = _HB_OPEN_Load_Coverage( &cp->Coverage, stream ) ) != HB_Err_Ok )
+ return error;
+ (void)FILE_Seek( cur_offset );
+
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail2;
+
+ count = cp->EntryExitCount = GET_UShort();
+
+ FORGET_Frame();
+
+ cp->EntryExitRecord = NULL;
+
+ if ( ALLOC_ARRAY( cp->EntryExitRecord, count, HB_EntryExitRecord ) )
+ goto Fail2;
+
+ eer = cp->EntryExitRecord;
+
+ for ( n = 0; n < count; n++ )
+ {
+ HB_UInt entry_offset;
+
+ if ( ACCESS_Frame( 2L ) )
+ return error;
+
+ entry_offset = new_offset = GET_UShort();
+
+ FORGET_Frame();
+
+ if ( new_offset )
+ {
+ new_offset += base_offset;
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = Load_Anchor( &eer[n].EntryAnchor,
+ stream ) ) != HB_Err_Ok )
+ goto Fail1;
+ (void)FILE_Seek( cur_offset );
+ }
+ else
+ eer[n].EntryAnchor.PosFormat = 0;
+
+ if ( ACCESS_Frame( 2L ) )
+ return error;
+
+ new_offset = GET_UShort();
+
+ FORGET_Frame();
+
+ if ( new_offset )
+ {
+ new_offset += base_offset;
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = Load_Anchor( &eer[n].ExitAnchor,
+ stream ) ) != HB_Err_Ok )
+ {
+ if ( entry_offset )
+ Free_Anchor( &eer[n].EntryAnchor );
+ goto Fail1;
+ }
+ (void)FILE_Seek( cur_offset );
+ }
+ else
+ eer[n].ExitAnchor.PosFormat = 0;
+ }
+
+ return HB_Err_Ok;
+
+Fail1:
+ for ( m = 0; m < n; m++ )
+ {
+ Free_Anchor( &eer[m].EntryAnchor );
+ Free_Anchor( &eer[m].ExitAnchor );
+ }
+
+ FREE( eer );
+
+Fail2:
+ _HB_OPEN_Free_Coverage( &cp->Coverage );
+ return error;
+}
+
+
+static void Free_CursivePos( HB_GPOS_SubTable* st )
+{
+ HB_UShort n, count;
+ HB_CursivePos* cp = &st->cursive;
+
+ HB_EntryExitRecord* eer;
+
+
+ if ( cp->EntryExitRecord )
+ {
+ count = cp->EntryExitCount;
+ eer = cp->EntryExitRecord;
+
+ for ( n = 0; n < count; n++ )
+ {
+ Free_Anchor( &eer[n].EntryAnchor );
+ Free_Anchor( &eer[n].ExitAnchor );
+ }
+
+ FREE( eer );
+ }
+
+ _HB_OPEN_Free_Coverage( &cp->Coverage );
+}
+
+
+static HB_Error Lookup_CursivePos( GPOS_Instance* gpi,
+ HB_GPOS_SubTable* st,
+ HB_Buffer buffer,
+ HB_UShort flags,
+ HB_UShort context_length,
+ int nesting_level )
+{
+ HB_UShort index, property;
+ HB_Error error;
+ HB_GPOSHeader* gpos = gpi->gpos;
+ HB_CursivePos* cp = &st->cursive;
+
+ HB_EntryExitRecord* eer;
+ HB_Fixed entry_x, entry_y;
+ HB_Fixed exit_x, exit_y;
+
+ HB_UNUSED(nesting_level);
+
+ if ( context_length != 0xFFFF && context_length < 1 )
+ {
+ gpi->last = 0xFFFF;
+ return HB_Err_Not_Covered;
+ }
+
+ /* Glyphs not having the right GDEF properties will be ignored, i.e.,
+ gpi->last won't be reset (contrary to user defined properties). */
+
+ if ( CHECK_Property( gpos->layout, IN_CURITEM(), flags, &property ) )
+ return error;
+
+ /* We don't handle mark glyphs here. According to Andrei, this isn't
+ possible, but who knows... */
+
+ if ( property == HB_OT_LAYOUT_GLYPH_CLASS_MARK )
+ {
+ gpi->last = 0xFFFF;
+ return HB_Err_Not_Covered;
+ }
+
+ error = _HB_OPEN_Coverage_Index( &cp->Coverage, IN_CURGLYPH(), &index );
+ if ( error )
+ {
+ gpi->last = 0xFFFF;
+ return error;
+ }
+
+ if ( index >= cp->EntryExitCount )
+ return ERR(HB_Err_Invalid_SubTable);
+
+ eer = &cp->EntryExitRecord[index];
+
+ /* Now comes the messiest part of the whole OpenType
+ specification. At first glance, cursive connections seem easy
+ to understand, but there are pitfalls! The reason is that
+ the specs don't mention how to compute the advance values
+ resp. glyph offsets. I was told it would be an omission, to
+ be fixed in the next OpenType version... Again many thanks to
+ Andrei Burago <andreib@microsoft.com> for clarifications.
+
+ Consider the following example:
+
+ | xadv1 |
+ +---------+
+ | |
+ +-----+--+ 1 |
+ | | .| |
+ | 0+--+------+
+ | 2 |
+ | |
+ 0+--------+
+ | xadv2 |
+
+ glyph1: advance width = 12
+ anchor point = (3,1)
+
+ glyph2: advance width = 11
+ anchor point = (9,4)
+
+ LSB is 1 for both glyphs (so the boxes drawn above are glyph
+ bboxes). Writing direction is R2L; `0' denotes the glyph's
+ coordinate origin.
+
+ Now the surprising part: The advance width of the *left* glyph
+ (resp. of the *bottom* glyph) will be modified, no matter
+ whether the writing direction is L2R or R2L (resp. T2B or
+ B2T)! This assymetry is caused by the fact that the glyph's
+ coordinate origin is always the lower left corner for all
+ writing directions.
+
+ Continuing the above example, we can compute the new
+ (horizontal) advance width of glyph2 as
+
+ 9 - 3 = 6 ,
+
+ and the new vertical offset of glyph2 as
+
+ 1 - 4 = -3 .
+
+
+ Vertical writing direction is far more complicated:
+
+ a) Assuming that we recompute the advance height of the lower glyph:
+
+ --
+ +---------+
+ -- | |
+ +-----+--+ 1 | yadv1
+ | | .| |
+ yadv2 | 0+--+------+ -- BSB1 --
+ | 2 | -- -- y_offset
+ | |
+ BSB2 -- 0+--------+ --
+ -- --
+
+ glyph1: advance height = 6
+ anchor point = (3,1)
+
+ glyph2: advance height = 7
+ anchor point = (9,4)
+
+ TSB is 1 for both glyphs; writing direction is T2B.
+
+
+ BSB1 = yadv1 - (TSB1 + ymax1)
+ BSB2 = yadv2 - (TSB2 + ymax2)
+ y_offset = y2 - y1
+
+ vertical advance width of glyph2
+ = y_offset + BSB2 - BSB1
+ = (y2 - y1) + (yadv2 - (TSB2 + ymax2)) - (yadv1 - (TSB1 + ymax1))
+ = y2 - y1 + yadv2 - TSB2 - ymax2 - (yadv1 - TSB1 - ymax1)
+ = y2 - y1 + yadv2 - TSB2 - ymax2 - yadv1 + TSB1 + ymax1
+
+
+ b) Assuming that we recompute the advance height of the upper glyph:
+
+ -- --
+ +---------+ -- TSB1
+ -- -- | |
+ TSB2 -- +-----+--+ 1 | yadv1 ymax1
+ | | .| |
+ yadv2 | 0+--+------+ -- --
+ ymax2 | 2 | -- y_offset
+ | |
+ -- 0+--------+ --
+ --
+
+ glyph1: advance height = 6
+ anchor point = (3,1)
+
+ glyph2: advance height = 7
+ anchor point = (9,4)
+
+ TSB is 1 for both glyphs; writing direction is T2B.
+
+ y_offset = y2 - y1
+
+ vertical advance width of glyph2
+ = TSB1 + ymax1 + y_offset - (TSB2 + ymax2)
+ = TSB1 + ymax1 + y2 - y1 - TSB2 - ymax2
+
+
+ Comparing a) with b) shows that b) is easier to compute. I'll wait
+ for a reply from Andrei to see what should really be implemented...
+
+ Since horizontal advance widths or vertical advance heights
+ can be used alone but not together, no ambiguity occurs. */
+
+ if ( gpi->last == 0xFFFF )
+ goto end;
+
+ /* Get_Anchor() returns HB_Err_Not_Covered if there is no anchor
+ table. */
+
+ error = Get_Anchor( gpi, &eer->EntryAnchor, IN_CURGLYPH(),
+ &entry_x, &entry_y );
+ if ( error == HB_Err_Not_Covered )
+ goto end;
+ if ( error )
+ return error;
+
+ if ( gpi->r2l )
+ {
+ POSITION( buffer->in_pos )->x_advance = entry_x - gpi->anchor_x;
+ POSITION( buffer->in_pos )->new_advance = TRUE;
+ }
+ else
+ {
+ POSITION( gpi->last )->x_advance = gpi->anchor_x - entry_x;
+ POSITION( gpi->last )->new_advance = TRUE;
+ }
+
+ if ( flags & HB_LOOKUP_FLAG_RIGHT_TO_LEFT )
+ {
+ POSITION( gpi->last )->cursive_chain = gpi->last - buffer->in_pos;
+ POSITION( gpi->last )->y_pos = entry_y - gpi->anchor_y;
+ }
+ else
+ {
+ POSITION( buffer->in_pos )->cursive_chain = buffer->in_pos - gpi->last;
+ POSITION( buffer->in_pos )->y_pos = gpi->anchor_y - entry_y;
+ }
+
+end:
+ error = Get_Anchor( gpi, &eer->ExitAnchor, IN_CURGLYPH(),
+ &exit_x, &exit_y );
+ if ( error == HB_Err_Not_Covered )
+ gpi->last = 0xFFFF;
+ else
+ {
+ gpi->last = buffer->in_pos;
+ gpi->anchor_x = exit_x;
+ gpi->anchor_y = exit_y;
+ }
+ if ( error )
+ return error;
+
+ (buffer->in_pos)++;
+
+ return HB_Err_Ok;
+}
+
+
+/* LookupType 4 */
+
+/* BaseArray */
+
+static HB_Error Load_BaseArray( HB_BaseArray* ba,
+ HB_UShort num_classes,
+ HB_Stream stream )
+{
+ HB_Error error;
+
+ HB_UShort m, n, count;
+ HB_UInt cur_offset, new_offset, base_offset;
+
+ HB_BaseRecord *br;
+ HB_Anchor *ban, *bans;
+
+
+ base_offset = FILE_Pos();
+
+ if ( ACCESS_Frame( 2L ) )
+ return error;
+
+ count = ba->BaseCount = GET_UShort();
+
+ FORGET_Frame();
+
+ ba->BaseRecord = NULL;
+
+ if ( ALLOC_ARRAY( ba->BaseRecord, count, HB_BaseRecord ) )
+ return error;
+
+ br = ba->BaseRecord;
+
+ bans = NULL;
+
+ if ( ALLOC_ARRAY( bans, count * num_classes, HB_Anchor ) )
+ goto Fail;
+
+ for ( m = 0; m < count; m++ )
+ {
+ br[m].BaseAnchor = NULL;
+
+ ban = br[m].BaseAnchor = bans + m * num_classes;
+
+ for ( n = 0; n < num_classes; n++ )
+ {
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail;
+
+ new_offset = GET_UShort() + base_offset;
+
+ FORGET_Frame();
+
+ if (new_offset == base_offset) {
+ /* XXX
+ * Doulos SIL Regular is buggy and has zero offsets here.
+ * Skip it
+ */
+ ban[n].PosFormat = 0;
+ continue;
+ }
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = Load_Anchor( &ban[n], stream ) ) != HB_Err_Ok )
+ goto Fail;
+ (void)FILE_Seek( cur_offset );
+ }
+ }
+
+ return HB_Err_Ok;
+
+Fail:
+ FREE( bans );
+ FREE( br );
+ return error;
+}
+
+
+static void Free_BaseArray( HB_BaseArray* ba,
+ HB_UShort num_classes )
+{
+ HB_BaseRecord *br;
+ HB_Anchor *bans;
+
+ HB_UNUSED(num_classes);
+
+ if ( ba->BaseRecord )
+ {
+ br = ba->BaseRecord;
+
+ if ( ba->BaseCount )
+ {
+ bans = br[0].BaseAnchor;
+ FREE( bans );
+ }
+
+ FREE( br );
+ }
+}
+
+
+/* MarkBasePosFormat1 */
+
+static HB_Error Load_MarkBasePos( HB_GPOS_SubTable* st,
+ HB_Stream stream )
+{
+ HB_Error error;
+ HB_MarkBasePos* mbp = &st->markbase;
+
+ HB_UInt cur_offset, new_offset, base_offset;
+
+
+ base_offset = FILE_Pos();
+
+ if ( ACCESS_Frame( 4L ) )
+ return error;
+
+ mbp->PosFormat = GET_UShort();
+ new_offset = GET_UShort() + base_offset;
+
+ FORGET_Frame();
+
+ if (mbp->PosFormat != 1)
+ return ERR(HB_Err_Invalid_SubTable_Format);
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = _HB_OPEN_Load_Coverage( &mbp->MarkCoverage, stream ) ) != HB_Err_Ok )
+ return error;
+ (void)FILE_Seek( cur_offset );
+
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail3;
+
+ new_offset = GET_UShort() + base_offset;
+
+ FORGET_Frame();
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = _HB_OPEN_Load_Coverage( &mbp->BaseCoverage, stream ) ) != HB_Err_Ok )
+ goto Fail3;
+ (void)FILE_Seek( cur_offset );
+
+ if ( ACCESS_Frame( 4L ) )
+ goto Fail2;
+
+ mbp->ClassCount = GET_UShort();
+ new_offset = GET_UShort() + base_offset;
+
+ FORGET_Frame();
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = Load_MarkArray( &mbp->MarkArray, stream ) ) != HB_Err_Ok )
+ goto Fail2;
+ (void)FILE_Seek( cur_offset );
+
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail1;
+
+ new_offset = GET_UShort() + base_offset;
+
+ FORGET_Frame();
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = Load_BaseArray( &mbp->BaseArray, mbp->ClassCount,
+ stream ) ) != HB_Err_Ok )
+ goto Fail1;
+
+ return HB_Err_Ok;
+
+Fail1:
+ Free_MarkArray( &mbp->MarkArray );
+
+Fail2:
+ _HB_OPEN_Free_Coverage( &mbp->BaseCoverage );
+
+Fail3:
+ _HB_OPEN_Free_Coverage( &mbp->MarkCoverage );
+ return error;
+}
+
+
+static void Free_MarkBasePos( HB_GPOS_SubTable* st )
+{
+ HB_MarkBasePos* mbp = &st->markbase;
+
+ Free_BaseArray( &mbp->BaseArray, mbp->ClassCount );
+ Free_MarkArray( &mbp->MarkArray );
+ _HB_OPEN_Free_Coverage( &mbp->BaseCoverage );
+ _HB_OPEN_Free_Coverage( &mbp->MarkCoverage );
+}
+
+
+static HB_Error Lookup_MarkBasePos( GPOS_Instance* gpi,
+ HB_GPOS_SubTable* st,
+ HB_Buffer buffer,
+ HB_UShort flags,
+ HB_UShort context_length,
+ int nesting_level )
+{
+ HB_UShort i, j, mark_index, base_index, property, class;
+ HB_Fixed x_mark_value, y_mark_value, x_base_value, y_base_value;
+ HB_Error error;
+ HB_GPOSHeader* gpos = gpi->gpos;
+ HB_MarkBasePos* mbp = &st->markbase;
+
+ HB_MarkArray* ma;
+ HB_BaseArray* ba;
+ HB_BaseRecord* br;
+ HB_Anchor* mark_anchor;
+ HB_Anchor* base_anchor;
+
+ HB_Position o;
+
+ HB_UNUSED(nesting_level);
+
+ if ( context_length != 0xFFFF && context_length < 1 )
+ return HB_Err_Not_Covered;
+
+ if ( flags & HB_LOOKUP_FLAG_IGNORE_BASE_GLYPHS )
+ return HB_Err_Not_Covered;
+
+ if ( CHECK_Property( gpos->layout, IN_CURITEM(),
+ flags, &property ) )
+ return error;
+
+ error = _HB_OPEN_Coverage_Index( &mbp->MarkCoverage, IN_CURGLYPH(),
+ &mark_index );
+ if ( error )
+ return error;
+
+ /* now we search backwards for a non-mark glyph */
+
+ i = 1;
+ j = buffer->in_pos - 1;
+
+ while ( i <= buffer->in_pos )
+ {
+ property = _hb_ot_layout_get_glyph_properties (gpos->layout, IN_GLYPH(j));
+ if ( !property )
+ return HB_Err_Not_Covered;
+
+ if ( !( property == HB_OT_LAYOUT_GLYPH_CLASS_MARK || property & HB_LOOKUP_FLAG_IGNORE_SPECIAL_MARKS ) )
+ break;
+
+ i++;
+ j--;
+ }
+
+ /* The following assertion is too strong -- at least for mangal.ttf. */
+#if 0
+ if ( property != HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH )
+ return HB_Err_Not_Covered;
+#endif
+
+ if ( i > buffer->in_pos )
+ return HB_Err_Not_Covered;
+
+ error = _HB_OPEN_Coverage_Index( &mbp->BaseCoverage, IN_GLYPH( j ),
+ &base_index );
+ if ( error )
+ return error;
+
+ ma = &mbp->MarkArray;
+
+ if ( mark_index >= ma->MarkCount )
+ return ERR(HB_Err_Invalid_SubTable);
+
+ class = ma->MarkRecord[mark_index].Class;
+ mark_anchor = &ma->MarkRecord[mark_index].MarkAnchor;
+
+ if ( class >= mbp->ClassCount )
+ return ERR(HB_Err_Invalid_SubTable);
+
+ ba = &mbp->BaseArray;
+
+ if ( base_index >= ba->BaseCount )
+ return ERR(HB_Err_Invalid_SubTable);
+
+ br = &ba->BaseRecord[base_index];
+ base_anchor = &br->BaseAnchor[class];
+
+ error = Get_Anchor( gpi, mark_anchor, IN_CURGLYPH(),
+ &x_mark_value, &y_mark_value );
+ if ( error )
+ return error;
+
+ error = Get_Anchor( gpi, base_anchor, IN_GLYPH( j ),
+ &x_base_value, &y_base_value );
+ if ( error )
+ return error;
+
+ /* anchor points are not cumulative */
+
+ o = POSITION( buffer->in_pos );
+
+ o->x_pos = x_base_value - x_mark_value;
+ o->y_pos = y_base_value - y_mark_value;
+ o->x_advance = 0;
+ o->y_advance = 0;
+ o->back = i;
+
+ (buffer->in_pos)++;
+
+ return HB_Err_Ok;
+}
+
+
+/* LookupType 5 */
+
+/* LigatureAttach */
+
+static HB_Error Load_LigatureAttach( HB_LigatureAttach* lat,
+ HB_UShort num_classes,
+ HB_Stream stream )
+{
+ HB_Error error;
+
+ HB_UShort m, n, k, count;
+ HB_UInt cur_offset, new_offset, base_offset;
+
+ HB_ComponentRecord* cr;
+ HB_Anchor* lan;
+
+
+ base_offset = FILE_Pos();
+
+ if ( ACCESS_Frame( 2L ) )
+ return error;
+
+ count = lat->ComponentCount = GET_UShort();
+
+ FORGET_Frame();
+
+ lat->ComponentRecord = NULL;
+
+ if ( ALLOC_ARRAY( lat->ComponentRecord, count, HB_ComponentRecord ) )
+ return error;
+
+ cr = lat->ComponentRecord;
+
+ for ( m = 0; m < count; m++ )
+ {
+ cr[m].LigatureAnchor = NULL;
+
+ if ( ALLOC_ARRAY( cr[m].LigatureAnchor, num_classes, HB_Anchor ) )
+ goto Fail;
+
+ lan = cr[m].LigatureAnchor;
+
+ for ( n = 0; n < num_classes; n++ )
+ {
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail0;
+
+ new_offset = GET_UShort();
+
+ FORGET_Frame();
+
+ if ( new_offset )
+ {
+ new_offset += base_offset;
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = Load_Anchor( &lan[n], stream ) ) != HB_Err_Ok )
+ goto Fail0;
+ (void)FILE_Seek( cur_offset );
+ }
+ else
+ lan[n].PosFormat = 0;
+ }
+
+ continue;
+ Fail0:
+ for ( k = 0; k < n; k++ )
+ Free_Anchor( &lan[k] );
+ goto Fail;
+ }
+
+ return HB_Err_Ok;
+
+Fail:
+ for ( k = 0; k < m; k++ )
+ {
+ lan = cr[k].LigatureAnchor;
+
+ for ( n = 0; n < num_classes; n++ )
+ Free_Anchor( &lan[n] );
+
+ FREE( lan );
+ }
+
+ FREE( cr );
+ return error;
+}
+
+
+static void Free_LigatureAttach( HB_LigatureAttach* lat,
+ HB_UShort num_classes )
+{
+ HB_UShort m, n, count;
+
+ HB_ComponentRecord* cr;
+ HB_Anchor* lan;
+
+
+ if ( lat->ComponentRecord )
+ {
+ count = lat->ComponentCount;
+ cr = lat->ComponentRecord;
+
+ for ( m = 0; m < count; m++ )
+ {
+ lan = cr[m].LigatureAnchor;
+
+ for ( n = 0; n < num_classes; n++ )
+ Free_Anchor( &lan[n] );
+
+ FREE( lan );
+ }
+
+ FREE( cr );
+ }
+}
+
+
+/* LigatureArray */
+
+static HB_Error Load_LigatureArray( HB_LigatureArray* la,
+ HB_UShort num_classes,
+ HB_Stream stream )
+{
+ HB_Error error;
+
+ HB_UShort n, m, count;
+ HB_UInt cur_offset, new_offset, base_offset;
+
+ HB_LigatureAttach* lat;
+
+
+ base_offset = FILE_Pos();
+
+ if ( ACCESS_Frame( 2L ) )
+ return error;
+
+ count = la->LigatureCount = GET_UShort();
+
+ FORGET_Frame();
+
+ la->LigatureAttach = NULL;
+
+ if ( ALLOC_ARRAY( la->LigatureAttach, count, HB_LigatureAttach ) )
+ return error;
+
+ lat = la->LigatureAttach;
+
+ for ( n = 0; n < count; n++ )
+ {
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail;
+
+ new_offset = GET_UShort() + base_offset;
+
+ FORGET_Frame();
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = Load_LigatureAttach( &lat[n], num_classes,
+ stream ) ) != HB_Err_Ok )
+ goto Fail;
+ (void)FILE_Seek( cur_offset );
+ }
+
+ return HB_Err_Ok;
+
+Fail:
+ for ( m = 0; m < n; m++ )
+ Free_LigatureAttach( &lat[m], num_classes );
+
+ FREE( lat );
+ return error;
+}
+
+
+static void Free_LigatureArray( HB_LigatureArray* la,
+ HB_UShort num_classes )
+{
+ HB_UShort n, count;
+
+ HB_LigatureAttach* lat;
+
+
+ if ( la->LigatureAttach )
+ {
+ count = la->LigatureCount;
+ lat = la->LigatureAttach;
+
+ for ( n = 0; n < count; n++ )
+ Free_LigatureAttach( &lat[n], num_classes );
+
+ FREE( lat );
+ }
+}
+
+
+/* MarkLigPosFormat1 */
+
+static HB_Error Load_MarkLigPos( HB_GPOS_SubTable* st,
+ HB_Stream stream )
+{
+ HB_Error error;
+ HB_MarkLigPos* mlp = &st->marklig;
+
+ HB_UInt cur_offset, new_offset, base_offset;
+
+
+ base_offset = FILE_Pos();
+
+ if ( ACCESS_Frame( 4L ) )
+ return error;
+
+ mlp->PosFormat = GET_UShort();
+ new_offset = GET_UShort() + base_offset;
+
+ FORGET_Frame();
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = _HB_OPEN_Load_Coverage( &mlp->MarkCoverage, stream ) ) != HB_Err_Ok )
+ return error;
+ (void)FILE_Seek( cur_offset );
+
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail3;
+
+ new_offset = GET_UShort() + base_offset;
+
+ FORGET_Frame();
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = _HB_OPEN_Load_Coverage( &mlp->LigatureCoverage,
+ stream ) ) != HB_Err_Ok )
+ goto Fail3;
+ (void)FILE_Seek( cur_offset );
+
+ if ( ACCESS_Frame( 4L ) )
+ goto Fail2;
+
+ mlp->ClassCount = GET_UShort();
+ new_offset = GET_UShort() + base_offset;
+
+ FORGET_Frame();
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = Load_MarkArray( &mlp->MarkArray, stream ) ) != HB_Err_Ok )
+ goto Fail2;
+ (void)FILE_Seek( cur_offset );
+
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail1;
+
+ new_offset = GET_UShort() + base_offset;
+
+ FORGET_Frame();
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = Load_LigatureArray( &mlp->LigatureArray, mlp->ClassCount,
+ stream ) ) != HB_Err_Ok )
+ goto Fail1;
+
+ return HB_Err_Ok;
+
+Fail1:
+ Free_MarkArray( &mlp->MarkArray );
+
+Fail2:
+ _HB_OPEN_Free_Coverage( &mlp->LigatureCoverage );
+
+Fail3:
+ _HB_OPEN_Free_Coverage( &mlp->MarkCoverage );
+ return error;
+}
+
+
+static void Free_MarkLigPos( HB_GPOS_SubTable* st )
+{
+ HB_MarkLigPos* mlp = &st->marklig;
+
+ Free_LigatureArray( &mlp->LigatureArray, mlp->ClassCount );
+ Free_MarkArray( &mlp->MarkArray );
+ _HB_OPEN_Free_Coverage( &mlp->LigatureCoverage );
+ _HB_OPEN_Free_Coverage( &mlp->MarkCoverage );
+}
+
+
+static HB_Error Lookup_MarkLigPos( GPOS_Instance* gpi,
+ HB_GPOS_SubTable* st,
+ HB_Buffer buffer,
+ HB_UShort flags,
+ HB_UShort context_length,
+ int nesting_level )
+{
+ HB_UShort i, j, mark_index, lig_index, property, class;
+ HB_UShort mark_glyph;
+ HB_Fixed x_mark_value, y_mark_value, x_lig_value, y_lig_value;
+ HB_Error error;
+ HB_GPOSHeader* gpos = gpi->gpos;
+ HB_MarkLigPos* mlp = &st->marklig;
+
+ HB_MarkArray* ma;
+ HB_LigatureArray* la;
+ HB_LigatureAttach* lat;
+ HB_ComponentRecord* cr;
+ HB_UShort comp_index;
+ HB_Anchor* mark_anchor;
+ HB_Anchor* lig_anchor;
+
+ HB_Position o;
+
+ HB_UNUSED(nesting_level);
+
+ if ( context_length != 0xFFFF && context_length < 1 )
+ return HB_Err_Not_Covered;
+
+ if ( flags & HB_LOOKUP_FLAG_IGNORE_LIGATURES )
+ return HB_Err_Not_Covered;
+
+ mark_glyph = IN_CURGLYPH();
+
+ if ( CHECK_Property( gpos->layout, IN_CURITEM(), flags, &property ) )
+ return error;
+
+ error = _HB_OPEN_Coverage_Index( &mlp->MarkCoverage, mark_glyph, &mark_index );
+ if ( error )
+ return error;
+
+ /* now we search backwards for a non-mark glyph */
+
+ i = 1;
+ j = buffer->in_pos - 1;
+
+ while ( i <= buffer->in_pos )
+ {
+ property = _hb_ot_layout_get_glyph_properties (gpos->layout, IN_GLYPH(j));
+ if ( !property )
+ return HB_Err_Not_Covered;
+
+ if ( !( property == HB_OT_LAYOUT_GLYPH_CLASS_MARK || property & HB_LOOKUP_FLAG_IGNORE_SPECIAL_MARKS ) )
+ break;
+
+ i++;
+ j--;
+ }
+
+ /* Similar to Lookup_MarkBasePos(), I suspect that this assertion is
+ too strong, thus it is commented out. */
+#if 0
+ if ( property != HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE )
+ return HB_Err_Not_Covered;
+#endif
+
+ if ( i > buffer->in_pos )
+ return HB_Err_Not_Covered;
+
+ error = _HB_OPEN_Coverage_Index( &mlp->LigatureCoverage, IN_GLYPH( j ),
+ &lig_index );
+ if ( error )
+ return error;
+
+ ma = &mlp->MarkArray;
+
+ if ( mark_index >= ma->MarkCount )
+ return ERR(HB_Err_Invalid_SubTable);
+
+ class = ma->MarkRecord[mark_index].Class;
+ mark_anchor = &ma->MarkRecord[mark_index].MarkAnchor;
+
+ if ( class >= mlp->ClassCount )
+ return ERR(HB_Err_Invalid_SubTable);
+
+ la = &mlp->LigatureArray;
+
+ if ( lig_index >= la->LigatureCount )
+ return ERR(HB_Err_Invalid_SubTable);
+
+ lat = &la->LigatureAttach[lig_index];
+
+ /* We must now check whether the ligature ID of the current mark glyph
+ is identical to the ligature ID of the found ligature. If yes, we
+ can directly use the component index. If not, we attach the mark
+ glyph to the last component of the ligature. */
+
+ if ( IN_LIGID( j ) == IN_LIGID( buffer->in_pos) )
+ {
+ comp_index = IN_COMPONENT( buffer->in_pos );
+ if ( comp_index >= lat->ComponentCount )
+ return HB_Err_Not_Covered;
+ }
+ else
+ comp_index = lat->ComponentCount - 1;
+
+ cr = &lat->ComponentRecord[comp_index];
+ lig_anchor = &cr->LigatureAnchor[class];
+
+ error = Get_Anchor( gpi, mark_anchor, IN_CURGLYPH(),
+ &x_mark_value, &y_mark_value );
+ if ( error )
+ return error;
+ error = Get_Anchor( gpi, lig_anchor, IN_GLYPH( j ),
+ &x_lig_value, &y_lig_value );
+ if ( error )
+ return error;
+
+ /* anchor points are not cumulative */
+
+ o = POSITION( buffer->in_pos );
+
+ o->x_pos = x_lig_value - x_mark_value;
+ o->y_pos = y_lig_value - y_mark_value;
+ o->x_advance = 0;
+ o->y_advance = 0;
+ o->back = i;
+
+ (buffer->in_pos)++;
+
+ return HB_Err_Ok;
+}
+
+
+/* LookupType 6 */
+
+/* Mark2Array */
+
+static HB_Error Load_Mark2Array( HB_Mark2Array* m2a,
+ HB_UShort num_classes,
+ HB_Stream stream )
+{
+ HB_Error error;
+
+ HB_UShort k, m, n, count;
+ HB_UInt cur_offset, new_offset, base_offset;
+
+ HB_Mark2Record *m2r;
+ HB_Anchor *m2an, *m2ans;
+
+
+ base_offset = FILE_Pos();
+
+ if ( ACCESS_Frame( 2L ) )
+ return error;
+
+ count = m2a->Mark2Count = GET_UShort();
+
+ FORGET_Frame();
+
+ m2a->Mark2Record = NULL;
+
+ if ( ALLOC_ARRAY( m2a->Mark2Record, count, HB_Mark2Record ) )
+ return error;
+
+ m2r = m2a->Mark2Record;
+
+ m2ans = NULL;
+
+ if ( ALLOC_ARRAY( m2ans, count * num_classes, HB_Anchor ) )
+ goto Fail;
+
+ for ( m = 0; m < count; m++ )
+ {
+ m2an = m2r[m].Mark2Anchor = m2ans + m * num_classes;
+
+ for ( n = 0; n < num_classes; n++ )
+ {
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail;
+
+ new_offset = GET_UShort() + base_offset;
+
+ FORGET_Frame();
+
+ if (new_offset == base_offset) {
+ /* Anchor table not provided. Skip loading.
+ * Some versions of FreeSans hit this. */
+ m2an[n].PosFormat = 0;
+ continue;
+ }
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = Load_Anchor( &m2an[n], stream ) ) != HB_Err_Ok )
+ goto Fail;
+ (void)FILE_Seek( cur_offset );
+ }
+ }
+
+ return HB_Err_Ok;
+
+Fail:
+ FREE( m2ans );
+ FREE( m2r );
+ return error;
+}
+
+
+static void Free_Mark2Array( HB_Mark2Array* m2a,
+ HB_UShort num_classes )
+{
+ HB_Mark2Record *m2r;
+ HB_Anchor *m2ans;
+
+ HB_UNUSED(num_classes);
+
+ if ( m2a->Mark2Record )
+ {
+ m2r = m2a->Mark2Record;
+
+ if ( m2a->Mark2Count )
+ {
+ m2ans = m2r[0].Mark2Anchor;
+ FREE( m2ans );
+ }
+
+ FREE( m2r );
+ }
+}
+
+
+/* MarkMarkPosFormat1 */
+
+static HB_Error Load_MarkMarkPos( HB_GPOS_SubTable* st,
+ HB_Stream stream )
+{
+ HB_Error error;
+ HB_MarkMarkPos* mmp = &st->markmark;
+
+ HB_UInt cur_offset, new_offset, base_offset;
+
+
+ base_offset = FILE_Pos();
+
+ if ( ACCESS_Frame( 4L ) )
+ return error;
+
+ mmp->PosFormat = GET_UShort();
+ new_offset = GET_UShort() + base_offset;
+
+ FORGET_Frame();
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = _HB_OPEN_Load_Coverage( &mmp->Mark1Coverage,
+ stream ) ) != HB_Err_Ok )
+ return error;
+ (void)FILE_Seek( cur_offset );
+
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail3;
+
+ new_offset = GET_UShort() + base_offset;
+
+ FORGET_Frame();
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = _HB_OPEN_Load_Coverage( &mmp->Mark2Coverage,
+ stream ) ) != HB_Err_Ok )
+ goto Fail3;
+ (void)FILE_Seek( cur_offset );
+
+ if ( ACCESS_Frame( 4L ) )
+ goto Fail2;
+
+ mmp->ClassCount = GET_UShort();
+ new_offset = GET_UShort() + base_offset;
+
+ FORGET_Frame();
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = Load_MarkArray( &mmp->Mark1Array, stream ) ) != HB_Err_Ok )
+ goto Fail2;
+ (void)FILE_Seek( cur_offset );
+
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail1;
+
+ new_offset = GET_UShort() + base_offset;
+
+ FORGET_Frame();
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = Load_Mark2Array( &mmp->Mark2Array, mmp->ClassCount,
+ stream ) ) != HB_Err_Ok )
+ goto Fail1;
+
+ return HB_Err_Ok;
+
+Fail1:
+ Free_MarkArray( &mmp->Mark1Array );
+
+Fail2:
+ _HB_OPEN_Free_Coverage( &mmp->Mark2Coverage );
+
+Fail3:
+ _HB_OPEN_Free_Coverage( &mmp->Mark1Coverage );
+ return error;
+}
+
+
+static void Free_MarkMarkPos( HB_GPOS_SubTable* st )
+{
+ HB_MarkMarkPos* mmp = &st->markmark;
+
+ Free_Mark2Array( &mmp->Mark2Array, mmp->ClassCount );
+ Free_MarkArray( &mmp->Mark1Array );
+ _HB_OPEN_Free_Coverage( &mmp->Mark2Coverage );
+ _HB_OPEN_Free_Coverage( &mmp->Mark1Coverage );
+}
+
+
+static HB_Error Lookup_MarkMarkPos( GPOS_Instance* gpi,
+ HB_GPOS_SubTable* st,
+ HB_Buffer buffer,
+ HB_UShort flags,
+ HB_UShort context_length,
+ int nesting_level )
+{
+ HB_UShort i, j, mark1_index, mark2_index, property, class;
+ HB_Fixed x_mark1_value, y_mark1_value,
+ x_mark2_value, y_mark2_value;
+ HB_Error error;
+ HB_GPOSHeader* gpos = gpi->gpos;
+ HB_MarkMarkPos* mmp = &st->markmark;
+
+ HB_MarkArray* ma1;
+ HB_Mark2Array* ma2;
+ HB_Mark2Record* m2r;
+ HB_Anchor* mark1_anchor;
+ HB_Anchor* mark2_anchor;
+
+ HB_Position o;
+
+ HB_UNUSED(nesting_level);
+
+ if ( context_length != 0xFFFF && context_length < 1 )
+ return HB_Err_Not_Covered;
+
+ if ( flags & HB_LOOKUP_FLAG_IGNORE_MARKS )
+ return HB_Err_Not_Covered;
+
+ if ( CHECK_Property( gpos->layout, IN_CURITEM(),
+ flags, &property ) )
+ return error;
+
+ error = _HB_OPEN_Coverage_Index( &mmp->Mark1Coverage, IN_CURGLYPH(),
+ &mark1_index );
+ if ( error )
+ return error;
+
+ /* now we search backwards for a suitable mark glyph until a non-mark
+ glyph */
+
+ if ( buffer->in_pos == 0 )
+ return HB_Err_Not_Covered;
+
+ i = 1;
+ j = buffer->in_pos - 1;
+ while ( i <= buffer->in_pos )
+ {
+ property = _hb_ot_layout_get_glyph_properties (gpos->layout, IN_GLYPH(j));
+ if ( !property )
+ return HB_Err_Not_Covered;
+
+ if ( !( property == HB_OT_LAYOUT_GLYPH_CLASS_MARK || property & HB_LOOKUP_FLAG_IGNORE_SPECIAL_MARKS ) )
+ return HB_Err_Not_Covered;
+
+ if ( flags & HB_LOOKUP_FLAG_IGNORE_SPECIAL_MARKS )
+ {
+ if ( property == (flags & 0xFF00) )
+ break;
+ }
+ else
+ break;
+
+ i++;
+ j--;
+ }
+
+ error = _HB_OPEN_Coverage_Index( &mmp->Mark2Coverage, IN_GLYPH( j ),
+ &mark2_index );
+ if ( error )
+ return error;
+
+ ma1 = &mmp->Mark1Array;
+
+ if ( mark1_index >= ma1->MarkCount )
+ return ERR(HB_Err_Invalid_SubTable);
+
+ class = ma1->MarkRecord[mark1_index].Class;
+ mark1_anchor = &ma1->MarkRecord[mark1_index].MarkAnchor;
+
+ if ( class >= mmp->ClassCount )
+ return ERR(HB_Err_Invalid_SubTable);
+
+ ma2 = &mmp->Mark2Array;
+
+ if ( mark2_index >= ma2->Mark2Count )
+ return ERR(HB_Err_Invalid_SubTable);
+
+ m2r = &ma2->Mark2Record[mark2_index];
+ mark2_anchor = &m2r->Mark2Anchor[class];
+
+ error = Get_Anchor( gpi, mark1_anchor, IN_CURGLYPH(),
+ &x_mark1_value, &y_mark1_value );
+ if ( error )
+ return error;
+ error = Get_Anchor( gpi, mark2_anchor, IN_GLYPH( j ),
+ &x_mark2_value, &y_mark2_value );
+ if ( error )
+ return error;
+
+ /* anchor points are not cumulative */
+
+ o = POSITION( buffer->in_pos );
+
+ o->x_pos = x_mark2_value - x_mark1_value;
+ o->y_pos = y_mark2_value - y_mark1_value;
+ o->x_advance = 0;
+ o->y_advance = 0;
+ o->back = 1;
+
+ (buffer->in_pos)++;
+
+ return HB_Err_Ok;
+}
+
+
+/* Do the actual positioning for a context positioning (either format
+ 7 or 8). This is only called after we've determined that the stream
+ matches the subrule. */
+
+static HB_Error Do_ContextPos( GPOS_Instance* gpi,
+ HB_UShort GlyphCount,
+ HB_UShort PosCount,
+ HB_PosLookupRecord* pos,
+ HB_Buffer buffer,
+ int nesting_level )
+{
+ HB_Error error;
+ HB_UInt i, old_pos;
+
+
+ i = 0;
+
+ while ( i < GlyphCount )
+ {
+ if ( PosCount && i == pos->SequenceIndex )
+ {
+ old_pos = buffer->in_pos;
+
+ /* Do a positioning */
+
+ error = GPOS_Do_Glyph_Lookup( gpi, pos->LookupListIndex, buffer,
+ GlyphCount, nesting_level );
+
+ if ( error )
+ return error;
+
+ pos++;
+ PosCount--;
+ i += buffer->in_pos - old_pos;
+ }
+ else
+ {
+ i++;
+ (buffer->in_pos)++;
+ }
+ }
+
+ return HB_Err_Ok;
+}
+
+
+/* LookupType 7 */
+
+/* PosRule */
+
+static HB_Error Load_PosRule( HB_PosRule* pr,
+ HB_Stream stream )
+{
+ HB_Error error;
+
+ HB_UShort n, count;
+ HB_UShort* i;
+
+ HB_PosLookupRecord* plr;
+
+
+ if ( ACCESS_Frame( 4L ) )
+ return error;
+
+ pr->GlyphCount = GET_UShort();
+ pr->PosCount = GET_UShort();
+
+ FORGET_Frame();
+
+ pr->Input = NULL;
+
+ count = pr->GlyphCount - 1; /* only GlyphCount - 1 elements */
+
+ if ( ALLOC_ARRAY( pr->Input, count, HB_UShort ) )
+ return error;
+
+ i = pr->Input;
+
+ if ( ACCESS_Frame( count * 2L ) )
+ goto Fail2;
+
+ for ( n = 0; n < count; n++ )
+ i[n] = GET_UShort();
+
+ FORGET_Frame();
+
+ pr->PosLookupRecord = NULL;
+
+ count = pr->PosCount;
+
+ if ( ALLOC_ARRAY( pr->PosLookupRecord, count, HB_PosLookupRecord ) )
+ goto Fail2;
+
+ plr = pr->PosLookupRecord;
+
+ if ( ACCESS_Frame( count * 4L ) )
+ goto Fail1;
+
+ for ( n = 0; n < count; n++ )
+ {
+ plr[n].SequenceIndex = GET_UShort();
+ plr[n].LookupListIndex = GET_UShort();
+ }
+
+ FORGET_Frame();
+
+ return HB_Err_Ok;
+
+Fail1:
+ FREE( plr );
+
+Fail2:
+ FREE( i );
+ return error;
+}
+
+
+static void Free_PosRule( HB_PosRule* pr )
+{
+ FREE( pr->PosLookupRecord );
+ FREE( pr->Input );
+}
+
+
+/* PosRuleSet */
+
+static HB_Error Load_PosRuleSet( HB_PosRuleSet* prs,
+ HB_Stream stream )
+{
+ HB_Error error;
+
+ HB_UShort n, m, count;
+ HB_UInt cur_offset, new_offset, base_offset;
+
+ HB_PosRule* pr;
+
+
+ base_offset = FILE_Pos();
+
+ if ( ACCESS_Frame( 2L ) )
+ return error;
+
+ count = prs->PosRuleCount = GET_UShort();
+
+ FORGET_Frame();
+
+ prs->PosRule = NULL;
+
+ if ( ALLOC_ARRAY( prs->PosRule, count, HB_PosRule ) )
+ return error;
+
+ pr = prs->PosRule;
+
+ for ( n = 0; n < count; n++ )
+ {
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail;
+
+ new_offset = GET_UShort() + base_offset;
+
+ FORGET_Frame();
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = Load_PosRule( &pr[n], stream ) ) != HB_Err_Ok )
+ goto Fail;
+ (void)FILE_Seek( cur_offset );
+ }
+
+ return HB_Err_Ok;
+
+Fail:
+ for ( m = 0; m < n; m++ )
+ Free_PosRule( &pr[m] );
+
+ FREE( pr );
+ return error;
+}
+
+
+static void Free_PosRuleSet( HB_PosRuleSet* prs )
+{
+ HB_UShort n, count;
+
+ HB_PosRule* pr;
+
+
+ if ( prs->PosRule )
+ {
+ count = prs->PosRuleCount;
+ pr = prs->PosRule;
+
+ for ( n = 0; n < count; n++ )
+ Free_PosRule( &pr[n] );
+
+ FREE( pr );
+ }
+}
+
+
+/* ContextPosFormat1 */
+
+static HB_Error Load_ContextPos1( HB_ContextPosFormat1* cpf1,
+ HB_Stream stream )
+{
+ HB_Error error;
+
+ HB_UShort n, m, count;
+ HB_UInt cur_offset, new_offset, base_offset;
+
+ HB_PosRuleSet* prs;
+
+
+ base_offset = FILE_Pos() - 2L;
+
+ if ( ACCESS_Frame( 2L ) )
+ return error;
+
+ new_offset = GET_UShort() + base_offset;
+
+ FORGET_Frame();
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = _HB_OPEN_Load_Coverage( &cpf1->Coverage, stream ) ) != HB_Err_Ok )
+ return error;
+ (void)FILE_Seek( cur_offset );
+
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail2;
+
+ count = cpf1->PosRuleSetCount = GET_UShort();
+
+ FORGET_Frame();
+
+ cpf1->PosRuleSet = NULL;
+
+ if ( ALLOC_ARRAY( cpf1->PosRuleSet, count, HB_PosRuleSet ) )
+ goto Fail2;
+
+ prs = cpf1->PosRuleSet;
+
+ for ( n = 0; n < count; n++ )
+ {
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail1;
+
+ new_offset = GET_UShort() + base_offset;
+
+ FORGET_Frame();
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = Load_PosRuleSet( &prs[n], stream ) ) != HB_Err_Ok )
+ goto Fail1;
+ (void)FILE_Seek( cur_offset );
+ }
+
+ return HB_Err_Ok;
+
+Fail1:
+ for ( m = 0; m < n; m++ )
+ Free_PosRuleSet( &prs[m] );
+
+ FREE( prs );
+
+Fail2:
+ _HB_OPEN_Free_Coverage( &cpf1->Coverage );
+ return error;
+}
+
+
+static void Free_ContextPos1( HB_ContextPosFormat1* cpf1 )
+{
+ HB_UShort n, count;
+
+ HB_PosRuleSet* prs;
+
+
+ if ( cpf1->PosRuleSet )
+ {
+ count = cpf1->PosRuleSetCount;
+ prs = cpf1->PosRuleSet;
+
+ for ( n = 0; n < count; n++ )
+ Free_PosRuleSet( &prs[n] );
+
+ FREE( prs );
+ }
+
+ _HB_OPEN_Free_Coverage( &cpf1->Coverage );
+}
+
+
+/* PosClassRule */
+
+static HB_Error Load_PosClassRule( HB_ContextPosFormat2* cpf2,
+ HB_PosClassRule* pcr,
+ HB_Stream stream )
+{
+ HB_Error error;
+
+ HB_UShort n, count;
+
+ HB_UShort* c;
+ HB_PosLookupRecord* plr;
+
+
+ if ( ACCESS_Frame( 4L ) )
+ return error;
+
+ pcr->GlyphCount = GET_UShort();
+ pcr->PosCount = GET_UShort();
+
+ FORGET_Frame();
+
+ if ( pcr->GlyphCount > cpf2->MaxContextLength )
+ cpf2->MaxContextLength = pcr->GlyphCount;
+
+ pcr->Class = NULL;
+
+ count = pcr->GlyphCount - 1; /* only GlyphCount - 1 elements */
+
+ if ( ALLOC_ARRAY( pcr->Class, count, HB_UShort ) )
+ return error;
+
+ c = pcr->Class;
+
+ if ( ACCESS_Frame( count * 2L ) )
+ goto Fail2;
+
+ for ( n = 0; n < count; n++ )
+ c[n] = GET_UShort();
+
+ FORGET_Frame();
+
+ pcr->PosLookupRecord = NULL;
+
+ count = pcr->PosCount;
+
+ if ( ALLOC_ARRAY( pcr->PosLookupRecord, count, HB_PosLookupRecord ) )
+ goto Fail2;
+
+ plr = pcr->PosLookupRecord;
+
+ if ( ACCESS_Frame( count * 4L ) )
+ goto Fail1;
+
+ for ( n = 0; n < count; n++ )
+ {
+ plr[n].SequenceIndex = GET_UShort();
+ plr[n].LookupListIndex = GET_UShort();
+ }
+
+ FORGET_Frame();
+
+ return HB_Err_Ok;
+
+Fail1:
+ FREE( plr );
+
+Fail2:
+ FREE( c );
+ return error;
+}
+
+
+static void Free_PosClassRule( HB_PosClassRule* pcr )
+{
+ FREE( pcr->PosLookupRecord );
+ FREE( pcr->Class );
+}
+
+
+/* PosClassSet */
+
+static HB_Error Load_PosClassSet( HB_ContextPosFormat2* cpf2,
+ HB_PosClassSet* pcs,
+ HB_Stream stream )
+{
+ HB_Error error;
+
+ HB_UShort n, m, count;
+ HB_UInt cur_offset, new_offset, base_offset;
+
+ HB_PosClassRule* pcr;
+
+
+ base_offset = FILE_Pos();
+
+ if ( ACCESS_Frame( 2L ) )
+ return error;
+
+ count = pcs->PosClassRuleCount = GET_UShort();
+
+ FORGET_Frame();
+
+ pcs->PosClassRule = NULL;
+
+ if ( ALLOC_ARRAY( pcs->PosClassRule, count, HB_PosClassRule ) )
+ return error;
+
+ pcr = pcs->PosClassRule;
+
+ for ( n = 0; n < count; n++ )
+ {
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail;
+
+ new_offset = GET_UShort() + base_offset;
+
+ FORGET_Frame();
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = Load_PosClassRule( cpf2, &pcr[n],
+ stream ) ) != HB_Err_Ok )
+ goto Fail;
+ (void)FILE_Seek( cur_offset );
+ }
+
+ return HB_Err_Ok;
+
+Fail:
+ for ( m = 0; m < n; m++ )
+ Free_PosClassRule( &pcr[m] );
+
+ FREE( pcr );
+ return error;
+}
+
+
+static void Free_PosClassSet( HB_PosClassSet* pcs )
+{
+ HB_UShort n, count;
+
+ HB_PosClassRule* pcr;
+
+
+ if ( pcs->PosClassRule )
+ {
+ count = pcs->PosClassRuleCount;
+ pcr = pcs->PosClassRule;
+
+ for ( n = 0; n < count; n++ )
+ Free_PosClassRule( &pcr[n] );
+
+ FREE( pcr );
+ }
+}
+
+
+/* ContextPosFormat2 */
+
+static HB_Error Load_ContextPos2( HB_ContextPosFormat2* cpf2,
+ HB_Stream stream )
+{
+ HB_Error error;
+
+ HB_UShort n, m, count;
+ HB_UInt cur_offset, new_offset, base_offset;
+
+ HB_PosClassSet* pcs;
+
+
+ base_offset = FILE_Pos() - 2;
+
+ if ( ACCESS_Frame( 2L ) )
+ return error;
+
+ new_offset = GET_UShort() + base_offset;
+
+ FORGET_Frame();
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = _HB_OPEN_Load_Coverage( &cpf2->Coverage, stream ) ) != HB_Err_Ok )
+ return error;
+ (void)FILE_Seek( cur_offset );
+
+ if ( ACCESS_Frame( 4L ) )
+ goto Fail3;
+
+ new_offset = GET_UShort() + base_offset;
+
+ /* `PosClassSetCount' is the upper limit for class values, thus we
+ read it now to make an additional safety check. */
+
+ count = cpf2->PosClassSetCount = GET_UShort();
+
+ FORGET_Frame();
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = _HB_OPEN_Load_ClassDefinition( &cpf2->ClassDef, count,
+ stream ) ) != HB_Err_Ok )
+ goto Fail3;
+ (void)FILE_Seek( cur_offset );
+
+ cpf2->PosClassSet = NULL;
+ cpf2->MaxContextLength = 0;
+
+ if ( ALLOC_ARRAY( cpf2->PosClassSet, count, HB_PosClassSet ) )
+ goto Fail2;
+
+ pcs = cpf2->PosClassSet;
+
+ for ( n = 0; n < count; n++ )
+ {
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail1;
+
+ new_offset = GET_UShort() + base_offset;
+
+ FORGET_Frame();
+
+ if ( new_offset != base_offset ) /* not a NULL offset */
+ {
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = Load_PosClassSet( cpf2, &pcs[n],
+ stream ) ) != HB_Err_Ok )
+ goto Fail1;
+ (void)FILE_Seek( cur_offset );
+ }
+ else
+ {
+ /* we create a PosClassSet table with no entries */
+
+ cpf2->PosClassSet[n].PosClassRuleCount = 0;
+ cpf2->PosClassSet[n].PosClassRule = NULL;
+ }
+ }
+
+ return HB_Err_Ok;
+
+Fail1:
+ for ( m = 0; m < n; n++ )
+ Free_PosClassSet( &pcs[m] );
+
+ FREE( pcs );
+
+Fail2:
+ _HB_OPEN_Free_ClassDefinition( &cpf2->ClassDef );
+
+Fail3:
+ _HB_OPEN_Free_Coverage( &cpf2->Coverage );
+ return error;
+}
+
+
+static void Free_ContextPos2( HB_ContextPosFormat2* cpf2 )
+{
+ HB_UShort n, count;
+
+ HB_PosClassSet* pcs;
+
+
+ if ( cpf2->PosClassSet )
+ {
+ count = cpf2->PosClassSetCount;
+ pcs = cpf2->PosClassSet;
+
+ for ( n = 0; n < count; n++ )
+ Free_PosClassSet( &pcs[n] );
+
+ FREE( pcs );
+ }
+
+ _HB_OPEN_Free_ClassDefinition( &cpf2->ClassDef );
+ _HB_OPEN_Free_Coverage( &cpf2->Coverage );
+}
+
+
+/* ContextPosFormat3 */
+
+static HB_Error Load_ContextPos3( HB_ContextPosFormat3* cpf3,
+ HB_Stream stream )
+{
+ HB_Error error;
+
+ HB_UShort n, count;
+ HB_UInt cur_offset, new_offset, base_offset;
+
+ HB_Coverage* c;
+ HB_PosLookupRecord* plr;
+
+
+ base_offset = FILE_Pos() - 2L;
+
+ if ( ACCESS_Frame( 4L ) )
+ return error;
+
+ cpf3->GlyphCount = GET_UShort();
+ cpf3->PosCount = GET_UShort();
+
+ FORGET_Frame();
+
+ cpf3->Coverage = NULL;
+
+ count = cpf3->GlyphCount;
+
+ if ( ALLOC_ARRAY( cpf3->Coverage, count, HB_Coverage ) )
+ return error;
+
+ c = cpf3->Coverage;
+
+ for ( n = 0; n < count; n++ )
+ {
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail2;
+
+ new_offset = GET_UShort() + base_offset;
+
+ FORGET_Frame();
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = _HB_OPEN_Load_Coverage( &c[n], stream ) ) != HB_Err_Ok )
+ goto Fail2;
+ (void)FILE_Seek( cur_offset );
+ }
+
+ cpf3->PosLookupRecord = NULL;
+
+ count = cpf3->PosCount;
+
+ if ( ALLOC_ARRAY( cpf3->PosLookupRecord, count, HB_PosLookupRecord ) )
+ goto Fail2;
+
+ plr = cpf3->PosLookupRecord;
+
+ if ( ACCESS_Frame( count * 4L ) )
+ goto Fail1;
+
+ for ( n = 0; n < count; n++ )
+ {
+ plr[n].SequenceIndex = GET_UShort();
+ plr[n].LookupListIndex = GET_UShort();
+ }
+
+ FORGET_Frame();
+
+ return HB_Err_Ok;
+
+Fail1:
+ FREE( plr );
+
+Fail2:
+ for ( n = 0; n < count; n++ )
+ _HB_OPEN_Free_Coverage( &c[n] );
+
+ FREE( c );
+ return error;
+}
+
+
+static void Free_ContextPos3( HB_ContextPosFormat3* cpf3 )
+{
+ HB_UShort n, count;
+
+ HB_Coverage* c;
+
+
+ FREE( cpf3->PosLookupRecord );
+
+ if ( cpf3->Coverage )
+ {
+ count = cpf3->GlyphCount;
+ c = cpf3->Coverage;
+
+ for ( n = 0; n < count; n++ )
+ _HB_OPEN_Free_Coverage( &c[n] );
+
+ FREE( c );
+ }
+}
+
+
+/* ContextPos */
+
+static HB_Error Load_ContextPos( HB_GPOS_SubTable* st,
+ HB_Stream stream )
+{
+ HB_Error error;
+ HB_ContextPos* cp = &st->context;
+
+
+ if ( ACCESS_Frame( 2L ) )
+ return error;
+
+ cp->PosFormat = GET_UShort();
+
+ FORGET_Frame();
+
+ switch ( cp->PosFormat )
+ {
+ case 1:
+ return Load_ContextPos1( &cp->cpf.cpf1, stream );
+
+ case 2:
+ return Load_ContextPos2( &cp->cpf.cpf2, stream );
+
+ case 3:
+ return Load_ContextPos3( &cp->cpf.cpf3, stream );
+
+ default:
+ return ERR(HB_Err_Invalid_SubTable_Format);
+ }
+
+ return HB_Err_Ok; /* never reached */
+}
+
+
+static void Free_ContextPos( HB_GPOS_SubTable* st )
+{
+ HB_ContextPos* cp = &st->context;
+
+ switch ( cp->PosFormat )
+ {
+ case 1: Free_ContextPos1( &cp->cpf.cpf1 ); break;
+ case 2: Free_ContextPos2( &cp->cpf.cpf2 ); break;
+ case 3: Free_ContextPos3( &cp->cpf.cpf3 ); break;
+ default: break;
+ }
+}
+
+
+static HB_Error Lookup_ContextPos1( GPOS_Instance* gpi,
+ HB_ContextPosFormat1* cpf1,
+ HB_Buffer buffer,
+ HB_UShort flags,
+ HB_UShort context_length,
+ int nesting_level )
+{
+ HB_UShort index, property;
+ HB_UShort i, j, k, numpr;
+ HB_Error error;
+ HB_GPOSHeader* gpos = gpi->gpos;
+
+ HB_PosRule* pr;
+ hb_ot_layout_t* layout;
+
+
+ layout = gpos->layout;
+
+ if ( CHECK_Property( layout, IN_CURITEM(), flags, &property ) )
+ return error;
+
+ error = _HB_OPEN_Coverage_Index( &cpf1->Coverage, IN_CURGLYPH(), &index );
+ if ( error )
+ return error;
+
+ pr = cpf1->PosRuleSet[index].PosRule;
+ numpr = cpf1->PosRuleSet[index].PosRuleCount;
+
+ for ( k = 0; k < numpr; k++ )
+ {
+ if ( context_length != 0xFFFF && context_length < pr[k].GlyphCount )
+ goto next_posrule;
+
+ if ( buffer->in_pos + pr[k].GlyphCount > buffer->in_length )
+ goto next_posrule; /* context is too long */
+
+ for ( i = 1, j = buffer->in_pos + 1; i < pr[k].GlyphCount; i++, j++ )
+ {
+ while ( CHECK_Property( layout, IN_ITEM( j ), flags, &property ) )
+ {
+ if ( error && error != HB_Err_Not_Covered )
+ return error;
+
+ if ( j + pr[k].GlyphCount - i == (HB_Int)buffer->in_length )
+ goto next_posrule;
+ j++;
+ }
+
+ if ( IN_GLYPH( j ) != pr[k].Input[i - 1] )
+ goto next_posrule;
+ }
+
+ return Do_ContextPos( gpi, pr[k].GlyphCount,
+ pr[k].PosCount, pr[k].PosLookupRecord,
+ buffer,
+ nesting_level );
+
+ next_posrule:
+ ;
+ }
+
+ return HB_Err_Not_Covered;
+}
+
+
+static HB_Error Lookup_ContextPos2( GPOS_Instance* gpi,
+ HB_ContextPosFormat2* cpf2,
+ HB_Buffer buffer,
+ HB_UShort flags,
+ HB_UShort context_length,
+ int nesting_level )
+{
+ HB_UShort index, property;
+ HB_Error error;
+ HB_UShort i, j, k, known_classes;
+
+ HB_UShort* classes;
+ HB_UShort* cl;
+ HB_GPOSHeader* gpos = gpi->gpos;
+
+ HB_PosClassSet* pcs;
+ HB_PosClassRule* pr;
+ hb_ot_layout_t* layout;
+
+
+ layout = gpos->layout;
+
+ if ( CHECK_Property( layout, IN_CURITEM(), flags, &property ) )
+ return error;
+
+ /* Note: The coverage table in format 2 doesn't give an index into
+ anything. It just lets us know whether or not we need to
+ do any lookup at all. */
+
+ error = _HB_OPEN_Coverage_Index( &cpf2->Coverage, IN_CURGLYPH(), &index );
+ if ( error )
+ return error;
+
+ if (cpf2->MaxContextLength < 1)
+ return HB_Err_Not_Covered;
+
+ if ( ALLOC_ARRAY( classes, cpf2->MaxContextLength, HB_UShort ) )
+ return error;
+
+ error = _HB_OPEN_Get_Class( &cpf2->ClassDef, IN_CURGLYPH(),
+ &classes[0], NULL );
+ if ( error && error != HB_Err_Not_Covered )
+ goto End;
+ known_classes = 0;
+
+ pcs = &cpf2->PosClassSet[classes[0]];
+ if ( !pcs )
+ {
+ error = ERR(HB_Err_Invalid_SubTable);
+ goto End;
+ }
+
+ for ( k = 0; k < pcs->PosClassRuleCount; k++ )
+ {
+ pr = &pcs->PosClassRule[k];
+
+ if ( context_length != 0xFFFF && context_length < pr->GlyphCount )
+ goto next_posclassrule;
+
+ if ( buffer->in_pos + pr->GlyphCount > buffer->in_length )
+ goto next_posclassrule; /* context is too long */
+
+ cl = pr->Class;
+
+ /* Start at 1 because [0] is implied */
+
+ for ( i = 1, j = buffer->in_pos + 1; i < pr->GlyphCount; i++, j++ )
+ {
+ while ( CHECK_Property( layout, IN_ITEM( j ), flags, &property ) )
+ {
+ if ( error && error != HB_Err_Not_Covered )
+ goto End;
+
+ if ( j + pr->GlyphCount - i == (HB_Int)buffer->in_length )
+ goto next_posclassrule;
+ j++;
+ }
+
+ if ( i > known_classes )
+ {
+ /* Keeps us from having to do this for each rule */
+
+ error = _HB_OPEN_Get_Class( &cpf2->ClassDef, IN_GLYPH( j ), &classes[i], NULL );
+ if ( error && error != HB_Err_Not_Covered )
+ goto End;
+ known_classes = i;
+ }
+
+ if ( cl[i - 1] != classes[i] )
+ goto next_posclassrule;
+ }
+
+ error = Do_ContextPos( gpi, pr->GlyphCount,
+ pr->PosCount, pr->PosLookupRecord,
+ buffer,
+ nesting_level );
+ goto End;
+
+ next_posclassrule:
+ ;
+ }
+
+ error = HB_Err_Not_Covered;
+
+End:
+ FREE( classes );
+ return error;
+}
+
+
+static HB_Error Lookup_ContextPos3( GPOS_Instance* gpi,
+ HB_ContextPosFormat3* cpf3,
+ HB_Buffer buffer,
+ HB_UShort flags,
+ HB_UShort context_length,
+ int nesting_level )
+{
+ HB_Error error;
+ HB_UShort index, i, j, property;
+ HB_GPOSHeader* gpos = gpi->gpos;
+
+ HB_Coverage* c;
+ hb_ot_layout_t* layout;
+
+
+ layout = gpos->layout;
+
+ if ( CHECK_Property( layout, IN_CURITEM(), flags, &property ) )
+ return error;
+
+ if ( context_length != 0xFFFF && context_length < cpf3->GlyphCount )
+ return HB_Err_Not_Covered;
+
+ if ( buffer->in_pos + cpf3->GlyphCount > buffer->in_length )
+ return HB_Err_Not_Covered; /* context is too long */
+
+ c = cpf3->Coverage;
+
+ for ( i = 1, j = 1; i < cpf3->GlyphCount; i++, j++ )
+ {
+ while ( CHECK_Property( layout, IN_ITEM( j ), flags, &property ) )
+ {
+ if ( error && error != HB_Err_Not_Covered )
+ return error;
+
+ if ( j + cpf3->GlyphCount - i == (HB_Int)buffer->in_length )
+ return HB_Err_Not_Covered;
+ j++;
+ }
+
+ error = _HB_OPEN_Coverage_Index( &c[i], IN_GLYPH( j ), &index );
+ if ( error )
+ return error;
+ }
+
+ return Do_ContextPos( gpi, cpf3->GlyphCount,
+ cpf3->PosCount, cpf3->PosLookupRecord,
+ buffer,
+ nesting_level );
+}
+
+
+static HB_Error Lookup_ContextPos( GPOS_Instance* gpi,
+ HB_GPOS_SubTable* st,
+ HB_Buffer buffer,
+ HB_UShort flags,
+ HB_UShort context_length,
+ int nesting_level )
+{
+ HB_ContextPos* cp = &st->context;
+
+ switch ( cp->PosFormat )
+ {
+ case 1:
+ return Lookup_ContextPos1( gpi, &cp->cpf.cpf1, buffer,
+ flags, context_length, nesting_level );
+
+ case 2:
+ return Lookup_ContextPos2( gpi, &cp->cpf.cpf2, buffer,
+ flags, context_length, nesting_level );
+
+ case 3:
+ return Lookup_ContextPos3( gpi, &cp->cpf.cpf3, buffer,
+ flags, context_length, nesting_level );
+
+ default:
+ return ERR(HB_Err_Invalid_SubTable_Format);
+ }
+
+ return HB_Err_Ok; /* never reached */
+}
+
+
+/* LookupType 8 */
+
+/* ChainPosRule */
+
+static HB_Error Load_ChainPosRule( HB_ChainPosRule* cpr,
+ HB_Stream stream )
+{
+ HB_Error error;
+
+ HB_UShort n, count;
+ HB_UShort* b;
+ HB_UShort* i;
+ HB_UShort* l;
+
+ HB_PosLookupRecord* plr;
+
+
+ if ( ACCESS_Frame( 2L ) )
+ return error;
+
+ cpr->BacktrackGlyphCount = GET_UShort();
+
+ FORGET_Frame();
+
+ cpr->Backtrack = NULL;
+
+ count = cpr->BacktrackGlyphCount;
+
+ if ( ALLOC_ARRAY( cpr->Backtrack, count, HB_UShort ) )
+ return error;
+
+ b = cpr->Backtrack;
+
+ if ( ACCESS_Frame( count * 2L ) )
+ goto Fail4;
+
+ for ( n = 0; n < count; n++ )
+ b[n] = GET_UShort();
+
+ FORGET_Frame();
+
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail4;
+
+ cpr->InputGlyphCount = GET_UShort();
+
+ FORGET_Frame();
+
+ cpr->Input = NULL;
+
+ count = cpr->InputGlyphCount - 1; /* only InputGlyphCount - 1 elements */
+
+ if ( ALLOC_ARRAY( cpr->Input, count, HB_UShort ) )
+ goto Fail4;
+
+ i = cpr->Input;
+
+ if ( ACCESS_Frame( count * 2L ) )
+ goto Fail3;
+
+ for ( n = 0; n < count; n++ )
+ i[n] = GET_UShort();
+
+ FORGET_Frame();
+
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail3;
+
+ cpr->LookaheadGlyphCount = GET_UShort();
+
+ FORGET_Frame();
+
+ cpr->Lookahead = NULL;
+
+ count = cpr->LookaheadGlyphCount;
+
+ if ( ALLOC_ARRAY( cpr->Lookahead, count, HB_UShort ) )
+ goto Fail3;
+
+ l = cpr->Lookahead;
+
+ if ( ACCESS_Frame( count * 2L ) )
+ goto Fail2;
+
+ for ( n = 0; n < count; n++ )
+ l[n] = GET_UShort();
+
+ FORGET_Frame();
+
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail2;
+
+ cpr->PosCount = GET_UShort();
+
+ FORGET_Frame();
+
+ cpr->PosLookupRecord = NULL;
+
+ count = cpr->PosCount;
+
+ if ( ALLOC_ARRAY( cpr->PosLookupRecord, count, HB_PosLookupRecord ) )
+ goto Fail2;
+
+ plr = cpr->PosLookupRecord;
+
+ if ( ACCESS_Frame( count * 4L ) )
+ goto Fail1;
+
+ for ( n = 0; n < count; n++ )
+ {
+ plr[n].SequenceIndex = GET_UShort();
+ plr[n].LookupListIndex = GET_UShort();
+ }
+
+ FORGET_Frame();
+
+ return HB_Err_Ok;
+
+Fail1:
+ FREE( plr );
+
+Fail2:
+ FREE( l );
+
+Fail3:
+ FREE( i );
+
+Fail4:
+ FREE( b );
+ return error;
+}
+
+
+static void Free_ChainPosRule( HB_ChainPosRule* cpr )
+{
+ FREE( cpr->PosLookupRecord );
+ FREE( cpr->Lookahead );
+ FREE( cpr->Input );
+ FREE( cpr->Backtrack );
+}
+
+
+/* ChainPosRuleSet */
+
+static HB_Error Load_ChainPosRuleSet( HB_ChainPosRuleSet* cprs,
+ HB_Stream stream )
+{
+ HB_Error error;
+
+ HB_UShort n, m, count;
+ HB_UInt cur_offset, new_offset, base_offset;
+
+ HB_ChainPosRule* cpr;
+
+
+ base_offset = FILE_Pos();
+
+ if ( ACCESS_Frame( 2L ) )
+ return error;
+
+ count = cprs->ChainPosRuleCount = GET_UShort();
+
+ FORGET_Frame();
+
+ cprs->ChainPosRule = NULL;
+
+ if ( ALLOC_ARRAY( cprs->ChainPosRule, count, HB_ChainPosRule ) )
+ return error;
+
+ cpr = cprs->ChainPosRule;
+
+ for ( n = 0; n < count; n++ )
+ {
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail;
+
+ new_offset = GET_UShort() + base_offset;
+
+ FORGET_Frame();
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = Load_ChainPosRule( &cpr[n], stream ) ) != HB_Err_Ok )
+ goto Fail;
+ (void)FILE_Seek( cur_offset );
+ }
+
+ return HB_Err_Ok;
+
+Fail:
+ for ( m = 0; m < n; m++ )
+ Free_ChainPosRule( &cpr[m] );
+
+ FREE( cpr );
+ return error;
+}
+
+
+static void Free_ChainPosRuleSet( HB_ChainPosRuleSet* cprs )
+{
+ HB_UShort n, count;
+
+ HB_ChainPosRule* cpr;
+
+
+ if ( cprs->ChainPosRule )
+ {
+ count = cprs->ChainPosRuleCount;
+ cpr = cprs->ChainPosRule;
+
+ for ( n = 0; n < count; n++ )
+ Free_ChainPosRule( &cpr[n] );
+
+ FREE( cpr );
+ }
+}
+
+
+/* ChainContextPosFormat1 */
+
+static HB_Error Load_ChainContextPos1( HB_ChainContextPosFormat1* ccpf1,
+ HB_Stream stream )
+{
+ HB_Error error;
+
+ HB_UShort n, m, count;
+ HB_UInt cur_offset, new_offset, base_offset;
+
+ HB_ChainPosRuleSet* cprs;
+
+
+ base_offset = FILE_Pos() - 2L;
+
+ if ( ACCESS_Frame( 2L ) )
+ return error;
+
+ new_offset = GET_UShort() + base_offset;
+
+ FORGET_Frame();
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = _HB_OPEN_Load_Coverage( &ccpf1->Coverage, stream ) ) != HB_Err_Ok )
+ return error;
+ (void)FILE_Seek( cur_offset );
+
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail2;
+
+ count = ccpf1->ChainPosRuleSetCount = GET_UShort();
+
+ FORGET_Frame();
+
+ ccpf1->ChainPosRuleSet = NULL;
+
+ if ( ALLOC_ARRAY( ccpf1->ChainPosRuleSet, count, HB_ChainPosRuleSet ) )
+ goto Fail2;
+
+ cprs = ccpf1->ChainPosRuleSet;
+
+ for ( n = 0; n < count; n++ )
+ {
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail1;
+
+ new_offset = GET_UShort() + base_offset;
+
+ FORGET_Frame();
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = Load_ChainPosRuleSet( &cprs[n], stream ) ) != HB_Err_Ok )
+ goto Fail1;
+ (void)FILE_Seek( cur_offset );
+ }
+
+ return HB_Err_Ok;
+
+Fail1:
+ for ( m = 0; m < n; m++ )
+ Free_ChainPosRuleSet( &cprs[m] );
+
+ FREE( cprs );
+
+Fail2:
+ _HB_OPEN_Free_Coverage( &ccpf1->Coverage );
+ return error;
+}
+
+
+static void Free_ChainContextPos1( HB_ChainContextPosFormat1* ccpf1 )
+{
+ HB_UShort n, count;
+
+ HB_ChainPosRuleSet* cprs;
+
+
+ if ( ccpf1->ChainPosRuleSet )
+ {
+ count = ccpf1->ChainPosRuleSetCount;
+ cprs = ccpf1->ChainPosRuleSet;
+
+ for ( n = 0; n < count; n++ )
+ Free_ChainPosRuleSet( &cprs[n] );
+
+ FREE( cprs );
+ }
+
+ _HB_OPEN_Free_Coverage( &ccpf1->Coverage );
+}
+
+
+/* ChainPosClassRule */
+
+static HB_Error Load_ChainPosClassRule(
+ HB_ChainContextPosFormat2* ccpf2,
+ HB_ChainPosClassRule* cpcr,
+ HB_Stream stream )
+{
+ HB_Error error;
+
+ HB_UShort n, count;
+
+ HB_UShort* b;
+ HB_UShort* i;
+ HB_UShort* l;
+ HB_PosLookupRecord* plr;
+
+
+ if ( ACCESS_Frame( 2L ) )
+ return error;
+
+ cpcr->BacktrackGlyphCount = GET_UShort();
+
+ FORGET_Frame();
+
+ if ( cpcr->BacktrackGlyphCount > ccpf2->MaxBacktrackLength )
+ ccpf2->MaxBacktrackLength = cpcr->BacktrackGlyphCount;
+
+ cpcr->Backtrack = NULL;
+
+ count = cpcr->BacktrackGlyphCount;
+
+ if ( ALLOC_ARRAY( cpcr->Backtrack, count, HB_UShort ) )
+ return error;
+
+ b = cpcr->Backtrack;
+
+ if ( ACCESS_Frame( count * 2L ) )
+ goto Fail4;
+
+ for ( n = 0; n < count; n++ )
+ b[n] = GET_UShort();
+
+ FORGET_Frame();
+
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail4;
+
+ cpcr->InputGlyphCount = GET_UShort();
+
+ if ( cpcr->InputGlyphCount > ccpf2->MaxInputLength )
+ ccpf2->MaxInputLength = cpcr->InputGlyphCount;
+
+ FORGET_Frame();
+
+ cpcr->Input = NULL;
+
+ count = cpcr->InputGlyphCount - 1; /* only InputGlyphCount - 1 elements */
+
+ if ( ALLOC_ARRAY( cpcr->Input, count, HB_UShort ) )
+ goto Fail4;
+
+ i = cpcr->Input;
+
+ if ( ACCESS_Frame( count * 2L ) )
+ goto Fail3;
+
+ for ( n = 0; n < count; n++ )
+ i[n] = GET_UShort();
+
+ FORGET_Frame();
+
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail3;
+
+ cpcr->LookaheadGlyphCount = GET_UShort();
+
+ FORGET_Frame();
+
+ if ( cpcr->LookaheadGlyphCount > ccpf2->MaxLookaheadLength )
+ ccpf2->MaxLookaheadLength = cpcr->LookaheadGlyphCount;
+
+ cpcr->Lookahead = NULL;
+
+ count = cpcr->LookaheadGlyphCount;
+
+ if ( ALLOC_ARRAY( cpcr->Lookahead, count, HB_UShort ) )
+ goto Fail3;
+
+ l = cpcr->Lookahead;
+
+ if ( ACCESS_Frame( count * 2L ) )
+ goto Fail2;
+
+ for ( n = 0; n < count; n++ )
+ l[n] = GET_UShort();
+
+ FORGET_Frame();
+
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail2;
+
+ cpcr->PosCount = GET_UShort();
+
+ FORGET_Frame();
+
+ cpcr->PosLookupRecord = NULL;
+
+ count = cpcr->PosCount;
+
+ if ( ALLOC_ARRAY( cpcr->PosLookupRecord, count, HB_PosLookupRecord ) )
+ goto Fail2;
+
+ plr = cpcr->PosLookupRecord;
+
+ if ( ACCESS_Frame( count * 4L ) )
+ goto Fail1;
+
+ for ( n = 0; n < count; n++ )
+ {
+ plr[n].SequenceIndex = GET_UShort();
+ plr[n].LookupListIndex = GET_UShort();
+ }
+
+ FORGET_Frame();
+
+ return HB_Err_Ok;
+
+Fail1:
+ FREE( plr );
+
+Fail2:
+ FREE( l );
+
+Fail3:
+ FREE( i );
+
+Fail4:
+ FREE( b );
+ return error;
+}
+
+
+static void Free_ChainPosClassRule( HB_ChainPosClassRule* cpcr )
+{
+ FREE( cpcr->PosLookupRecord );
+ FREE( cpcr->Lookahead );
+ FREE( cpcr->Input );
+ FREE( cpcr->Backtrack );
+}
+
+
+/* PosClassSet */
+
+static HB_Error Load_ChainPosClassSet(
+ HB_ChainContextPosFormat2* ccpf2,
+ HB_ChainPosClassSet* cpcs,
+ HB_Stream stream )
+{
+ HB_Error error;
+
+ HB_UShort n, m, count;
+ HB_UInt cur_offset, new_offset, base_offset;
+
+ HB_ChainPosClassRule* cpcr;
+
+
+ base_offset = FILE_Pos();
+
+ if ( ACCESS_Frame( 2L ) )
+ return error;
+
+ count = cpcs->ChainPosClassRuleCount = GET_UShort();
+
+ FORGET_Frame();
+
+ cpcs->ChainPosClassRule = NULL;
+
+ if ( ALLOC_ARRAY( cpcs->ChainPosClassRule, count,
+ HB_ChainPosClassRule ) )
+ return error;
+
+ cpcr = cpcs->ChainPosClassRule;
+
+ for ( n = 0; n < count; n++ )
+ {
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail;
+
+ new_offset = GET_UShort() + base_offset;
+
+ FORGET_Frame();
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = Load_ChainPosClassRule( ccpf2, &cpcr[n],
+ stream ) ) != HB_Err_Ok )
+ goto Fail;
+ (void)FILE_Seek( cur_offset );
+ }
+
+ return HB_Err_Ok;
+
+Fail:
+ for ( m = 0; m < n; m++ )
+ Free_ChainPosClassRule( &cpcr[m] );
+
+ FREE( cpcr );
+ return error;
+}
+
+
+static void Free_ChainPosClassSet( HB_ChainPosClassSet* cpcs )
+{
+ HB_UShort n, count;
+
+ HB_ChainPosClassRule* cpcr;
+
+
+ if ( cpcs->ChainPosClassRule )
+ {
+ count = cpcs->ChainPosClassRuleCount;
+ cpcr = cpcs->ChainPosClassRule;
+
+ for ( n = 0; n < count; n++ )
+ Free_ChainPosClassRule( &cpcr[n] );
+
+ FREE( cpcr );
+ }
+}
+
+
+/* ChainContextPosFormat2 */
+
+static HB_Error Load_ChainContextPos2( HB_ChainContextPosFormat2* ccpf2,
+ HB_Stream stream )
+{
+ HB_Error error;
+
+ HB_UShort n, m, count;
+ HB_UInt cur_offset, new_offset, base_offset;
+ HB_UInt backtrack_offset, input_offset, lookahead_offset;
+
+ HB_ChainPosClassSet* cpcs;
+
+
+ base_offset = FILE_Pos() - 2;
+
+ if ( ACCESS_Frame( 2L ) )
+ return error;
+
+ new_offset = GET_UShort() + base_offset;
+
+ FORGET_Frame();
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = _HB_OPEN_Load_Coverage( &ccpf2->Coverage, stream ) ) != HB_Err_Ok )
+ return error;
+ (void)FILE_Seek( cur_offset );
+
+ if ( ACCESS_Frame( 8L ) )
+ goto Fail5;
+
+ backtrack_offset = GET_UShort();
+ input_offset = GET_UShort();
+ lookahead_offset = GET_UShort();
+
+ /* `ChainPosClassSetCount' is the upper limit for input class values,
+ thus we read it now to make an additional safety check. No limit
+ is known or needed for the other two class definitions */
+
+ count = ccpf2->ChainPosClassSetCount = GET_UShort();
+
+ FORGET_Frame();
+
+ if ( ( error = _HB_OPEN_Load_EmptyOrClassDefinition( &ccpf2->BacktrackClassDef, 65535,
+ backtrack_offset, base_offset,
+ stream ) ) != HB_Err_Ok )
+ goto Fail5;
+ if ( ( error = _HB_OPEN_Load_EmptyOrClassDefinition( &ccpf2->InputClassDef, count,
+ input_offset, base_offset,
+ stream ) ) != HB_Err_Ok )
+ goto Fail4;
+ if ( ( error = _HB_OPEN_Load_EmptyOrClassDefinition( &ccpf2->LookaheadClassDef, 65535,
+ lookahead_offset, base_offset,
+ stream ) ) != HB_Err_Ok )
+ goto Fail3;
+
+ ccpf2->ChainPosClassSet = NULL;
+ ccpf2->MaxBacktrackLength = 0;
+ ccpf2->MaxInputLength = 0;
+ ccpf2->MaxLookaheadLength = 0;
+
+ if ( ALLOC_ARRAY( ccpf2->ChainPosClassSet, count, HB_ChainPosClassSet ) )
+ goto Fail2;
+
+ cpcs = ccpf2->ChainPosClassSet;
+
+ for ( n = 0; n < count; n++ )
+ {
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail1;
+
+ new_offset = GET_UShort() + base_offset;
+
+ FORGET_Frame();
+
+ if ( new_offset != base_offset ) /* not a NULL offset */
+ {
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = Load_ChainPosClassSet( ccpf2, &cpcs[n],
+ stream ) ) != HB_Err_Ok )
+ goto Fail1;
+ (void)FILE_Seek( cur_offset );
+ }
+ else
+ {
+ /* we create a ChainPosClassSet table with no entries */
+
+ ccpf2->ChainPosClassSet[n].ChainPosClassRuleCount = 0;
+ ccpf2->ChainPosClassSet[n].ChainPosClassRule = NULL;
+ }
+ }
+
+ return HB_Err_Ok;
+
+Fail1:
+ for ( m = 0; m < n; m++ )
+ Free_ChainPosClassSet( &cpcs[m] );
+
+ FREE( cpcs );
+
+Fail2:
+ _HB_OPEN_Free_ClassDefinition( &ccpf2->LookaheadClassDef );
+
+Fail3:
+ _HB_OPEN_Free_ClassDefinition( &ccpf2->InputClassDef );
+
+Fail4:
+ _HB_OPEN_Free_ClassDefinition( &ccpf2->BacktrackClassDef );
+
+Fail5:
+ _HB_OPEN_Free_Coverage( &ccpf2->Coverage );
+ return error;
+}
+
+
+static void Free_ChainContextPos2( HB_ChainContextPosFormat2* ccpf2 )
+{
+ HB_UShort n, count;
+
+ HB_ChainPosClassSet* cpcs;
+
+
+ if ( ccpf2->ChainPosClassSet )
+ {
+ count = ccpf2->ChainPosClassSetCount;
+ cpcs = ccpf2->ChainPosClassSet;
+
+ for ( n = 0; n < count; n++ )
+ Free_ChainPosClassSet( &cpcs[n] );
+
+ FREE( cpcs );
+ }
+
+ _HB_OPEN_Free_ClassDefinition( &ccpf2->LookaheadClassDef );
+ _HB_OPEN_Free_ClassDefinition( &ccpf2->InputClassDef );
+ _HB_OPEN_Free_ClassDefinition( &ccpf2->BacktrackClassDef );
+
+ _HB_OPEN_Free_Coverage( &ccpf2->Coverage );
+}
+
+
+/* ChainContextPosFormat3 */
+
+static HB_Error Load_ChainContextPos3( HB_ChainContextPosFormat3* ccpf3,
+ HB_Stream stream )
+{
+ HB_Error error;
+
+ HB_UShort n, nb, ni, nl, m, count;
+ HB_UShort backtrack_count, input_count, lookahead_count;
+ HB_UInt cur_offset, new_offset, base_offset;
+
+ HB_Coverage* b;
+ HB_Coverage* i;
+ HB_Coverage* l;
+ HB_PosLookupRecord* plr;
+
+
+ base_offset = FILE_Pos() - 2L;
+
+ if ( ACCESS_Frame( 2L ) )
+ return error;
+
+ ccpf3->BacktrackGlyphCount = GET_UShort();
+
+ FORGET_Frame();
+
+ ccpf3->BacktrackCoverage = NULL;
+
+ backtrack_count = ccpf3->BacktrackGlyphCount;
+
+ if ( ALLOC_ARRAY( ccpf3->BacktrackCoverage, backtrack_count,
+ HB_Coverage ) )
+ return error;
+
+ b = ccpf3->BacktrackCoverage;
+
+ for ( nb = 0; nb < backtrack_count; nb++ )
+ {
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail4;
+
+ new_offset = GET_UShort() + base_offset;
+
+ FORGET_Frame();
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = _HB_OPEN_Load_Coverage( &b[nb], stream ) ) != HB_Err_Ok )
+ goto Fail4;
+ (void)FILE_Seek( cur_offset );
+ }
+
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail4;
+
+ ccpf3->InputGlyphCount = GET_UShort();
+
+ FORGET_Frame();
+
+ ccpf3->InputCoverage = NULL;
+
+ input_count = ccpf3->InputGlyphCount;
+
+ if ( ALLOC_ARRAY( ccpf3->InputCoverage, input_count, HB_Coverage ) )
+ goto Fail4;
+
+ i = ccpf3->InputCoverage;
+
+ for ( ni = 0; ni < input_count; ni++ )
+ {
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail3;
+
+ new_offset = GET_UShort() + base_offset;
+
+ FORGET_Frame();
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = _HB_OPEN_Load_Coverage( &i[ni], stream ) ) != HB_Err_Ok )
+ goto Fail3;
+ (void)FILE_Seek( cur_offset );
+ }
+
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail3;
+
+ ccpf3->LookaheadGlyphCount = GET_UShort();
+
+ FORGET_Frame();
+
+ ccpf3->LookaheadCoverage = NULL;
+
+ lookahead_count = ccpf3->LookaheadGlyphCount;
+
+ if ( ALLOC_ARRAY( ccpf3->LookaheadCoverage, lookahead_count,
+ HB_Coverage ) )
+ goto Fail3;
+
+ l = ccpf3->LookaheadCoverage;
+
+ for ( nl = 0; nl < lookahead_count; nl++ )
+ {
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail2;
+
+ new_offset = GET_UShort() + base_offset;
+
+ FORGET_Frame();
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = _HB_OPEN_Load_Coverage( &l[nl], stream ) ) != HB_Err_Ok )
+ goto Fail2;
+ (void)FILE_Seek( cur_offset );
+ }
+
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail2;
+
+ ccpf3->PosCount = GET_UShort();
+
+ FORGET_Frame();
+
+ ccpf3->PosLookupRecord = NULL;
+
+ count = ccpf3->PosCount;
+
+ if ( ALLOC_ARRAY( ccpf3->PosLookupRecord, count, HB_PosLookupRecord ) )
+ goto Fail2;
+
+ plr = ccpf3->PosLookupRecord;
+
+ if ( ACCESS_Frame( count * 4L ) )
+ goto Fail1;
+
+ for ( n = 0; n < count; n++ )
+ {
+ plr[n].SequenceIndex = GET_UShort();
+ plr[n].LookupListIndex = GET_UShort();
+ }
+
+ FORGET_Frame();
+
+ return HB_Err_Ok;
+
+Fail1:
+ FREE( plr );
+
+Fail2:
+ for ( m = 0; m < nl; m++ )
+ _HB_OPEN_Free_Coverage( &l[m] );
+
+ FREE( l );
+
+Fail3:
+ for ( m = 0; m < ni; m++ )
+ _HB_OPEN_Free_Coverage( &i[m] );
+
+ FREE( i );
+
+Fail4:
+ for ( m = 0; m < nb; m++ )
+ _HB_OPEN_Free_Coverage( &b[m] );
+
+ FREE( b );
+ return error;
+}
+
+
+static void Free_ChainContextPos3( HB_ChainContextPosFormat3* ccpf3 )
+{
+ HB_UShort n, count;
+
+ HB_Coverage* c;
+
+
+ FREE( ccpf3->PosLookupRecord );
+
+ if ( ccpf3->LookaheadCoverage )
+ {
+ count = ccpf3->LookaheadGlyphCount;
+ c = ccpf3->LookaheadCoverage;
+
+ for ( n = 0; n < count; n++ )
+ _HB_OPEN_Free_Coverage( &c[n] );
+
+ FREE( c );
+ }
+
+ if ( ccpf3->InputCoverage )
+ {
+ count = ccpf3->InputGlyphCount;
+ c = ccpf3->InputCoverage;
+
+ for ( n = 0; n < count; n++ )
+ _HB_OPEN_Free_Coverage( &c[n] );
+
+ FREE( c );
+ }
+
+ if ( ccpf3->BacktrackCoverage )
+ {
+ count = ccpf3->BacktrackGlyphCount;
+ c = ccpf3->BacktrackCoverage;
+
+ for ( n = 0; n < count; n++ )
+ _HB_OPEN_Free_Coverage( &c[n] );
+
+ FREE( c );
+ }
+}
+
+
+/* ChainContextPos */
+
+static HB_Error Load_ChainContextPos( HB_GPOS_SubTable* st,
+ HB_Stream stream )
+{
+ HB_Error error;
+ HB_ChainContextPos* ccp = &st->chain;
+
+
+ if ( ACCESS_Frame( 2L ) )
+ return error;
+
+ ccp->PosFormat = GET_UShort();
+
+ FORGET_Frame();
+
+ switch ( ccp->PosFormat )
+ {
+ case 1:
+ return Load_ChainContextPos1( &ccp->ccpf.ccpf1, stream );
+
+ case 2:
+ return Load_ChainContextPos2( &ccp->ccpf.ccpf2, stream );
+
+ case 3:
+ return Load_ChainContextPos3( &ccp->ccpf.ccpf3, stream );
+
+ default:
+ return ERR(HB_Err_Invalid_SubTable_Format);
+ }
+
+ return HB_Err_Ok; /* never reached */
+}
+
+
+static void Free_ChainContextPos( HB_GPOS_SubTable* st )
+{
+ HB_ChainContextPos* ccp = &st->chain;
+
+ switch ( ccp->PosFormat )
+ {
+ case 1: Free_ChainContextPos1( &ccp->ccpf.ccpf1 ); break;
+ case 2: Free_ChainContextPos2( &ccp->ccpf.ccpf2 ); break;
+ case 3: Free_ChainContextPos3( &ccp->ccpf.ccpf3 ); break;
+ default: break;
+ }
+}
+
+
+static HB_Error Lookup_ChainContextPos1(
+ GPOS_Instance* gpi,
+ HB_ChainContextPosFormat1* ccpf1,
+ HB_Buffer buffer,
+ HB_UShort flags,
+ HB_UShort context_length,
+ int nesting_level )
+{
+ HB_UShort index, property;
+ HB_UShort i, j, k, num_cpr;
+ HB_UShort bgc, igc, lgc;
+ HB_Error error;
+ HB_GPOSHeader* gpos = gpi->gpos;
+
+ HB_ChainPosRule* cpr;
+ HB_ChainPosRule curr_cpr;
+ hb_ot_layout_t* layout;
+
+
+ layout = gpos->layout;
+
+ if ( CHECK_Property( layout, IN_CURITEM(), flags, &property ) )
+ return error;
+
+ error = _HB_OPEN_Coverage_Index( &ccpf1->Coverage, IN_CURGLYPH(), &index );
+ if ( error )
+ return error;
+
+ cpr = ccpf1->ChainPosRuleSet[index].ChainPosRule;
+ num_cpr = ccpf1->ChainPosRuleSet[index].ChainPosRuleCount;
+
+ for ( k = 0; k < num_cpr; k++ )
+ {
+ curr_cpr = cpr[k];
+ bgc = curr_cpr.BacktrackGlyphCount;
+ igc = curr_cpr.InputGlyphCount;
+ lgc = curr_cpr.LookaheadGlyphCount;
+
+ if ( context_length != 0xFFFF && context_length < igc )
+ goto next_chainposrule;
+
+ /* check whether context is too long; it is a first guess only */
+
+ if ( bgc > buffer->in_pos || buffer->in_pos + igc + lgc > buffer->in_length )
+ goto next_chainposrule;
+
+ if ( bgc )
+ {
+ /* Since we don't know in advance the number of glyphs to inspect,
+ we search backwards for matches in the backtrack glyph array */
+
+ for ( i = 0, j = buffer->in_pos - 1; i < bgc; i++, j-- )
+ {
+ while ( CHECK_Property( layout, IN_ITEM( j ), flags, &property ) )
+ {
+ if ( error && error != HB_Err_Not_Covered )
+ return error;
+
+ if ( j + 1 == bgc - i )
+ goto next_chainposrule;
+ j--;
+ }
+
+ /* In OpenType 1.3, it is undefined whether the offsets of
+ backtrack glyphs is in logical order or not. Version 1.4
+ will clarify this:
+
+ Logical order - a b c d e f g h i j
+ i
+ Input offsets - 0 1
+ Backtrack offsets - 3 2 1 0
+ Lookahead offsets - 0 1 2 3 */
+
+ if ( IN_GLYPH( j ) != curr_cpr.Backtrack[i] )
+ goto next_chainposrule;
+ }
+ }
+
+ /* Start at 1 because [0] is implied */
+
+ for ( i = 1, j = buffer->in_pos + 1; i < igc; i++, j++ )
+ {
+ while ( CHECK_Property( layout, IN_ITEM( j ), flags, &property ) )
+ {
+ if ( error && error != HB_Err_Not_Covered )
+ return error;
+
+ if ( j + igc - i + lgc == (HB_Int)buffer->in_length )
+ goto next_chainposrule;
+ j++;
+ }
+
+ if ( IN_GLYPH( j ) != curr_cpr.Input[i - 1] )
+ goto next_chainposrule;
+ }
+
+ /* we are starting to check for lookahead glyphs right after the
+ last context glyph */
+
+ for ( i = 0; i < lgc; i++, j++ )
+ {
+ while ( CHECK_Property( layout, IN_ITEM( j ), flags, &property ) )
+ {
+ if ( error && error != HB_Err_Not_Covered )
+ return error;
+
+ if ( j + lgc - i == (HB_Int)buffer->in_length )
+ goto next_chainposrule;
+ j++;
+ }
+
+ if ( IN_GLYPH( j ) != curr_cpr.Lookahead[i] )
+ goto next_chainposrule;
+ }
+
+ return Do_ContextPos( gpi, igc,
+ curr_cpr.PosCount,
+ curr_cpr.PosLookupRecord,
+ buffer,
+ nesting_level );
+
+ next_chainposrule:
+ ;
+ }
+
+ return HB_Err_Not_Covered;
+}
+
+
+static HB_Error Lookup_ChainContextPos2(
+ GPOS_Instance* gpi,
+ HB_ChainContextPosFormat2* ccpf2,
+ HB_Buffer buffer,
+ HB_UShort flags,
+ HB_UShort context_length,
+ int nesting_level )
+{
+ HB_UShort index, property;
+ HB_Error error;
+ HB_UShort i, j, k;
+ HB_UShort bgc, igc, lgc;
+ HB_UShort known_backtrack_classes,
+ known_input_classes,
+ known_lookahead_classes;
+
+ HB_UShort* backtrack_classes;
+ HB_UShort* input_classes;
+ HB_UShort* lookahead_classes;
+
+ HB_UShort* bc;
+ HB_UShort* ic;
+ HB_UShort* lc;
+ HB_GPOSHeader* gpos = gpi->gpos;
+
+ HB_ChainPosClassSet* cpcs;
+ HB_ChainPosClassRule cpcr;
+ hb_ot_layout_t* layout;
+
+
+ layout = gpos->layout;
+
+ if ( CHECK_Property( layout, IN_CURITEM(), flags, &property ) )
+ return error;
+
+ /* Note: The coverage table in format 2 doesn't give an index into
+ anything. It just lets us know whether or not we need to
+ do any lookup at all. */
+
+ error = _HB_OPEN_Coverage_Index( &ccpf2->Coverage, IN_CURGLYPH(), &index );
+ if ( error )
+ return error;
+
+ if ( ALLOC_ARRAY( backtrack_classes, ccpf2->MaxBacktrackLength, HB_UShort ) )
+ return error;
+ known_backtrack_classes = 0;
+
+ if (ccpf2->MaxInputLength < 1)
+ return HB_Err_Not_Covered;
+
+ if ( ALLOC_ARRAY( input_classes, ccpf2->MaxInputLength, HB_UShort ) )
+ goto End3;
+ known_input_classes = 1;
+
+ if ( ALLOC_ARRAY( lookahead_classes, ccpf2->MaxLookaheadLength, HB_UShort ) )
+ goto End2;
+ known_lookahead_classes = 0;
+
+ error = _HB_OPEN_Get_Class( &ccpf2->InputClassDef, IN_CURGLYPH(),
+ &input_classes[0], NULL );
+ if ( error && error != HB_Err_Not_Covered )
+ goto End1;
+
+ cpcs = &ccpf2->ChainPosClassSet[input_classes[0]];
+ if ( !cpcs )
+ {
+ error = ERR(HB_Err_Invalid_SubTable);
+ goto End1;
+ }
+
+ for ( k = 0; k < cpcs->ChainPosClassRuleCount; k++ )
+ {
+ cpcr = cpcs->ChainPosClassRule[k];
+ bgc = cpcr.BacktrackGlyphCount;
+ igc = cpcr.InputGlyphCount;
+ lgc = cpcr.LookaheadGlyphCount;
+
+ if ( context_length != 0xFFFF && context_length < igc )
+ goto next_chainposclassrule;
+
+ /* check whether context is too long; it is a first guess only */
+
+ if ( bgc > buffer->in_pos || buffer->in_pos + igc + lgc > buffer->in_length )
+ goto next_chainposclassrule;
+
+ if ( bgc )
+ {
+ /* Since we don't know in advance the number of glyphs to inspect,
+ we search backwards for matches in the backtrack glyph array.
+ Note that `known_backtrack_classes' starts at index 0. */
+
+ bc = cpcr.Backtrack;
+
+ for ( i = 0, j = buffer->in_pos - 1; i < bgc; i++, j-- )
+ {
+ while ( CHECK_Property( layout, IN_ITEM( j ), flags, &property ) )
+ {
+ if ( error && error != HB_Err_Not_Covered )
+ goto End1;
+
+ if ( j + 1 == bgc - i )
+ goto next_chainposclassrule;
+ j++;
+ }
+
+ if ( i >= known_backtrack_classes )
+ {
+ /* Keeps us from having to do this for each rule */
+
+ error = _HB_OPEN_Get_Class( &ccpf2->BacktrackClassDef, IN_GLYPH( j ),
+ &backtrack_classes[i], NULL );
+ if ( error && error != HB_Err_Not_Covered )
+ goto End1;
+ known_backtrack_classes = i;
+ }
+
+ if ( bc[i] != backtrack_classes[i] )
+ goto next_chainposclassrule;
+ }
+ }
+
+ ic = cpcr.Input;
+
+ /* Start at 1 because [0] is implied */
+
+ for ( i = 1, j = buffer->in_pos + 1; i < igc; i++, j++ )
+ {
+ while ( CHECK_Property( layout, IN_ITEM( j ), flags, &property ) )
+ {
+ if ( error && error != HB_Err_Not_Covered )
+ goto End1;
+
+ if ( j + igc - i + lgc == (HB_Int)buffer->in_length )
+ goto next_chainposclassrule;
+ j++;
+ }
+
+ if ( i >= known_input_classes )
+ {
+ error = _HB_OPEN_Get_Class( &ccpf2->InputClassDef, IN_GLYPH( j ),
+ &input_classes[i], NULL );
+ if ( error && error != HB_Err_Not_Covered )
+ goto End1;
+ known_input_classes = i;
+ }
+
+ if ( ic[i - 1] != input_classes[i] )
+ goto next_chainposclassrule;
+ }
+
+ /* we are starting to check for lookahead glyphs right after the
+ last context glyph */
+
+ lc = cpcr.Lookahead;
+
+ for ( i = 0; i < lgc; i++, j++ )
+ {
+ while ( CHECK_Property( layout, IN_ITEM( j ), flags, &property ) )
+ {
+ if ( error && error != HB_Err_Not_Covered )
+ goto End1;
+
+ if ( j + lgc - i == (HB_Int)buffer->in_length )
+ goto next_chainposclassrule;
+ j++;
+ }
+
+ if ( i >= known_lookahead_classes )
+ {
+ error = _HB_OPEN_Get_Class( &ccpf2->LookaheadClassDef, IN_GLYPH( j ),
+ &lookahead_classes[i], NULL );
+ if ( error && error != HB_Err_Not_Covered )
+ goto End1;
+ known_lookahead_classes = i;
+ }
+
+ if ( lc[i] != lookahead_classes[i] )
+ goto next_chainposclassrule;
+ }
+
+ error = Do_ContextPos( gpi, igc,
+ cpcr.PosCount,
+ cpcr.PosLookupRecord,
+ buffer,
+ nesting_level );
+ goto End1;
+
+ next_chainposclassrule:
+ ;
+ }
+
+ error = HB_Err_Not_Covered;
+
+End1:
+ FREE( lookahead_classes );
+
+End2:
+ FREE( input_classes );
+
+End3:
+ FREE( backtrack_classes );
+ return error;
+}
+
+
+static HB_Error Lookup_ChainContextPos3(
+ GPOS_Instance* gpi,
+ HB_ChainContextPosFormat3* ccpf3,
+ HB_Buffer buffer,
+ HB_UShort flags,
+ HB_UShort context_length,
+ int nesting_level )
+{
+ HB_UShort index, i, j, property;
+ HB_UShort bgc, igc, lgc;
+ HB_Error error;
+ HB_GPOSHeader* gpos = gpi->gpos;
+
+ HB_Coverage* bc;
+ HB_Coverage* ic;
+ HB_Coverage* lc;
+ hb_ot_layout_t* layout;
+
+
+ layout = gpos->layout;
+
+ if ( CHECK_Property( layout, IN_CURITEM(), flags, &property ) )
+ return error;
+
+ bgc = ccpf3->BacktrackGlyphCount;
+ igc = ccpf3->InputGlyphCount;
+ lgc = ccpf3->LookaheadGlyphCount;
+
+ if ( context_length != 0xFFFF && context_length < igc )
+ return HB_Err_Not_Covered;
+
+ /* check whether context is too long; it is a first guess only */
+
+ if ( bgc > buffer->in_pos || buffer->in_pos + igc + lgc > buffer->in_length )
+ return HB_Err_Not_Covered;
+
+ if ( bgc )
+ {
+ /* Since we don't know in advance the number of glyphs to inspect,
+ we search backwards for matches in the backtrack glyph array */
+
+ bc = ccpf3->BacktrackCoverage;
+
+ for ( i = 0, j = buffer->in_pos - 1; i < bgc; i++, j-- )
+ {
+ while ( CHECK_Property( layout, IN_ITEM( j ), flags, &property ) )
+ {
+ if ( error && error != HB_Err_Not_Covered )
+ return error;
+
+ if ( j + 1 == bgc - i )
+ return HB_Err_Not_Covered;
+ j--;
+ }
+
+ error = _HB_OPEN_Coverage_Index( &bc[i], IN_GLYPH( j ), &index );
+ if ( error )
+ return error;
+ }
+ }
+
+ ic = ccpf3->InputCoverage;
+
+ for ( i = 0, j = buffer->in_pos; i < igc; i++, j++ )
+ {
+ /* We already called CHECK_Property for IN_GLYPH ( buffer->in_pos ) */
+ while ( j > buffer->in_pos && CHECK_Property( layout, IN_ITEM( j ), flags, &property ) )
+ {
+ if ( error && error != HB_Err_Not_Covered )
+ return error;
+
+ if ( j + igc - i + lgc == (HB_Int)buffer->in_length )
+ return HB_Err_Not_Covered;
+ j++;
+ }
+
+ error = _HB_OPEN_Coverage_Index( &ic[i], IN_GLYPH( j ), &index );
+ if ( error )
+ return error;
+ }
+
+ /* we are starting to check for lookahead glyphs right after the
+ last context glyph */
+
+ lc = ccpf3->LookaheadCoverage;
+
+ for ( i = 0; i < lgc; i++, j++ )
+ {
+ while ( CHECK_Property( layout, IN_ITEM( j ), flags, &property ) )
+ {
+ if ( error && error != HB_Err_Not_Covered )
+ return error;
+
+ if ( j + lgc - i == (HB_Int)buffer->in_length )
+ return HB_Err_Not_Covered;
+ j++;
+ }
+
+ error = _HB_OPEN_Coverage_Index( &lc[i], IN_GLYPH( j ), &index );
+ if ( error )
+ return error;
+ }
+
+ return Do_ContextPos( gpi, igc,
+ ccpf3->PosCount,
+ ccpf3->PosLookupRecord,
+ buffer,
+ nesting_level );
+}
+
+
+static HB_Error Lookup_ChainContextPos(
+ GPOS_Instance* gpi,
+ HB_GPOS_SubTable* st,
+ HB_Buffer buffer,
+ HB_UShort flags,
+ HB_UShort context_length,
+ int nesting_level )
+{
+ HB_ChainContextPos* ccp = &st->chain;
+
+ switch ( ccp->PosFormat )
+ {
+ case 1:
+ return Lookup_ChainContextPos1( gpi, &ccp->ccpf.ccpf1, buffer,
+ flags, context_length,
+ nesting_level );
+
+ case 2:
+ return Lookup_ChainContextPos2( gpi, &ccp->ccpf.ccpf2, buffer,
+ flags, context_length,
+ nesting_level );
+
+ case 3:
+ return Lookup_ChainContextPos3( gpi, &ccp->ccpf.ccpf3, buffer,
+ flags, context_length,
+ nesting_level );
+
+ default:
+ return ERR(HB_Err_Invalid_SubTable_Format);
+ }
+
+ return HB_Err_Ok; /* never reached */
+}
+
+
+
+/***********
+ * GPOS API
+ ***********/
+
+
+
+HB_Error HB_GPOS_Select_Script( HB_GPOSHeader* gpos,
+ HB_UInt script_tag,
+ HB_UShort* script_index )
+{
+ HB_UShort n;
+
+ HB_ScriptList* sl;
+ HB_ScriptRecord* sr;
+
+
+ if ( !gpos || !script_index )
+ return ERR(HB_Err_Invalid_Argument);
+
+ sl = &gpos->ScriptList;
+ sr = sl->ScriptRecord;
+
+ for ( n = 0; n < sl->ScriptCount; n++ )
+ if ( script_tag == sr[n].ScriptTag )
+ {
+ *script_index = n;
+
+ return HB_Err_Ok;
+ }
+
+ return HB_Err_Not_Covered;
+}
+
+
+
+HB_Error HB_GPOS_Select_Language( HB_GPOSHeader* gpos,
+ HB_UInt language_tag,
+ HB_UShort script_index,
+ HB_UShort* language_index,
+ HB_UShort* req_feature_index )
+{
+ HB_UShort n;
+
+ HB_ScriptList* sl;
+ HB_ScriptRecord* sr;
+ HB_ScriptTable* s;
+ HB_LangSysRecord* lsr;
+
+
+ if ( !gpos || !language_index || !req_feature_index )
+ return ERR(HB_Err_Invalid_Argument);
+
+ sl = &gpos->ScriptList;
+ sr = sl->ScriptRecord;
+
+ if ( script_index >= sl->ScriptCount )
+ return ERR(HB_Err_Invalid_Argument);
+
+ s = &sr[script_index].Script;
+ lsr = s->LangSysRecord;
+
+ for ( n = 0; n < s->LangSysCount; n++ )
+ if ( language_tag == lsr[n].LangSysTag )
+ {
+ *language_index = n;
+ *req_feature_index = lsr[n].LangSys.ReqFeatureIndex;
+
+ return HB_Err_Ok;
+ }
+
+ return HB_Err_Not_Covered;
+}
+
+
+/* selecting 0xFFFF for language_index asks for the values of the
+ default language (DefaultLangSys) */
+
+
+HB_Error HB_GPOS_Select_Feature( HB_GPOSHeader* gpos,
+ HB_UInt feature_tag,
+ HB_UShort script_index,
+ HB_UShort language_index,
+ HB_UShort* feature_index )
+{
+ HB_UShort n;
+
+ HB_ScriptList* sl;
+ HB_ScriptRecord* sr;
+ HB_ScriptTable* s;
+ HB_LangSysRecord* lsr;
+ HB_LangSys* ls;
+ HB_UShort* fi;
+
+ HB_FeatureList* fl;
+ HB_FeatureRecord* fr;
+
+
+ if ( !gpos || !feature_index )
+ return ERR(HB_Err_Invalid_Argument);
+
+ sl = &gpos->ScriptList;
+ sr = sl->ScriptRecord;
+
+ fl = &gpos->FeatureList;
+ fr = fl->FeatureRecord;
+
+ if ( script_index >= sl->ScriptCount )
+ return ERR(HB_Err_Invalid_Argument);
+
+ s = &sr[script_index].Script;
+ lsr = s->LangSysRecord;
+
+ if ( language_index == 0xFFFF )
+ ls = &s->DefaultLangSys;
+ else
+ {
+ if ( language_index >= s->LangSysCount )
+ return ERR(HB_Err_Invalid_Argument);
+
+ ls = &lsr[language_index].LangSys;
+ }
+
+ fi = ls->FeatureIndex;
+
+ for ( n = 0; n < ls->FeatureCount; n++ )
+ {
+ if ( fi[n] >= fl->FeatureCount )
+ return ERR(HB_Err_Invalid_SubTable_Format);
+
+ if ( feature_tag == fr[fi[n]].FeatureTag )
+ {
+ *feature_index = fi[n];
+
+ return HB_Err_Ok;
+ }
+ }
+
+ return HB_Err_Not_Covered;
+}
+
+
+/* The next three functions return a null-terminated list */
+
+
+HB_Error HB_GPOS_Query_Scripts( HB_GPOSHeader* gpos,
+ HB_UInt** script_tag_list )
+{
+ HB_Error error;
+ HB_UShort n;
+ HB_UInt* stl;
+
+ HB_ScriptList* sl;
+ HB_ScriptRecord* sr;
+
+
+ if ( !gpos || !script_tag_list )
+ return ERR(HB_Err_Invalid_Argument);
+
+ sl = &gpos->ScriptList;
+ sr = sl->ScriptRecord;
+
+ if ( ALLOC_ARRAY( stl, sl->ScriptCount + 1, HB_UInt ) )
+ return error;
+
+ for ( n = 0; n < sl->ScriptCount; n++ )
+ stl[n] = sr[n].ScriptTag;
+ stl[n] = 0;
+
+ *script_tag_list = stl;
+
+ return HB_Err_Ok;
+}
+
+
+
+HB_Error HB_GPOS_Query_Languages( HB_GPOSHeader* gpos,
+ HB_UShort script_index,
+ HB_UInt** language_tag_list )
+{
+ HB_Error error;
+ HB_UShort n;
+ HB_UInt* ltl;
+
+ HB_ScriptList* sl;
+ HB_ScriptRecord* sr;
+ HB_ScriptTable* s;
+ HB_LangSysRecord* lsr;
+
+
+ if ( !gpos || !language_tag_list )
+ return ERR(HB_Err_Invalid_Argument);
+
+ sl = &gpos->ScriptList;
+ sr = sl->ScriptRecord;
+
+ if ( script_index >= sl->ScriptCount )
+ return ERR(HB_Err_Invalid_Argument);
+
+ s = &sr[script_index].Script;
+ lsr = s->LangSysRecord;
+
+ if ( ALLOC_ARRAY( ltl, s->LangSysCount + 1, HB_UInt ) )
+ return error;
+
+ for ( n = 0; n < s->LangSysCount; n++ )
+ ltl[n] = lsr[n].LangSysTag;
+ ltl[n] = 0;
+
+ *language_tag_list = ltl;
+
+ return HB_Err_Ok;
+}
+
+
+/* selecting 0xFFFF for language_index asks for the values of the
+ default language (DefaultLangSys) */
+
+
+HB_Error HB_GPOS_Query_Features( HB_GPOSHeader* gpos,
+ HB_UShort script_index,
+ HB_UShort language_index,
+ HB_UInt** feature_tag_list )
+{
+ HB_UShort n;
+ HB_Error error;
+ HB_UInt* ftl;
+
+ HB_ScriptList* sl;
+ HB_ScriptRecord* sr;
+ HB_ScriptTable* s;
+ HB_LangSysRecord* lsr;
+ HB_LangSys* ls;
+ HB_UShort* fi;
+
+ HB_FeatureList* fl;
+ HB_FeatureRecord* fr;
+
+
+ if ( !gpos || !feature_tag_list )
+ return ERR(HB_Err_Invalid_Argument);
+
+ sl = &gpos->ScriptList;
+ sr = sl->ScriptRecord;
+
+ fl = &gpos->FeatureList;
+ fr = fl->FeatureRecord;
+
+ if ( script_index >= sl->ScriptCount )
+ return ERR(HB_Err_Invalid_Argument);
+
+ s = &sr[script_index].Script;
+ lsr = s->LangSysRecord;
+
+ if ( language_index == 0xFFFF )
+ ls = &s->DefaultLangSys;
+ else
+ {
+ if ( language_index >= s->LangSysCount )
+ return ERR(HB_Err_Invalid_Argument);
+
+ ls = &lsr[language_index].LangSys;
+ }
+
+ fi = ls->FeatureIndex;
+
+ if ( ALLOC_ARRAY( ftl, ls->FeatureCount + 1, HB_UInt ) )
+ return error;
+
+ for ( n = 0; n < ls->FeatureCount; n++ )
+ {
+ if ( fi[n] >= fl->FeatureCount )
+ {
+ FREE( ftl );
+ return ERR(HB_Err_Invalid_SubTable_Format);
+ }
+ ftl[n] = fr[fi[n]].FeatureTag;
+ }
+ ftl[n] = 0;
+
+ *feature_tag_list = ftl;
+
+ return HB_Err_Ok;
+}
+
+
+/* Do an individual subtable lookup. Returns HB_Err_Ok if positioning
+ has been done, or HB_Err_Not_Covered if not. */
+static HB_Error GPOS_Do_Glyph_Lookup( GPOS_Instance* gpi,
+ HB_UShort lookup_index,
+ HB_Buffer buffer,
+ HB_UShort context_length,
+ int nesting_level )
+{
+ HB_Error error = HB_Err_Not_Covered;
+ HB_UShort i, flags, lookup_count;
+ HB_GPOSHeader* gpos = gpi->gpos;
+ HB_Lookup* lo;
+ int lookup_type;
+
+
+ nesting_level++;
+
+ if ( nesting_level > HB_MAX_NESTING_LEVEL )
+ return ERR(HB_Err_Not_Covered); /* ERR() call intended */
+
+ lookup_count = gpos->LookupList.LookupCount;
+ if (lookup_index >= lookup_count)
+ return error;
+
+ lo = &gpos->LookupList.Lookup[lookup_index];
+ flags = lo->LookupFlag;
+ lookup_type = lo->LookupType;
+
+ for ( i = 0; i < lo->SubTableCount; i++ )
+ {
+ HB_GPOS_SubTable *st = &lo->SubTable[i].st.gpos;
+
+ switch (lookup_type) {
+ case HB_GPOS_LOOKUP_SINGLE:
+ error = Lookup_SinglePos ( gpi, st, buffer, flags, context_length, nesting_level ); break;
+ case HB_GPOS_LOOKUP_PAIR:
+ error = Lookup_PairPos ( gpi, st, buffer, flags, context_length, nesting_level ); break;
+ case HB_GPOS_LOOKUP_CURSIVE:
+ error = Lookup_CursivePos ( gpi, st, buffer, flags, context_length, nesting_level ); break;
+ case HB_GPOS_LOOKUP_MARKBASE:
+ error = Lookup_MarkBasePos ( gpi, st, buffer, flags, context_length, nesting_level ); break;
+ case HB_GPOS_LOOKUP_MARKLIG:
+ error = Lookup_MarkLigPos ( gpi, st, buffer, flags, context_length, nesting_level ); break;
+ case HB_GPOS_LOOKUP_MARKMARK:
+ error = Lookup_MarkMarkPos ( gpi, st, buffer, flags, context_length, nesting_level ); break;
+ case HB_GPOS_LOOKUP_CONTEXT:
+ error = Lookup_ContextPos ( gpi, st, buffer, flags, context_length, nesting_level ); break;
+ case HB_GPOS_LOOKUP_CHAIN:
+ error = Lookup_ChainContextPos ( gpi, st, buffer, flags, context_length, nesting_level ); break;
+ /*case HB_GPOS_LOOKUP_EXTENSION:
+ error = Lookup_ExtensionPos ( gpi, st, buffer, flags, context_length, nesting_level ); break;*/
+ default:
+ error = HB_Err_Not_Covered;
+ }
+
+ /* Check whether we have a successful positioning or an error other
+ than HB_Err_Not_Covered */
+ if ( error != HB_Err_Not_Covered )
+ return error;
+ }
+
+ return HB_Err_Not_Covered;
+}
+
+
+HB_INTERNAL HB_Error
+_HB_GPOS_Load_SubTable( HB_GPOS_SubTable* st,
+ HB_Stream stream,
+ HB_UShort lookup_type )
+{
+ switch ( lookup_type ) {
+ case HB_GPOS_LOOKUP_SINGLE: return Load_SinglePos ( st, stream );
+ case HB_GPOS_LOOKUP_PAIR: return Load_PairPos ( st, stream );
+ case HB_GPOS_LOOKUP_CURSIVE: return Load_CursivePos ( st, stream );
+ case HB_GPOS_LOOKUP_MARKBASE: return Load_MarkBasePos ( st, stream );
+ case HB_GPOS_LOOKUP_MARKLIG: return Load_MarkLigPos ( st, stream );
+ case HB_GPOS_LOOKUP_MARKMARK: return Load_MarkMarkPos ( st, stream );
+ case HB_GPOS_LOOKUP_CONTEXT: return Load_ContextPos ( st, stream );
+ case HB_GPOS_LOOKUP_CHAIN: return Load_ChainContextPos ( st, stream );
+ /*case HB_GPOS_LOOKUP_EXTENSION: return Load_ExtensionPos ( st, stream );*/
+ default: return ERR(HB_Err_Invalid_SubTable_Format);
+ }
+}
+
+
+HB_INTERNAL void
+_HB_GPOS_Free_SubTable( HB_GPOS_SubTable* st,
+ HB_UShort lookup_type )
+{
+ switch ( lookup_type ) {
+ case HB_GPOS_LOOKUP_SINGLE: Free_SinglePos ( st ); return;
+ case HB_GPOS_LOOKUP_PAIR: Free_PairPos ( st ); return;
+ case HB_GPOS_LOOKUP_CURSIVE: Free_CursivePos ( st ); return;
+ case HB_GPOS_LOOKUP_MARKBASE: Free_MarkBasePos ( st ); return;
+ case HB_GPOS_LOOKUP_MARKLIG: Free_MarkLigPos ( st ); return;
+ case HB_GPOS_LOOKUP_MARKMARK: Free_MarkMarkPos ( st ); return;
+ case HB_GPOS_LOOKUP_CONTEXT: Free_ContextPos ( st ); return;
+ case HB_GPOS_LOOKUP_CHAIN: Free_ChainContextPos ( st ); return;
+ /*case HB_GPOS_LOOKUP_EXTENSION: Free_ExtensionPos ( st ); return;*/
+ default: return;
+ }
+}
+
+
+/* apply one lookup to the input string object */
+
+static HB_Error GPOS_Do_String_Lookup( GPOS_Instance* gpi,
+ HB_UShort lookup_index,
+ HB_Buffer buffer )
+{
+ HB_Error error, retError = HB_Err_Not_Covered;
+ HB_GPOSHeader* gpos = gpi->gpos;
+
+ HB_UInt* properties = gpos->LookupList.Properties;
+
+ const int nesting_level = 0;
+ /* 0xFFFF indicates that we don't have a context length yet */
+ const HB_UShort context_length = 0xFFFF;
+
+
+ gpi->last = 0xFFFF; /* no last valid glyph for cursive pos. */
+
+ buffer->in_pos = 0;
+ while ( buffer->in_pos < buffer->in_length )
+ {
+ if ( ~IN_PROPERTIES( buffer->in_pos ) & properties[lookup_index] )
+ {
+ /* Note that the connection between mark and base glyphs hold
+ exactly one (string) lookup. For example, it would be possible
+ that in the first lookup, mark glyph X is attached to base
+ glyph A, and in the next lookup it is attached to base glyph B.
+ It is up to the font designer to provide meaningful lookups and
+ lookup order. */
+
+ error = GPOS_Do_Glyph_Lookup( gpi, lookup_index, buffer, context_length, nesting_level );
+ if ( error && error != HB_Err_Not_Covered )
+ return error;
+ }
+ else
+ {
+ /* Contrary to properties defined in GDEF, user-defined properties
+ will always stop a possible cursive positioning. */
+ gpi->last = 0xFFFF;
+
+ error = HB_Err_Not_Covered;
+ }
+
+ if ( error == HB_Err_Not_Covered )
+ (buffer->in_pos)++;
+ else
+ retError = error;
+ }
+
+ return retError;
+}
+
+
+static HB_Error Position_CursiveChain ( HB_Buffer buffer )
+{
+ HB_UInt i, j;
+ HB_Position positions = buffer->positions;
+
+ /* First handle all left-to-right connections */
+ for (j = 0; j < buffer->in_length; j++)
+ {
+ if (positions[j].cursive_chain > 0)
+ positions[j].y_pos += positions[j - positions[j].cursive_chain].y_pos;
+ }
+
+ /* Then handle all right-to-left connections */
+ for (i = buffer->in_length; i > 0; i--)
+ {
+ j = i - 1;
+
+ if (positions[j].cursive_chain < 0)
+ positions[j].y_pos += positions[j - positions[j].cursive_chain].y_pos;
+ }
+
+ return HB_Err_Ok;
+}
+
+
+HB_Error HB_GPOS_Add_Feature( HB_GPOSHeader* gpos,
+ HB_UShort feature_index,
+ HB_UInt property )
+{
+ HB_UShort i;
+
+ HB_Feature feature;
+ HB_UInt* properties;
+ HB_UShort* index;
+ HB_UShort lookup_count;
+
+ /* Each feature can only be added once */
+
+ if ( !gpos ||
+ feature_index >= gpos->FeatureList.FeatureCount ||
+ gpos->FeatureList.ApplyCount == gpos->FeatureList.FeatureCount )
+ return ERR(HB_Err_Invalid_Argument);
+
+ gpos->FeatureList.ApplyOrder[gpos->FeatureList.ApplyCount++] = feature_index;
+
+ properties = gpos->LookupList.Properties;
+
+ feature = gpos->FeatureList.FeatureRecord[feature_index].Feature;
+ index = feature.LookupListIndex;
+ lookup_count = gpos->LookupList.LookupCount;
+
+ for ( i = 0; i < feature.LookupListCount; i++ )
+ {
+ HB_UShort lookup_index = index[i];
+ if (lookup_index < lookup_count)
+ properties[lookup_index] |= property;
+ }
+
+ return HB_Err_Ok;
+}
+
+
+
+HB_Error HB_GPOS_Clear_Features( HB_GPOSHeader* gpos )
+{
+ HB_UShort i;
+
+ HB_UInt* properties;
+
+
+ if ( !gpos )
+ return ERR(HB_Err_Invalid_Argument);
+
+ gpos->FeatureList.ApplyCount = 0;
+
+ properties = gpos->LookupList.Properties;
+
+ for ( i = 0; i < gpos->LookupList.LookupCount; i++ )
+ properties[i] = 0;
+
+ return HB_Err_Ok;
+}
+
+
+
+HB_Error HB_GPOS_Register_Glyph_Function( HB_GPOSHeader* gpos,
+ HB_GlyphFunction gfunc )
+{
+ if ( !gpos )
+ return ERR(HB_Err_Invalid_Argument);
+
+ gpos->gfunc = gfunc;
+
+ return HB_Err_Ok;
+}
+
+
+
+HB_Error HB_GPOS_Register_MM_Function( HB_GPOSHeader* gpos,
+ HB_MMFunction mmfunc,
+ void* data )
+{
+ if ( !gpos )
+ return ERR(HB_Err_Invalid_Argument);
+
+ gpos->mmfunc = mmfunc;
+ gpos->data = data;
+
+ return HB_Err_Ok;
+}
+
+/* If `dvi' is TRUE, glyph contour points for anchor points and device
+ tables are ignored -- you will get device independent values. */
+
+
+HB_Error HB_GPOS_Apply_String( HB_Font font,
+ HB_GPOSHeader* gpos,
+ HB_UShort load_flags,
+ HB_Buffer buffer,
+ HB_Bool dvi,
+ HB_Bool r2l )
+{
+ HB_Error error, retError = HB_Err_Not_Covered;
+ GPOS_Instance gpi;
+ int i, j, lookup_count, num_features;
+
+ if ( !font || !gpos || !buffer )
+ return ERR(HB_Err_Invalid_Argument);
+
+ if ( buffer->in_length == 0 )
+ return HB_Err_Not_Covered;
+
+ gpi.font = font;
+ gpi.gpos = gpos;
+ gpi.load_flags = load_flags;
+ gpi.r2l = r2l;
+ gpi.dvi = dvi;
+
+ lookup_count = gpos->LookupList.LookupCount;
+ num_features = gpos->FeatureList.ApplyCount;
+
+ if ( num_features )
+ {
+ error = _hb_buffer_clear_positions( buffer );
+ if ( error )
+ return error;
+ }
+
+ for ( i = 0; i < num_features; i++ )
+ {
+ HB_UShort feature_index = gpos->FeatureList.ApplyOrder[i];
+ HB_Feature feature = gpos->FeatureList.FeatureRecord[feature_index].Feature;
+
+ for ( j = 0; j < feature.LookupListCount; j++ )
+ {
+ HB_UShort lookup_index = feature.LookupListIndex[j];
+
+ /* Skip nonexistant lookups */
+ if (lookup_index >= lookup_count)
+ continue;
+
+ error = GPOS_Do_String_Lookup( &gpi, lookup_index, buffer );
+ if ( error )
+ {
+ if ( error != HB_Err_Not_Covered )
+ return error;
+ }
+ else
+ retError = error;
+ }
+ }
+
+ if ( num_features )
+ {
+ error = Position_CursiveChain ( buffer );
+ if ( error )
+ return error;
+ }
+
+ return retError;
+}
+
+/* END */
diff --git a/src/harfbuzz-gpos.h b/src/harfbuzz-gpos.h
new file mode 100644
index 0000000..b1aa135
--- /dev/null
+++ b/src/harfbuzz-gpos.h
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 1998-2004 David Turner and Werner Lemberg
+ * Copyright (C) 2006 Behdad Esfahbod
+ *
+ * This is part of HarfBuzz, an OpenType Layout engine 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 HARFBUZZ_GPOS_H
+#define HARFBUZZ_GPOS_H
+
+#include "harfbuzz-open.h"
+#include "harfbuzz-buffer.h"
+
+HB_BEGIN_HEADER
+
+
+/* Lookup types for glyph positioning */
+
+#define HB_GPOS_LOOKUP_SINGLE 1
+#define HB_GPOS_LOOKUP_PAIR 2
+#define HB_GPOS_LOOKUP_CURSIVE 3
+#define HB_GPOS_LOOKUP_MARKBASE 4
+#define HB_GPOS_LOOKUP_MARKLIG 5
+#define HB_GPOS_LOOKUP_MARKMARK 6
+#define HB_GPOS_LOOKUP_CONTEXT 7
+#define HB_GPOS_LOOKUP_CHAIN 8
+#define HB_GPOS_LOOKUP_EXTENSION 9
+
+
+/* A pointer to a function which loads a glyph. Its parameters are
+ the same as in a call to Load_Glyph() -- if no glyph loading
+ function will be registered with HB_GPOS_Register_Glyph_Function(),
+ Load_Glyph() will be called indeed. The purpose of this function
+ pointer is to provide a hook for caching glyph outlines and sbits
+ (using the instance's generic pointer to hold the data).
+
+ If for some reason no outline data is available (e.g. for an
+ embedded bitmap glyph), _glyph->outline.n_points should be set to
+ zero. */
+
+typedef HB_Error (*HB_GlyphFunction)(HB_Font font,
+ HB_UInt glyphIndex,
+ HB_Int loadFlags );
+
+
+/* A pointer to a function which accesses the PostScript interpreter.
+ Multiple Master fonts need this interface to convert a metric ID
+ (as stored in an OpenType font version 1.2 or higher) `metric_id'
+ into a metric value (returned in `metric_value').
+
+ `data' points to the user-defined structure specified during a
+ call to HB_GPOS_Register_MM_Function().
+
+ `metric_value' must be returned as a scaled value (but shouldn't
+ be rounded). */
+
+typedef HB_Error (*HB_MMFunction)(HB_Font font,
+ HB_UShort metric_id,
+ HB_Fixed* metric_value,
+ void* data );
+
+
+struct HB_GPOSHeader_
+{
+ HB_16Dot16 Version;
+
+ HB_ScriptList ScriptList;
+ HB_FeatureList FeatureList;
+ HB_LookupList LookupList;
+
+ hb_ot_layout_t *layout;
+
+ /* the next field is used for a callback function to get the
+ glyph outline. */
+
+ HB_GlyphFunction gfunc;
+
+ /* this is OpenType 1.2 -- Multiple Master fonts need this
+ callback function to get various metric values from the
+ PostScript interpreter. */
+
+ HB_MMFunction mmfunc;
+ void* data;
+};
+
+typedef struct HB_GPOSHeader_ HB_GPOSHeader;
+typedef HB_GPOSHeader* HB_GPOS;
+
+
+HB_Error HB_Load_GPOS_Table( HB_Font font,
+ HB_GPOSHeader** gpos,
+ hb_ot_layout_t *layout );
+
+
+HB_Error HB_Done_GPOS_Table( HB_GPOSHeader* gpos );
+
+
+HB_Error HB_GPOS_Select_Script( HB_GPOSHeader* gpos,
+ HB_UInt script_tag,
+ HB_UShort* script_index );
+
+HB_Error HB_GPOS_Select_Language( HB_GPOSHeader* gpos,
+ HB_UInt language_tag,
+ HB_UShort script_index,
+ HB_UShort* language_index,
+ HB_UShort* req_feature_index );
+
+HB_Error HB_GPOS_Select_Feature( HB_GPOSHeader* gpos,
+ HB_UInt feature_tag,
+ HB_UShort script_index,
+ HB_UShort language_index,
+ HB_UShort* feature_index );
+
+
+HB_Error HB_GPOS_Query_Scripts( HB_GPOSHeader* gpos,
+ HB_UInt** script_tag_list );
+
+HB_Error HB_GPOS_Query_Languages( HB_GPOSHeader* gpos,
+ HB_UShort script_index,
+ HB_UInt** language_tag_list );
+
+HB_Error HB_GPOS_Query_Features( HB_GPOSHeader* gpos,
+ HB_UShort script_index,
+ HB_UShort language_index,
+ HB_UInt** feature_tag_list );
+
+
+HB_Error HB_GPOS_Add_Feature( HB_GPOSHeader* gpos,
+ HB_UShort feature_index,
+ HB_UInt property );
+
+HB_Error HB_GPOS_Clear_Features( HB_GPOSHeader* gpos );
+
+
+HB_Error HB_GPOS_Register_Glyph_Function( HB_GPOSHeader* gpos,
+ HB_GlyphFunction gfunc );
+
+
+HB_Error HB_GPOS_Register_MM_Function( HB_GPOSHeader* gpos,
+ HB_MMFunction mmfunc,
+ void* data );
+
+/* If `dvi' is TRUE, glyph contour points for anchor points and device
+ tables are ignored -- you will get device independent values. */
+
+
+HB_Error HB_GPOS_Apply_String( HB_Font font,
+ HB_GPOSHeader* gpos,
+ HB_UShort load_flags,
+ HB_Buffer buffer,
+ HB_Bool dvi,
+ HB_Bool r2l );
+
+HB_END_HEADER
+
+#endif /* HARFBUZZ_GPOS_H */
diff --git a/src/harfbuzz-gsub-private.h b/src/harfbuzz-gsub-private.h
new file mode 100644
index 0000000..d4cb651
--- /dev/null
+++ b/src/harfbuzz-gsub-private.h
@@ -0,0 +1,476 @@
+/*
+ * Copyright (C) 1998-2004 David Turner and Werner Lemberg
+ * Copyright (C) 2006 Behdad Esfahbod
+ *
+ * This is part of HarfBuzz, an OpenType Layout engine 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 HARFBUZZ_GSUB_PRIVATE_H
+#define HARFBUZZ_GSUB_PRIVATE_H
+
+#include "harfbuzz-impl.h"
+#include "harfbuzz-stream-private.h"
+#include "harfbuzz-gsub.h"
+
+HB_BEGIN_HEADER
+
+
+typedef union HB_GSUB_SubTable_ HB_GSUB_SubTable;
+
+/* LookupType 1 */
+
+struct HB_SingleSubstFormat1_
+{
+ HB_Short DeltaGlyphID; /* constant added to get
+ substitution glyph index */
+};
+
+typedef struct HB_SingleSubstFormat1_ HB_SingleSubstFormat1;
+
+
+struct HB_SingleSubstFormat2_
+{
+ HB_UShort GlyphCount; /* number of glyph IDs in
+ Substitute array */
+ HB_UShort* Substitute; /* array of substitute glyph IDs */
+};
+
+typedef struct HB_SingleSubstFormat2_ HB_SingleSubstFormat2;
+
+
+struct HB_SingleSubst_
+{
+ HB_UShort SubstFormat; /* 1 or 2 */
+ HB_Coverage Coverage; /* Coverage table */
+
+ union
+ {
+ HB_SingleSubstFormat1 ssf1;
+ HB_SingleSubstFormat2 ssf2;
+ } ssf;
+};
+
+typedef struct HB_SingleSubst_ HB_SingleSubst;
+
+
+/* LookupType 2 */
+
+struct HB_Sequence_
+{
+ HB_UShort GlyphCount; /* number of glyph IDs in the
+ Substitute array */
+ HB_UShort* Substitute; /* string of glyph IDs to
+ substitute */
+};
+
+typedef struct HB_Sequence_ HB_Sequence;
+
+
+struct HB_MultipleSubst_
+{
+ HB_UShort SubstFormat; /* always 1 */
+ HB_Coverage Coverage; /* Coverage table */
+ HB_UShort SequenceCount; /* number of Sequence tables */
+ HB_Sequence* Sequence; /* array of Sequence tables */
+};
+
+typedef struct HB_MultipleSubst_ HB_MultipleSubst;
+
+
+/* LookupType 3 */
+
+struct HB_AlternateSet_
+{
+ HB_UShort GlyphCount; /* number of glyph IDs in the
+ Alternate array */
+ HB_UShort* Alternate; /* array of alternate glyph IDs */
+};
+
+typedef struct HB_AlternateSet_ HB_AlternateSet;
+
+
+struct HB_AlternateSubst_
+{
+ HB_UShort SubstFormat; /* always 1 */
+ HB_Coverage Coverage; /* Coverage table */
+ HB_UShort AlternateSetCount;
+ /* number of AlternateSet tables */
+ HB_AlternateSet* AlternateSet; /* array of AlternateSet tables */
+};
+
+typedef struct HB_AlternateSubst_ HB_AlternateSubst;
+
+
+/* LookupType 4 */
+
+struct HB_Ligature_
+{
+ HB_UShort LigGlyph; /* glyphID of ligature
+ to substitute */
+ HB_UShort ComponentCount; /* number of components in ligature */
+ HB_UShort* Component; /* array of component glyph IDs */
+};
+
+typedef struct HB_Ligature_ HB_Ligature;
+
+
+struct HB_LigatureSet_
+{
+ HB_UShort LigatureCount; /* number of Ligature tables */
+ HB_Ligature* Ligature; /* array of Ligature tables */
+};
+
+typedef struct HB_LigatureSet_ HB_LigatureSet;
+
+
+struct HB_LigatureSubst_
+{
+ HB_UShort SubstFormat; /* always 1 */
+ HB_Coverage Coverage; /* Coverage table */
+ HB_UShort LigatureSetCount; /* number of LigatureSet tables */
+ HB_LigatureSet* LigatureSet; /* array of LigatureSet tables */
+};
+
+typedef struct HB_LigatureSubst_ HB_LigatureSubst;
+
+
+/* needed by both lookup type 5 and 6 */
+
+struct HB_SubstLookupRecord_
+{
+ HB_UShort SequenceIndex; /* index into current
+ glyph sequence */
+ HB_UShort LookupListIndex; /* Lookup to apply to that pos. */
+};
+
+typedef struct HB_SubstLookupRecord_ HB_SubstLookupRecord;
+
+
+/* LookupType 5 */
+
+struct HB_SubRule_
+{
+ HB_UShort GlyphCount; /* total number of input glyphs */
+ HB_UShort SubstCount; /* number of SubstLookupRecord
+ tables */
+ HB_UShort* Input; /* array of input glyph IDs */
+ HB_SubstLookupRecord* SubstLookupRecord;
+ /* array of SubstLookupRecord
+ tables */
+};
+
+typedef struct HB_SubRule_ HB_SubRule;
+
+
+struct HB_SubRuleSet_
+{
+ HB_UShort SubRuleCount; /* number of SubRule tables */
+ HB_SubRule* SubRule; /* array of SubRule tables */
+};
+
+typedef struct HB_SubRuleSet_ HB_SubRuleSet;
+
+
+struct HB_ContextSubstFormat1_
+{
+ HB_Coverage Coverage; /* Coverage table */
+ HB_UShort SubRuleSetCount; /* number of SubRuleSet tables */
+ HB_SubRuleSet* SubRuleSet; /* array of SubRuleSet tables */
+};
+
+typedef struct HB_ContextSubstFormat1_ HB_ContextSubstFormat1;
+
+
+struct HB_SubClassRule_
+{
+ HB_UShort GlyphCount; /* total number of context classes */
+ HB_UShort SubstCount; /* number of SubstLookupRecord
+ tables */
+ HB_UShort* Class; /* array of classes */
+ HB_SubstLookupRecord* SubstLookupRecord;
+ /* array of SubstLookupRecord
+ tables */
+};
+
+typedef struct HB_SubClassRule_ HB_SubClassRule;
+
+
+struct HB_SubClassSet_
+{
+ HB_UShort SubClassRuleCount;
+ /* number of SubClassRule tables */
+ HB_SubClassRule* SubClassRule; /* array of SubClassRule tables */
+};
+
+typedef struct HB_SubClassSet_ HB_SubClassSet;
+
+
+/* The `MaxContextLength' field is not defined in the TTO specification
+ but simplifies the implementation of this format. It holds the
+ maximal context length used in the context rules. */
+
+struct HB_ContextSubstFormat2_
+{
+ HB_UShort MaxContextLength;
+ /* maximal context length */
+ HB_Coverage Coverage; /* Coverage table */
+ HB_ClassDefinition ClassDef; /* ClassDef table */
+ HB_UShort SubClassSetCount;
+ /* number of SubClassSet tables */
+ HB_SubClassSet* SubClassSet; /* array of SubClassSet tables */
+};
+
+typedef struct HB_ContextSubstFormat2_ HB_ContextSubstFormat2;
+
+
+struct HB_ContextSubstFormat3_
+{
+ HB_UShort GlyphCount; /* number of input glyphs */
+ HB_UShort SubstCount; /* number of SubstLookupRecords */
+ HB_Coverage* Coverage; /* array of Coverage tables */
+ HB_SubstLookupRecord* SubstLookupRecord;
+ /* array of substitution lookups */
+};
+
+typedef struct HB_ContextSubstFormat3_ HB_ContextSubstFormat3;
+
+
+struct HB_ContextSubst_
+{
+ HB_UShort SubstFormat; /* 1, 2, or 3 */
+
+ union
+ {
+ HB_ContextSubstFormat1 csf1;
+ HB_ContextSubstFormat2 csf2;
+ HB_ContextSubstFormat3 csf3;
+ } csf;
+};
+
+typedef struct HB_ContextSubst_ HB_ContextSubst;
+
+
+/* LookupType 6 */
+
+struct HB_ChainSubRule_
+{
+ HB_UShort BacktrackGlyphCount;
+ /* total number of backtrack glyphs */
+ HB_UShort* Backtrack; /* array of backtrack glyph IDs */
+ HB_UShort InputGlyphCount;
+ /* total number of input glyphs */
+ HB_UShort* Input; /* array of input glyph IDs */
+ HB_UShort LookaheadGlyphCount;
+ /* total number of lookahead glyphs */
+ HB_UShort* Lookahead; /* array of lookahead glyph IDs */
+ HB_UShort SubstCount; /* number of SubstLookupRecords */
+ HB_SubstLookupRecord* SubstLookupRecord;
+ /* array of SubstLookupRecords */
+};
+
+typedef struct HB_ChainSubRule_ HB_ChainSubRule;
+
+
+struct HB_ChainSubRuleSet_
+{
+ HB_UShort ChainSubRuleCount;
+ /* number of ChainSubRule tables */
+ HB_ChainSubRule* ChainSubRule; /* array of ChainSubRule tables */
+};
+
+typedef struct HB_ChainSubRuleSet_ HB_ChainSubRuleSet;
+
+
+struct HB_ChainContextSubstFormat1_
+{
+ HB_Coverage Coverage; /* Coverage table */
+ HB_UShort ChainSubRuleSetCount;
+ /* number of ChainSubRuleSet tables */
+ HB_ChainSubRuleSet* ChainSubRuleSet;
+ /* array of ChainSubRuleSet tables */
+};
+
+typedef struct HB_ChainContextSubstFormat1_ HB_ChainContextSubstFormat1;
+
+
+struct HB_ChainSubClassRule_
+{
+ HB_UShort BacktrackGlyphCount;
+ /* total number of backtrack
+ classes */
+ HB_UShort* Backtrack; /* array of backtrack classes */
+ HB_UShort InputGlyphCount;
+ /* total number of context classes */
+ HB_UShort* Input; /* array of context classes */
+ HB_UShort LookaheadGlyphCount;
+ /* total number of lookahead
+ classes */
+ HB_UShort* Lookahead; /* array of lookahead classes */
+ HB_UShort SubstCount; /* number of SubstLookupRecords */
+ HB_SubstLookupRecord* SubstLookupRecord;
+ /* array of substitution lookups */
+};
+
+typedef struct HB_ChainSubClassRule_ HB_ChainSubClassRule;
+
+
+struct HB_ChainSubClassSet_
+{
+ HB_UShort ChainSubClassRuleCount;
+ /* number of ChainSubClassRule
+ tables */
+ HB_ChainSubClassRule* ChainSubClassRule;
+ /* array of ChainSubClassRule
+ tables */
+};
+
+typedef struct HB_ChainSubClassSet_ HB_ChainSubClassSet;
+
+
+/* The `MaxXXXLength' fields are not defined in the TTO specification
+ but simplifies the implementation of this format. It holds the
+ maximal context length used in the specific context rules. */
+
+struct HB_ChainContextSubstFormat2_
+{
+ HB_Coverage Coverage; /* Coverage table */
+
+ HB_UShort MaxBacktrackLength;
+ /* maximal backtrack length */
+ HB_ClassDefinition BacktrackClassDef;
+ /* BacktrackClassDef table */
+ HB_UShort MaxInputLength;
+ /* maximal input length */
+ HB_ClassDefinition InputClassDef;
+ /* InputClassDef table */
+ HB_UShort MaxLookaheadLength;
+ /* maximal lookahead length */
+ HB_ClassDefinition LookaheadClassDef;
+ /* LookaheadClassDef table */
+
+ HB_UShort ChainSubClassSetCount;
+ /* number of ChainSubClassSet
+ tables */
+ HB_ChainSubClassSet* ChainSubClassSet;
+ /* array of ChainSubClassSet
+ tables */
+};
+
+typedef struct HB_ChainContextSubstFormat2_ HB_ChainContextSubstFormat2;
+
+
+struct HB_ChainContextSubstFormat3_
+{
+ HB_UShort BacktrackGlyphCount;
+ /* number of backtrack glyphs */
+ HB_Coverage* BacktrackCoverage;
+ /* array of backtrack Coverage
+ tables */
+ HB_UShort InputGlyphCount;
+ /* number of input glyphs */
+ HB_Coverage* InputCoverage;
+ /* array of input coverage
+ tables */
+ HB_UShort LookaheadGlyphCount;
+ /* number of lookahead glyphs */
+ HB_Coverage* LookaheadCoverage;
+ /* array of lookahead coverage
+ tables */
+ HB_UShort SubstCount; /* number of SubstLookupRecords */
+ HB_SubstLookupRecord* SubstLookupRecord;
+ /* array of substitution lookups */
+};
+
+typedef struct HB_ChainContextSubstFormat3_ HB_ChainContextSubstFormat3;
+
+
+struct HB_ChainContextSubst_
+{
+ HB_UShort SubstFormat; /* 1, 2, or 3 */
+
+ union
+ {
+ HB_ChainContextSubstFormat1 ccsf1;
+ HB_ChainContextSubstFormat2 ccsf2;
+ HB_ChainContextSubstFormat3 ccsf3;
+ } ccsf;
+};
+
+typedef struct HB_ChainContextSubst_ HB_ChainContextSubst;
+
+
+#if 0
+/* LookupType 7 */
+struct HB_ExtensionSubst_
+{
+ HB_UShort SubstFormat; /* always 1 */
+ HB_UShort LookuptType; /* lookup-type of referenced subtable */
+ HB_GSUB_SubTable *subtable; /* referenced subtable */
+};
+
+typedef struct HB_ExtensionSubst_ HB_ExtensionSubst;
+#endif
+
+
+/* LookupType 8 */
+struct HB_ReverseChainContextSubst_
+{
+ HB_UShort SubstFormat; /* always 1 */
+ HB_Coverage Coverage; /* coverage table for input glyphs */
+ HB_UShort BacktrackGlyphCount; /* number of backtrack glyphs */
+ HB_Coverage* BacktrackCoverage; /* array of backtrack Coverage
+ tables */
+ HB_UShort LookaheadGlyphCount; /* number of lookahead glyphs */
+ HB_Coverage* LookaheadCoverage; /* array of lookahead Coverage
+ tables */
+ HB_UShort GlyphCount; /* number of Glyph IDs */
+ HB_UShort* Substitute; /* array of substitute Glyph ID */
+};
+
+typedef struct HB_ReverseChainContextSubst_ HB_ReverseChainContextSubst;
+
+
+union HB_GSUB_SubTable_
+{
+ HB_SingleSubst single;
+ HB_MultipleSubst multiple;
+ HB_AlternateSubst alternate;
+ HB_LigatureSubst ligature;
+ HB_ContextSubst context;
+ HB_ChainContextSubst chain;
+ HB_ReverseChainContextSubst reverse;
+};
+
+
+
+
+HB_INTERNAL HB_Error
+_HB_GSUB_Load_SubTable( HB_GSUB_SubTable* st,
+ HB_Stream stream,
+ HB_UShort lookup_type );
+
+HB_INTERNAL void
+_HB_GSUB_Free_SubTable( HB_GSUB_SubTable* st,
+ HB_UShort lookup_type );
+
+HB_END_HEADER
+
+#endif /* HARFBUZZ_GSUB_PRIVATE_H */
diff --git a/src/harfbuzz-gsub.c b/src/harfbuzz-gsub.c
new file mode 100644
index 0000000..38879a8
--- /dev/null
+++ b/src/harfbuzz-gsub.c
@@ -0,0 +1,4304 @@
+/*
+ * Copyright (C) 1998-2004 David Turner and Werner Lemberg
+ * Copyright (C) 2006 Behdad Esfahbod
+ * Copyright (C) 2007 Red Hat, Inc.
+ *
+ * This is part of HarfBuzz, an OpenType Layout engine library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Red Hat Author(s): Behdad Esfahbod
+ */
+
+#include "harfbuzz-impl.h"
+#include "harfbuzz-gsub-private.h"
+#include "harfbuzz-open-private.h"
+#include "harfbuzz-gdef-private.h"
+
+static HB_Error GSUB_Do_Glyph_Lookup( HB_GSUBHeader* gsub,
+ HB_UShort lookup_index,
+ HB_Buffer buffer,
+ HB_UShort context_length,
+ int nesting_level );
+
+
+
+/**********************
+ * Auxiliary functions
+ **********************/
+
+
+
+HB_Error HB_Load_GSUB_Table( HB_Font font,
+ HB_GSUBHeader** retptr,
+ hb_ot_layout_t *layout )
+{
+ HB_Stream stream = font->stream;
+ HB_Error error;
+ HB_UInt cur_offset, new_offset, base_offset;
+
+ HB_GSUBHeader* gsub;
+
+ if ( !retptr || !layout )
+ return ERR(HB_Err_Invalid_Argument);
+
+ if ( GOTO_Table( TTAG_GSUB ) )
+ return error;
+
+ base_offset = FILE_Pos();
+
+ if ( ALLOC ( gsub, sizeof( *gsub ) ) )
+ return error;
+
+
+ /* skip version */
+
+ if ( FILE_Seek( base_offset + 4L ) ||
+ ACCESS_Frame( 2L ) )
+ goto Fail4;
+
+ new_offset = GET_UShort() + base_offset;
+
+ FORGET_Frame();
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = _HB_OPEN_Load_ScriptList( &gsub->ScriptList,
+ stream ) ) != HB_Err_Ok )
+ goto Fail4;
+ (void)FILE_Seek( cur_offset );
+
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail3;
+
+ new_offset = GET_UShort() + base_offset;
+
+ FORGET_Frame();
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = _HB_OPEN_Load_FeatureList( &gsub->FeatureList,
+ stream ) ) != HB_Err_Ok )
+ goto Fail3;
+ (void)FILE_Seek( cur_offset );
+
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail2;
+
+ new_offset = GET_UShort() + base_offset;
+
+ FORGET_Frame();
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = _HB_OPEN_Load_LookupList( &gsub->LookupList,
+ stream, HB_Type_GSUB ) ) != HB_Err_Ok )
+ goto Fail2;
+
+ gsub->layout = layout; /* can be NULL */
+
+ *retptr = gsub;
+
+ return HB_Err_Ok;
+
+Fail2:
+ _HB_OPEN_Free_FeatureList( &gsub->FeatureList );
+
+Fail3:
+ _HB_OPEN_Free_ScriptList( &gsub->ScriptList );
+
+Fail4:
+ FREE ( gsub );
+
+
+ return error;
+}
+
+
+HB_Error HB_Done_GSUB_Table( HB_GSUBHeader* gsub )
+{
+ _HB_OPEN_Free_LookupList( &gsub->LookupList, HB_Type_GSUB );
+ _HB_OPEN_Free_FeatureList( &gsub->FeatureList );
+ _HB_OPEN_Free_ScriptList( &gsub->ScriptList );
+
+ FREE( gsub );
+
+ return HB_Err_Ok;
+}
+
+/*****************************
+ * SubTable related functions
+ *****************************/
+
+
+/* LookupType 1 */
+
+/* SingleSubstFormat1 */
+/* SingleSubstFormat2 */
+
+static HB_Error Load_SingleSubst( HB_GSUB_SubTable* st,
+ HB_Stream stream )
+{
+ HB_Error error;
+ HB_SingleSubst* ss = &st->single;
+
+ HB_UShort n, count;
+ HB_UInt cur_offset, new_offset, base_offset;
+
+ HB_UShort* s;
+
+
+ base_offset = FILE_Pos();
+
+ if ( ACCESS_Frame( 4L ) )
+ return error;
+
+ ss->SubstFormat = GET_UShort();
+ new_offset = GET_UShort() + base_offset;
+
+ FORGET_Frame();
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = _HB_OPEN_Load_Coverage( &ss->Coverage, stream ) ) != HB_Err_Ok )
+ return error;
+ (void)FILE_Seek( cur_offset );
+
+ switch ( ss->SubstFormat )
+ {
+ case 1:
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail2;
+
+ ss->ssf.ssf1.DeltaGlyphID = GET_UShort();
+
+ FORGET_Frame();
+
+ break;
+
+ case 2:
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail2;
+
+ count = ss->ssf.ssf2.GlyphCount = GET_UShort();
+
+ FORGET_Frame();
+
+ ss->ssf.ssf2.Substitute = NULL;
+
+ if ( ALLOC_ARRAY( ss->ssf.ssf2.Substitute, count, HB_UShort ) )
+ goto Fail2;
+
+ s = ss->ssf.ssf2.Substitute;
+
+ if ( ACCESS_Frame( count * 2L ) )
+ goto Fail1;
+
+ for ( n = 0; n < count; n++ )
+ s[n] = GET_UShort();
+
+ FORGET_Frame();
+
+ break;
+
+ default:
+ return ERR(HB_Err_Invalid_SubTable_Format);
+ }
+
+ return HB_Err_Ok;
+
+Fail1:
+ FREE( s );
+
+Fail2:
+ _HB_OPEN_Free_Coverage( &ss->Coverage );
+ return error;
+}
+
+
+static void Free_SingleSubst( HB_GSUB_SubTable* st )
+{
+ HB_SingleSubst* ss = &st->single;
+
+ switch ( ss->SubstFormat )
+ {
+ case 1:
+ break;
+
+ case 2:
+ FREE( ss->ssf.ssf2.Substitute );
+ break;
+
+ default:
+ break;
+ }
+
+ _HB_OPEN_Free_Coverage( &ss->Coverage );
+}
+
+
+static HB_Error Lookup_SingleSubst( HB_GSUBHeader* gsub,
+ HB_GSUB_SubTable* st,
+ HB_Buffer buffer,
+ HB_UShort flags,
+ HB_UShort context_length,
+ int nesting_level )
+{
+ HB_UShort index, value, property;
+ HB_Error error;
+ HB_SingleSubst* ss = &st->single;
+ hb_ot_layout_t* layout = gsub->layout;
+
+ HB_UNUSED(nesting_level);
+
+ if ( context_length != 0xFFFF && context_length < 1 )
+ return HB_Err_Not_Covered;
+
+ if ( CHECK_Property( layout, IN_CURITEM(), flags, &property ) )
+ return error;
+
+ error = _HB_OPEN_Coverage_Index( &ss->Coverage, IN_CURGLYPH(), &index );
+ if ( error )
+ return error;
+
+ switch ( ss->SubstFormat )
+ {
+ case 1:
+ value = (IN_CURGLYPH() + ss->ssf.ssf1.DeltaGlyphID ) & 0xFFFF;
+ if ( REPLACE_Glyph( buffer, value, nesting_level ) )
+ return error;
+ break;
+
+ case 2:
+ if ( index >= ss->ssf.ssf2.GlyphCount )
+ return ERR(HB_Err_Invalid_SubTable);
+ value = ss->ssf.ssf2.Substitute[index];
+ if ( REPLACE_Glyph( buffer, value, nesting_level ) )
+ return error;
+ break;
+
+ default:
+ return ERR(HB_Err_Invalid_SubTable);
+ }
+
+ if ( _hb_ot_layout_has_new_glyph_classes (layout) )
+ {
+ /* we inherit the old glyph class to the substituted glyph */
+
+ hb_ot_layout_set_glyph_class (layout, value, property);
+ }
+
+ return HB_Err_Ok;
+}
+
+
+/* LookupType 2 */
+
+/* Sequence */
+
+static HB_Error Load_Sequence( HB_Sequence* s,
+ HB_Stream stream )
+{
+ HB_Error error;
+
+ HB_UShort n, count;
+ HB_UShort* sub;
+
+
+ if ( ACCESS_Frame( 2L ) )
+ return error;
+
+ count = s->GlyphCount = GET_UShort();
+
+ FORGET_Frame();
+
+ s->Substitute = NULL;
+
+ if ( count )
+ {
+ if ( ALLOC_ARRAY( s->Substitute, count, HB_UShort ) )
+ return error;
+
+ sub = s->Substitute;
+
+ if ( ACCESS_Frame( count * 2L ) )
+ {
+ FREE( sub );
+ return error;
+ }
+
+ for ( n = 0; n < count; n++ )
+ sub[n] = GET_UShort();
+
+ FORGET_Frame();
+ }
+
+ return HB_Err_Ok;
+}
+
+
+static void Free_Sequence( HB_Sequence* s )
+{
+ FREE( s->Substitute );
+}
+
+
+/* MultipleSubstFormat1 */
+
+static HB_Error Load_MultipleSubst( HB_GSUB_SubTable* st,
+ HB_Stream stream )
+{
+ HB_Error error;
+ HB_MultipleSubst* ms = &st->multiple;
+
+ HB_UShort n = 0, m, count;
+ HB_UInt cur_offset, new_offset, base_offset;
+
+ HB_Sequence* s;
+
+
+ base_offset = FILE_Pos();
+
+ if ( ACCESS_Frame( 4L ) )
+ return error;
+
+ ms->SubstFormat = GET_UShort(); /* should be 1 */
+ new_offset = GET_UShort() + base_offset;
+
+ FORGET_Frame();
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = _HB_OPEN_Load_Coverage( &ms->Coverage, stream ) ) != HB_Err_Ok )
+ return error;
+ (void)FILE_Seek( cur_offset );
+
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail2;
+
+ count = ms->SequenceCount = GET_UShort();
+
+ FORGET_Frame();
+
+ ms->Sequence = NULL;
+
+ if ( ALLOC_ARRAY( ms->Sequence, count, HB_Sequence ) )
+ goto Fail2;
+
+ s = ms->Sequence;
+
+ for ( n = 0; n < count; n++ )
+ {
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail1;
+
+ new_offset = GET_UShort() + base_offset;
+
+ FORGET_Frame();
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = Load_Sequence( &s[n], stream ) ) != HB_Err_Ok )
+ goto Fail1;
+ (void)FILE_Seek( cur_offset );
+ }
+
+ return HB_Err_Ok;
+
+Fail1:
+ for ( m = 0; m < n; m++ )
+ Free_Sequence( &s[m] );
+
+ FREE( s );
+
+Fail2:
+ _HB_OPEN_Free_Coverage( &ms->Coverage );
+ return error;
+}
+
+
+static void Free_MultipleSubst( HB_GSUB_SubTable* st )
+{
+ HB_UShort n, count;
+ HB_MultipleSubst* ms = &st->multiple;
+
+ HB_Sequence* s;
+
+
+ if ( ms->Sequence )
+ {
+ count = ms->SequenceCount;
+ s = ms->Sequence;
+
+ for ( n = 0; n < count; n++ )
+ Free_Sequence( &s[n] );
+
+ FREE( s );
+ }
+
+ _HB_OPEN_Free_Coverage( &ms->Coverage );
+}
+
+
+static HB_Error Lookup_MultipleSubst( HB_GSUBHeader* gsub,
+ HB_GSUB_SubTable* st,
+ HB_Buffer buffer,
+ HB_UShort flags,
+ HB_UShort context_length,
+ int nesting_level )
+{
+ HB_Error error;
+ HB_UShort index, property, n, count;
+ HB_UShort*s;
+ HB_MultipleSubst* ms = &st->multiple;
+ hb_ot_layout_t* layout = gsub->layout;
+
+ HB_UNUSED(nesting_level);
+
+ if ( context_length != 0xFFFF && context_length < 1 )
+ return HB_Err_Not_Covered;
+
+ if ( CHECK_Property( layout, IN_CURITEM(), flags, &property ) )
+ return error;
+
+ error = _HB_OPEN_Coverage_Index( &ms->Coverage, IN_CURGLYPH(), &index );
+ if ( error )
+ return error;
+
+ if ( index >= ms->SequenceCount )
+ return ERR(HB_Err_Invalid_SubTable);
+
+ count = ms->Sequence[index].GlyphCount;
+ s = ms->Sequence[index].Substitute;
+
+ if ( ADD_String( buffer, 1, count, s, 0xFFFF, 0xFFFF ) )
+ return error;
+
+ if ( _hb_ot_layout_has_new_glyph_classes (layout) )
+ {
+ /* this is a guess only ... */
+
+ if ( property == HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE )
+ property = HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH;
+
+ for ( n = 0; n < count; n++ )
+ hb_ot_layout_set_glyph_class (layout, s[n], property);
+ }
+
+ return HB_Err_Ok;
+}
+
+
+/* LookupType 3 */
+
+/* AlternateSet */
+
+static HB_Error Load_AlternateSet( HB_AlternateSet* as,
+ HB_Stream stream )
+{
+ HB_Error error;
+
+ HB_UShort n, count;
+ HB_UShort* a;
+
+
+ if ( ACCESS_Frame( 2L ) )
+ return error;
+
+ count = as->GlyphCount = GET_UShort();
+
+ FORGET_Frame();
+
+ as->Alternate = NULL;
+
+ if ( ALLOC_ARRAY( as->Alternate, count, HB_UShort ) )
+ return error;
+
+ a = as->Alternate;
+
+ if ( ACCESS_Frame( count * 2L ) )
+ {
+ FREE( a );
+ return error;
+ }
+
+ for ( n = 0; n < count; n++ )
+ a[n] = GET_UShort();
+
+ FORGET_Frame();
+
+ return HB_Err_Ok;
+}
+
+
+static void Free_AlternateSet( HB_AlternateSet* as )
+{
+ FREE( as->Alternate );
+}
+
+
+/* AlternateSubstFormat1 */
+
+static HB_Error Load_AlternateSubst( HB_GSUB_SubTable* st,
+ HB_Stream stream )
+{
+ HB_Error error;
+ HB_AlternateSubst* as = &st->alternate;
+
+ HB_UShort n = 0, m, count;
+ HB_UInt cur_offset, new_offset, base_offset;
+
+ HB_AlternateSet* aset;
+
+
+ base_offset = FILE_Pos();
+
+ if ( ACCESS_Frame( 4L ) )
+ return error;
+
+ as->SubstFormat = GET_UShort(); /* should be 1 */
+ new_offset = GET_UShort() + base_offset;
+
+ FORGET_Frame();
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = _HB_OPEN_Load_Coverage( &as->Coverage, stream ) ) != HB_Err_Ok )
+ return error;
+ (void)FILE_Seek( cur_offset );
+
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail2;
+
+ count = as->AlternateSetCount = GET_UShort();
+
+ FORGET_Frame();
+
+ as->AlternateSet = NULL;
+
+ if ( ALLOC_ARRAY( as->AlternateSet, count, HB_AlternateSet ) )
+ goto Fail2;
+
+ aset = as->AlternateSet;
+
+ for ( n = 0; n < count; n++ )
+ {
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail1;
+
+ new_offset = GET_UShort() + base_offset;
+
+ FORGET_Frame();
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = Load_AlternateSet( &aset[n], stream ) ) != HB_Err_Ok )
+ goto Fail1;
+ (void)FILE_Seek( cur_offset );
+ }
+
+ return HB_Err_Ok;
+
+Fail1:
+ for ( m = 0; m < n; m++ )
+ Free_AlternateSet( &aset[m] );
+
+ FREE( aset );
+
+Fail2:
+ _HB_OPEN_Free_Coverage( &as->Coverage );
+ return error;
+}
+
+
+static void Free_AlternateSubst( HB_GSUB_SubTable* st )
+{
+ HB_UShort n, count;
+ HB_AlternateSubst* as = &st->alternate;
+
+ HB_AlternateSet* aset;
+
+
+ if ( as->AlternateSet )
+ {
+ count = as->AlternateSetCount;
+ aset = as->AlternateSet;
+
+ for ( n = 0; n < count; n++ )
+ Free_AlternateSet( &aset[n] );
+
+ FREE( aset );
+ }
+
+ _HB_OPEN_Free_Coverage( &as->Coverage );
+}
+
+
+static HB_Error Lookup_AlternateSubst( HB_GSUBHeader* gsub,
+ HB_GSUB_SubTable* st,
+ HB_Buffer buffer,
+ HB_UShort flags,
+ HB_UShort context_length,
+ int nesting_level )
+{
+ HB_Error error;
+ HB_UShort index, value, alt_index, property;
+ HB_AlternateSubst* as = &st->alternate;
+ hb_ot_layout_t* layout = gsub->layout;
+ HB_AlternateSet aset;
+
+ HB_UNUSED(nesting_level);
+
+ if ( context_length != 0xFFFF && context_length < 1 )
+ return HB_Err_Not_Covered;
+
+ if ( CHECK_Property( layout, IN_CURITEM(), flags, &property ) )
+ return error;
+
+ error = _HB_OPEN_Coverage_Index( &as->Coverage, IN_CURGLYPH(), &index );
+ if ( error )
+ return error;
+
+ aset = as->AlternateSet[index];
+
+ /* we use a user-defined callback function to get the alternate index */
+
+ if ( gsub->altfunc )
+ alt_index = (gsub->altfunc)( buffer->out_pos, IN_CURGLYPH(),
+ aset.GlyphCount, aset.Alternate,
+ gsub->data );
+ else
+ alt_index = 0;
+
+ value = aset.Alternate[alt_index];
+ if ( REPLACE_Glyph( buffer, value, nesting_level ) )
+ return error;
+
+ if ( _hb_ot_layout_has_new_glyph_classes (layout) )
+ /* we inherit the old glyph class to the substituted glyph */
+ hb_ot_layout_set_glyph_class (layout, value, property);
+
+ return HB_Err_Ok;
+}
+
+
+/* LookupType 4 */
+
+/* Ligature */
+
+static HB_Error Load_Ligature( HB_Ligature* l,
+ HB_Stream stream )
+{
+ HB_Error error;
+
+ HB_UShort n, count;
+ HB_UShort* c;
+
+
+ if ( ACCESS_Frame( 4L ) )
+ return error;
+
+ l->LigGlyph = GET_UShort();
+ l->ComponentCount = GET_UShort();
+
+ FORGET_Frame();
+
+ l->Component = NULL;
+
+ count = l->ComponentCount - 1; /* only ComponentCount - 1 elements */
+
+ if ( ALLOC_ARRAY( l->Component, count, HB_UShort ) )
+ return error;
+
+ c = l->Component;
+
+ if ( ACCESS_Frame( count * 2L ) )
+ {
+ FREE( c );
+ return error;
+ }
+
+ for ( n = 0; n < count; n++ )
+ c[n] = GET_UShort();
+
+ FORGET_Frame();
+
+ return HB_Err_Ok;
+}
+
+
+static void Free_Ligature( HB_Ligature* l )
+{
+ FREE( l->Component );
+}
+
+
+/* LigatureSet */
+
+static HB_Error Load_LigatureSet( HB_LigatureSet* ls,
+ HB_Stream stream )
+{
+ HB_Error error;
+
+ HB_UShort n = 0, m, count;
+ HB_UInt cur_offset, new_offset, base_offset;
+
+ HB_Ligature* l;
+
+
+ base_offset = FILE_Pos();
+
+ if ( ACCESS_Frame( 2L ) )
+ return error;
+
+ count = ls->LigatureCount = GET_UShort();
+
+ FORGET_Frame();
+
+ ls->Ligature = NULL;
+
+ if ( ALLOC_ARRAY( ls->Ligature, count, HB_Ligature ) )
+ return error;
+
+ l = ls->Ligature;
+
+ for ( n = 0; n < count; n++ )
+ {
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail;
+
+ new_offset = GET_UShort() + base_offset;
+
+ FORGET_Frame();
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = Load_Ligature( &l[n], stream ) ) != HB_Err_Ok )
+ goto Fail;
+ (void)FILE_Seek( cur_offset );
+ }
+
+ return HB_Err_Ok;
+
+Fail:
+ for ( m = 0; m < n; m++ )
+ Free_Ligature( &l[m] );
+
+ FREE( l );
+ return error;
+}
+
+
+static void Free_LigatureSet( HB_LigatureSet* ls )
+{
+ HB_UShort n, count;
+
+ HB_Ligature* l;
+
+
+ if ( ls->Ligature )
+ {
+ count = ls->LigatureCount;
+ l = ls->Ligature;
+
+ for ( n = 0; n < count; n++ )
+ Free_Ligature( &l[n] );
+
+ FREE( l );
+ }
+}
+
+
+/* LigatureSubstFormat1 */
+
+static HB_Error Load_LigatureSubst( HB_GSUB_SubTable* st,
+ HB_Stream stream )
+{
+ HB_Error error;
+ HB_LigatureSubst* ls = &st->ligature;
+
+ HB_UShort n = 0, m, count;
+ HB_UInt cur_offset, new_offset, base_offset;
+
+ HB_LigatureSet* lset;
+
+
+ base_offset = FILE_Pos();
+
+ if ( ACCESS_Frame( 4L ) )
+ return error;
+
+ ls->SubstFormat = GET_UShort(); /* should be 1 */
+ new_offset = GET_UShort() + base_offset;
+
+ FORGET_Frame();
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = _HB_OPEN_Load_Coverage( &ls->Coverage, stream ) ) != HB_Err_Ok )
+ return error;
+ (void)FILE_Seek( cur_offset );
+
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail2;
+
+ count = ls->LigatureSetCount = GET_UShort();
+
+ FORGET_Frame();
+
+ ls->LigatureSet = NULL;
+
+ if ( ALLOC_ARRAY( ls->LigatureSet, count, HB_LigatureSet ) )
+ goto Fail2;
+
+ lset = ls->LigatureSet;
+
+ for ( n = 0; n < count; n++ )
+ {
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail1;
+
+ new_offset = GET_UShort() + base_offset;
+
+ FORGET_Frame();
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = Load_LigatureSet( &lset[n], stream ) ) != HB_Err_Ok )
+ goto Fail1;
+ (void)FILE_Seek( cur_offset );
+ }
+
+ return HB_Err_Ok;
+
+Fail1:
+ for ( m = 0; m < n; m++ )
+ Free_LigatureSet( &lset[m] );
+
+ FREE( lset );
+
+Fail2:
+ _HB_OPEN_Free_Coverage( &ls->Coverage );
+ return error;
+}
+
+
+static void Free_LigatureSubst( HB_GSUB_SubTable* st )
+{
+ HB_UShort n, count;
+ HB_LigatureSubst* ls = &st->ligature;
+
+ HB_LigatureSet* lset;
+
+
+ if ( ls->LigatureSet )
+ {
+ count = ls->LigatureSetCount;
+ lset = ls->LigatureSet;
+
+ for ( n = 0; n < count; n++ )
+ Free_LigatureSet( &lset[n] );
+
+ FREE( lset );
+ }
+
+ _HB_OPEN_Free_Coverage( &ls->Coverage );
+}
+
+
+static HB_Error Lookup_LigatureSubst( HB_GSUBHeader* gsub,
+ HB_GSUB_SubTable* st,
+ HB_Buffer buffer,
+ HB_UShort flags,
+ HB_UShort context_length,
+ int nesting_level )
+{
+ HB_UShort index, property;
+ HB_Error error;
+ HB_UShort numlig, i, j, is_mark, first_is_mark = FALSE;
+ HB_UShort* c;
+ HB_LigatureSubst* ls = &st->ligature;
+ hb_ot_layout_t* layout = gsub->layout;
+
+ HB_Ligature* lig;
+
+ HB_UNUSED(nesting_level);
+
+ if ( CHECK_Property( layout, IN_CURITEM(), flags, &property ) )
+ return error;
+
+ if ( property == HB_OT_LAYOUT_GLYPH_CLASS_MARK || property & HB_LOOKUP_FLAG_IGNORE_SPECIAL_MARKS )
+ first_is_mark = TRUE;
+
+ error = _HB_OPEN_Coverage_Index( &ls->Coverage, IN_CURGLYPH(), &index );
+ if ( error )
+ return error;
+
+ if ( index >= ls->LigatureSetCount )
+ return ERR(HB_Err_Invalid_SubTable);
+
+ lig = ls->LigatureSet[index].Ligature;
+
+ for ( numlig = ls->LigatureSet[index].LigatureCount;
+ numlig;
+ numlig--, lig++ )
+ {
+ if ( buffer->in_pos + lig->ComponentCount > buffer->in_length )
+ goto next_ligature; /* Not enough glyphs in input */
+
+ c = lig->Component;
+
+ is_mark = first_is_mark;
+
+ if ( context_length != 0xFFFF && context_length < lig->ComponentCount )
+ break;
+
+ for ( i = 1, j = buffer->in_pos + 1; i < lig->ComponentCount; i++, j++ )
+ {
+ while ( CHECK_Property( layout, IN_ITEM( j ), flags, &property ) )
+ {
+ if ( error && error != HB_Err_Not_Covered )
+ return error;
+
+ if ( j + lig->ComponentCount - i == (HB_Int)buffer->in_length )
+ goto next_ligature;
+ j++;
+ }
+
+ if ( !( property == HB_OT_LAYOUT_GLYPH_CLASS_MARK || property & HB_LOOKUP_FLAG_IGNORE_SPECIAL_MARKS ) )
+ is_mark = FALSE;
+
+ if ( IN_GLYPH( j ) != c[i - 1] )
+ goto next_ligature;
+ }
+
+ if ( _hb_ot_layout_has_new_glyph_classes (layout) )
+ /* this is just a guess ... */
+ hb_ot_layout_set_glyph_class (layout, lig->LigGlyph, is_mark ? HB_OT_LAYOUT_GLYPH_CLASS_MARK : HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE);
+
+ if ( j == buffer->in_pos + i ) /* No input glyphs skipped */
+ {
+ /* We don't use a new ligature ID if there are no skipped
+ glyphs and the ligature already has an ID. */
+
+ if ( IN_LIGID( buffer->in_pos ) )
+ {
+ if ( ADD_String( buffer, i, 1, &lig->LigGlyph,
+ 0xFFFF, 0xFFFF ) )
+ return error;
+ }
+ else
+ {
+ HB_UShort ligID = _hb_buffer_allocate_ligid( buffer );
+ if ( ADD_String( buffer, i, 1, &lig->LigGlyph,
+ 0xFFFF, ligID ) )
+ return error;
+ }
+ }
+ else
+ {
+ HB_UShort ligID = _hb_buffer_allocate_ligid( buffer );
+ if ( ADD_Glyph( buffer, lig->LigGlyph, 0xFFFF, ligID ) )
+ return error;
+
+ /* Now we must do a second loop to copy the skipped glyphs to
+ `out' and assign component values to it. We start with the
+ glyph after the first component. Glyphs between component
+ i and i+1 belong to component i. Together with the ligID
+ value it is later possible to check whether a specific
+ component value really belongs to a given ligature. */
+
+ for ( i = 0; i < lig->ComponentCount - 1; i++ )
+ {
+ while ( CHECK_Property( layout, IN_CURITEM(),
+ flags, &property ) )
+ if ( ADD_Glyph( buffer, IN_CURGLYPH(), i, ligID ) )
+ return error;
+
+ (buffer->in_pos)++;
+ }
+ }
+
+ return HB_Err_Ok;
+
+ next_ligature:
+ ;
+ }
+
+ return HB_Err_Not_Covered;
+}
+
+
+/* Do the actual substitution for a context substitution (either format
+ 5 or 6). This is only called after we've determined that the input
+ matches the subrule. */
+
+static HB_Error Do_ContextSubst( HB_GSUBHeader* gsub,
+ HB_UShort GlyphCount,
+ HB_UShort SubstCount,
+ HB_SubstLookupRecord* subst,
+ HB_Buffer buffer,
+ int nesting_level )
+{
+ HB_Error error;
+ HB_UInt i, old_pos;
+
+
+ i = 0;
+
+ while ( i < GlyphCount )
+ {
+ if ( SubstCount && i == subst->SequenceIndex )
+ {
+ old_pos = buffer->in_pos;
+
+ /* Do a substitution */
+
+ error = GSUB_Do_Glyph_Lookup( gsub, subst->LookupListIndex, buffer,
+ GlyphCount, nesting_level );
+
+ subst++;
+ SubstCount--;
+ i += buffer->in_pos - old_pos;
+
+ if ( error == HB_Err_Not_Covered )
+ {
+ if ( COPY_Glyph( buffer ) )
+ return error;
+ i++;
+ }
+ else if ( error )
+ return error;
+ }
+ else
+ {
+ /* No substitution for this index */
+
+ if ( COPY_Glyph( buffer ) )
+ return error;
+ i++;
+ }
+ }
+
+ return HB_Err_Ok;
+}
+
+
+/* LookupType 5 */
+
+/* SubRule */
+
+static HB_Error Load_SubRule( HB_SubRule* sr,
+ HB_Stream stream )
+{
+ HB_Error error;
+
+ HB_UShort n, count;
+ HB_UShort* i;
+
+ HB_SubstLookupRecord* slr;
+
+
+ if ( ACCESS_Frame( 4L ) )
+ return error;
+
+ sr->GlyphCount = GET_UShort();
+ sr->SubstCount = GET_UShort();
+
+ FORGET_Frame();
+
+ sr->Input = NULL;
+
+ count = sr->GlyphCount - 1; /* only GlyphCount - 1 elements */
+
+ if ( ALLOC_ARRAY( sr->Input, count, HB_UShort ) )
+ return error;
+
+ i = sr->Input;
+
+ if ( ACCESS_Frame( count * 2L ) )
+ goto Fail2;
+
+ for ( n = 0; n < count; n++ )
+ i[n] = GET_UShort();
+
+ FORGET_Frame();
+
+ sr->SubstLookupRecord = NULL;
+
+ count = sr->SubstCount;
+
+ if ( ALLOC_ARRAY( sr->SubstLookupRecord, count, HB_SubstLookupRecord ) )
+ goto Fail2;
+
+ slr = sr->SubstLookupRecord;
+
+ if ( ACCESS_Frame( count * 4L ) )
+ goto Fail1;
+
+ for ( n = 0; n < count; n++ )
+ {
+ slr[n].SequenceIndex = GET_UShort();
+ slr[n].LookupListIndex = GET_UShort();
+ }
+
+ FORGET_Frame();
+
+ return HB_Err_Ok;
+
+Fail1:
+ FREE( slr );
+
+Fail2:
+ FREE( i );
+ return error;
+}
+
+
+static void Free_SubRule( HB_SubRule* sr )
+{
+ FREE( sr->SubstLookupRecord );
+ FREE( sr->Input );
+}
+
+
+/* SubRuleSet */
+
+static HB_Error Load_SubRuleSet( HB_SubRuleSet* srs,
+ HB_Stream stream )
+{
+ HB_Error error;
+
+ HB_UShort n = 0, m, count;
+ HB_UInt cur_offset, new_offset, base_offset;
+
+ HB_SubRule* sr;
+
+
+ base_offset = FILE_Pos();
+
+ if ( ACCESS_Frame( 2L ) )
+ return error;
+
+ count = srs->SubRuleCount = GET_UShort();
+
+ FORGET_Frame();
+
+ srs->SubRule = NULL;
+
+ if ( ALLOC_ARRAY( srs->SubRule, count, HB_SubRule ) )
+ return error;
+
+ sr = srs->SubRule;
+
+ for ( n = 0; n < count; n++ )
+ {
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail;
+
+ new_offset = GET_UShort() + base_offset;
+
+ FORGET_Frame();
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = Load_SubRule( &sr[n], stream ) ) != HB_Err_Ok )
+ goto Fail;
+ (void)FILE_Seek( cur_offset );
+ }
+
+ return HB_Err_Ok;
+
+Fail:
+ for ( m = 0; m < n; m++ )
+ Free_SubRule( &sr[m] );
+
+ FREE( sr );
+ return error;
+}
+
+
+static void Free_SubRuleSet( HB_SubRuleSet* srs )
+{
+ HB_UShort n, count;
+
+ HB_SubRule* sr;
+
+
+ if ( srs->SubRule )
+ {
+ count = srs->SubRuleCount;
+ sr = srs->SubRule;
+
+ for ( n = 0; n < count; n++ )
+ Free_SubRule( &sr[n] );
+
+ FREE( sr );
+ }
+}
+
+
+/* ContextSubstFormat1 */
+
+static HB_Error Load_ContextSubst1( HB_ContextSubstFormat1* csf1,
+ HB_Stream stream )
+{
+ HB_Error error;
+
+ HB_UShort n = 0, m, count;
+ HB_UInt cur_offset, new_offset, base_offset;
+
+ HB_SubRuleSet* srs;
+
+
+ base_offset = FILE_Pos() - 2L;
+
+ if ( ACCESS_Frame( 2L ) )
+ return error;
+
+ new_offset = GET_UShort() + base_offset;
+
+ FORGET_Frame();
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = _HB_OPEN_Load_Coverage( &csf1->Coverage, stream ) ) != HB_Err_Ok )
+ return error;
+ (void)FILE_Seek( cur_offset );
+
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail2;
+
+ count = csf1->SubRuleSetCount = GET_UShort();
+
+ FORGET_Frame();
+
+ csf1->SubRuleSet = NULL;
+
+ if ( ALLOC_ARRAY( csf1->SubRuleSet, count, HB_SubRuleSet ) )
+ goto Fail2;
+
+ srs = csf1->SubRuleSet;
+
+ for ( n = 0; n < count; n++ )
+ {
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail1;
+
+ new_offset = GET_UShort() + base_offset;
+
+ FORGET_Frame();
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = Load_SubRuleSet( &srs[n], stream ) ) != HB_Err_Ok )
+ goto Fail1;
+ (void)FILE_Seek( cur_offset );
+ }
+
+ return HB_Err_Ok;
+
+Fail1:
+ for ( m = 0; m < n; m++ )
+ Free_SubRuleSet( &srs[m] );
+
+ FREE( srs );
+
+Fail2:
+ _HB_OPEN_Free_Coverage( &csf1->Coverage );
+ return error;
+}
+
+
+static void Free_ContextSubst1( HB_ContextSubstFormat1* csf1 )
+{
+ HB_UShort n, count;
+
+ HB_SubRuleSet* srs;
+
+
+ if ( csf1->SubRuleSet )
+ {
+ count = csf1->SubRuleSetCount;
+ srs = csf1->SubRuleSet;
+
+ for ( n = 0; n < count; n++ )
+ Free_SubRuleSet( &srs[n] );
+
+ FREE( srs );
+ }
+
+ _HB_OPEN_Free_Coverage( &csf1->Coverage );
+}
+
+
+/* SubClassRule */
+
+static HB_Error Load_SubClassRule( HB_ContextSubstFormat2* csf2,
+ HB_SubClassRule* scr,
+ HB_Stream stream )
+{
+ HB_Error error;
+
+ HB_UShort n, count;
+
+ HB_UShort* c;
+ HB_SubstLookupRecord* slr;
+
+
+ if ( ACCESS_Frame( 4L ) )
+ return error;
+
+ scr->GlyphCount = GET_UShort();
+ scr->SubstCount = GET_UShort();
+
+ if ( scr->GlyphCount > csf2->MaxContextLength )
+ csf2->MaxContextLength = scr->GlyphCount;
+
+ FORGET_Frame();
+
+ scr->Class = NULL;
+
+ count = scr->GlyphCount - 1; /* only GlyphCount - 1 elements */
+
+ if ( ALLOC_ARRAY( scr->Class, count, HB_UShort ) )
+ return error;
+
+ c = scr->Class;
+
+ if ( ACCESS_Frame( count * 2L ) )
+ goto Fail2;
+
+ for ( n = 0; n < count; n++ )
+ c[n] = GET_UShort();
+
+ FORGET_Frame();
+
+ scr->SubstLookupRecord = NULL;
+
+ count = scr->SubstCount;
+
+ if ( ALLOC_ARRAY( scr->SubstLookupRecord, count, HB_SubstLookupRecord ) )
+ goto Fail2;
+
+ slr = scr->SubstLookupRecord;
+
+ if ( ACCESS_Frame( count * 4L ) )
+ goto Fail1;
+
+ for ( n = 0; n < count; n++ )
+ {
+ slr[n].SequenceIndex = GET_UShort();
+ slr[n].LookupListIndex = GET_UShort();
+ }
+
+ FORGET_Frame();
+
+ return HB_Err_Ok;
+
+Fail1:
+ FREE( slr );
+
+Fail2:
+ FREE( c );
+ return error;
+}
+
+
+static void Free_SubClassRule( HB_SubClassRule* scr )
+{
+ FREE( scr->SubstLookupRecord );
+ FREE( scr->Class );
+}
+
+
+/* SubClassSet */
+
+static HB_Error Load_SubClassSet( HB_ContextSubstFormat2* csf2,
+ HB_SubClassSet* scs,
+ HB_Stream stream )
+{
+ HB_Error error;
+
+ HB_UShort n = 0, m, count;
+ HB_UInt cur_offset, new_offset, base_offset;
+
+ HB_SubClassRule* scr;
+
+
+ base_offset = FILE_Pos();
+
+ if ( ACCESS_Frame( 2L ) )
+ return error;
+
+ count = scs->SubClassRuleCount = GET_UShort();
+
+ FORGET_Frame();
+
+ scs->SubClassRule = NULL;
+
+ if ( ALLOC_ARRAY( scs->SubClassRule, count, HB_SubClassRule ) )
+ return error;
+
+ scr = scs->SubClassRule;
+
+ for ( n = 0; n < count; n++ )
+ {
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail;
+
+ new_offset = GET_UShort() + base_offset;
+
+ FORGET_Frame();
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = Load_SubClassRule( csf2, &scr[n],
+ stream ) ) != HB_Err_Ok )
+ goto Fail;
+ (void)FILE_Seek( cur_offset );
+ }
+
+ return HB_Err_Ok;
+
+Fail:
+ for ( m = 0; m < n; m++ )
+ Free_SubClassRule( &scr[m] );
+
+ FREE( scr );
+ return error;
+}
+
+
+static void Free_SubClassSet( HB_SubClassSet* scs )
+{
+ HB_UShort n, count;
+
+ HB_SubClassRule* scr;
+
+
+ if ( scs->SubClassRule )
+ {
+ count = scs->SubClassRuleCount;
+ scr = scs->SubClassRule;
+
+ for ( n = 0; n < count; n++ )
+ Free_SubClassRule( &scr[n] );
+
+ FREE( scr );
+ }
+}
+
+
+/* ContextSubstFormat2 */
+
+static HB_Error Load_ContextSubst2( HB_ContextSubstFormat2* csf2,
+ HB_Stream stream )
+{
+ HB_Error error;
+
+ HB_UShort n = 0, m, count;
+ HB_UInt cur_offset, new_offset, base_offset;
+
+ HB_SubClassSet* scs;
+
+
+ base_offset = FILE_Pos() - 2;
+
+ if ( ACCESS_Frame( 2L ) )
+ return error;
+
+ new_offset = GET_UShort() + base_offset;
+
+ FORGET_Frame();
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = _HB_OPEN_Load_Coverage( &csf2->Coverage, stream ) ) != HB_Err_Ok )
+ return error;
+ (void)FILE_Seek( cur_offset );
+
+ if ( ACCESS_Frame( 4L ) )
+ goto Fail3;
+
+ new_offset = GET_UShort() + base_offset;
+
+ /* `SubClassSetCount' is the upper limit for class values, thus we
+ read it now to make an additional safety check. */
+
+ count = csf2->SubClassSetCount = GET_UShort();
+
+ FORGET_Frame();
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = _HB_OPEN_Load_ClassDefinition( &csf2->ClassDef, count,
+ stream ) ) != HB_Err_Ok )
+ goto Fail3;
+ (void)FILE_Seek( cur_offset );
+
+ csf2->SubClassSet = NULL;
+ csf2->MaxContextLength = 0;
+
+ if ( ALLOC_ARRAY( csf2->SubClassSet, count, HB_SubClassSet ) )
+ goto Fail2;
+
+ scs = csf2->SubClassSet;
+
+ for ( n = 0; n < count; n++ )
+ {
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail1;
+
+ new_offset = GET_UShort() + base_offset;
+
+ FORGET_Frame();
+
+ if ( new_offset != base_offset ) /* not a NULL offset */
+ {
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = Load_SubClassSet( csf2, &scs[n],
+ stream ) ) != HB_Err_Ok )
+ goto Fail1;
+ (void)FILE_Seek( cur_offset );
+ }
+ else
+ {
+ /* we create a SubClassSet table with no entries */
+
+ csf2->SubClassSet[n].SubClassRuleCount = 0;
+ csf2->SubClassSet[n].SubClassRule = NULL;
+ }
+ }
+
+ return HB_Err_Ok;
+
+Fail1:
+ for ( m = 0; m < n; m++ )
+ Free_SubClassSet( &scs[m] );
+
+ FREE( scs );
+
+Fail2:
+ _HB_OPEN_Free_ClassDefinition( &csf2->ClassDef );
+
+Fail3:
+ _HB_OPEN_Free_Coverage( &csf2->Coverage );
+ return error;
+}
+
+
+static void Free_ContextSubst2( HB_ContextSubstFormat2* csf2 )
+{
+ HB_UShort n, count;
+
+ HB_SubClassSet* scs;
+
+
+ if ( csf2->SubClassSet )
+ {
+ count = csf2->SubClassSetCount;
+ scs = csf2->SubClassSet;
+
+ for ( n = 0; n < count; n++ )
+ Free_SubClassSet( &scs[n] );
+
+ FREE( scs );
+ }
+
+ _HB_OPEN_Free_ClassDefinition( &csf2->ClassDef );
+ _HB_OPEN_Free_Coverage( &csf2->Coverage );
+}
+
+
+/* ContextSubstFormat3 */
+
+static HB_Error Load_ContextSubst3( HB_ContextSubstFormat3* csf3,
+ HB_Stream stream )
+{
+ HB_Error error;
+
+ HB_UShort n = 0, m, count;
+ HB_UInt cur_offset, new_offset, base_offset;
+
+ HB_Coverage* c;
+ HB_SubstLookupRecord* slr;
+
+
+ base_offset = FILE_Pos() - 2L;
+
+ if ( ACCESS_Frame( 4L ) )
+ return error;
+
+ csf3->GlyphCount = GET_UShort();
+ csf3->SubstCount = GET_UShort();
+
+ FORGET_Frame();
+
+ csf3->Coverage = NULL;
+
+ count = csf3->GlyphCount;
+
+ if ( ALLOC_ARRAY( csf3->Coverage, count, HB_Coverage ) )
+ return error;
+
+ c = csf3->Coverage;
+
+ for ( n = 0; n < count; n++ )
+ {
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail2;
+
+ new_offset = GET_UShort() + base_offset;
+
+ FORGET_Frame();
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = _HB_OPEN_Load_Coverage( &c[n], stream ) ) != HB_Err_Ok )
+ goto Fail2;
+ (void)FILE_Seek( cur_offset );
+ }
+
+ csf3->SubstLookupRecord = NULL;
+
+ count = csf3->SubstCount;
+
+ if ( ALLOC_ARRAY( csf3->SubstLookupRecord, count,
+ HB_SubstLookupRecord ) )
+ goto Fail2;
+
+ slr = csf3->SubstLookupRecord;
+
+ if ( ACCESS_Frame( count * 4L ) )
+ goto Fail1;
+
+ for ( n = 0; n < count; n++ )
+ {
+ slr[n].SequenceIndex = GET_UShort();
+ slr[n].LookupListIndex = GET_UShort();
+ }
+
+ FORGET_Frame();
+
+ return HB_Err_Ok;
+
+Fail1:
+ FREE( slr );
+
+Fail2:
+ for ( m = 0; m < n; m++ )
+ _HB_OPEN_Free_Coverage( &c[m] );
+
+ FREE( c );
+ return error;
+}
+
+
+static void Free_ContextSubst3( HB_ContextSubstFormat3* csf3 )
+{
+ HB_UShort n, count;
+
+ HB_Coverage* c;
+
+
+ FREE( csf3->SubstLookupRecord );
+
+ if ( csf3->Coverage )
+ {
+ count = csf3->GlyphCount;
+ c = csf3->Coverage;
+
+ for ( n = 0; n < count; n++ )
+ _HB_OPEN_Free_Coverage( &c[n] );
+
+ FREE( c );
+ }
+}
+
+
+/* ContextSubst */
+
+static HB_Error Load_ContextSubst( HB_GSUB_SubTable* st,
+ HB_Stream stream )
+{
+ HB_Error error;
+ HB_ContextSubst* cs = &st->context;
+
+
+ if ( ACCESS_Frame( 2L ) )
+ return error;
+
+ cs->SubstFormat = GET_UShort();
+
+ FORGET_Frame();
+
+ switch ( cs->SubstFormat )
+ {
+ case 1: return Load_ContextSubst1( &cs->csf.csf1, stream );
+ case 2: return Load_ContextSubst2( &cs->csf.csf2, stream );
+ case 3: return Load_ContextSubst3( &cs->csf.csf3, stream );
+ default: return ERR(HB_Err_Invalid_SubTable_Format);
+ }
+
+ return HB_Err_Ok; /* never reached */
+}
+
+
+static void Free_ContextSubst( HB_GSUB_SubTable* st )
+{
+ HB_ContextSubst* cs = &st->context;
+
+ switch ( cs->SubstFormat )
+ {
+ case 1: Free_ContextSubst1( &cs->csf.csf1 ); break;
+ case 2: Free_ContextSubst2( &cs->csf.csf2 ); break;
+ case 3: Free_ContextSubst3( &cs->csf.csf3 ); break;
+ default: break;
+ }
+}
+
+
+static HB_Error Lookup_ContextSubst1( HB_GSUBHeader* gsub,
+ HB_ContextSubstFormat1* csf1,
+ HB_Buffer buffer,
+ HB_UShort flags,
+ HB_UShort context_length,
+ int nesting_level )
+{
+ HB_UShort index, property;
+ HB_UShort i, j, k, numsr;
+ HB_Error error;
+
+ HB_SubRule* sr;
+ hb_ot_layout_t* layout;
+
+
+ layout = gsub->layout;
+
+ if ( CHECK_Property( layout, IN_CURITEM(), flags, &property ) )
+ return error;
+
+ error = _HB_OPEN_Coverage_Index( &csf1->Coverage, IN_CURGLYPH(), &index );
+ if ( error )
+ return error;
+
+ sr = csf1->SubRuleSet[index].SubRule;
+ numsr = csf1->SubRuleSet[index].SubRuleCount;
+
+ for ( k = 0; k < numsr; k++ )
+ {
+ if ( context_length != 0xFFFF && context_length < sr[k].GlyphCount )
+ goto next_subrule;
+
+ if ( buffer->in_pos + sr[k].GlyphCount > buffer->in_length )
+ goto next_subrule; /* context is too long */
+
+ for ( i = 1, j = buffer->in_pos + 1; i < sr[k].GlyphCount; i++, j++ )
+ {
+ while ( CHECK_Property( layout, IN_ITEM( j ), flags, &property ) )
+ {
+ if ( error && error != HB_Err_Not_Covered )
+ return error;
+
+ if ( j + sr[k].GlyphCount - i == (HB_Int)buffer->in_length )
+ goto next_subrule;
+ j++;
+ }
+
+ if ( IN_GLYPH( j ) != sr[k].Input[i - 1] )
+ goto next_subrule;
+ }
+
+ return Do_ContextSubst( gsub, sr[k].GlyphCount,
+ sr[k].SubstCount, sr[k].SubstLookupRecord,
+ buffer,
+ nesting_level );
+ next_subrule:
+ ;
+ }
+
+ return HB_Err_Not_Covered;
+}
+
+
+static HB_Error Lookup_ContextSubst2( HB_GSUBHeader* gsub,
+ HB_ContextSubstFormat2* csf2,
+ HB_Buffer buffer,
+ HB_UShort flags,
+ HB_UShort context_length,
+ int nesting_level )
+{
+ HB_UShort index, property;
+ HB_Error error;
+ HB_UShort i, j, k, known_classes;
+
+ HB_UShort* classes;
+ HB_UShort* cl;
+
+ HB_SubClassSet* scs;
+ HB_SubClassRule* sr;
+ hb_ot_layout_t* layout;
+
+
+ layout = gsub->layout;
+
+ if ( CHECK_Property( layout, IN_CURITEM(), flags, &property ) )
+ return error;
+
+ /* Note: The coverage table in format 2 doesn't give an index into
+ anything. It just lets us know whether or not we need to
+ do any lookup at all. */
+
+ error = _HB_OPEN_Coverage_Index( &csf2->Coverage, IN_CURGLYPH(), &index );
+ if ( error )
+ return error;
+
+ if (csf2->MaxContextLength < 1)
+ return HB_Err_Not_Covered;
+
+ if ( ALLOC_ARRAY( classes, csf2->MaxContextLength, HB_UShort ) )
+ return error;
+
+ error = _HB_OPEN_Get_Class( &csf2->ClassDef, IN_CURGLYPH(),
+ &classes[0], NULL );
+ if ( error && error != HB_Err_Not_Covered )
+ goto End;
+ known_classes = 0;
+
+ scs = &csf2->SubClassSet[classes[0]];
+ if ( !scs )
+ {
+ error = ERR(HB_Err_Invalid_SubTable);
+ goto End;
+ }
+
+ for ( k = 0; k < scs->SubClassRuleCount; k++ )
+ {
+ sr = &scs->SubClassRule[k];
+
+ if ( context_length != 0xFFFF && context_length < sr->GlyphCount )
+ goto next_subclassrule;
+
+ if ( buffer->in_pos + sr->GlyphCount > buffer->in_length )
+ goto next_subclassrule; /* context is too long */
+
+ cl = sr->Class;
+
+ /* Start at 1 because [0] is implied */
+
+ for ( i = 1, j = buffer->in_pos + 1; i < sr->GlyphCount; i++, j++ )
+ {
+ while ( CHECK_Property( layout, IN_ITEM( j ), flags, &property ) )
+ {
+ if ( error && error != HB_Err_Not_Covered )
+ goto End;
+
+ if ( j + sr->GlyphCount - i < (HB_Int)buffer->in_length )
+ goto next_subclassrule;
+ j++;
+ }
+
+ if ( i > known_classes )
+ {
+ /* Keeps us from having to do this for each rule */
+
+ error = _HB_OPEN_Get_Class( &csf2->ClassDef, IN_GLYPH( j ), &classes[i], NULL );
+ if ( error && error != HB_Err_Not_Covered )
+ goto End;
+ known_classes = i;
+ }
+
+ if ( cl[i - 1] != classes[i] )
+ goto next_subclassrule;
+ }
+
+ error = Do_ContextSubst( gsub, sr->GlyphCount,
+ sr->SubstCount, sr->SubstLookupRecord,
+ buffer,
+ nesting_level );
+ goto End;
+
+ next_subclassrule:
+ ;
+ }
+
+ error = HB_Err_Not_Covered;
+
+End:
+ FREE( classes );
+ return error;
+}
+
+
+static HB_Error Lookup_ContextSubst3( HB_GSUBHeader* gsub,
+ HB_ContextSubstFormat3* csf3,
+ HB_Buffer buffer,
+ HB_UShort flags,
+ HB_UShort context_length,
+ int nesting_level )
+{
+ HB_Error error;
+ HB_UShort index, i, j, property;
+
+ HB_Coverage* c;
+ hb_ot_layout_t* layout;
+
+
+ layout = gsub->layout;
+
+ if ( CHECK_Property( layout, IN_CURITEM(), flags, &property ) )
+ return error;
+
+ if ( context_length != 0xFFFF && context_length < csf3->GlyphCount )
+ return HB_Err_Not_Covered;
+
+ if ( buffer->in_pos + csf3->GlyphCount > buffer->in_length )
+ return HB_Err_Not_Covered; /* context is too long */
+
+ c = csf3->Coverage;
+
+ for ( i = 1, j = buffer->in_pos + 1; i < csf3->GlyphCount; i++, j++ )
+ {
+ while ( CHECK_Property( layout, IN_ITEM( j ), flags, &property ) )
+ {
+ if ( error && error != HB_Err_Not_Covered )
+ return error;
+
+ if ( j + csf3->GlyphCount - i == (HB_Int)buffer->in_length )
+ return HB_Err_Not_Covered;
+ j++;
+ }
+
+ error = _HB_OPEN_Coverage_Index( &c[i], IN_GLYPH( j ), &index );
+ if ( error )
+ return error;
+ }
+
+ return Do_ContextSubst( gsub, csf3->GlyphCount,
+ csf3->SubstCount, csf3->SubstLookupRecord,
+ buffer,
+ nesting_level );
+}
+
+
+static HB_Error Lookup_ContextSubst( HB_GSUBHeader* gsub,
+ HB_GSUB_SubTable* st,
+ HB_Buffer buffer,
+ HB_UShort flags,
+ HB_UShort context_length,
+ int nesting_level )
+{
+ HB_ContextSubst* cs = &st->context;
+
+ switch ( cs->SubstFormat )
+ {
+ case 1: return Lookup_ContextSubst1( gsub, &cs->csf.csf1, buffer, flags, context_length, nesting_level );
+ case 2: return Lookup_ContextSubst2( gsub, &cs->csf.csf2, buffer, flags, context_length, nesting_level );
+ case 3: return Lookup_ContextSubst3( gsub, &cs->csf.csf3, buffer, flags, context_length, nesting_level );
+ default: return ERR(HB_Err_Invalid_SubTable_Format);
+ }
+
+ return HB_Err_Ok; /* never reached */
+}
+
+
+/* LookupType 6 */
+
+/* ChainSubRule */
+
+static HB_Error Load_ChainSubRule( HB_ChainSubRule* csr,
+ HB_Stream stream )
+{
+ HB_Error error;
+
+ HB_UShort n, count;
+ HB_UShort* b;
+ HB_UShort* i;
+ HB_UShort* l;
+
+ HB_SubstLookupRecord* slr;
+
+
+ if ( ACCESS_Frame( 2L ) )
+ return error;
+
+ csr->BacktrackGlyphCount = GET_UShort();
+
+ FORGET_Frame();
+
+ csr->Backtrack = NULL;
+
+ count = csr->BacktrackGlyphCount;
+
+ if ( ALLOC_ARRAY( csr->Backtrack, count, HB_UShort ) )
+ return error;
+
+ b = csr->Backtrack;
+
+ if ( ACCESS_Frame( count * 2L ) )
+ goto Fail4;
+
+ for ( n = 0; n < count; n++ )
+ b[n] = GET_UShort();
+
+ FORGET_Frame();
+
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail4;
+
+ csr->InputGlyphCount = GET_UShort();
+
+ FORGET_Frame();
+
+ csr->Input = NULL;
+
+ count = csr->InputGlyphCount - 1; /* only InputGlyphCount - 1 elements */
+
+ if ( ALLOC_ARRAY( csr->Input, count, HB_UShort ) )
+ goto Fail4;
+
+ i = csr->Input;
+
+ if ( ACCESS_Frame( count * 2L ) )
+ goto Fail3;
+
+ for ( n = 0; n < count; n++ )
+ i[n] = GET_UShort();
+
+ FORGET_Frame();
+
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail3;
+
+ csr->LookaheadGlyphCount = GET_UShort();
+
+ FORGET_Frame();
+
+ csr->Lookahead = NULL;
+
+ count = csr->LookaheadGlyphCount;
+
+ if ( ALLOC_ARRAY( csr->Lookahead, count, HB_UShort ) )
+ goto Fail3;
+
+ l = csr->Lookahead;
+
+ if ( ACCESS_Frame( count * 2L ) )
+ goto Fail2;
+
+ for ( n = 0; n < count; n++ )
+ l[n] = GET_UShort();
+
+ FORGET_Frame();
+
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail2;
+
+ csr->SubstCount = GET_UShort();
+
+ FORGET_Frame();
+
+ csr->SubstLookupRecord = NULL;
+
+ count = csr->SubstCount;
+
+ if ( ALLOC_ARRAY( csr->SubstLookupRecord, count, HB_SubstLookupRecord ) )
+ goto Fail2;
+
+ slr = csr->SubstLookupRecord;
+
+ if ( ACCESS_Frame( count * 4L ) )
+ goto Fail1;
+
+ for ( n = 0; n < count; n++ )
+ {
+ slr[n].SequenceIndex = GET_UShort();
+ slr[n].LookupListIndex = GET_UShort();
+ }
+
+ FORGET_Frame();
+
+ return HB_Err_Ok;
+
+Fail1:
+ FREE( slr );
+
+Fail2:
+ FREE( l );
+
+Fail3:
+ FREE( i );
+
+Fail4:
+ FREE( b );
+ return error;
+}
+
+
+static void Free_ChainSubRule( HB_ChainSubRule* csr )
+{
+ FREE( csr->SubstLookupRecord );
+ FREE( csr->Lookahead );
+ FREE( csr->Input );
+ FREE( csr->Backtrack );
+}
+
+
+/* ChainSubRuleSet */
+
+static HB_Error Load_ChainSubRuleSet( HB_ChainSubRuleSet* csrs,
+ HB_Stream stream )
+{
+ HB_Error error;
+
+ HB_UShort n = 0, m, count;
+ HB_UInt cur_offset, new_offset, base_offset;
+
+ HB_ChainSubRule* csr;
+
+
+ base_offset = FILE_Pos();
+
+ if ( ACCESS_Frame( 2L ) )
+ return error;
+
+ count = csrs->ChainSubRuleCount = GET_UShort();
+
+ FORGET_Frame();
+
+ csrs->ChainSubRule = NULL;
+
+ if ( ALLOC_ARRAY( csrs->ChainSubRule, count, HB_ChainSubRule ) )
+ return error;
+
+ csr = csrs->ChainSubRule;
+
+ for ( n = 0; n < count; n++ )
+ {
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail;
+
+ new_offset = GET_UShort() + base_offset;
+
+ FORGET_Frame();
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = Load_ChainSubRule( &csr[n], stream ) ) != HB_Err_Ok )
+ goto Fail;
+ (void)FILE_Seek( cur_offset );
+ }
+
+ return HB_Err_Ok;
+
+Fail:
+ for ( m = 0; m < n; m++ )
+ Free_ChainSubRule( &csr[m] );
+
+ FREE( csr );
+ return error;
+}
+
+
+static void Free_ChainSubRuleSet( HB_ChainSubRuleSet* csrs )
+{
+ HB_UShort n, count;
+
+ HB_ChainSubRule* csr;
+
+
+ if ( csrs->ChainSubRule )
+ {
+ count = csrs->ChainSubRuleCount;
+ csr = csrs->ChainSubRule;
+
+ for ( n = 0; n < count; n++ )
+ Free_ChainSubRule( &csr[n] );
+
+ FREE( csr );
+ }
+}
+
+
+/* ChainContextSubstFormat1 */
+
+static HB_Error Load_ChainContextSubst1(
+ HB_ChainContextSubstFormat1* ccsf1,
+ HB_Stream stream )
+{
+ HB_Error error;
+
+ HB_UShort n = 0, m, count;
+ HB_UInt cur_offset, new_offset, base_offset;
+
+ HB_ChainSubRuleSet* csrs;
+
+
+ base_offset = FILE_Pos() - 2L;
+
+ if ( ACCESS_Frame( 2L ) )
+ return error;
+
+ new_offset = GET_UShort() + base_offset;
+
+ FORGET_Frame();
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = _HB_OPEN_Load_Coverage( &ccsf1->Coverage, stream ) ) != HB_Err_Ok )
+ return error;
+ (void)FILE_Seek( cur_offset );
+
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail2;
+
+ count = ccsf1->ChainSubRuleSetCount = GET_UShort();
+
+ FORGET_Frame();
+
+ ccsf1->ChainSubRuleSet = NULL;
+
+ if ( ALLOC_ARRAY( ccsf1->ChainSubRuleSet, count, HB_ChainSubRuleSet ) )
+ goto Fail2;
+
+ csrs = ccsf1->ChainSubRuleSet;
+
+ for ( n = 0; n < count; n++ )
+ {
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail1;
+
+ new_offset = GET_UShort() + base_offset;
+
+ FORGET_Frame();
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = Load_ChainSubRuleSet( &csrs[n], stream ) ) != HB_Err_Ok )
+ goto Fail1;
+ (void)FILE_Seek( cur_offset );
+ }
+
+ return HB_Err_Ok;
+
+Fail1:
+ for ( m = 0; m < n; m++ )
+ Free_ChainSubRuleSet( &csrs[m] );
+
+ FREE( csrs );
+
+Fail2:
+ _HB_OPEN_Free_Coverage( &ccsf1->Coverage );
+ return error;
+}
+
+
+static void Free_ChainContextSubst1( HB_ChainContextSubstFormat1* ccsf1 )
+{
+ HB_UShort n, count;
+
+ HB_ChainSubRuleSet* csrs;
+
+
+ if ( ccsf1->ChainSubRuleSet )
+ {
+ count = ccsf1->ChainSubRuleSetCount;
+ csrs = ccsf1->ChainSubRuleSet;
+
+ for ( n = 0; n < count; n++ )
+ Free_ChainSubRuleSet( &csrs[n] );
+
+ FREE( csrs );
+ }
+
+ _HB_OPEN_Free_Coverage( &ccsf1->Coverage );
+}
+
+
+/* ChainSubClassRule */
+
+static HB_Error Load_ChainSubClassRule(
+ HB_ChainContextSubstFormat2* ccsf2,
+ HB_ChainSubClassRule* cscr,
+ HB_Stream stream )
+{
+ HB_Error error;
+
+ HB_UShort n, count;
+
+ HB_UShort* b;
+ HB_UShort* i;
+ HB_UShort* l;
+ HB_SubstLookupRecord* slr;
+
+
+ if ( ACCESS_Frame( 2L ) )
+ return error;
+
+ cscr->BacktrackGlyphCount = GET_UShort();
+
+ FORGET_Frame();
+
+ if ( cscr->BacktrackGlyphCount > ccsf2->MaxBacktrackLength )
+ ccsf2->MaxBacktrackLength = cscr->BacktrackGlyphCount;
+
+ cscr->Backtrack = NULL;
+
+ count = cscr->BacktrackGlyphCount;
+
+ if ( ALLOC_ARRAY( cscr->Backtrack, count, HB_UShort ) )
+ return error;
+
+ b = cscr->Backtrack;
+
+ if ( ACCESS_Frame( count * 2L ) )
+ goto Fail4;
+
+ for ( n = 0; n < count; n++ )
+ b[n] = GET_UShort();
+
+ FORGET_Frame();
+
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail4;
+
+ cscr->InputGlyphCount = GET_UShort();
+
+ FORGET_Frame();
+
+ if ( cscr->InputGlyphCount > ccsf2->MaxInputLength )
+ ccsf2->MaxInputLength = cscr->InputGlyphCount;
+
+ cscr->Input = NULL;
+
+ count = cscr->InputGlyphCount - 1; /* only InputGlyphCount - 1 elements */
+
+ if ( ALLOC_ARRAY( cscr->Input, count, HB_UShort ) )
+ goto Fail4;
+
+ i = cscr->Input;
+
+ if ( ACCESS_Frame( count * 2L ) )
+ goto Fail3;
+
+ for ( n = 0; n < count; n++ )
+ i[n] = GET_UShort();
+
+ FORGET_Frame();
+
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail3;
+
+ cscr->LookaheadGlyphCount = GET_UShort();
+
+ FORGET_Frame();
+
+ if ( cscr->LookaheadGlyphCount > ccsf2->MaxLookaheadLength )
+ ccsf2->MaxLookaheadLength = cscr->LookaheadGlyphCount;
+
+ cscr->Lookahead = NULL;
+
+ count = cscr->LookaheadGlyphCount;
+
+ if ( ALLOC_ARRAY( cscr->Lookahead, count, HB_UShort ) )
+ goto Fail3;
+
+ l = cscr->Lookahead;
+
+ if ( ACCESS_Frame( count * 2L ) )
+ goto Fail2;
+
+ for ( n = 0; n < count; n++ )
+ l[n] = GET_UShort();
+
+ FORGET_Frame();
+
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail2;
+
+ cscr->SubstCount = GET_UShort();
+
+ FORGET_Frame();
+
+ cscr->SubstLookupRecord = NULL;
+
+ count = cscr->SubstCount;
+
+ if ( ALLOC_ARRAY( cscr->SubstLookupRecord, count,
+ HB_SubstLookupRecord ) )
+ goto Fail2;
+
+ slr = cscr->SubstLookupRecord;
+
+ if ( ACCESS_Frame( count * 4L ) )
+ goto Fail1;
+
+ for ( n = 0; n < count; n++ )
+ {
+ slr[n].SequenceIndex = GET_UShort();
+ slr[n].LookupListIndex = GET_UShort();
+ }
+
+ FORGET_Frame();
+
+ return HB_Err_Ok;
+
+Fail1:
+ FREE( slr );
+
+Fail2:
+ FREE( l );
+
+Fail3:
+ FREE( i );
+
+Fail4:
+ FREE( b );
+ return error;
+}
+
+
+static void Free_ChainSubClassRule( HB_ChainSubClassRule* cscr )
+{
+ FREE( cscr->SubstLookupRecord );
+ FREE( cscr->Lookahead );
+ FREE( cscr->Input );
+ FREE( cscr->Backtrack );
+}
+
+
+/* SubClassSet */
+
+static HB_Error Load_ChainSubClassSet(
+ HB_ChainContextSubstFormat2* ccsf2,
+ HB_ChainSubClassSet* cscs,
+ HB_Stream stream )
+{
+ HB_Error error;
+
+ HB_UShort n = 0, m, count;
+ HB_UInt cur_offset, new_offset, base_offset;
+
+ HB_ChainSubClassRule* cscr;
+
+
+ base_offset = FILE_Pos();
+
+ if ( ACCESS_Frame( 2L ) )
+ return error;
+
+ count = cscs->ChainSubClassRuleCount = GET_UShort();
+
+ FORGET_Frame();
+
+ cscs->ChainSubClassRule = NULL;
+
+ if ( ALLOC_ARRAY( cscs->ChainSubClassRule, count,
+ HB_ChainSubClassRule ) )
+ return error;
+
+ cscr = cscs->ChainSubClassRule;
+
+ for ( n = 0; n < count; n++ )
+ {
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail;
+
+ new_offset = GET_UShort() + base_offset;
+
+ FORGET_Frame();
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = Load_ChainSubClassRule( ccsf2, &cscr[n],
+ stream ) ) != HB_Err_Ok )
+ goto Fail;
+ (void)FILE_Seek( cur_offset );
+ }
+
+ return HB_Err_Ok;
+
+Fail:
+ for ( m = 0; m < n; m++ )
+ Free_ChainSubClassRule( &cscr[m] );
+
+ FREE( cscr );
+ return error;
+}
+
+
+static void Free_ChainSubClassSet( HB_ChainSubClassSet* cscs )
+{
+ HB_UShort n, count;
+
+ HB_ChainSubClassRule* cscr;
+
+
+ if ( cscs->ChainSubClassRule )
+ {
+ count = cscs->ChainSubClassRuleCount;
+ cscr = cscs->ChainSubClassRule;
+
+ for ( n = 0; n < count; n++ )
+ Free_ChainSubClassRule( &cscr[n] );
+
+ FREE( cscr );
+ }
+}
+
+
+/* ChainContextSubstFormat2 */
+
+static HB_Error Load_ChainContextSubst2(
+ HB_ChainContextSubstFormat2* ccsf2,
+ HB_Stream stream )
+{
+ HB_Error error;
+
+ HB_UShort n = 0, m, count;
+ HB_UInt cur_offset, new_offset, base_offset;
+ HB_UInt backtrack_offset, input_offset, lookahead_offset;
+
+ HB_ChainSubClassSet* cscs;
+
+
+ base_offset = FILE_Pos() - 2;
+
+ if ( ACCESS_Frame( 2L ) )
+ return error;
+
+ new_offset = GET_UShort() + base_offset;
+
+ FORGET_Frame();
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = _HB_OPEN_Load_Coverage( &ccsf2->Coverage, stream ) ) != HB_Err_Ok )
+ return error;
+ (void)FILE_Seek( cur_offset );
+
+ if ( ACCESS_Frame( 8L ) )
+ goto Fail5;
+
+ backtrack_offset = GET_UShort();
+ input_offset = GET_UShort();
+ lookahead_offset = GET_UShort();
+
+ /* `ChainSubClassSetCount' is the upper limit for input class values,
+ thus we read it now to make an additional safety check. No limit
+ is known or needed for the other two class definitions */
+
+ count = ccsf2->ChainSubClassSetCount = GET_UShort();
+
+ FORGET_Frame();
+
+ if ( ( error = _HB_OPEN_Load_EmptyOrClassDefinition( &ccsf2->BacktrackClassDef, 65535,
+ backtrack_offset, base_offset,
+ stream ) ) != HB_Err_Ok )
+ goto Fail5;
+
+ if ( ( error = _HB_OPEN_Load_EmptyOrClassDefinition( &ccsf2->InputClassDef, count,
+ input_offset, base_offset,
+ stream ) ) != HB_Err_Ok )
+ goto Fail4;
+ if ( ( error = _HB_OPEN_Load_EmptyOrClassDefinition( &ccsf2->LookaheadClassDef, 65535,
+ lookahead_offset, base_offset,
+ stream ) ) != HB_Err_Ok )
+ goto Fail3;
+
+ ccsf2->ChainSubClassSet = NULL;
+ ccsf2->MaxBacktrackLength = 0;
+ ccsf2->MaxInputLength = 0;
+ ccsf2->MaxLookaheadLength = 0;
+
+ if ( ALLOC_ARRAY( ccsf2->ChainSubClassSet, count, HB_ChainSubClassSet ) )
+ goto Fail2;
+
+ cscs = ccsf2->ChainSubClassSet;
+
+ for ( n = 0; n < count; n++ )
+ {
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail1;
+
+ new_offset = GET_UShort() + base_offset;
+
+ FORGET_Frame();
+
+ if ( new_offset != base_offset ) /* not a NULL offset */
+ {
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = Load_ChainSubClassSet( ccsf2, &cscs[n],
+ stream ) ) != HB_Err_Ok )
+ goto Fail1;
+ (void)FILE_Seek( cur_offset );
+ }
+ else
+ {
+ /* we create a ChainSubClassSet table with no entries */
+
+ ccsf2->ChainSubClassSet[n].ChainSubClassRuleCount = 0;
+ ccsf2->ChainSubClassSet[n].ChainSubClassRule = NULL;
+ }
+ }
+
+ return HB_Err_Ok;
+
+Fail1:
+ for ( m = 0; m < n; m++ )
+ Free_ChainSubClassSet( &cscs[m] );
+
+ FREE( cscs );
+
+Fail2:
+ _HB_OPEN_Free_ClassDefinition( &ccsf2->LookaheadClassDef );
+
+Fail3:
+ _HB_OPEN_Free_ClassDefinition( &ccsf2->InputClassDef );
+
+Fail4:
+ _HB_OPEN_Free_ClassDefinition( &ccsf2->BacktrackClassDef );
+
+Fail5:
+ _HB_OPEN_Free_Coverage( &ccsf2->Coverage );
+ return error;
+}
+
+
+static void Free_ChainContextSubst2( HB_ChainContextSubstFormat2* ccsf2 )
+{
+ HB_UShort n, count;
+
+ HB_ChainSubClassSet* cscs;
+
+
+ if ( ccsf2->ChainSubClassSet )
+ {
+ count = ccsf2->ChainSubClassSetCount;
+ cscs = ccsf2->ChainSubClassSet;
+
+ for ( n = 0; n < count; n++ )
+ Free_ChainSubClassSet( &cscs[n] );
+
+ FREE( cscs );
+ }
+
+ _HB_OPEN_Free_ClassDefinition( &ccsf2->LookaheadClassDef );
+ _HB_OPEN_Free_ClassDefinition( &ccsf2->InputClassDef );
+ _HB_OPEN_Free_ClassDefinition( &ccsf2->BacktrackClassDef );
+
+ _HB_OPEN_Free_Coverage( &ccsf2->Coverage );
+}
+
+
+/* ChainContextSubstFormat3 */
+
+static HB_Error Load_ChainContextSubst3(
+ HB_ChainContextSubstFormat3* ccsf3,
+ HB_Stream stream )
+{
+ HB_Error error;
+
+ HB_UShort n, nb = 0, ni =0, nl = 0, m, count;
+ HB_UShort backtrack_count, input_count, lookahead_count;
+ HB_UInt cur_offset, new_offset, base_offset;
+
+ HB_Coverage* b;
+ HB_Coverage* i;
+ HB_Coverage* l;
+ HB_SubstLookupRecord* slr;
+
+
+ base_offset = FILE_Pos() - 2L;
+
+ if ( ACCESS_Frame( 2L ) )
+ return error;
+
+ ccsf3->BacktrackGlyphCount = GET_UShort();
+
+ FORGET_Frame();
+
+ ccsf3->BacktrackCoverage = NULL;
+
+ backtrack_count = ccsf3->BacktrackGlyphCount;
+
+ if ( ALLOC_ARRAY( ccsf3->BacktrackCoverage, backtrack_count,
+ HB_Coverage ) )
+ return error;
+
+ b = ccsf3->BacktrackCoverage;
+
+ for ( nb = 0; nb < backtrack_count; nb++ )
+ {
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail4;
+
+ new_offset = GET_UShort() + base_offset;
+
+ FORGET_Frame();
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = _HB_OPEN_Load_Coverage( &b[nb], stream ) ) != HB_Err_Ok )
+ goto Fail4;
+ (void)FILE_Seek( cur_offset );
+ }
+
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail4;
+
+ ccsf3->InputGlyphCount = GET_UShort();
+
+ FORGET_Frame();
+
+ ccsf3->InputCoverage = NULL;
+
+ input_count = ccsf3->InputGlyphCount;
+
+ if ( ALLOC_ARRAY( ccsf3->InputCoverage, input_count, HB_Coverage ) )
+ goto Fail4;
+
+ i = ccsf3->InputCoverage;
+
+ for ( ni = 0; ni < input_count; ni++ )
+ {
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail3;
+
+ new_offset = GET_UShort() + base_offset;
+
+ FORGET_Frame();
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = _HB_OPEN_Load_Coverage( &i[ni], stream ) ) != HB_Err_Ok )
+ goto Fail3;
+ (void)FILE_Seek( cur_offset );
+ }
+
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail3;
+
+ ccsf3->LookaheadGlyphCount = GET_UShort();
+
+ FORGET_Frame();
+
+ ccsf3->LookaheadCoverage = NULL;
+
+ lookahead_count = ccsf3->LookaheadGlyphCount;
+
+ if ( ALLOC_ARRAY( ccsf3->LookaheadCoverage, lookahead_count,
+ HB_Coverage ) )
+ goto Fail3;
+
+ l = ccsf3->LookaheadCoverage;
+
+ for ( nl = 0; nl < lookahead_count; nl++ )
+ {
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail2;
+
+ new_offset = GET_UShort() + base_offset;
+
+ FORGET_Frame();
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = _HB_OPEN_Load_Coverage( &l[nl], stream ) ) != HB_Err_Ok )
+ goto Fail2;
+ (void)FILE_Seek( cur_offset );
+ }
+
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail2;
+
+ ccsf3->SubstCount = GET_UShort();
+
+ FORGET_Frame();
+
+ ccsf3->SubstLookupRecord = NULL;
+
+ count = ccsf3->SubstCount;
+
+ if ( ALLOC_ARRAY( ccsf3->SubstLookupRecord, count,
+ HB_SubstLookupRecord ) )
+ goto Fail2;
+
+ slr = ccsf3->SubstLookupRecord;
+
+ if ( ACCESS_Frame( count * 4L ) )
+ goto Fail1;
+
+ for ( n = 0; n < count; n++ )
+ {
+ slr[n].SequenceIndex = GET_UShort();
+ slr[n].LookupListIndex = GET_UShort();
+ }
+
+ FORGET_Frame();
+
+ return HB_Err_Ok;
+
+Fail1:
+ FREE( slr );
+
+Fail2:
+ for ( m = 0; m < nl; m++ )
+ _HB_OPEN_Free_Coverage( &l[m] );
+
+ FREE( l );
+
+Fail3:
+ for ( m = 0; m < ni; m++ )
+ _HB_OPEN_Free_Coverage( &i[m] );
+
+ FREE( i );
+
+Fail4:
+ for ( m = 0; m < nb; m++ )
+ _HB_OPEN_Free_Coverage( &b[m] );
+
+ FREE( b );
+ return error;
+}
+
+
+static void Free_ChainContextSubst3( HB_ChainContextSubstFormat3* ccsf3 )
+{
+ HB_UShort n, count;
+
+ HB_Coverage* c;
+
+
+ FREE( ccsf3->SubstLookupRecord );
+
+ if ( ccsf3->LookaheadCoverage )
+ {
+ count = ccsf3->LookaheadGlyphCount;
+ c = ccsf3->LookaheadCoverage;
+
+ for ( n = 0; n < count; n++ )
+ _HB_OPEN_Free_Coverage( &c[n] );
+
+ FREE( c );
+ }
+
+ if ( ccsf3->InputCoverage )
+ {
+ count = ccsf3->InputGlyphCount;
+ c = ccsf3->InputCoverage;
+
+ for ( n = 0; n < count; n++ )
+ _HB_OPEN_Free_Coverage( &c[n] );
+
+ FREE( c );
+ }
+
+ if ( ccsf3->BacktrackCoverage )
+ {
+ count = ccsf3->BacktrackGlyphCount;
+ c = ccsf3->BacktrackCoverage;
+
+ for ( n = 0; n < count; n++ )
+ _HB_OPEN_Free_Coverage( &c[n] );
+
+ FREE( c );
+ }
+}
+
+
+/* ChainContextSubst */
+
+static HB_Error Load_ChainContextSubst( HB_GSUB_SubTable* st,
+ HB_Stream stream )
+{
+ HB_Error error;
+ HB_ChainContextSubst* ccs = &st->chain;
+
+ if ( ACCESS_Frame( 2L ) )
+ return error;
+
+ ccs->SubstFormat = GET_UShort();
+
+ FORGET_Frame();
+
+ switch ( ccs->SubstFormat ) {
+ case 1: return Load_ChainContextSubst1( &ccs->ccsf.ccsf1, stream );
+ case 2: return Load_ChainContextSubst2( &ccs->ccsf.ccsf2, stream );
+ case 3: return Load_ChainContextSubst3( &ccs->ccsf.ccsf3, stream );
+ default: return ERR(HB_Err_Invalid_SubTable_Format);
+ }
+
+ return HB_Err_Ok; /* never reached */
+}
+
+
+static void Free_ChainContextSubst( HB_GSUB_SubTable* st )
+{
+ HB_ChainContextSubst* ccs = &st->chain;
+
+ switch ( ccs->SubstFormat ) {
+ case 1: Free_ChainContextSubst1( &ccs->ccsf.ccsf1 ); break;
+ case 2: Free_ChainContextSubst2( &ccs->ccsf.ccsf2 ); break;
+ case 3: Free_ChainContextSubst3( &ccs->ccsf.ccsf3 ); break;
+ default: break;
+ }
+}
+
+
+static HB_Error Lookup_ChainContextSubst1( HB_GSUBHeader* gsub,
+ HB_ChainContextSubstFormat1* ccsf1,
+ HB_Buffer buffer,
+ HB_UShort flags,
+ HB_UShort context_length,
+ int nesting_level )
+{
+ HB_UShort index, property;
+ HB_UShort i, j, k, num_csr;
+ HB_UShort bgc, igc, lgc;
+ HB_Error error;
+
+ HB_ChainSubRule* csr;
+ HB_ChainSubRule curr_csr;
+ hb_ot_layout_t* layout;
+
+
+ layout = gsub->layout;
+
+ if ( CHECK_Property( layout, IN_CURITEM(), flags, &property ) )
+ return error;
+
+ error = _HB_OPEN_Coverage_Index( &ccsf1->Coverage, IN_CURGLYPH(), &index );
+ if ( error )
+ return error;
+
+ csr = ccsf1->ChainSubRuleSet[index].ChainSubRule;
+ num_csr = ccsf1->ChainSubRuleSet[index].ChainSubRuleCount;
+
+ for ( k = 0; k < num_csr; k++ )
+ {
+ curr_csr = csr[k];
+ bgc = curr_csr.BacktrackGlyphCount;
+ igc = curr_csr.InputGlyphCount;
+ lgc = curr_csr.LookaheadGlyphCount;
+
+ if ( context_length != 0xFFFF && context_length < igc )
+ goto next_chainsubrule;
+
+ /* check whether context is too long; it is a first guess only */
+
+ if ( bgc > buffer->out_pos || buffer->in_pos + igc + lgc > buffer->in_length )
+ goto next_chainsubrule;
+
+ if ( bgc )
+ {
+ /* since we don't know in advance the number of glyphs to inspect,
+ we search backwards for matches in the backtrack glyph array */
+
+ for ( i = 0, j = buffer->out_pos - 1; i < bgc; i++, j-- )
+ {
+ while ( CHECK_Property( layout, OUT_ITEM( j ), flags, &property ) )
+ {
+ if ( error && error != HB_Err_Not_Covered )
+ return error;
+
+ if ( j + 1 == bgc - i )
+ goto next_chainsubrule;
+ j--;
+ }
+
+ /* In OpenType 1.3, it is undefined whether the offsets of
+ backtrack glyphs is in logical order or not. Version 1.4
+ will clarify this:
+
+ Logical order - a b c d e f g h i j
+ i
+ Input offsets - 0 1
+ Backtrack offsets - 3 2 1 0
+ Lookahead offsets - 0 1 2 3 */
+
+ if ( OUT_GLYPH( j ) != curr_csr.Backtrack[i] )
+ goto next_chainsubrule;
+ }
+ }
+
+ /* Start at 1 because [0] is implied */
+
+ for ( i = 1, j = buffer->in_pos + 1; i < igc; i++, j++ )
+ {
+ while ( CHECK_Property( layout, IN_ITEM( j ), flags, &property ) )
+ {
+ if ( error && error != HB_Err_Not_Covered )
+ return error;
+
+ if ( j + igc - i + lgc == (HB_Int)buffer->in_length )
+ goto next_chainsubrule;
+ j++;
+ }
+
+ if ( IN_GLYPH( j ) != curr_csr.Input[i - 1] )
+ goto next_chainsubrule;
+ }
+
+ /* we are starting to check for lookahead glyphs right after the
+ last context glyph */
+
+ for ( i = 0; i < lgc; i++, j++ )
+ {
+ while ( CHECK_Property( layout, IN_ITEM( j ), flags, &property ) )
+ {
+ if ( error && error != HB_Err_Not_Covered )
+ return error;
+
+ if ( j + lgc - i == (HB_Int)buffer->in_length )
+ goto next_chainsubrule;
+ j++;
+ }
+
+ if ( IN_GLYPH( j ) != curr_csr.Lookahead[i] )
+ goto next_chainsubrule;
+ }
+
+ return Do_ContextSubst( gsub, igc,
+ curr_csr.SubstCount,
+ curr_csr.SubstLookupRecord,
+ buffer,
+ nesting_level );
+
+ next_chainsubrule:
+ ;
+ }
+
+ return HB_Err_Not_Covered;
+}
+
+
+static HB_Error Lookup_ChainContextSubst2( HB_GSUBHeader* gsub,
+ HB_ChainContextSubstFormat2* ccsf2,
+ HB_Buffer buffer,
+ HB_UShort flags,
+ HB_UShort context_length,
+ int nesting_level )
+{
+ HB_UShort index, property;
+ HB_Error error;
+ HB_UShort i, j, k;
+ HB_UShort bgc, igc, lgc;
+ HB_UShort known_backtrack_classes,
+ known_input_classes,
+ known_lookahead_classes;
+
+ HB_UShort* backtrack_classes;
+ HB_UShort* input_classes;
+ HB_UShort* lookahead_classes;
+
+ HB_UShort* bc;
+ HB_UShort* ic;
+ HB_UShort* lc;
+
+ HB_ChainSubClassSet* cscs;
+ HB_ChainSubClassRule ccsr;
+ hb_ot_layout_t* layout;
+
+
+ layout = gsub->layout;
+
+ if ( CHECK_Property( layout, IN_CURITEM(), flags, &property ) )
+ return error;
+
+ /* Note: The coverage table in format 2 doesn't give an index into
+ anything. It just lets us know whether or not we need to
+ do any lookup at all. */
+
+ error = _HB_OPEN_Coverage_Index( &ccsf2->Coverage, IN_CURGLYPH(), &index );
+ if ( error )
+ return error;
+
+ if ( ALLOC_ARRAY( backtrack_classes, ccsf2->MaxBacktrackLength, HB_UShort ) )
+ return error;
+ known_backtrack_classes = 0;
+
+ if (ccsf2->MaxInputLength < 1)
+ return HB_Err_Not_Covered;
+
+ if ( ALLOC_ARRAY( input_classes, ccsf2->MaxInputLength, HB_UShort ) )
+ goto End3;
+ known_input_classes = 1;
+
+ if ( ALLOC_ARRAY( lookahead_classes, ccsf2->MaxLookaheadLength, HB_UShort ) )
+ goto End2;
+ known_lookahead_classes = 0;
+
+ error = _HB_OPEN_Get_Class( &ccsf2->InputClassDef, IN_CURGLYPH(),
+ &input_classes[0], NULL );
+ if ( error && error != HB_Err_Not_Covered )
+ goto End1;
+
+ cscs = &ccsf2->ChainSubClassSet[input_classes[0]];
+ if ( !cscs )
+ {
+ error = ERR(HB_Err_Invalid_SubTable);
+ goto End1;
+ }
+
+ for ( k = 0; k < cscs->ChainSubClassRuleCount; k++ )
+ {
+ ccsr = cscs->ChainSubClassRule[k];
+ bgc = ccsr.BacktrackGlyphCount;
+ igc = ccsr.InputGlyphCount;
+ lgc = ccsr.LookaheadGlyphCount;
+
+ if ( context_length != 0xFFFF && context_length < igc )
+ goto next_chainsubclassrule;
+
+ /* check whether context is too long; it is a first guess only */
+
+ if ( bgc > buffer->out_pos || buffer->in_pos + igc + lgc > buffer->in_length )
+ goto next_chainsubclassrule;
+
+ if ( bgc )
+ {
+ /* Since we don't know in advance the number of glyphs to inspect,
+ we search backwards for matches in the backtrack glyph array.
+ Note that `known_backtrack_classes' starts at index 0. */
+
+ bc = ccsr.Backtrack;
+
+ for ( i = 0, j = buffer->out_pos - 1; i < bgc; i++, j-- )
+ {
+ while ( CHECK_Property( layout, OUT_ITEM( j ), flags, &property ) )
+ {
+ if ( error && error != HB_Err_Not_Covered )
+ goto End1;
+
+ if ( j + 1 == bgc - i )
+ goto next_chainsubclassrule;
+ j--;
+ }
+
+ if ( i >= known_backtrack_classes )
+ {
+ /* Keeps us from having to do this for each rule */
+
+ error = _HB_OPEN_Get_Class( &ccsf2->BacktrackClassDef, OUT_GLYPH( j ),
+ &backtrack_classes[i], NULL );
+ if ( error && error != HB_Err_Not_Covered )
+ goto End1;
+ known_backtrack_classes = i;
+ }
+
+ if ( bc[i] != backtrack_classes[i] )
+ goto next_chainsubclassrule;
+ }
+ }
+
+ ic = ccsr.Input;
+
+ /* Start at 1 because [0] is implied */
+
+ for ( i = 1, j = buffer->in_pos + 1; i < igc; i++, j++ )
+ {
+ while ( CHECK_Property( layout, IN_ITEM( j ), flags, &property ) )
+ {
+ if ( error && error != HB_Err_Not_Covered )
+ goto End1;
+
+ if ( j + igc - i + lgc == (HB_Int)buffer->in_length )
+ goto next_chainsubclassrule;
+ j++;
+ }
+
+ if ( i >= known_input_classes )
+ {
+ error = _HB_OPEN_Get_Class( &ccsf2->InputClassDef, IN_GLYPH( j ),
+ &input_classes[i], NULL );
+ if ( error && error != HB_Err_Not_Covered )
+ goto End1;
+ known_input_classes = i;
+ }
+
+ if ( ic[i - 1] != input_classes[i] )
+ goto next_chainsubclassrule;
+ }
+
+ /* we are starting to check for lookahead glyphs right after the
+ last context glyph */
+
+ lc = ccsr.Lookahead;
+
+ for ( i = 0; i < lgc; i++, j++ )
+ {
+ while ( CHECK_Property( layout, IN_ITEM( j ), flags, &property ) )
+ {
+ if ( error && error != HB_Err_Not_Covered )
+ goto End1;
+
+ if ( j + lgc - i == (HB_Int)buffer->in_length )
+ goto next_chainsubclassrule;
+ j++;
+ }
+
+ if ( i >= known_lookahead_classes )
+ {
+ error = _HB_OPEN_Get_Class( &ccsf2->LookaheadClassDef, IN_GLYPH( j ),
+ &lookahead_classes[i], NULL );
+ if ( error && error != HB_Err_Not_Covered )
+ goto End1;
+ known_lookahead_classes = i;
+ }
+
+ if ( lc[i] != lookahead_classes[i] )
+ goto next_chainsubclassrule;
+ }
+
+ error = Do_ContextSubst( gsub, igc,
+ ccsr.SubstCount,
+ ccsr.SubstLookupRecord,
+ buffer,
+ nesting_level );
+ goto End1;
+
+ next_chainsubclassrule:
+ ;
+ }
+
+ error = HB_Err_Not_Covered;
+
+End1:
+ FREE( lookahead_classes );
+
+End2:
+ FREE( input_classes );
+
+End3:
+ FREE( backtrack_classes );
+ return error;
+}
+
+
+static HB_Error Lookup_ChainContextSubst3( HB_GSUBHeader* gsub,
+ HB_ChainContextSubstFormat3* ccsf3,
+ HB_Buffer buffer,
+ HB_UShort flags,
+ HB_UShort context_length,
+ int nesting_level )
+{
+ HB_UShort index, i, j, property;
+ HB_UShort bgc, igc, lgc;
+ HB_Error error;
+
+ HB_Coverage* bc;
+ HB_Coverage* ic;
+ HB_Coverage* lc;
+ hb_ot_layout_t* layout;
+
+
+ layout = gsub->layout;
+
+ if ( CHECK_Property( layout, IN_CURITEM(), flags, &property ) )
+ return error;
+
+ bgc = ccsf3->BacktrackGlyphCount;
+ igc = ccsf3->InputGlyphCount;
+ lgc = ccsf3->LookaheadGlyphCount;
+
+ if ( context_length != 0xFFFF && context_length < igc )
+ return HB_Err_Not_Covered;
+
+ /* check whether context is too long; it is a first guess only */
+
+ if ( bgc > buffer->out_pos || buffer->in_pos + igc + lgc > buffer->in_length )
+ return HB_Err_Not_Covered;
+
+ if ( bgc )
+ {
+ /* Since we don't know in advance the number of glyphs to inspect,
+ we search backwards for matches in the backtrack glyph array */
+
+ bc = ccsf3->BacktrackCoverage;
+
+ for ( i = 0, j = buffer->out_pos - 1; i < bgc; i++, j-- )
+ {
+ while ( CHECK_Property( layout, OUT_ITEM( j ), flags, &property ) )
+ {
+ if ( error && error != HB_Err_Not_Covered )
+ return error;
+
+ if ( j + 1 == bgc - i )
+ return HB_Err_Not_Covered;
+ j--;
+ }
+
+ error = _HB_OPEN_Coverage_Index( &bc[i], OUT_GLYPH( j ), &index );
+ if ( error )
+ return error;
+ }
+ }
+
+ ic = ccsf3->InputCoverage;
+
+ for ( i = 0, j = buffer->in_pos; i < igc; i++, j++ )
+ {
+ /* We already called CHECK_Property for IN_GLYPH( buffer->in_pos ) */
+ while ( j > buffer->in_pos && CHECK_Property( layout, IN_ITEM( j ), flags, &property ) )
+ {
+ if ( error && error != HB_Err_Not_Covered )
+ return error;
+
+ if ( j + igc - i + lgc == (HB_Int)buffer->in_length )
+ return HB_Err_Not_Covered;
+ j++;
+ }
+
+ error = _HB_OPEN_Coverage_Index( &ic[i], IN_GLYPH( j ), &index );
+ if ( error )
+ return error;
+ }
+
+ /* we are starting for lookahead glyphs right after the last context
+ glyph */
+
+ lc = ccsf3->LookaheadCoverage;
+
+ for ( i = 0; i < lgc; i++, j++ )
+ {
+ while ( CHECK_Property( layout, IN_ITEM( j ), flags, &property ) )
+ {
+ if ( error && error != HB_Err_Not_Covered )
+ return error;
+
+ if ( j + lgc - i == (HB_Int)buffer->in_length )
+ return HB_Err_Not_Covered;
+ j++;
+ }
+
+ error = _HB_OPEN_Coverage_Index( &lc[i], IN_GLYPH( j ), &index );
+ if ( error )
+ return error;
+ }
+
+ return Do_ContextSubst( gsub, igc,
+ ccsf3->SubstCount,
+ ccsf3->SubstLookupRecord,
+ buffer,
+ nesting_level );
+}
+
+
+static HB_Error Lookup_ChainContextSubst( HB_GSUBHeader* gsub,
+ HB_GSUB_SubTable* st,
+ HB_Buffer buffer,
+ HB_UShort flags,
+ HB_UShort context_length,
+ int nesting_level )
+{
+ HB_ChainContextSubst* ccs = &st->chain;
+
+ switch ( ccs->SubstFormat ) {
+ case 1: return Lookup_ChainContextSubst1( gsub, &ccs->ccsf.ccsf1, buffer, flags, context_length, nesting_level );
+ case 2: return Lookup_ChainContextSubst2( gsub, &ccs->ccsf.ccsf2, buffer, flags, context_length, nesting_level );
+ case 3: return Lookup_ChainContextSubst3( gsub, &ccs->ccsf.ccsf3, buffer, flags, context_length, nesting_level );
+ default: return ERR(HB_Err_Invalid_SubTable_Format);
+ }
+}
+
+
+static HB_Error Load_ReverseChainContextSubst( HB_GSUB_SubTable* st,
+ HB_Stream stream )
+{
+ HB_Error error;
+ HB_ReverseChainContextSubst* rccs = &st->reverse;
+
+ HB_UShort m, count;
+
+ HB_UShort nb = 0, nl = 0, n;
+ HB_UShort backtrack_count, lookahead_count;
+ HB_UInt cur_offset, new_offset, base_offset;
+
+ HB_Coverage* b;
+ HB_Coverage* l;
+ HB_UShort* sub;
+
+ base_offset = FILE_Pos();
+
+ if ( ACCESS_Frame( 2L ) )
+ return error;
+
+ rccs->SubstFormat = GET_UShort();
+
+ if ( rccs->SubstFormat != 1 )
+ return ERR(HB_Err_Invalid_SubTable_Format);
+
+ FORGET_Frame();
+
+ if ( ACCESS_Frame( 2L ) )
+ return error;
+
+ new_offset = GET_UShort() + base_offset;
+
+ FORGET_Frame();
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = _HB_OPEN_Load_Coverage( &rccs->Coverage, stream ) ) != HB_Err_Ok )
+ return error;
+ (void)FILE_Seek( cur_offset );
+
+
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail4;
+
+ rccs->BacktrackGlyphCount = GET_UShort();
+
+ FORGET_Frame();
+
+ rccs->BacktrackCoverage = NULL;
+
+ backtrack_count = rccs->BacktrackGlyphCount;
+
+ if ( ALLOC_ARRAY( rccs->BacktrackCoverage, backtrack_count,
+ HB_Coverage ) )
+ goto Fail4;
+
+ b = rccs->BacktrackCoverage;
+
+ for ( nb = 0; nb < backtrack_count; nb++ )
+ {
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail3;
+
+ new_offset = GET_UShort() + base_offset;
+
+ FORGET_Frame();
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = _HB_OPEN_Load_Coverage( &b[nb], stream ) ) != HB_Err_Ok )
+ goto Fail3;
+ (void)FILE_Seek( cur_offset );
+ }
+
+
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail3;
+
+ rccs->LookaheadGlyphCount = GET_UShort();
+
+ FORGET_Frame();
+
+ rccs->LookaheadCoverage = NULL;
+
+ lookahead_count = rccs->LookaheadGlyphCount;
+
+ if ( ALLOC_ARRAY( rccs->LookaheadCoverage, lookahead_count,
+ HB_Coverage ) )
+ goto Fail3;
+
+ l = rccs->LookaheadCoverage;
+
+ for ( nl = 0; nl < lookahead_count; nl++ )
+ {
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail2;
+
+ new_offset = GET_UShort() + base_offset;
+
+ FORGET_Frame();
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = _HB_OPEN_Load_Coverage( &l[nl], stream ) ) != HB_Err_Ok )
+ goto Fail2;
+ (void)FILE_Seek( cur_offset );
+ }
+
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail2;
+
+ rccs->GlyphCount = GET_UShort();
+
+ FORGET_Frame();
+
+ rccs->Substitute = NULL;
+
+ count = rccs->GlyphCount;
+
+ if ( ALLOC_ARRAY( rccs->Substitute, count,
+ HB_UShort ) )
+ goto Fail2;
+
+ sub = rccs->Substitute;
+
+ if ( ACCESS_Frame( count * 2L ) )
+ goto Fail1;
+
+ for ( n = 0; n < count; n++ )
+ sub[n] = GET_UShort();
+
+ FORGET_Frame();
+
+ return HB_Err_Ok;
+
+Fail1:
+ FREE( sub );
+
+Fail2:
+ for ( m = 0; m < nl; m++ )
+ _HB_OPEN_Free_Coverage( &l[m] );
+
+ FREE( l );
+
+Fail3:
+ for ( m = 0; m < nb; m++ )
+ _HB_OPEN_Free_Coverage( &b[m] );
+
+ FREE( b );
+
+Fail4:
+ _HB_OPEN_Free_Coverage( &rccs->Coverage );
+ return error;
+}
+
+
+static void Free_ReverseChainContextSubst( HB_GSUB_SubTable* st )
+{
+ HB_UShort n, count;
+ HB_ReverseChainContextSubst* rccs = &st->reverse;
+
+ HB_Coverage* c;
+
+ _HB_OPEN_Free_Coverage( &rccs->Coverage );
+
+ if ( rccs->LookaheadCoverage )
+ {
+ count = rccs->LookaheadGlyphCount;
+ c = rccs->LookaheadCoverage;
+
+ for ( n = 0; n < count; n++ )
+ _HB_OPEN_Free_Coverage( &c[n] );
+
+ FREE( c );
+ }
+
+ if ( rccs->BacktrackCoverage )
+ {
+ count = rccs->BacktrackGlyphCount;
+ c = rccs->BacktrackCoverage;
+
+ for ( n = 0; n < count; n++ )
+ _HB_OPEN_Free_Coverage( &c[n] );
+
+ FREE( c );
+ }
+
+ FREE ( rccs->Substitute );
+}
+
+
+static HB_Error Lookup_ReverseChainContextSubst( HB_GSUBHeader* gsub,
+ HB_GSUB_SubTable* st,
+ HB_Buffer buffer,
+ HB_UShort flags,
+ HB_UShort context_length,
+ int nesting_level )
+{
+ HB_UShort index, input_index, i, j, property;
+ HB_UShort bgc, lgc;
+ HB_Error error;
+
+ HB_ReverseChainContextSubst* rccs = &st->reverse;
+ HB_Coverage* bc;
+ HB_Coverage* lc;
+ hb_ot_layout_t* layout;
+
+ if ( nesting_level != 1 || context_length != 0xFFFF )
+ return HB_Err_Not_Covered;
+
+ layout = gsub->layout;
+
+ if ( CHECK_Property( layout, IN_CURITEM(), flags, &property ) )
+ return error;
+
+ bgc = rccs->BacktrackGlyphCount;
+ lgc = rccs->LookaheadGlyphCount;
+
+ /* check whether context is too long; it is a first guess only */
+
+ if ( bgc > buffer->in_pos || buffer->in_pos + 1 + lgc > buffer->in_length )
+ return HB_Err_Not_Covered;
+
+ if ( bgc )
+ {
+ /* Since we don't know in advance the number of glyphs to inspect,
+ we search backwards for matches in the backtrack glyph array */
+
+ bc = rccs->BacktrackCoverage;
+
+ for ( i = 0, j = buffer->in_pos - 1; i < bgc; i++, j-- )
+ {
+ while ( CHECK_Property( layout, IN_ITEM( j ), flags, &property ) )
+ {
+ if ( error && error != HB_Err_Not_Covered )
+ return error;
+
+ if ( j + 1 == bgc - i )
+ return HB_Err_Not_Covered;
+ j--;
+ }
+
+ error = _HB_OPEN_Coverage_Index( &bc[i], IN_GLYPH( j ), &index );
+ if ( error )
+ return error;
+ }
+ }
+
+ j = buffer->in_pos;
+
+ error = _HB_OPEN_Coverage_Index( &rccs->Coverage, IN_GLYPH( j ), &input_index );
+ if ( error )
+ return error;
+
+ lc = rccs->LookaheadCoverage;
+
+ for ( i = 0, j = buffer->in_pos + 1; i < lgc; i++, j++ )
+ {
+ while ( CHECK_Property( layout, IN_ITEM( j ), flags, &property ) )
+ {
+ if ( error && error != HB_Err_Not_Covered )
+ return error;
+
+ if ( j + lgc - i == (HB_Int)buffer->in_length )
+ return HB_Err_Not_Covered;
+ j++;
+ }
+
+ error = _HB_OPEN_Coverage_Index( &lc[i], IN_GLYPH( j ), &index );
+ if ( error )
+ return error;
+ }
+
+ IN_CURGLYPH() = rccs->Substitute[input_index];
+ buffer->in_pos--; /* Reverse! */
+
+ return error;
+}
+
+
+
+/***********
+ * GSUB API
+ ***********/
+
+
+
+HB_Error HB_GSUB_Select_Script( HB_GSUBHeader* gsub,
+ HB_UInt script_tag,
+ HB_UShort* script_index )
+{
+ HB_UShort n;
+
+ HB_ScriptList* sl;
+ HB_ScriptRecord* sr;
+
+
+ if ( !gsub || !script_index )
+ return ERR(HB_Err_Invalid_Argument);
+
+ sl = &gsub->ScriptList;
+ sr = sl->ScriptRecord;
+
+ for ( n = 0; n < sl->ScriptCount; n++ )
+ if ( script_tag == sr[n].ScriptTag )
+ {
+ *script_index = n;
+
+ return HB_Err_Ok;
+ }
+
+ return HB_Err_Not_Covered;
+}
+
+
+
+HB_Error HB_GSUB_Select_Language( HB_GSUBHeader* gsub,
+ HB_UInt language_tag,
+ HB_UShort script_index,
+ HB_UShort* language_index,
+ HB_UShort* req_feature_index )
+{
+ HB_UShort n;
+
+ HB_ScriptList* sl;
+ HB_ScriptRecord* sr;
+ HB_ScriptTable* s;
+ HB_LangSysRecord* lsr;
+
+
+ if ( !gsub || !language_index || !req_feature_index )
+ return ERR(HB_Err_Invalid_Argument);
+
+ sl = &gsub->ScriptList;
+ sr = sl->ScriptRecord;
+
+ if ( script_index >= sl->ScriptCount )
+ return ERR(HB_Err_Invalid_Argument);
+
+ s = &sr[script_index].Script;
+ lsr = s->LangSysRecord;
+
+ for ( n = 0; n < s->LangSysCount; n++ )
+ if ( language_tag == lsr[n].LangSysTag )
+ {
+ *language_index = n;
+ *req_feature_index = lsr[n].LangSys.ReqFeatureIndex;
+
+ return HB_Err_Ok;
+ }
+
+ return HB_Err_Not_Covered;
+}
+
+
+/* selecting 0xFFFF for language_index asks for the values of the
+ default language (DefaultLangSys) */
+
+
+HB_Error HB_GSUB_Select_Feature( HB_GSUBHeader* gsub,
+ HB_UInt feature_tag,
+ HB_UShort script_index,
+ HB_UShort language_index,
+ HB_UShort* feature_index )
+{
+ HB_UShort n;
+
+ HB_ScriptList* sl;
+ HB_ScriptRecord* sr;
+ HB_ScriptTable* s;
+ HB_LangSysRecord* lsr;
+ HB_LangSys* ls;
+ HB_UShort* fi;
+
+ HB_FeatureList* fl;
+ HB_FeatureRecord* fr;
+
+
+ if ( !gsub || !feature_index )
+ return ERR(HB_Err_Invalid_Argument);
+
+ sl = &gsub->ScriptList;
+ sr = sl->ScriptRecord;
+
+ fl = &gsub->FeatureList;
+ fr = fl->FeatureRecord;
+
+ if ( script_index >= sl->ScriptCount )
+ return ERR(HB_Err_Invalid_Argument);
+
+ s = &sr[script_index].Script;
+ lsr = s->LangSysRecord;
+
+ if ( language_index == 0xFFFF )
+ ls = &s->DefaultLangSys;
+ else
+ {
+ if ( language_index >= s->LangSysCount )
+ return ERR(HB_Err_Invalid_Argument);
+
+ ls = &lsr[language_index].LangSys;
+ }
+
+ fi = ls->FeatureIndex;
+
+ for ( n = 0; n < ls->FeatureCount; n++ )
+ {
+ if ( fi[n] >= fl->FeatureCount )
+ return ERR(HB_Err_Invalid_SubTable_Format);
+
+ if ( feature_tag == fr[fi[n]].FeatureTag )
+ {
+ *feature_index = fi[n];
+
+ return HB_Err_Ok;
+ }
+ }
+
+ return HB_Err_Not_Covered;
+}
+
+
+/* The next three functions return a null-terminated list */
+
+
+HB_Error HB_GSUB_Query_Scripts( HB_GSUBHeader* gsub,
+ HB_UInt** script_tag_list )
+{
+ HB_UShort n;
+ HB_Error error;
+ HB_UInt* stl;
+
+ HB_ScriptList* sl;
+ HB_ScriptRecord* sr;
+
+
+ if ( !gsub || !script_tag_list )
+ return ERR(HB_Err_Invalid_Argument);
+
+ sl = &gsub->ScriptList;
+ sr = sl->ScriptRecord;
+
+ if ( ALLOC_ARRAY( stl, sl->ScriptCount + 1, HB_UInt ) )
+ return error;
+
+ for ( n = 0; n < sl->ScriptCount; n++ )
+ stl[n] = sr[n].ScriptTag;
+ stl[n] = 0;
+
+ *script_tag_list = stl;
+
+ return HB_Err_Ok;
+}
+
+
+
+HB_Error HB_GSUB_Query_Languages( HB_GSUBHeader* gsub,
+ HB_UShort script_index,
+ HB_UInt** language_tag_list )
+{
+ HB_UShort n;
+ HB_Error error;
+ HB_UInt* ltl;
+
+ HB_ScriptList* sl;
+ HB_ScriptRecord* sr;
+ HB_ScriptTable* s;
+ HB_LangSysRecord* lsr;
+
+
+ if ( !gsub || !language_tag_list )
+ return ERR(HB_Err_Invalid_Argument);
+
+ sl = &gsub->ScriptList;
+ sr = sl->ScriptRecord;
+
+ if ( script_index >= sl->ScriptCount )
+ return ERR(HB_Err_Invalid_Argument);
+
+ s = &sr[script_index].Script;
+ lsr = s->LangSysRecord;
+
+ if ( ALLOC_ARRAY( ltl, s->LangSysCount + 1, HB_UInt ) )
+ return error;
+
+ for ( n = 0; n < s->LangSysCount; n++ )
+ ltl[n] = lsr[n].LangSysTag;
+ ltl[n] = 0;
+
+ *language_tag_list = ltl;
+
+ return HB_Err_Ok;
+}
+
+
+/* selecting 0xFFFF for language_index asks for the values of the
+ default language (DefaultLangSys) */
+
+
+HB_Error HB_GSUB_Query_Features( HB_GSUBHeader* gsub,
+ HB_UShort script_index,
+ HB_UShort language_index,
+ HB_UInt** feature_tag_list )
+{
+ HB_UShort n;
+ HB_Error error;
+ HB_UInt* ftl;
+
+ HB_ScriptList* sl;
+ HB_ScriptRecord* sr;
+ HB_ScriptTable* s;
+ HB_LangSysRecord* lsr;
+ HB_LangSys* ls;
+ HB_UShort* fi;
+
+ HB_FeatureList* fl;
+ HB_FeatureRecord* fr;
+
+
+ if ( !gsub || !feature_tag_list )
+ return ERR(HB_Err_Invalid_Argument);
+
+ sl = &gsub->ScriptList;
+ sr = sl->ScriptRecord;
+
+ fl = &gsub->FeatureList;
+ fr = fl->FeatureRecord;
+
+ if ( script_index >= sl->ScriptCount )
+ return ERR(HB_Err_Invalid_Argument);
+
+ s = &sr[script_index].Script;
+ lsr = s->LangSysRecord;
+
+ if ( language_index == 0xFFFF )
+ ls = &s->DefaultLangSys;
+ else
+ {
+ if ( language_index >= s->LangSysCount )
+ return ERR(HB_Err_Invalid_Argument);
+
+ ls = &lsr[language_index].LangSys;
+ }
+
+ fi = ls->FeatureIndex;
+
+ if ( ALLOC_ARRAY( ftl, ls->FeatureCount + 1, HB_UInt ) )
+ return error;
+
+ for ( n = 0; n < ls->FeatureCount; n++ )
+ {
+ if ( fi[n] >= fl->FeatureCount )
+ {
+ FREE( ftl );
+ return ERR(HB_Err_Invalid_SubTable_Format);
+ }
+ ftl[n] = fr[fi[n]].FeatureTag;
+ }
+ ftl[n] = 0;
+
+ *feature_tag_list = ftl;
+
+ return HB_Err_Ok;
+}
+
+
+/* Do an individual subtable lookup. Returns HB_Err_Ok if substitution
+ has been done, or HB_Err_Not_Covered if not. */
+static HB_Error GSUB_Do_Glyph_Lookup( HB_GSUBHeader* gsub,
+ HB_UShort lookup_index,
+ HB_Buffer buffer,
+ HB_UShort context_length,
+ int nesting_level )
+{
+ HB_Error error = HB_Err_Not_Covered;
+ HB_UShort i, flags, lookup_count;
+ HB_Lookup* lo;
+ int lookup_type;
+
+ nesting_level++;
+
+ if ( nesting_level > HB_MAX_NESTING_LEVEL )
+ return ERR(HB_Err_Not_Covered); /* ERR() call intended */
+
+ lookup_count = gsub->LookupList.LookupCount;
+ if (lookup_index >= lookup_count)
+ return error;
+
+ lo = &gsub->LookupList.Lookup[lookup_index];
+ flags = lo->LookupFlag;
+ lookup_type = lo->LookupType;
+
+ for ( i = 0; i < lo->SubTableCount; i++ )
+ {
+ HB_GSUB_SubTable *st = &lo->SubTable[i].st.gsub;
+
+ switch (lookup_type) {
+ case HB_GSUB_LOOKUP_SINGLE:
+ error = Lookup_SingleSubst ( gsub, st, buffer, flags, context_length, nesting_level ); break;
+ case HB_GSUB_LOOKUP_MULTIPLE:
+ error = Lookup_MultipleSubst ( gsub, st, buffer, flags, context_length, nesting_level ); break;
+ case HB_GSUB_LOOKUP_ALTERNATE:
+ error = Lookup_AlternateSubst ( gsub, st, buffer, flags, context_length, nesting_level ); break;
+ case HB_GSUB_LOOKUP_LIGATURE:
+ error = Lookup_LigatureSubst ( gsub, st, buffer, flags, context_length, nesting_level ); break;
+ case HB_GSUB_LOOKUP_CONTEXT:
+ error = Lookup_ContextSubst ( gsub, st, buffer, flags, context_length, nesting_level ); break;
+ case HB_GSUB_LOOKUP_CHAIN:
+ error = Lookup_ChainContextSubst ( gsub, st, buffer, flags, context_length, nesting_level ); break;
+ /*case HB_GSUB_LOOKUP_EXTENSION:
+ error = Lookup_ExtensionSubst ( gsub, st, buffer, flags, context_length, nesting_level ); break;*/
+ case HB_GSUB_LOOKUP_REVERSE_CHAIN:
+ error = Lookup_ReverseChainContextSubst ( gsub, st, buffer, flags, context_length, nesting_level ); break;
+ default:
+ error = HB_Err_Not_Covered;
+ };
+
+ /* Check whether we have a successful substitution or an error other
+ than HB_Err_Not_Covered */
+ if ( error != HB_Err_Not_Covered )
+ return error;
+ }
+
+ return HB_Err_Not_Covered;
+}
+
+
+HB_INTERNAL HB_Error
+_HB_GSUB_Load_SubTable( HB_GSUB_SubTable* st,
+ HB_Stream stream,
+ HB_UShort lookup_type )
+{
+ switch (lookup_type) {
+ case HB_GSUB_LOOKUP_SINGLE: return Load_SingleSubst ( st, stream );
+ case HB_GSUB_LOOKUP_MULTIPLE: return Load_MultipleSubst ( st, stream );
+ case HB_GSUB_LOOKUP_ALTERNATE: return Load_AlternateSubst ( st, stream );
+ case HB_GSUB_LOOKUP_LIGATURE: return Load_LigatureSubst ( st, stream );
+ case HB_GSUB_LOOKUP_CONTEXT: return Load_ContextSubst ( st, stream );
+ case HB_GSUB_LOOKUP_CHAIN: return Load_ChainContextSubst ( st, stream );
+ /*case HB_GSUB_LOOKUP_EXTENSION: return Load_ExtensionSubst ( st, stream );*/
+ case HB_GSUB_LOOKUP_REVERSE_CHAIN: return Load_ReverseChainContextSubst ( st, stream );
+ default: return ERR(HB_Err_Invalid_SubTable_Format);
+ };
+}
+
+
+HB_INTERNAL void
+_HB_GSUB_Free_SubTable( HB_GSUB_SubTable* st,
+ HB_UShort lookup_type )
+{
+ switch ( lookup_type ) {
+ case HB_GSUB_LOOKUP_SINGLE: Free_SingleSubst ( st ); return;
+ case HB_GSUB_LOOKUP_MULTIPLE: Free_MultipleSubst ( st ); return;
+ case HB_GSUB_LOOKUP_ALTERNATE: Free_AlternateSubst ( st ); return;
+ case HB_GSUB_LOOKUP_LIGATURE: Free_LigatureSubst ( st ); return;
+ case HB_GSUB_LOOKUP_CONTEXT: Free_ContextSubst ( st ); return;
+ case HB_GSUB_LOOKUP_CHAIN: Free_ChainContextSubst ( st ); return;
+ /*case HB_GSUB_LOOKUP_EXTENSION: Free_ExtensionSubst ( st ); return;*/
+ case HB_GSUB_LOOKUP_REVERSE_CHAIN: Free_ReverseChainContextSubst ( st ); return;
+ default: return;
+ };
+}
+
+
+
+/* apply one lookup to the input string object */
+
+static HB_Error GSUB_Do_String_Lookup( HB_GSUBHeader* gsub,
+ HB_UShort lookup_index,
+ HB_Buffer buffer )
+{
+ HB_Error error, retError = HB_Err_Not_Covered;
+
+ HB_UInt* properties = gsub->LookupList.Properties;
+ int lookup_type = gsub->LookupList.Lookup[lookup_index].LookupType;
+
+ const int nesting_level = 0;
+ /* 0xFFFF indicates that we don't have a context length yet */
+ const HB_UShort context_length = 0xFFFF;
+
+ switch (lookup_type) {
+
+ case HB_GSUB_LOOKUP_SINGLE:
+ case HB_GSUB_LOOKUP_MULTIPLE:
+ case HB_GSUB_LOOKUP_ALTERNATE:
+ case HB_GSUB_LOOKUP_LIGATURE:
+ case HB_GSUB_LOOKUP_CONTEXT:
+ case HB_GSUB_LOOKUP_CHAIN:
+ /* in/out forward substitution (implemented lazy) */
+
+ _hb_buffer_clear_output ( buffer );
+ buffer->in_pos = 0;
+ while ( buffer->in_pos < buffer->in_length )
+ {
+ if ( ~IN_PROPERTIES( buffer->in_pos ) & properties[lookup_index] )
+ {
+ error = GSUB_Do_Glyph_Lookup( gsub, lookup_index, buffer, context_length, nesting_level );
+ if ( error )
+ {
+ if ( error != HB_Err_Not_Covered )
+ return error;
+ }
+ else
+ retError = error;
+ }
+ else
+ error = HB_Err_Not_Covered;
+
+ if ( error == HB_Err_Not_Covered )
+ if ( COPY_Glyph ( buffer ) )
+ return error;
+ }
+ /* we shouldn't swap if error occurred.
+ *
+ * also don't swap if nothing changed (ie HB_Err_Not_Covered).
+ * shouldn't matter in that case though.
+ */
+ if ( retError == HB_Err_Ok )
+ _hb_buffer_swap( buffer );
+
+ return retError;
+
+ case HB_GSUB_LOOKUP_REVERSE_CHAIN:
+ /* in-place backward substitution */
+
+ buffer->in_pos = buffer->in_length - 1;
+ do
+ {
+ if ( ~IN_PROPERTIES( buffer->in_pos ) & properties[lookup_index] )
+ {
+ error = GSUB_Do_Glyph_Lookup( gsub, lookup_index, buffer, context_length, nesting_level );
+ if ( error )
+ {
+ if ( error != HB_Err_Not_Covered )
+ return error;
+ }
+ else
+ retError = error;
+ }
+ else
+ error = HB_Err_Not_Covered;
+
+ if ( error == HB_Err_Not_Covered )
+ buffer->in_pos--;
+ }
+ while ((HB_Int) buffer->in_pos >= 0);
+
+ return retError;
+
+ /*case HB_GSUB_LOOKUP_EXTENSION:*/
+ default:
+ return retError;
+ };
+}
+
+
+HB_Error HB_GSUB_Add_Feature( HB_GSUBHeader* gsub,
+ HB_UShort feature_index,
+ HB_UInt property )
+{
+ HB_UShort i;
+
+ HB_Feature feature;
+ HB_UInt* properties;
+ HB_UShort* index;
+ HB_UShort lookup_count;
+
+ /* Each feature can only be added once */
+
+ if ( !gsub ||
+ feature_index >= gsub->FeatureList.FeatureCount ||
+ gsub->FeatureList.ApplyCount == gsub->FeatureList.FeatureCount )
+ return ERR(HB_Err_Invalid_Argument);
+
+ gsub->FeatureList.ApplyOrder[gsub->FeatureList.ApplyCount++] = feature_index;
+
+ properties = gsub->LookupList.Properties;
+
+ feature = gsub->FeatureList.FeatureRecord[feature_index].Feature;
+ index = feature.LookupListIndex;
+ lookup_count = gsub->LookupList.LookupCount;
+
+ for ( i = 0; i < feature.LookupListCount; i++ )
+ {
+ HB_UShort lookup_index = index[i];
+ if (lookup_index < lookup_count)
+ properties[lookup_index] |= property;
+ }
+
+ return HB_Err_Ok;
+}
+
+
+
+HB_Error HB_GSUB_Clear_Features( HB_GSUBHeader* gsub )
+{
+ HB_UShort i;
+
+ HB_UInt* properties;
+
+
+ if ( !gsub )
+ return ERR(HB_Err_Invalid_Argument);
+
+ gsub->FeatureList.ApplyCount = 0;
+
+ properties = gsub->LookupList.Properties;
+
+ for ( i = 0; i < gsub->LookupList.LookupCount; i++ )
+ properties[i] = 0;
+
+ return HB_Err_Ok;
+}
+
+
+
+HB_Error HB_GSUB_Register_Alternate_Function( HB_GSUBHeader* gsub,
+ HB_AltFunction altfunc,
+ void* data )
+{
+ if ( !gsub )
+ return ERR(HB_Err_Invalid_Argument);
+
+ gsub->altfunc = altfunc;
+ gsub->data = data;
+
+ return HB_Err_Ok;
+}
+
+/* returns error if one happened, otherwise returns HB_Err_Not_Covered if no
+ * feature were applied, or HB_Err_Ok otherwise.
+ */
+HB_Error HB_GSUB_Apply_String( HB_GSUBHeader* gsub,
+ HB_Buffer buffer )
+{
+ HB_Error error, retError = HB_Err_Not_Covered;
+ int i, j, lookup_count, num_features;
+
+ if ( !gsub ||
+ !buffer)
+ return ERR(HB_Err_Invalid_Argument);
+
+ if ( buffer->in_length == 0 )
+ return retError;
+
+ lookup_count = gsub->LookupList.LookupCount;
+ num_features = gsub->FeatureList.ApplyCount;
+
+ for ( i = 0; i < num_features; i++)
+ {
+ HB_UShort feature_index = gsub->FeatureList.ApplyOrder[i];
+ HB_Feature feature = gsub->FeatureList.FeatureRecord[feature_index].Feature;
+
+ for ( j = 0; j < feature.LookupListCount; j++ )
+ {
+ HB_UShort lookup_index = feature.LookupListIndex[j];
+
+ /* Skip nonexistant lookups */
+ if (lookup_index >= lookup_count)
+ continue;
+
+ error = GSUB_Do_String_Lookup( gsub, lookup_index, buffer );
+ if ( error )
+ {
+ if ( error != HB_Err_Not_Covered )
+ return error;
+ }
+ else
+ retError = error;
+ }
+ }
+
+ error = retError;
+
+ return error;
+}
+
+
+/* END */
diff --git a/src/harfbuzz-gsub.h b/src/harfbuzz-gsub.h
new file mode 100644
index 0000000..b1e78ce
--- /dev/null
+++ b/src/harfbuzz-gsub.h
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 1998-2004 David Turner and Werner Lemberg
+ * Copyright (C) 2006 Behdad Esfahbod
+ *
+ * This is part of HarfBuzz, an OpenType Layout engine 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 HARFBUZZ_GSUB_H
+#define HARFBUZZ_GSUB_H
+
+#include "harfbuzz-gdef.h"
+#include "harfbuzz-buffer.h"
+
+HB_BEGIN_HEADER
+
+
+/* Lookup types for glyph substitution */
+
+#define HB_GSUB_LOOKUP_SINGLE 1
+#define HB_GSUB_LOOKUP_MULTIPLE 2
+#define HB_GSUB_LOOKUP_ALTERNATE 3
+#define HB_GSUB_LOOKUP_LIGATURE 4
+#define HB_GSUB_LOOKUP_CONTEXT 5
+#define HB_GSUB_LOOKUP_CHAIN 6
+#define HB_GSUB_LOOKUP_EXTENSION 7
+#define HB_GSUB_LOOKUP_REVERSE_CHAIN 8
+
+
+/* A pointer to a function which selects the alternate glyph. `pos' is
+ the position of the glyph with index `glyphID', `num_alternates'
+ gives the number of alternates in the `alternates' array. `data'
+ points to the user-defined structure specified during a call to
+ HB_GSUB_Register_Alternate_Function(). The function must return an
+ index into the `alternates' array. */
+
+typedef HB_UShort (*HB_AltFunction)(HB_UInt pos,
+ HB_UShort glyphID,
+ HB_UShort num_alternates,
+ HB_UShort* alternates,
+ void* data );
+
+
+struct HB_GSUBHeader_
+{
+ HB_UInt offset;
+
+ HB_16Dot16 Version;
+
+ HB_ScriptList ScriptList;
+ HB_FeatureList FeatureList;
+ HB_LookupList LookupList;
+
+ hb_ot_layout_t *layout;
+
+ /* the next two fields are used for an alternate substitution callback
+ function to select the proper alternate glyph. */
+
+ HB_AltFunction altfunc;
+ void* data;
+};
+
+typedef struct HB_GSUBHeader_ HB_GSUBHeader;
+typedef HB_GSUBHeader* HB_GSUB;
+
+
+HB_Error HB_Load_GSUB_Table( HB_Font font,
+ HB_GSUBHeader** gsub,
+ hb_ot_layout_t *layout );
+
+
+HB_Error HB_Done_GSUB_Table( HB_GSUBHeader* gsub );
+
+
+HB_Error HB_GSUB_Select_Script( HB_GSUBHeader* gsub,
+ HB_UInt script_tag,
+ HB_UShort* script_index );
+
+HB_Error HB_GSUB_Select_Language( HB_GSUBHeader* gsub,
+ HB_UInt language_tag,
+ HB_UShort script_index,
+ HB_UShort* language_index,
+ HB_UShort* req_feature_index );
+
+HB_Error HB_GSUB_Select_Feature( HB_GSUBHeader* gsub,
+ HB_UInt feature_tag,
+ HB_UShort script_index,
+ HB_UShort language_index,
+ HB_UShort* feature_index );
+
+
+HB_Error HB_GSUB_Query_Scripts( HB_GSUBHeader* gsub,
+ HB_UInt** script_tag_list );
+
+HB_Error HB_GSUB_Query_Languages( HB_GSUBHeader* gsub,
+ HB_UShort script_index,
+ HB_UInt** language_tag_list );
+
+HB_Error HB_GSUB_Query_Features( HB_GSUBHeader* gsub,
+ HB_UShort script_index,
+ HB_UShort language_index,
+ HB_UInt** feature_tag_list );
+
+
+HB_Error HB_GSUB_Add_Feature( HB_GSUBHeader* gsub,
+ HB_UShort feature_index,
+ HB_UInt property );
+
+HB_Error HB_GSUB_Clear_Features( HB_GSUBHeader* gsub );
+
+
+HB_Error HB_GSUB_Register_Alternate_Function( HB_GSUBHeader* gsub,
+ HB_AltFunction altfunc,
+ void* data );
+
+
+HB_Error HB_GSUB_Apply_String( HB_GSUBHeader* gsub,
+ HB_Buffer buffer );
+
+
+HB_END_HEADER
+
+#endif /* HARFBUZZ_GSUB_H */
diff --git a/src/harfbuzz-impl.c b/src/harfbuzz-impl.c
new file mode 100644
index 0000000..ffcef80
--- /dev/null
+++ b/src/harfbuzz-impl.c
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 1998-2004 David Turner and Werner Lemberg
+ * Copyright (C) 2007 Trolltech ASA
+ * Copyright (C) 2007 Red Hat, Inc.
+ *
+ * This is part of HarfBuzz, an OpenType Layout engine library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Red Hat Author(s): Behdad Esfahbod
+ */
+
+#include "harfbuzz-impl.h"
+
+
+HB_INTERNAL HB_Pointer
+_hb_alloc( size_t size,
+ HB_Error *perror )
+{
+ HB_Error error = 0;
+ HB_Pointer block = NULL;
+
+ if ( size > 0 )
+ {
+ block = calloc( 1, size );
+ if ( !block )
+ error = ERR(HB_Err_Out_Of_Memory);
+ }
+
+ *perror = error;
+ return block;
+}
+
+
+HB_INTERNAL HB_Pointer
+_hb_realloc( HB_Pointer block,
+ size_t new_size,
+ HB_Error *perror )
+{
+ HB_Pointer block2 = NULL;
+ HB_Error error = 0;
+
+ block2 = realloc( block, new_size );
+ if ( block2 == NULL && new_size != 0 )
+ error = ERR(HB_Err_Out_Of_Memory);
+
+ if ( !error )
+ block = block2;
+
+ *perror = error;
+ return block;
+}
+
+
+HB_INTERNAL void
+_hb_free( HB_Pointer block )
+{
+ if ( block )
+ free( block );
+}
+
+
+/* helper func to set a breakpoint on */
+HB_INTERNAL HB_Error
+_hb_err (HB_Error code)
+{
+ return code;
+}
diff --git a/src/harfbuzz-impl.h b/src/harfbuzz-impl.h
new file mode 100644
index 0000000..29101f8
--- /dev/null
+++ b/src/harfbuzz-impl.h
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 1998-2004 David Turner and Werner Lemberg
+ * Copyright (C) 2006 Behdad Esfahbod
+ *
+ * This is part of HarfBuzz, an OpenType Layout engine 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 HARFBUZZ_IMPL_H
+#define HARFBUZZ_IMPL_H
+
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include FT_TRUETYPE_TAGS_H
+
+#include "harfbuzz-global.h"
+
+#include <stdlib.h>
+
+/* XXX */
+#include "hb-ot-layout-private.h"
+
+HB_BEGIN_HEADER
+
+#ifndef HB_INTERNAL
+# define HB_INTERNAL
+#endif
+
+#ifndef NULL
+# define NULL ((void *)0)
+#endif
+
+#ifndef FALSE
+# define FALSE 0
+#endif
+
+#ifndef TRUE
+# define TRUE 1
+#endif
+
+#ifndef TTAG_GDEF
+# define TTAG_GDEF HB_MAKE_TAG( 'G', 'D', 'E', 'F' )
+#endif
+#ifndef TTAG_GPOS
+# define TTAG_GPOS HB_MAKE_TAG( 'G', 'P', 'O', 'S' )
+#endif
+#ifndef TTAG_GSUB
+# define TTAG_GSUB HB_MAKE_TAG( 'G', 'S', 'U', 'B' )
+#endif
+
+#ifndef HB_UNUSED
+# define HB_UNUSED(arg) ((arg) = (arg))
+#endif
+
+#ifndef HB_LIKELY
+# define HB_LIKELY(cond) (cond)
+#endif
+#ifndef HB_UNLIKELY
+# define HB_UNLIKELY(cond) (cond)
+#endif
+
+
+#define ALLOC(_ptr,_size) \
+ ( (_ptr) = _hb_alloc( _size, &error ), error != 0 )
+
+#define REALLOC(_ptr,_newsz) \
+ ( (_ptr) = _hb_realloc( (_ptr), (_newsz), &error ), error != 0 )
+
+#define FREE(_ptr) \
+ do { \
+ if ( (_ptr) ) \
+ { \
+ _hb_free( _ptr ); \
+ _ptr = NULL; \
+ } \
+ } while (0)
+
+#define ALLOC_ARRAY(_ptr,_count,_type) \
+ ALLOC(_ptr,(_count)*sizeof(_type))
+
+#define REALLOC_ARRAY(_ptr,_newcnt,_type) \
+ REALLOC(_ptr,(_newcnt)*sizeof(_type))
+
+#define MEM_Copy(dest,source,count) memcpy( (char*)(dest), (const char*)(source), (size_t)(count) )
+
+#define ERR(err) _hb_err (err)
+
+
+HB_INTERNAL HB_Pointer
+_hb_alloc( size_t size,
+ HB_Error *perror_ );
+
+HB_INTERNAL HB_Pointer
+_hb_realloc( HB_Pointer block,
+ size_t new_size,
+ HB_Error *perror_ );
+
+HB_INTERNAL void
+_hb_free( HB_Pointer block );
+
+
+/* helper func to set a breakpoint on */
+HB_INTERNAL HB_Error
+_hb_err (HB_Error code);
+
+
+HB_END_HEADER
+
+#endif /* HARFBUZZ_IMPL_H */
diff --git a/src/harfbuzz-open-private.h b/src/harfbuzz-open-private.h
new file mode 100644
index 0000000..e14753d
--- /dev/null
+++ b/src/harfbuzz-open-private.h
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 1998-2004 David Turner and Werner Lemberg
+ * Copyright (C) 2006 Behdad Esfahbod
+ *
+ * This is part of HarfBuzz, an OpenType Layout engine 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 HARFBUZZ_OPEN_PRIVATE_H
+#define HARFBUZZ_OPEN_PRIVATE_H
+
+#include "harfbuzz-impl.h"
+#include "harfbuzz-open.h"
+#include "harfbuzz-gsub-private.h"
+#include "harfbuzz-gpos-private.h"
+
+HB_BEGIN_HEADER
+
+
+struct HB_SubTable_
+{
+ union
+ {
+ HB_GSUB_SubTable gsub;
+ HB_GPOS_SubTable gpos;
+ } st;
+};
+
+
+HB_INTERNAL HB_Error
+_HB_OPEN_Load_ScriptList( HB_ScriptList* sl,
+ HB_Stream input );
+HB_INTERNAL HB_Error
+_HB_OPEN_Load_FeatureList( HB_FeatureList* fl,
+ HB_Stream input );
+HB_INTERNAL HB_Error
+_HB_OPEN_Load_LookupList( HB_LookupList* ll,
+ HB_Stream input,
+ HB_Type type );
+
+HB_INTERNAL HB_Error
+_HB_OPEN_Load_Coverage( HB_Coverage* c,
+ HB_Stream input );
+HB_INTERNAL HB_Error
+_HB_OPEN_Load_ClassDefinition( HB_ClassDefinition* cd,
+ HB_UShort limit,
+ HB_Stream input );
+HB_INTERNAL HB_Error
+_HB_OPEN_Load_EmptyOrClassDefinition( HB_ClassDefinition* cd,
+ HB_UShort limit,
+ HB_UInt class_offset,
+ HB_UInt base_offset,
+ HB_Stream input );
+HB_INTERNAL HB_Error
+_HB_OPEN_Load_Device( HB_Device* d,
+ HB_Stream input );
+
+HB_INTERNAL void _HB_OPEN_Free_ScriptList( HB_ScriptList* sl );
+HB_INTERNAL void _HB_OPEN_Free_FeatureList( HB_FeatureList* fl );
+HB_INTERNAL void _HB_OPEN_Free_LookupList( HB_LookupList* ll,
+ HB_Type type );
+
+HB_INTERNAL void _HB_OPEN_Free_Coverage( HB_Coverage* c );
+HB_INTERNAL void _HB_OPEN_Free_ClassDefinition( HB_ClassDefinition* cd );
+HB_INTERNAL void _HB_OPEN_Free_Device( HB_Device* d );
+
+
+
+HB_INTERNAL HB_Error
+_HB_OPEN_Coverage_Index( HB_Coverage* c,
+ HB_UShort glyphID,
+ HB_UShort* index );
+HB_INTERNAL HB_Error
+_HB_OPEN_Get_Class( HB_ClassDefinition* cd,
+ HB_UShort glyphID,
+ HB_UShort* klass,
+ HB_UShort* index );
+HB_INTERNAL HB_Error
+_HB_OPEN_Get_Device( HB_Device* d,
+ HB_UShort size,
+ HB_Short* value );
+
+HB_END_HEADER
+
+#endif /* HARFBUZZ_OPEN_PRIVATE_H */
diff --git a/src/harfbuzz-open.c b/src/harfbuzz-open.c
new file mode 100644
index 0000000..68b27d4
--- /dev/null
+++ b/src/harfbuzz-open.c
@@ -0,0 +1,1405 @@
+/*
+ * Copyright (C) 1998-2004 David Turner and Werner Lemberg
+ * Copyright (C) 2006 Behdad Esfahbod
+ *
+ * This is part of HarfBuzz, an OpenType Layout engine 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 "harfbuzz-impl.h"
+#include "harfbuzz-open-private.h"
+
+
+/***************************
+ * Script related functions
+ ***************************/
+
+
+/* LangSys */
+
+static HB_Error Load_LangSys( HB_LangSys* ls,
+ HB_Stream stream )
+{
+ HB_Error error;
+ HB_UShort n, count;
+ HB_UShort* fi;
+
+
+ if ( ACCESS_Frame( 6L ) )
+ return error;
+
+ ls->LookupOrderOffset = GET_UShort(); /* should be 0 */
+ ls->ReqFeatureIndex = GET_UShort();
+ count = ls->FeatureCount = GET_UShort();
+
+ FORGET_Frame();
+
+ ls->FeatureIndex = NULL;
+
+ if ( ALLOC_ARRAY( ls->FeatureIndex, count, HB_UShort ) )
+ return error;
+
+ if ( ACCESS_Frame( count * 2L ) )
+ {
+ FREE( ls->FeatureIndex );
+ return error;
+ }
+
+ fi = ls->FeatureIndex;
+
+ for ( n = 0; n < count; n++ )
+ fi[n] = GET_UShort();
+
+ FORGET_Frame();
+
+ return HB_Err_Ok;
+}
+
+
+static void Free_LangSys( HB_LangSys* ls )
+{
+ FREE( ls->FeatureIndex );
+}
+
+
+/* Script */
+
+static HB_Error Load_Script( HB_ScriptTable* s,
+ HB_Stream stream )
+{
+ HB_Error error;
+ HB_UShort n, m, count;
+ HB_UInt cur_offset, new_offset, base_offset;
+
+ HB_LangSysRecord* lsr;
+
+
+ base_offset = FILE_Pos();
+
+ if ( ACCESS_Frame( 2L ) )
+ return error;
+
+ new_offset = GET_UShort() + base_offset;
+
+ FORGET_Frame();
+
+ if ( new_offset != base_offset ) /* not a NULL offset */
+ {
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = Load_LangSys( &s->DefaultLangSys,
+ stream ) ) != HB_Err_Ok )
+ return error;
+ (void)FILE_Seek( cur_offset );
+ }
+ else
+ {
+ /* we create a DefaultLangSys table with no entries */
+
+ s->DefaultLangSys.LookupOrderOffset = 0;
+ s->DefaultLangSys.ReqFeatureIndex = 0xFFFF;
+ s->DefaultLangSys.FeatureCount = 0;
+ s->DefaultLangSys.FeatureIndex = NULL;
+ }
+
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail2;
+
+ count = s->LangSysCount = GET_UShort();
+
+ FORGET_Frame();
+
+ s->LangSysRecord = NULL;
+
+ if ( ALLOC_ARRAY( s->LangSysRecord, count, HB_LangSysRecord ) )
+ goto Fail2;
+
+ lsr = s->LangSysRecord;
+
+ for ( n = 0; n < count; n++ )
+ {
+ if ( ACCESS_Frame( 6L ) )
+ goto Fail1;
+
+ lsr[n].LangSysTag = GET_ULong();
+ new_offset = GET_UShort() + base_offset;
+
+ FORGET_Frame();
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = Load_LangSys( &lsr[n].LangSys, stream ) ) != HB_Err_Ok )
+ goto Fail1;
+ (void)FILE_Seek( cur_offset );
+ }
+
+ return HB_Err_Ok;
+
+Fail1:
+ for ( m = 0; m < n; m++ )
+ Free_LangSys( &lsr[m].LangSys );
+
+ FREE( s->LangSysRecord );
+
+Fail2:
+ Free_LangSys( &s->DefaultLangSys );
+ return error;
+}
+
+
+static void Free_Script( HB_ScriptTable* s )
+{
+ HB_UShort n, count;
+
+ HB_LangSysRecord* lsr;
+
+
+ Free_LangSys( &s->DefaultLangSys );
+
+ if ( s->LangSysRecord )
+ {
+ count = s->LangSysCount;
+ lsr = s->LangSysRecord;
+
+ for ( n = 0; n < count; n++ )
+ Free_LangSys( &lsr[n].LangSys );
+
+ FREE( lsr );
+ }
+}
+
+
+/* ScriptList */
+
+HB_INTERNAL HB_Error
+_HB_OPEN_Load_ScriptList( HB_ScriptList* sl,
+ HB_Stream stream )
+{
+ HB_Error error;
+
+ HB_UShort n, script_count;
+ HB_UInt cur_offset, new_offset, base_offset;
+
+ HB_ScriptRecord* sr;
+
+
+ base_offset = FILE_Pos();
+
+ if ( ACCESS_Frame( 2L ) )
+ return error;
+
+ script_count = GET_UShort();
+
+ FORGET_Frame();
+
+ sl->ScriptRecord = NULL;
+
+ if ( ALLOC_ARRAY( sl->ScriptRecord, script_count, HB_ScriptRecord ) )
+ return error;
+
+ sr = sl->ScriptRecord;
+
+ sl->ScriptCount= 0;
+ for ( n = 0; n < script_count; n++ )
+ {
+ if ( ACCESS_Frame( 6L ) )
+ goto Fail;
+
+ sr[sl->ScriptCount].ScriptTag = GET_ULong();
+ new_offset = GET_UShort() + base_offset;
+
+ FORGET_Frame();
+
+ cur_offset = FILE_Pos();
+
+ if ( FILE_Seek( new_offset ) )
+ goto Fail;
+
+ error = Load_Script( &sr[sl->ScriptCount].Script, stream );
+ if ( error == HB_Err_Ok )
+ sl->ScriptCount += 1;
+ else if ( error != HB_Err_Not_Covered )
+ goto Fail;
+
+ (void)FILE_Seek( cur_offset );
+ }
+
+ /* Empty tables are harmless and generated by fontforge.
+ * See http://bugzilla.gnome.org/show_bug.cgi?id=347073
+ */
+#if 0
+ if ( sl->ScriptCount == 0 )
+ {
+ error = ERR(HB_Err_Invalid_SubTable);
+ goto Fail;
+ }
+#endif
+
+ return HB_Err_Ok;
+
+Fail:
+ for ( n = 0; n < sl->ScriptCount; n++ )
+ Free_Script( &sr[n].Script );
+
+ FREE( sl->ScriptRecord );
+ return error;
+}
+
+
+HB_INTERNAL void
+_HB_OPEN_Free_ScriptList( HB_ScriptList* sl )
+{
+ HB_UShort n, count;
+
+ HB_ScriptRecord* sr;
+
+
+ if ( sl->ScriptRecord )
+ {
+ count = sl->ScriptCount;
+ sr = sl->ScriptRecord;
+
+ for ( n = 0; n < count; n++ )
+ Free_Script( &sr[n].Script );
+
+ FREE( sr );
+ }
+}
+
+
+
+/*********************************
+ * Feature List related functions
+ *********************************/
+
+
+/* Feature */
+
+static HB_Error Load_Feature( HB_Feature* f,
+ HB_Stream stream )
+{
+ HB_Error error;
+
+ HB_UShort n, count;
+
+ HB_UShort* lli;
+
+
+ if ( ACCESS_Frame( 4L ) )
+ return error;
+
+ f->FeatureParams = GET_UShort(); /* should be 0 */
+ count = f->LookupListCount = GET_UShort();
+
+ FORGET_Frame();
+
+ f->LookupListIndex = NULL;
+
+ if ( ALLOC_ARRAY( f->LookupListIndex, count, HB_UShort ) )
+ return error;
+
+ lli = f->LookupListIndex;
+
+ if ( ACCESS_Frame( count * 2L ) )
+ {
+ FREE( f->LookupListIndex );
+ return error;
+ }
+
+ for ( n = 0; n < count; n++ )
+ lli[n] = GET_UShort();
+
+ FORGET_Frame();
+
+ return HB_Err_Ok;
+}
+
+
+static void Free_Feature( HB_Feature* f )
+{
+ FREE( f->LookupListIndex );
+}
+
+
+/* FeatureList */
+
+HB_INTERNAL HB_Error
+_HB_OPEN_Load_FeatureList( HB_FeatureList* fl,
+ HB_Stream stream )
+{
+ HB_Error error;
+
+ HB_UShort n, m, count;
+ HB_UInt cur_offset, new_offset, base_offset;
+
+ HB_FeatureRecord* fr;
+
+
+ base_offset = FILE_Pos();
+
+ if ( ACCESS_Frame( 2L ) )
+ return error;
+
+ count = fl->FeatureCount = GET_UShort();
+
+ FORGET_Frame();
+
+ fl->FeatureRecord = NULL;
+
+ if ( ALLOC_ARRAY( fl->FeatureRecord, count, HB_FeatureRecord ) )
+ return error;
+ if ( ALLOC_ARRAY( fl->ApplyOrder, count, HB_UShort ) )
+ goto Fail2;
+
+ fl->ApplyCount = 0;
+
+ fr = fl->FeatureRecord;
+
+ for ( n = 0; n < count; n++ )
+ {
+ if ( ACCESS_Frame( 6L ) )
+ goto Fail1;
+
+ fr[n].FeatureTag = GET_ULong();
+ new_offset = GET_UShort() + base_offset;
+
+ FORGET_Frame();
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = Load_Feature( &fr[n].Feature, stream ) ) != HB_Err_Ok )
+ goto Fail1;
+ (void)FILE_Seek( cur_offset );
+ }
+
+ return HB_Err_Ok;
+
+Fail1:
+ for ( m = 0; m < n; m++ )
+ Free_Feature( &fr[m].Feature );
+
+ FREE( fl->ApplyOrder );
+
+Fail2:
+ FREE( fl->FeatureRecord );
+
+ return error;
+}
+
+
+HB_INTERNAL void
+_HB_OPEN_Free_FeatureList( HB_FeatureList* fl )
+{
+ HB_UShort n, count;
+
+ HB_FeatureRecord* fr;
+
+
+ if ( fl->FeatureRecord )
+ {
+ count = fl->FeatureCount;
+ fr = fl->FeatureRecord;
+
+ for ( n = 0; n < count; n++ )
+ Free_Feature( &fr[n].Feature );
+
+ FREE( fr );
+ }
+
+ FREE( fl->ApplyOrder );
+}
+
+
+
+/********************************
+ * Lookup List related functions
+ ********************************/
+
+/* the subroutines of the following two functions are defined in
+ ftxgsub.c and ftxgpos.c respectively */
+
+
+/* SubTable */
+
+static HB_Error Load_SubTable( HB_SubTable* st,
+ HB_Stream stream,
+ HB_Type table_type,
+ HB_UShort lookup_type )
+{
+ if ( table_type == HB_Type_GSUB )
+ return _HB_GSUB_Load_SubTable ( &st->st.gsub, stream, lookup_type );
+ else
+ return _HB_GPOS_Load_SubTable ( &st->st.gpos, stream, lookup_type );
+}
+
+
+static void Free_SubTable( HB_SubTable* st,
+ HB_Type table_type,
+ HB_UShort lookup_type )
+{
+ if ( table_type == HB_Type_GSUB )
+ _HB_GSUB_Free_SubTable ( &st->st.gsub, lookup_type );
+ else
+ _HB_GPOS_Free_SubTable ( &st->st.gpos, lookup_type );
+}
+
+
+/* Lookup */
+
+static HB_Error Load_Lookup( HB_Lookup* l,
+ HB_Stream stream,
+ HB_Type type )
+{
+ HB_Error error;
+
+ HB_UShort n, m, count;
+ HB_UInt cur_offset, new_offset, base_offset;
+
+ HB_SubTable* st;
+
+ HB_Bool is_extension = FALSE;
+
+
+ base_offset = FILE_Pos();
+
+ if ( ACCESS_Frame( 6L ) )
+ return error;
+
+ l->LookupType = GET_UShort();
+ l->LookupFlag = GET_UShort();
+ count = l->SubTableCount = GET_UShort();
+
+ FORGET_Frame();
+
+ l->SubTable = NULL;
+
+ if ( ALLOC_ARRAY( l->SubTable, count, HB_SubTable ) )
+ return error;
+
+ st = l->SubTable;
+
+ if ( ( type == HB_Type_GSUB && l->LookupType == HB_GSUB_LOOKUP_EXTENSION ) ||
+ ( type == HB_Type_GPOS && l->LookupType == HB_GPOS_LOOKUP_EXTENSION ) )
+ is_extension = TRUE;
+
+ for ( n = 0; n < count; n++ )
+ {
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail;
+
+ new_offset = GET_UShort() + base_offset;
+
+ FORGET_Frame();
+
+ cur_offset = FILE_Pos();
+
+ if ( is_extension )
+ {
+ if ( FILE_Seek( new_offset ) || ACCESS_Frame( 8L ) )
+ goto Fail;
+
+ if (GET_UShort() != 1) /* format should be 1 */
+ goto Fail;
+
+ l->LookupType = GET_UShort();
+ new_offset += GET_ULong();
+
+ FORGET_Frame();
+ }
+
+ if ( FILE_Seek( new_offset ) ||
+ ( error = Load_SubTable( &st[n], stream,
+ type, l->LookupType ) ) != HB_Err_Ok )
+ goto Fail;
+ (void)FILE_Seek( cur_offset );
+ }
+
+ return HB_Err_Ok;
+
+Fail:
+ for ( m = 0; m < n; m++ )
+ Free_SubTable( &st[m], type, l->LookupType );
+
+ FREE( l->SubTable );
+ return error;
+}
+
+
+static void Free_Lookup( HB_Lookup* l,
+ HB_Type type )
+{
+ HB_UShort n, count;
+
+ HB_SubTable* st;
+
+
+ if ( l->SubTable )
+ {
+ count = l->SubTableCount;
+ st = l->SubTable;
+
+ for ( n = 0; n < count; n++ )
+ Free_SubTable( &st[n], type, l->LookupType );
+
+ FREE( st );
+ }
+}
+
+
+/* LookupList */
+
+HB_INTERNAL HB_Error
+_HB_OPEN_Load_LookupList( HB_LookupList* ll,
+ HB_Stream stream,
+ HB_Type type )
+{
+ HB_Error error;
+
+ HB_UShort n, m, count;
+ HB_UInt cur_offset, new_offset, base_offset;
+
+ HB_Lookup* l;
+
+
+ base_offset = FILE_Pos();
+
+ if ( ACCESS_Frame( 2L ) )
+ return error;
+
+ count = ll->LookupCount = GET_UShort();
+
+ FORGET_Frame();
+
+ ll->Lookup = NULL;
+
+ if ( ALLOC_ARRAY( ll->Lookup, count, HB_Lookup ) )
+ return error;
+ if ( ALLOC_ARRAY( ll->Properties, count, HB_UInt ) )
+ goto Fail2;
+
+ l = ll->Lookup;
+
+ for ( n = 0; n < count; n++ )
+ {
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail1;
+
+ new_offset = GET_UShort() + base_offset;
+
+ FORGET_Frame();
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = Load_Lookup( &l[n], stream, type ) ) != HB_Err_Ok )
+ goto Fail1;
+ (void)FILE_Seek( cur_offset );
+ }
+
+ return HB_Err_Ok;
+
+Fail1:
+ FREE( ll->Properties );
+
+ for ( m = 0; m < n; m++ )
+ Free_Lookup( &l[m], type );
+
+Fail2:
+ FREE( ll->Lookup );
+ return error;
+}
+
+
+HB_INTERNAL void
+_HB_OPEN_Free_LookupList( HB_LookupList* ll,
+ HB_Type type )
+{
+ HB_UShort n, count;
+
+ HB_Lookup* l;
+
+
+ FREE( ll->Properties );
+
+ if ( ll->Lookup )
+ {
+ count = ll->LookupCount;
+ l = ll->Lookup;
+
+ for ( n = 0; n < count; n++ )
+ Free_Lookup( &l[n], type );
+
+ FREE( l );
+ }
+}
+
+
+
+/*****************************
+ * Coverage related functions
+ *****************************/
+
+
+/* CoverageFormat1 */
+
+static HB_Error Load_Coverage1( HB_CoverageFormat1* cf1,
+ HB_Stream stream )
+{
+ HB_Error error;
+
+ HB_UShort n, count;
+
+ HB_UShort* ga;
+
+
+ if ( ACCESS_Frame( 2L ) )
+ return error;
+
+ count = cf1->GlyphCount = GET_UShort();
+
+ FORGET_Frame();
+
+ cf1->GlyphArray = NULL;
+
+ if ( ALLOC_ARRAY( cf1->GlyphArray, count, HB_UShort ) )
+ return error;
+
+ ga = cf1->GlyphArray;
+
+ if ( ACCESS_Frame( count * 2L ) )
+ {
+ FREE( cf1->GlyphArray );
+ return error;
+ }
+
+ for ( n = 0; n < count; n++ )
+ ga[n] = GET_UShort();
+
+ FORGET_Frame();
+
+ return HB_Err_Ok;
+}
+
+
+static void Free_Coverage1( HB_CoverageFormat1* cf1 )
+{
+ FREE( cf1->GlyphArray );
+}
+
+
+/* CoverageFormat2 */
+
+static HB_Error Load_Coverage2( HB_CoverageFormat2* cf2,
+ HB_Stream stream )
+{
+ HB_Error error;
+
+ HB_UShort n, count;
+
+ HB_RangeRecord* rr;
+
+
+ if ( ACCESS_Frame( 2L ) )
+ return error;
+
+ count = cf2->RangeCount = GET_UShort();
+
+ FORGET_Frame();
+
+ cf2->RangeRecord = NULL;
+
+ if ( ALLOC_ARRAY( cf2->RangeRecord, count, HB_RangeRecord ) )
+ return error;
+
+ rr = cf2->RangeRecord;
+
+ if ( ACCESS_Frame( count * 6L ) )
+ goto Fail;
+
+ for ( n = 0; n < count; n++ )
+ {
+ rr[n].Start = GET_UShort();
+ rr[n].End = GET_UShort();
+ rr[n].StartCoverageIndex = GET_UShort();
+
+ /* sanity check; we are limited to 16bit integers */
+ if ( rr[n].Start > rr[n].End ||
+ ( rr[n].End - rr[n].Start + (long)rr[n].StartCoverageIndex ) >=
+ 0x10000L )
+ {
+ error = ERR(HB_Err_Invalid_SubTable);
+ goto Fail;
+ }
+ }
+
+ FORGET_Frame();
+
+ return HB_Err_Ok;
+
+Fail:
+ FREE( cf2->RangeRecord );
+ return error;
+}
+
+
+static void Free_Coverage2( HB_CoverageFormat2* cf2 )
+{
+ FREE( cf2->RangeRecord );
+}
+
+
+HB_INTERNAL HB_Error
+_HB_OPEN_Load_Coverage( HB_Coverage* c,
+ HB_Stream stream )
+{
+ HB_Error error;
+
+ if ( ACCESS_Frame( 2L ) )
+ return error;
+
+ c->CoverageFormat = GET_UShort();
+
+ FORGET_Frame();
+
+ switch ( c->CoverageFormat )
+ {
+ case 1: return Load_Coverage1( &c->cf.cf1, stream );
+ case 2: return Load_Coverage2( &c->cf.cf2, stream );
+ default: return ERR(HB_Err_Invalid_SubTable_Format);
+ }
+
+ return HB_Err_Ok; /* never reached */
+}
+
+
+HB_INTERNAL void
+_HB_OPEN_Free_Coverage( HB_Coverage* c )
+{
+ switch ( c->CoverageFormat )
+ {
+ case 1: Free_Coverage1( &c->cf.cf1 ); break;
+ case 2: Free_Coverage2( &c->cf.cf2 ); break;
+ default: break;
+ }
+}
+
+
+static HB_Error Coverage_Index1( HB_CoverageFormat1* cf1,
+ HB_UShort glyphID,
+ HB_UShort* index )
+{
+ HB_UShort min, max, new_min, new_max, middle;
+
+ HB_UShort* array = cf1->GlyphArray;
+
+
+ /* binary search */
+
+ if ( cf1->GlyphCount == 0 )
+ return HB_Err_Not_Covered;
+
+ new_min = 0;
+ new_max = cf1->GlyphCount - 1;
+
+ do
+ {
+ min = new_min;
+ max = new_max;
+
+ /* we use (min + max) / 2 = max - (max - min) / 2 to avoid
+ overflow and rounding errors */
+
+ middle = max - ( ( max - min ) >> 1 );
+
+ if ( glyphID == array[middle] )
+ {
+ *index = middle;
+ return HB_Err_Ok;
+ }
+ else if ( glyphID < array[middle] )
+ {
+ if ( middle == min )
+ break;
+ new_max = middle - 1;
+ }
+ else
+ {
+ if ( middle == max )
+ break;
+ new_min = middle + 1;
+ }
+ } while ( min < max );
+
+ return HB_Err_Not_Covered;
+}
+
+
+static HB_Error Coverage_Index2( HB_CoverageFormat2* cf2,
+ HB_UShort glyphID,
+ HB_UShort* index )
+{
+ HB_UShort min, max, new_min, new_max, middle;
+
+ HB_RangeRecord* rr = cf2->RangeRecord;
+
+
+ /* binary search */
+
+ if ( cf2->RangeCount == 0 )
+ return HB_Err_Not_Covered;
+
+ new_min = 0;
+ new_max = cf2->RangeCount - 1;
+
+ do
+ {
+ min = new_min;
+ max = new_max;
+
+ /* we use (min + max) / 2 = max - (max - min) / 2 to avoid
+ overflow and rounding errors */
+
+ middle = max - ( ( max - min ) >> 1 );
+
+ if ( glyphID >= rr[middle].Start && glyphID <= rr[middle].End )
+ {
+ *index = rr[middle].StartCoverageIndex + glyphID - rr[middle].Start;
+ return HB_Err_Ok;
+ }
+ else if ( glyphID < rr[middle].Start )
+ {
+ if ( middle == min )
+ break;
+ new_max = middle - 1;
+ }
+ else
+ {
+ if ( middle == max )
+ break;
+ new_min = middle + 1;
+ }
+ } while ( min < max );
+
+ return HB_Err_Not_Covered;
+}
+
+
+HB_INTERNAL HB_Error
+_HB_OPEN_Coverage_Index( HB_Coverage* c,
+ HB_UShort glyphID,
+ HB_UShort* index )
+{
+ switch ( c->CoverageFormat )
+ {
+ case 1: return Coverage_Index1( &c->cf.cf1, glyphID, index );
+ case 2: return Coverage_Index2( &c->cf.cf2, glyphID, index );
+ default: return ERR(HB_Err_Invalid_SubTable_Format);
+ }
+
+ return HB_Err_Ok; /* never reached */
+}
+
+
+
+/*************************************
+ * Class Definition related functions
+ *************************************/
+
+
+/* ClassDefFormat1 */
+
+static HB_Error Load_ClassDef1( HB_ClassDefinition* cd,
+ HB_UShort limit,
+ HB_Stream stream )
+{
+ HB_Error error;
+
+ HB_UShort n, count;
+
+ HB_UShort* cva;
+
+ HB_ClassDefFormat1* cdf1;
+
+
+ cdf1 = &cd->cd.cd1;
+
+ if ( ACCESS_Frame( 4L ) )
+ return error;
+
+ cdf1->StartGlyph = GET_UShort();
+ count = cdf1->GlyphCount = GET_UShort();
+
+ FORGET_Frame();
+
+ /* sanity check; we are limited to 16bit integers */
+
+ if ( cdf1->StartGlyph + (long)count >= 0x10000L )
+ return ERR(HB_Err_Invalid_SubTable);
+
+ cdf1->ClassValueArray = NULL;
+
+ if ( ALLOC_ARRAY( cdf1->ClassValueArray, count, HB_UShort ) )
+ return error;
+
+ cva = cdf1->ClassValueArray;
+
+ if ( ACCESS_Frame( count * 2L ) )
+ goto Fail;
+
+ for ( n = 0; n < count; n++ )
+ {
+ cva[n] = GET_UShort();
+ if ( cva[n] >= limit )
+ {
+ error = ERR(HB_Err_Invalid_SubTable);
+ goto Fail;
+ }
+ }
+
+ FORGET_Frame();
+
+ return HB_Err_Ok;
+
+Fail:
+ FREE( cva );
+
+ return error;
+}
+
+
+static void Free_ClassDef1( HB_ClassDefFormat1* cdf1 )
+{
+ FREE( cdf1->ClassValueArray );
+}
+
+
+/* ClassDefFormat2 */
+
+static HB_Error Load_ClassDef2( HB_ClassDefinition* cd,
+ HB_UShort limit,
+ HB_Stream stream )
+{
+ HB_Error error;
+
+ HB_UShort n, count;
+
+ HB_ClassRangeRecord* crr;
+
+ HB_ClassDefFormat2* cdf2;
+
+
+ cdf2 = &cd->cd.cd2;
+
+ if ( ACCESS_Frame( 2L ) )
+ return error;
+
+ count = GET_UShort();
+ cdf2->ClassRangeCount = 0; /* zero for now. we fill with the number of good entries later */
+
+ FORGET_Frame();
+
+ cdf2->ClassRangeRecord = NULL;
+
+ if ( ALLOC_ARRAY( cdf2->ClassRangeRecord, count, HB_ClassRangeRecord ) )
+ return error;
+
+ crr = cdf2->ClassRangeRecord;
+
+ if ( ACCESS_Frame( count * 6L ) )
+ goto Fail;
+
+ for ( n = 0; n < count; n++ )
+ {
+ crr[n].Start = GET_UShort();
+ crr[n].End = GET_UShort();
+ crr[n].Class = GET_UShort();
+
+ /* sanity check */
+
+ if ( crr[n].Start > crr[n].End ||
+ crr[n].Class >= limit )
+ {
+ /* XXX
+ * Corrupt entry. Skip it.
+ * This is hit by Nafees Nastaliq font for example
+ */
+ n--;
+ count--;
+ }
+ }
+
+ FORGET_Frame();
+
+ cdf2->ClassRangeCount = count;
+
+ return HB_Err_Ok;
+
+Fail:
+ FREE( crr );
+
+ return error;
+}
+
+
+static void Free_ClassDef2( HB_ClassDefFormat2* cdf2 )
+{
+ FREE( cdf2->ClassRangeRecord );
+}
+
+
+/* ClassDefinition */
+
+HB_INTERNAL HB_Error
+_HB_OPEN_Load_ClassDefinition( HB_ClassDefinition* cd,
+ HB_UShort limit,
+ HB_Stream stream )
+{
+ HB_Error error;
+
+ if ( ACCESS_Frame( 2L ) )
+ return error;
+
+ cd->ClassFormat = GET_UShort();
+
+ FORGET_Frame();
+
+ switch ( cd->ClassFormat )
+ {
+ case 1: error = Load_ClassDef1( cd, limit, stream ); break;
+ case 2: error = Load_ClassDef2( cd, limit, stream ); break;
+ default: error = ERR(HB_Err_Invalid_SubTable_Format); break;
+ }
+
+ if ( error )
+ return error;
+
+ cd->loaded = TRUE;
+
+ return HB_Err_Ok;
+}
+
+
+static HB_Error
+_HB_OPEN_Load_EmptyClassDefinition( HB_ClassDefinition* cd )
+{
+ HB_Error error;
+
+ cd->ClassFormat = 1; /* Meaningless */
+
+ if ( ALLOC_ARRAY( cd->cd.cd1.ClassValueArray, 1, HB_UShort ) )
+ return error;
+
+ return HB_Err_Ok;
+}
+
+HB_INTERNAL HB_Error
+_HB_OPEN_Load_EmptyOrClassDefinition( HB_ClassDefinition* cd,
+ HB_UShort limit,
+ HB_UInt class_offset,
+ HB_UInt base_offset,
+ HB_Stream stream )
+{
+ HB_Error error;
+ HB_UInt cur_offset;
+
+ cur_offset = FILE_Pos();
+
+ if ( class_offset )
+ {
+ if ( !FILE_Seek( class_offset + base_offset ) )
+ error = _HB_OPEN_Load_ClassDefinition( cd, limit, stream );
+ }
+ else
+ error = _HB_OPEN_Load_EmptyClassDefinition ( cd );
+
+ if (error == HB_Err_Ok)
+ (void)FILE_Seek( cur_offset ); /* Changes error as a side-effect */
+
+ return error;
+}
+
+HB_INTERNAL void
+_HB_OPEN_Free_ClassDefinition( HB_ClassDefinition* cd )
+{
+ if ( !cd->loaded )
+ return;
+
+ switch ( cd->ClassFormat )
+ {
+ case 1: Free_ClassDef1( &cd->cd.cd1 ); break;
+ case 2: Free_ClassDef2( &cd->cd.cd2 ); break;
+ default: break;
+ }
+}
+
+
+static HB_Error Get_Class1( HB_ClassDefFormat1* cdf1,
+ HB_UShort glyphID,
+ HB_UShort* klass,
+ HB_UShort* index )
+{
+ HB_UShort* cva = cdf1->ClassValueArray;
+
+
+ if ( index )
+ *index = 0;
+
+ if ( glyphID >= cdf1->StartGlyph &&
+ glyphID < cdf1->StartGlyph + cdf1->GlyphCount )
+ {
+ *klass = cva[glyphID - cdf1->StartGlyph];
+ return HB_Err_Ok;
+ }
+ else
+ {
+ *klass = 0;
+ return HB_Err_Not_Covered;
+ }
+}
+
+
+/* we need the index value of the last searched class range record
+ in case of failure for constructed GDEF tables */
+
+static HB_Error Get_Class2( HB_ClassDefFormat2* cdf2,
+ HB_UShort glyphID,
+ HB_UShort* klass,
+ HB_UShort* index )
+{
+ HB_Error error = HB_Err_Ok;
+ HB_UShort min, max, new_min, new_max, middle;
+
+ HB_ClassRangeRecord* crr = cdf2->ClassRangeRecord;
+
+
+ /* binary search */
+
+ if ( cdf2->ClassRangeCount == 0 )
+ {
+ *klass = 0;
+ if ( index )
+ *index = 0;
+
+ return HB_Err_Not_Covered;
+ }
+
+ new_min = 0;
+ new_max = cdf2->ClassRangeCount - 1;
+
+ do
+ {
+ min = new_min;
+ max = new_max;
+
+ /* we use (min + max) / 2 = max - (max - min) / 2 to avoid
+ overflow and rounding errors */
+
+ middle = max - ( ( max - min ) >> 1 );
+
+ if ( glyphID >= crr[middle].Start && glyphID <= crr[middle].End )
+ {
+ *klass = crr[middle].Class;
+ error = HB_Err_Ok;
+ break;
+ }
+ else if ( glyphID < crr[middle].Start )
+ {
+ if ( middle == min )
+ {
+ *klass = 0;
+ error = HB_Err_Not_Covered;
+ break;
+ }
+ new_max = middle - 1;
+ }
+ else
+ {
+ if ( middle == max )
+ {
+ *klass = 0;
+ error = HB_Err_Not_Covered;
+ break;
+ }
+ new_min = middle + 1;
+ }
+ } while ( min < max );
+
+ if ( index )
+ *index = middle;
+
+ return error;
+}
+
+
+HB_INTERNAL HB_Error
+_HB_OPEN_Get_Class( HB_ClassDefinition* cd,
+ HB_UShort glyphID,
+ HB_UShort* klass,
+ HB_UShort* index )
+{
+ switch ( cd->ClassFormat )
+ {
+ case 1: return Get_Class1( &cd->cd.cd1, glyphID, klass, index );
+ case 2: return Get_Class2( &cd->cd.cd2, glyphID, klass, index );
+ default: return ERR(HB_Err_Invalid_SubTable_Format);
+ }
+
+ return HB_Err_Ok; /* never reached */
+}
+
+
+
+/***************************
+ * Device related functions
+ ***************************/
+
+
+HB_INTERNAL HB_Error
+_HB_OPEN_Load_Device( HB_Device* d,
+ HB_Stream stream )
+{
+ HB_Error error;
+
+ HB_UShort n, count;
+
+ HB_UShort* dv;
+
+
+ if ( ACCESS_Frame( 6L ) )
+ return error;
+
+ d->StartSize = GET_UShort();
+ d->EndSize = GET_UShort();
+ d->DeltaFormat = GET_UShort();
+
+ FORGET_Frame();
+
+ d->DeltaValue = NULL;
+
+ if ( d->StartSize > d->EndSize ||
+ d->DeltaFormat == 0 || d->DeltaFormat > 3 )
+ {
+ /* XXX
+ * I've seen fontforge generate DeltaFormat == 0.
+ * Just return Ok and let the NULL DeltaValue disable
+ * this table.
+ */
+ return HB_Err_Ok;
+ }
+
+ count = ( ( d->EndSize - d->StartSize + 1 ) >>
+ ( 4 - d->DeltaFormat ) ) + 1;
+
+ if ( ALLOC_ARRAY( d->DeltaValue, count, HB_UShort ) )
+ return error;
+
+ if ( ACCESS_Frame( count * 2L ) )
+ {
+ FREE( d->DeltaValue );
+ return error;
+ }
+
+ dv = d->DeltaValue;
+
+ for ( n = 0; n < count; n++ )
+ dv[n] = GET_UShort();
+
+ FORGET_Frame();
+
+ return HB_Err_Ok;
+}
+
+
+HB_INTERNAL void
+_HB_OPEN_Free_Device( HB_Device* d )
+{
+ FREE( d->DeltaValue );
+}
+
+
+/* Since we have the delta values stored in compressed form, we must
+ uncompress it now. To simplify the interface, the function always
+ returns a meaningful value in `value'; the error is just for
+ information.
+ | |
+ format = 1: 0011223344556677|8899101112131415|...
+ | |
+ byte 1 byte 2
+
+ 00: (byte >> 14) & mask
+ 11: (byte >> 12) & mask
+ ...
+
+ mask = 0x0003
+ | |
+ format = 2: 0000111122223333|4444555566667777|...
+ | |
+ byte 1 byte 2
+
+ 0000: (byte >> 12) & mask
+ 1111: (byte >> 8) & mask
+ ...
+
+ mask = 0x000F
+ | |
+ format = 3: 0000000011111111|2222222233333333|...
+ | |
+ byte 1 byte 2
+
+ 00000000: (byte >> 8) & mask
+ 11111111: (byte >> 0) & mask
+ ....
+
+ mask = 0x00FF */
+
+HB_INTERNAL HB_Error
+_HB_OPEN_Get_Device( HB_Device* d,
+ HB_UShort size,
+ HB_Short* value )
+{
+ HB_UShort byte, bits, mask, f, s;
+
+
+ f = d->DeltaFormat;
+
+ if ( d->DeltaValue && size >= d->StartSize && size <= d->EndSize )
+ {
+ s = size - d->StartSize;
+ byte = d->DeltaValue[s >> ( 4 - f )];
+ bits = byte >> ( 16 - ( ( s % ( 1 << ( 4 - f ) ) + 1 ) << f ) );
+ mask = 0xFFFF >> ( 16 - ( 1 << f ) );
+
+ *value = (HB_Short)( bits & mask );
+
+ /* conversion to a signed value */
+
+ if ( *value >= ( ( mask + 1 ) >> 1 ) )
+ *value -= mask + 1;
+
+ return HB_Err_Ok;
+ }
+ else
+ {
+ *value = 0;
+ return HB_Err_Not_Covered;
+ }
+}
+
+
+/* END */
diff --git a/src/harfbuzz-open.h b/src/harfbuzz-open.h
new file mode 100644
index 0000000..2ed4440
--- /dev/null
+++ b/src/harfbuzz-open.h
@@ -0,0 +1,282 @@
+/*
+ * Copyright (C) 1998-2004 David Turner and Werner Lemberg
+ * Copyright (C) 2006 Behdad Esfahbod
+ *
+ * This is part of HarfBuzz, an OpenType Layout engine 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 HARFBUZZ_OPEN_H
+#define HARFBUZZ_OPEN_H
+
+#include "harfbuzz-global.h"
+
+HB_BEGIN_HEADER
+
+/* Use this if a feature applies to all glyphs */
+#define HB_ALL_GLYPHS 0xFFFF
+
+#define HB_DEFAULT_LANGUAGE 0xFFFF
+
+#define HB_MAX_NESTING_LEVEL 100
+
+
+/* Script list related structures */
+
+struct HB_LangSys_
+{
+ HB_UShort LookupOrderOffset; /* always 0 for TT Open 1.0 */
+ HB_UShort ReqFeatureIndex; /* required FeatureIndex */
+ HB_UShort FeatureCount; /* number of Feature indices */
+ HB_UShort* FeatureIndex; /* array of Feature indices */
+};
+
+typedef struct HB_LangSys_ HB_LangSys;
+
+
+struct HB_LangSysRecord_
+{
+ HB_UInt LangSysTag; /* LangSysTag identifier */
+ HB_LangSys LangSys; /* LangSys table */
+};
+
+typedef struct HB_LangSysRecord_ HB_LangSysRecord;
+
+
+struct HB_ScriptTable_
+{
+ HB_LangSys DefaultLangSys; /* DefaultLangSys table */
+ HB_UShort LangSysCount; /* number of LangSysRecords */
+ HB_LangSysRecord* LangSysRecord; /* array of LangSysRecords */
+};
+
+typedef struct HB_ScriptTable_ HB_ScriptTable;
+
+
+struct HB_ScriptRecord_
+{
+ HB_UInt ScriptTag; /* ScriptTag identifier */
+ HB_ScriptTable Script; /* Script table */
+};
+
+typedef struct HB_ScriptRecord_ HB_ScriptRecord;
+
+
+struct HB_ScriptList_
+{
+ HB_UShort ScriptCount; /* number of ScriptRecords */
+ HB_ScriptRecord* ScriptRecord; /* array of ScriptRecords */
+};
+
+typedef struct HB_ScriptList_ HB_ScriptList;
+
+
+/* Feature list related structures */
+
+struct HB_Feature_
+{
+ HB_UShort FeatureParams; /* always 0 for TT Open 1.0 */
+ HB_UShort LookupListCount; /* number of LookupList indices */
+ HB_UShort* LookupListIndex; /* array of LookupList indices */
+};
+
+typedef struct HB_Feature_ HB_Feature;
+
+
+struct HB_FeatureRecord_
+{
+ HB_UInt FeatureTag; /* FeatureTag identifier */
+ HB_Feature Feature; /* Feature table */
+};
+
+typedef struct HB_FeatureRecord_ HB_FeatureRecord;
+
+
+struct HB_FeatureList_
+{
+ HB_UShort FeatureCount; /* number of FeatureRecords */
+ HB_FeatureRecord* FeatureRecord; /* array of FeatureRecords */
+ HB_UShort* ApplyOrder; /* order to apply features */
+ HB_UShort ApplyCount; /* number of elements in ApplyOrder */
+};
+
+typedef struct HB_FeatureList_ HB_FeatureList;
+
+
+/* Lookup list related structures */
+
+typedef struct HB_SubTable_ HB_SubTable;
+
+
+struct HB_Lookup_
+{
+ HB_UShort LookupType; /* Lookup type */
+ HB_UShort LookupFlag; /* Lookup qualifiers */
+ HB_UShort SubTableCount; /* number of SubTables */
+ HB_SubTable* SubTable; /* array of SubTables */
+};
+
+typedef struct HB_Lookup_ HB_Lookup;
+
+
+/* The `Properties' field is not defined in the OpenType specification but
+ is needed for processing lookups. If properties[n] is > 0, the
+ functions HB_GSUB_Apply_String() resp. HB_GPOS_Apply_String() will
+ process Lookup[n] for glyphs which have the specific bit not set in
+ the `properties' field of the input string object. */
+
+struct HB_LookupList_
+{
+ HB_UShort LookupCount; /* number of Lookups */
+ HB_Lookup* Lookup; /* array of Lookup records */
+ HB_UInt* Properties; /* array of flags */
+};
+
+typedef struct HB_LookupList_ HB_LookupList;
+
+
+/* Possible LookupFlag bit masks. `HB_LOOKUP_FLAG_IGNORE_SPECIAL_MARKS' comes from the
+ OpenType 1.2 specification; HB_LOOKUP_FLAG_RIGHT_TO_LEFT has been (re)introduced in
+ OpenType 1.3 -- if set, the last glyph in a cursive attachment
+ sequence has to be positioned on the baseline -- regardless of the
+ writing direction. */
+
+#define HB_LOOKUP_FLAG_RIGHT_TO_LEFT 0x0001
+#define HB_LOOKUP_FLAG_IGNORE_BASE_GLYPHS 0x0002
+#define HB_LOOKUP_FLAG_IGNORE_LIGATURES 0x0004
+#define HB_LOOKUP_FLAG_IGNORE_MARKS 0x0008
+#define HB_LOOKUP_FLAG_IGNORE_SPECIAL_MARKS 0xFF00
+
+
+struct HB_CoverageFormat1_
+{
+ HB_UShort GlyphCount; /* number of glyphs in GlyphArray */
+ HB_UShort* GlyphArray; /* array of glyph IDs */
+};
+
+typedef struct HB_CoverageFormat1_ HB_CoverageFormat1;
+
+
+struct HB_RangeRecord_
+{
+ HB_UShort Start; /* first glyph ID in the range */
+ HB_UShort End; /* last glyph ID in the range */
+ HB_UShort StartCoverageIndex; /* coverage index of first
+ glyph ID in the range */
+};
+
+typedef struct HB_RangeRecord_ HB_RangeRecord;
+
+
+struct HB_CoverageFormat2_
+{
+ HB_UShort RangeCount; /* number of RangeRecords */
+ HB_RangeRecord* RangeRecord; /* array of RangeRecords */
+};
+
+typedef struct HB_CoverageFormat2_ HB_CoverageFormat2;
+
+
+struct HB_Coverage_
+{
+ HB_UShort CoverageFormat; /* 1 or 2 */
+
+ union
+ {
+ HB_CoverageFormat1 cf1;
+ HB_CoverageFormat2 cf2;
+ } cf;
+};
+
+typedef struct HB_Coverage_ HB_Coverage;
+
+
+struct HB_ClassDefFormat1_
+{
+ HB_UShort StartGlyph; /* first glyph ID of the
+ ClassValueArray */
+ HB_UShort GlyphCount; /* size of the ClassValueArray */
+ HB_UShort* ClassValueArray; /* array of class values */
+};
+
+typedef struct HB_ClassDefFormat1_ HB_ClassDefFormat1;
+
+
+struct HB_ClassRangeRecord_
+{
+ HB_UShort Start; /* first glyph ID in the range */
+ HB_UShort End; /* last glyph ID in the range */
+ HB_UShort Class; /* applied to all glyphs in range */
+};
+
+typedef struct HB_ClassRangeRecord_ HB_ClassRangeRecord;
+
+
+struct HB_ClassDefFormat2_
+{
+ HB_UShort ClassRangeCount;
+ /* number of ClassRangeRecords */
+ HB_ClassRangeRecord* ClassRangeRecord;
+ /* array of ClassRangeRecords */
+};
+
+typedef struct HB_ClassDefFormat2_ HB_ClassDefFormat2;
+
+
+struct HB_ClassDefinition_
+{
+ HB_Bool loaded;
+
+ HB_UShort ClassFormat; /* 1 or 2 */
+
+ union
+ {
+ HB_ClassDefFormat1 cd1;
+ HB_ClassDefFormat2 cd2;
+ } cd;
+};
+
+typedef struct HB_ClassDefinition_ HB_ClassDefinition;
+
+
+struct HB_Device_
+{
+ HB_UShort StartSize; /* smallest size to correct */
+ HB_UShort EndSize; /* largest size to correct */
+ HB_UShort DeltaFormat; /* DeltaValue array data format:
+ 1, 2, or 3 */
+ HB_UShort* DeltaValue; /* array of compressed data */
+};
+
+typedef struct HB_Device_ HB_Device;
+
+
+enum HB_Type_
+{
+ HB_Type_GSUB,
+ HB_Type_GPOS
+};
+
+typedef enum HB_Type_ HB_Type;
+
+
+HB_END_HEADER
+
+#endif /* HARFBUZZ_OPEN_H */
diff --git a/src/harfbuzz-stream-private.h b/src/harfbuzz-stream-private.h
new file mode 100644
index 0000000..1d88584
--- /dev/null
+++ b/src/harfbuzz-stream-private.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 1998-2004 David Turner and Werner Lemberg
+ * Copyright (C) 2006 Behdad Esfahbod
+ *
+ * This is part of HarfBuzz, an OpenType Layout engine 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 HARFBUZZ_STREAM_PRIVATE_H
+#define HARFBUZZ_STREAM_PRIVATE_H
+
+#include "harfbuzz-impl.h"
+
+HB_BEGIN_HEADER
+
+typedef FT_Stream HB_Stream;
+
+HB_INTERNAL HB_Int
+_hb_stream_pos( HB_Stream stream );
+
+HB_INTERNAL HB_Error
+_hb_stream_seek( HB_Stream stream,
+ HB_UInt pos );
+
+HB_INTERNAL HB_Error
+_hb_stream_frame_enter( HB_Stream stream,
+ HB_UInt size );
+
+HB_INTERNAL void
+_hb_stream_frame_exit( HB_Stream stream );
+
+HB_INTERNAL HB_Error
+_hb_font_goto_table( HB_Font font,
+ HB_UInt tag );
+
+/* convenience macros */
+
+#define SET_ERR(c) ( (error = (c)) != 0 )
+
+#define GOTO_Table(tag) SET_ERR( _hb_font_goto_table( font, tag ) )
+#define FILE_Pos() _hb_stream_pos( stream )
+#define FILE_Seek(pos) SET_ERR( _hb_stream_seek( stream, pos ) )
+#define ACCESS_Frame(size) SET_ERR( _hb_stream_frame_enter( stream, size ) )
+#define FORGET_Frame() _hb_stream_frame_exit( stream )
+
+#define GET_Byte() (*stream->cursor++)
+#define GET_Short() (stream->cursor += 2, (HB_Short)( \
+ (*(((HB_Byte*)stream->cursor)-2) << 8) | \
+ *(((HB_Byte*)stream->cursor)-1) \
+ ))
+#define GET_Long() (stream->cursor += 4, (HB_Int)( \
+ (*(((HB_Byte*)stream->cursor)-4) << 24) | \
+ (*(((HB_Byte*)stream->cursor)-3) << 16) | \
+ (*(((HB_Byte*)stream->cursor)-2) << 8) | \
+ *(((HB_Byte*)stream->cursor)-1) \
+ ))
+
+
+#define GET_Char() ((HB_Char)GET_Byte())
+#define GET_UShort() ((HB_UShort)GET_Short())
+#define GET_ULong() ((HB_UInt)GET_Long())
+#define GET_Tag4() GET_ULong()
+
+HB_END_HEADER
+
+#endif /* HARFBUZZ_STREAM_PRIVATE_H */
diff --git a/src/harfbuzz-stream.c b/src/harfbuzz-stream.c
new file mode 100644
index 0000000..6d35479
--- /dev/null
+++ b/src/harfbuzz-stream.c
@@ -0,0 +1,257 @@
+/*
+ * Copyright (C) 2005 David Turner
+ * Copyright (C) 2007 Trolltech ASA
+ * Copyright (C) 2007 Red Hat, Inc.
+ *
+ * This is part of HarfBuzz, an OpenType Layout engine library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Red Hat Author(s): Behdad Esfahbod
+ */
+
+#include "harfbuzz-impl.h"
+#include "harfbuzz-stream-private.h"
+#include <stdlib.h>
+
+#if 0
+#include <stdio.h>
+#define LOG(x) _hb_log x
+
+static void
+_hb_log( const char* format, ... )
+{
+ va_list ap;
+
+ va_start( ap, format );
+ vfprintf( stderr, format, ap );
+ va_end( ap );
+}
+
+#else
+#define LOG(x) do {} while (0)
+#endif
+
+
+/* only used internally */
+static HB_Pointer
+_hb_qalloc( HB_UInt size,
+ HB_Error *perror )
+{
+ HB_Error error = 0;
+ HB_Pointer block = NULL;
+
+ if ( size > 0 )
+ {
+ block = malloc( size );
+ if ( !block )
+ error = ERR(HB_Err_Out_Of_Memory);
+ }
+
+ *perror = error;
+ return block;
+}
+
+#undef QALLOC /* just in case */
+#define QALLOC(ptr,size) ( (ptr) = _hb_qalloc( (size), &error ), error != 0 )
+
+HB_INTERNAL HB_Int
+_hb_stream_pos( HB_Stream stream )
+{
+ LOG(( "_hb_stream_pos() -> %ld\n", stream->pos ));
+ return stream->pos;
+}
+
+
+HB_INTERNAL HB_Error
+_hb_stream_seek( HB_Stream stream,
+ HB_UInt pos )
+{
+ HB_Error error = 0;
+
+ stream->pos = pos;
+ if ( stream->read )
+ {
+ if ( stream->read( stream, pos, NULL, 0 ) )
+ error = ERR(HB_Err_Read_Error);
+ }
+ else if ( pos > stream->size )
+ error = ERR(HB_Err_Read_Error);
+
+ LOG(( "_hb_stream_seek(%ld) -> 0x%04X\n", pos, error ));
+ return error;
+}
+
+
+HB_INTERNAL HB_Error
+_hb_stream_frame_enter( HB_Stream stream,
+ HB_UInt count )
+{
+ HB_Error error = HB_Err_Ok;
+ HB_UInt read_bytes;
+
+ if ( stream->read )
+ {
+ /* allocate the frame in memory */
+
+ if ( QALLOC( stream->base, count ) )
+ goto Exit;
+
+ /* read it */
+ read_bytes = stream->read( stream, stream->pos,
+ stream->base, count );
+ if ( read_bytes < count )
+ {
+ FREE( stream->base );
+ error = ERR(HB_Err_Read_Error);
+ }
+ stream->cursor = stream->base;
+ stream->limit = stream->cursor + count;
+ stream->pos += read_bytes;
+ }
+ else
+ {
+ /* check new position, watch for overflow */
+ if (HB_UNLIKELY (stream->pos + count > stream->size ||
+ stream->pos + count < stream->pos))
+ {
+ error = ERR(HB_Err_Read_Error);
+ goto Exit;
+ }
+
+ /* set cursor */
+ stream->cursor = stream->base + stream->pos;
+ stream->limit = stream->cursor + count;
+ stream->pos += count;
+ }
+
+Exit:
+ LOG(( "_hb_stream_frame_enter(%ld) -> 0x%04X\n", count, error ));
+ return error;
+}
+
+
+HB_INTERNAL void
+_hb_stream_frame_exit( HB_Stream stream )
+{
+ if ( stream->read )
+ {
+ FREE( stream->base );
+ }
+ stream->cursor = NULL;
+ stream->limit = NULL;
+
+ LOG(( "_hb_stream_frame_exit()\n" ));
+}
+
+
+HB_INTERNAL HB_Error
+_hb_font_goto_table( HB_Font font,
+ HB_UInt the_tag )
+{
+ HB_Stream stream = font->stream;
+
+ HB_UInt offset = 0, sig;
+ HB_UInt count, nn;
+ HB_Error error;
+
+ LOG(( "_hb_font_goto_table( %p, %c%c%c%c, %p )\n",
+ font,
+ (int)((the_tag >> 24) & 0xFF),
+ (int)((the_tag >> 16) & 0xFF),
+ (int)((the_tag >> 8) & 0xFF),
+ (int)(the_tag & 0xFF),
+ stream ));
+
+ if ( !FT_IS_SFNT(font) )
+ {
+ LOG(( "not a SFNT font !!\n" ));
+ error = ERR(HB_Err_Invalid_Argument);
+ goto Exit;
+ }
+
+ /* parse the directory table directly, without using
+ * FreeType's built-in data structures
+ */
+
+ if ( FILE_Seek( 0 ) || ACCESS_Frame( 4 ) )
+ goto Exit;
+
+ sig = GET_Tag4();
+
+ FORGET_Frame();
+
+ if ( sig == HB_MAKE_TAG( 't', 't', 'c', 'f' ) )
+ {
+ /* deal with TrueType collections */
+ LOG(( ">> This is a TrueType Collection\n" ));
+
+ if ( FILE_Seek( 12 + font->face_index*4 ) ||
+ ACCESS_Frame( 4 ) )
+ goto Exit;
+
+ offset = GET_ULong();
+
+ FORGET_Frame();
+ }
+
+ LOG(( "TrueType offset = %ld\n", offset ));
+
+ if ( FILE_Seek( offset+4 ) ||
+ ACCESS_Frame( 2 ) )
+ goto Exit;
+
+ count = GET_UShort();
+
+ FORGET_Frame();
+
+ if ( FILE_Seek( offset+12 ) ||
+ ACCESS_Frame( count*16 ) )
+ goto Exit;
+
+ for ( nn = 0; nn < count; nn++ )
+ {
+ HB_UInt tag = GET_ULong();
+ HB_UInt checksum = GET_ULong();
+ HB_UInt start = GET_ULong();
+ HB_UInt size = GET_ULong();
+
+ HB_UNUSED(checksum);
+ HB_UNUSED(size);
+
+ if ( tag == the_tag )
+ {
+ LOG(( "TrueType table (start: %ld) (size: %ld)\n", start, size ));
+ error = _hb_stream_seek( stream, start );
+ goto FoundIt;
+ }
+ }
+ error = HB_Err_Not_Covered;
+
+FoundIt:
+ FORGET_Frame();
+
+Exit:
+ LOG(( "TrueType error=%d\n", error ));
+
+ return error;
+}
+
+#undef QALLOC
+
diff --git a/src/harfbuzz.c b/src/harfbuzz.c
new file mode 100644
index 0000000..b7e64bc
--- /dev/null
+++ b/src/harfbuzz.c
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2006 Behdad Esfahbod
+ *
+ * This is part of HarfBuzz, an OpenType Layout engine 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.
+ */
+
+#define HB_INTERNAL static
+#include "harfbuzz-buffer.c"
+#include "harfbuzz-gdef.c"
+#include "harfbuzz-gsub.c"
+#include "harfbuzz-gpos.c"
+#include "harfbuzz-impl.c"
+#include "harfbuzz-open.c"
+#include "harfbuzz-stream.c"
diff --git a/src/harfbuzz.h b/src/harfbuzz.h
new file mode 100644
index 0000000..d23b6bc
--- /dev/null
+++ b/src/harfbuzz.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 1998-2004 David Turner and Werner Lemberg
+ * Copyright (C) 2006 Behdad Esfahbod
+ *
+ * This is part of HarfBuzz, an OpenType Layout engine 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 HARFBUZZ_H
+#define HARFBUZZ_H
+
+#include "harfbuzz-global.h"
+#include "harfbuzz-buffer.h"
+#include "harfbuzz-gsub.h"
+#include "harfbuzz-gpos.h"
+#include "harfbuzz-open.h"
+
+#endif /* HARFBUZZ_OPEN_H */
diff --git a/src/hb-ot-layout-gsub-private.h b/src/hb-ot-layout-gsub-private.h
index ae41090..c8d5405 100644
--- a/src/hb-ot-layout-gsub-private.h
+++ b/src/hb-ot-layout-gsub-private.h
@@ -549,13 +549,13 @@
if (HB_UNLIKELY (nesting_level_left == 0))
return false;
nesting_level_left--;
-
+
for (unsigned int i = 0; i < get_subtable_count (); i++)
if (get_subtable (i).substitute (layout, buffer,
context_length, nesting_level_left,
lookup_type))
return true;
-
+
return false;
}
};
diff --git a/src/hb-ot-layout-open-private.h b/src/hb-ot-layout-open-private.h
index 66fd55e..d5ca810 100644
--- a/src/hb-ot-layout-open-private.h
+++ b/src/hb-ot-layout-open-private.h
@@ -617,7 +617,7 @@
private:
Offset featureParams; /* Offset to Feature Parameters table (if one
* has been defined for the feature), relative
- * to the beginning of the Feature Table; = Null
+ * to the beginning of the Feature Table; = Null
* if not required */
USHORT lookupCount; /* Number of LookupList indices for this
* feature */
@@ -935,7 +935,7 @@
uint16_t byte = deltaValue[s >> (4 - f)];
uint16_t bits = byte >> (16 - (((s & ((1 << (4 - f)) - 1)) + 1) << f));
uint16_t mask = 0xFFFF >> (16 - (1 << f));
-
+
int delta = bits & mask;
if (delta >= ((mask + 1) >> 1))
diff --git a/src/hb-ot-layout.cc b/src/hb-ot-layout.cc
index 68fd3e5..01923db 100644
--- a/src/hb-ot-layout.cc
+++ b/src/hb-ot-layout.cc
@@ -223,7 +223,7 @@
if (G_UNLIKELY (!new_klasses))
return;
-
+
memset (new_klasses + len, 0, new_len - len);
layout->new_gdef.klasses = new_klasses;
diff --git a/src/main.cc b/src/main.cc
index 4c24ff4..ffd13b4 100644
--- a/src/main.cc
+++ b/src/main.cc
@@ -43,9 +43,9 @@
GMappedFile *mf = g_mapped_file_new (argv[1], FALSE, NULL);
const char *font_data = g_mapped_file_get_contents (mf);
int len = g_mapped_file_get_length (mf);
-
+
printf ("Opened font file %s: %d bytes long\n", argv[1], len);
-
+
const OpenTypeFontFile &ot = OpenTypeFontFile::get_for_data (font_data);
switch (ot.get_tag()) {
diff --git a/src/makefile.msc b/src/makefile.msc
new file mode 100644
index 0000000..2943883
--- /dev/null
+++ b/src/makefile.msc
@@ -0,0 +1,19 @@
+TOP = ..\..\..
+!INCLUDE $(TOP)\glib\build\win32\make.msc
+
+INCLUDES = -I . -I ..\.. $(FREETYPE2_CFLAGS) $(GLIB_CFLAGS)
+DEFINES = -DPANGO_ENABLE_ENGINE
+
+OBJECTS = \
+ ftglue.obj \
+ harfbuzz-buffer.obj \
+ harfbuzz-gdef.obj \
+ harfbuzz-gpos.obj \
+ harfbuzz-gsub.obj \
+ harfbuzz-open.obj \
+
+all : \
+ pango-ot.lib
+
+pango-ot.lib : $(OBJECTS)
+ lib /out:$@ $(OBJECTS)
\ No newline at end of file